vendor/scheb/2fa-bundle/Security/TwoFactor/Provider/TwoFactorProviderPreparationListener.php line 107

Open in your IDE?
  1. <?php
  2. declare(strict_types=1);
  3. namespace Scheb\TwoFactorBundle\Security\TwoFactor\Provider;
  4. use Psr\Log\LoggerInterface;
  5. use Psr\Log\NullLogger;
  6. use Scheb\TwoFactorBundle\Security\Authentication\Token\TwoFactorTokenInterface;
  7. use Scheb\TwoFactorBundle\Security\TwoFactor\Event\TwoFactorAuthenticationEvent;
  8. use Scheb\TwoFactorBundle\Security\TwoFactor\Event\TwoFactorAuthenticationEvents;
  9. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  10. use Symfony\Component\HttpKernel\Event\ResponseEvent;
  11. use Symfony\Component\HttpKernel\KernelEvents;
  12. use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
  13. use Symfony\Component\Security\Core\AuthenticationEvents;
  14. use Symfony\Component\Security\Core\Event\AuthenticationEvent;
  15. /**
  16.  * @final
  17.  */
  18. class TwoFactorProviderPreparationListener implements EventSubscriberInterface
  19. {
  20.     public const LISTENER_PRIORITY PHP_INT_MAX;
  21.     /**
  22.      * @var TwoFactorProviderRegistry
  23.      */
  24.     private $providerRegistry;
  25.     /**
  26.      * @var PreparationRecorderInterface
  27.      */
  28.     private $preparationRecorder;
  29.     /**
  30.      * @var TwoFactorTokenInterface|null
  31.      */
  32.     private $twoFactorToken;
  33.     /**
  34.      * @var LoggerInterface
  35.      */
  36.     private $logger;
  37.     /**
  38.      * @var string
  39.      */
  40.     private $firewallName;
  41.     /**
  42.      * @var bool
  43.      */
  44.     private $prepareOnLogin;
  45.     /**
  46.      * @var bool
  47.      */
  48.     private $prepareOnAccessDenied;
  49.     public function __construct(
  50.         TwoFactorProviderRegistry $providerRegistry,
  51.         PreparationRecorderInterface $preparationRecorder,
  52.         ?LoggerInterface $logger,
  53.         string $firewallName,
  54.         bool $prepareOnLogin,
  55.         bool $prepareOnAccessDenied
  56.     ) {
  57.         $this->providerRegistry $providerRegistry;
  58.         $this->preparationRecorder $preparationRecorder;
  59.         $this->logger $logger ?? new NullLogger();
  60.         $this->firewallName $firewallName;
  61.         $this->prepareOnLogin $prepareOnLogin;
  62.         $this->prepareOnAccessDenied $prepareOnAccessDenied;
  63.     }
  64.     public function onLogin(AuthenticationEvent $event): void
  65.     {
  66.         $token $event->getAuthenticationToken();
  67.         if ($this->prepareOnLogin && $this->supports($token)) {
  68.             /** @var TwoFactorTokenInterface $token */
  69.             // After login, when the token is a TwoFactorTokenInterface, execute preparation
  70.             $this->twoFactorToken $token;
  71.         }
  72.     }
  73.     public function onAccessDenied(TwoFactorAuthenticationEvent $event): void
  74.     {
  75.         $token $event->getToken();
  76.         if ($this->prepareOnAccessDenied && $this->supports($token)) {
  77.             /** @var TwoFactorTokenInterface $token */
  78.             // Whenever two-factor authentication is required, execute preparation
  79.             $this->twoFactorToken $token;
  80.         }
  81.     }
  82.     public function onTwoFactorForm(TwoFactorAuthenticationEvent $event): void
  83.     {
  84.         $token $event->getToken();
  85.         if ($this->supports($token)) {
  86.             /** @var TwoFactorTokenInterface $token */
  87.             // Whenever two-factor authentication form is shown, execute preparation
  88.             $this->twoFactorToken $token;
  89.         }
  90.     }
  91.     public function onKernelResponse(ResponseEvent $event): void
  92.     {
  93.         if (!$event->isMasterRequest()) {
  94.             return;
  95.         }
  96.         // Unset the token from context. This is important for environments where this instance of the class is reused
  97.         // for multiple requests, such as PHP PM.
  98.         $twoFactorToken $this->twoFactorToken;
  99.         $this->twoFactorToken null;
  100.         if (!($twoFactorToken instanceof TwoFactorTokenInterface)) {
  101.             return;
  102.         }
  103.         $providerName $twoFactorToken->getCurrentTwoFactorProvider();
  104.         if (null === $providerName) {
  105.             return;
  106.         }
  107.         $firewallName $twoFactorToken->getProviderKey();
  108.         if ($this->preparationRecorder->isTwoFactorProviderPrepared($firewallName$providerName)) {
  109.             $this->logger->info(sprintf('Two-factor provider "%s" was already prepared.'$providerName));
  110.             return;
  111.         }
  112.         $user $twoFactorToken->getUser();
  113.         $this->providerRegistry->getProvider($providerName)->prepareAuthentication($user);
  114.         $this->preparationRecorder->setTwoFactorProviderPrepared($firewallName$providerName);
  115.         $this->logger->info(sprintf('Two-factor provider "%s" prepared.'$providerName));
  116.     }
  117.     private function supports(TokenInterface $token): bool
  118.     {
  119.         return $token instanceof TwoFactorTokenInterface && $token->getProviderKey() === $this->firewallName;
  120.     }
  121.     public static function getSubscribedEvents()
  122.     {
  123.         return [
  124.             // This must trigger very first, followed by AuthenticationSuccessEventSuppressor
  125.             AuthenticationEvents::AUTHENTICATION_SUCCESS => ['onLogin'self::LISTENER_PRIORITY],
  126.             TwoFactorAuthenticationEvents::REQUIRE => 'onAccessDenied',
  127.             TwoFactorAuthenticationEvents::FORM => 'onTwoFactorForm',
  128.             KernelEvents::RESPONSE => 'onKernelResponse',
  129.         ];
  130.     }
  131. }