1 <?php
declare(strict_types
=1);
4 * This file is part of the Rapsys UserBundle package.
6 * (c) Raphaël Gertz <symfony@rapsys.eu>
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
12 namespace Rapsys\UserBundle\Handler
;
14 use Psr\Log\LoggerInterface
;
15 use Symfony\Component\DependencyInjection\ContainerInterface
;
16 use Symfony\Component\HttpFoundation\RedirectResponse
;
17 use Symfony\Component\HttpFoundation\Request
;
18 use Symfony\Component\HttpFoundation\Response
;
19 use Symfony\Component\HttpKernel\HttpKernelInterface
;
20 use Symfony\Component\Routing\Exception\ResourceNotFoundException
;
21 use Symfony\Component\Routing\RequestContext
;
22 use Symfony\Component\Routing\RouterInterface
;
23 use Symfony\Component\Security\Core\Exception\AuthenticationException
;
24 use Symfony\Component\Security\Core\Exception\BadCredentialsException
;
25 use Symfony\Component\Security\Core\Exception\DisabledException
;
26 use Symfony\Component\Security\Http\Authentication\DefaultAuthenticationFailureHandler
;
27 use Symfony\Component\Security\Http\HttpUtils
;
28 use Symfony\Component\Security\Http\ParameterBagUtils
;
30 use Rapsys\PackBundle\Util\SluggerUtil
;
31 use Rapsys\UserBundle\Exception\UnactivatedException
;
32 use Rapsys\UserBundle\RapsysUserBundle
;
37 class AuthenticationFailureHandler
extends DefaultAuthenticationFailureHandler
{
43 protected $defaultOptions = [
44 'failure_path' => null,
45 'failure_forward' => false,
46 'login_path' => '/login',
47 'failure_path_parameter' => '_failure_path',
63 public function __construct(HttpKernelInterface
$httpKernel, HttpUtils
$httpUtils, array $options = [], LoggerInterface
$logger, ContainerInterface
$container, RouterInterface
$router, SluggerUtil
$slugger) {
65 $this->config
= $container->getParameter(self
::getAlias());
68 $this->router
= $router;
71 $this->slugger
= $slugger;
73 //Call parent constructor
74 parent
::__construct($httpKernel, $httpUtils, $options, $logger);
78 * This is called when an interactive authentication attempt fails
80 * User may retrieve mail + field + hash for each unactivated/locked accounts
84 public function onAuthenticationFailure(Request
$request, AuthenticationException
$exception): Response
{
85 //With bad credential exception
86 if ($exception instanceof BadCredentialsException
) {
87 //With parent exception
88 if ($parent = $exception->getPrevious()) {
90 //TODO: check form _token validity ???
92 $request->request
->has('login') &&
93 !empty($login = $request->request
->get('login')) &&
94 !empty($mail = $login['mail'])
96 //Redirect on register
97 if ($parent instanceof UnactivatedException
|| $parent instanceof DisabledException
) {
98 //Set extra parameters
99 $extra = ['mail' => $smail = $this->slugger
->short($mail), 'field' => $sfield = $this->slugger
->serialize([]), 'hash' => $this->slugger
->hash($smail.$sfield)];
101 //With failure target path option
102 if (!empty($failurePath = $this->options
['failure_path'])) {
104 if ($failurePath[0] == '/') {
105 //Create login path request instance
106 $req = Request
::create($failurePath);
108 //Get login path pathinfo
109 $path = $req->getPathInfo();
112 $path = str_replace($request->getScriptName(), '', $path);
114 //Try with login path path
117 $oldContext = $this->router
->getContext();
119 //Force clean context
120 //XXX: prevent MethodNotAllowedException on GET only routes because our context method is POST
121 //XXX: see vendor/symfony/routing/Matcher/Dumper/CompiledUrlMatcherTrait.php +42
122 $this->router
->setContext(new RequestContext());
124 //Retrieve route matching path
125 $route = $this->router
->match($path);
128 $this->router
->setContext($oldContext);
134 if ($name = $route['_route']) {
135 //Remove route and controller from route defaults
136 unset($route['_route'], $route['_controller'], $route['_canonical_route']);
139 $url = $this->router
->generate($name, $extra+
$route);
141 //Return redirect to url response
142 return new RedirectResponse($url, 302);
145 } catch (ResourceNotFoundException
$e) {
146 //Unset default path, name and route
147 unset($failurePath, $name, $route);
151 //Try with login path route
153 //Retrieve route matching path
154 $url = $this->router
->generate($failurePath, $extra);
156 //Return redirect to url response
157 return new RedirectResponse($url, 302);
158 //Route not found, missing parameter or invalid parameter
159 } catch (RouteNotFoundException
|MissingMandatoryParametersException
|InvalidParameterException
$e) {
160 //Unset default path and url
161 unset($failurePath, $url);
166 //With index route from config
167 if (!empty($name = $this->config
['route']['register']['name']) && is_array($context = $this->config
['route']['register']['context'])) {
171 $url = $this->router
->generate($name, $extra+
$context);
173 //Return generated route
174 return new RedirectResponse($url, 302);
176 } catch (ResourceNotFoundException
$e) {
177 //Unset name and context
178 unset($name, $context);
182 //With login target path option
183 if (!empty($loginPath = $this->options
['login_path'])) {
185 if ($loginPath[0] == '/') {
186 //Create login path request instance
187 $req = Request
::create($loginPath);
189 //Get login path pathinfo
190 $path = $req->getPathInfo();
193 $path = str_replace($request->getScriptName(), '', $path);
195 //Try with login path path
198 $oldContext = $this->router
->getContext();
200 //Force clean context
201 //XXX: prevent MethodNotAllowedException on GET only routes because our context method is POST
202 //XXX: see vendor/symfony/routing/Matcher/Dumper/CompiledUrlMatcherTrait.php +42
203 $this->router
->setContext(new RequestContext());
205 //Retrieve route matching path
206 $route = $this->router
->match($path);
209 $this->router
->setContext($oldContext);
215 if ($name = $route['_route']) {
216 //Remove route and controller from route defaults
217 unset($route['_route'], $route['_controller'], $route['_canonical_route']);
220 $url = $this->router
->generate($name, $extra+
$route);
222 //Return redirect to url response
223 return new RedirectResponse($url, 302);
226 } catch (ResourceNotFoundException
$e) {
227 //Unset default path, name and route
228 unset($loginPath, $name, $route);
232 //Try with login path route
234 //Retrieve route matching path
235 $url = $this->router
->generate($loginPath, $extra);
237 //Return redirect to url response
238 return new RedirectResponse($url, 302);
239 //Route not found, missing parameter or invalid parameter
240 } catch (RouteNotFoundException
|MissingMandatoryParametersException
|InvalidParameterException
$e) {
241 //Unset default path and url
242 unset($loginPath, $url);
251 //Call parent function
252 return parent
::onAuthenticationFailure($request, $exception);
258 public function getAlias(): string {
259 return RapsysUserBundle
::getAlias();