Completed
Push — feature/implement-state-handli... ( bd5ae0 )
by Michiel
01:52
created

ResponseContext::saveSelectedSecondFactor()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

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