AuthnRequest::__construct()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 42
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 18
nc 1
nop 19
dl 0
loc 42
rs 9.6666
c 0
b 0
f 0

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\SAML2\XML\samlp;
6
7
use DateTimeImmutable;
8
use DOMElement;
9
use SimpleSAML\SAML2\Assert\Assert;
10
use SimpleSAML\SAML2\Exception\Protocol\RequestVersionTooHighException;
11
use SimpleSAML\SAML2\Exception\Protocol\RequestVersionTooLowException;
12
use SimpleSAML\SAML2\Exception\ProtocolViolationException;
13
use SimpleSAML\SAML2\XML\saml\Conditions;
14
use SimpleSAML\SAML2\XML\saml\Issuer;
15
use SimpleSAML\SAML2\XML\saml\Subject;
16
use SimpleSAML\XML\Exception\InvalidDOMElementException;
17
use SimpleSAML\XML\Exception\TooManyElementsException;
18
use SimpleSAML\XML\SchemaValidatableElementInterface;
19
use SimpleSAML\XML\SchemaValidatableElementTrait;
20
use SimpleSAML\XMLSecurity\XML\ds\Signature;
21
22
use function array_pop;
23
use function strval;
24
25
/**
26
 * Class for SAML 2 authentication request messages.
27
 *
28
 * @package simplesamlphp/saml2
29
 */
30
class AuthnRequest extends AbstractRequest implements SchemaValidatableElementInterface
31
{
32
    use SchemaValidatableElementTrait;
33
34
35
    /**
36
     * Constructor for SAML 2 AuthnRequest
37
     *
38
     * @param \DateTimeImmutable $issueInstant
39
     * @param \SimpleSAML\SAML2\XML\samlp\RequestedAuthnContext|null $requestedAuthnContext
40
     * @param \SimpleSAML\SAML2\XML\saml\Subject|null $subject
41
     * @param \SimpleSAML\SAML2\XML\samlp\NameIDPolicy|null $nameIdPolicy
42
     * @param \SimpleSAML\SAML2\XML\saml\Conditions|null $conditions
43
     * @param bool|null $forceAuthn
44
     * @param bool|null $isPassive
45
     * @param string|null $assertionConsumerServiceURL
46
     * @param int|null $assertionConsumerServiceIndex
47
     * @param string|null $protocolBinding
48
     * @param int|null $attributeConsumingServiceIndex
49
     * @param string|null $providerName
50
     * @param \SimpleSAML\SAML2\XML\saml\Issuer|null $issuer
51
     * @param string|null $id
52
     * @param string $version
53
     * @param string|null $destination
54
     * @param string|null $consent
55
     * @param \SimpleSAML\SAML2\XML\samlp\Extensions|null $extensions
56
     * @param \SimpleSAML\SAML2\XML\samlp\Scoping|null $scoping
57
     * @throws \Exception
58
     */
59
    final public function __construct(
60
        DateTimeImmutable $issueInstant,
61
        protected ?RequestedAuthnContext $requestedAuthnContext = null,
62
        protected ?Subject $subject = null,
63
        protected ?NameIDPolicy $nameIdPolicy = null,
64
        protected ?Conditions $conditions = null,
65
        protected ?bool $forceAuthn = null,
66
        protected ?bool $isPassive = null,
67
        protected ?string $assertionConsumerServiceURL = null,
68
        protected ?int $assertionConsumerServiceIndex = null,
69
        protected ?string $protocolBinding = null,
70
        protected ?int $attributeConsumingServiceIndex = null,
71
        protected ?string $providerName = null,
72
        ?Issuer $issuer = null,
73
        ?string $id = null,
74
        string $version = '2.0',
75
        ?string $destination = null,
76
        ?string $consent = null,
77
        ?Extensions $extensions = null,
78
        protected ?Scoping $scoping = null,
79
    ) {
80
        Assert::nullOrNotWhitespaceOnly($providerName);
81
        Assert::oneOf(
82
            null,
83
            [$assertionConsumerServiceURL, $assertionConsumerServiceIndex],
84
            'The AssertionConsumerServiceURL and AssertionConsumerServiceIndex are mutually exclusive;'
85
            . ' please specify one or the other.',
86
            ProtocolViolationException::class,
87
        );
88
        Assert::oneOf(
89
            null,
90
            [$protocolBinding, $assertionConsumerServiceIndex],
91
            'The ProtocolBinding and AssertionConsumerServiceIndex are mutually exclusive;'
92
            . ' please specify one or the other.',
93
            ProtocolViolationException::class,
94
        );
95
        Assert::nullOrValidURL($assertionConsumerServiceURL);
96
        Assert::nullOrValidURI($protocolBinding);
97
        Assert::nullOrRange($attributeConsumingServiceIndex, 0, 65535);
98
        Assert::nullOrRange($assertionConsumerServiceIndex, 0, 65535);
99
100
        parent::__construct($issuer, $id, $version, $issueInstant, $destination, $consent, $extensions);
101
    }
102
103
104
    /**
105
     * @return \SimpleSAML\SAML2\XML\saml\Subject|null
106
     */
107
    public function getSubject(): ?Subject
108
    {
109
        return $this->subject;
110
    }
111
112
113
    /**
114
     * @return \SimpleSAML\SAML2\XML\samlp\Scoping|null
115
     */
116
    public function getScoping(): ?Scoping
117
    {
118
        return $this->scoping;
119
    }
120
121
122
    /**
123
     * @return \SimpleSAML\SAML2\XML\saml\Conditions|null
124
     */
125
    public function getConditions(): ?Conditions
126
    {
127
        return $this->conditions;
128
    }
129
130
131
    /**
132
     * Retrieve the NameIdPolicy.
133
     *
134
     * @see \SimpleSAML\SAML2\AuthnRequest::setNameIdPolicy()
135
     * @return \SimpleSAML\SAML2\XML\samlp\NameIDPolicy|null The NameIdPolicy.
136
     */
137
    public function getNameIdPolicy(): ?NameIDPolicy
138
    {
139
        return $this->nameIdPolicy;
140
    }
141
142
143
    /**
144
     * Retrieve the value of the ForceAuthn attribute.
145
     *
146
     * @return bool|null The ForceAuthn attribute.
147
     */
148
    public function getForceAuthn(): ?bool
149
    {
150
        return $this->forceAuthn;
151
    }
152
153
154
    /**
155
     * Retrieve the value of the ProviderName attribute.
156
     *
157
     * @return string|null The ProviderName attribute.
158
     */
159
    public function getProviderName(): ?string
160
    {
161
        return $this->providerName;
162
    }
163
164
165
    /**
166
     * Retrieve the value of the IsPassive attribute.
167
     *
168
     * @return bool|null The IsPassive attribute.
169
     */
170
    public function getIsPassive(): ?bool
171
    {
172
        return $this->isPassive;
173
    }
174
175
176
    /**
177
     * Retrieve the value of the AssertionConsumerServiceURL attribute.
178
     *
179
     * @return string|null The AssertionConsumerServiceURL attribute.
180
     */
181
    public function getAssertionConsumerServiceURL(): ?string
182
    {
183
        return $this->assertionConsumerServiceURL;
184
    }
185
186
187
    /**
188
     * Retrieve the value of the ProtocolBinding attribute.
189
     *
190
     * @return string|null The ProtocolBinding attribute.
191
     */
192
    public function getProtocolBinding(): ?string
193
    {
194
        return $this->protocolBinding;
195
    }
196
197
198
    /**
199
     * Retrieve the value of the AttributeConsumingServiceIndex attribute.
200
     *
201
     * @return int|null The AttributeConsumingServiceIndex attribute.
202
     */
203
    public function getAttributeConsumingServiceIndex(): ?int
204
    {
205
        return $this->attributeConsumingServiceIndex;
206
    }
207
208
209
    /**
210
     * Retrieve the value of the AssertionConsumerServiceIndex attribute.
211
     *
212
     * @return int|null The AssertionConsumerServiceIndex attribute.
213
     */
214
    public function getAssertionConsumerServiceIndex(): ?int
215
    {
216
        return $this->assertionConsumerServiceIndex;
217
    }
218
219
220
    /**
221
     * Retrieve the RequestedAuthnContext.
222
     *
223
     * @return \SimpleSAML\SAML2\XML\samlp\RequestedAuthnContext|null The RequestedAuthnContext.
224
     */
225
    public function getRequestedAuthnContext(): ?RequestedAuthnContext
226
    {
227
        return $this->requestedAuthnContext;
228
    }
229
230
231
    /**
232
     * Convert XML into an AuthnRequest
233
     *
234
     * @param \DOMElement $xml The XML element we should load
235
     * @return static
236
     *
237
     * @throws \SimpleSAML\XML\Exception\InvalidDOMElementException
238
     *   if the qualified name of the supplied element is wrong
239
     * @throws \SimpleSAML\XML\Exception\MissingAttributeException
240
     *   if the supplied element is missing one of the mandatory attributes
241
     * @throws \SimpleSAML\XML\Exception\TooManyElementsException
242
     *   if too many child-elements of a type are specified
243
     */
244
    public static function fromXML(DOMElement $xml): static
245
    {
246
        Assert::same($xml->localName, 'AuthnRequest', InvalidDOMElementException::class);
247
        Assert::same($xml->namespaceURI, AuthnRequest::NS, InvalidDOMElementException::class);
248
249
        $version = self::getAttribute($xml, 'Version');
250
        Assert::true(version_compare('2.0', $version, '<='), RequestVersionTooLowException::class);
251
        Assert::true(version_compare('2.0', $version, '>='), RequestVersionTooHighException::class);
252
253
        $id = self::getAttribute($xml, 'ID');
254
        Assert::validNCName($id); // Covers the empty string
255
256
        $issueInstant = self::getAttribute($xml, 'IssueInstant');
257
        // Strip sub-seconds - See paragraph 1.3.3 of SAML core specifications
258
        $issueInstant = preg_replace('/([.][0-9]+Z)$/', 'Z', $issueInstant, 1);
259
260
        Assert::validDateTime($issueInstant, ProtocolViolationException::class);
261
        $issueInstant = new DateTimeImmutable($issueInstant);
262
263
        $attributeConsumingServiceIndex = self::getOptionalIntegerAttribute(
264
            $xml,
265
            'AttributeConsumingServiceIndex',
266
            null,
267
        );
268
        $assertionConsumerServiceIndex = self::getOptionalIntegerAttribute(
269
            $xml,
270
            'AssertionConsumerServiceIndex',
271
            null,
272
        );
273
274
        $conditions = Conditions::getChildrenOfClass($xml);
275
        Assert::maxCount(
276
            $conditions,
277
            1,
278
            'Only one <saml:Conditions> element is allowed.',
279
            TooManyElementsException::class,
280
        );
281
282
        $nameIdPolicy = NameIDPolicy::getChildrenOfClass($xml);
283
        Assert::maxCount(
284
            $nameIdPolicy,
285
            1,
286
            'Only one <samlp:NameIDPolicy> element is allowed.',
287
            TooManyElementsException::class,
288
        );
289
290
        $subject = Subject::getChildrenOfClass($xml);
291
        Assert::maxCount($subject, 1, 'Only one <saml:Subject> element is allowed.', TooManyElementsException::class);
292
293
        $issuer = Issuer::getChildrenOfClass($xml);
294
        Assert::maxCount($issuer, 1, 'Only one <saml:Issuer> element is allowed.', TooManyElementsException::class);
295
296
        $requestedAuthnContext = RequestedAuthnContext::getChildrenOfClass($xml);
297
        Assert::maxCount(
298
            $requestedAuthnContext,
299
            1,
300
            'Only one <samlp:RequestedAuthnContext> element is allowed.',
301
            TooManyElementsException::class,
302
        );
303
304
        $extensions = Extensions::getChildrenOfClass($xml);
305
        Assert::maxCount(
306
            $extensions,
307
            1,
308
            'Only one <samlp:Extensions> element is allowed.',
309
            TooManyElementsException::class,
310
        );
311
312
        $signature = Signature::getChildrenOfClass($xml);
313
        Assert::maxCount(
314
            $signature,
315
            1,
316
            'Only one <ds:Signature> element is allowed.',
317
            TooManyElementsException::class,
318
        );
319
320
        $scoping = Scoping::getChildrenOfClass($xml);
321
        Assert::maxCount($scoping, 1, 'Only one <samlp:Scoping> element is allowed.', TooManyElementsException::class);
322
323
        $request = new static(
324
            $issueInstant,
325
            array_pop($requestedAuthnContext),
326
            array_pop($subject),
327
            array_pop($nameIdPolicy),
328
            array_pop($conditions),
329
            self::getOptionalBooleanAttribute($xml, 'ForceAuthn', null),
330
            self::getOptionalBooleanAttribute($xml, 'IsPassive', null),
331
            self::getOptionalAttribute($xml, 'AssertionConsumerServiceURL', null),
332
            $assertionConsumerServiceIndex,
333
            self::getOptionalAttribute($xml, 'ProtocolBinding', null),
334
            $attributeConsumingServiceIndex,
335
            self::getOptionalAttribute($xml, 'ProviderName', null),
336
            array_pop($issuer),
337
            $id,
338
            $version,
339
            self::getOptionalAttribute($xml, 'Destination', null),
340
            self::getOptionalAttribute($xml, 'Consent', null),
341
            array_pop($extensions),
342
            array_pop($scoping),
343
        );
344
345
        if (!empty($signature)) {
346
            $request->setSignature($signature[0]);
347
            $request->messageContainedSignatureUponConstruction = true;
348
            $request->setXML($xml);
349
        }
350
351
        return $request;
352
    }
353
354
355
    /**
356
     * Convert this message to an unsigned XML document.
357
     * This method does not sign the resulting XML document.
358
     *
359
     * @return \DOMElement The root element of the DOM tree
360
     */
361
    protected function toUnsignedXML(?DOMElement $parent = null): DOMElement
362
    {
363
        $e = parent::toUnsignedXML($parent);
364
365
        if ($this->getForceAuthn() === true) {
366
            $e->setAttribute('ForceAuthn', 'true');
367
        }
368
369
        if ($this->getProviderName() !== null) {
370
            $e->setAttribute('ProviderName', $this->getProviderName());
371
        }
372
373
        if ($this->getIsPassive() === true) {
374
            $e->setAttribute('IsPassive', 'true');
375
        }
376
377
        if ($this->getAssertionConsumerServiceIndex() !== null) {
378
            $e->setAttribute('AssertionConsumerServiceIndex', strval($this->getAssertionConsumerServiceIndex()));
379
        } else {
380
            if ($this->getAssertionConsumerServiceURL() !== null) {
381
                $e->setAttribute('AssertionConsumerServiceURL', $this->getAssertionConsumerServiceURL());
382
            }
383
            if ($this->getProtocolBinding() !== null) {
384
                $e->setAttribute('ProtocolBinding', $this->getProtocolBinding());
385
            }
386
        }
387
388
        if ($this->getAttributeConsumingServiceIndex() !== null) {
389
            $e->setAttribute('AttributeConsumingServiceIndex', strval($this->getAttributeConsumingServiceIndex()));
390
        }
391
392
        $this->getSubject()?->toXML($e);
393
394
        $nameIdPolicy = $this->getNameIdPolicy();
395
        if ($nameIdPolicy !== null && !$nameIdPolicy->isEmptyElement()) {
396
            $nameIdPolicy->toXML($e);
397
        }
398
399
        $conditions = $this->getConditions();
400
        if ($conditions !== null && !$conditions->isEmptyElement()) {
401
            $conditions->toXML($e);
402
        }
403
404
        $this->getRequestedAuthnContext()?->toXML($e);
405
406
        $scoping = $this->getScoping();
407
        if ($scoping !== null && !$scoping->isEmptyElement()) {
408
            $scoping->toXML($e);
409
        }
410
411
        return $e;
412
    }
413
}
414