Completed
Push — feature/use-authn-request-acs-... ( 994cc5...dc9b8d )
by
unknown
02:32
created

getUnvalidatedRequestAssertionConsumerServiceUrl()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 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
 */
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 Surfnet\SamlBundle\Entity\IdentityProvider;
27
use Surfnet\StepupGateway\GatewayBundle\Entity\SecondFactor;
28
use Surfnet\StepupGateway\GatewayBundle\Entity\ServiceProvider;
29
use Surfnet\StepupGateway\GatewayBundle\Saml\Proxy\ProxyStateHandler;
30
use Surfnet\StepupGateway\GatewayBundle\Service\SamlEntityService;
31
32
class ResponseContext
33
{
34
    /**
35
     * @var IdentityProvider
36
     */
37
    private $hostedIdentityProvider;
38
39
    /**
40
     * @var \Surfnet\StepupGateway\GatewayBundle\Service\SamlEntityService
41
     */
42
    private $samlEntityService;
43
44
    /**
45
     * @var ProxyStateHandler
46
     */
47
    private $stateHandler;
48
49
    /**
50
     * @var LoggerInterface
51
     */
52
    private $logger;
53
54
    /**
55
     * @var DateTime
56
     */
57
    private $generationTime;
58
59
    /**
60
     * @var IdentityProvider|null
61
     */
62
    private $authenticatingIdp;
63
64
    /**
65
     * @var ServiceProvider
66
     */
67
    private $targetServiceProvider;
68
69 View Code Duplication
    public function __construct(
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...
70
        IdentityProvider $identityProvider,
71
        SamlEntityService $samlEntityService,
72
        ProxyStateHandler $stateHandler,
73
        LoggerInterface $logger
74
    ) {
75
        $this->hostedIdentityProvider = $identityProvider;
76
        $this->samlEntityService      = $samlEntityService;
77
        $this->stateHandler           = $stateHandler;
78
        $this->logger                 = $logger;
79
        $this->generationTime         = new DateTime('now', new DateTimeZone('UTC'));
80
    }
81
82
    /**
83
     * The ACS location requested in the AuthnRequest.
84
     *
85
     * This value should never be trusted and only be used if it matches the
86
     * configured allowed ACS locations. Use determineAcsLocation() and
87
     * determineAcsLocationForAdfs() instead. This getter exists to provide
88
     * the SamlProxyController with the unvalidated ACS location in the
89
     * request.
90
     *
91
     * @return string
0 ignored issues
show
Documentation introduced by
Should the return type not be string|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
92
     */
93
    public function getUnvalidatedRequestAssertionConsumerServiceUrl()
94
    {
95
        return $this->stateHandler->getRequestAssertionConsumerServiceUrl();
96
    }
97
98
    /**
99
     * @return string
100
     */
101
    public function getDestination()
102
    {
103
        $requestAcsUrl = $this->getUnvalidatedRequestAssertionConsumerServiceUrl();
104
105
        return $this->getServiceProvider()->determineAcsLocation($requestAcsUrl, $this->logger);
106
    }
107
108
    /**
109
     * @return string
110
     */
111
    public function getDestinationForAdfs()
112
    {
113
        $requestAcsUrl = $this->getUnvalidatedRequestAssertionConsumerServiceUrl();
114
115
        return $this->getServiceProvider()->determineAcsLocationForAdfs($requestAcsUrl);
116
    }
117
118
    /**
119
     * @return null|string
120
     */
121
    public function getIssuer()
122
    {
123
        return $this->hostedIdentityProvider->getEntityId();
124
    }
125
126
    /**
127
     * @return int
128
     */
129
    public function getIssueInstant()
130
    {
131
        return $this->generationTime->getTimestamp();
132
    }
133
134
    /**
135
     * @return null|string
136
     */
137
    public function getInResponseTo()
138
    {
139
        return $this->stateHandler->getRequestId();
140
    }
141
142
    /**
143
     * @return null|string
144
     */
145
    public function getExpectedInResponseTo()
146
    {
147
        return $this->stateHandler->getGatewayRequestId();
148
    }
149
150
    /**
151
     * @return null|string
152
     */
153
    public function getRequiredLoa()
154
    {
155
        return $this->stateHandler->getRequiredLoaIdentifier();
156
    }
157
158
    /**
159
     * @return IdentityProvider
160
     */
161
    public function getIdentityProvider()
162
    {
163
        return $this->hostedIdentityProvider;
164
    }
165
166
    /**
167
     * @return null|ServiceProvider
168
     */
169
    public function getServiceProvider()
170
    {
171
        if (isset($this->targetServiceProvider)) {
172
            return $this->targetServiceProvider;
173
        }
174
175
        $serviceProviderId = $this->stateHandler->getRequestServiceProvider();
176
177
        return $this->targetServiceProvider = $this->samlEntityService->getServiceProvider($serviceProviderId);
178
    }
179
180
    /**
181
     * @return null|string
182
     */
183
    public function getRelayState()
184
    {
185
        return $this->stateHandler->getRelayState();
186
    }
187
188
    /**
189
     * @param Assertion $assertion
190
     */
191
    public function saveAssertion(Assertion $assertion)
192
    {
193
        // we pluck the NameId to make it easier to access it without having to reconstitute the assertion
194
        $nameId = $assertion->getNameId();
195
        if (!is_null($nameId->value)) {
196
            $this->stateHandler->saveIdentityNameId($nameId->value);
197
        }
198
199
        // same for the entityId of the authenticating Authority
200
        $authenticatingAuthorities = $assertion->getAuthenticatingAuthority();
201
        if (!empty($authenticatingAuthorities)) {
202
            $this->stateHandler->setAuthenticatingIdp(reset($authenticatingAuthorities));
203
        }
204
205
        // And also attempt to save the user's schacHomeOrganization
206
        $attributes = $assertion->getAttributes();
207
        if (!empty($attributes['urn:mace:terena.org:attribute-def:schacHomeOrganization'])) {
208
            $schacHomeOrganization = $attributes['urn:mace:terena.org:attribute-def:schacHomeOrganization'];
209
            $this->stateHandler->setSchacHomeOrganization(reset($schacHomeOrganization));
210
        }
211
212
        $this->stateHandler->saveAssertion($assertion->toXML()->ownerDocument->saveXML());
213
    }
214
215
    /**
216
     * @return Assertion
217
     */
218
    public function reconstituteAssertion()
219
    {
220
        $assertionAsXML    = $this->stateHandler->getAssertion();
221
        $assertionDocument = new DOMDocument();
222
        $assertionDocument->loadXML($assertionAsXML);
223
224
        return new Assertion($assertionDocument->documentElement);
225
    }
226
227
    /**
228
     * @return null|string
229
     */
230
    public function getIdentityNameId()
231
    {
232
        return $this->stateHandler->getIdentityNameId();
233
    }
234
235
    /**
236
     * Return the lower-cased schacHomeOrganization value from the assertion.
237
     *
238
     * Comparisons on SHO values should always be case insensitive. Stepup
239
     * configuration always contains SHO values lower-cased, so this getter
240
     * can be used to compare the SHO with configured values.
241
     *
242
     * @see StepUpAuthenticationService::resolveHighestRequiredLoa()
243
     *
244
     * @return null|string
245
     */
246
    public function getNormalizedSchacHomeOrganization()
247
    {
248
        return strtolower(
249
            $this->stateHandler->getSchacHomeOrganization()
250
        );
251
    }
252
253
    /**
254
     * @return null|IdentityProvider
255
     */
256
    public function getAuthenticatingIdp()
257
    {
258
        $entityId = $this->stateHandler->getAuthenticatingIdp();
259
260
        if (!$entityId) {
261
            return null;
262
        }
263
264
        if (isset($this->authenticatingIdp)) {
265
            return $this->authenticatingIdp;
266
        }
267
268
        $this->authenticatingIdp = $this->samlEntityService->hasIdentityProvider($entityId)
269
            ? $this->samlEntityService->getIdentityProvider($entityId)
270
            : null;
271
272
        return $this->authenticatingIdp;
273
    }
274
275
    /**
276
     * @param SecondFactor $secondFactor
277
     */
278
    public function saveSelectedSecondFactor(SecondFactor $secondFactor)
279
    {
280
        $this->stateHandler->setSelectedSecondFactorId($secondFactor->secondFactorId);
281
        $this->stateHandler->setSecondFactorVerified(false);
282
        $this->stateHandler->setPreferredLocale($secondFactor->displayLocale);
283
    }
284
285
    /**
286
     * @return null|string
287
     */
288
    public function getSelectedSecondFactor()
289
    {
290
        return $this->stateHandler->getSelectedSecondFactorId();
291
    }
292
293
    public function markSecondFactorVerified()
294
    {
295
        $this->stateHandler->setSecondFactorVerified(true);
296
    }
297
298
    /**
299
     * @return bool
300
     */
301
    public function isSecondFactorVerified()
302
    {
303
        return $this->stateHandler->getSelectedSecondFactorId() && $this->stateHandler->isSecondFactorVerified();
304
    }
305
306
    public function getResponseAction()
307
    {
308
        return $this->stateHandler->getResponseAction();
309
    }
310
311
    /**
312
     * Resets some state after the response is sent
313
     * (e.g. resets which second factor was selected and whether it was verified).
314
     */
315
    public function responseSent()
316
    {
317
        $this->stateHandler->setSelectedSecondFactorId(null);
318
        $this->stateHandler->setSecondFactorVerified(false);
319
    }
320
}
321