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 
{ 
  41         protected array $config; 
  42         protected array $options; 
  43         protected array $defaultOptions = [ 
  44                 'failure_path' => null, 
  45                 'failure_forward' => false, 
  46                 'login_path' => '/login', 
  47                 'failure_path_parameter' => '_failure_path', 
  53         protected RouterInterface 
$router; 
  58         protected SluggerUtil 
$slugger; 
  61          * @xxx Second argument will be replaced by security.firewalls.main.logout.target 
  62          * @see vendor/symfony/security-bundle/DependencyInjection/SecurityExtension.php +360 
  66         public function __construct(HttpKernelInterface 
$httpKernel, HttpUtils 
$httpUtils, array $options, LoggerInterface 
$logger, ContainerInterface 
$container, RouterInterface 
$router, SluggerUtil 
$slugger) { 
  68                 $this->config 
= $container->getParameter(self
::getAlias()); 
  71                 $this->router 
= $router; 
  74                 $this->slugger 
= $slugger; 
  76                 //Call parent constructor 
  77                 parent
::__construct($httpKernel, $httpUtils, $options, $logger); 
  81          * This is called when an interactive authentication attempt fails 
  83          * User may retrieve mail + field + hash for each unactivated/locked accounts 
  87         public function onAuthenticationFailure(Request 
$request, AuthenticationException 
$exception): Response 
{ 
  88                 //With bad credential exception 
  89                 if ($exception instanceof BadCredentialsException
) { 
  90                         //With parent exception 
  91                         if ($parent = $exception->getPrevious()) { 
  93                                 //TODO: check form _token validity ??? 
  95                                         $request->request
->has('login') && 
  96                                         !empty($login = $request->request
->get('login')) && 
  97                                         !empty($mail = $login['mail']) 
  99                                         //Redirect on register 
 100                                         if ($parent instanceof UnactivatedException 
|| $parent instanceof DisabledException
) { 
 101                                                 //Set extra parameters 
 102                                                 $extra = ['mail' => $smail = $this->slugger
->short($mail), 'field' => $sfield = $this->slugger
->serialize([]), 'hash' => $this->slugger
->hash($smail.$sfield)]; 
 104                                                 //With failure target path option 
 105                                                 if (!empty($failurePath = $this->options
['failure_path'])) { 
 107                                                         if ($failurePath[0] == '/') { 
 108                                                                 //Create login path request instance 
 109                                                                 $req = Request
::create($failurePath); 
 111                                                                 //Get login path pathinfo 
 112                                                                 $path = $req->getPathInfo(); 
 115                                                                 $path = str_replace($request->getScriptName(), '', $path); 
 117                                                                 //Try with login path path 
 120                                                                         $oldContext = $this->router
->getContext(); 
 122                                                                         //Force clean context 
 123                                                                         //XXX: prevent MethodNotAllowedException on GET only routes because our context method is POST 
 124                                                                         //XXX: see vendor/symfony/routing/Matcher/Dumper/CompiledUrlMatcherTrait.php +42 
 125                                                                         $this->router
->setContext(new RequestContext()); 
 127                                                                         //Retrieve route matching path 
 128                                                                         $route = $this->router
->match($path); 
 131                                                                         $this->router
->setContext($oldContext); 
 137                                                                         if ($name = $route['_route']) { 
 138                                                                                 //Remove route and controller from route defaults 
 139                                                                                 unset($route['_route'], $route['_controller'], $route['_canonical_route']); 
 142                                                                                 $url = $this->router
->generate($name, $extra+
$route); 
 144                                                                                 //Return redirect to url response 
 145                                                                                 return new RedirectResponse($url, 302); 
 148                                                                 } catch (ResourceNotFoundException 
$e) { 
 149                                                                         //Unset default path, name and route 
 150                                                                         unset($failurePath, $name, $route); 
 154                                                                 //Try with login path route 
 156                                                                         //Retrieve route matching path 
 157                                                                         $url = $this->router
->generate($failurePath, $extra); 
 159                                                                         //Return redirect to url response 
 160                                                                         return new RedirectResponse($url, 302); 
 161                                                                 //Route not found, missing parameter or invalid parameter 
 162                                                                 } catch (RouteNotFoundException
|MissingMandatoryParametersException
|InvalidParameterException 
$e) { 
 163                                                                         //Unset default path and url 
 164                                                                         unset($failurePath, $url); 
 169                                                 //With index route from config 
 170                                                 if (!empty($name = $this->config
['route']['register']['name']) && is_array($context = $this->config
['route']['register']['context'])) { 
 174                                                                 $url = $this->router
->generate($name, $extra+
$context); 
 176                                                                 //Return generated route 
 177                                                                 return new RedirectResponse($url, 302); 
 179                                                         } catch (ResourceNotFoundException 
$e) { 
 180                                                                 //Unset name and context 
 181                                                                 unset($name, $context); 
 185                                                 //With login target path option 
 186                                                 if (!empty($loginPath = $this->options
['login_path'])) { 
 188                                                         if ($loginPath[0] == '/') { 
 189                                                                 //Create login path request instance 
 190                                                                 $req = Request
::create($loginPath); 
 192                                                                 //Get login path pathinfo 
 193                                                                 $path = $req->getPathInfo(); 
 196                                                                 $path = str_replace($request->getScriptName(), '', $path); 
 198                                                                 //Try with login path path 
 201                                                                         $oldContext = $this->router
->getContext(); 
 203                                                                         //Force clean context 
 204                                                                         //XXX: prevent MethodNotAllowedException on GET only routes because our context method is POST 
 205                                                                         //XXX: see vendor/symfony/routing/Matcher/Dumper/CompiledUrlMatcherTrait.php +42 
 206                                                                         $this->router
->setContext(new RequestContext()); 
 208                                                                         //Retrieve route matching path 
 209                                                                         $route = $this->router
->match($path); 
 212                                                                         $this->router
->setContext($oldContext); 
 218                                                                         if ($name = $route['_route']) { 
 219                                                                                 //Remove route and controller from route defaults 
 220                                                                                 unset($route['_route'], $route['_controller'], $route['_canonical_route']); 
 223                                                                                 $url = $this->router
->generate($name, $extra+
$route); 
 225                                                                                 //Return redirect to url response 
 226                                                                                 return new RedirectResponse($url, 302); 
 229                                                                 } catch (ResourceNotFoundException 
$e) { 
 230                                                                         //Unset default path, name and route 
 231                                                                         unset($loginPath, $name, $route); 
 235                                                                 //Try with login path route 
 237                                                                         //Retrieve route matching path 
 238                                                                         $url = $this->router
->generate($loginPath, $extra); 
 240                                                                         //Return redirect to url response 
 241                                                                         return new RedirectResponse($url, 302); 
 242                                                                 //Route not found, missing parameter or invalid parameter 
 243                                                                 } catch (RouteNotFoundException
|MissingMandatoryParametersException
|InvalidParameterException 
$e) { 
 244                                                                         //Unset default path and url 
 245                                                                         unset($loginPath, $url); 
 254                 //Call parent function 
 255                 return parent
::onAuthenticationFailure($request, $exception); 
 261         public function getAlias(): string { 
 262                 return RapsysUserBundle
::getAlias();