Completed
Push — master ( 8bbc6f...d6db6c )
by Thijs
02:45
created

AuthnRequest::parseNameIdPolicy()   B

Complexity

Conditions 5
Paths 9

Size

Total Lines 18
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 11
nc 9
nop 1
dl 0
loc 18
rs 8.8571
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 = array();
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 = array();
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
     * @var \SAML2\XML\saml\SubjectConfirmation[]
113
     */
114
    private $subjectConfirmation = array();
115
116
    /**
117
     * @var string
118
     */
119
    private $encryptedNameId;
120
121
    /**
122
     * @var string
123
     */
124
    private $nameId;
125
126
    /**
127
     * Constructor for SAML 2 authentication request messages.
128
     *
129
     * @param \DOMElement|null $xml The input message.
130
     * @throws \Exception
131
     */
132
    public function __construct(\DOMElement $xml = null)
133
    {
134
        parent::__construct('AuthnRequest', $xml);
135
136
        $this->nameIdPolicy = array();
137
        $this->forceAuthn = false;
138
        $this->isPassive = false;
139
140
        if ($xml === null) {
141
            return;
142
        }
143
144
        $this->forceAuthn = Utils::parseBoolean($xml, 'ForceAuthn', false);
145
        $this->isPassive = Utils::parseBoolean($xml, 'IsPassive', false);
146
147
        if ($xml->hasAttribute('AssertionConsumerServiceURL')) {
148
            $this->assertionConsumerServiceURL = $xml->getAttribute('AssertionConsumerServiceURL');
149
        }
150
151
        if ($xml->hasAttribute('ProtocolBinding')) {
152
            $this->protocolBinding = $xml->getAttribute('ProtocolBinding');
153
        }
154
155
        if ($xml->hasAttribute('AttributeConsumingServiceIndex')) {
156
            $this->attributeConsumingServiceIndex = (int) $xml->getAttribute('AttributeConsumingServiceIndex');
157
        }
158
159
        if ($xml->hasAttribute('AssertionConsumerServiceIndex')) {
160
            $this->assertionConsumerServiceIndex = (int) $xml->getAttribute('AssertionConsumerServiceIndex');
161
        }
162
163
        if ($xml->hasAttribute('ProviderName')) {
164
            $this->ProviderName = $xml->getAttribute('ProviderName');
165
        }
166
167
        $this->parseSubject($xml);
168
        $this->parseNameIdPolicy($xml);
169
        $this->parseRequestedAuthnContext($xml);
170
        $this->parseScoping($xml);
171
    }
172
173
    /**
174
     * @param $xml
175
     *
176
     * @throws \Exception
177
     */
178
    private function parseSubject(\DOMElement $xml)
179
    {
180
        $subject = Utils::xpQuery($xml, './saml_assertion:Subject');
181
        if (empty($subject)) {
182
            return;
183
        }
184
185
        if (count($subject) > 1) {
186
            throw new \Exception('More than one <saml:Subject> in <saml:AuthnRequest>.');
187
        }
188
        $subject = $subject[0];
189
190
        $nameId = Utils::xpQuery(
191
            $subject,
192
            './saml_assertion:NameID | ./saml_assertion:EncryptedID/xenc:EncryptedData'
193
        );
194
        if (empty($nameId)) {
195
            throw new \Exception('Missing <saml:NameID> or <saml:EncryptedID> in <saml:Subject>.');
196
        } elseif (count($nameId) > 1) {
197
            throw new \Exception('More than one <saml:NameID> or <saml:EncryptedID> in <saml:Subject>.');
198
        }
199
        $nameId = $nameId[0];
200
        if ($nameId->localName === 'EncryptedData') {
201
            /* The NameID element is encrypted. */
202
            $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...
203
        } else {
204
            $this->nameId = Utils::parseNameId($nameId);
0 ignored issues
show
Documentation Bug introduced by
It seems like \SAML2\Utils::parseNameId($nameId) of type array<string,string> is incompatible with the declared type string of property $nameId.

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...
205
        }
206
207
        $subjectConfirmation = Utils::xpQuery($subject, './saml_assertion:SubjectConfirmation');
208
        foreach ($subjectConfirmation as $sc) {
209
            $this->subjectConfirmation[] = new SubjectConfirmation($sc);
210
        }
211
    }
212
213
    /**
214
     * @param \DOMElement $xml
215
     *
216
     * @throws \Exception
217
     */
218
    protected function parseNameIdPolicy(\DOMElement $xml)
219
    {
220
        $nameIdPolicy = Utils::xpQuery($xml, './saml_protocol:NameIDPolicy');
221
        if (empty($nameIdPolicy)) {
222
            return;
223
        }
224
225
        $nameIdPolicy = $nameIdPolicy[0];
226
        if ($nameIdPolicy->hasAttribute('Format')) {
227
            $this->nameIdPolicy['Format'] = $nameIdPolicy->getAttribute('Format');
228
        }
229
        if ($nameIdPolicy->hasAttribute('SPNameQualifier')) {
230
            $this->nameIdPolicy['SPNameQualifier'] = $nameIdPolicy->getAttribute('SPNameQualifier');
231
        }
232
        if ($nameIdPolicy->hasAttribute('AllowCreate')) {
233
            $this->nameIdPolicy['AllowCreate'] = Utils::parseBoolean($nameIdPolicy, 'AllowCreate', false);
234
        }
235
    }
236
237
    /**
238
     * @param \DOMElement $xml
239
     */
240
    protected function parseRequestedAuthnContext(\DOMElement $xml)
241
    {
242
        $requestedAuthnContext = Utils::xpQuery($xml, './saml_protocol:RequestedAuthnContext');
243
        if (empty($requestedAuthnContext)) {
244
            return;
245
        }
246
247
        $requestedAuthnContext = $requestedAuthnContext[0];
248
249
        $rac = array(
250
            'AuthnContextClassRef' => array(),
251
            'Comparison'           => Constants::COMPARISON_EXACT,
252
        );
253
254
        $accr = Utils::xpQuery($requestedAuthnContext, './saml_assertion:AuthnContextClassRef');
255
        foreach ($accr as $i) {
256
            $rac['AuthnContextClassRef'][] = trim($i->textContent);
257
        }
258
259
        if ($requestedAuthnContext->hasAttribute('Comparison')) {
260
            $rac['Comparison'] = $requestedAuthnContext->getAttribute('Comparison');
261
        }
262
263
        $this->requestedAuthnContext = $rac;
264
    }
265
266
    /**
267
     * @param \DOMElement $xml
268
     *
269
     * @throws \Exception
270
     */
271
    protected function parseScoping(\DOMElement $xml)
272
    {
273
        $scoping = Utils::xpQuery($xml, './saml_protocol:Scoping');
274
        if (empty($scoping)) {
275
            return;
276
        }
277
278
        $scoping = $scoping[0];
279
280
        if ($scoping->hasAttribute('ProxyCount')) {
281
            $this->ProxyCount = (int) $scoping->getAttribute('ProxyCount');
282
        }
283
        $idpEntries = Utils::xpQuery($scoping, './saml_protocol:IDPList/saml_protocol:IDPEntry');
284
285
        foreach ($idpEntries as $idpEntry) {
286
            if (!$idpEntry->hasAttribute('ProviderID')) {
287
                throw new \Exception("Could not get ProviderID from Scoping/IDPEntry element in AuthnRequest object");
288
            }
289
            $this->IDPList[] = $idpEntry->getAttribute('ProviderID');
290
        }
291
292
        $requesterIDs = Utils::xpQuery($scoping, './saml_protocol:RequesterID');
293
        foreach ($requesterIDs as $requesterID) {
294
            $this->RequesterID[] = trim($requesterID->textContent);
295
        }
296
    }
297
298
    /**
299
     * Retrieve the NameIdPolicy.
300
     *
301
     * @see \SAML2\AuthnRequest::setNameIdPolicy()
302
     * @return array The NameIdPolicy.
303
     */
304
    public function getNameIdPolicy()
305
    {
306
        return $this->nameIdPolicy;
307
    }
308
309
310
    /**
311
     * Set the NameIDPolicy.
312
     *
313
     * This function accepts an array with the following options:
314
     *  - 'Format' (string)
315
     *  - 'SPNameQualifier' (string)
316
     *  - 'AllowCreate' (bool)
317
     *
318
     * @param array $nameIdPolicy The NameIDPolicy.
319
     */
320
    public function setNameIdPolicy(array $nameIdPolicy)
321
    {
322 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...
323
            throw InvalidArgumentException::invalidType('string', $nameIdPolicy['Format']);
324
        }
325 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...
326
            throw InvalidArgumentException::invalidType('string', $nameIdPolicy['SPNameQualifier']);
327
        }
328
        if (isset($nameIdPolicy['AllowCreate']) && !is_bool($nameIdPolicy['AllowCreate'])) {
329
            throw InvalidArgumentException::invalidType('bool', $nameIdPolicy['AllowCreate']);
330
        }
331
332
        $this->nameIdPolicy = $nameIdPolicy;
333
    }
334
335
336
    /**
337
     * Retrieve the value of the ForceAuthn attribute.
338
     *
339
     * @return bool The ForceAuthn attribute.
340
     */
341
    public function getForceAuthn()
342
    {
343
        return $this->forceAuthn;
344
    }
345
346
347
    /**
348
     * Set the value of the ForceAuthn attribute.
349
     *
350
     * @param bool $forceAuthn The ForceAuthn attribute.
351
     */
352
    public function setForceAuthn($forceAuthn)
353
    {
354
        assert('is_bool($forceAuthn)');
355
356
        $this->forceAuthn = $forceAuthn;
357
    }
358
359
360
    /**
361
     * Retrieve the value of the ProviderName attribute.
362
     *
363
     * @return string The ProviderName attribute.
364
     */
365
    public function getProviderName()
366
    {
367
        return $this->ProviderName;
368
    }
369
370
371
    /**
372
     * Set the value of the ProviderName attribute.
373
     *
374
     * @param string $ProviderName The ProviderName attribute.
375
     */
376
    public function setProviderName($ProviderName)
377
    {
378
        assert('is_string($ProviderName)');
379
380
        $this->ProviderName = $ProviderName;
381
    }
382
383
384
    /**
385
     * Retrieve the value of the IsPassive attribute.
386
     *
387
     * @return bool The IsPassive attribute.
388
     */
389
    public function getIsPassive()
390
    {
391
        return $this->isPassive;
392
    }
393
394
395
    /**
396
     * Set the value of the IsPassive attribute.
397
     *
398
     * @param bool $isPassive The IsPassive attribute.
399
     */
400
    public function setIsPassive($isPassive)
401
    {
402
        assert('is_bool($isPassive)');
403
404
        $this->isPassive = $isPassive;
405
    }
406
407
408
    /**
409
     * This function sets the scoping for the request.
410
     * See Core 3.4.1.2 for the definition of scoping.
411
     * Currently we support an IDPList of idpEntries.
412
     *
413
     * Each idpEntries consists of an array, containing
414
     * keys (mapped to attributes) and corresponding values.
415
     * Allowed attributes: Loc, Name, ProviderID.
416
     *
417
     * For backward compatibility, an idpEntries can also
418
     * be a string instead of an array, where each string
419
     * is mapped to the value of attribute ProviderID.
420
     */
421
    public function setIDPList($IDPList)
422
    {
423
        assert('is_array($IDPList)');
424
        $this->IDPList = $IDPList;
425
    }
426
427
428
    /**
429
     * This function retrieves the list of providerIDs from this authentication request.
430
     * Currently we only support a list of ipd ientity id's.
431
     * @return array List of idp EntityIDs from the request
432
     */
433
    public function getIDPList()
434
    {
435
        return $this->IDPList;
436
    }
437
438
    /**
439
     * @param int $ProxyCount
440
     */
441
    public function setProxyCount($ProxyCount)
442
    {
443
        assert('is_int($ProxyCount)');
444
        $this->ProxyCount = $ProxyCount;
445
    }
446
447
    /**
448
     * @return int
449
     */
450
    public function getProxyCount()
451
    {
452
        return $this->ProxyCount;
453
    }
454
455
    /**
456
     * @param array $RequesterID
457
     */
458
    public function setRequesterID(array $RequesterID)
459
    {
460
        $this->RequesterID = $RequesterID;
461
    }
462
463
    /**
464
     * @return array
465
     */
466
    public function getRequesterID()
467
    {
468
        return $this->RequesterID;
469
    }
470
471
    /**
472
     * Retrieve the value of the AssertionConsumerServiceURL attribute.
473
     *
474
     * @return string|null The AssertionConsumerServiceURL attribute.
475
     */
476
    public function getAssertionConsumerServiceURL()
477
    {
478
        return $this->assertionConsumerServiceURL;
479
    }
480
481
    /**
482
     * Set the value of the AssertionConsumerServiceURL attribute.
483
     *
484
     * @param string|null $assertionConsumerServiceURL The AssertionConsumerServiceURL attribute.
485
     */
486
    public function setAssertionConsumerServiceURL($assertionConsumerServiceURL)
487
    {
488
        assert('is_string($assertionConsumerServiceURL) || is_null($assertionConsumerServiceURL)');
489
490
        $this->assertionConsumerServiceURL = $assertionConsumerServiceURL;
491
    }
492
493
    /**
494
     * Retrieve the value of the ProtocolBinding attribute.
495
     *
496
     * @return string|null The ProtocolBinding attribute.
497
     */
498
    public function getProtocolBinding()
499
    {
500
        return $this->protocolBinding;
501
    }
502
503
    /**
504
     * Set the value of the ProtocolBinding attribute.
505
     *
506
     * @param string $protocolBinding The ProtocolBinding attribute.
507
     */
508
    public function setProtocolBinding($protocolBinding)
509
    {
510
        assert('is_string($protocolBinding) || is_null($protocolBinding)');
511
512
        $this->protocolBinding = $protocolBinding;
513
    }
514
515
    /**
516
     * Retrieve the value of the AttributeConsumingServiceIndex attribute.
517
     *
518
     * @return int|null The AttributeConsumingServiceIndex attribute.
519
     */
520
    public function getAttributeConsumingServiceIndex()
521
    {
522
        return $this->attributeConsumingServiceIndex;
523
    }
524
525
    /**
526
     * Set the value of the AttributeConsumingServiceIndex attribute.
527
     *
528
     * @param int|null $attributeConsumingServiceIndex The AttributeConsumingServiceIndex attribute.
529
     */
530
    public function setAttributeConsumingServiceIndex($attributeConsumingServiceIndex)
531
    {
532
        assert('is_int($attributeConsumingServiceIndex) || is_null($attributeConsumingServiceIndex)');
533
534
        $this->attributeConsumingServiceIndex = $attributeConsumingServiceIndex;
535
    }
536
537
    /**
538
     * Retrieve the value of the AssertionConsumerServiceIndex attribute.
539
     *
540
     * @return int|null The AssertionConsumerServiceIndex attribute.
541
     */
542
    public function getAssertionConsumerServiceIndex()
543
    {
544
        return $this->assertionConsumerServiceIndex;
545
    }
546
547
    /**
548
     * Set the value of the AssertionConsumerServiceIndex attribute.
549
     *
550
     * @param int|null $assertionConsumerServiceIndex The AssertionConsumerServiceIndex attribute.
551
     */
552
    public function setAssertionConsumerServiceIndex($assertionConsumerServiceIndex)
553
    {
554
        assert('is_int($assertionConsumerServiceIndex) || is_null($assertionConsumerServiceIndex)');
555
556
        $this->assertionConsumerServiceIndex = $assertionConsumerServiceIndex;
557
    }
558
559
    /**
560
     * Retrieve the RequestedAuthnContext.
561
     *
562
     * @return array|null The RequestedAuthnContext.
563
     */
564
    public function getRequestedAuthnContext()
565
    {
566
        return $this->requestedAuthnContext;
567
    }
568
569
    /**
570
     * Set the RequestedAuthnContext.
571
     *
572
     * @param array|null $requestedAuthnContext The RequestedAuthnContext.
573
     */
574
    public function setRequestedAuthnContext($requestedAuthnContext)
575
    {
576
        assert('is_array($requestedAuthnContext) || is_null($requestedAuthnContext)');
577
578
        $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...
579
    }
580
581
    /**
582
     * Retrieve the NameId of the subject in the assertion.
583
     *
584
     * The returned NameId is in the format used by \SAML2\Utils::addNameId().
585
     *
586
     * @see \SAML2\Utils::addNameId()
587
     * @return array|null The name identifier of the assertion.
588
     * @throws \Exception
589
     */
590
    public function getNameId()
591
    {
592
        if ($this->encryptedNameId !== null) {
593
            throw new \Exception('Attempted to retrieve encrypted NameID without decrypting it first.');
594
        }
595
596
        return $this->nameId;
597
    }
598
599
    /**
600
     * Set the NameId of the subject in the assertion.
601
     *
602
     * The NameId must be in the format accepted by \SAML2\Utils::addNameId().
603
     *
604
     * @see \SAML2\Utils::addNameId()
605
     *
606
     * @param array|null $nameId The name identifier of the assertion.
607
     */
608
    public function setNameId($nameId)
609
    {
610
        assert('is_array($nameId) || is_null($nameId)');
611
612
        $this->nameId = $nameId;
0 ignored issues
show
Documentation Bug introduced by
It seems like $nameId can also be of type array. However, the property $nameId 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...
613
    }
614
615
    /**
616
     * Encrypt the NameID in the AuthnRequest.
617
     *
618
     * @param XMLSecurityKey $key The encryption key.
619
     */
620 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...
621
    {
622
        /* First create a XML representation of the NameID. */
623
        $doc  = new \DOMDocument();
624
        $root = $doc->createElement('root');
625
        $doc->appendChild($root);
626
        Utils::addNameId($root, $this->nameId);
0 ignored issues
show
Documentation introduced by
$this->nameId is of type string, but the function expects a array.

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...
627
        $nameId = $root->firstChild;
628
629
        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...
630
631
        /* Encrypt the NameID. */
632
        $enc = new XMLSecEnc();
633
        $enc->setNode($nameId);
634
        // @codingStandardsIgnoreStart
635
        $enc->type = XMLSecEnc::Element;
636
        // @codingStandardsIgnoreEnd
637
638
        $symmetricKey = new XMLSecurityKey(XMLSecurityKey::AES128_CBC);
639
        $symmetricKey->generateSessionKey();
640
        $enc->encryptKey($key, $symmetricKey);
641
642
        $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...
643
        $this->nameId          = null;
644
    }
645
646
    /**
647
     * Decrypt the NameId of the subject in the assertion.
648
     *
649
     * @param XMLSecurityKey $key       The decryption key.
650
     * @param array          $blacklist Blacklisted decryption algorithms.
651
     */
652 View Code Duplication
    public function decryptNameId(XMLSecurityKey $key, array $blacklist = array())
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...
653
    {
654
        if ($this->encryptedNameId === null) {
655
            /* No NameID to decrypt. */
656
            return;
657
        }
658
659
        $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...
660
        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...
661
        $this->nameId = Utils::parseNameId($nameId);
0 ignored issues
show
Documentation Bug introduced by
It seems like \SAML2\Utils::parseNameId($nameId) of type array<string,string> is incompatible with the declared type string of property $nameId.

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...
662
663
        $this->encryptedNameId = null;
664
    }
665
666
    /**
667
     * Retrieve the SubjectConfirmation elements we have in our Subject element.
668
     *
669
     * @return \SAML2\XML\saml\SubjectConfirmation[]
670
     */
671
    public function getSubjectConfirmation()
672
    {
673
        return $this->subjectConfirmation;
674
    }
675
676
    /**
677
     * Set the SubjectConfirmation elements that should be included in the assertion.
678
     *
679
     * @param array \SAML2\XML\saml\SubjectConfirmation[]
680
     */
681
    public function setSubjectConfirmation(array $subjectConfirmation)
682
    {
683
        $this->subjectConfirmation = $subjectConfirmation;
684
    }
685
686
    /**
687
     * Convert this authentication request to an XML element.
688
     *
689
     * @return \DOMElement This authentication request.
690
     */
691
    public function toUnsignedXML()
692
    {
693
        $root = parent::toUnsignedXML();
694
695
        if ($this->forceAuthn) {
696
            $root->setAttribute('ForceAuthn', 'true');
697
        }
698
699
        if ($this->ProviderName !== null) {
700
            $root->setAttribute('ProviderName', $this->ProviderName);
701
        }
702
703
        if ($this->isPassive) {
704
            $root->setAttribute('IsPassive', 'true');
705
        }
706
707
        if ($this->assertionConsumerServiceIndex !== null) {
708
            $root->setAttribute('AssertionConsumerServiceIndex', $this->assertionConsumerServiceIndex);
709
        } else {
710
            if ($this->assertionConsumerServiceURL !== null) {
711
                $root->setAttribute('AssertionConsumerServiceURL', $this->assertionConsumerServiceURL);
712
            }
713
            if ($this->protocolBinding !== null) {
714
                $root->setAttribute('ProtocolBinding', $this->protocolBinding);
715
            }
716
        }
717
718
        if ($this->attributeConsumingServiceIndex !== null) {
719
            $root->setAttribute('AttributeConsumingServiceIndex', $this->attributeConsumingServiceIndex);
720
        }
721
722
        $this->addSubject($root);
723
724
        if (!empty($this->nameIdPolicy)) {
725
            $nameIdPolicy = $this->document->createElementNS(Constants::NS_SAMLP, 'NameIDPolicy');
726
            if (array_key_exists('Format', $this->nameIdPolicy)) {
727
                $nameIdPolicy->setAttribute('Format', $this->nameIdPolicy['Format']);
728
            }
729
            if (array_key_exists('SPNameQualifier', $this->nameIdPolicy)) {
730
                $nameIdPolicy->setAttribute('SPNameQualifier', $this->nameIdPolicy['SPNameQualifier']);
731
            }
732
            if (array_key_exists('AllowCreate', $this->nameIdPolicy)) {
733
                $nameIdPolicy->setAttribute('AllowCreate', ($this->nameIdPolicy['AllowCreate']) ? 'true' : 'false');
734
            }
735
            $root->appendChild($nameIdPolicy);
736
        }
737
738
        $rac = $this->requestedAuthnContext;
739
        if (!empty($rac) && !empty($rac['AuthnContextClassRef'])) {
740
            $e = $this->document->createElementNS(Constants::NS_SAMLP, 'RequestedAuthnContext');
741
            $root->appendChild($e);
742
            if (isset($rac['Comparison']) && $rac['Comparison'] !== Constants::COMPARISON_EXACT) {
743
                $e->setAttribute('Comparison', $rac['Comparison']);
744
            }
745
            foreach ($rac['AuthnContextClassRef'] as $accr) {
746
                Utils::addString($e, Constants::NS_SAML, 'AuthnContextClassRef', $accr);
747
            }
748
        }
749
750
        if ($this->ProxyCount !== null || count($this->IDPList) > 0 || count($this->RequesterID) > 0) {
751
            $scoping = $this->document->createElementNS(Constants::NS_SAMLP, 'Scoping');
752
            $root->appendChild($scoping);
753
            if ($this->ProxyCount !== null) {
754
                $scoping->setAttribute('ProxyCount', $this->ProxyCount);
755
            }
756
            if (count($this->IDPList) > 0) {
757
                $idplist = $this->document->createElementNS(Constants::NS_SAMLP, 'IDPList');
758
                foreach ($this->IDPList as $provider) {
759
                    $idpEntry = $this->document->createElementNS(Constants::NS_SAMLP, 'IDPEntry');
760
                    if (is_string($provider)) {
761
                        $idpEntry->setAttribute('ProviderID', $provider);
762
                    } elseif (is_array($provider)) {
763
                        foreach ($provider as $attribute => $value) {
764
                            if (in_array($attribute, array(
765
                                'ProviderID',
766
                                'Loc',
767
                                'Name'
768
                            ))) {
769
                                $idpEntry->setAttribute($attribute, $value);
770
                            }
771
                        }
772
                    }
773
                    $idplist->appendChild($idpEntry);
774
                }
775
                $scoping->appendChild($idplist);
776
            }
777
            if (count($this->RequesterID) > 0) {
778
                Utils::addStrings($scoping, Constants::NS_SAMLP, 'RequesterID', false, $this->RequesterID);
779
            }
780
        }
781
782
        return $root;
783
    }
784
785
    /**
786
     * Add a Subject-node to the assertion.
787
     *
788
     * @param \DOMElement $root The assertion element we should add the subject to.
789
     */
790 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...
791
    {
792
        // If there is no nameId (encrypted or not) there is nothing to create a subject for
793
        if ($this->nameId === null && $this->encryptedNameId === null) {
794
            return;
795
        }
796
797
        $subject = $root->ownerDocument->createElementNS(Constants::NS_SAML, 'saml:Subject');
798
        $root->appendChild($subject);
799
800
        if ($this->encryptedNameId === null) {
801
            Utils::addNameId($subject, $this->nameId);
0 ignored issues
show
Documentation introduced by
$this->nameId is of type string, but the function expects a array.

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...
802
        } else {
803
            $eid = $subject->ownerDocument->createElementNS(Constants::NS_SAML, 'saml:EncryptedID');
804
            $eid->appendChild($subject->ownerDocument->importNode($this->encryptedNameId, true));
805
            $subject->appendChild($eid);
806
        }
807
808
        foreach ($this->subjectConfirmation as $sc) {
809
            $sc->toXML($subject);
810
        }
811
    }
812
}
813