Completed
Push — master ( e96383...13486f )
by
unknown
06:11
created

ResponseContext::getDestinationForAdfs()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

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