X-Git-Url: https://git.rapsys.eu/userbundle/blobdiff_plain/038e583ee27e5e1fe587c4798d5dcc7d831be5a8..be90ff0448f23198efda2b575744cd160088b94d:/Listener/LogoutListener.php diff --git a/Listener/LogoutListener.php b/Listener/LogoutListener.php new file mode 100644 index 0000000..29b69e4 --- /dev/null +++ b/Listener/LogoutListener.php @@ -0,0 +1,221 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Rapsys\UserBundle\Listener; + +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\HttpFoundation\RedirectResponse; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\Routing\Exception\InvalidParameterException; +use Symfony\Component\Routing\Exception\MissingMandatoryParametersException; +use Symfony\Component\Routing\Exception\ResourceNotFoundException; +use Symfony\Component\Routing\Exception\RouteNotFoundException; +use Symfony\Component\Routing\RequestContext; +use Symfony\Component\Routing\RouterInterface; +use Symfony\Component\Security\Http\Event\LogoutEvent; + +use Rapsys\UserBundle\RapsysUserBundle; + +/** + * {@inheritdoc} + */ +class LogoutListener implements EventSubscriberInterface { + /** + * Config array + */ + protected $config; + + /** + * Target url + */ + private $targetUrl; + + /** + * {@inheritdoc} + * + * @xxx Second argument will be replaced by security.firewalls.main.logout.target + * @see vendor/symfony/security-bundle/DependencyInjection/SecurityExtension.php +445 + */ + public function __construct(ContainerInterface $container, string $targetUrl, RouterInterface $router) { + //Set config + $this->config = $container->getParameter(RapsysUserBundle::getAlias()); + + //Set target url + $this->targetUrl = $targetUrl; + + //Set router + $this->router = $router; + } + + /** + * {@inheritdoc} + */ + public function onLogout(LogoutEvent $event): void { + //Get request + $request = $event->getRequest(); + + //Retrieve logout route + $logout = $request->attributes->get('_route'); + + //Extract and process referer + if (($referer = $request->headers->get('referer'))) { + //Create referer request instance + $req = Request::create($referer); + + //Get referer path + $path = $req->getPathInfo(); + + //Get referer query string + $query = $req->getQueryString(); + + //Remove script name + $path = str_replace($request->getScriptName(), '', $path); + + //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); + + //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); + + //Set event response + $event->setResponse(new RedirectResponse($url, 302)); + + //Return + return; + //With logout route name + } else { + //Unset referer and route + unset($referer, $route); + } + //No route matched + } catch (ResourceNotFoundException $e) { + //Unset referer and route + unset($referer, $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 != $logout) { + //Try index route + try { + //Generate url + $url = $this->router->generate($name, $context); + + //Set event response + $event->setResponse(new RedirectResponse($url, 302)); + + //Return + return; + //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); + } + } + + //Try target url + 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()); + + //With logout target path + if ($this->targetUrl[0] == '/') { + //Retrieve route matching target url + $route = $this->router->match($this->targetUrl); + + //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); + + //Set event response + $event->setResponse(new RedirectResponse($url, 302)); + + //Return + return; + //With logout route name + } else { + //Unset name and route + unset($name, $route); + } + //With route name + } else { + //Retrieve route matching path + $url = $this->router->generate($this->targetUrl); + + //Set event response + $event->setResponse(new RedirectResponse($url, 302)); + + //Return + return; + } + //Get first route from route collection if / path was not matched + } catch (ResourceNotFoundException|RouteNotFoundException|MissingMandatoryParametersException|InvalidParameterException $e) { + //Unset name and route + unset($name, $route); + } + + //Set event response + $event->setResponse(new RedirectResponse('/', 302)); + } + + /** + * {@inheritdoc} + */ + public static function getSubscribedEvents(): array { + return [ + LogoutEvent::class => ['onLogout', 64], + ]; + } +}