vendor/scheb/2fa-bundle/Security/Http/Firewall/TwoFactorAccessListener.php line 21

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\Authorization\TwoFactorAccessDecider;
  6. use Scheb\TwoFactorBundle\Security\TwoFactor\TwoFactorFirewallConfig;
  7. use Symfony\Component\HttpFoundation\Request;
  8. use Symfony\Component\HttpKernel\Event\RequestEvent;
  9. use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
  10. use Symfony\Component\Security\Core\Exception\AccessDeniedException;
  11. use Symfony\Component\Security\Http\Firewall\AbstractListener;
  12. /**
  13. * Handles access control in the "2fa in progress" phase.
  14. *
  15. * @final
  16. */
  17. class TwoFactorAccessListener extends AbstractListener implements FirewallListenerInterface
  18. {
  19. /**
  20. * @var TwoFactorFirewallConfig
  21. */
  22. private $twoFactorFirewallConfig;
  23. /**
  24. * @var TokenStorageInterface
  25. */
  26. private $tokenStorage;
  27. /**
  28. * @var TwoFactorAccessDecider
  29. */
  30. private $twoFactorAccessDecider;
  31. public function __construct(
  32. TwoFactorFirewallConfig $twoFactorFirewallConfig,
  33. TokenStorageInterface $tokenStorage,
  34. TwoFactorAccessDecider $twoFactorAccessDecider
  35. ) {
  36. $this->twoFactorFirewallConfig = $twoFactorFirewallConfig;
  37. $this->tokenStorage = $tokenStorage;
  38. $this->twoFactorAccessDecider = $twoFactorAccessDecider;
  39. }
  40. public function supports(Request $request): ?bool
  41. {
  42. // When the path is explicitly configured for anonymous access, no need to check access (important for lazy
  43. // firewalls, to prevent the response cache control to be flagged "private")
  44. return !$this->twoFactorAccessDecider->isPubliclyAccessible($request);
  45. }
  46. public function authenticate(RequestEvent $event): void
  47. {
  48. // When the firewall is lazy, the token is not initialized in the "supports" stage, so this check does only work
  49. // within the "authenticate" stage.
  50. $token = $this->tokenStorage->getToken();
  51. if (!($token instanceof TwoFactorTokenInterface)) {
  52. // No need to check for firewall name here, the listener is bound to the firewall context
  53. return;
  54. }
  55. /** @var TwoFactorTokenInterface $token */
  56. $token = $this->tokenStorage->getToken();
  57. $request = $event->getRequest();
  58. if ($this->twoFactorFirewallConfig->isCheckPathRequest($request)) {
  59. return;
  60. }
  61. if ($this->twoFactorFirewallConfig->isAuthFormRequest($request)) {
  62. return;
  63. }
  64. if (!$this->twoFactorAccessDecider->isAccessible($request, $token)) {
  65. $exception = new AccessDeniedException('User is in a two-factor authentication process.');
  66. $exception->setSubject($request);
  67. throw $exception;
  68. }
  69. }
  70. public static function getPriority(): int
  71. {
  72. // When the class is injected via FirewallListenerFactoryInterface
  73. // Inject before Symfony's AccessListener (-255) and after the LogoutListener (-127)
  74. return -191;
  75. }
  76. }