Passed
Push — master ( d3e6a5...90893b )
by Tim
02:27
created

AuthnRequest::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 35
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 13
nc 1
nop 18
dl 0
loc 35
rs 9.8333
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 DOMElement;
8
use SimpleSAML\Assert\Assert;
9
use SimpleSAML\SAML2\Exception\InvalidArgumentException;
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\SAML2\XML\saml\SubjectConfirmation;
17
use SimpleSAML\XML\Exception\InvalidDOMElementException;
18
use SimpleSAML\XML\Exception\TooManyElementsException;
19
use SimpleSAML\XML\Utils as XMLUtils;
20
use SimpleSAML\XMLSecurity\XML\ds\Signature;
21
22
use function array_pop;
23
use function filter_var;
24
use function is_null;
25
use function strval;
26
27
/**
28
 * Class for SAML 2 authentication request messages.
29
 *
30
 * @package simplesamlphp/saml2
31
 */
32
class AuthnRequest extends AbstractRequest
33
{
34
    /**
35
     * @var \SimpleSAML\SAML2\XML\saml\Subject|null
36
     */
37
    protected ?Subject $subject = null;
38
39
    /**
40
     * @var \SimpleSAML\SAML2\XML\samlp\Scoping|null
41
     */
42
    protected ?Scoping $scoping = null;
43
44
    /**
45
     * The options for what type of name identifier should be returned.
46
     *
47
     * @var \SimpleSAML\SAML2\XML\samlp\NameIDPolicy|null
48
     */
49
    protected ?NameIDPolicy $nameIdPolicy = null;
50
51
    /**
52
     * Whether the Identity Provider must authenticate the user again.
53
     *
54
     * @var bool|null
55
     */
56
    protected ?bool $forceAuthn = false;
57
58
    /**
59
     * Optional ProviderID attribute
60
     *
61
     * @var string|null
62
     */
63
    protected ?string $ProviderName = null;
64
65
    /**
66
     * Set to true if this request is passive.
67
     *
68
     * @var bool|null
69
     */
70
    protected ?bool $isPassive = false;
71
72
    /**
73
     * The URL of the assertion consumer service where the response should be delivered.
74
     *
75
     * @var string|null
76
     */
77
    protected ?string $assertionConsumerServiceURL;
78
79
    /**
80
     * What binding should be used when sending the response.
81
     *
82
     * @var string|null
83
     */
84
    protected ?string $protocolBinding;
85
86
    /**
87
     * The index of the AttributeConsumingService.
88
     *
89
     * @var int|null
90
     */
91
    protected ?int $attributeConsumingServiceIndex;
92
93
    /**
94
     * The index of the AssertionConsumerService.
95
     *
96
     * @var int|null
97
     */
98
    protected ?int $assertionConsumerServiceIndex = null;
99
100
    /**
101
     * What authentication context was requested.
102
     *
103
     * @var \SimpleSAML\SAML2\XML\samlp\RequestedAuthnContext|null
104
     */
105
    protected ?RequestedAuthnContext $requestedAuthnContext;
106
107
    /**
108
     * @var \SimpleSAML\SAML2\XML\saml\Conditions|null
109
     */
110
    protected ?Conditions $conditions = null;
111
112
113
    /**
114
     * Constructor for SAML 2 AuthnRequest
115
     *
116
     * @param \SimpleSAML\SAML2\XML\samlp\RequestedAuthnContext $requestedAuthnContext
117
     * @param \SimpleSAML\SAML2\XML\saml\Subject $subject
118
     * @param \SimpleSAML\SAML2\XML\samlp\NameIDPolicy $nameIdPolicy
119
     * @param \SimpleSAML\SAML2\XML\saml\Conditions $conditions
120
     * @param bool $forceAuthn
121
     * @param bool $isPassive
122
     * @param string|null $assertionConsumerServiceUrl
123
     * @param int|null $assertionConsumerServiceIndex
124
     * @param string|null $protocolBinding
125
     * @param int|null $attributeConsumingServiceIndex
126
     * @param string|null $providerName
127
     * @param \SimpleSAML\SAML2\XML\saml\Issuer|null $issuer
128
     * @param string|null $id
129
     * @param int|null $issueInstant
130
     * @param string|null $destination
131
     * @param string|null $consent
132
     * @param \SimpleSAML\SAML2\XML\samlp\Extensions|null $extensions
133
     * @param \SimpleSAML\SAML2\XML\samlp\Scoping|null $scoping
134
     * @throws \Exception
135
     */
136
    public function __construct(
137
        ?RequestedAuthnContext $requestedAuthnContext = null,
138
        ?Subject $subject = null,
139
        ?NameIDPolicy $nameIdPolicy = null,
140
        Conditions $conditions = null,
141
        ?bool $forceAuthn = null,
142
        ?bool $isPassive = null,
143
        ?string $assertionConsumerServiceUrl = null,
144
        ?int $assertionConsumerServiceIndex = null,
145
        ?string $protocolBinding = null,
146
        ?int $attributeConsumingServiceIndex = null,
147
        ?string $providerName = null,
148
        ?Issuer $issuer = null,
149
        ?string $id = null,
150
        ?int $issueInstant = null,
151
        ?string $destination = null,
152
        ?string $consent = null,
153
        ?Extensions $extensions = null,
154
        ?Scoping $scoping = null
155
    ) {
156
        parent::__construct($issuer, $id, $issueInstant, $destination, $consent, $extensions);
157
158
        $this->setRequestedAuthnContext($requestedAuthnContext);
159
        $this->setSubject($subject);
160
        $this->setNameIdPolicy($nameIdPolicy);
161
        $this->setConditions($conditions);
162
163
        $this->setForceAuthn($forceAuthn);
164
        $this->setIsPassive($isPassive);
165
        $this->setAssertionConsumerServiceUrl($assertionConsumerServiceUrl);
166
        $this->setAssertionConsumerServiceIndex($assertionConsumerServiceIndex);
167
        $this->setProtocolBinding($protocolBinding);
168
        $this->setAttributeConsumingServiceIndex($attributeConsumingServiceIndex);
169
        $this->setProviderName($providerName);
170
        $this->setScoping($scoping);
171
    }
172
173
174
    /**
175
     * @param \SimpleSAML\SAML2\XML\saml\Subject|null $subject
176
     */
177
    private function setSubject(?Subject $subject): void
178
    {
179
        $this->subject = $subject;
180
    }
181
182
183
    /**
184
     * @return \SimpleSAML\SAML2\XML\saml\Subject|null
185
     */
186
    public function getSubject(): ?Subject
187
    {
188
        return $this->subject;
189
    }
190
191
192
    /**
193
     * @param \SimpleSAML\SAML2\XML\samlp\Scoping|null $scoping
194
     */
195
    private function setScoping(?Scoping $scoping): void
196
    {
197
        $this->scoping = $scoping;
198
    }
199
200
201
    /**
202
     * @return \SimpleSAML\SAML2\XML\samlp\Scoping|null
203
     */
204
    public function getScoping(): ?Scoping
205
    {
206
        return $this->scoping;
207
    }
208
209
210
    /**
211
     * @param \SimpleSAML\SAML2\XML\saml\Conditions|null $conditions
212
     */
213
    private function setConditions(?Conditions $conditions): void
214
    {
215
        $this->conditions = $conditions;
216
    }
217
218
219
    /**
220
     * @return \SimpleSAML\SAML2\XML\saml\Conditions|null
221
     */
222
    public function getConditions(): ?Conditions
223
    {
224
        return $this->conditions;
225
    }
226
227
228
    /**
229
     * Retrieve the NameIdPolicy.
230
     *
231
     * @see \SimpleSAML\SAML2\AuthnRequest::setNameIdPolicy()
232
     * @return \SimpleSAML\SAML2\XML\samlp\NameIDPolicy|null The NameIdPolicy.
233
     */
234
    public function getNameIdPolicy(): ?NameIDPolicy
235
    {
236
        return $this->nameIdPolicy;
237
    }
238
239
240
    /**
241
     * Set the NameIDPolicy.
242
     *
243
     * @param \SimpleSAML\SAML2\XML\samlp\NameIDPolicy|null $nameIdPolicy The NameIDPolicy.
244
     */
245
    private function setNameIdPolicy(?NameIDPolicy $nameIdPolicy): void
246
    {
247
        $this->nameIdPolicy = $nameIdPolicy;
248
    }
249
250
251
    /**
252
     * Retrieve the value of the ForceAuthn attribute.
253
     *
254
     * @return bool|null The ForceAuthn attribute.
255
     */
256
    public function getForceAuthn(): ?bool
257
    {
258
        return $this->forceAuthn;
259
    }
260
261
262
    /**
263
     * Set the value of the ForceAuthn attribute.
264
     *
265
     * @param bool $forceAuthn The ForceAuthn attribute.
266
     */
267
    private function setForceAuthn(?bool $forceAuthn): void
268
    {
269
        $this->forceAuthn = $forceAuthn;
270
    }
271
272
273
    /**
274
     * Retrieve the value of the ProviderName attribute.
275
     *
276
     * @return string|null The ProviderName attribute.
277
     */
278
    public function getProviderName(): ?string
279
    {
280
        return $this->ProviderName;
281
    }
282
283
284
    /**
285
     * Set the value of the ProviderName attribute.
286
     *
287
     * @param string|null $ProviderName The ProviderName attribute.
288
     */
289
    private function setProviderName(?string $ProviderName): void
290
    {
291
        Assert::nullOrNotWhitespaceOnly($ProviderName);
292
        $this->ProviderName = $ProviderName;
293
    }
294
295
296
    /**
297
     * Retrieve the value of the IsPassive attribute.
298
     *
299
     * @return bool|null The IsPassive attribute.
300
     */
301
    public function getIsPassive(): ?bool
302
    {
303
        return $this->isPassive;
304
    }
305
306
307
    /**
308
     * Set the value of the IsPassive attribute.
309
     *
310
     * @param bool|null $isPassive The IsPassive attribute.
311
     */
312
    private function setIsPassive(?bool $isPassive): void
313
    {
314
        $this->isPassive = $isPassive;
315
    }
316
317
318
    /**
319
     * Retrieve the value of the AssertionConsumerServiceURL attribute.
320
     *
321
     * @return string|null The AssertionConsumerServiceURL attribute.
322
     */
323
    public function getAssertionConsumerServiceURL(): ?string
324
    {
325
        return $this->assertionConsumerServiceURL;
326
    }
327
328
329
    /**
330
     * Set the value of the AssertionConsumerServiceURL attribute.
331
     *
332
     * @param string|null $assertionConsumerServiceURL The AssertionConsumerServiceURL attribute.
333
     */
334
    private function setAssertionConsumerServiceURL(?string $assertionConsumerServiceURL): void
335
    {
336
        Assert::nullOrValidURL($assertionConsumerServiceURL);
337
        $this->assertionConsumerServiceURL = $assertionConsumerServiceURL;
338
    }
339
340
341
    /**
342
     * Retrieve the value of the ProtocolBinding attribute.
343
     *
344
     * @return string|null The ProtocolBinding attribute.
345
     */
346
    public function getProtocolBinding(): ?string
347
    {
348
        return $this->protocolBinding;
349
    }
350
351
352
    /**
353
     * Set the value of the ProtocolBinding attribute.
354
     *
355
     * @param string|null $protocolBinding The ProtocolBinding attribute.
356
     */
357
    private function setProtocolBinding(?string $protocolBinding): void
358
    {
359
        Assert::nullOrValidURI($protocolBinding); // Covers the empty string
360
361
        $this->protocolBinding = $protocolBinding;
362
    }
363
364
365
    /**
366
     * Retrieve the value of the AttributeConsumingServiceIndex attribute.
367
     *
368
     * @return int|null The AttributeConsumingServiceIndex attribute.
369
     */
370
    public function getAttributeConsumingServiceIndex(): ?int
371
    {
372
        return $this->attributeConsumingServiceIndex;
373
    }
374
375
376
    /**
377
     * Set the value of the AttributeConsumingServiceIndex attribute.
378
     *
379
     * @param int|null $attributeConsumingServiceIndex The AttributeConsumingServiceIndex attribute.
380
     */
381
    private function setAttributeConsumingServiceIndex(?int $attributeConsumingServiceIndex): void
382
    {
383
        Assert::nullOrRange($attributeConsumingServiceIndex, 0, 65535);
384
        $this->attributeConsumingServiceIndex = $attributeConsumingServiceIndex;
385
    }
386
387
388
    /**
389
     * Retrieve the value of the AssertionConsumerServiceIndex attribute.
390
     *
391
     * @return int|null The AssertionConsumerServiceIndex attribute.
392
     */
393
    public function getAssertionConsumerServiceIndex(): ?int
394
    {
395
        return $this->assertionConsumerServiceIndex;
396
    }
397
398
399
    /**
400
     * Set the value of the AssertionConsumerServiceIndex attribute.
401
     *
402
     * @param int|null $assertionConsumerServiceIndex The AssertionConsumerServiceIndex attribute.
403
     */
404
    private function setAssertionConsumerServiceIndex(?int $assertionConsumerServiceIndex): void
405
    {
406
        Assert::nullOrRange($assertionConsumerServiceIndex, 0, 65535);
407
        $this->assertionConsumerServiceIndex = $assertionConsumerServiceIndex;
408
    }
409
410
411
    /**
412
     * Retrieve the RequestedAuthnContext.
413
     *
414
     * @return \SimpleSAML\SAML2\XML\samlp\RequestedAuthnContext|null The RequestedAuthnContext.
415
     */
416
    public function getRequestedAuthnContext(): ?RequestedAuthnContext
417
    {
418
        return $this->requestedAuthnContext;
419
    }
420
421
422
    /**
423
     * Set the RequestedAuthnContext.
424
     *
425
     * @param \SimpleSAML\SAML2\XML\samlp\RequestedAuthnContext|null $requestedAuthnContext The RequestedAuthnContext.
426
     */
427
    private function setRequestedAuthnContext(RequestedAuthnContext $requestedAuthnContext = null): void
428
    {
429
        $this->requestedAuthnContext = $requestedAuthnContext;
430
    }
431
432
433
    /**
434
     * Convert XML into an AuthnRequest
435
     *
436
     * @param \DOMElement $xml The XML element we should load
437
     * @return \SimpleSAML\SAML2\XML\samlp\AuthnRequest
438
     *
439
     * @throws \SimpleSAML\XML\Exception\InvalidDOMElementException if the qualified name of the supplied element is wrong
440
     * @throws \SimpleSAML\XML\Exception\MissingAttributeException if the supplied element is missing one of the mandatory attributes
441
     * @throws \SimpleSAML\XML\Exception\TooManyElementsException if too many child-elements of a type are specified
442
     */
443
    public static function fromXML(DOMElement $xml): static
444
    {
445
        Assert::same($xml->localName, 'AuthnRequest', InvalidDOMElementException::class);
446
        Assert::same($xml->namespaceURI, AuthnRequest::NS, InvalidDOMElementException::class);
447
448
        Assert::true(version_compare('2.0', self::getAttribute($xml, 'Version'), '<='), RequestVersionTooLowException::class);
449
        Assert::true(version_compare('2.0', self::getAttribute($xml, 'Version'), '>='), RequestVersionTooHighException::class);
450
451
        $issueInstant = self::getAttribute($xml, 'IssueInstant');
452
        // Strip sub-seconds - See paragraph 1.3.3 of SAML core specifications
453
        $issueInstant = preg_replace('/([.][0-9]+Z)$/', 'Z', $issueInstant, 1);
454
455
        Assert::validDateTimeZulu($issueInstant, ProtocolViolationException::class);
456
        $issueInstant = XMLUtils::xsDateTimeToTimestamp($issueInstant);
457
458
        $attributeConsumingServiceIndex = self::getIntegerAttribute($xml, 'AttributeConsumingServiceIndex', null);
459
        $assertionConsumerServiceIndex = self::getIntegerAttribute($xml, 'AssertionConsumerServiceIndex', null);
460
461
        $conditions = Conditions::getChildrenOfClass($xml);
462
        Assert::maxCount($conditions, 1, 'Only one <saml:Conditions> element is allowed.', TooManyElementsException::class);
463
464
        $nameIdPolicy = NameIDPolicy::getChildrenOfClass($xml);
465
        Assert::maxCount($nameIdPolicy, 1, 'Only one <samlp:NameIDPolicy> element is allowed.', TooManyElementsException::class);
466
467
        $subject = Subject::getChildrenOfClass($xml);
468
        Assert::maxCount($subject, 1, 'Only one <saml:Subject> element is allowed.', TooManyElementsException::class);
469
470
        $issuer = Issuer::getChildrenOfClass($xml);
471
        Assert::maxCount($issuer, 1, 'Only one <saml:Issuer> element is allowed.', TooManyElementsException::class);
472
473
        $requestedAuthnContext = RequestedAuthnContext::getChildrenOfClass($xml);
474
        Assert::maxCount(
475
            $requestedAuthnContext,
476
            1,
477
            'Only one <samlp:RequestedAuthnContext> element is allowed.',
478
            TooManyElementsException::class
479
        );
480
481
        $extensions = Extensions::getChildrenOfClass($xml);
482
        Assert::maxCount($extensions, 1, 'Only one <samlp:Extensions> element is allowed.', TooManyElementsException::class);
483
484
        $signature = Signature::getChildrenOfClass($xml);
485
        Assert::maxCount($signature, 1, 'Only one <ds:Signature> element is allowed.', TooManyElementsException::class);
486
487
        $scoping = Scoping::getChildrenOfClass($xml);
488
        Assert::maxCount($scoping, 1, 'Only one <samlp:Scoping> element is allowed.', TooManyElementsException::class);
489
490
        $request = new static(
491
            array_pop($requestedAuthnContext),
492
            array_pop($subject),
493
            array_pop($nameIdPolicy),
494
            array_pop($conditions),
495
            self::getBooleanAttribute($xml, 'ForceAuthn', null),
496
            self::getBooleanAttribute($xml, 'IsPassive', null),
497
            self::getAttribute($xml, 'AssertionConsumerServiceURL', null),
498
            $assertionConsumerServiceIndex,
499
            self::getAttribute($xml, 'ProtocolBinding', null),
500
            $attributeConsumingServiceIndex,
501
            self::getAttribute($xml, 'ProviderName', null),
502
            array_pop($issuer),
503
            self::getAttribute($xml, 'ID'),
504
            $issueInstant,
505
            self::getAttribute($xml, 'Destination', null),
506
            self::getAttribute($xml, 'Consent', null),
507
            array_pop($extensions),
508
            array_pop($scoping)
509
        );
510
511
        if (!empty($signature)) {
512
            $request->setSignature($signature[0]);
513
            $request->messageContainedSignatureUponConstruction = true;
514
            $request->setXML($xml);
515
        }
516
517
        return $request;
518
    }
519
520
521
    /**
522
     * Convert this message to an unsigned XML document.
523
     * This method does not sign the resulting XML document.
524
     *
525
     * @return \DOMElement The root element of the DOM tree
526
     */
527
    protected function toUnsignedXML(?DOMElement $parent = null): DOMElement
528
    {
529
        $e = parent::toUnsignedXML($parent);
530
531
        if ($this->getForceAuthn() === true) {
532
            $e->setAttribute('ForceAuthn', 'true');
533
        }
534
535
        if (!empty($this->getProviderName())) {
536
            $e->setAttribute('ProviderName', $this->getProviderName());
537
        }
538
539
        if ($this->getIsPassive() === true) {
540
            $e->setAttribute('IsPassive', 'true');
541
        }
542
543
        if ($this->getAssertionConsumerServiceIndex() !== null) {
544
            $e->setAttribute('AssertionConsumerServiceIndex', strval($this->getAssertionConsumerServiceIndex()));
545
        } else {
546
            if ($this->getAssertionConsumerServiceURL() !== null) {
547
                $e->setAttribute('AssertionConsumerServiceURL', $this->getAssertionConsumerServiceURL());
548
            }
549
            if ($this->getProtocolBinding() !== null) {
550
                $e->setAttribute('ProtocolBinding', $this->getProtocolBinding());
551
            }
552
        }
553
554
        if ($this->getAttributeConsumingServiceIndex() !== null) {
555
            $e->setAttribute('AttributeConsumingServiceIndex', strval($this->getAttributeConsumingServiceIndex()));
556
        }
557
558
        $this->getSubject()?->toXML($e);
559
560
        if ($this->getNameIdPolicy() !== null && !$this->getNameIdPolicy()->isEmptyElement()) {
561
            $this->getNameIdPolicy()->toXML($e);
562
        }
563
564
        if ($this->getConditions() !== null && !$this->getConditions()->isEmptyElement()) {
565
            $this->getConditions()->toXML($e);
566
        }
567
568
        $this->getRequestedAuthnContext()?->toXML($e);
569
570
        if ($this->getScoping() !== null && !$this->getScoping()->isEmptyElement()) {
571
            $this->getScoping()->toXML($e);
572
        }
573
574
        return $e;
575
    }
576
}
577