Failed Conditions
Push — v7 ( 69552d...3dace9 )
by Florent
03:23
created

JSONGeneralSerializer::unserialize()   F

Complexity

Conditions 15
Paths 239

Size

Total Lines 47
Code Lines 33

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 47
rs 3.9217
c 0
b 0
f 0
cc 15
eloc 33
nc 239
nop 1

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * The MIT License (MIT)
7
 *
8
 * Copyright (c) 2014-2017 Spomky-Labs
9
 *
10
 * This software may be modified and distributed under the terms
11
 * of the MIT license.  See the LICENSE file for details.
12
 */
13
14
namespace Jose\Component\Signature\Serializer;
15
16
use Base64Url\Base64Url;
17
use Jose\Component\Signature\JWS;
18
19
/**
20
 * Class JSONGeneralSerializer.
21
 */
22
final class JSONGeneralSerializer extends AbstractSerializer
23
{
24
    public const NAME = 'jws_json_general';
25
26
    /**
27
     * {@inheritdoc}
28
     */
29
    public function displayName(): string
30
    {
31
        return 'JWS JSON General';
32
    }
33
34
    /**
35
     * {@inheritdoc}
36
     */
37
    public function name(): string
38
    {
39
        return self::NAME;
40
    }
41
42
    /**
43
     * {@inheritdoc}
44
     */
45
    public function serialize(JWS $jws, ?int $signatureIndex = null): string
46
    {
47
        if (0 === $jws->countSignatures()) {
48
            throw new \LogicException('No signature.');
49
        }
50
51
        $data = [];
52
        $this->checkPayloadEncoding($jws);
53
54
        if (false === $jws->isPayloadDetached()) {
55
            $data['payload'] = $jws->getEncodedPayload();
56
        }
57
58
        $data['signatures'] = [];
59
        foreach ($jws->getSignatures() as $signature) {
60
            $tmp = ['signature' => Base64Url::encode($signature->getSignature())];
61
            $values = [
62
                'protected' => $signature->getEncodedProtectedHeaders(),
63
                'header' => $signature->getHeaders(),
64
            ];
65
66
            foreach ($values as $key => $value) {
67
                if (!empty($value)) {
68
                    $tmp[$key] = $value;
69
                }
70
            }
71
            $data['signatures'][] = $tmp;
72
        }
73
74
        return json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
75
    }
76
77
    /**
78
     * {@inheritdoc}
79
     */
80
    public function unserialize(string $input): JWS
81
    {
82
        $data = json_decode($input, true);
83
        if (!is_array($data) || !array_key_exists('signatures', $data)) {
84
            throw new \InvalidArgumentException('Unsupported input.');
85
        }
86
87
        $isPayloadEncoded = null;
88
        $rawPayload = array_key_exists('payload', $data) ? $data['payload'] : null;
89
        $signatures = [];
90
        foreach ($data['signatures'] as $signature) {
91
            if (!is_array($signature) || !array_key_exists('signature', $signature)) {
92
                throw new \InvalidArgumentException('Unsupported input.');
93
            }
94
            $encodedProtectedHeaders = array_key_exists('protected', $signature) ? $signature['protected'] : null;
95
            $protectedHeaders = null !== $encodedProtectedHeaders ? json_decode(Base64Url::decode($encodedProtectedHeaders), true) : [];
96
            $signatures[] = [
97
                'signature' => Base64Url::decode($signature['signature']),
98
                'protected' => $protectedHeaders,
99
                'encoded_protected' => $encodedProtectedHeaders,
100
                'header' => array_key_exists('header', $signature) ? $signature['header'] : [],
101
            ];
102
            if (null === $isPayloadEncoded) {
103
                $isPayloadEncoded = self::isPayloadEncoded($protectedHeaders);
104
            }
105
            if ($this->isPayloadEncoded($protectedHeaders) !== $isPayloadEncoded) {
106
                throw new \InvalidArgumentException('Foreign payload encoding detected.');
107
            }
108
        }
109
110
        if (null === $rawPayload) {
111
            $payload = null;
112
        } else {
113
            $payload = false === $isPayloadEncoded ? $rawPayload : Base64Url::decode($rawPayload);
114
        }
115
        $jws = JWS::create($payload, $rawPayload);
116
        foreach ($signatures as $signature) {
117
            $jws = $jws->addSignature(
118
                $signature['signature'],
119
                $signature['protected'],
120
                $signature['encoded_protected'],
121
                $signature['header']
122
            );
123
        }
124
125
        return $jws;
126
    }
127
128
    /**
129
     * @param JWS $jws
130
     */
131
    private function checkPayloadEncoding(JWS $jws)
132
    {
133
        if ($jws->isPayloadDetached()) {
134
            return;
135
        }
136
        $is_encoded = null;
137
        foreach ($jws->getSignatures() as $signature) {
138
            if (null === $is_encoded) {
139
                $is_encoded = $this->isPayloadEncoded($signature->getProtectedHeaders());
140
            }
141
            if (false === $jws->isPayloadDetached()) {
142
                if ($is_encoded !== $this->isPayloadEncoded($signature->getProtectedHeaders())) {
143
                    throw new \LogicException('Foreign payload encoding detected.');
144
                }
145
            }
146
        }
147
    }
148
}
149