vendor/ezsystems/ezpublish-kernel/eZ/Bundle/EzPublishRestBundle/EventListener/CsrfListener.php line 76

Open in your IDE?
  1. <?php
  2. /**
  3.  * @copyright Copyright (C) eZ Systems AS. All rights reserved.
  4.  * @license For full copyright and license information view LICENSE file distributed with this source code.
  5.  */
  6. namespace eZ\Bundle\EzPublishRestBundle\EventListener;
  7. use Symfony\Component\EventDispatcher\EventDispatcherInterface;
  8. use Symfony\Component\HttpFoundation\Request;
  9. use Symfony\Component\HttpKernel\KernelEvents;
  10. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  11. use Symfony\Component\HttpKernel\Event\GetResponseEvent;
  12. use eZ\Publish\Core\Base\Exceptions\UnauthorizedException;
  13. use eZ\Bundle\EzPublishRestBundle\RestEvents;
  14. use Symfony\Component\Security\Csrf\CsrfToken;
  15. use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
  16. class CsrfListener implements EventSubscriberInterface
  17. {
  18.     /**
  19.      * Name of the HTTP header containing CSRF token.
  20.      */
  21.     const CSRF_TOKEN_HEADER 'X-CSRF-Token';
  22.     /** @var CsrfTokenManagerInterface|null */
  23.     private $csrfTokenManager;
  24.     /** @var \Symfony\Component\EventDispatcher\EventDispatcherInterface */
  25.     private $eventDispatcher;
  26.     /** @var bool */
  27.     private $csrfEnabled;
  28.     /** @var bool */
  29.     private $csrfTokenIntention;
  30.     /**
  31.      * Note that CSRF provider needs to be optional as it will not be available
  32.      * when CSRF protection is disabled.
  33.      *
  34.      * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $eventDispatcher
  35.      * @param bool $csrfEnabled
  36.      * @param string $csrfTokenIntention
  37.      * @param CsrfTokenManagerInterface|null $csrfTokenManager
  38.      */
  39.     public function __construct(
  40.         EventDispatcherInterface $eventDispatcher,
  41.         $csrfEnabled,
  42.         $csrfTokenIntention,
  43.         CsrfTokenManagerInterface $csrfTokenManager null
  44.     ) {
  45.         $this->eventDispatcher $eventDispatcher;
  46.         $this->csrfEnabled $csrfEnabled;
  47.         $this->csrfTokenIntention $csrfTokenIntention;
  48.         $this->csrfTokenManager $csrfTokenManager;
  49.     }
  50.     /**
  51.      * @return array
  52.      */
  53.     public static function getSubscribedEvents()
  54.     {
  55.         return [
  56.             KernelEvents::REQUEST => 'onKernelRequest',
  57.         ];
  58.     }
  59.     /**
  60.      * This method validates CSRF token if CSRF protection is enabled.
  61.      *
  62.      * @param \Symfony\Component\HttpKernel\Event\GetResponseEvent $event
  63.      *
  64.      * @throws \eZ\Publish\Core\Base\Exceptions\UnauthorizedException
  65.      */
  66.     public function onKernelRequest(GetResponseEvent $event)
  67.     {
  68.         if (!$event->getRequest()->attributes->get('is_rest_request')) {
  69.             return;
  70.         }
  71.         if (!$this->csrfEnabled) {
  72.             return;
  73.         }
  74.         // skip CSRF validation if no session is running
  75.         if (!$event->getRequest()->getSession()->isStarted()) {
  76.             return;
  77.         }
  78.         if ($this->isMethodSafe($event->getRequest()->getMethod())) {
  79.             return;
  80.         }
  81.         if ($this->isSessionRoute($event->getRequest()->get('_route'))) {
  82.             return;
  83.         }
  84.         if (!$this->checkCsrfToken($event->getRequest())) {
  85.             throw new UnauthorizedException(
  86.                 'Missing or invalid CSRF token',
  87.                 $event->getRequest()->getMethod() . ' ' $event->getRequest()->getPathInfo()
  88.             );
  89.         }
  90.         // Dispatching event so that CSRF token intention can be injected into Legacy Stack
  91.         $this->eventDispatcher->dispatch(RestEvents::REST_CSRF_TOKEN_VALIDATED);
  92.     }
  93.     /**
  94.      * @param string $method
  95.      *
  96.      * @return bool
  97.      */
  98.     protected function isMethodSafe($method)
  99.     {
  100.         return in_array($method, ['GET''HEAD''OPTIONS']);
  101.     }
  102.     /**
  103.      * @param string $route
  104.      *
  105.      * @return bool
  106.      *
  107.      * @deprecated Deprecated since 6.5. Use isSessionRoute() instead.
  108.      */
  109.     protected function isLoginRequest($route)
  110.     {
  111.         return $route === 'ezpublish_rest_createSession';
  112.     }
  113.     /**
  114.      * Tests if a given $route is a session management one.
  115.      *
  116.      * @param string $route
  117.      *
  118.      * @return bool
  119.      */
  120.     protected function isSessionRoute($route)
  121.     {
  122.         return in_array(
  123.             $route,
  124.             ['ezpublish_rest_createSession''ezpublish_rest_refreshSession''ezpublish_rest_deleteSession']
  125.         );
  126.     }
  127.     /**
  128.      * Checks the validity of the request's csrf token header.
  129.      *
  130.      * @param Request $request
  131.      *
  132.      * @return bool true/false if the token is valid/invalid, false if none was found in the request's headers.
  133.      */
  134.     protected function checkCsrfToken(Request $request)
  135.     {
  136.         if (!$request->headers->has(self::CSRF_TOKEN_HEADER)) {
  137.             return false;
  138.         }
  139.         return $this->csrfTokenManager->isTokenValid(
  140.             new CsrfToken(
  141.                 $this->csrfTokenIntention,
  142.                 $request->headers->get(self::CSRF_TOKEN_HEADER)
  143.             )
  144.         );
  145.     }
  146. }