AbstractAssertionType::__construct()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 15
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
eloc 5
c 1
b 0
f 1
dl 0
loc 15
rs 10
cc 1
nc 1
nop 8

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace SimpleSAML\SAML11\XML\saml;
6
7
use DOMElement;
8
use SimpleSAML\SAML11\Assert\Assert;
9
use SimpleSAML\SAML11\Compat\ContainerSingleton;
10
use SimpleSAML\SAML11\Constants as C;
11
use SimpleSAML\SAML11\Exception\VersionMismatchException;
12
use SimpleSAML\SAML11\Type\SAMLDateTimeValue;
13
use SimpleSAML\SAML11\Type\SAMLStringValue;
14
use SimpleSAML\SAML11\Utils\XPath;
15
use SimpleSAML\XMLSchema\Exception\InvalidDOMElementException;
16
use SimpleSAML\XMLSchema\Exception\MissingElementException;
17
use SimpleSAML\XMLSchema\Exception\SchemaViolationException;
18
use SimpleSAML\XMLSchema\Exception\TooManyElementsException;
19
use SimpleSAML\XMLSchema\Type\IDValue;
20
use SimpleSAML\XMLSchema\Type\NonNegativeIntegerValue;
21
use SimpleSAML\XMLSecurity\XML\ds\Signature;
22
use SimpleSAML\XMLSecurity\XML\SignableElementInterface;
23
use SimpleSAML\XMLSecurity\XML\SignableElementTrait;
24
use SimpleSAML\XMLSecurity\XML\SignedElementInterface;
25
use SimpleSAML\XMLSecurity\XML\SignedElementTrait;
26
27
use function array_filter;
28
use function array_merge;
29
use function array_pop;
30
use function array_values;
31
use function strval;
32
33
/**
34
 * SAML Assertion Type abstract data type.
35
 *
36
 * @package simplesamlphp/saml11
37
 */
38
abstract class AbstractAssertionType extends AbstractSamlElement implements
39
    SignableElementInterface,
40
    SignedElementInterface
41
{
42
    use SignableElementTrait;
43
    use SignedElementTrait;
44
45
46
    /**
47
     * The original signed XML
48
     *
49
     * @var \DOMElement
50
     */
51
    protected DOMElement $xml;
52
53
54
    /**
55
     * Initialize a saml:AssertionType from scratch
56
     *
57
     * @param \SimpleSAML\XMLSchema\Type\NonNegativeIntegerValue $majorVersion
58
     * @param \SimpleSAML\XMLSchema\Type\NonNegativeIntegerValue $minorVersion
59
     * @param \SimpleSAML\XMLSchema\Type\IDValue $assertionID
60
     * @param \SimpleSAML\SAML11\Type\SAMLStringValue $issuer
61
     * @param \SimpleSAML\SAML11\Type\SAMLDateTimeValue $issueInstant
62
     * @param \SimpleSAML\SAML11\XML\saml\Conditions|null $conditions
63
     * @param \SimpleSAML\SAML11\XML\saml\Advice|null $advice
64
     * @param array<\SimpleSAML\SAML11\XML\saml\AbstractStatementType> $statements
65
     */
66
    final public function __construct(
67
        protected NonNegativeIntegerValue $majorVersion,
68
        protected NonNegativeIntegerValue $minorVersion,
69
        protected IDValue $assertionID,
70
        protected SAMLStringValue $issuer,
71
        protected SAMLDateTimeValue $issueInstant,
72
        protected ?Conditions $conditions = null,
73
        protected ?Advice $advice = null,
74
        protected array $statements = [],
75
    ) {
76
        Assert::same($majorVersion->getValue(), '1', VersionMismatchException::class);
77
        Assert::same($minorVersion->getValue(), '1', VersionMismatchException::class);
78
        Assert::minCount($statements, 1, MissingElementException::class);
79
        Assert::maxCount($statements, C::UNBOUNDED_LIMIT);
80
        Assert::allIsInstanceOf($statements, AbstractStatementType::class, SchemaViolationException::class);
81
    }
82
83
84
    /**
85
     * Collect the value of the majorVersion-property
86
     *
87
     * @return \SimpleSAML\XMLSchema\Type\NonNegativeIntegerValue
88
     */
89
    public function getMajorVersion(): NonNegativeIntegerValue
90
    {
91
        return $this->majorVersion;
92
    }
93
94
95
    /**
96
     * Collect the value of the minorVersion-property
97
     *
98
     * @return \SimpleSAML\XMLSchema\Type\NonNegativeIntegerValue
99
     */
100
    public function getMinorVersion(): NonNegativeIntegerValue
101
    {
102
        return $this->minorVersion;
103
    }
104
105
106
    /**
107
     * Collect the value of the assertionID-property
108
     *
109
     * Note: the name of this method is not consistent, but it has to be named getId for xml-security to work.
110
     *
111
     * @return \SimpleSAML\XMLSchema\Type\IDValue
112
     */
113
    public function getId(): IDValue
114
    {
115
        return $this->assertionID;
116
    }
117
118
119
    /**
120
     * Collect the value of the issuer-property
121
     *
122
     * @return \SimpleSAML\SAML11\Type\SAMLStringValue
123
     */
124
    public function getIssuer(): SAMLStringValue
125
    {
126
        return $this->issuer;
127
    }
128
129
130
    /**
131
     * Collect the value of the issueInstant-property
132
     *
133
     * @return \SimpleSAML\SAML11\Type\SAMLDateTimeValue
134
     */
135
    public function getIssueInstant(): SAMLDateTimeValue
136
    {
137
        return $this->issueInstant;
138
    }
139
140
141
    /**
142
     * Collect the value of the conditions-property
143
     *
144
     * @return \SimpleSAML\SAML11\XML\saml\Conditions|null
145
     */
146
    public function getConditions(): ?Conditions
147
    {
148
        return $this->conditions;
149
    }
150
151
152
    /**
153
     * Collect the value of the advice-property
154
     *
155
     * @return \SimpleSAML\SAML11\XML\saml\Advice|null
156
     */
157
    public function getAdvice(): ?Advice
158
    {
159
        return $this->advice;
160
    }
161
162
163
    /**
164
     * Collect the value of the statements-property
165
     *
166
     * @return array<\SimpleSAML\SAML11\XML\saml\AbstractStatementType>
167
     */
168
    public function getAllStatements(): array
169
    {
170
        return $this->statements;
171
    }
172
173
174
    /**
175
     * @return \SimpleSAML\SAML11\XML\saml\AbstractStatement[]
176
     */
177
    public function getStatements(): array
178
    {
179
        return array_values(array_filter($this->statements, function ($statement) {
180
            return $statement instanceof AbstractStatement;
181
        }));
182
    }
183
184
185
    /**
186
     * @return \SimpleSAML\SAML11\XML\saml\AbstractSubjectStatement[]
187
     */
188
    public function getSubjectStatements(): array
189
    {
190
        return array_values(array_filter($this->statements, function ($statement) {
191
            return $statement instanceof AbstractSubjectStatement;
192
        }));
193
    }
194
195
196
    /**
197
     * @return \SimpleSAML\SAML11\XML\saml\AuthenticationStatement[]
198
     */
199
    public function getAuthenticationStatements(): array
200
    {
201
        return array_values(array_filter($this->statements, function ($statement) {
202
            return $statement instanceof AuthenticationStatement;
203
        }));
204
    }
205
206
207
    /**
208
     * @return \SimpleSAML\SAML11\XML\saml\AuthorizationDecisionStatement[]
209
     */
210
    public function getAuthorizationDecisionStatements(): array
211
    {
212
        return array_values(array_filter($this->statements, function ($statement) {
213
            return $statement instanceof AuthorizationDecisionStatement;
214
        }));
215
    }
216
217
218
    /**
219
     * @return \SimpleSAML\SAML11\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
     * Set the XML element.
231
     *
232
     * @param \DOMElement $xml
233
     */
234
    private function setOriginalXML(DOMElement $xml): void
235
    {
236
        $this->xml = $xml;
237
    }
238
239
240
    /**
241
     * @return \DOMElement
242
     */
243
    protected function getOriginalXML(): DOMElement
244
    {
245
        return $this->xml ?? $this->toUnsignedXML();
246
    }
247
248
249
    public function getBlacklistedAlgorithms(): ?array
250
    {
251
        $container = ContainerSingleton::getInstance();
252
        return $container->getBlacklistedEncryptionAlgorithms();
253
    }
254
255
256
    /**
257
     * Convert XML into an AssertionType
258
     *
259
     * @param \DOMElement $xml The XML element we should load
260
     * @return static
261
     *
262
     * @throws \SimpleSAML\XML\Exception\InvalidDOMElementException
263
     *   if the qualified name of the supplied element is wrong
264
     */
265
    public static function fromXML(DOMElement $xml): static
266
    {
267
        Assert::same($xml->localName, static::getLocalName(), InvalidDOMElementException::class);
268
        Assert::same($xml->namespaceURI, static::NS, InvalidDOMElementException::class);
269
270
        $conditions = Conditions::getChildrenOfClass($xml);
271
        Assert::maxCount(
272
            $conditions,
273
            1,
274
            'More than one <saml:Conditions> in <saml:Assertion>.',
275
            TooManyElementsException::class,
276
        );
277
278
        $advice = Advice::getChildrenOfClass($xml);
279
        Assert::maxCount(
280
            $advice,
281
            1,
282
            'More than one <saml:Advice> in <saml:Assertion>.',
283
            TooManyElementsException::class,
284
        );
285
286
        $statements = AbstractStatement::getChildrenOfClass($xml);
287
        $subjectStatement = AbstractSubjectStatement::getChildrenOfClass($xml);
288
        $authnStatement = AuthenticationStatement::getChildrenOfClass($xml);
289
        $authzDecisionStatement = AuthorizationDecisionStatement::getChildrenOfClass($xml);
290
        $attrStatement = AttributeStatement::getChildrenOfClass($xml);
291
292
        $signature = Signature::getChildrenOfClass($xml);
293
        Assert::maxCount($signature, 1, 'Only one <ds:Signature> element is allowed.', TooManyElementsException::class);
294
295
        $assertion = new static(
296
            self::getAttribute($xml, 'MajorVersion', NonNegativeIntegerValue::class),
297
            self::getAttribute($xml, 'MinorVersion', NonNegativeIntegerValue::class),
298
            self::getAttribute($xml, 'AssertionID', IDValue::class),
299
            self::getAttribute($xml, 'Issuer', SAMLStringValue::class),
300
            self::getAttribute($xml, 'IssueInstant', SAMLDateTimeValue::class),
301
            array_pop($conditions),
302
            array_pop($advice),
303
            array_merge($statements, $subjectStatement, $authnStatement, $authzDecisionStatement, $attrStatement),
304
        );
305
306
        if (!empty($signature)) {
307
            $assertion->setSignature($signature[0]);
308
            $assertion->setOriginalXML($xml);
309
        }
310
311
        return $assertion;
312
    }
313
314
315
    /**
316
     * Convert this assertion to an unsigned XML document.
317
     * This method does not sign the resulting XML document.
318
     *
319
     * @return \DOMElement The root element of the DOM tree
320
     */
321
    protected function toUnsignedXML(?DOMElement $parent = null): DOMElement
322
    {
323
        $e = $this->instantiateParentElement($parent);
324
325
        $e->setAttribute('MajorVersion', strval($this->getMajorVersion()));
326
        $e->setAttribute('MinorVersion', strval($this->getMinorVersion()));
327
        $e->setAttribute('AssertionID', strval($this->getId()));
328
        $e->setAttribute('Issuer', strval($this->getIssuer()));
329
        $e->setAttribute('IssueInstant', strval($this->getIssueInstant()));
330
331
        $this->getConditions()?->toXML($e);
332
        $this->getAdvice()?->toXML($e);
333
334
        foreach ($this->getAllStatements() as $statement) {
335
            $statement->toXML($e);
336
        }
337
338
        return $e;
339
    }
340
341
342
    /**
343
     * Convert this assertion to a signed XML element, if a signer was set.
344
     *
345
     * @param \DOMElement|null $parent The DOM node the assertion should be created in.
346
     *
347
     * @return \DOMElement This assertion.
348
     * @throws \Exception
349
     */
350
    public function toXML(?DOMElement $parent = null): DOMElement
351
    {
352
        if ($this->isSigned() === true && $this->signer === null) {
353
            // We already have a signed document and no signer was set to re-sign it
354
            if ($parent === null) {
355
                return $this->getOriginalXML();
356
            }
357
358
            $node = $parent->ownerDocument?->importNode($this->getOriginalXML(), true);
359
            $parent->appendChild($node);
360
            return $parent;
361
        }
362
363
        $e = $this->toUnsignedXML($parent);
364
365
        if ($this->signer !== null) {
366
            $signedXML = $this->doSign($e);
367
368
            // Test for last element, if any
369
            $assertionElements = XPath::xpQuery(
370
                $signedXML,
371
                './saml_assertion/following-sibling::*[position() = last()]',
372
                XPath::getXPath($signedXML),
373
            );
374
            $last = array_pop($assertionElements);
375
376
            if ($last !== null) {
377
                $signedXML->insertBefore($this->signature?->toXML($signedXML), $last->nextSibling);
378
            } else {
379
                $signedXML->appendChild($this->signature?->toXML($signedXML));
380
            }
381
382
            return $signedXML;
383
        }
384
385
        return $e;
386
    }
387
}
388