1 | <?php declare(strict_types=1); |
||||
2 | |||||
3 | namespace SilverStripe\MFA\Authenticator; |
||||
4 | |||||
5 | use Psr\Log\LoggerInterface; |
||||
6 | use SilverStripe\Control\HTTPRequest; |
||||
7 | use SilverStripe\Control\HTTPResponse; |
||||
8 | use SilverStripe\Core\Injector\Injector; |
||||
9 | use SilverStripe\MFA\Exception\InvalidMethodException; |
||||
10 | use SilverStripe\MFA\Exception\MemberNotFoundException; |
||||
11 | use SilverStripe\MFA\Extension\MemberExtension; |
||||
12 | use SilverStripe\MFA\Method\MethodInterface; |
||||
13 | use SilverStripe\MFA\RequestHandler\BaseHandlerTrait; |
||||
14 | use SilverStripe\MFA\RequestHandler\RegistrationHandlerTrait; |
||||
15 | use SilverStripe\MFA\RequestHandler\VerificationHandlerTrait; |
||||
16 | use SilverStripe\MFA\Service\EnforcementManager; |
||||
17 | use SilverStripe\MFA\Service\MethodRegistry; |
||||
18 | use SilverStripe\MFA\Service\SchemaGenerator; |
||||
19 | use SilverStripe\ORM\ValidationException; |
||||
20 | use SilverStripe\ORM\ValidationResult; |
||||
21 | use SilverStripe\Security\IdentityStore; |
||||
22 | use SilverStripe\Security\Member; |
||||
23 | use SilverStripe\Security\MemberAuthenticator\LoginHandler as BaseLoginHandler; |
||||
24 | use SilverStripe\Security\MemberAuthenticator\MemberLoginForm; |
||||
25 | use SilverStripe\Security\Security; |
||||
26 | |||||
27 | class LoginHandler extends BaseLoginHandler |
||||
28 | { |
||||
29 | use BaseHandlerTrait; |
||||
30 | use VerificationHandlerTrait; |
||||
0 ignored issues
–
show
Bug
introduced
by
Loading history...
|
|||||
31 | use RegistrationHandlerTrait; |
||||
32 | |||||
33 | const SESSION_KEY = 'MFALogin'; |
||||
34 | |||||
35 | private static $url_handlers = [ |
||||
0 ignored issues
–
show
|
|||||
36 | 'GET mfa/schema' => 'getSchema', // Provides details about existing registered methods, etc. |
||||
37 | 'GET mfa/register/$Method' => 'startRegistration', // Initiates registration process for $Method |
||||
38 | 'POST mfa/register/$Method' => 'finishRegistration', // Completes registration process for $Method |
||||
39 | 'GET mfa/skip' => 'skipRegistration', // Allows the user to skip MFA registration |
||||
40 | 'GET mfa/verify/$Method' => 'startVerification', // Initiates verify process for $Method |
||||
41 | 'POST mfa/verify/$Method' => 'finishVerification', // Verifies verify via $Method |
||||
42 | 'GET mfa/complete' => 'redirectAfterSuccessfulLogin', |
||||
43 | 'GET mfa' => 'mfa', // Renders the MFA Login Page to init the app |
||||
44 | ]; |
||||
45 | |||||
46 | private static $allowed_actions = [ |
||||
0 ignored issues
–
show
|
|||||
47 | 'mfa', |
||||
48 | 'getSchema', |
||||
49 | 'startRegistration', |
||||
50 | 'finishRegistration', |
||||
51 | 'skipRegistration', |
||||
52 | 'startVerification', |
||||
53 | 'finishVerification', |
||||
54 | 'redirectAfterSuccessfulLogin', |
||||
55 | ]; |
||||
56 | |||||
57 | /** |
||||
58 | * Provide a user help link that will be available on the Introduction UI |
||||
59 | * |
||||
60 | * @config |
||||
61 | * @var string |
||||
62 | */ |
||||
63 | private static $user_help_link = ''; |
||||
0 ignored issues
–
show
|
|||||
64 | |||||
65 | /** |
||||
66 | * @var string[] |
||||
67 | */ |
||||
68 | private static $dependencies = [ |
||||
0 ignored issues
–
show
|
|||||
69 | 'Logger' => '%$' . LoggerInterface::class . '.mfa', |
||||
70 | ]; |
||||
71 | |||||
72 | /** |
||||
73 | * @var LoggerInterface |
||||
74 | */ |
||||
75 | protected $logger; |
||||
76 | |||||
77 | /** |
||||
78 | * Override the parent "doLogin" to insert extra steps into the flow |
||||
79 | * |
||||
80 | * @inheritdoc |
||||
81 | */ |
||||
82 | public function doLogin($data, MemberLoginForm $form, HTTPRequest $request) |
||||
83 | { |
||||
84 | /** @var Member&MemberExtension $member */ |
||||
85 | $member = $this->checkLogin($data, $request, $result); |
||||
86 | $enforcementManager = EnforcementManager::singleton(); |
||||
87 | |||||
88 | // If there's no member it's an invalid login. We'll delegate this to the parent |
||||
89 | // Additionally if there are no MFA methods registered then we will also delegate |
||||
90 | if (!$member || !$this->getMethodRegistry()->hasMethods()) { |
||||
0 ignored issues
–
show
|
|||||
91 | return parent::doLogin($data, $form, $request); |
||||
92 | } |
||||
93 | |||||
94 | // Enable sudo mode. This would usually be done by the default login handler's afterLogin() hook. |
||||
95 | $this->getSudoModeService()->activate($request->getSession()); |
||||
96 | |||||
97 | // Create a store for handling MFA for this member |
||||
98 | $store = $this->createStore($member); |
||||
99 | // We don't need to store the user's password |
||||
100 | $request->offsetUnset('Password'); |
||||
101 | // User code may adjust the request properties further if they have their own sensitive data which |
||||
102 | // should be excluded from the store. |
||||
103 | $this->extend('onBeforeSaveRequestToStore', $request, $store); |
||||
104 | $store->save($request); |
||||
105 | |||||
106 | // Store the BackURL for use after the process is complete |
||||
107 | if (!empty($data)) { |
||||
108 | $request->getSession()->set(static::SESSION_KEY . '.additionalData', $data); |
||||
109 | } |
||||
110 | |||||
111 | // If there is at least one MFA method registered then the user MUST login with it |
||||
112 | $request->getSession()->clear(static::SESSION_KEY . '.mustLogin'); |
||||
113 | if ($member->RegisteredMFAMethods()->count() > 0) { |
||||
114 | $request->getSession()->set(static::SESSION_KEY . '.mustLogin', true); |
||||
115 | } |
||||
116 | |||||
117 | // Bypass the MFA UI if the user can and has skipped it or MFA is not enabled |
||||
118 | if (!$enforcementManager->shouldRedirectToMFA($member)) { |
||||
119 | $this->doPerformLogin($request, $member); |
||||
120 | return $this->redirectAfterSuccessfulLogin(); |
||||
121 | } |
||||
122 | |||||
123 | // Redirect to the MFA step |
||||
124 | return $this->redirect($this->link('mfa')); |
||||
125 | } |
||||
126 | |||||
127 | /** |
||||
128 | * Action handler for loading the MFA authentication React app |
||||
129 | * Template variables defined here will be used by the rendering controller's template - normally Page.ss |
||||
130 | * |
||||
131 | * @return HTTPResponse|array |
||||
132 | */ |
||||
133 | public function mfa(HTTPRequest $request) |
||||
134 | { |
||||
135 | $store = $this->getStore(); |
||||
136 | if (!$store || !$store->getMember() || !$this->getSudoModeService()->check($request->getSession())) { |
||||
0 ignored issues
–
show
|
|||||
137 | return $this->redirectBack(); |
||||
138 | } |
||||
139 | |||||
140 | $this->applyRequirements(); |
||||
141 | |||||
142 | return [ |
||||
143 | 'Form' => $this->renderWith($this->getViewerTemplates()), |
||||
144 | 'ClassName' => 'mfa', |
||||
145 | ]; |
||||
146 | } |
||||
147 | |||||
148 | /** |
||||
149 | * Provides information about the current Member's MFA state |
||||
150 | * |
||||
151 | * @return HTTPResponse |
||||
152 | */ |
||||
153 | public function getSchema(): HTTPResponse |
||||
154 | { |
||||
155 | try { |
||||
156 | $member = $this->getMember(); |
||||
157 | $schema = SchemaGenerator::create()->getSchema($member); |
||||
158 | return $this->jsonResponse( |
||||
159 | $schema + [ |
||||
160 | 'endpoints' => [ |
||||
161 | 'register' => $this->Link('mfa/register/{urlSegment}'), |
||||
162 | 'verify' => $this->Link('mfa/verify/{urlSegment}'), |
||||
163 | 'complete' => $this->Link('mfa/complete'), |
||||
164 | 'skip' => $this->Link('mfa/skip'), |
||||
165 | ], |
||||
166 | ] |
||||
167 | ); |
||||
168 | } catch (MemberNotFoundException $exception) { |
||||
169 | // If we don't have a valid member we shouldn't be here... |
||||
170 | return $this->redirectBack(); |
||||
171 | } |
||||
172 | } |
||||
173 | |||||
174 | /** |
||||
175 | * Handles the request to start a registration |
||||
176 | * |
||||
177 | * @param HTTPRequest $request |
||||
178 | * @return HTTPResponse |
||||
179 | */ |
||||
180 | public function startRegistration(HTTPRequest $request): HTTPResponse |
||||
181 | { |
||||
182 | $store = $this->getStore(); |
||||
183 | $sessionMember = $store ? $store->getMember() : null; |
||||
0 ignored issues
–
show
|
|||||
184 | $loggedInMember = Security::getCurrentUser(); |
||||
185 | |||||
186 | if (($loggedInMember === null && $sessionMember === null) |
||||
187 | || !$this->getSudoModeService()->check($request->getSession()) |
||||
188 | ) { |
||||
189 | return $this->jsonResponse( |
||||
190 | ['errors' => [ |
||||
191 | _t( |
||||
192 | __CLASS__ . '.NOT_AUTHENTICATING', |
||||
193 | 'You must be logged in or logging in. Please refresh the page and try again.' |
||||
194 | ) |
||||
195 | ]], |
||||
196 | 403 |
||||
197 | ); |
||||
198 | } |
||||
199 | |||||
200 | $method = $this->getMethodRegistry()->getMethodByURLSegment($request->param('Method')); |
||||
201 | |||||
202 | // If the user isn't fully logged in and they already have a registered method, they can't register another |
||||
203 | // provided that they're not registering a backup method |
||||
204 | $registeredMethodCount = $sessionMember && $sessionMember->RegisteredMFAMethods()->count(); |
||||
205 | $isRegisteringBackupMethod = |
||||
206 | $method instanceof MethodInterface && $this->getMethodRegistry()->isBackupMethod($method); |
||||
207 | |||||
208 | if ($loggedInMember === null && $sessionMember && $registeredMethodCount > 0 && !$isRegisteringBackupMethod) { |
||||
209 | return $this->jsonResponse( |
||||
210 | ['errors' => [_t(__CLASS__ . '.MUST_USE_EXISTING_METHOD', 'This member already has an MFA method')]], |
||||
211 | 400 |
||||
212 | ); |
||||
213 | } |
||||
214 | |||||
215 | // Handle the case where the request hasn't provided an appropriate method to register |
||||
216 | if ($method === null) { |
||||
217 | return $this->jsonResponse( |
||||
218 | ['errors' => [_t(__CLASS__ . '.INVALID_METHOD', 'No such method is available')]], |
||||
219 | 400 |
||||
220 | ); |
||||
221 | } |
||||
222 | |||||
223 | // Ensure a store is available using the logged in member if the store doesn't exist |
||||
224 | if (!$store) { |
||||
0 ignored issues
–
show
|
|||||
225 | $store = $this->createStore($loggedInMember); |
||||
226 | } |
||||
227 | |||||
228 | // Delegate to the trait for common handling |
||||
229 | $response = $this->createStartRegistrationResponse($store, $method); |
||||
230 | |||||
231 | // Ensure details are saved to the session |
||||
232 | $store->save($request); |
||||
233 | |||||
234 | return $response; |
||||
235 | } |
||||
236 | |||||
237 | /** |
||||
238 | * Handles the request to verify and process a new registration |
||||
239 | * |
||||
240 | * @param HTTPRequest $request |
||||
241 | * @return HTTPResponse |
||||
242 | */ |
||||
243 | public function finishRegistration(HTTPRequest $request): HTTPResponse |
||||
244 | { |
||||
245 | $store = $this->getStore(); |
||||
246 | $sessionMember = $store ? $store->getMember() : null; |
||||
0 ignored issues
–
show
|
|||||
247 | $loggedInMember = Security::getCurrentUser(); |
||||
248 | |||||
249 | if (($loggedInMember === null && $sessionMember === null) |
||||
250 | || !$this->getSudoModeService()->check($request->getSession()) |
||||
251 | ) { |
||||
252 | return $this->jsonResponse( |
||||
253 | ['errors' => [ |
||||
254 | _t( |
||||
255 | __CLASS__ . '.NOT_AUTHENTICATING', |
||||
256 | 'You must be logged in or logging in. Please refresh the page and try again.' |
||||
257 | ) |
||||
258 | ]], |
||||
259 | 403 |
||||
260 | ); |
||||
261 | } |
||||
262 | |||||
263 | $method = $this->getMethodRegistry()->getMethodByURLSegment($request->param('Method')); |
||||
264 | $result = $this->completeRegistrationRequest($store, $method, $request); |
||||
0 ignored issues
–
show
It seems like
$method can also be of type null ; however, parameter $method of SilverStripe\MFA\Authent...teRegistrationRequest() does only seem to accept SilverStripe\MFA\Method\MethodInterface , 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
Loading history...
|
|||||
265 | |||||
266 | if (!$result->isSuccessful()) { |
||||
267 | return $this->jsonResponse( |
||||
268 | ['errors' => [$result->getMessage()]], |
||||
269 | $result->getContext()['code'] ?? 400 |
||||
270 | ); |
||||
271 | } |
||||
272 | |||||
273 | // If we've completed registration and the member is not already logged in then we need to log them in |
||||
274 | /** @var EnforcementManager $enforcementManager */ |
||||
275 | $enforcementManager = EnforcementManager::create(); |
||||
276 | $mustLogin = $request->getSession()->get(static::SESSION_KEY . '.mustLogin'); |
||||
277 | |||||
278 | // If the user has a valid registration at this point then we can log them in. We must ensure that they're not |
||||
279 | // required to log in though. The "mustLogin" flag is set at the beginning of the MFA process if they have at |
||||
280 | // least one method registered. They should always do that first. In that case we should assert |
||||
281 | // "isLoginComplete" |
||||
282 | if ((!$mustLogin || $this->isVerificationComplete($store)) |
||||
283 | && $enforcementManager->hasCompletedRegistration($sessionMember) |
||||
0 ignored issues
–
show
It seems like
$sessionMember can also be of type null ; however, parameter $member of SilverStripe\MFA\Service...CompletedRegistration() does only seem to accept SilverStripe\Security\Member , 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
Loading history...
|
|||||
284 | ) { |
||||
285 | $this->doPerformLogin($request, $sessionMember); |
||||
0 ignored issues
–
show
It seems like
$sessionMember can also be of type null ; however, parameter $member of SilverStripe\MFA\Authent...ndler::doPerformLogin() does only seem to accept SilverStripe\Security\Member , 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
Loading history...
|
|||||
286 | } |
||||
287 | |||||
288 | return $this->jsonResponse(['success' => true], 201); |
||||
289 | } |
||||
290 | |||||
291 | /** |
||||
292 | * Handle an HTTP request to skip MFA registration |
||||
293 | * |
||||
294 | * @param HTTPRequest $request |
||||
295 | * @return HTTPResponse |
||||
296 | * @throws ValidationException |
||||
297 | */ |
||||
298 | public function skipRegistration(HTTPRequest $request): HTTPResponse |
||||
299 | { |
||||
300 | $loginUrl = Security::login_url(); |
||||
301 | |||||
302 | try { |
||||
303 | $member = $this->getMember(); |
||||
304 | $enforcementManager = EnforcementManager::create(); |
||||
305 | |||||
306 | if (!$enforcementManager->canSkipMFA($member)) { |
||||
307 | Security::singleton()->setSessionMessage( |
||||
308 | _t(__CLASS__ . '.CANNOT_SKIP', 'You cannot skip MFA registration'), |
||||
309 | ValidationResult::TYPE_ERROR |
||||
310 | ); |
||||
311 | return $this->redirect($loginUrl); |
||||
312 | } |
||||
313 | |||||
314 | $member->update(['HasSkippedMFARegistration' => true])->write(); |
||||
315 | $this->extend('onSkipRegistration', $member); |
||||
316 | $this->doPerformLogin($request, $member); |
||||
317 | |||||
318 | // Redirect the user back to wherever they originally came from when they started the login process |
||||
319 | return $this->redirectAfterSuccessfulLogin(); |
||||
320 | } catch (MemberNotFoundException $exception) { |
||||
321 | Security::singleton()->setSessionMessage( |
||||
322 | _t(__CLASS__ . '.CANNOT_SKIP', 'You cannot skip MFA registration'), |
||||
323 | ValidationResult::TYPE_ERROR |
||||
324 | ); |
||||
325 | return $this->redirect($loginUrl); |
||||
326 | } |
||||
327 | } |
||||
328 | |||||
329 | /** |
||||
330 | * Handles the request to start an authentication process with an authenticator (possibly specified by the request) |
||||
331 | * |
||||
332 | * @param HTTPRequest $request |
||||
333 | * @return HTTPResponse |
||||
334 | */ |
||||
335 | public function startVerification(HTTPRequest $request): HTTPResponse |
||||
336 | { |
||||
337 | $store = $this->getStore(); |
||||
338 | // If we don't have a valid member we shouldn't be here, or if sudo mode is not active yet. |
||||
339 | if (!$store || !$store->getMember() || !$this->getSudoModeService()->check($request->getSession())) { |
||||
0 ignored issues
–
show
|
|||||
340 | return $this->jsonResponse(['message' => 'Forbidden'], 403); |
||||
341 | } |
||||
342 | |||||
343 | // Use the provided trait method for handling login |
||||
344 | $response = $this->createStartVerificationResponse( |
||||
345 | $store, |
||||
346 | $this->getMethodRegistry()->getMethodByURLSegment($request->param('Method')) |
||||
347 | ); |
||||
348 | |||||
349 | // Ensure detail is saved to the store |
||||
350 | $store->save($request); |
||||
351 | |||||
352 | // Respond with our method |
||||
353 | return $response; |
||||
354 | } |
||||
355 | |||||
356 | /** |
||||
357 | * Handles requests to authenticate from any MFA method, directing verification to the Method supplied. |
||||
358 | * |
||||
359 | * @param HTTPRequest $request |
||||
360 | * @return HTTPResponse |
||||
361 | */ |
||||
362 | public function finishVerification(HTTPRequest $request): HTTPResponse |
||||
363 | { |
||||
364 | $store = $this->getStore(); |
||||
365 | // Enforce sudo mode |
||||
366 | if (!$this->getSudoModeService()->check($request->getSession())) { |
||||
367 | return $this->jsonResponse([ |
||||
368 | 'message' => _t( |
||||
369 | __CLASS__ . '.SUDO_MODE_REQUIRED', |
||||
370 | 'You need to re-verify your account before continuing. Please reload and try again.' |
||||
371 | ), |
||||
372 | ], 403); |
||||
373 | } |
||||
374 | |||||
375 | if ($store && ($member = $store->getMember()) && $member->isLockedOut()) { |
||||
376 | return $this->jsonResponse([ |
||||
377 | 'message' => _t( |
||||
378 | __CLASS__ . '.LOCKED_OUT', |
||||
379 | 'Your account is temporarily locked. Please try again later.' |
||||
380 | ), |
||||
381 | ], 403); |
||||
382 | } |
||||
383 | |||||
384 | try { |
||||
385 | $result = $this->completeVerificationRequest($store, $request); |
||||
386 | } catch (InvalidMethodException $e) { |
||||
387 | // Invalid method usually means a timeout. A user might be trying to verify before "starting" |
||||
388 | return $this->jsonResponse(['message' => 'Forbidden'], 403); |
||||
389 | } |
||||
390 | |||||
391 | if (!$result->isSuccessful()) { |
||||
392 | $store->getMember()->registerFailedLogin(); |
||||
393 | $code = $result->getContext()['code'] ?? 401; |
||||
394 | |||||
395 | return $this->jsonResponse([ |
||||
396 | 'message' => $result->getMessage(), |
||||
397 | ], $code); |
||||
398 | } |
||||
399 | |||||
400 | if (!$this->isVerificationComplete($store)) { |
||||
401 | return $this->jsonResponse([ |
||||
402 | 'message' => 'Additional authentication required', |
||||
403 | ], 202); |
||||
404 | } |
||||
405 | |||||
406 | // Actually log in the member if the registration is complete |
||||
407 | $member = $store->getMember(); |
||||
408 | |||||
409 | if (EnforcementManager::create()->hasCompletedRegistration($member)) { |
||||
410 | $this->doPerformLogin($request, $member); |
||||
0 ignored issues
–
show
It seems like
$member can also be of type null ; however, parameter $member of SilverStripe\MFA\Authent...ndler::doPerformLogin() does only seem to accept SilverStripe\Security\Member , 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
Loading history...
|
|||||
411 | |||||
412 | // And also clear the session |
||||
413 | $store->clear($request); |
||||
414 | } |
||||
415 | |||||
416 | // We still indicate login has been completed here. The finalisation of registration should take care of it |
||||
417 | return $this->jsonResponse([ |
||||
418 | 'message' => 'Login complete', |
||||
419 | ], 200); |
||||
420 | } |
||||
421 | |||||
422 | public function redirectAfterSuccessfulLogin(): HTTPResponse |
||||
423 | { |
||||
424 | // Assert that we have a member logged in already. We explicitly don't use ->getMember as that will pull from |
||||
425 | // session during the MFA process |
||||
426 | $member = Security::getCurrentUser(); |
||||
427 | $loginUrl = Security::login_url(); |
||||
428 | |||||
429 | if (!$member) { |
||||
0 ignored issues
–
show
|
|||||
430 | Security::singleton()->setSessionMessage( |
||||
431 | _t(__CLASS__ . '.MFA_LOGIN_INCOMPLETE', 'You must provide MFA login details'), |
||||
432 | ValidationResult::TYPE_ERROR |
||||
433 | ); |
||||
434 | return $this->redirect($this->getBackURL() ?: $loginUrl); |
||||
435 | } |
||||
436 | |||||
437 | $request = $this->getRequest(); |
||||
438 | /** @var EnforcementManager $enforcementManager */ |
||||
439 | $enforcementManager = EnforcementManager::create(); |
||||
440 | |||||
441 | // Assert that the member has a valid registration. |
||||
442 | // This is potentially redundant logic as the member should only be logged in if they've fully registered. |
||||
443 | // They're allowed to login if they can skip - so only do assertions if they're not allowed to skip |
||||
444 | // We'll also check that they've registered the required MFA details |
||||
445 | if (!$enforcementManager->canSkipMFA($member) |
||||
446 | && !$enforcementManager->hasCompletedRegistration($member) |
||||
447 | ) { |
||||
448 | // Log them out again |
||||
449 | /** @var IdentityStore $identityStore */ |
||||
450 | $identityStore = Injector::inst()->get(IdentityStore::class); |
||||
451 | $identityStore->logOut($request); |
||||
452 | |||||
453 | Security::singleton()->setSessionMessage( |
||||
454 | _t(__CLASS__ . '.INVALID_REGISTRATION', 'You must complete MFA registration'), |
||||
455 | ValidationResult::TYPE_ERROR |
||||
456 | ); |
||||
457 | return $this->redirect($this->getBackURL() ?: $loginUrl); |
||||
458 | } |
||||
459 | |||||
460 | // Redirecting after successful login expects a getVar to be set, store it before clearing the session data |
||||
461 | /** @see HTTPRequest::offsetSet */ |
||||
462 | $request['BackURL'] = $this->getBackURL(); |
||||
463 | |||||
464 | // Clear the "additional data" |
||||
465 | $request->getSession()->clear(static::SESSION_KEY . '.additionalData'); |
||||
466 | |||||
467 | // Ensure any left over session state is cleaned up |
||||
468 | $store = $this->getStore(); |
||||
469 | if ($store) { |
||||
0 ignored issues
–
show
|
|||||
470 | $store->clear($request); |
||||
471 | } |
||||
472 | $request->getSession()->clear(static::SESSION_KEY . '.mustLogin'); |
||||
473 | |||||
474 | // Delegate to parent logic |
||||
475 | return parent::redirectAfterSuccessfulLogin(); |
||||
476 | } |
||||
477 | |||||
478 | /** |
||||
479 | * @return Member&MemberExtension |
||||
480 | * @throws MemberNotFoundException |
||||
481 | */ |
||||
482 | public function getMember() |
||||
483 | { |
||||
484 | $store = $this->getStore(); |
||||
485 | |||||
486 | if ($store && $store->getMember()) { |
||||
487 | return $store->getMember(); |
||||
488 | } |
||||
489 | |||||
490 | $member = Security::getCurrentUser(); |
||||
491 | |||||
492 | // If we don't have a valid member we shouldn't be here... |
||||
493 | if (!$member) { |
||||
0 ignored issues
–
show
|
|||||
494 | throw new MemberNotFoundException(); |
||||
495 | } |
||||
496 | |||||
497 | return $member; |
||||
498 | } |
||||
499 | |||||
500 | /** |
||||
501 | * @param LoggerInterface $logger |
||||
502 | * @return $this |
||||
503 | */ |
||||
504 | public function setLogger(LoggerInterface $logger): self |
||||
505 | { |
||||
506 | $this->logger = $logger; |
||||
507 | return $this; |
||||
508 | } |
||||
509 | |||||
510 | /** |
||||
511 | * @return LoggerInterface |
||||
512 | */ |
||||
513 | public function getLogger(): ?LoggerInterface |
||||
514 | { |
||||
515 | return $this->logger; |
||||
516 | } |
||||
517 | |||||
518 | /** |
||||
519 | * Adds more options for the back URL - to be returned from a current MFA session store |
||||
520 | * |
||||
521 | * @return string|null |
||||
522 | */ |
||||
523 | public function getBackURL(): ?string |
||||
524 | { |
||||
525 | $backURL = parent::getBackURL(); |
||||
526 | |||||
527 | if (!$backURL && $this->getRequest()) { |
||||
528 | $data = $this->getRequest()->getSession()->get(static::SESSION_KEY . '.additionalData'); |
||||
529 | if (isset($data['BackURL'])) { |
||||
530 | $backURL = $data['BackURL']; |
||||
531 | } |
||||
532 | } |
||||
533 | |||||
534 | return $backURL; |
||||
535 | } |
||||
536 | |||||
537 | /** |
||||
538 | * Respond with the given array as a JSON response |
||||
539 | * |
||||
540 | * @param array $response |
||||
541 | * @param int $code The HTTP response code to set on the response |
||||
542 | * @return HTTPResponse |
||||
543 | */ |
||||
544 | public function jsonResponse(array $response, int $code = 200): HTTPResponse |
||||
545 | { |
||||
546 | return HTTPResponse::create(json_encode($response)) |
||||
547 | ->addHeader('Content-Type', 'application/json') |
||||
548 | ->setStatusCode($code); |
||||
549 | } |
||||
550 | |||||
551 | /** |
||||
552 | * Complete the login process for the given member by calling "performLogin" on the parent class |
||||
553 | * |
||||
554 | * @param HTTPRequest $request |
||||
555 | * @param Member&MemberExtension $member |
||||
556 | */ |
||||
557 | protected function doPerformLogin(HTTPRequest $request, Member $member) |
||||
558 | { |
||||
559 | // Load the previously stored data from session and perform the login using it... |
||||
560 | $data = $request->getSession()->get(static::SESSION_KEY . '.additionalData') ?: []; |
||||
561 | |||||
562 | // Check that we don't have a logged in member before actually performing a login |
||||
563 | $currentMember = Security::getCurrentUser(); |
||||
564 | |||||
565 | if (!$currentMember) { |
||||
0 ignored issues
–
show
|
|||||
566 | // These next two lines are pulled from "parent::doLogin()" |
||||
567 | $this->performLogin($member, $data, $request); |
||||
568 | // Allow operations on the member after successful login |
||||
569 | parent::extend('afterLogin', $member); |
||||
570 | } |
||||
571 | } |
||||
572 | |||||
573 | /** |
||||
574 | * @return MethodRegistry |
||||
575 | */ |
||||
576 | protected function getMethodRegistry(): MethodRegistry |
||||
577 | { |
||||
578 | return MethodRegistry::singleton(); |
||||
579 | } |
||||
580 | } |
||||
581 |