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 Symfony\Component\HttpFoundation\RedirectResponse
; 
  15 use Symfony\Component\HttpFoundation\Request
; 
  16 use Symfony\Component\HttpFoundation\Response
; 
  17 use Symfony\Component\Routing\Exception\InvalidParameterException
; 
  18 use Symfony\Component\Routing\Exception\MissingMandatoryParametersException
; 
  19 use Symfony\Component\Routing\Exception\ResourceNotFoundException
; 
  20 use Symfony\Component\Routing\Exception\RouteNotFoundException
; 
  21 use Symfony\Component\Routing\RequestContext
; 
  22 use Symfony\Component\Routing\RouterInterface
; 
  23 use Symfony\Component\Security\Core\Authentication\Token\TokenInterface
; 
  24 use Symfony\Component\Security\Http\Authentication\DefaultAuthenticationSuccessHandler
; 
  25 use Symfony\Component\Security\Http\ParameterBagUtils
; 
  26 use Symfony\Component\Security\Http\Util\TargetPathTrait
; 
  31 class AuthenticationSuccessHandler 
extends DefaultAuthenticationSuccessHandler 
{ 
  33          * Allows to use getTargetPath and removeTargetPath private functions 
  40     protected $defaultOptions = [ 
  41         'always_use_default_target_path' => false, 
  42         'default_target_path' => '/', 
  43         'login_path' => '/login', 
  44         'target_path_parameter' => '_target_path', 
  45         'use_referer' => false, 
  61         public function __construct(RouterInterface 
$router, array $options = []) { 
  63                 $this->setOptions($options); 
  66                 $this->router 
= $router; 
  70          * This is called when an interactive authentication attempt succeeds 
  72          * In use_referer case it will handle correctly when login_path is a route name or path 
  76         public function onAuthenticationSuccess(Request 
$request, TokenInterface 
$token): Response 
{ 
  78                 $login = $request->get('_route'); 
  80                 //With login path option 
  81                 if (!empty($loginPath = $this->options
['login_path'])) { 
  83                         if ($loginPath[0] == '/') { 
  84                                 //Create login path request instance 
  85                                 $req = Request
::create($loginPath); 
  87                                 //Get login path pathinfo 
  88                                 $path = $req->getPathInfo(); 
  91                                 $path = str_replace($request->getScriptName(), '', $path); 
  93                                 //Try with login path path 
  96                                         $oldContext = $this->router
->getContext(); 
  99                                         //XXX: prevent MethodNotAllowedException on GET only routes because our context method is POST 
 100                                         //XXX: see vendor/symfony/routing/Matcher/Dumper/CompiledUrlMatcherTrait.php +42 
 101                                         $this->router
->setContext(new RequestContext()); 
 103                                         //Retrieve route matching path 
 104                                         $route = $this->router
->match($path); 
 107                                         $this->router
->setContext($oldContext); 
 113                                         if (!empty($route['_route'])) { 
 115                                                 $login = $route['_route']; 
 118                                 } catch (ResourceNotFoundException 
$e) { 
 119                                         throw new \
UnexpectedValueException(sprintf('The "login_path" path "%s" must match a route', $this->options
['login_path']), $e->getCode(), $e); 
 123                                 //Try with login path route 
 125                                         //Retrieve route matching path 
 126                                         $path = $this->router
->generate($loginPath); 
 131                                 } catch (RouteNotFoundException 
$e) { 
 132                                         throw new \
UnexpectedValueException(sprintf('The "login_path" route "%s" must match a route name', $this->options
['login_path']), $e->getCode(), $e); 
 133                                 //Ignore missing or invalid parameter 
 134                                 //XXX: useless or would not work ? 
 135                                 } catch (MissingMandatoryParametersException
|InvalidParameterException 
$e) { 
 142                 //Without always_use_default_target_path 
 143                 if (empty($this->options
['always_use_default_target_path'])) { 
 145                         if ($targetUrl = ParameterBagUtils
::getRequestParameterValue($request, $this->options
['target_path_parameter'])) { 
 149                                 //Return redirect to url response 
 150                                 return new RedirectResponse($url, 302); 
 151                         //With session and target path in session 
 153                                 !empty($this->providerKey
) && 
 154                                 ($session = $request->getSession()) && 
 155                                 ($targetUrl = $this->getTargetPath($session, $this->providerKey
)) 
 157                                 //Remove session target path 
 158                                 $this->removeTargetPath($session, $this->providerKey
); 
 163                                 //Return redirect to url response 
 164                                 return new RedirectResponse($url, 302); 
 165                         //Extract and process referer 
 166                         } elseif ($this->options
['use_referer'] && ($targetUrl = $request->headers
->get('referer'))) { 
 167                                 //Create referer request instance 
 168                                 $req = Request
::create($targetUrl); 
 171                                 $path = $req->getPathInfo(); 
 173                                 //Get referer query string 
 174                                 $query = $req->getQueryString(); 
 177                                 $path = str_replace($request->getScriptName(), '', $path); 
 179                                 //Try with referer path 
 182                                         $oldContext = $this->router
->getContext(); 
 184                                         //Force clean context 
 185                                         //XXX: prevent MethodNotAllowedException on GET only routes because our context method is POST 
 186                                         //XXX: see vendor/symfony/routing/Matcher/Dumper/CompiledUrlMatcherTrait.php +42 
 187                                         $this->router
->setContext(new RequestContext()); 
 189                                         //Retrieve route matching path 
 190                                         $route = $this->router
->match($path); 
 193                                         $this->router
->setContext($oldContext); 
 198                                         //With differing route from login one 
 199                                         if (($name = $route['_route']) != $login) { 
 200                                                 //Remove route and controller from route defaults 
 201                                                 unset($route['_route'], $route['_controller'], $route['_canonical_route']); 
 203                                                 //Set url to generated one from referer route 
 204                                                 $url = $this->router
->generate($name, $route); 
 206                                                 //Return redirect to url response 
 207                                                 return new RedirectResponse($url, 302); 
 210                                 } catch (ResourceNotFoundException 
$e) { 
 211                                         //Unset target url, route and name 
 212                                         unset($targetUrl, $route, $name); 
 217                 //With default target path option 
 218                 if (!empty($defaultPath = $this->options
['default_target_path'])) { 
 220                         if ($defaultPath[0] == '/') { 
 221                                 //Create login path request instance 
 222                                 $req = Request
::create($defaultPath); 
 224                                 //Get login path pathinfo 
 225                                 $path = $req->getPathInfo(); 
 228                                 $path = str_replace($request->getScriptName(), '', $path); 
 230                                 //Try with login path path 
 233                                         $oldContext = $this->router
->getContext(); 
 235                                         //Force clean context 
 236                                         //XXX: prevent MethodNotAllowedException on GET only routes because our context method is POST 
 237                                         //XXX: see vendor/symfony/routing/Matcher/Dumper/CompiledUrlMatcherTrait.php +42 
 238                                         $this->router
->setContext(new RequestContext()); 
 240                                         //Retrieve route matching path 
 241                                         $route = $this->router
->match($path); 
 244                                         $this->router
->setContext($oldContext); 
 249                                         //Without login route name 
 250                                         if (($name = $route['_route']) != $login) { 
 251                                                 //Remove route and controller from route defaults 
 252                                                 unset($route['_route'], $route['_controller'], $route['_canonical_route']); 
 255                                                 $url = $this->router
->generate($name, $route); 
 257                                                 //Return redirect to url response 
 258                                                 return new RedirectResponse($url, 302); 
 259                                         //With logout route name 
 261                                                 //Unset default path, name and route 
 262                                                 unset($defaultPath, $name, $route); 
 265                                 } catch (ResourceNotFoundException 
$e) { 
 266                                         throw \
Exception('', $e->getCode(), $e); 
 267                                         //Unset default path, name and route 
 268                                         unset($defaultPath, $name, $route); 
 270                         //Without login route name 
 271                         } elseif ($defaultPath != $login) { 
 272                                 //Try with login path route 
 274                                         //Retrieve route matching path 
 275                                         $url = $this->router
->generate($defaultPath); 
 277                                         //Return redirect to url response 
 278                                         return new RedirectResponse($url, 302); 
 279                                 //Route not found, missing parameter or invalid parameter 
 280                                 } catch (RouteNotFoundException
|MissingMandatoryParametersException
|InvalidParameterException 
$e) { 
 281                                         //Unset default path and url 
 282                                         unset($defaultPath, $url); 
 288                 throw new \
UnexpectedValueException('You must provide a valid login target url or route name');