Passed
Pull Request — develop (#295)
by Peter
04:30
created

ResponseContext::resolveNameIdValue()   A

Complexity

Conditions 5
Paths 3

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 7
nc 3
nop 1
dl 0
loc 12
rs 9.6111
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * Copyright 2014 SURFnet bv
5
 *
6
 * Licensed under the Apache License, Version 2.0 (the "License");
7
 * you may not use this file except in compliance with the License.
8
 * You may obtain a copy of the License at
9
 *
10
 *     http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing, software
13
 * distributed under the License is distributed on an "AS IS" BASIS,
14
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
 * See the License for the specific language governing permissions and
16
 * limitations under the License.
17
 */
0 ignored issues
show
Coding Style introduced by
PHP version not specified
Loading history...
Coding Style introduced by
Missing @category tag in file comment
Loading history...
Coding Style introduced by
Missing @package tag in file comment
Loading history...
Coding Style introduced by
Missing @author tag in file comment
Loading history...
Coding Style introduced by
Missing @license tag in file comment
Loading history...
Coding Style introduced by
Missing @link tag in file comment
Loading history...
18
19
namespace Surfnet\StepupGateway\GatewayBundle\Saml;
20
21
use DateTime;
22
use DateTimeZone;
23
use DOMDocument;
24
use Psr\Log\LoggerInterface;
25
use SAML2\Assertion;
26
use SAML2\XML\saml\Issuer;
27
use Surfnet\SamlBundle\Entity\IdentityProvider;
28
use Surfnet\StepupGateway\GatewayBundle\Entity\SecondFactor;
29
use Surfnet\StepupGateway\GatewayBundle\Entity\ServiceProvider;
30
use Surfnet\StepupGateway\GatewayBundle\Saml\Exception\RuntimeException;
31
use Surfnet\StepupGateway\GatewayBundle\Saml\Proxy\ProxyStateHandler;
32
use Surfnet\StepupGateway\GatewayBundle\Service\SamlEntityService;
33
34
class ResponseContext
0 ignored issues
show
Coding Style introduced by
Missing doc comment for class ResponseContext
Loading history...
35
{
36
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
37
     * @var IdentityProvider
38
     */
39
    private $hostedIdentityProvider;
0 ignored issues
show
Coding Style introduced by
Private member variable "hostedIdentityProvider" must be prefixed with an underscore
Loading history...
40
41
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
42
     * @var \Surfnet\StepupGateway\GatewayBundle\Service\SamlEntityService
43
     */
44
    private $samlEntityService;
0 ignored issues
show
Coding Style introduced by
Private member variable "samlEntityService" must be prefixed with an underscore
Loading history...
45
46
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
47
     * @var ProxyStateHandler
48
     */
49
    private $stateHandler;
0 ignored issues
show
Coding Style introduced by
Private member variable "stateHandler" must be prefixed with an underscore
Loading history...
50
51
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
52
     * @var LoggerInterface
53
     */
54
    private $logger;
0 ignored issues
show
Coding Style introduced by
Private member variable "logger" must be prefixed with an underscore
Loading history...
55
56
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
57
     * @var DateTime
58
     */
59
    private $generationTime;
0 ignored issues
show
Coding Style introduced by
Private member variable "generationTime" must be prefixed with an underscore
Loading history...
60
61
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
62
     * @var IdentityProvider|null
63
     */
64
    private $authenticatingIdp;
0 ignored issues
show
Coding Style introduced by
Private member variable "authenticatingIdp" must be prefixed with an underscore
Loading history...
65
66
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
67
     * @var ServiceProvider
68
     */
69
    private $targetServiceProvider;
0 ignored issues
show
Coding Style introduced by
Private member variable "targetServiceProvider" must be prefixed with an underscore
Loading history...
70
71
    public function __construct(
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function __construct()
Loading history...
72
        IdentityProvider $identityProvider,
73
        SamlEntityService $samlEntityService,
74
        ProxyStateHandler $stateHandler,
75
        LoggerInterface $logger,
76
        DateTime $now = null
77
    ) {
78
        $this->hostedIdentityProvider = $identityProvider;
79
        $this->samlEntityService      = $samlEntityService;
80
        $this->stateHandler           = $stateHandler;
81
        $this->logger                 = $logger;
82
        $this->generationTime         = is_null($now) ? new DateTime('now', new DateTimeZone('UTC')): $now;
83
    }
84
85
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
86
     * @return string
87
     */
88
    public function getDestination()
89
    {
90
        $requestAcsUrl = $this->stateHandler->getRequestAssertionConsumerServiceUrl();
91
92
        return $this->getServiceProvider()->determineAcsLocation($requestAcsUrl, $this->logger);
93
    }
94
95
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
96
     * @return string
97
     */
98
    public function getDestinationForAdfs()
99
    {
100
        $requestAcsUrl = $this->stateHandler->getRequestAssertionConsumerServiceUrl();
101
102
        return $this->getServiceProvider()->determineAcsLocationForAdfs($requestAcsUrl);
103
    }
104
105
    public function getIssuer(): Issuer
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function getIssuer()
Loading history...
106
    {
107
        $issuer = new Issuer();
108
        $issuer->setValue($this->hostedIdentityProvider->getEntityId());
0 ignored issues
show
Bug introduced by
It seems like $this->hostedIdentityProvider->getEntityId() can also be of type null; however, parameter $value of SAML2\XML\saml\NameIDType::setValue() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

108
        $issuer->setValue(/** @scrutinizer ignore-type */ $this->hostedIdentityProvider->getEntityId());
Loading history...
109
        return $issuer;
110
    }
111
112
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
113
     * @return int
114
     */
115
    public function getIssueInstant()
116
    {
117
        return $this->generationTime->getTimestamp();
118
    }
119
120
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
121
     * @return null|string
122
     */
123
    public function getInResponseTo()
124
    {
125
        return $this->stateHandler->getRequestId();
126
    }
127
128
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
129
     * @return null|string
130
     */
131
    public function getExpectedInResponseTo()
132
    {
133
        return $this->stateHandler->getGatewayRequestId();
134
    }
135
136
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
137
     * @return null|string
138
     */
139
    public function getRequiredLoa()
140
    {
141
        return $this->stateHandler->getRequiredLoaIdentifier();
142
    }
143
144
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
145
     * @return IdentityProvider
146
     */
147
    public function getIdentityProvider()
148
    {
149
        return $this->hostedIdentityProvider;
150
    }
151
152
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
153
     * @return null|ServiceProvider
154
     */
155
    public function getServiceProvider()
156
    {
157
        if (isset($this->targetServiceProvider)) {
158
            return $this->targetServiceProvider;
159
        }
160
161
        $serviceProviderId = $this->stateHandler->getRequestServiceProvider();
162
163
        return $this->targetServiceProvider = $this->samlEntityService->getServiceProvider($serviceProviderId);
164
    }
165
166
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
167
     * @return null|string
168
     */
169
    public function getRelayState()
170
    {
171
        return $this->stateHandler->getRelayState();
172
    }
173
174
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
175
     * @param Assertion $assertion
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
176
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
177
    public function saveAssertion(Assertion $assertion)
178
    {
179
        $this->stateHandler->saveIdentityNameId($this->resolveNameIdValue($assertion));
180
        // same for the entityId of the authenticating Authority
181
        $authenticatingAuthorities = $assertion->getAuthenticatingAuthority();
182
        if (!empty($authenticatingAuthorities)) {
183
            $this->stateHandler->setAuthenticatingIdp(reset($authenticatingAuthorities));
184
        }
185
186
        // And also attempt to save the user's schacHomeOrganization
187
        $attributes = $assertion->getAttributes();
188
        if (!empty($attributes['urn:mace:terena.org:attribute-def:schacHomeOrganization'])) {
189
            $schacHomeOrganization = $attributes['urn:mace:terena.org:attribute-def:schacHomeOrganization'];
190
            $this->stateHandler->setSchacHomeOrganization(reset($schacHomeOrganization));
191
        }
192
193
        $this->stateHandler->saveAssertion($assertion->toXML()->ownerDocument->saveXML());
0 ignored issues
show
Bug introduced by
The method saveXML() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

193
        $this->stateHandler->saveAssertion($assertion->toXML()->ownerDocument->/** @scrutinizer ignore-call */ saveXML());

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
194
    }
195
196
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
197
     * @return Assertion
198
     */
199
    public function reconstituteAssertion()
200
    {
201
        $assertionAsXML    = $this->stateHandler->getAssertion();
202
        $assertionDocument = new DOMDocument();
203
        $assertionDocument->loadXML($assertionAsXML);
204
205
        return new Assertion($assertionDocument->documentElement);
206
    }
207
208
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
209
     * @return null|string
210
     */
211
    public function getIdentityNameId(): string
212
    {
213
        return $this->stateHandler->getIdentityNameId();
214
    }
215
216
    /**
217
     * Return the lower-cased schacHomeOrganization value from the assertion.
218
     *
219
     * Comparisons on SHO values should always be case insensitive. Stepup
220
     * configuration always contains SHO values lower-cased, so this getter
221
     * can be used to compare the SHO with configured values.
222
     *
223
     * @see StepUpAuthenticationService::resolveHighestRequiredLoa()
224
     *
225
     * @return null|string
226
     */
227
    public function getNormalizedSchacHomeOrganization()
228
    {
229
        return strtolower(
230
            $this->stateHandler->getSchacHomeOrganization()
0 ignored issues
show
Bug introduced by
It seems like $this->stateHandler->getSchacHomeOrganization() can also be of type null; however, parameter $string of strtolower() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

230
            /** @scrutinizer ignore-type */ $this->stateHandler->getSchacHomeOrganization()
Loading history...
231
        );
232
    }
233
234
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
235
     * @return null|IdentityProvider
236
     */
237
    public function getAuthenticatingIdp()
238
    {
239
        $entityId = $this->stateHandler->getAuthenticatingIdp();
240
241
        if (!$entityId) {
242
            return null;
243
        }
244
245
        if (isset($this->authenticatingIdp)) {
246
            return $this->authenticatingIdp;
247
        }
248
249
        $this->authenticatingIdp = $this->samlEntityService->hasIdentityProvider($entityId)
250
            ? $this->samlEntityService->getIdentityProvider($entityId)
251
            : null;
252
253
        return $this->authenticatingIdp;
254
    }
255
256
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
257
     * @param SecondFactor $secondFactor
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
258
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
259
    public function saveSelectedSecondFactor(SecondFactor $secondFactor)
260
    {
261
        $this->stateHandler->setSelectedSecondFactorId($secondFactor->secondFactorId);
262
        $this->stateHandler->setSecondFactorVerified(false);
263
        $this->stateHandler->setPreferredLocale($secondFactor->displayLocale);
264
    }
265
266
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
267
     * @return null|string
268
     */
269
    public function getSelectedSecondFactor()
270
    {
271
        return $this->stateHandler->getSelectedSecondFactorId();
272
    }
273
274
    public function markSecondFactorVerified()
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function markSecondFactorVerified()
Loading history...
275
    {
276
        $this->stateHandler->setSecondFactorVerified(true);
277
    }
278
279
    public function finalizeAuthentication()
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function finalizeAuthentication()
Loading history...
280
    {
281
        $this->stateHandler->setSelectedSecondFactorId(null);
282
    }
283
284
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
285
     * @return bool
286
     */
287
    public function isSecondFactorVerified()
288
    {
289
        return $this->stateHandler->getSelectedSecondFactorId() && $this->stateHandler->isSecondFactorVerified();
290
    }
291
292
    public function getResponseAction()
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function getResponseAction()
Loading history...
293
    {
294
        return $this->stateHandler->getResponseAction();
295
    }
296
297
    /**
298
     * Resets some state after the response is sent
299
     * (e.g. resets which second factor was selected and whether it was verified).
300
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
301
    public function responseSent()
302
    {
303
        $this->stateHandler->setSecondFactorVerified(false);
304
        $this->stateHandler->setVerifiedBySsoOn2faCookie(false);
305
        $this->stateHandler->setSsoOn2faCookieFingerprint('');
306
    }
307
308
    /**
309
     * Retrieve the ResponseContextServiceId from state
310
     *
311
     * Used to determine we are dealing with a SFO or regular authentication. Both have different ResponseContext
312
     * instances, and it's imperative that successive consumers use the correct service.
313
     *
314
     * @return string|null
315
     */
316
    public function getResponseContextServiceId()
317
    {
318
        return $this->stateHandler->getResponseContextServiceId();
319
    }
320
321
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $assertion should have a doc-comment as per coding-style.
Loading history...
322
     * Either gets the internal-collabPersonId if present or falls back on the regular name id attribute
323
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
324
    private function resolveNameIdValue(Assertion $assertion): string
0 ignored issues
show
Coding Style introduced by
Private method name "ResponseContext::resolveNameIdValue" must be prefixed with an underscore
Loading history...
325
    {
326
        $attributes = $assertion->getAttributes();
327
        if (array_key_exists('urn:mace:surf.nl:attribute-def:internal-collabPersonId', $attributes)) {
328
            return reset($attributes['urn:mace:surf.nl:attribute-def:internal-collabPersonId']);
329
        }
330
        $nameId = $assertion->getNameId();
331
        if ($nameId && !is_null($nameId->getValue()) && is_string($nameId->getValue())) {
332
            return $nameId->getValue();
333
        }
334
335
        throw new RuntimeException('Unable to resolve an identifier from internalCollabPersonId or the Subject NameId');
336
    }
337
338
    public function isForceAuthn(): bool
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function isForceAuthn()
Loading history...
339
    {
340
        return $this->stateHandler->isForceAuthn();
341
    }
342
343
    public function markVerifiedBySsoOn2faCookie(string $fingerprint)
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function markVerifiedBySsoOn2faCookie()
Loading history...
344
    {
345
        $this->stateHandler->setVerifiedBySsoOn2faCookie(true);
346
        $this->stateHandler->setSsoOn2faCookieFingerprint($fingerprint);
347
    }
348
349
    public function isVerifiedBySsoOn2faCookie(): bool
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function isVerifiedBySsoOn2faCookie()
Loading history...
350
    {
351
        return $this->stateHandler->isVerifiedBySsoOn2faCookie();
352
    }
353
}
354