Passed
Push — master ( 045039...d7835c )
by Tim
02:26
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
440
     *   if the qualified name of the supplied element is wrong
441
     * @throws \SimpleSAML\XML\Exception\MissingAttributeException
442
     *   if the supplied element is missing one of the mandatory attributes
443
     * @throws \SimpleSAML\XML\Exception\TooManyElementsException
444
     *   if too many child-elements of a type are specified
445
     */
446
    public static function fromXML(DOMElement $xml): static
447
    {
448
        Assert::same($xml->localName, 'AuthnRequest', InvalidDOMElementException::class);
449
        Assert::same($xml->namespaceURI, AuthnRequest::NS, InvalidDOMElementException::class);
450
451
        Assert::true(
452
            version_compare('2.0', self::getAttribute($xml, 'Version'), '<='),
453
            RequestVersionTooLowException::class
454
        );
455
        Assert::true(
456
            version_compare('2.0', self::getAttribute($xml, 'Version'), '>='),
457
            RequestVersionTooHighException::class
458
        );
459
460
        $issueInstant = self::getAttribute($xml, 'IssueInstant');
461
        // Strip sub-seconds - See paragraph 1.3.3 of SAML core specifications
462
        $issueInstant = preg_replace('/([.][0-9]+Z)$/', 'Z', $issueInstant, 1);
463
464
        Assert::validDateTimeZulu($issueInstant, ProtocolViolationException::class);
465
        $issueInstant = XMLUtils::xsDateTimeToTimestamp($issueInstant);
466
467
        $attributeConsumingServiceIndex = self::getIntegerAttribute($xml, 'AttributeConsumingServiceIndex', null);
468
        $assertionConsumerServiceIndex = self::getIntegerAttribute($xml, 'AssertionConsumerServiceIndex', null);
469
470
        $conditions = Conditions::getChildrenOfClass($xml);
471
        Assert::maxCount(
472
            $conditions,
473
            1,
474
            'Only one <saml:Conditions> element is allowed.',
475
            TooManyElementsException::class
476
        );
477
478
        $nameIdPolicy = NameIDPolicy::getChildrenOfClass($xml);
479
        Assert::maxCount(
480
            $nameIdPolicy,
481
            1,
482
            'Only one <samlp:NameIDPolicy> element is allowed.',
483
            TooManyElementsException::class
484
        );
485
486
        $subject = Subject::getChildrenOfClass($xml);
487
        Assert::maxCount($subject, 1, 'Only one <saml:Subject> element is allowed.', TooManyElementsException::class);
488
489
        $issuer = Issuer::getChildrenOfClass($xml);
490
        Assert::maxCount($issuer, 1, 'Only one <saml:Issuer> element is allowed.', TooManyElementsException::class);
491
492
        $requestedAuthnContext = RequestedAuthnContext::getChildrenOfClass($xml);
493
        Assert::maxCount(
494
            $requestedAuthnContext,
495
            1,
496
            'Only one <samlp:RequestedAuthnContext> element is allowed.',
497
            TooManyElementsException::class
498
        );
499
500
        $extensions = Extensions::getChildrenOfClass($xml);
501
        Assert::maxCount(
502
            $extensions,
503
            1,
504
            'Only one <samlp:Extensions> element is allowed.',
505
            TooManyElementsException::class
506
        );
507
508
        $signature = Signature::getChildrenOfClass($xml);
509
        Assert::maxCount(
510
            $signature,
511
            1,
512
            'Only one <ds:Signature> element is allowed.',
513
            TooManyElementsException::class
514
        );
515
516
        $scoping = Scoping::getChildrenOfClass($xml);
517
        Assert::maxCount($scoping, 1, 'Only one <samlp:Scoping> element is allowed.', TooManyElementsException::class);
518
519
        $request = new static(
520
            array_pop($requestedAuthnContext),
521
            array_pop($subject),
522
            array_pop($nameIdPolicy),
523
            array_pop($conditions),
524
            self::getBooleanAttribute($xml, 'ForceAuthn', null),
525
            self::getBooleanAttribute($xml, 'IsPassive', null),
526
            self::getAttribute($xml, 'AssertionConsumerServiceURL', null),
527
            $assertionConsumerServiceIndex,
528
            self::getAttribute($xml, 'ProtocolBinding', null),
529
            $attributeConsumingServiceIndex,
530
            self::getAttribute($xml, 'ProviderName', null),
531
            array_pop($issuer),
532
            self::getAttribute($xml, 'ID'),
533
            $issueInstant,
534
            self::getAttribute($xml, 'Destination', null),
535
            self::getAttribute($xml, 'Consent', null),
536
            array_pop($extensions),
537
            array_pop($scoping)
538
        );
539
540
        if (!empty($signature)) {
541
            $request->setSignature($signature[0]);
542
            $request->messageContainedSignatureUponConstruction = true;
543
            $request->setXML($xml);
544
        }
545
546
        return $request;
547
    }
548
549
550
    /**
551
     * Convert this message to an unsigned XML document.
552
     * This method does not sign the resulting XML document.
553
     *
554
     * @return \DOMElement The root element of the DOM tree
555
     */
556
    protected function toUnsignedXML(?DOMElement $parent = null): DOMElement
557
    {
558
        $e = parent::toUnsignedXML($parent);
559
560
        if ($this->getForceAuthn() === true) {
561
            $e->setAttribute('ForceAuthn', 'true');
562
        }
563
564
        if (!empty($this->getProviderName())) {
565
            $e->setAttribute('ProviderName', $this->getProviderName());
566
        }
567
568
        if ($this->getIsPassive() === true) {
569
            $e->setAttribute('IsPassive', 'true');
570
        }
571
572
        if ($this->getAssertionConsumerServiceIndex() !== null) {
573
            $e->setAttribute('AssertionConsumerServiceIndex', strval($this->getAssertionConsumerServiceIndex()));
574
        } else {
575
            if ($this->getAssertionConsumerServiceURL() !== null) {
576
                $e->setAttribute('AssertionConsumerServiceURL', $this->getAssertionConsumerServiceURL());
577
            }
578
            if ($this->getProtocolBinding() !== null) {
579
                $e->setAttribute('ProtocolBinding', $this->getProtocolBinding());
580
            }
581
        }
582
583
        if ($this->getAttributeConsumingServiceIndex() !== null) {
584
            $e->setAttribute('AttributeConsumingServiceIndex', strval($this->getAttributeConsumingServiceIndex()));
585
        }
586
587
        $this->getSubject()?->toXML($e);
588
589
        $nameIdPolicy = $this->getNameIdPolicy();
590
        if ($nameIdPolicy !== null && !$nameIdPolicy->isEmptyElement()) {
591
            $nameIdPolicy->toXML($e);
592
        }
593
594
        $conditions = $this->getConditions();
595
        if ($conditions !== null && !$conditions->isEmptyElement()) {
596
            $conditions->toXML($e);
597
        }
598
599
        $this->getRequestedAuthnContext()?->toXML($e);
600
601
        $scoping = $this->getScoping();
602
        if ($scoping !== null && !$scoping->isEmptyElement()) {
603
            $scoping->toXML($e);
604
        }
605
606
        return $e;
607
    }
608
}
609