Passed
Pull Request — master (#280)
by Tim
02:22
created

AuthnRequest::toXML()   F

Complexity

Conditions 16
Paths 3840

Size

Total Lines 54
Code Lines 28

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 16
eloc 28
c 1
b 0
f 0
nc 3840
nop 1
dl 0
loc 54
rs 1.4

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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 = null): void
335
    {
336
        if (!is_null($assertionConsumerServiceURL) && !filter_var($assertionConsumerServiceURL, FILTER_VALIDATE_URL)) {
337
            throw new InvalidArgumentException('AuthnRequest AssertionConsumerServiceURL is not a valid URL.');
338
        }
339
340
        $this->assertionConsumerServiceURL = $assertionConsumerServiceURL;
341
    }
342
343
344
    /**
345
     * Retrieve the value of the ProtocolBinding attribute.
346
     *
347
     * @return string|null The ProtocolBinding attribute.
348
     */
349
    public function getProtocolBinding(): ?string
350
    {
351
        return $this->protocolBinding;
352
    }
353
354
355
    /**
356
     * Set the value of the ProtocolBinding attribute.
357
     *
358
     * @param string|null $protocolBinding The ProtocolBinding attribute.
359
     */
360
    private function setProtocolBinding(?string $protocolBinding): void
361
    {
362
        Assert::nullOrNotWhitespaceOnly($protocolBinding);
363
364
        $this->protocolBinding = $protocolBinding;
365
    }
366
367
368
    /**
369
     * Retrieve the value of the AttributeConsumingServiceIndex attribute.
370
     *
371
     * @return int|null The AttributeConsumingServiceIndex attribute.
372
     */
373
    public function getAttributeConsumingServiceIndex(): ?int
374
    {
375
        return $this->attributeConsumingServiceIndex;
376
    }
377
378
379
    /**
380
     * Set the value of the AttributeConsumingServiceIndex attribute.
381
     *
382
     * @param int|null $attributeConsumingServiceIndex The AttributeConsumingServiceIndex attribute.
383
     */
384
    private function setAttributeConsumingServiceIndex(?int $attributeConsumingServiceIndex): void
385
    {
386
        Assert::nullOrRange($attributeConsumingServiceIndex, 0, 65535);
387
        $this->attributeConsumingServiceIndex = $attributeConsumingServiceIndex;
388
    }
389
390
391
    /**
392
     * Retrieve the value of the AssertionConsumerServiceIndex attribute.
393
     *
394
     * @return int|null The AssertionConsumerServiceIndex attribute.
395
     */
396
    public function getAssertionConsumerServiceIndex(): ?int
397
    {
398
        return $this->assertionConsumerServiceIndex;
399
    }
400
401
402
    /**
403
     * Set the value of the AssertionConsumerServiceIndex attribute.
404
     *
405
     * @param int|null $assertionConsumerServiceIndex The AssertionConsumerServiceIndex attribute.
406
     */
407
    private function setAssertionConsumerServiceIndex(?int $assertionConsumerServiceIndex): void
408
    {
409
        Assert::nullOrRange($assertionConsumerServiceIndex, 0, 65535);
410
        $this->assertionConsumerServiceIndex = $assertionConsumerServiceIndex;
411
    }
412
413
414
    /**
415
     * Retrieve the RequestedAuthnContext.
416
     *
417
     * @return \SimpleSAML\SAML2\XML\samlp\RequestedAuthnContext|null The RequestedAuthnContext.
418
     */
419
    public function getRequestedAuthnContext(): ?RequestedAuthnContext
420
    {
421
        return $this->requestedAuthnContext;
422
    }
423
424
425
    /**
426
     * Set the RequestedAuthnContext.
427
     *
428
     * @param \SimpleSAML\SAML2\XML\samlp\RequestedAuthnContext|null $requestedAuthnContext The RequestedAuthnContext.
429
     */
430
    private function setRequestedAuthnContext(RequestedAuthnContext $requestedAuthnContext = null): void
431
    {
432
        $this->requestedAuthnContext = $requestedAuthnContext;
433
    }
434
435
436
    /**
437
     * Convert XML into an AuthnRequest
438
     *
439
     * @param \DOMElement $xml The XML element we should load
440
     * @return \SimpleSAML\SAML2\XML\samlp\AuthnRequest
441
     *
442
     * @throws \SimpleSAML\XML\Exception\InvalidDOMElementException if the qualified name of the supplied element is wrong
443
     * @throws \SimpleSAML\XML\Exception\MissingAttributeException if the supplied element is missing one of the mandatory attributes
444
     * @throws \SimpleSAML\XML\Exception\TooManyElementsException if too many child-elements of a type are specified
445
     */
446
    public static function fromXML(DOMElement $xml): object
447
    {
448
        Assert::same($xml->localName, 'AuthnRequest', InvalidDOMElementException::class);
449
        Assert::same($xml->namespaceURI, AuthnRequest::NS, InvalidDOMElementException::class);
450
451
        Assert::true(version_compare('2.0', self::getAttribute($xml, 'Version'), '<='), RequestVersionTooLowException::class);
452
        Assert::true(version_compare('2.0', self::getAttribute($xml, 'Version'), '>='), RequestVersionTooHighException::class);
453
454
        $issueInstant = self::getAttribute($xml, 'IssueInstant');
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 self(
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
        }
515
516
        $request->setXML($xml);
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
     * @param \DOMElement|null $parent
526
     * @return \DOMElement The root element of the DOM tree
527
     */
528
    protected function toUnsignedXML(?DOMElement $parent = null): DOMElement
529
    {
530
        $e = parent::toUnsignedXML($parent);
531
532
        if ($this->forceAuthn === true) {
533
            $e->setAttribute('ForceAuthn', 'true');
534
        }
535
536
        if (!empty($this->ProviderName)) {
537
            $e->setAttribute('ProviderName', $this->ProviderName);
538
        }
539
540
        if ($this->isPassive === true) {
541
            $e->setAttribute('IsPassive', 'true');
542
        }
543
544
        if ($this->assertionConsumerServiceIndex !== null) {
545
            $e->setAttribute('AssertionConsumerServiceIndex', strval($this->assertionConsumerServiceIndex));
546
        } else {
547
            if ($this->assertionConsumerServiceURL !== null) {
548
                $e->setAttribute('AssertionConsumerServiceURL', $this->assertionConsumerServiceURL);
549
            }
550
            if ($this->protocolBinding !== null) {
551
                $e->setAttribute('ProtocolBinding', $this->protocolBinding);
552
            }
553
        }
554
555
        if ($this->attributeConsumingServiceIndex !== null) {
556
            $e->setAttribute('AttributeConsumingServiceIndex', strval($this->attributeConsumingServiceIndex));
557
        }
558
559
        if ($this->subject !== null) {
560
            $this->subject->toXML($e);
561
        }
562
563
        if ($this->nameIdPolicy !== null) {
564
            if (!$this->nameIdPolicy->isEmptyElement()) {
565
                $this->nameIdPolicy->toXML($e);
566
            }
567
        }
568
569
        if ($this->conditions !== null && !$this->conditions->isEmptyElement()) {
570
            $this->conditions->toXML($parent);
571
        }
572
573
        if (!empty($this->requestedAuthnContext)) {
574
            $this->requestedAuthnContext->toXML($e);
575
        }
576
577
        if ($this->scoping !== null && !$this->scoping->isEmptyElement()) {
578
            $this->scoping->toXML($parent);
579
        }
580
581
        return $e;
582
    }
583
}
584