app/GraphQL/EventSubscriber/UniversalApiThrottlingSubscriber.php line 29

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. namespace Sq\GraphQL\EventSubscriber;
  3. use Sq\Entity\Schema\ORM as Entity;
  4. use Sq\GraphQL\Exception\SqGraphQLException;
  5. use Sq\GraphQL\Security\RateLimiter\UniversalApiAnonymousThrottling;
  6. use Sq\GraphQL\Security\RateLimiter\UniversalApiUserThrottling;
  7. use Sq\Service\Environment;
  8. use Sq\Service\Log\ExceptionLogger;
  9. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  10. use Symfony\Component\HttpFoundation\JsonResponse;
  11. use Symfony\Component\HttpFoundation\Response;
  12. use Symfony\Component\HttpKernel\Event\ControllerEvent;
  13. use Symfony\Component\HttpKernel\KernelEvents;
  14. use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
  15. class UniversalApiThrottlingSubscriber implements EventSubscriberInterface
  16. {
  17.     public function __construct(
  18.         protected UniversalApiUserThrottling $userThrottling,
  19.         protected UniversalApiAnonymousThrottling $anonymousThrottling,
  20.         protected TokenStorageInterface $tokenStorage,
  21.         protected Environment $env,
  22.         protected ExceptionLogger $exceptionLogger
  23.     ) {
  24.     }
  25.     public function onController(ControllerEvent $event): void
  26.     {
  27.         $request $event->getRequest();
  28.         if (!$this->isProd() || !is_graphql_request($request))
  29.         {
  30.             return;
  31.         }
  32.         try
  33.         {
  34.             $loggedInUser $this->getLoggedInUser();
  35.             if ($loggedInUser === null)
  36.             {
  37.                 $this->anonymousThrottling->ifThrottledThrowError($request->getClientIp());
  38.             }
  39.             else
  40.             {
  41.                 $this->userThrottling->ifThrottledThrowError((string) $loggedInUser->getId());
  42.             }
  43.         }
  44.         catch (SqGraphQLException $e)
  45.         {
  46.             $this->exceptionLogger->logGqlException($e$this->getLoggedInUser());
  47.             $waitInSeconds $e->getExtensions()['wait'] ?? null;
  48.             $result = new \GraphQL\Executor\ExecutionResult(null, [
  49.                 new \GraphQL\Error\Error(SqGraphQLException::httpRateLimited()->getMessage(), nullnull, [], nullnullis_int($waitInSeconds) ? ['wait' => $waitInSeconds] : []),
  50.             ]);
  51.             $response = new JsonResponse($result->toArray(), 429is_int($waitInSeconds) ? ['Retry-After' => $waitInSeconds] : []);
  52.             $event->setController(fn (): Response => $response);
  53.         }
  54.     }
  55.     protected function getLoggedInUser(): ?Entity\User
  56.     {
  57.         $user $this->tokenStorage->getToken() ? $this->tokenStorage->getToken()->getUser() : null;
  58.         return $user instanceof Entity\User $user null;
  59.     }
  60.     protected function isProd(): bool
  61.     {
  62.         return $this->env->isProd();
  63.     }
  64.     /** @inheritDoc */
  65.     public static function getSubscribedEvents()
  66.     {
  67.         return [
  68.             KernelEvents::CONTROLLER => ['onController', -64],
  69.         ];
  70.     }
  71. }