Completed
Push — master ( 233091...30e9c1 )
by Thijs
14s queued 10s
created

AuthnRequest::getAudiences()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace SAML2;
4
5
use RobRichards\XMLSecLibs\XMLSecEnc;
6
use RobRichards\XMLSecLibs\XMLSecurityKey;
7
use SAML2\XML\saml\SubjectConfirmation;
8
use SAML2\Exception\InvalidArgumentException;
9
10
/**
11
 * Class for SAML 2 authentication request messages.
12
 *
13
 * @package SimpleSAMLphp
14
 */
15
class AuthnRequest extends Request
16
{
17
    /**
18
     * The options for what type of name identifier should be returned.
19
     *
20
     * @var array
21
     */
22
    private $nameIdPolicy;
23
24
    /**
25
     * Whether the Identity Provider must authenticate the user again.
26
     *
27
     * @var bool
28
     */
29
    private $forceAuthn;
30
31
32
    /**
33
     * Optional ProviderID attribute
34
     *
35
     * @var string
36
     */
37
    private $ProviderName;
38
39
40
    /**
41
     * Set to true if this request is passive.
42
     *
43
     * @var bool.
44
     */
45
    private $isPassive;
46
47
    /**
48
     * The list of providerIDs in this request's scoping element
49
     *
50
     * @var array
51
     */
52
    private $IDPList = [];
53
54
    /**
55
     * The ProxyCount in this request's scoping element
56
     *
57
     * @var int
58
     */
59
    private $ProxyCount = null;
60
61
    /**
62
     * The RequesterID list in this request's scoping element
63
     *
64
     * @var array
65
     */
66
67
    private $RequesterID = [];
68
69
    /**
70
     * The URL of the asertion consumer service where the response should be delivered.
71
     *
72
     * @var string|null
73
     */
74
    private $assertionConsumerServiceURL;
75
76
77
    /**
78
     * What binding should be used when sending the response.
79
     *
80
     * @var string|null
81
     */
82
    private $protocolBinding;
83
84
85
    /**
86
     * The index of the AttributeConsumingService.
87
     *
88
     * @var int|null
89
     */
90
    private $attributeConsumingServiceIndex;
91
92
    /**
93
     * The index of the AssertionConsumerService.
94
     *
95
     * @var int|null
96
     */
97
    private $assertionConsumerServiceIndex;
98
99
100
    /**
101
     * What authentication context was requested.
102
     *
103
     * Array with the following elements.
104
     * - AuthnContextClassRef (required)
105
     * - Comparison (optinal)
106
     *
107
     * @var array
108
     */
109
    private $requestedAuthnContext;
110
111
    /**
112
     * Audiences to send in the request.
113
     *
114
     * @var array
115
     */
116
    private $audiences;
117
118
    /**
119
     * @var \SAML2\XML\saml\SubjectConfirmation[]
120
     */
121
    private $subjectConfirmation = [];
122
123
    /**
124
     * @var string
125
     */
126
    private $encryptedNameId;
127
128
    /**
129
     * @var \SAML2\XML\saml\NameID
130
     */
131
    private $nameId;
132
133
    /**
134
     * Constructor for SAML 2 authentication request messages.
135
     *
136
     * @param \DOMElement|null $xml The input message.
137
     * @throws \Exception
138
     */
139
    public function __construct(\DOMElement $xml = null)
140
    {
141
        parent::__construct('AuthnRequest', $xml);
142
143
        $this->nameIdPolicy = [];
144
        $this->forceAuthn = false;
145
        $this->isPassive = false;
146
147
        if ($xml === null) {
148
            return;
149
        }
150
151
        $this->forceAuthn = Utils::parseBoolean($xml, 'ForceAuthn', false);
152
        $this->isPassive = Utils::parseBoolean($xml, 'IsPassive', false);
153
154
        if ($xml->hasAttribute('AssertionConsumerServiceURL')) {
155
            $this->assertionConsumerServiceURL = $xml->getAttribute('AssertionConsumerServiceURL');
156
        }
157
158
        if ($xml->hasAttribute('ProtocolBinding')) {
159
            $this->protocolBinding = $xml->getAttribute('ProtocolBinding');
160
        }
161
162
        if ($xml->hasAttribute('AttributeConsumingServiceIndex')) {
163
            $this->attributeConsumingServiceIndex = (int) $xml->getAttribute('AttributeConsumingServiceIndex');
164
        }
165
166
        if ($xml->hasAttribute('AssertionConsumerServiceIndex')) {
167
            $this->assertionConsumerServiceIndex = (int) $xml->getAttribute('AssertionConsumerServiceIndex');
168
        }
169
170
        if ($xml->hasAttribute('ProviderName')) {
171
            $this->ProviderName = $xml->getAttribute('ProviderName');
172
        }
173
174
        $this->parseSubject($xml);
175
        $this->parseNameIdPolicy($xml);
176
        $this->parseRequestedAuthnContext($xml);
177
        $this->parseScoping($xml);
178
        $this->parseConditions($xml);
179
    }
180
181
    /**
182
     * @param $xml
183
     *
184
     * @throws \Exception
185
     */
186
    private function parseSubject(\DOMElement $xml)
187
    {
188
        $subject = Utils::xpQuery($xml, './saml_assertion:Subject');
189
        if (empty($subject)) {
190
            return;
191
        }
192
193
        if (count($subject) > 1) {
194
            throw new \Exception('More than one <saml:Subject> in <saml:AuthnRequest>.');
195
        }
196
        $subject = $subject[0];
197
198
        $nameId = Utils::xpQuery(
199
            $subject,
200
            './saml_assertion:NameID | ./saml_assertion:EncryptedID/xenc:EncryptedData'
201
        );
202 View Code Duplication
        if (empty($nameId)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
203
            throw new \Exception('Missing <saml:NameID> or <saml:EncryptedID> in <saml:Subject>.');
204
        } elseif (count($nameId) > 1) {
205
            throw new \Exception('More than one <saml:NameID> or <saml:EncryptedID> in <saml:Subject>.');
206
        }
207
        $nameId = $nameId[0];
208 View Code Duplication
        if ($nameId->localName === 'EncryptedData') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
209
            /* The NameID element is encrypted. */
210
            $this->encryptedNameId = $nameId;
0 ignored issues
show
Documentation Bug introduced by
It seems like $nameId of type object<DOMElement> is incompatible with the declared type string of property $encryptedNameId.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
211
        } else {
212
            $this->nameId = new XML\saml\NameID($nameId);
213
        }
214
215
        $subjectConfirmation = Utils::xpQuery($subject, './saml_assertion:SubjectConfirmation');
216
        foreach ($subjectConfirmation as $sc) {
217
            $this->subjectConfirmation[] = new SubjectConfirmation($sc);
218
        }
219
    }
220
221
    /**
222
     * @param \DOMElement $xml
223
     *
224
     * @throws \Exception
225
     */
226
    protected function parseNameIdPolicy(\DOMElement $xml)
227
    {
228
        $nameIdPolicy = Utils::xpQuery($xml, './saml_protocol:NameIDPolicy');
229
        if (empty($nameIdPolicy)) {
230
            return;
231
        }
232
233
        $nameIdPolicy = $nameIdPolicy[0];
234
        if ($nameIdPolicy->hasAttribute('Format')) {
235
            $this->nameIdPolicy['Format'] = $nameIdPolicy->getAttribute('Format');
236
        }
237
        if ($nameIdPolicy->hasAttribute('SPNameQualifier')) {
238
            $this->nameIdPolicy['SPNameQualifier'] = $nameIdPolicy->getAttribute('SPNameQualifier');
239
        }
240
        if ($nameIdPolicy->hasAttribute('AllowCreate')) {
241
            $this->nameIdPolicy['AllowCreate'] = Utils::parseBoolean($nameIdPolicy, 'AllowCreate', false);
242
        }
243
    }
244
245
    /**
246
     * @param \DOMElement $xml
247
     */
248
    protected function parseRequestedAuthnContext(\DOMElement $xml)
249
    {
250
        $requestedAuthnContext = Utils::xpQuery($xml, './saml_protocol:RequestedAuthnContext');
251
        if (empty($requestedAuthnContext)) {
252
            return;
253
        }
254
255
        $requestedAuthnContext = $requestedAuthnContext[0];
256
257
        $rac = [
258
            'AuthnContextClassRef' => [],
259
            'Comparison'           => Constants::COMPARISON_EXACT,
260
        ];
261
262
        $accr = Utils::xpQuery($requestedAuthnContext, './saml_assertion:AuthnContextClassRef');
263
        foreach ($accr as $i) {
264
            $rac['AuthnContextClassRef'][] = trim($i->textContent);
265
        }
266
267
        if ($requestedAuthnContext->hasAttribute('Comparison')) {
268
            $rac['Comparison'] = $requestedAuthnContext->getAttribute('Comparison');
269
        }
270
271
        $this->requestedAuthnContext = $rac;
272
    }
273
274
    /**
275
     * @param \DOMElement $xml
276
     *
277
     * @throws \Exception
278
     */
279
    protected function parseScoping(\DOMElement $xml)
280
    {
281
        $scoping = Utils::xpQuery($xml, './saml_protocol:Scoping');
282
        if (empty($scoping)) {
283
            return;
284
        }
285
286
        $scoping = $scoping[0];
287
288
        if ($scoping->hasAttribute('ProxyCount')) {
289
            $this->ProxyCount = (int) $scoping->getAttribute('ProxyCount');
290
        }
291
        $idpEntries = Utils::xpQuery($scoping, './saml_protocol:IDPList/saml_protocol:IDPEntry');
292
293
        foreach ($idpEntries as $idpEntry) {
294
            if (!$idpEntry->hasAttribute('ProviderID')) {
295
                throw new \Exception("Could not get ProviderID from Scoping/IDPEntry element in AuthnRequest object");
296
            }
297
            $this->IDPList[] = $idpEntry->getAttribute('ProviderID');
298
        }
299
300
        $requesterIDs = Utils::xpQuery($scoping, './saml_protocol:RequesterID');
301
        foreach ($requesterIDs as $requesterID) {
302
            $this->RequesterID[] = trim($requesterID->textContent);
303
        }
304
    }
305
306
    /**
307
     * @param \DOMElement $xml
308
     */
309
    protected function parseConditions(\DOMElement $xml)
310
    {
311
        $conditions = Utils::xpQuery($xml, './saml_assertion:Conditions');
312
        if (empty($conditions)) {
313
            return;
314
        }
315
        $conditions = $conditions[0];
316
317
        $ar = Utils::xpQuery($conditions, './saml_assertion:AudienceRestriction');
318
        if (empty($ar)) {
319
            return;
320
        }
321
        $ar = $ar[0];
322
323
        $audiences = Utils::xpQuery($ar, './saml_assertion:Audience');
324
        $this->audiences = array();
325
        foreach ($audiences as $a) {
326
            $this->audiences[] = trim($a->textContent);
327
        }
328
    }
329
330
    /**
331
     * Retrieve the NameIdPolicy.
332
     *
333
     * @see \SAML2\AuthnRequest::setNameIdPolicy()
334
     * @return array The NameIdPolicy.
335
     */
336
    public function getNameIdPolicy()
337
    {
338
        return $this->nameIdPolicy;
339
    }
340
341
342
    /**
343
     * Set the NameIDPolicy.
344
     *
345
     * This function accepts an array with the following options:
346
     *  - 'Format' (string)
347
     *  - 'SPNameQualifier' (string)
348
     *  - 'AllowCreate' (bool)
349
     *
350
     * @param array $nameIdPolicy The NameIDPolicy.
351
     */
352
    public function setNameIdPolicy(array $nameIdPolicy)
353
    {
354 View Code Duplication
        if (isset($nameIdPolicy['Format']) && !is_string($nameIdPolicy['Format'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
355
            throw InvalidArgumentException::invalidType('string', $nameIdPolicy['Format']);
356
        }
357 View Code Duplication
        if (isset($nameIdPolicy['SPNameQualifier']) && !is_string($nameIdPolicy['SPNameQualifier'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
358
            throw InvalidArgumentException::invalidType('string', $nameIdPolicy['SPNameQualifier']);
359
        }
360
        if (isset($nameIdPolicy['AllowCreate']) && !is_bool($nameIdPolicy['AllowCreate'])) {
361
            throw InvalidArgumentException::invalidType('bool', $nameIdPolicy['AllowCreate']);
362
        }
363
364
        $this->nameIdPolicy = $nameIdPolicy;
365
    }
366
367
368
    /**
369
     * Retrieve the value of the ForceAuthn attribute.
370
     *
371
     * @return bool The ForceAuthn attribute.
372
     */
373
    public function getForceAuthn()
374
    {
375
        return $this->forceAuthn;
376
    }
377
378
379
    /**
380
     * Set the value of the ForceAuthn attribute.
381
     *
382
     * @param bool $forceAuthn The ForceAuthn attribute.
383
     */
384
    public function setForceAuthn($forceAuthn)
385
    {
386
        assert(is_bool($forceAuthn));
387
388
        $this->forceAuthn = $forceAuthn;
389
    }
390
391
392
    /**
393
     * Retrieve the value of the ProviderName attribute.
394
     *
395
     * @return string The ProviderName attribute.
396
     */
397
    public function getProviderName()
398
    {
399
        return $this->ProviderName;
400
    }
401
402
403
    /**
404
     * Set the value of the ProviderName attribute.
405
     *
406
     * @param string $ProviderName The ProviderName attribute.
407
     */
408
    public function setProviderName($ProviderName)
409
    {
410
        assert(is_string($ProviderName));
411
412
        $this->ProviderName = $ProviderName;
413
    }
414
415
416
    /**
417
     * Retrieve the value of the IsPassive attribute.
418
     *
419
     * @return bool The IsPassive attribute.
420
     */
421
    public function getIsPassive()
422
    {
423
        return $this->isPassive;
424
    }
425
426
427
    /**
428
     * Set the value of the IsPassive attribute.
429
     *
430
     * @param bool $isPassive The IsPassive attribute.
431
     */
432
    public function setIsPassive($isPassive)
433
    {
434
        assert(is_bool($isPassive));
435
436
        $this->isPassive = $isPassive;
437
    }
438
439
    /**
440
     * Retrieve the audiences from the request.
441
     *
442
     * This may be null, in which case no audience is included.
443
     *
444
     * @return array|null The audiences.
445
     */
446
    public function getAudiences()
447
    {
448
        return $this->audiences;
449
    }
450
451
    /**
452
     * Set the audiences to send in the request.
453
     *
454
     * This may be null, in which case no audience will be sent.
455
     *
456
     * @param array|null $audiences The audiences.
457
     */
458
    public function setAudiences(array $audiences = null)
459
    {
460
        $this->audiences = $audiences;
0 ignored issues
show
Documentation Bug introduced by
It seems like $audiences can be null. However, the property $audiences is declared as array. Maybe change the type of the property to array|null or add a type check?

Our type inference engine has found an assignment of a scalar value (like a string, an integer or null) to a property which is an array.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property.

To type hint that a parameter can be either an array or null, you can set a type hint of array and a default value of null. The PHP interpreter will then accept both an array or null for that parameter.

function aContainsB(array $needle = null, array  $haystack) {
    if (!$needle) {
        return false;
    }

    return array_intersect($haystack, $needle) == $haystack;
}

The function can be called with either null or an array for the parameter $needle but will only accept an array as $haystack.

Loading history...
461
    }
462
463
464
    /**
465
     * This function sets the scoping for the request.
466
     * See Core 3.4.1.2 for the definition of scoping.
467
     * Currently we support an IDPList of idpEntries.
468
     *
469
     * Each idpEntries consists of an array, containing
470
     * keys (mapped to attributes) and corresponding values.
471
     * Allowed attributes: Loc, Name, ProviderID.
472
     *
473
     * For backward compatibility, an idpEntries can also
474
     * be a string instead of an array, where each string
475
     * is mapped to the value of attribute ProviderID.
476
     *
477
     * @param array List of idpEntries to scope the request to.
478
     */
479
    public function setIDPList(array $IDPList)
480
    {
481
        $this->IDPList = $IDPList;
482
    }
483
484
485
    /**
486
     * This function retrieves the list of providerIDs from this authentication request.
487
     * Currently we only support a list of ipd ientity id's.
488
     * @return array List of idp EntityIDs from the request
489
     */
490
    public function getIDPList()
491
    {
492
        return $this->IDPList;
493
    }
494
495
    /**
496
     * @param int $ProxyCount
497
     */
498
    public function setProxyCount($ProxyCount)
499
    {
500
        assert(is_int($ProxyCount));
501
        $this->ProxyCount = $ProxyCount;
502
    }
503
504
    /**
505
     * @return int|null
506
     */
507
    public function getProxyCount()
508
    {
509
        return $this->ProxyCount;
510
    }
511
512
    /**
513
     * @param array $RequesterID
514
     */
515
    public function setRequesterID(array $RequesterID)
516
    {
517
        $this->RequesterID = $RequesterID;
518
    }
519
520
    /**
521
     * @return array
522
     */
523
    public function getRequesterID()
524
    {
525
        return $this->RequesterID;
526
    }
527
528
    /**
529
     * Retrieve the value of the AssertionConsumerServiceURL attribute.
530
     *
531
     * @return string|null The AssertionConsumerServiceURL attribute.
532
     */
533
    public function getAssertionConsumerServiceURL()
534
    {
535
        return $this->assertionConsumerServiceURL;
536
    }
537
538
    /**
539
     * Set the value of the AssertionConsumerServiceURL attribute.
540
     *
541
     * @param string|null $assertionConsumerServiceURL The AssertionConsumerServiceURL attribute.
542
     */
543
    public function setAssertionConsumerServiceURL($assertionConsumerServiceURL)
544
    {
545
        assert(is_string($assertionConsumerServiceURL) || is_null($assertionConsumerServiceURL));
546
547
        $this->assertionConsumerServiceURL = $assertionConsumerServiceURL;
548
    }
549
550
    /**
551
     * Retrieve the value of the ProtocolBinding attribute.
552
     *
553
     * @return string|null The ProtocolBinding attribute.
554
     */
555
    public function getProtocolBinding()
556
    {
557
        return $this->protocolBinding;
558
    }
559
560
    /**
561
     * Set the value of the ProtocolBinding attribute.
562
     *
563
     * @param string $protocolBinding The ProtocolBinding attribute.
564
     */
565
    public function setProtocolBinding($protocolBinding)
566
    {
567
        assert(is_string($protocolBinding) || is_null($protocolBinding));
568
569
        $this->protocolBinding = $protocolBinding;
570
    }
571
572
    /**
573
     * Retrieve the value of the AttributeConsumingServiceIndex attribute.
574
     *
575
     * @return int|null The AttributeConsumingServiceIndex attribute.
576
     */
577
    public function getAttributeConsumingServiceIndex()
578
    {
579
        return $this->attributeConsumingServiceIndex;
580
    }
581
582
    /**
583
     * Set the value of the AttributeConsumingServiceIndex attribute.
584
     *
585
     * @param int|null $attributeConsumingServiceIndex The AttributeConsumingServiceIndex attribute.
586
     */
587
    public function setAttributeConsumingServiceIndex($attributeConsumingServiceIndex)
588
    {
589
        assert(is_int($attributeConsumingServiceIndex) || is_null($attributeConsumingServiceIndex));
590
591
        $this->attributeConsumingServiceIndex = $attributeConsumingServiceIndex;
592
    }
593
594
    /**
595
     * Retrieve the value of the AssertionConsumerServiceIndex attribute.
596
     *
597
     * @return int|null The AssertionConsumerServiceIndex attribute.
598
     */
599
    public function getAssertionConsumerServiceIndex()
600
    {
601
        return $this->assertionConsumerServiceIndex;
602
    }
603
604
    /**
605
     * Set the value of the AssertionConsumerServiceIndex attribute.
606
     *
607
     * @param int|null $assertionConsumerServiceIndex The AssertionConsumerServiceIndex attribute.
608
     */
609
    public function setAssertionConsumerServiceIndex($assertionConsumerServiceIndex)
610
    {
611
        assert(is_int($assertionConsumerServiceIndex) || is_null($assertionConsumerServiceIndex));
612
613
        $this->assertionConsumerServiceIndex = $assertionConsumerServiceIndex;
614
    }
615
616
    /**
617
     * Retrieve the RequestedAuthnContext.
618
     *
619
     * @return array|null The RequestedAuthnContext.
620
     */
621
    public function getRequestedAuthnContext()
622
    {
623
        return $this->requestedAuthnContext;
624
    }
625
626
    /**
627
     * Set the RequestedAuthnContext.
628
     *
629
     * @param array|null $requestedAuthnContext The RequestedAuthnContext.
630
     */
631
    public function setRequestedAuthnContext($requestedAuthnContext)
632
    {
633
        assert(is_array($requestedAuthnContext) || is_null($requestedAuthnContext));
634
635
        $this->requestedAuthnContext = $requestedAuthnContext;
0 ignored issues
show
Documentation Bug introduced by
It seems like $requestedAuthnContext can be null. However, the property $requestedAuthnContext is declared as array. Maybe change the type of the property to array|null or add a type check?

Our type inference engine has found an assignment of a scalar value (like a string, an integer or null) to a property which is an array.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property.

To type hint that a parameter can be either an array or null, you can set a type hint of array and a default value of null. The PHP interpreter will then accept both an array or null for that parameter.

function aContainsB(array $needle = null, array  $haystack) {
    if (!$needle) {
        return false;
    }

    return array_intersect($haystack, $needle) == $haystack;
}

The function can be called with either null or an array for the parameter $needle but will only accept an array as $haystack.

Loading history...
636
    }
637
638
    /**
639
     * Retrieve the NameId of the subject in the assertion.
640
     *
641
     * @return \SAML2\XML\saml\NameID|null The name identifier of the assertion.
642
     * @throws \Exception
643
     */
644
    public function getNameId()
645
    {
646
        if ($this->encryptedNameId !== null) {
647
            throw new \Exception('Attempted to retrieve encrypted NameID without decrypting it first.');
648
        }
649
650
        return $this->nameId;
651
    }
652
653
    /**
654
     * Set the NameId of the subject in the assertion.
655
     *
656
     * @param \SAML2\XML\saml\NameID|null $nameId The name identifier of the assertion.
657
     */
658 View Code Duplication
    public function setNameId($nameId)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
659
    {
660
        assert(is_array($nameId) || is_null($nameId) || $nameId instanceof XML\saml\NameID);
661
662
        if (is_array($nameId)) {
663
            $nameId = XML\saml\NameID::fromArray($nameId);
0 ignored issues
show
Deprecated Code introduced by
The method SAML2\XML\saml\NameIDType::fromArray() has been deprecated.

This method has been deprecated.

Loading history...
664
        }
665
        $this->nameId = $nameId;
666
    }
667
668
    /**
669
     * Encrypt the NameID in the AuthnRequest.
670
     *
671
     * @param XMLSecurityKey $key The encryption key.
672
     */
673 View Code Duplication
    public function encryptNameId(XMLSecurityKey $key)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
674
    {
675
        /* First create a XML representation of the NameID. */
676
        $doc  = new \DOMDocument();
677
        $root = $doc->createElement('root');
678
        $doc->appendChild($root);
679
        $this->nameId->toXML($root);
680
        $nameId = $root->firstChild;
681
682
        Utils::getContainer()->debugMessage($nameId, 'encrypt');
0 ignored issues
show
Documentation introduced by
$nameId is of type object<DOMNode>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
683
684
        /* Encrypt the NameID. */
685
        $enc = new XMLSecEnc();
686
        $enc->setNode($nameId);
687
        // @codingStandardsIgnoreStart
688
        $enc->type = XMLSecEnc::Element;
689
        // @codingStandardsIgnoreEnd
690
691
        $symmetricKey = new XMLSecurityKey(XMLSecurityKey::AES128_CBC);
692
        $symmetricKey->generateSessionKey();
693
        $enc->encryptKey($key, $symmetricKey);
694
695
        $this->encryptedNameId = $enc->encryptNode($symmetricKey);
0 ignored issues
show
Documentation Bug introduced by
It seems like $enc->encryptNode($symmetricKey) can also be of type object<DOMNode>. However, the property $encryptedNameId is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
696
        $this->nameId          = null;
697
    }
698
699
    /**
700
     * Decrypt the NameId of the subject in the assertion.
701
     *
702
     * @param XMLSecurityKey $key       The decryption key.
703
     * @param array          $blacklist Blacklisted decryption algorithms.
704
     */
705 View Code Duplication
    public function decryptNameId(XMLSecurityKey $key, array $blacklist = [])
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
706
    {
707
        if ($this->encryptedNameId === null) {
708
            /* No NameID to decrypt. */
709
            return;
710
        }
711
712
        $nameId = Utils::decryptElement($this->encryptedNameId, $key, $blacklist);
0 ignored issues
show
Documentation introduced by
$this->encryptedNameId is of type string, but the function expects a object<DOMElement>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
713
        Utils::getContainer()->debugMessage($nameId, 'decrypt');
0 ignored issues
show
Documentation introduced by
$nameId is of type object<DOMElement>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
714
        $this->nameId = new XML\saml\NameID($nameId);
715
716
        $this->encryptedNameId = null;
717
    }
718
719
    /**
720
     * Retrieve the SubjectConfirmation elements we have in our Subject element.
721
     *
722
     * @return \SAML2\XML\saml\SubjectConfirmation[]
723
     */
724
    public function getSubjectConfirmation()
725
    {
726
        return $this->subjectConfirmation;
727
    }
728
729
    /**
730
     * Set the SubjectConfirmation elements that should be included in the assertion.
731
     *
732
     * @param array \SAML2\XML\saml\SubjectConfirmation[]
733
     */
734
    public function setSubjectConfirmation(array $subjectConfirmation)
735
    {
736
        $this->subjectConfirmation = $subjectConfirmation;
737
    }
738
739
    /**
740
     * Convert this authentication request to an XML element.
741
     *
742
     * @return \DOMElement This authentication request.
743
     */
744
    public function toUnsignedXML()
745
    {
746
        $root = parent::toUnsignedXML();
747
748
        if ($this->forceAuthn) {
749
            $root->setAttribute('ForceAuthn', 'true');
750
        }
751
752
        if ($this->ProviderName !== null) {
753
            $root->setAttribute('ProviderName', $this->ProviderName);
754
        }
755
756
        if ($this->isPassive) {
757
            $root->setAttribute('IsPassive', 'true');
758
        }
759
760
        if ($this->assertionConsumerServiceIndex !== null) {
761
            $root->setAttribute('AssertionConsumerServiceIndex', $this->assertionConsumerServiceIndex);
762
        } else {
763
            if ($this->assertionConsumerServiceURL !== null) {
764
                $root->setAttribute('AssertionConsumerServiceURL', $this->assertionConsumerServiceURL);
765
            }
766
            if ($this->protocolBinding !== null) {
767
                $root->setAttribute('ProtocolBinding', $this->protocolBinding);
768
            }
769
        }
770
771
        if ($this->attributeConsumingServiceIndex !== null) {
772
            $root->setAttribute('AttributeConsumingServiceIndex', $this->attributeConsumingServiceIndex);
773
        }
774
775
        $this->addSubject($root);
776
777
        if (!empty($this->nameIdPolicy)) {
778
            $nameIdPolicy = $this->document->createElementNS(Constants::NS_SAMLP, 'NameIDPolicy');
779
            if (array_key_exists('Format', $this->nameIdPolicy)) {
780
                $nameIdPolicy->setAttribute('Format', $this->nameIdPolicy['Format']);
781
            }
782
            if (array_key_exists('SPNameQualifier', $this->nameIdPolicy)) {
783
                $nameIdPolicy->setAttribute('SPNameQualifier', $this->nameIdPolicy['SPNameQualifier']);
784
            }
785
            if (array_key_exists('AllowCreate', $this->nameIdPolicy)) {
786
                $nameIdPolicy->setAttribute('AllowCreate', ($this->nameIdPolicy['AllowCreate']) ? 'true' : 'false');
787
            }
788
            $root->appendChild($nameIdPolicy);
789
        }
790
791
        $this->addConditions($root);
792
793
        $rac = $this->requestedAuthnContext;
794
        if (!empty($rac) && !empty($rac['AuthnContextClassRef'])) {
795
            $e = $this->document->createElementNS(Constants::NS_SAMLP, 'RequestedAuthnContext');
796
            $root->appendChild($e);
797
            if (isset($rac['Comparison']) && $rac['Comparison'] !== Constants::COMPARISON_EXACT) {
798
                $e->setAttribute('Comparison', $rac['Comparison']);
799
            }
800
            foreach ($rac['AuthnContextClassRef'] as $accr) {
801
                Utils::addString($e, Constants::NS_SAML, 'AuthnContextClassRef', $accr);
802
            }
803
        }
804
805
        if ($this->ProxyCount !== null || count($this->IDPList) > 0 || count($this->RequesterID) > 0) {
806
            $scoping = $this->document->createElementNS(Constants::NS_SAMLP, 'Scoping');
807
            $root->appendChild($scoping);
808
            if ($this->ProxyCount !== null) {
809
                $scoping->setAttribute('ProxyCount', $this->ProxyCount);
810
            }
811
            if (count($this->IDPList) > 0) {
812
                $idplist = $this->document->createElementNS(Constants::NS_SAMLP, 'IDPList');
813
                foreach ($this->IDPList as $provider) {
814
                    $idpEntry = $this->document->createElementNS(Constants::NS_SAMLP, 'IDPEntry');
815
                    if (is_string($provider)) {
816
                        $idpEntry->setAttribute('ProviderID', $provider);
817
                    } elseif (is_array($provider)) {
818
                        foreach ($provider as $attribute => $value) {
819
                            if (in_array($attribute, [
820
                                'ProviderID',
821
                                'Loc',
822
                                'Name'
823
                            ], true)) {
824
                                $idpEntry->setAttribute($attribute, $value);
825
                            }
826
                        }
827
                    }
828
                    $idplist->appendChild($idpEntry);
829
                }
830
                $scoping->appendChild($idplist);
831
            }
832
            if (count($this->RequesterID) > 0) {
833
                Utils::addStrings($scoping, Constants::NS_SAMLP, 'RequesterID', false, $this->RequesterID);
834
            }
835
        }
836
837
        return $root;
838
    }
839
840
    /**
841
     * Add a Subject-node to the assertion.
842
     *
843
     * @param \DOMElement $root The assertion element we should add the subject to.
844
     */
845 View Code Duplication
    private function addSubject(\DOMElement $root)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
846
    {
847
        // If there is no nameId (encrypted or not) there is nothing to create a subject for
848
        if ($this->nameId === null && $this->encryptedNameId === null) {
849
            return;
850
        }
851
852
        $subject = $root->ownerDocument->createElementNS(Constants::NS_SAML, 'saml:Subject');
853
        $root->appendChild($subject);
854
855
        if ($this->encryptedNameId === null) {
856
            $this->nameId->toXML($subject);
857
        } else {
858
            $eid = $subject->ownerDocument->createElementNS(Constants::NS_SAML, 'saml:EncryptedID');
859
            $eid->appendChild($subject->ownerDocument->importNode($this->encryptedNameId, true));
860
            $subject->appendChild($eid);
861
        }
862
863
        foreach ($this->subjectConfirmation as $sc) {
864
            $sc->toXML($subject);
865
        }
866
    }
867
868
    /**
869
     * Add a Conditions-node to the request.
870
     *
871
     * @param \DOMElement $root The request element we should add the conditions to.
872
     */
873
    private function addConditions(\DOMElement $root)
874
    {
875
        if ($this->audiences !== null) {
876
            $document = $root->ownerDocument;
877
878
            $conditions = $document->createElementNS(Constants::NS_SAML, 'saml:Conditions');
879
            $root->appendChild($conditions);
880
881
            $ar = $document->createElementNS(Constants::NS_SAML, 'saml:AudienceRestriction');
882
            $conditions->appendChild($ar);
883
884
            Utils::addStrings($ar, Constants::NS_SAML, 'saml:Audience', false, $this->audiences);
885
        }
886
    }
887
}
888