]> Raphaël G. Git Repositories - userbundle/blobdiff - Handler/LogoutSuccessHandler.php
Add auth success handler
[userbundle] / Handler / LogoutSuccessHandler.php
index 34a7b088bdea159b19b7763bb75db673dd5d4dc4..825cd5b69bd5a7f973a0ad9b78185b5fca4ede3e 100644 (file)
@@ -1,19 +1,35 @@
-<?php
+<?php declare(strict_types=1);
+
+/*
+ * This file is part of the Rapsys UserBundle package.
+ *
+ * (c) Raphaël Gertz <symfony@rapsys.eu>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
 
 namespace Rapsys\UserBundle\Handler;
 
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\HttpFoundation\RedirectResponse;
 use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
 use Symfony\Component\Routing\Exception\ResourceNotFoundException;
+use Symfony\Component\Routing\RequestContext;
 use Symfony\Component\Routing\RouterInterface;
-use Symfony\Component\Security\Http\Logout\LogoutSuccessHandlerInterface;
+use Symfony\Component\Security\Http\Logout\DefaultLogoutSuccessHandler;
+
+use Rapsys\UserBundle\RapsysUserBundle;
 
-class LogoutSuccessHandler implements LogoutSuccessHandlerInterface {
+/**
+ * {@inheritdoc}
+ */
+class LogoutSuccessHandler extends DefaultLogoutSuccessHandler {
        /**
-        * {@inheritdoc}
+        * Config array
         */
-       protected $container;
+       protected $config;
 
        /**
         * {@inheritdoc}
@@ -23,20 +39,34 @@ class LogoutSuccessHandler implements LogoutSuccessHandlerInterface {
        /**
         * {@inheritdoc}
         */
-       public function __construct(ContainerInterface $container, RouterInterface $router) {
-               $this->container = $container;
+       protected $targetUrl;
+
+       /**
+        * @xxx Second argument will be replaced by security.firewalls.main.logout.target
+        * @see vendor/symfony/security-bundle/DependencyInjection/SecurityExtension.php +360
+        *
+        * {@inheritdoc}
+        */
+       public function __construct(ContainerInterface $container, string $targetUrl = '/', RouterInterface $router) {
+               //Set config
+               $this->config = $container->getParameter(self::getAlias());
+
+               //Set target url
+               $this->targetUrl = $targetUrl;
+
+               //Set router
                $this->router = $router;
        }
 
        /**
         * {@inheritdoc}
         */
-       public function onLogoutSuccess(Request $request) {
+       public function onLogoutSuccess(Request $request): Response {
                //Retrieve logout route
                $logout = $request->get('_route');
 
                //Extract and process referer
-               if ($referer = $request->headers->get('referer')) {
+               if (($referer = $request->headers->get('referer'))) {
                        //Create referer request instance
                        $req = Request::create($referer);
 
@@ -51,76 +81,116 @@ class LogoutSuccessHandler implements LogoutSuccessHandlerInterface {
 
                        //Try with referer path
                        try {
+                               //Save old context
+                               $oldContext = $this->router->getContext();
+
+                               //Force clean context
+                               //XXX: prevent MethodNotAllowedException on GET only routes because our context method is POST
+                               //XXX: see vendor/symfony/routing/Matcher/Dumper/CompiledUrlMatcherTrait.php +42
+                               $this->router->setContext(new RequestContext());
+
                                //Retrieve route matching path
                                $route = $this->router->match($path);
 
-                               //With router differing from logout one
-                               if (($name = $route['_route']) == $logout) {
-                                       #throw new ResourceNotFoundException('Identical referer and logout route');
-                                       //Unset referer to fallback to default route
-                                       unset($referer);
-                               //With route matching logout
-                               } else {
+                               //Reset context
+                               $this->router->setContext($oldContext);
+
+                               //Clear old context
+                               unset($oldContext);
+
+                               //Without logout route name
+                               if (($name = $route['_route']) != $logout) {
                                        //Remove route and controller from route defaults
                                        unset($route['_route'], $route['_controller'], $route['_canonical_route']);
 
                                        //Generate url
                                        $url = $this->router->generate($name, $route);
+
+                                       //Return generated route
+                                       return new RedirectResponse($url, 302);
+                               //With logout route name
+                               } else {
+                                       //Unset referer and route
+                                       unset($referer, $route);
                                }
                        //No route matched
                        } catch (ResourceNotFoundException $e) {
-                               //Unset referer to fallback to default route
-                               unset($referer);
+                               //Unset referer and route
+                               unset($referer, $route);
                        }
                }
 
-               //Referer empty or unusable
-               if (empty($referer)) {
-                       //Try with / path
-                       try {
-                               //Retrieve route matching /
-                               $route = $this->router->match('/');
-
-                               //Verify that it differ from current one
-                               if (($name = $route['_route']) == $logout) {
-                                       throw new ResourceNotFoundException('Identical referer and logout route');
+               //With index route from config
+               if (!empty($name = $this->config['route']['index']['name']) && is_array($context = $this->config['route']['index']['context'])) {
+                       //Without logout route name
+                       if (($name = $route['_route']) != $logout) {
+                               //Try index route
+                               try {
+                                       //Generate url
+                                       $url = $this->router->generate($name, $context);
+
+                                       //Return generated route
+                                       return new RedirectResponse($url, 302);
+                               //No route matched
+                               } catch (ResourceNotFoundException $e) {
+                                       //Unset name and context
+                                       unset($name, $context);
                                }
+                       //With logout route name
+                       } else {
+                               //Unset name and context
+                               unset($name, $context);
+                       }
+               }
 
-                               //Remove route and controller from route defaults
-                               unset($route['_route'], $route['_controller'], $route['_canonical_route']);
+               //Try target url
+               try {
+                       //Save old context
+                       $oldContext = $this->router->getContext();
 
-                               //Generate url
-                               $url = $this->router->generate($name, $route);
-                       //Get first route from route collection if / path was not matched
-                       } catch (ResourceNotFoundException $e) {
-                               //Fetch all routes
-                               //XXX: this method regenerate the Routing cache making apps very slow
-                               //XXX: see https://github.com/symfony/symfony-docs/issues/6710
-                               //XXX: it should be fine to call it without referer and a / route
-                               foreach ($this->router->getRouteCollection() as $name => $route) {
-                                       //Return on first public route excluding logout one
-                                       if (!empty($name) && $name[0] != '_' && $name != $logout) {
-                                               break;
-                                       }
-                               }
+                       //Force clean context
+                       //XXX: prevent MethodNotAllowedException on GET only routes because our context method is POST
+                       //XXX: see vendor/symfony/routing/Matcher/Dumper/CompiledUrlMatcherTrait.php +42
+                       $this->router->setContext(new RequestContext());
 
-                               //Bail out if no route found
-                               if (!isset($name) || !isset($route)) {
-                                       throw new \RuntimeException('Unable to retrieve default route');
-                               }
+                       //Retrieve route matching target url
+                       $route = $this->router->match($this->targetUrl);
 
-                               //Retrieve route defaults
-                               $defaults = $route->getDefaults();
+                       //Reset context
+                       $this->router->setContext($oldContext);
 
+                       //Clear old context
+                       unset($oldContext);
+
+                       //Without logout route name
+                       if (($name = $route['_route']) != $logout) {
                                //Remove route and controller from route defaults
-                               unset($defaults['_route'], $defaults['_controller'], $defaults['_canonical_route']);
+                               unset($route['_route'], $route['_controller'], $route['_canonical_route']);
 
                                //Generate url
-                               $url = $this->router->generate($name, $defaults);
+                               $url = $this->router->generate($name, $route);
+
+                               //Return generated route
+                               return new RedirectResponse($url, 302);
+                       //With logout route name
+                       } else {
+                               //Unset name and route
+                               unset($name, $route);
                        }
+               //Get first route from route collection if / path was not matched
+               } catch (ResourceNotFoundException $e) {
+                       //Unset name and route
+                       unset($name, $route);
                }
 
-               //Return redirect response
-               return new RedirectResponse($url, 302);
+               //Throw exception
+               throw new \RuntimeException('You must provide a valid logout target url or route name');
+       }
+
+       /**
+        * {@inheritdoc}
+        */
+       public function getAlias(): string {
+               return RapsysUserBundle::getAlias();
        }
 }