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

AuthnRequest::toXML()   F

Complexity

Conditions 16
Paths 8640

Size

Total Lines 58
Code Lines 30

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 16
eloc 30
nc 8640
nop 1
dl 0
loc 58
rs 1.4
c 0
b 0
f 0

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