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