Passed
Push — master ( 33b650...296d9c )
by Tim
02:23
created

Assertion::getEncryptionBackend()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 5
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace SimpleSAML\SAML2\XML\saml;
6
7
use DOMElement;
8
use Exception;
9
use SimpleSAML\Assert\Assert;
10
use SimpleSAML\SAML2\Constants;
11
use SimpleSAML\SAML2\Exception\ProtocolViolationException;
12
use SimpleSAML\SAML2\Utilities\Temporal;
13
use SimpleSAML\SAML2\Utils;
14
use SimpleSAML\SAML2\Utils\XPath;
15
use SimpleSAML\XML\Chunk;
16
use SimpleSAML\XML\DOMDocumentFactory;
17
use SimpleSAML\XML\Exception\InvalidDOMElementException;
18
use SimpleSAML\XML\Exception\MissingElementException;
19
use SimpleSAML\XML\Exception\TooManyElementsException;
20
use SimpleSAML\XML\Utils as XMLUtils;
21
use SimpleSAML\XMLSecurity\Utils\Security as SecurityUtils;
22
use SimpleSAML\XMLSecurity\XML\ds\Signature;
23
use SimpleSAML\XMLSecurity\XML\EncryptableElementInterface;
24
use SimpleSAML\XMLSecurity\XML\EncryptableElementTrait;
25
use SimpleSAML\XMLSecurity\XML\SignableElementInterface;
26
use SimpleSAML\XMLSecurity\XML\SignableElementTrait;
27
use SimpleSAML\XMLSecurity\XML\SignedElementInterface;
28
use SimpleSAML\XMLSecurity\XML\SignedElementTrait;
29
use SimpleSAML\XMLSecurity\XMLSecEnc;
0 ignored issues
show
Bug introduced by
The type SimpleSAML\XMLSecurity\XMLSecEnc was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
30
use SimpleSAML\XMLSecurity\XMLSecurityKey;
31
32
use function array_filter;
33
use function array_merge;
34
use function array_pop;
35
use function array_values;
36
37
/**
38
 * Class representing a SAML 2 assertion.
39
 *
40
 * @package simplesamlphp/saml2
41
 */
42
class Assertion extends AbstractSamlElement implements
43
    EncryptableElementInterface,
44
    SignableElementInterface,
45
    SignedElementInterface
46
{
47
    use EncryptableElementTrait;
48
    use SignableElementTrait;
49
    use SignedElementTrait;
1 ignored issue
show
introduced by
The trait SimpleSAML\XMLSecurity\XML\SignedElementTrait requires some properties which are not provided by SimpleSAML\SAML2\XML\saml\Assertion: $ownerDocument, $documentElement
Loading history...
50
51
    /**
52
     * The identifier of this assertion.
53
     *
54
     * @var string
55
     */
56
    protected string $id;
57
58
    /**
59
     * The issue timestamp of this assertion, as an UNIX timestamp.
60
     *
61
     * @var int
62
     */
63
    protected int $issueInstant;
64
65
    /**
66
     * The issuer of this assertion.
67
     *
68
     * If the issuer's format is \SAML2\Constants::NAMEID_ENTITY, this property will just take the issuer's string
69
     * value.
70
     *
71
     * @var \SimpleSAML\SAML2\XML\saml\Issuer
72
     */
73
    protected Issuer $issuer;
74
75
    /**
76
     * The subject of this assertion
77
     *
78
     * @var \SimpleSAML\SAML2\XML\saml\Subject|null
79
     */
80
    protected ?Subject $subject;
81
82
    /**
83
     * The subject of this assertion
84
     *
85
     * If the NameId is null, no subject was included in the assertion.
86
     *
87
     * @var \SimpleSAML\SAML2\XML\saml\NameID|null
88
     */
89
    protected ?NameID $nameId = null;
90
91
    /**
92
     * The encrypted NameId of the subject.
93
     *
94
     * If this is not null, the NameId needs decryption before it can be accessed.
95
     *
96
     * @var \DOMElement|null
97
     */
98
    protected ?DOMElement $encryptedNameId = null;
99
100
    /**
101
     * The statements made by this assertion.
102
     *
103
     * @var \SimpleSAML\SAML2\XML\saml\AbstractStatement[]
104
     */
105
    protected array $statements = [];
106
107
    /**
108
     * The attributes, as an associative array, indexed by attribute name
109
     *
110
     * To ease handling, all attribute values are represented as an array of values, also for values with a multiplicity
111
     * of single. There are 5 possible variants of datatypes for the values: a string, an integer, an array, a
112
     * DOMNodeList or a \SimpleSAML\SAML2\XML\saml\NameID object.
113
     *
114
     * If the attribute is an eduPersonTargetedID, the values will be SAML2\XML\saml\NameID objects.
115
     * If the attribute value has an type-definition (xsi:string or xsi:int), the values will be of that type.
116
     * If the attribute value contains a nested XML structure, the values will be a DOMNodeList
117
     * In all other cases the values are treated as strings
118
     *
119
     * **WARNING** a DOMNodeList cannot be serialized without data-loss and should be handled explicitly
120
     *
121
     * @var array multi-dimensional array of \DOMNodeList|\SimpleSAML\SAML2\XML\saml\NameID|string|int|array
122
     */
123
    protected array $attributes = [];
124
125
    /**
126
     * The SubjectConfirmation elements of the Subject in the assertion.
127
     *
128
     * @var \SimpleSAML\SAML2\XML\saml\SubjectConfirmation[]
129
     */
130
    protected array $SubjectConfirmation = [];
131
132
    /**
133
     * @var bool
134
     */
135
    protected bool $wasSignedAtConstruction = false;
136
137
    /**
138
     * @var \SimpleSAML\SAML2\XML\saml\Conditions|null
139
     */
140
    protected $conditions;
141
142
143
    /**
144
     * Assertion constructor.
145
     *
146
     * @param \SimpleSAML\SAML2\XML\saml\Issuer $issuer
147
     * @param string|null $id
148
     * @param int|null $issueInstant
149
     * @param \SimpleSAML\SAML2\XML\saml\Subject|null $subject
150
     * @param \SimpleSAML\SAML2\XML\saml\Conditions|null $conditions
151
     * @param \SimpleSAML\SAML2\XML\saml\AbstractStatement[] $statements
152
     */
153
    public function __construct(
154
        Issuer $issuer,
155
        ?string $id = null,
156
        ?int $issueInstant = null,
157
        ?Subject $subject = null,
158
        ?Conditions $conditions = null,
159
        array $statements = []
160
    ) {
161
        Assert::true(
162
            $subject || !empty($statements),
163
            "Either a <saml:Subject> or some statement must be present in a <saml:Assertion>"
164
        );
165
        $this->setIssuer($issuer);
166
        $this->setId($id);
167
        $this->setIssueInstant($issueInstant);
168
        $this->setSubject($subject);
169
        $this->setConditions($conditions);
170
        $this->setStatements($statements);
171
    }
172
173
174
    /**
175
     * Collect the value of the subject
176
     *
177
     * @return \SimpleSAML\SAML2\XML\saml\Subject|null
178
     */
179
    public function getSubject(): ?Subject
180
    {
181
        return $this->subject;
182
    }
183
184
185
    /**
186
     * Set the value of the subject-property
187
     *
188
     * @param \SimpleSAML\SAML2\XML\saml\Subject|null $subject
189
     */
190
    protected function setSubject(?Subject $subject): void
191
    {
192
        $this->subject = $subject;
193
    }
194
195
196
    /**
197
     * Collect the value of the conditions-property
198
     *
199
     * @return \SimpleSAML\SAML2\XML\saml\Conditions|null
200
     */
201
    public function getConditions(): ?Conditions
202
    {
203
        return $this->conditions;
204
    }
205
206
207
    /**
208
     * Set the value of the conditions-property
209
     *
210
     * @param \SimpleSAML\SAML2\XML\saml\Conditions|null $conditions
211
     */
212
    protected function setConditions(?Conditions $conditions): void
213
    {
214
        $this->conditions = $conditions;
215
    }
216
217
218
    /**
219
     * @return \SimpleSAML\SAML2\XML\saml\AttributeStatement[]
220
     */
221
    public function getAttributeStatements(): array
222
    {
223
        return array_values(array_filter($this->statements, function ($statement) {
224
            return $statement instanceof AttributeStatement;
225
        }));
226
    }
227
228
229
    /**
230
     * @return \SimpleSAML\SAML2\XML\saml\AuthnStatement[]
231
     */
232
    public function getAuthnStatements(): array
233
    {
234
        return array_values(array_filter($this->statements, function ($statement) {
235
            return $statement instanceof AuthnStatement;
236
        }));
237
    }
238
239
240
    /**
241
     * @return \SimpleSAML\SAML2\XML\saml\Statement[]
242
     */
243
    public function getStatements(): array
244
    {
245
        return array_values(array_filter($this->statements, function ($statement) {
246
            return $statement instanceof Statement;
247
        }));
248
    }
249
250
251
    /**
252
     * Set the statements in this assertion
253
     *
254
     * @param \SimpleSAML\SAML2\XML\saml\AbstractStatement[] $statements
255
     */
256
    protected function setStatements(array $statements): void
257
    {
258
        Assert::allIsInstanceOf($statements, AbstractStatement::class);
259
260
        $this->statements = $statements;
261
    }
262
263
264
    /**
265
     * Retrieve the identifier of this assertion.
266
     *
267
     * @return string The identifier of this assertion.
268
     */
269
    public function getId(): string
270
    {
271
        return $this->id;
272
    }
273
274
275
    /**
276
     * Set the identifier of this assertion.
277
     *
278
     * @param string|null $id The new identifier of this assertion.
279
     */
280
    public function setId(?string $id): void
281
    {
282
        Assert::nullOrNotWhitespaceOnly($id);
283
284
        if ($id === null) {
285
            $id = Utils::getContainer()->generateId();
286
        }
287
        $this->id = $id;
288
    }
289
290
291
    /**
292
     * Retrieve the issue timestamp of this assertion.
293
     *
294
     * @return int The issue timestamp of this assertion, as an UNIX timestamp.
295
     */
296
    public function getIssueInstant(): int
297
    {
298
        return $this->issueInstant;
299
    }
300
301
302
    /**
303
     * Set the issue timestamp of this assertion.
304
     *
305
     * @param int|null $issueInstant The new issue timestamp of this assertion, as an UNIX timestamp.
306
     */
307
    public function setIssueInstant(?int $issueInstant): void
308
    {
309
        if ($issueInstant === null) {
310
            $issueInstant = Temporal::getTime();
311
        }
312
313
        $this->issueInstant = $issueInstant;
314
    }
315
316
317
    /**
318
     * Retrieve the issuer if this assertion.
319
     *
320
     * @return \SimpleSAML\SAML2\XML\saml\Issuer The issuer of this assertion.
321
     */
322
    public function getIssuer(): Issuer
323
    {
324
        return $this->issuer;
325
    }
326
327
328
    /**
329
     * Set the issuer of this message.
330
     *
331
     * @param \SimpleSAML\SAML2\XML\saml\Issuer $issuer The new issuer of this assertion.
332
     */
333
    public function setIssuer(Issuer $issuer): void
334
    {
335
        $this->issuer = $issuer;
336
    }
337
338
339
    /**
340
     * Retrieve the SubjectConfirmation elements we have in our Subject element.
341
     *
342
     * @return array Array of \SimpleSAML\SAML2\XML\saml\SubjectConfirmation elements.
343
     */
344
    public function getSubjectConfirmation(): array
345
    {
346
        return $this->SubjectConfirmation;
347
    }
348
349
350
    /**
351
     * Set the SubjectConfirmation elements that should be included in the assertion.
352
     *
353
     * @param array $SubjectConfirmation Array of \SimpleSAML\SAML2\XML\saml\SubjectConfirmation elements.
354
     * @throws \SimpleSAML\Assert\AssertionFailedException if assertions are false
355
     */
356
    public function setSubjectConfirmation(array $SubjectConfirmation): void
357
    {
358
        Assert::allIsInstanceOf($SubjectConfirmation, SubjectConfirmation::class);
359
        $this->SubjectConfirmation = $SubjectConfirmation;
360
    }
361
362
363
    /**
364
     * @return bool
365
     */
366
    public function wasSignedAtConstruction(): bool
367
    {
368
        return $this->wasSignedAtConstruction;
369
    }
370
371
372
    /**
373
     * Get the XML element.
374
     *
375
     * @return \DOMElement
376
     */
377
    public function getXML(): DOMElement
378
    {
379
        return $this->xml;
380
    }
381
382
383
    /**
384
     * Set the XML element.
385
     *
386
     * @param \DOMElement $xml
387
     */
388
    private function setXML(DOMElement $xml): void
0 ignored issues
show
Unused Code introduced by
The method setXML() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
389
    {
390
        $this->xml = $xml;
0 ignored issues
show
Bug Best Practice introduced by
The property xml does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
391
    }
392
393
394
    /**
395
     * @inheritDoc
396
     */
397
    protected function getOriginalXML(): DOMElement
398
    {
399
        return $this->xml ?? $this->toUnsignedXML();
400
    }
401
402
403
    public function getBlacklistedAlgorithms(): ?array
404
    {
405
        $container = ContainerSingleton::getInstance();
0 ignored issues
show
Bug introduced by
The type SimpleSAML\SAML2\XML\saml\ContainerSingleton was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
406
        return $container->getBlacklistedEncryptionAlgorithms();
407
    }
408
409
410
    public function getEncryptionBackend(): ?EncryptionBackend
0 ignored issues
show
Bug introduced by
The type SimpleSAML\SAML2\XML\saml\EncryptionBackend was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
411
    {
412
        // return the encryption backend you want to use,
413
        // or null if you are fine with the default
414
        return null;
415
    }
416
417
418
    /**
419
     * Convert XML into an Assertion
420
     *
421
     * @param \DOMElement $xml The XML element we should load
422
     * @return \SimpleSAML\SAML2\XML\saml\Assertion
423
     *
424
     * @throws \SimpleSAML\Assert\AssertionFailedException if assertions are false
425
     * @throws \SimpleSAML\XML\Exception\InvalidDOMElementException
426
     *   if the qualified name of the supplied element is wrong
427
     * @throws \SimpleSAML\XML\Exception\MissingAttributeException
428
     *   if the supplied element is missing one of the mandatory attributes
429
     * @throws \SimpleSAML\XML\Exception\MissingElementException if one of the mandatory child-elements is missing
430
     * @throws \SimpleSAML\XML\Exception\TooManyElementsException if too many child-elements of a type are specified
431
     * @throws \Exception
432
     */
433
    public static function fromXML(DOMElement $xml): object
434
    {
435
        Assert::same($xml->localName, 'Assertion', InvalidDOMElementException::class);
436
        Assert::same($xml->namespaceURI, Assertion::NS, InvalidDOMElementException::class);
437
        Assert::same(self::getAttribute($xml, 'Version'), '2.0', 'Unsupported version: %s');
438
439
        $issueInstant = self::getAttribute($xml, 'IssueInstant');
440
        Assert::validDateTimeZulu($issueInstant, ProtocolViolationException::class);
441
        $issueInstant = XMLUtils::xsDateTimeToTimestamp($issueInstant);
442
443
        $issuer = Issuer::getChildrenOfClass($xml);
444
        Assert::minCount($issuer, 1, 'Missing <saml:Issuer> in assertion.', MissingElementException::class);
445
        Assert::maxCount($issuer, 1, 'More than one <saml:Issuer> in assertion.', TooManyElementsException::class);
446
447
        $subject = Subject::getChildrenOfClass($xml);
448
        Assert::maxCount(
449
            $subject,
450
            1,
451
            'More than one <saml:Subject> in <saml:Assertion>',
452
            TooManyElementsException::class,
453
        );
454
455
        $conditions = Conditions::getChildrenOfClass($xml);
456
        Assert::maxCount(
457
            $conditions,
458
            1,
459
            'More than one <saml:Conditions> in <saml:Assertion>.',
460
            TooManyElementsException::class,
461
        );
462
463
        $signature = Signature::getChildrenOfClass($xml);
464
        Assert::maxCount($signature, 1, 'Only one <ds:Signature> element is allowed.', TooManyElementsException::class);
465
466
        $authnStatement = AuthnStatement::getChildrenOfClass($xml);
467
        $attrStatement = AttributeStatement::getChildrenOfClass($xml);
468
        $statements = Statement::getChildrenOfClass($xml);
469
470
        $assertion = new self(
471
            array_pop($issuer),
472
            self::getAttribute($xml, 'ID'),
473
            $issueInstant,
474
            array_pop($subject),
475
            array_pop($conditions),
476
            array_merge($authnStatement, $attrStatement, $statements)
477
        );
478
479
        if (!empty($signature)) {
480
            $assertion->setSignature($signature[0]);
481
            $assertion->wasSignedAtConstruction = true;
482
        }
483
484
        return $assertion;
485
    }
486
487
488
    /**
489
     * Convert this assertion to an unsigned XML document.
490
     * This method does not sign the resulting XML document.
491
     *
492
     * @return \DOMElement The root element of the DOM tree
493
     */
494
    protected function toUnsignedXML(?DOMElement $parent = null): DOMElement
495
    {
496
        $e = $this->instantiateParentElement($parent);
497
498
        $e->setAttribute('Version', '2.0');
499
        $e->setAttribute('ID', $this->id);
500
        $e->setAttribute('IssueInstant', gmdate('Y-m-d\TH:i:s\Z', $this->issueInstant));
501
502
        $issuer = $this->issuer->toXML($e);
0 ignored issues
show
Unused Code introduced by
The assignment to $issuer is dead and can be removed.
Loading history...
503
504
        if ($this->subject !== null) {
505
            $this->subject->toXML($e);
506
        }
507
508
        if ($this->conditions !== null) {
509
            $this->conditions->toXML($e);
510
        }
511
512
        foreach ($this->statements as $statement) {
513
            $statement->toXML($e);
514
        }
515
516
        return $e;
517
    }
518
519
520
    /**
521
     * Convert this assertion to a signed XML element, if a signer was set.
522
     *
523
     * @param \DOMElement|null $parent The DOM node the assertion should be created in.
524
     *
525
     * @return \DOMElement This assertion.
526
     * @throws \Exception
527
     */
528
    public function toXML(DOMElement $parent = null): DOMElement
529
    {
530
        $e = $this->toUnsignedXML($parent);
531
532
        if ($this->signer !== null) {
533
            $signedXML = $this->doSign($e);
534
535
            // Test for an Issuer
536
            $messageElements = XPath::xpQuery($signedXML, './saml_assertion:Issuer', XPath::getXPath($signedXML));
537
            $issuer = array_pop($messageElements);
538
539
            $signedXML->insertBefore($this->signature->toXML($signedXML), $issuer->nextSibling);
540
            return $signedXML;
541
        }
542
543
        return $e;
544
    }
545
}
546