vendor/symfony/security-guard/Firewall/GuardAuthenticationListener.php line 44

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of the Symfony package.
  4.  *
  5.  * (c) Fabien Potencier <fabien@symfony.com>
  6.  *
  7.  * For the full copyright and license information, please view the LICENSE
  8.  * file that was distributed with this source code.
  9.  */
  10. namespace Symfony\Component\Security\Guard\Firewall;
  11. use Psr\Log\LoggerInterface;
  12. use Symfony\Component\HttpFoundation\Request;
  13. use Symfony\Component\HttpFoundation\Response;
  14. use Symfony\Component\HttpKernel\Event\RequestEvent;
  15. use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
  16. use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
  17. use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
  18. use Symfony\Component\Security\Core\Exception\AccountStatusException;
  19. use Symfony\Component\Security\Core\Exception\AuthenticationException;
  20. use Symfony\Component\Security\Core\Exception\BadCredentialsException;
  21. use Symfony\Component\Security\Core\Exception\CustomUserMessageAccountStatusException;
  22. use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
  23. use Symfony\Component\Security\Guard\AuthenticatorInterface;
  24. use Symfony\Component\Security\Guard\GuardAuthenticatorHandler;
  25. use Symfony\Component\Security\Guard\Token\PreAuthenticationGuardToken;
  26. use Symfony\Component\Security\Http\Firewall\AbstractListener;
  27. use Symfony\Component\Security\Http\RememberMe\RememberMeServicesInterface;
  28. trigger_deprecation('symfony/security-guard''5.3''The "%s" class is deprecated, use the new authenticator system instead.'GuardAuthenticationListener::class);
  29. /**
  30.  * Authentication listener for the "guard" system.
  31.  *
  32.  * @author Ryan Weaver <ryan@knpuniversity.com>
  33.  * @author Amaury Leroux de Lens <amaury@lerouxdelens.com>
  34.  *
  35.  * @final
  36.  *
  37.  * @deprecated since Symfony 5.3, use the new authenticator system instead
  38.  */
  39. class GuardAuthenticationListener extends AbstractListener
  40. {
  41.     private $guardHandler;
  42.     private $authenticationManager;
  43.     private $providerKey;
  44.     private $guardAuthenticators;
  45.     private $logger;
  46.     private $rememberMeServices;
  47.     private $hideUserNotFoundExceptions;
  48.     private $tokenStorage;
  49.     /**
  50.      * @param string                                      $providerKey         The provider (i.e. firewall) key
  51.      * @param iterable<array-key, AuthenticatorInterface> $guardAuthenticators The authenticators, with keys that match what's passed to GuardAuthenticationProvider
  52.      */
  53.     public function __construct(GuardAuthenticatorHandler $guardHandlerAuthenticationManagerInterface $authenticationManagerstring $providerKeyiterable $guardAuthenticators, ?LoggerInterface $logger nullbool $hideUserNotFoundExceptions true, ?TokenStorageInterface $tokenStorage null)
  54.     {
  55.         if (empty($providerKey)) {
  56.             throw new \InvalidArgumentException('$providerKey must not be empty.');
  57.         }
  58.         $this->guardHandler $guardHandler;
  59.         $this->authenticationManager $authenticationManager;
  60.         $this->providerKey $providerKey;
  61.         $this->guardAuthenticators $guardAuthenticators;
  62.         $this->logger $logger;
  63.         $this->hideUserNotFoundExceptions $hideUserNotFoundExceptions;
  64.         $this->tokenStorage $tokenStorage;
  65.     }
  66.     /**
  67.      * {@inheritdoc}
  68.      */
  69.     public function supports(Request $request): ?bool
  70.     {
  71.         if (null !== $this->logger) {
  72.             $context = ['firewall_key' => $this->providerKey];
  73.             if ($this->guardAuthenticators instanceof \Countable || \is_array($this->guardAuthenticators)) {
  74.                 $context['authenticators'] = \count($this->guardAuthenticators);
  75.             }
  76.             $this->logger->debug('Checking for guard authentication credentials.'$context);
  77.         }
  78.         $guardAuthenticators = [];
  79.         foreach ($this->guardAuthenticators as $key => $guardAuthenticator) {
  80.             if (null !== $this->logger) {
  81.                 $this->logger->debug('Checking support on guard authenticator.', ['firewall_key' => $this->providerKey'authenticator' => \get_class($guardAuthenticator)]);
  82.             }
  83.             if ($guardAuthenticator->supports($request)) {
  84.                 $guardAuthenticators[$key] = $guardAuthenticator;
  85.             } elseif (null !== $this->logger) {
  86.                 $this->logger->debug('Guard authenticator does not support the request.', ['firewall_key' => $this->providerKey'authenticator' => \get_class($guardAuthenticator)]);
  87.             }
  88.         }
  89.         if (!$guardAuthenticators) {
  90.             return false;
  91.         }
  92.         $request->attributes->set('_guard_authenticators'$guardAuthenticators);
  93.         return true;
  94.     }
  95.     /**
  96.      * Iterates over each authenticator to see if each wants to authenticate the request.
  97.      */
  98.     public function authenticate(RequestEvent $event)
  99.     {
  100.         $request $event->getRequest();
  101.         $guardAuthenticators $request->attributes->get('_guard_authenticators');
  102.         $request->attributes->remove('_guard_authenticators');
  103.         foreach ($guardAuthenticators as $key => $guardAuthenticator) {
  104.             // get a key that's unique to *this* guard authenticator
  105.             // this MUST be the same as GuardAuthenticationProvider
  106.             $uniqueGuardKey $this->providerKey.'_'.$key;
  107.             $this->executeGuardAuthenticator($uniqueGuardKey$guardAuthenticator$event);
  108.             if ($event->hasResponse()) {
  109.                 if (null !== $this->logger) {
  110.                     $this->logger->debug('The "{authenticator}" authenticator set the response. Any later authenticator will not be called', ['authenticator' => \get_class($guardAuthenticator)]);
  111.                 }
  112.                 break;
  113.             }
  114.         }
  115.     }
  116.     private function executeGuardAuthenticator(string $uniqueGuardKeyAuthenticatorInterface $guardAuthenticatorRequestEvent $event)
  117.     {
  118.         $request $event->getRequest();
  119.         $previousToken $this->tokenStorage $this->tokenStorage->getToken() : null;
  120.         try {
  121.             if (null !== $this->logger) {
  122.                 $this->logger->debug('Calling getCredentials() on guard authenticator.', ['firewall_key' => $this->providerKey'authenticator' => \get_class($guardAuthenticator)]);
  123.             }
  124.             // allow the authenticator to fetch authentication info from the request
  125.             $credentials $guardAuthenticator->getCredentials($request);
  126.             if (null === $credentials) {
  127.                 throw new \UnexpectedValueException(sprintf('The return value of "%1$s::getCredentials()" must not be null. Return false from "%1$s::supports()" instead.'get_debug_type($guardAuthenticator)));
  128.             }
  129.             // create a token with the unique key, so that the provider knows which authenticator to use
  130.             $token = new PreAuthenticationGuardToken($credentials$uniqueGuardKey);
  131.             if (null !== $this->logger) {
  132.                 $this->logger->debug('Passing guard token information to the GuardAuthenticationProvider', ['firewall_key' => $this->providerKey'authenticator' => \get_class($guardAuthenticator)]);
  133.             }
  134.             // pass the token into the AuthenticationManager system
  135.             // this indirectly calls GuardAuthenticationProvider::authenticate()
  136.             $token $this->authenticationManager->authenticate($token);
  137.             if (null !== $this->logger) {
  138.                 $this->logger->info('Guard authentication successful!', ['token' => $token'authenticator' => \get_class($guardAuthenticator)]);
  139.             }
  140.             // sets the token on the token storage, etc
  141.             if ($this->tokenStorage) {
  142.                 $this->guardHandler->authenticateWithToken($token$request$this->providerKey$previousToken);
  143.             } else {
  144.                 $this->guardHandler->authenticateWithToken($token$request$this->providerKey);
  145.             }
  146.         } catch (AuthenticationException $e) {
  147.             // oh no! Authentication failed!
  148.             if (null !== $this->logger) {
  149.                 $this->logger->info('Guard authentication failed.', ['exception' => $e'authenticator' => \get_class($guardAuthenticator)]);
  150.             }
  151.             // Avoid leaking error details in case of invalid user (e.g. user not found or invalid account status)
  152.             // to prevent user enumeration via response content
  153.             if ($this->hideUserNotFoundExceptions && ($e instanceof UsernameNotFoundException || ($e instanceof AccountStatusException && !$e instanceof CustomUserMessageAccountStatusException))) {
  154.                 $e = new BadCredentialsException('Bad credentials.'0$e);
  155.             }
  156.             $response $this->guardHandler->handleAuthenticationFailure($e$request$guardAuthenticator$this->providerKey);
  157.             if ($response instanceof Response) {
  158.                 $event->setResponse($response);
  159.             }
  160.             return;
  161.         }
  162.         // success!
  163.         $response $this->guardHandler->handleAuthenticationSuccess($token$request$guardAuthenticator$this->providerKey);
  164.         if ($response instanceof Response) {
  165.             if (null !== $this->logger) {
  166.                 $this->logger->debug('Guard authenticator set success response.', ['response' => $response'authenticator' => \get_class($guardAuthenticator)]);
  167.             }
  168.             $event->setResponse($response);
  169.         } else {
  170.             if (null !== $this->logger) {
  171.                 $this->logger->debug('Guard authenticator set no success response: request continues.', ['authenticator' => \get_class($guardAuthenticator)]);
  172.             }
  173.         }
  174.         // attempt to trigger the remember me functionality
  175.         $this->triggerRememberMe($guardAuthenticator$request$token$response);
  176.     }
  177.     /**
  178.      * Should be called if this listener will support remember me.
  179.      */
  180.     public function setRememberMeServices(RememberMeServicesInterface $rememberMeServices)
  181.     {
  182.         $this->rememberMeServices $rememberMeServices;
  183.     }
  184.     /**
  185.      * Checks to see if remember me is supported in the authenticator and
  186.      * on the firewall. If it is, the RememberMeServicesInterface is notified.
  187.      */
  188.     private function triggerRememberMe(AuthenticatorInterface $guardAuthenticatorRequest $requestTokenInterface $token, ?Response $response null)
  189.     {
  190.         if (null === $this->rememberMeServices) {
  191.             if (null !== $this->logger) {
  192.                 $this->logger->debug('Remember me skipped: it is not configured for the firewall.', ['authenticator' => \get_class($guardAuthenticator)]);
  193.             }
  194.             return;
  195.         }
  196.         if (!$guardAuthenticator->supportsRememberMe()) {
  197.             if (null !== $this->logger) {
  198.                 $this->logger->debug('Remember me skipped: your authenticator does not support it.', ['authenticator' => \get_class($guardAuthenticator)]);
  199.             }
  200.             return;
  201.         }
  202.         if (!$response instanceof Response) {
  203.             throw new \LogicException(sprintf('"%s::onAuthenticationSuccess()" *must* return a Response if you want to use the remember me functionality. Return a Response, or set remember_me to false under the guard configuration.'get_debug_type($guardAuthenticator)));
  204.         }
  205.         $this->rememberMeServices->loginSuccess($request$response$token);
  206.     }
  207. }