Passed
Push — master ( 045039...d7835c )
by Tim
02:26
created

AuthnRequest::setIsPassive()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
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