GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

ValidationContext   A
last analyzed

Complexity

Total Complexity 38

Size/Duplication

Total Lines 403
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 38
eloc 103
dl 0
loc 403
ccs 91
cts 91
cp 1
rs 9.36
c 0
b 0
f 0

24 Methods

Rating   Name   Duplication   Size   Complexity  
A withPermittedAlgorithms() 0 5 1
A withProhibitedAlgorithms() 0 7 2
A withPermittedAlgorithmsAdded() 0 7 2
A referenceTime() 0 6 2
A withReferenceTime() 0 5 1
A validator() 0 6 2
A isPermittedAlgorithm() 0 4 2
A hasConstraint() 0 3 1
A __construct() 0 8 3
A withIssuer() 0 3 1
A fromJWK() 0 3 1
A hasValidator() 0 3 1
A withAudience() 0 3 1
A isUnsecuredAllowed() 0 3 1
A hasReferenceTime() 0 3 1
A withConstraint() 0 9 2
A withUnsecuredAllowed() 0 5 1
A leeway() 0 3 1
A constraint() 0 6 2
A withLeeway() 0 5 1
A withID() 0 3 1
A keys() 0 3 1
A withSubject() 0 3 1
A validate() 0 22 6
1
<?php
2
3
declare(strict_types = 1);
4
5
namespace Sop\JWX\JWT;
6
7
use Sop\JWX\JWA\JWA;
8
use Sop\JWX\JWK\JWK;
9
use Sop\JWX\JWK\JWKSet;
10
use Sop\JWX\JWT\Claim\RegisteredClaim;
11
use Sop\JWX\JWT\Claim\Validator\Validator;
12
use Sop\JWX\JWT\Exception\ValidationException;
13
14
/**
15
 * Class to provide context for claims validation.
16
 *
17
 * Validation constraints are variables, that are compared against the claims.
18
 * Validation of the expiration, not-before and not-after claims is provided by
19
 * default.
20
 *
21
 * Constraints configured for the validation context must be present in the
22
 * validated set of claims, or else validation fails.
23
 *
24
 * Context also provides a set of JSON Web Keys, that shall be used for the
25
 * JWS signature validation or JWE payload decryption.
26
 *
27
 * Registered claims provide their own validation logic. Claims that are not
28
 * supported by this library must be provided with an explicit validator along
29
 * with the constraint.
30
 */
31
class ValidationContext
32
{
33
    /**
34
     * Reference time.
35
     *
36
     * @var int
37
     */
38
    protected $_refTime;
39
40
    /**
41
     * Leeway in seconds for the reference time constraints.
42
     *
43
     * @var int
44
     */
45
    protected $_leeway;
46
47
    /**
48
     * Validation constraints.
49
     *
50
     * @var array
51
     */
52
    protected $_constraints;
53
54
    /**
55
     * Explicitly defined validators for named claims.
56
     *
57
     * @var Validator[]
58
     */
59
    protected $_validators;
60
61
    /**
62
     * Set of JSON Web Keys usable for the validation.
63
     *
64
     * @var JWKSet
65
     */
66
    protected $_keys;
67
68
    /**
69
     * Whether to allow unsecured JWT's, that is, claims without integrity
70
     * protection nor encryption.
71
     *
72
     * @var bool
73
     */
74
    protected $_allowUnsecured;
75
76
    /**
77
     * List of permitted algorithms.
78
     *
79
     * By default only asymmetric key derivation algorithms are prohibited.
80
     *
81
     * @var string[]
82
     */
83
    protected $_permittedAlgoritms = [
84
        // all signature algorithms are safe to use
85
        JWA::ALGO_HS256 => true,
86
        JWA::ALGO_HS384 => true,
87
        JWA::ALGO_HS512 => true,
88
        JWA::ALGO_RS256 => true,
89
        JWA::ALGO_RS384 => true,
90
        JWA::ALGO_RS512 => true,
91
        JWA::ALGO_ES256 => true,
92
        JWA::ALGO_ES384 => true,
93
        JWA::ALGO_ES512 => true,
94
        // unsecured JWS may be used when explicitly allowed
95
        JWA::ALGO_NONE => true,
96
        // all symmetric key derivation algorithms are safe to use
97
        JWA::ALGO_A128KW => true,
98
        JWA::ALGO_A192KW => true,
99
        JWA::ALGO_A256KW => true,
100
        JWA::ALGO_A128GCMKW => true,
101
        JWA::ALGO_A192GCMKW => true,
102
        JWA::ALGO_A256GCMKW => true,
103
        JWA::ALGO_PBES2_HS256_A128KW => true,
104
        JWA::ALGO_PBES2_HS384_A192KW => true,
105
        JWA::ALGO_PBES2_HS512_A256KW => true,
106
        JWA::ALGO_DIR => true,
107
        /* asymmetric key derivation algorithms are subject to
108
           "sign/encrypt confusion" vulnerability, in which the JWT consumer
109
           can be tricked to accept a JWE token instead of a JWS by using
110
           the public key for encryption. */
111
        JWA::ALGO_RSA1_5 => false,
112
        JWA::ALGO_RSA_OAEP => false,
113
        // all encryption algorithms are safe to use
114
        JWA::ALGO_A128CBC_HS256 => true,
115
        JWA::ALGO_A192CBC_HS384 => true,
116
        JWA::ALGO_A256CBC_HS512 => true,
117
        JWA::ALGO_A128GCM => true,
118
        JWA::ALGO_A192GCM => true,
119
        JWA::ALGO_A256GCM => true,
120
        // all compression algorithms are safe to use
121
        JWA::ALGO_DEFLATE => true,
122
    ];
123
124
    /**
125
     * Constructor.
126
     *
127
     * @param null|array  $constraints Optional array of constraints for the
128
     *                                 registered claims
129
     * @param null|JWKSet $keys        Optional set of JSON Web Keys used for
130
     *                                 signature validation and/or decryption
131
     */
132 42
    public function __construct(?array $constraints = null, ?JWKSet $keys = null)
133
    {
134 42
        $this->_refTime = time();
135 42
        $this->_leeway = 60;
136 42
        $this->_constraints = $constraints ? $constraints : [];
137 42
        $this->_validators = [];
138 42
        $this->_keys = $keys ? $keys : new JWKSet();
139 42
        $this->_allowUnsecured = false;
140 42
    }
141
142
    /**
143
     * Initialize with a single JSON Web Key.
144
     *
145
     * @param JWK        $key         JSON Web Key
146
     * @param null|array $constraints Optional constraints
147
     */
148 3
    public static function fromJWK(JWK $key, ?array $constraints = null): self
149
    {
150 3
        return new self($constraints, new JWKSet($key));
151
    }
152
153
    /**
154
     * Get self with the reference time.
155
     *
156
     * @param null|int $ts Unix timestamp
157
     */
158 23
    public function withReferenceTime(?int $ts): self
159
    {
160 23
        $obj = clone $this;
161 23
        $obj->_refTime = $ts;
162 23
        return $obj;
163
    }
164
165
    /**
166
     * Check whether the reference time is set.
167
     */
168 19
    public function hasReferenceTime(): bool
169
    {
170 19
        return isset($this->_refTime);
171
    }
172
173
    /**
174
     * Get the reference time.
175
     *
176
     * @throws \LogicException
177
     */
178 13
    public function referenceTime(): int
179
    {
180 13
        if (!$this->hasReferenceTime()) {
181 1
            throw new \LogicException('Reference time not set.');
182
        }
183 12
        return $this->_refTime;
184
    }
185
186
    /**
187
     * Get self with the reference time leeway.
188
     */
189 11
    public function withLeeway(int $seconds): self
190
    {
191 11
        $obj = clone $this;
192 11
        $obj->_leeway = $seconds;
193 11
        return $obj;
194
    }
195
196
    /**
197
     * Get the reference time leeway.
198
     */
199 12
    public function leeway(): int
200
    {
201 12
        return $this->_leeway;
202
    }
203
204
    /**
205
     * Get self with a validation constraint.
206
     *
207
     * If the claim does not provide its own validator, an explicit validator
208
     * must be given.
209
     *
210
     * @param string         $name       Claim name
211
     * @param mixed          $constraint Value to check claim against
212
     * @param null|Validator $validator  Optional explicit validator
213
     */
214 18
    public function withConstraint(string $name, $constraint,
215
        ?Validator $validator = null): self
216
    {
217 18
        $obj = clone $this;
218 18
        $obj->_constraints[$name] = $constraint;
219 18
        if ($validator) {
220 2
            $obj->_validators[$name] = $validator;
221
        }
222 18
        return $obj;
223
    }
224
225
    /**
226
     * Get self with the issuer constraint.
227
     *
228
     * @param string $issuer Issuer name
229
     */
230 6
    public function withIssuer(string $issuer): self
231
    {
232 6
        return $this->withConstraint(RegisteredClaim::NAME_ISSUER, $issuer);
233
    }
234
235
    /**
236
     * Get self with the subject constraint.
237
     *
238
     * @param string $subject Subject name
239
     */
240 3
    public function withSubject(string $subject): self
241
    {
242 3
        return $this->withConstraint(RegisteredClaim::NAME_SUBJECT, $subject);
243
    }
244
245
    /**
246
     * Get self with the audience constraint.
247
     *
248
     * @param string $audience Audience name
249
     */
250 3
    public function withAudience(string $audience): self
251
    {
252 3
        return $this->withConstraint(RegisteredClaim::NAME_AUDIENCE, $audience);
253
    }
254
255
    /**
256
     * Get self with the JWT ID constraint.
257
     *
258
     * @param string $id JWT ID
259
     */
260 3
    public function withID(string $id): self
261
    {
262 3
        return $this->withConstraint(RegisteredClaim::NAME_JWT_ID, $id);
263
    }
264
265
    /**
266
     * Check whether a named constraint is present.
267
     *
268
     * @param string $name Claim name
269
     */
270 36
    public function hasConstraint(string $name): bool
271
    {
272 36
        return isset($this->_constraints[$name]);
273
    }
274
275
    /**
276
     * Get a constraint value by the claim name.
277
     *
278
     * @param string $name Claim name
279
     *
280
     * @throws \LogicException If constraint is not set
281
     *
282
     * @return mixed Constraint value
283
     */
284 23
    public function constraint(string $name)
285
    {
286 23
        if (!$this->hasConstraint($name)) {
287 1
            throw new \LogicException("Constraint {$name} not set.");
288
        }
289 22
        return $this->_constraints[$name];
290
    }
291
292
    /**
293
     * Check whether a validator is defined for the given claim name.
294
     *
295
     * @param string $name Claim name
296
     */
297 18
    public function hasValidator(string $name): bool
298
    {
299 18
        return isset($this->_validators[$name]);
300
    }
301
302
    /**
303
     * Get explicitly defined validator by the claim name.
304
     *
305
     * @param string $name Claim name
306
     *
307
     * @throws \LogicException If validator is not set
308
     */
309 3
    public function validator(string $name): Validator
310
    {
311 3
        if (!$this->hasValidator($name)) {
312 1
            throw new \LogicException("Validator {$name} not set.");
313
        }
314 2
        return $this->_validators[$name];
315
    }
316
317
    /**
318
     * Get a set of JSON Web Keys defined in this context.
319
     */
320 8
    public function keys(): JWKSet
321
    {
322 8
        return $this->_keys;
323
    }
324
325
    /**
326
     * Get self with 'allow unsecured' flag set.
327
     *
328
     * If the unsecured JWT's are allowed, claims shall be considered valid even
329
     * though they are not signed nor encrypted.
330
     *
331
     * @param bool $allow Whether to allow unsecured JWT's
332
     */
333 3
    public function withUnsecuredAllowed(bool $allow): self
334
    {
335 3
        $obj = clone $this;
336 3
        $obj->_allowUnsecured = $allow;
337 3
        return $obj;
338
    }
339
340
    /**
341
     * Check whether the unsecured JWT's are allowed.
342
     */
343 3
    public function isUnsecuredAllowed(): bool
344
    {
345 3
        return $this->_allowUnsecured;
346
    }
347
348
    /**
349
     * Get self with only given permitted algorithms.
350
     *
351
     * @param string ...$names Algorithm name
352
     *
353
     * @see \Sop\JWX\JWA\JWA
354
     */
355 1
    public function withPermittedAlgorithms(string ...$names): self
356
    {
357 1
        $obj = clone $this;
358 1
        $obj->_permittedAlgoritms = [];
359 1
        return $obj->withPermittedAlgorithmsAdded(...$names);
360
    }
361
362
    /**
363
     * Get self with given algorithms added to the permitted set.
364
     *
365
     * @param string ...$names Algorithm name
366
     *
367
     * @see \Sop\JWX\JWA\JWA
368
     */
369 2
    public function withPermittedAlgorithmsAdded(string ...$names): self
370
    {
371 2
        $obj = clone $this;
372 2
        foreach ($names as $name) {
373 2
            $obj->_permittedAlgoritms[$name] = true;
374
        }
375 2
        return $obj;
376
    }
377
378
    /**
379
     * Get self with given algorithms removed from the permitted set.
380
     *
381
     * @param string ...$names Algorithm name
382
     *
383
     * @see \Sop\JWX\JWA\JWA
384
     */
385 1
    public function withProhibitedAlgorithms(string ...$names): self
386
    {
387 1
        $obj = clone $this;
388 1
        foreach ($names as $name) {
389 1
            $obj->_permittedAlgoritms[$name] = false;
390
        }
391 1
        return $obj;
392
    }
393
394
    /**
395
     * Check whether given algorithm is permitted.
396
     *
397
     * @param string $name Algorithm name
398
     *
399
     * @see \Sop\JWX\JWA\JWA
400
     */
401 15
    public function isPermittedAlgorithm(string $name): bool
402
    {
403 15
        return isset($this->_permittedAlgoritms[$name])
404 15
            && true === $this->_permittedAlgoritms[$name];
405
    }
406
407
    /**
408
     * Validate claims.
409
     *
410
     * @throws ValidationException If any of the claims is not valid
411
     */
412 27
    public function validate(Claims $claims): self
413
    {
414 27
        $claimset = iterator_to_array($claims);
415
        // validate required constraints
416 27
        foreach (array_keys($this->_constraints) as $name) {
417 15
            if (!isset($claimset[$name])) {
418 1
                throw new ValidationException("Claim '{$name}' is required.");
419
            }
420 14
            if (!$claimset[$name]->validateWithContext($this)) {
421 8
                throw new ValidationException(
422 8
                    "Validation of claim '{$name}' failed.");
423
            }
424 6
            unset($claimset[$name]);
425
        }
426
        // validate remaining claims using default validators
427 18
        foreach ($claimset as $name => $claim) {
428 17
            if (!$claim->validateWithContext($this)) {
429 3
                throw new ValidationException(
430 17
                    "Validation of claim '{$name}' failed.");
431
            }
432
        }
433 15
        return $this;
434
    }
435
}
436