vendor/symfony/symfony/src/Symfony/Component/Security/Http/Firewall/LogoutListener.php line 37

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of the Symfony package.
  4.  *
  5.  * (c) Fabien Potencier <fabien@symfony.com>
  6.  *
  7.  * For the full copyright and license information, please view the LICENSE
  8.  * file that was distributed with this source code.
  9.  */
  10. namespace Symfony\Component\Security\Http\Firewall;
  11. use Symfony\Component\EventDispatcher\EventDispatcher;
  12. use Symfony\Component\HttpFoundation\Request;
  13. use Symfony\Component\HttpFoundation\Response;
  14. use Symfony\Component\HttpKernel\Event\RequestEvent;
  15. use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
  16. use Symfony\Component\Security\Core\Exception\LogicException;
  17. use Symfony\Component\Security\Core\Exception\LogoutException;
  18. use Symfony\Component\Security\Csrf\CsrfToken;
  19. use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
  20. use Symfony\Component\Security\Http\Event\LogoutEvent;
  21. use Symfony\Component\Security\Http\HttpUtils;
  22. use Symfony\Component\Security\Http\Logout\LogoutHandlerInterface;
  23. use Symfony\Component\Security\Http\Logout\LogoutSuccessHandlerInterface;
  24. use Symfony\Component\Security\Http\ParameterBagUtils;
  25. use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
  26. /**
  27.  * LogoutListener logout users.
  28.  *
  29.  * @author Fabien Potencier <fabien@symfony.com>
  30.  *
  31.  * @final
  32.  */
  33. class LogoutListener extends AbstractListener
  34. {
  35.     private $tokenStorage;
  36.     private $options;
  37.     private $httpUtils;
  38.     private $csrfTokenManager;
  39.     private $eventDispatcher;
  40.     /**
  41.      * @param EventDispatcherInterface $eventDispatcher
  42.      * @param array                    $options         An array of options to process a logout attempt
  43.      */
  44.     public function __construct(TokenStorageInterface $tokenStorageHttpUtils $httpUtils$eventDispatcher, array $options = [], ?CsrfTokenManagerInterface $csrfTokenManager null)
  45.     {
  46.         if (!$eventDispatcher instanceof EventDispatcherInterface) {
  47.             trigger_deprecation('symfony/security-http''5.1''Passing a logout success handler to "%s" is deprecated, pass an instance of "%s" instead.'__METHOD__EventDispatcherInterface::class);
  48.             if (!$eventDispatcher instanceof LogoutSuccessHandlerInterface) {
  49.                 throw new \TypeError(sprintf('Argument 3 of "%s" must be instance of "%s" or "%s", "%s" given.'__METHOD__EventDispatcherInterface::class, LogoutSuccessHandlerInterface::class, get_debug_type($eventDispatcher)));
  50.             }
  51.             $successHandler $eventDispatcher;
  52.             $eventDispatcher = new EventDispatcher();
  53.             $eventDispatcher->addListener(LogoutEvent::class, function (LogoutEvent $event) use ($successHandler) {
  54.                 $event->setResponse($r $successHandler->onLogoutSuccess($event->getRequest()));
  55.             });
  56.         }
  57.         $this->tokenStorage $tokenStorage;
  58.         $this->httpUtils $httpUtils;
  59.         $this->options array_merge([
  60.             'csrf_parameter' => '_csrf_token',
  61.             'csrf_token_id' => 'logout',
  62.             'logout_path' => '/logout',
  63.         ], $options);
  64.         $this->csrfTokenManager $csrfTokenManager;
  65.         $this->eventDispatcher $eventDispatcher;
  66.     }
  67.     /**
  68.      * @deprecated since Symfony 5.1
  69.      */
  70.     public function addHandler(LogoutHandlerInterface $handler)
  71.     {
  72.         trigger_deprecation('symfony/security-http''5.1''Calling "%s" is deprecated, register a listener on the "%s" event instead.'__METHOD__LogoutEvent::class);
  73.         $this->eventDispatcher->addListener(LogoutEvent::class, function (LogoutEvent $event) use ($handler) {
  74.             if (null === $event->getResponse()) {
  75.                 throw new LogicException(sprintf('No response was set for this logout action. Make sure the DefaultLogoutListener or another listener has set the response before "%s" is called.'__CLASS__));
  76.             }
  77.             $handler->logout($event->getRequest(), $event->getResponse(), $event->getToken());
  78.         });
  79.     }
  80.     /**
  81.      * {@inheritdoc}
  82.      */
  83.     public function supports(Request $request): ?bool
  84.     {
  85.         return $this->requiresLogout($request);
  86.     }
  87.     /**
  88.      * Performs the logout if requested.
  89.      *
  90.      * If a CsrfTokenManagerInterface instance is available, it will be used to
  91.      * validate the request.
  92.      *
  93.      * @throws LogoutException   if the CSRF token is invalid
  94.      * @throws \RuntimeException if the LogoutSuccessHandlerInterface instance does not return a response
  95.      */
  96.     public function authenticate(RequestEvent $event)
  97.     {
  98.         $request $event->getRequest();
  99.         if (null !== $this->csrfTokenManager) {
  100.             $csrfToken ParameterBagUtils::getRequestParameterValue($request$this->options['csrf_parameter']);
  101.             if (!\is_string($csrfToken) || false === $this->csrfTokenManager->isTokenValid(new CsrfToken($this->options['csrf_token_id'], $csrfToken))) {
  102.                 throw new LogoutException('Invalid CSRF token.');
  103.             }
  104.         }
  105.         $logoutEvent = new LogoutEvent($request$this->tokenStorage->getToken());
  106.         $this->eventDispatcher->dispatch($logoutEvent);
  107.         if (!$response $logoutEvent->getResponse()) {
  108.             throw new \RuntimeException('No logout listener set the Response, make sure at least the DefaultLogoutListener is registered.');
  109.         }
  110.         $this->tokenStorage->setToken(null);
  111.         $event->setResponse($response);
  112.     }
  113.     /**
  114.      * Whether this request is asking for logout.
  115.      *
  116.      * The default implementation only processed requests to a specific path,
  117.      * but a subclass could change this to logout requests where
  118.      * certain parameters is present.
  119.      */
  120.     protected function requiresLogout(Request $request): bool
  121.     {
  122.         return isset($this->options['logout_path']) && $this->httpUtils->checkRequestPath($request$this->options['logout_path']);
  123.     }
  124.     public static function getPriority(): int
  125.     {
  126.         return -127;
  127.     }
  128. }