vendor/ibexa/rest/src/bundle/EventListener/CsrfListener.php line 84

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