Completed
Push — master ( 250ad9...8bbc6f )
by Thijs
10s
created

AuthnRequest::setNameIdPolicy()   B

Complexity

Conditions 7
Paths 4

Size

Total Lines 14
Code Lines 8

Duplication

Lines 6
Ratio 42.86 %

Importance

Changes 0
Metric Value
cc 7
eloc 8
nc 4
nop 1
dl 6
loc 14
rs 8.2222
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
        $this->parseSubject($xml);
164
        $this->parseNameIdPolicy($xml);
165
        $this->parseRequestedAuthnContext($xml);
166
        $this->parseScoping($xml);
167
    }
168
169
    /**
170
     * @param $xml
171
     *
172
     * @throws \Exception
173
     */
174
    private function parseSubject(\DOMElement $xml)
175
    {
176
        $subject = Utils::xpQuery($xml, './saml_assertion:Subject');
177
        if (empty($subject)) {
178
            return;
179
        }
180
181
        if (count($subject) > 1) {
182
            throw new \Exception('More than one <saml:Subject> in <saml:AuthnRequest>.');
183
        }
184
        $subject = $subject[0];
185
186
        $nameId = Utils::xpQuery(
187
            $subject,
188
            './saml_assertion:NameID | ./saml_assertion:EncryptedID/xenc:EncryptedData'
189
        );
190
        if (empty($nameId)) {
191
            throw new \Exception('Missing <saml:NameID> or <saml:EncryptedID> in <saml:Subject>.');
192
        } elseif (count($nameId) > 1) {
193
            throw new \Exception('More than one <saml:NameID> or <saml:EncryptedID> in <saml:Subject>.');
194
        }
195
        $nameId = $nameId[0];
196
        if ($nameId->localName === 'EncryptedData') {
197
            /* The NameID element is encrypted. */
198
            $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...
199
        } else {
200
            $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...
201
        }
202
203
        $subjectConfirmation = Utils::xpQuery($subject, './saml_assertion:SubjectConfirmation');
204
        foreach ($subjectConfirmation as $sc) {
205
            $this->subjectConfirmation[] = new SubjectConfirmation($sc);
206
        }
207
    }
208
209
    /**
210
     * @param \DOMElement $xml
211
     *
212
     * @throws \Exception
213
     */
214
    protected function parseNameIdPolicy(\DOMElement $xml)
215
    {
216
        $nameIdPolicy = Utils::xpQuery($xml, './saml_protocol:NameIDPolicy');
217
        if (empty($nameIdPolicy)) {
218
            return;
219
        }
220
221
        $nameIdPolicy = $nameIdPolicy[0];
222
        if ($nameIdPolicy->hasAttribute('Format')) {
223
            $this->nameIdPolicy['Format'] = $nameIdPolicy->getAttribute('Format');
224
        }
225
        if ($nameIdPolicy->hasAttribute('SPNameQualifier')) {
226
            $this->nameIdPolicy['SPNameQualifier'] = $nameIdPolicy->getAttribute('SPNameQualifier');
227
        }
228
        if ($nameIdPolicy->hasAttribute('AllowCreate')) {
229
            $this->nameIdPolicy['AllowCreate'] = Utils::parseBoolean($nameIdPolicy, 'AllowCreate', false);
230
        }
231
    }
232
233
    /**
234
     * @param \DOMElement $xml
235
     */
236
    protected function parseRequestedAuthnContext(\DOMElement $xml)
237
    {
238
        $requestedAuthnContext = Utils::xpQuery($xml, './saml_protocol:RequestedAuthnContext');
239
        if (empty($requestedAuthnContext)) {
240
            return;
241
        }
242
243
        $requestedAuthnContext = $requestedAuthnContext[0];
244
245
        $rac = array(
246
            'AuthnContextClassRef' => array(),
247
            'Comparison'           => Constants::COMPARISON_EXACT,
248
        );
249
250
        $accr = Utils::xpQuery($requestedAuthnContext, './saml_assertion:AuthnContextClassRef');
251
        foreach ($accr as $i) {
252
            $rac['AuthnContextClassRef'][] = trim($i->textContent);
253
        }
254
255
        if ($requestedAuthnContext->hasAttribute('Comparison')) {
256
            $rac['Comparison'] = $requestedAuthnContext->getAttribute('Comparison');
257
        }
258
259
        $this->requestedAuthnContext = $rac;
260
    }
261
262
    /**
263
     * @param \DOMElement $xml
264
     *
265
     * @throws \Exception
266
     */
267
    protected function parseScoping(\DOMElement $xml)
268
    {
269
        $scoping = Utils::xpQuery($xml, './saml_protocol:Scoping');
270
        if (empty($scoping)) {
271
            return;
272
        }
273
274
        $scoping = $scoping[0];
275
276
        if ($scoping->hasAttribute('ProxyCount')) {
277
            $this->ProxyCount = (int) $scoping->getAttribute('ProxyCount');
278
        }
279
        $idpEntries = Utils::xpQuery($scoping, './saml_protocol:IDPList/saml_protocol:IDPEntry');
280
281
        foreach ($idpEntries as $idpEntry) {
282
            if (!$idpEntry->hasAttribute('ProviderID')) {
283
                throw new \Exception("Could not get ProviderID from Scoping/IDPEntry element in AuthnRequest object");
284
            }
285
            $this->IDPList[] = $idpEntry->getAttribute('ProviderID');
286
        }
287
288
        $requesterIDs = Utils::xpQuery($scoping, './saml_protocol:RequesterID');
289
        foreach ($requesterIDs as $requesterID) {
290
            $this->RequesterID[] = trim($requesterID->textContent);
291
        }
292
    }
293
294
    /**
295
     * Retrieve the NameIdPolicy.
296
     *
297
     * @see \SAML2\AuthnRequest::setNameIdPolicy()
298
     * @return array The NameIdPolicy.
299
     */
300
    public function getNameIdPolicy()
301
    {
302
        return $this->nameIdPolicy;
303
    }
304
305
306
    /**
307
     * Set the NameIDPolicy.
308
     *
309
     * This function accepts an array with the following options:
310
     *  - 'Format' (string)
311
     *  - 'SPNameQualifier' (string)
312
     *  - 'AllowCreate' (bool)
313
     *
314
     * @param array $nameIdPolicy The NameIDPolicy.
315
     */
316
    public function setNameIdPolicy(array $nameIdPolicy)
317
    {
318 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...
319
            throw InvalidArgumentException::invalidType('string', $nameIdPolicy['Format']);
320
        }
321 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...
322
            throw InvalidArgumentException::invalidType('string', $nameIdPolicy['SPNameQualifier']);
323
        }
324
        if (isset($nameIdPolicy['AllowCreate']) && !is_bool($nameIdPolicy['AllowCreate'])) {
325
            throw InvalidArgumentException::invalidType('bool', $nameIdPolicy['AllowCreate']);
326
        }
327
328
        $this->nameIdPolicy = $nameIdPolicy;
329
    }
330
331
332
    /**
333
     * Retrieve the value of the ForceAuthn attribute.
334
     *
335
     * @return bool The ForceAuthn attribute.
336
     */
337
    public function getForceAuthn()
338
    {
339
        return $this->forceAuthn;
340
    }
341
342
343
    /**
344
     * Set the value of the ForceAuthn attribute.
345
     *
346
     * @param bool $forceAuthn The ForceAuthn attribute.
347
     */
348
    public function setForceAuthn($forceAuthn)
349
    {
350
        assert('is_bool($forceAuthn)');
351
352
        $this->forceAuthn = $forceAuthn;
353
    }
354
355
356
    /**
357
     * Retrieve the value of the ProviderName attribute.
358
     *
359
     * @return string The ProviderName attribute.
360
     */
361
    public function getProviderName()
362
    {
363
        return $this->ProviderName;
364
    }
365
366
367
    /**
368
     * Set the value of the ProviderName attribute.
369
     *
370
     * @param string $ProviderName The ProviderName attribute.
371
     */
372
    public function setProviderName($ProviderName)
373
    {
374
        assert('is_string($ProviderName)');
375
376
        $this->ProviderName = $ProviderName;
377
    }
378
379
380
    /**
381
     * Retrieve the value of the IsPassive attribute.
382
     *
383
     * @return bool The IsPassive attribute.
384
     */
385
    public function getIsPassive()
386
    {
387
        return $this->isPassive;
388
    }
389
390
391
    /**
392
     * Set the value of the IsPassive attribute.
393
     *
394
     * @param bool $isPassive The IsPassive attribute.
395
     */
396
    public function setIsPassive($isPassive)
397
    {
398
        assert('is_bool($isPassive)');
399
400
        $this->isPassive = $isPassive;
401
    }
402
403
404
    /**
405
     * This function sets the scoping for the request.
406
     * See Core 3.4.1.2 for the definition of scoping.
407
     * Currently we support an IDPList of idpEntries.
408
     *
409
     * Each idpEntries consists of an array, containing
410
     * keys (mapped to attributes) and corresponding values.
411
     * Allowed attributes: Loc, Name, ProviderID.
412
     *
413
     * For backward compatibility, an idpEntries can also
414
     * be a string instead of an array, where each string
415
     * is mapped to the value of attribute ProviderID.
416
     */
417
    public function setIDPList($IDPList)
418
    {
419
        assert('is_array($IDPList)');
420
        $this->IDPList = $IDPList;
421
    }
422
423
424
    /**
425
     * This function retrieves the list of providerIDs from this authentication request.
426
     * Currently we only support a list of ipd ientity id's.
427
     * @return array List of idp EntityIDs from the request
428
     */
429
    public function getIDPList()
430
    {
431
        return $this->IDPList;
432
    }
433
434
    /**
435
     * @param int $ProxyCount
436
     */
437
    public function setProxyCount($ProxyCount)
438
    {
439
        assert('is_int($ProxyCount)');
440
        $this->ProxyCount = $ProxyCount;
441
    }
442
443
    /**
444
     * @return int
445
     */
446
    public function getProxyCount()
447
    {
448
        return $this->ProxyCount;
449
    }
450
451
    /**
452
     * @param array $RequesterID
453
     */
454
    public function setRequesterID(array $RequesterID)
455
    {
456
        $this->RequesterID = $RequesterID;
457
    }
458
459
    /**
460
     * @return array
461
     */
462
    public function getRequesterID()
463
    {
464
        return $this->RequesterID;
465
    }
466
467
    /**
468
     * Retrieve the value of the AssertionConsumerServiceURL attribute.
469
     *
470
     * @return string|null The AssertionConsumerServiceURL attribute.
471
     */
472
    public function getAssertionConsumerServiceURL()
473
    {
474
        return $this->assertionConsumerServiceURL;
475
    }
476
477
    /**
478
     * Set the value of the AssertionConsumerServiceURL attribute.
479
     *
480
     * @param string|null $assertionConsumerServiceURL The AssertionConsumerServiceURL attribute.
481
     */
482
    public function setAssertionConsumerServiceURL($assertionConsumerServiceURL)
483
    {
484
        assert('is_string($assertionConsumerServiceURL) || is_null($assertionConsumerServiceURL)');
485
486
        $this->assertionConsumerServiceURL = $assertionConsumerServiceURL;
487
    }
488
489
    /**
490
     * Retrieve the value of the ProtocolBinding attribute.
491
     *
492
     * @return string|null The ProtocolBinding attribute.
493
     */
494
    public function getProtocolBinding()
495
    {
496
        return $this->protocolBinding;
497
    }
498
499
    /**
500
     * Set the value of the ProtocolBinding attribute.
501
     *
502
     * @param string $protocolBinding The ProtocolBinding attribute.
503
     */
504
    public function setProtocolBinding($protocolBinding)
505
    {
506
        assert('is_string($protocolBinding) || is_null($protocolBinding)');
507
508
        $this->protocolBinding = $protocolBinding;
509
    }
510
511
    /**
512
     * Retrieve the value of the AttributeConsumingServiceIndex attribute.
513
     *
514
     * @return int|null The AttributeConsumingServiceIndex attribute.
515
     */
516
    public function getAttributeConsumingServiceIndex()
517
    {
518
        return $this->attributeConsumingServiceIndex;
519
    }
520
521
    /**
522
     * Set the value of the AttributeConsumingServiceIndex attribute.
523
     *
524
     * @param int|null $attributeConsumingServiceIndex The AttributeConsumingServiceIndex attribute.
525
     */
526
    public function setAttributeConsumingServiceIndex($attributeConsumingServiceIndex)
527
    {
528
        assert('is_int($attributeConsumingServiceIndex) || is_null($attributeConsumingServiceIndex)');
529
530
        $this->attributeConsumingServiceIndex = $attributeConsumingServiceIndex;
531
    }
532
533
    /**
534
     * Retrieve the value of the AssertionConsumerServiceIndex attribute.
535
     *
536
     * @return int|null The AssertionConsumerServiceIndex attribute.
537
     */
538
    public function getAssertionConsumerServiceIndex()
539
    {
540
        return $this->assertionConsumerServiceIndex;
541
    }
542
543
    /**
544
     * Set the value of the AssertionConsumerServiceIndex attribute.
545
     *
546
     * @param int|null $assertionConsumerServiceIndex The AssertionConsumerServiceIndex attribute.
547
     */
548
    public function setAssertionConsumerServiceIndex($assertionConsumerServiceIndex)
549
    {
550
        assert('is_int($assertionConsumerServiceIndex) || is_null($assertionConsumerServiceIndex)');
551
552
        $this->assertionConsumerServiceIndex = $assertionConsumerServiceIndex;
553
    }
554
555
    /**
556
     * Retrieve the RequestedAuthnContext.
557
     *
558
     * @return array|null The RequestedAuthnContext.
559
     */
560
    public function getRequestedAuthnContext()
561
    {
562
        return $this->requestedAuthnContext;
563
    }
564
565
    /**
566
     * Set the RequestedAuthnContext.
567
     *
568
     * @param array|null $requestedAuthnContext The RequestedAuthnContext.
569
     */
570
    public function setRequestedAuthnContext($requestedAuthnContext)
571
    {
572
        assert('is_array($requestedAuthnContext) || is_null($requestedAuthnContext)');
573
574
        $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...
575
    }
576
577
    /**
578
     * Retrieve the NameId of the subject in the assertion.
579
     *
580
     * The returned NameId is in the format used by \SAML2\Utils::addNameId().
581
     *
582
     * @see \SAML2\Utils::addNameId()
583
     * @return array|null The name identifier of the assertion.
584
     * @throws \Exception
585
     */
586
    public function getNameId()
587
    {
588
        if ($this->encryptedNameId !== null) {
589
            throw new \Exception('Attempted to retrieve encrypted NameID without decrypting it first.');
590
        }
591
592
        return $this->nameId;
593
    }
594
595
    /**
596
     * Set the NameId of the subject in the assertion.
597
     *
598
     * The NameId must be in the format accepted by \SAML2\Utils::addNameId().
599
     *
600
     * @see \SAML2\Utils::addNameId()
601
     *
602
     * @param array|null $nameId The name identifier of the assertion.
603
     */
604
    public function setNameId($nameId)
605
    {
606
        assert('is_array($nameId) || is_null($nameId)');
607
608
        $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...
609
    }
610
611
    /**
612
     * Encrypt the NameID in the AuthnRequest.
613
     *
614
     * @param XMLSecurityKey $key The encryption key.
615
     */
616 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...
617
    {
618
        /* First create a XML representation of the NameID. */
619
        $doc  = new \DOMDocument();
620
        $root = $doc->createElement('root');
621
        $doc->appendChild($root);
622
        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...
623
        $nameId = $root->firstChild;
624
625
        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...
626
627
        /* Encrypt the NameID. */
628
        $enc = new XMLSecEnc();
629
        $enc->setNode($nameId);
630
        // @codingStandardsIgnoreStart
631
        $enc->type = XMLSecEnc::Element;
632
        // @codingStandardsIgnoreEnd
633
634
        $symmetricKey = new XMLSecurityKey(XMLSecurityKey::AES128_CBC);
635
        $symmetricKey->generateSessionKey();
636
        $enc->encryptKey($key, $symmetricKey);
637
638
        $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...
639
        $this->nameId          = null;
640
    }
641
642
    /**
643
     * Decrypt the NameId of the subject in the assertion.
644
     *
645
     * @param XMLSecurityKey $key       The decryption key.
646
     * @param array          $blacklist Blacklisted decryption algorithms.
647
     */
648 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...
649
    {
650
        if ($this->encryptedNameId === null) {
651
            /* No NameID to decrypt. */
652
            return;
653
        }
654
655
        $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...
656
        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...
657
        $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...
658
659
        $this->encryptedNameId = null;
660
    }
661
662
    /**
663
     * Retrieve the SubjectConfirmation elements we have in our Subject element.
664
     *
665
     * @return \SAML2\XML\saml\SubjectConfirmation[]
666
     */
667
    public function getSubjectConfirmation()
668
    {
669
        return $this->subjectConfirmation;
670
    }
671
672
    /**
673
     * Set the SubjectConfirmation elements that should be included in the assertion.
674
     *
675
     * @param array \SAML2\XML\saml\SubjectConfirmation[]
676
     */
677
    public function setSubjectConfirmation(array $subjectConfirmation)
678
    {
679
        $this->subjectConfirmation = $subjectConfirmation;
680
    }
681
682
    /**
683
     * Convert this authentication request to an XML element.
684
     *
685
     * @return \DOMElement This authentication request.
686
     */
687
    public function toUnsignedXML()
688
    {
689
        $root = parent::toUnsignedXML();
690
691
        if ($this->forceAuthn) {
692
            $root->setAttribute('ForceAuthn', 'true');
693
        }
694
695
        if ($this->ProviderName !== null) {
696
            $root->setAttribute('ProviderName', $this->ProviderName);
697
        }
698
699
        if ($this->isPassive) {
700
            $root->setAttribute('IsPassive', 'true');
701
        }
702
703
        if ($this->assertionConsumerServiceIndex !== null) {
704
            $root->setAttribute('AssertionConsumerServiceIndex', $this->assertionConsumerServiceIndex);
705
        } else {
706
            if ($this->assertionConsumerServiceURL !== null) {
707
                $root->setAttribute('AssertionConsumerServiceURL', $this->assertionConsumerServiceURL);
708
            }
709
            if ($this->protocolBinding !== null) {
710
                $root->setAttribute('ProtocolBinding', $this->protocolBinding);
711
            }
712
        }
713
714
        if ($this->attributeConsumingServiceIndex !== null) {
715
            $root->setAttribute('AttributeConsumingServiceIndex', $this->attributeConsumingServiceIndex);
716
        }
717
718
        $this->addSubject($root);
719
720
        if (!empty($this->nameIdPolicy)) {
721
            $nameIdPolicy = $this->document->createElementNS(Constants::NS_SAMLP, 'NameIDPolicy');
722
            if (array_key_exists('Format', $this->nameIdPolicy)) {
723
                $nameIdPolicy->setAttribute('Format', $this->nameIdPolicy['Format']);
724
            }
725
            if (array_key_exists('SPNameQualifier', $this->nameIdPolicy)) {
726
                $nameIdPolicy->setAttribute('SPNameQualifier', $this->nameIdPolicy['SPNameQualifier']);
727
            }
728
            if (array_key_exists('AllowCreate', $this->nameIdPolicy)) {
729
                $nameIdPolicy->setAttribute('AllowCreate', ($this->nameIdPolicy['AllowCreate']) ? 'true' : 'false');
730
            }
731
            $root->appendChild($nameIdPolicy);
732
        }
733
734
        $rac = $this->requestedAuthnContext;
735
        if (!empty($rac) && !empty($rac['AuthnContextClassRef'])) {
736
            $e = $this->document->createElementNS(Constants::NS_SAMLP, 'RequestedAuthnContext');
737
            $root->appendChild($e);
738
            if (isset($rac['Comparison']) && $rac['Comparison'] !== Constants::COMPARISON_EXACT) {
739
                $e->setAttribute('Comparison', $rac['Comparison']);
740
            }
741
            foreach ($rac['AuthnContextClassRef'] as $accr) {
742
                Utils::addString($e, Constants::NS_SAML, 'AuthnContextClassRef', $accr);
743
            }
744
        }
745
746
        if ($this->ProxyCount !== null || count($this->IDPList) > 0 || count($this->RequesterID) > 0) {
747
            $scoping = $this->document->createElementNS(Constants::NS_SAMLP, 'Scoping');
748
            $root->appendChild($scoping);
749
            if ($this->ProxyCount !== null) {
750
                $scoping->setAttribute('ProxyCount', $this->ProxyCount);
751
            }
752
            if (count($this->IDPList) > 0) {
753
                $idplist = $this->document->createElementNS(Constants::NS_SAMLP, 'IDPList');
754
                foreach ($this->IDPList as $provider) {
755
                    $idpEntry = $this->document->createElementNS(Constants::NS_SAMLP, 'IDPEntry');
756
                    if (is_string($provider)) {
757
                        $idpEntry->setAttribute('ProviderID', $provider);
758
                    } elseif (is_array($provider)) {
759
                        foreach ($provider as $attribute => $value) {
760
                            if (in_array($attribute, array(
761
                                'ProviderID',
762
                                'Loc',
763
                                'Name'
764
                            ))) {
765
                                $idpEntry->setAttribute($attribute, $value);
766
                            }
767
                        }
768
                    }
769
                    $idplist->appendChild($idpEntry);
770
                }
771
                $scoping->appendChild($idplist);
772
            }
773
            if (count($this->RequesterID) > 0) {
774
                Utils::addStrings($scoping, Constants::NS_SAMLP, 'RequesterID', false, $this->RequesterID);
775
            }
776
        }
777
778
        return $root;
779
    }
780
781
    /**
782
     * Add a Subject-node to the assertion.
783
     *
784
     * @param \DOMElement $root The assertion element we should add the subject to.
785
     */
786 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...
787
    {
788
        // If there is no nameId (encrypted or not) there is nothing to create a subject for
789
        if ($this->nameId === null && $this->encryptedNameId === null) {
790
            return;
791
        }
792
793
        $subject = $root->ownerDocument->createElementNS(Constants::NS_SAML, 'saml:Subject');
794
        $root->appendChild($subject);
795
796
        if ($this->encryptedNameId === null) {
797
            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...
798
        } else {
799
            $eid = $subject->ownerDocument->createElementNS(Constants::NS_SAML, 'saml:EncryptedID');
800
            $eid->appendChild($subject->ownerDocument->importNode($this->encryptedNameId, true));
801
            $subject->appendChild($eid);
802
        }
803
804
        foreach ($this->subjectConfirmation as $sc) {
805
            $sc->toXML($subject);
806
        }
807
    }
808
}
809