vendor/scheb/2fa-bundle/Security/Http/Firewall/ExceptionListener.php line 58

Open in your IDE?
  1. <?php
  2. declare(strict_types=1);
  3. namespace Scheb\TwoFactorBundle\Security\Http\Firewall;
  4. use Scheb\TwoFactorBundle\Security\Authentication\Token\TwoFactorTokenInterface;
  5. use Scheb\TwoFactorBundle\Security\Http\Authentication\AuthenticationRequiredHandlerInterface;
  6. use Scheb\TwoFactorBundle\Security\TwoFactor\Event\TwoFactorAuthenticationEvent;
  7. use Scheb\TwoFactorBundle\Security\TwoFactor\Event\TwoFactorAuthenticationEvents;
  8. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  9. use Symfony\Component\HttpKernel\Event\ExceptionEvent;
  10. use Symfony\Component\HttpKernel\KernelEvents;
  11. use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
  12. use Symfony\Component\Security\Core\Exception\AccessDeniedException;
  13. use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
  14. /**
  15. * @final
  16. */
  17. class ExceptionListener implements EventSubscriberInterface
  18. {
  19. // Just before the firewall's Symfony\Component\Security\Http\Firewall\ExceptionListener
  20. private const LISTENER_PRIORITY = 2;
  21. /**
  22. * @var string
  23. */
  24. private $firewallName;
  25. /**
  26. * @var TokenStorageInterface
  27. */
  28. private $tokenStorage;
  29. /**
  30. * @var AuthenticationRequiredHandlerInterface
  31. */
  32. private $authenticationRequiredHandler;
  33. /**
  34. * @var EventDispatcherInterface
  35. */
  36. private $eventDispatcher;
  37. public function __construct(
  38. string $firewallName,
  39. TokenStorageInterface $tokenStorage,
  40. AuthenticationRequiredHandlerInterface $authenticationRequiredHandler,
  41. EventDispatcherInterface $eventDispatcher
  42. ) {
  43. $this->firewallName = $firewallName;
  44. $this->tokenStorage = $tokenStorage;
  45. $this->authenticationRequiredHandler = $authenticationRequiredHandler;
  46. $this->eventDispatcher = $eventDispatcher;
  47. }
  48. public function onKernelException(ExceptionEvent $event): void
  49. {
  50. $exception = $event->getThrowable();
  51. do {
  52. if ($exception instanceof AccessDeniedException) {
  53. $this->handleAccessDeniedException($event);
  54. return;
  55. }
  56. } while (null !== $exception = $exception->getPrevious());
  57. }
  58. private function handleAccessDeniedException(ExceptionEvent $exceptionEvent): void
  59. {
  60. $token = $this->tokenStorage->getToken();
  61. if (!($token instanceof TwoFactorTokenInterface && $token->getProviderKey(true) === $this->firewallName)) {
  62. return;
  63. }
  64. /** @var TwoFactorTokenInterface $token */
  65. $request = $exceptionEvent->getRequest();
  66. $event = new TwoFactorAuthenticationEvent($request, $token);
  67. $this->eventDispatcher->dispatch($event, TwoFactorAuthenticationEvents::REQUIRE);
  68. $response = $this->authenticationRequiredHandler->onAuthenticationRequired($request, $token);
  69. $exceptionEvent->allowCustomResponseCode();
  70. $exceptionEvent->setResponse($response);
  71. }
  72. public static function getSubscribedEvents(): array
  73. {
  74. return [
  75. KernelEvents::EXCEPTION => ['onKernelException', self::LISTENER_PRIORITY],
  76. ];
  77. }
  78. }