Completed
Push — master ( 384ebc...73c777 )
by Florent
02:36
created

JWS::toJSON()   B

Complexity

Conditions 5
Paths 8

Size

Total Lines 29
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 1
Metric Value
c 2
b 0
f 1
dl 0
loc 29
rs 8.439
cc 5
eloc 17
nc 8
nop 0
1
<?php
2
3
/*
4
 * The MIT License (MIT)
5
 *
6
 * Copyright (c) 2014-2016 Spomky-Labs
7
 *
8
 * This software may be modified and distributed under the terms
9
 * of the MIT license.  See the LICENSE file for details.
10
 */
11
12
namespace Jose\Object;
13
14
use Assert\Assertion;
15
use Base64Url\Base64Url;
16
17
/**
18
 * Class JWS.
19
 */
20
final class JWS implements JWSInterface
21
{
22
    use JWT;
23
24
    /**
25
     * @var bool
26
     */
27
    private $is_payload_detached = false;
28
29
    /**
30
     * @var string|null
31
     */
32
    private $encoded_payload = null;
33
34
    /**
35
     * @var \Jose\Object\SignatureInterface[]
36
     */
37
    private $signatures = [];
38
39
    /**
40
     * {@inheritdoc}
41
     */
42
    public function isPayloadDetached()
43
    {
44
        return $this->is_payload_detached;
45
    }
46
47
    /**
48
     * {@inheritdoc}
49
     */
50
    public function withDetachedPayload()
51
    {
52
        $jwt = clone $this;
53
        $jwt->is_payload_detached = true;
54
55
        return $jwt;
56
    }
57
58
    /**
59
     * {@inheritdoc}
60
     */
61
    public function withAttachedPayload()
62
    {
63
        $jwt = clone $this;
64
        $jwt->is_payload_detached = false;
65
66
        return $jwt;
67
    }
68
69
    /**
70
     * {@inheritdoc}
71
     */
72
    public function withEncodedPayload($encoded_payload)
73
    {
74
        $jwt = clone $this;
75
        $jwt->encoded_payload = $encoded_payload;
76
77
        return $jwt;
78
    }
79
80
    /**
81
     * {@inheritdoc}
82
     */
83
    public function getEncodedPayload(SignatureInterface $signature)
84
    {
85
        if (true === $this->isPayloadDetached()) {
86
            return;
87
        }
88
        if (null !== $this->encoded_payload) {
89
            return $this->encoded_payload;
90
        }
91
        $payload = $this->getPayload();
92
        if (!is_string($payload)) {
93
            $payload = json_encode($payload);
94
        }
95
        Assertion::notNull($payload, 'Unsupported payload.');
96
97
        return $this->isPayloadEncoded($signature) ? Base64Url::encode($payload) : $payload;
98
    }
99
100
    /**
101
     * {@inheritdoc}
102
     */
103
    public function getSignatures()
104
    {
105
        return $this->signatures;
106
    }
107
108
    /**
109
     * {@inheritdoc}
110
     */
111
    public function &getSignature($id)
112
    {
113
        if (isset($this->signatures[$id])) {
114
            return $this->signatures[$id];
115
        }
116
        throw new \InvalidArgumentException('The signature does not exist.');
117
    }
118
119
    /**
120
     * {@inheritdoc}
121
     */
122
    public function addSignatureInformation(JWKInterface $signature_key, array $protected_headers, array $headers = [])
123
    {
124
        $jws = clone $this;
125
        $jws->signatures[] = Signature::createSignature($signature_key, $protected_headers, $headers);
126
127
        return $jws;
128
    }
129
130
    /**
131
     * {@inheritdoc}
132
     */
133
    public function addSignatureFromLoadedData($signature, $encoded_protected_headers, array $headers)
134
    {
135
        $jws = clone $this;
136
        $jws->signatures[] = Signature::createSignatureFromLoadedData($signature, $encoded_protected_headers, $headers);
137
138
        return $jws;
139
    }
140
141
    /**
142
     * {@inheritdoc}
143
     */
144
    public function countSignatures()
145
    {
146
        return count($this->signatures);
147
    }
148
149
    /**
150
     * {@inheritdoc}
151
     */
152
    public function toCompactJSON($id)
153
    {
154
        $signature = $this->getSignature($id);
155
156
        Assertion::true(
157
            empty($signature->getHeaders()),
158
            'The signature contains unprotected headers and cannot be converted into compact JSON'
159
        );
160
        Assertion::true($this->isPayloadEncoded($signature) || empty($this->getEncodedPayload($signature)), 'Unable to convert the JWS with non-encoded payload.');
161
162
        return sprintf(
163
            '%s.%s.%s',
164
            $signature->getEncodedProtectedHeaders(),
165
            $this->getEncodedPayload($signature),
166
            Base64Url::encode($signature->getSignature())
167
        );
168
    }
169
170
    /**
171
     * {@inheritdoc}
172
     */
173
    public function toFlattenedJSON($id)
174
    {
175
        $signature = $this->getSignature($id);
176
177
        $data = [];
178
        $values = [
179
            'payload'   => $this->getEncodedPayload($signature),
180
            'protected' => $signature->getEncodedProtectedHeaders(),
181
            'header'    => $signature->getHeaders(),
182
        ];
183
184
        foreach ($values as $key => $value) {
185
            if (!empty($value)) {
186
                $data[$key] = $value;
187
            }
188
        }
189
        $data['signature'] = Base64Url::encode($signature->getSignature());
190
191
        return json_encode($data);
192
    }
193
194
    /**
195
     * {@inheritdoc}
196
     */
197
    public function toJSON()
198
    {
199
        Assertion::greaterThan($this->countSignatures(), 0, 'No signature.');
200
201
        $data = [];
202
        $this->checkPayloadEncoding();
203
204
        if (false === $this->isPayloadDetached()) {
205
            $data['payload'] = $this->getEncodedPayload($this->getSignature(0));
206
        }
207
208
        $data['signatures'] = [];
209
        foreach ($this->getSignatures() as $signature) {
210
            $tmp = ['signature' => Base64Url::encode($signature->getSignature())];
211
            $values = [
212
                'protected' => $signature->getEncodedProtectedHeaders(),
213
                'header'    => $signature->getHeaders(),
214
            ];
215
216
            foreach ($values as $key => $value) {
217
                if (!empty($value)) {
218
                    $tmp[$key] = $value;
219
                }
220
            }
221
            $data['signatures'][] = $tmp;
222
        }
223
224
        return json_encode($data);
225
    }
226
227
    /**
228
     * @param \Jose\Object\SignatureInterface $signature
229
     *
230
     * @return bool
231
     */
232
    private function isPayloadEncoded(SignatureInterface $signature)
233
    {
234
        return !$signature->hasProtectedHeader('b64') || true === $signature->getProtectedHeader('b64');
235
    }
236
237
    private function checkPayloadEncoding()
238
    {
239
        $is_encoded = null;
240
        foreach ($this->getSignatures() as $signature) {
241
            if (null === $is_encoded) {
242
                $is_encoded = $this->isPayloadEncoded($signature);
243
            }
244
            if (false === $this->isPayloadDetached()) {
245
                Assertion::eq($is_encoded, $this->isPayloadEncoded($signature), 'Foreign payload encoding detected. The JWS cannot be converted.');
246
            }
247
        }
248
    }
249
}
250