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 array $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, 
  51         public function __construct(protected RouterInterface 
$router, protected array $options = []) { 
  53                 $this->setOptions($options); 
  59          * This is called when an interactive authentication attempt succeeds 
  61          * In use_referer case it will handle correctly when login_path is a route name or path 
  63         public function onAuthenticationSuccess(Request 
$request, TokenInterface 
$token): Response 
{ 
  65                 $login = $request->get('_route'); 
  67                 //With login path option 
  68                 if (!empty($loginPath = $this->options
['login_path'])) { 
  70                         if ($loginPath[0] == '/') { 
  71                                 //Create login path request instance 
  72                                 $req = Request
::create($loginPath); 
  74                                 //Get login path pathinfo 
  75                                 $path = $req->getPathInfo(); 
  78                                 $path = str_replace($request->getScriptName(), '', $path); 
  80                                 //Try with login path path 
  83                                         $oldContext = $this->router
->getContext(); 
  86                                         //XXX: prevent MethodNotAllowedException on GET only routes because our context method is POST 
  87                                         //XXX: see vendor/symfony/routing/Matcher/Dumper/CompiledUrlMatcherTrait.php +42 
  88                                         $this->router
->setContext(new RequestContext()); 
  90                                         //Retrieve route matching path 
  91                                         $route = $this->router
->match($path); 
  94                                         $this->router
->setContext($oldContext); 
 100                                         if (!empty($route['_route'])) { 
 102                                                 $login = $route['_route']; 
 105                                 } catch (ResourceNotFoundException 
$e) { 
 106                                         throw new \
UnexpectedValueException(sprintf('The "login_path" path "%s" must match a route', $this->options
['login_path']), $e->getCode(), $e); 
 110                                 //Try with login path route 
 112                                         //Retrieve route matching path 
 113                                         $path = $this->router
->generate($loginPath); 
 118                                 } catch (RouteNotFoundException 
$e) { 
 119                                         throw new \
UnexpectedValueException(sprintf('The "login_path" route "%s" must match a route name', $this->options
['login_path']), $e->getCode(), $e); 
 120                                 //Ignore missing or invalid parameter 
 121                                 //XXX: useless or would not work ? 
 122                                 } catch (MissingMandatoryParametersException
|InvalidParameterException 
$e) { 
 129                 //Without always_use_default_target_path 
 130                 if (empty($this->options
['always_use_default_target_path'])) { 
 132                         if ($targetUrl = ParameterBagUtils
::getRequestParameterValue($request, $this->options
['target_path_parameter'])) { 
 136                                 //Return redirect to url response 
 137                                 return new RedirectResponse($url, 302); 
 138                         //With session and target path in session 
 140                                 !empty($this->providerKey
) && 
 141                                 ($session = $request->getSession()) && 
 142                                 ($targetUrl = $this->getTargetPath($session, $this->providerKey
)) 
 144                                 //Remove session target path 
 145                                 $this->removeTargetPath($session, $this->providerKey
); 
 150                                 //Return redirect to url response 
 151                                 return new RedirectResponse($url, 302); 
 152                         //Extract and process referer 
 153                         } elseif ($this->options
['use_referer'] && ($targetUrl = $request->headers
->get('referer'))) { 
 154                                 //Create referer request instance 
 155                                 $req = Request
::create($targetUrl); 
 158                                 $path = $req->getPathInfo(); 
 160                                 //Get referer query string 
 161                                 $query = $req->getQueryString(); 
 164                                 $path = str_replace($request->getScriptName(), '', $path); 
 166                                 //Try with referer path 
 169                                         $oldContext = $this->router
->getContext(); 
 171                                         //Force clean context 
 172                                         //XXX: prevent MethodNotAllowedException on GET only routes because our context method is POST 
 173                                         //XXX: see vendor/symfony/routing/Matcher/Dumper/CompiledUrlMatcherTrait.php +42 
 174                                         $this->router
->setContext(new RequestContext()); 
 176                                         //Retrieve route matching path 
 177                                         $route = $this->router
->match($path); 
 180                                         $this->router
->setContext($oldContext); 
 185                                         //With differing route from login one 
 186                                         if (($name = $route['_route']) != $login) { 
 187                                                 //Remove route and controller from route defaults 
 188                                                 unset($route['_route'], $route['_controller'], $route['_canonical_route']); 
 190                                                 //Set url to generated one from referer route 
 191                                                 $url = $this->router
->generate($name, $route); 
 193                                                 //Return redirect to url response 
 194                                                 return new RedirectResponse($url, 302); 
 197                                 } catch (ResourceNotFoundException 
$e) { 
 198                                         //Unset target url, route and name 
 199                                         unset($targetUrl, $route, $name); 
 204                 //With default target path option 
 205                 if (!empty($defaultPath = $this->options
['default_target_path'])) { 
 207                         if ($defaultPath[0] == '/') { 
 208                                 //Create login path request instance 
 209                                 $req = Request
::create($defaultPath); 
 211                                 //Get login path pathinfo 
 212                                 $path = $req->getPathInfo(); 
 215                                 $path = str_replace($request->getScriptName(), '', $path); 
 217                                 //Try with login path path 
 220                                         $oldContext = $this->router
->getContext(); 
 222                                         //Force clean context 
 223                                         //XXX: prevent MethodNotAllowedException on GET only routes because our context method is POST 
 224                                         //XXX: see vendor/symfony/routing/Matcher/Dumper/CompiledUrlMatcherTrait.php +42 
 225                                         $this->router
->setContext(new RequestContext()); 
 227                                         //Retrieve route matching path 
 228                                         $route = $this->router
->match($path); 
 231                                         $this->router
->setContext($oldContext); 
 236                                         //Without login route name 
 237                                         if (($name = $route['_route']) != $login) { 
 238                                                 //Remove route and controller from route defaults 
 239                                                 unset($route['_route'], $route['_controller'], $route['_canonical_route']); 
 242                                                 $url = $this->router
->generate($name, $route); 
 244                                                 //Return redirect to url response 
 245                                                 return new RedirectResponse($url, 302); 
 246                                         //With logout route name 
 248                                                 //Unset default path, name and route 
 249                                                 unset($defaultPath, $name, $route); 
 252                                 } catch (ResourceNotFoundException 
$e) { 
 253                                         throw \
Exception('', $e->getCode(), $e); 
 254                                         //Unset default path, name and route 
 255                                         unset($defaultPath, $name, $route); 
 257                         //Without login route name 
 258                         } elseif ($defaultPath != $login) { 
 259                                 //Try with login path route 
 261                                         //Retrieve route matching path 
 262                                         $url = $this->router
->generate($defaultPath); 
 264                                         //Return redirect to url response 
 265                                         return new RedirectResponse($url, 302); 
 266                                 //Route not found, missing parameter or invalid parameter 
 267                                 } catch (RouteNotFoundException
|MissingMandatoryParametersException
|InvalidParameterException 
$e) { 
 268                                         //Unset default path and url 
 269                                         unset($defaultPath, $url); 
 275                 throw new \
UnexpectedValueException('You must provide a valid login target url or route name');