vendor/symfony/symfony/src/Symfony/Component/Security/Guard/Firewall/GuardAuthenticationListener.php line 38

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\GetResponseEvent;
  15. use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
  16. use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
  17. use Symfony\Component\Security\Core\Exception\AccountStatusException;
  18. use Symfony\Component\Security\Core\Exception\AuthenticationException;
  19. use Symfony\Component\Security\Core\Exception\BadCredentialsException;
  20. use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
  21. use Symfony\Component\Security\Guard\AbstractGuardAuthenticator;
  22. use Symfony\Component\Security\Guard\AuthenticatorInterface;
  23. use Symfony\Component\Security\Guard\GuardAuthenticatorHandler;
  24. use Symfony\Component\Security\Guard\GuardAuthenticatorInterface;
  25. use Symfony\Component\Security\Guard\Token\PreAuthenticationGuardToken;
  26. use Symfony\Component\Security\Http\Firewall\ListenerInterface;
  27. use Symfony\Component\Security\Http\RememberMe\RememberMeServicesInterface;
  28. /**
  29.  * Authentication listener for the "guard" system.
  30.  *
  31.  * @author Ryan Weaver <ryan@knpuniversity.com>
  32.  * @author Amaury Leroux de Lens <amaury@lerouxdelens.com>
  33.  */
  34. class GuardAuthenticationListener implements ListenerInterface
  35. {
  36.     private $guardHandler;
  37.     private $authenticationManager;
  38.     private $providerKey;
  39.     private $guardAuthenticators;
  40.     private $logger;
  41.     private $rememberMeServices;
  42.     private $hideUserNotFoundExceptions;
  43.     /**
  44.      * @param GuardAuthenticatorHandler         $guardHandler          The Guard handler
  45.      * @param AuthenticationManagerInterface    $authenticationManager An AuthenticationManagerInterface instance
  46.      * @param string                            $providerKey           The provider (i.e. firewall) key
  47.      * @param iterable|AuthenticatorInterface[] $guardAuthenticators   The authenticators, with keys that match what's passed to GuardAuthenticationProvider
  48.      * @param LoggerInterface                   $logger                A LoggerInterface instance
  49.      */
  50.     public function __construct(GuardAuthenticatorHandler $guardHandlerAuthenticationManagerInterface $authenticationManager$providerKey$guardAuthenticatorsLoggerInterface $logger null$hideUserNotFoundExceptions true)
  51.     {
  52.         if (empty($providerKey)) {
  53.             throw new \InvalidArgumentException('$providerKey must not be empty.');
  54.         }
  55.         $this->guardHandler $guardHandler;
  56.         $this->authenticationManager $authenticationManager;
  57.         $this->providerKey $providerKey;
  58.         $this->guardAuthenticators $guardAuthenticators;
  59.         $this->logger $logger;
  60.         $this->hideUserNotFoundExceptions $hideUserNotFoundExceptions;
  61.     }
  62.     /**
  63.      * Iterates over each authenticator to see if each wants to authenticate the request.
  64.      */
  65.     public function handle(GetResponseEvent $event)
  66.     {
  67.         if (null !== $this->logger) {
  68.             $context = ['firewall_key' => $this->providerKey];
  69.             if ($this->guardAuthenticators instanceof \Countable || \is_array($this->guardAuthenticators)) {
  70.                 $context['authenticators'] = \count($this->guardAuthenticators);
  71.             }
  72.             $this->logger->debug('Checking for guard authentication credentials.'$context);
  73.         }
  74.         foreach ($this->guardAuthenticators as $key => $guardAuthenticator) {
  75.             // get a key that's unique to *this* guard authenticator
  76.             // this MUST be the same as GuardAuthenticationProvider
  77.             $uniqueGuardKey $this->providerKey.'_'.$key;
  78.             $this->executeGuardAuthenticator($uniqueGuardKey$guardAuthenticator$event);
  79.             if ($event->hasResponse()) {
  80.                 if (null !== $this->logger) {
  81.                     $this->logger->debug('The "{authenticator}" authenticator set the response. Any later authenticator will not be called', ['authenticator' => \get_class($guardAuthenticator)]);
  82.                 }
  83.                 break;
  84.             }
  85.         }
  86.     }
  87.     private function executeGuardAuthenticator($uniqueGuardKeyGuardAuthenticatorInterface $guardAuthenticatorGetResponseEvent $event)
  88.     {
  89.         $request $event->getRequest();
  90.         try {
  91.             // abort the execution of the authenticator if it doesn't support the request
  92.             if ($guardAuthenticator instanceof AuthenticatorInterface) {
  93.                 if (null !== $this->logger) {
  94.                     $this->logger->debug('Checking support on guard authenticator.', ['firewall_key' => $this->providerKey'authenticator' => \get_class($guardAuthenticator)]);
  95.                 }
  96.                 if (!$guardAuthenticator->supports($request)) {
  97.                     if (null !== $this->logger) {
  98.                         $this->logger->debug('Guard authenticator does not support the request.', ['firewall_key' => $this->providerKey'authenticator' => \get_class($guardAuthenticator)]);
  99.                     }
  100.                     return;
  101.                 }
  102.                 // as there was a support for given request,
  103.                 // authenticator is expected to give not-null credentials.
  104.                 $credentialsCanBeNull false;
  105.             } else {
  106.                 // deprecated since version 3.4, to be removed in 4.0
  107.                 $credentialsCanBeNull true;
  108.             }
  109.             if (null !== $this->logger) {
  110.                 $this->logger->debug('Calling getCredentials() on guard authenticator.', ['firewall_key' => $this->providerKey'authenticator' => \get_class($guardAuthenticator)]);
  111.             }
  112.             // allow the authenticator to fetch authentication info from the request
  113.             $credentials $guardAuthenticator->getCredentials($request);
  114.             if (null === $credentials) {
  115.                 // deprecated since version 3.4, to be removed in 4.0
  116.                 if ($credentialsCanBeNull) {
  117.                     return;
  118.                 }
  119.                 if ($guardAuthenticator instanceof AbstractGuardAuthenticator) {
  120.                     @trigger_error(sprintf('Returning null from "%1$s::getCredentials()" is deprecated since Symfony 3.4 and will throw an \UnexpectedValueException in 4.0. Return false from "%1$s::supports()" instead.', \get_class($guardAuthenticator)), \E_USER_DEPRECATED);
  121.                     return;
  122.                 }
  123.                 throw new \UnexpectedValueException(sprintf('The return value of "%1$s::getCredentials()" must not be null. Return false from "%1$s::supports()" instead.', \get_class($guardAuthenticator)));
  124.             }
  125.             // create a token with the unique key, so that the provider knows which authenticator to use
  126.             $token = new PreAuthenticationGuardToken($credentials$uniqueGuardKey);
  127.             if (null !== $this->logger) {
  128.                 $this->logger->debug('Passing guard token information to the GuardAuthenticationProvider', ['firewall_key' => $this->providerKey'authenticator' => \get_class($guardAuthenticator)]);
  129.             }
  130.             // pass the token into the AuthenticationManager system
  131.             // this indirectly calls GuardAuthenticationProvider::authenticate()
  132.             $token $this->authenticationManager->authenticate($token);
  133.             if (null !== $this->logger) {
  134.                 $this->logger->info('Guard authentication successful!', ['token' => $token'authenticator' => \get_class($guardAuthenticator)]);
  135.             }
  136.             // sets the token on the token storage, etc
  137.             $this->guardHandler->authenticateWithToken($token$request$this->providerKey);
  138.         } catch (AuthenticationException $e) {
  139.             // oh no! Authentication failed!
  140.             if (null !== $this->logger) {
  141.                 $this->logger->info('Guard authentication failed.', ['exception' => $e'authenticator' => \get_class($guardAuthenticator)]);
  142.             }
  143.             // Avoid leaking error details in case of invalid user (e.g. user not found or invalid account status)
  144.             // to prevent user enumeration via response content
  145.             if ($this->hideUserNotFoundExceptions && ($e instanceof UsernameNotFoundException || $e instanceof AccountStatusException)) {
  146.                 $e = new BadCredentialsException('Bad credentials.'0$e);
  147.             }
  148.             $response $this->guardHandler->handleAuthenticationFailure($e$request$guardAuthenticator$this->providerKey);
  149.             if ($response instanceof Response) {
  150.                 $event->setResponse($response);
  151.             }
  152.             return;
  153.         }
  154.         // success!
  155.         $response $this->guardHandler->handleAuthenticationSuccess($token$request$guardAuthenticator$this->providerKey);
  156.         if ($response instanceof Response) {
  157.             if (null !== $this->logger) {
  158.                 $this->logger->debug('Guard authenticator set success response.', ['response' => $response'authenticator' => \get_class($guardAuthenticator)]);
  159.             }
  160.             $event->setResponse($response);
  161.         } else {
  162.             if (null !== $this->logger) {
  163.                 $this->logger->debug('Guard authenticator set no success response: request continues.', ['authenticator' => \get_class($guardAuthenticator)]);
  164.             }
  165.         }
  166.         // attempt to trigger the remember me functionality
  167.         $this->triggerRememberMe($guardAuthenticator$request$token$response);
  168.     }
  169.     /**
  170.      * Should be called if this listener will support remember me.
  171.      */
  172.     public function setRememberMeServices(RememberMeServicesInterface $rememberMeServices)
  173.     {
  174.         $this->rememberMeServices $rememberMeServices;
  175.     }
  176.     /**
  177.      * Checks to see if remember me is supported in the authenticator and
  178.      * on the firewall. If it is, the RememberMeServicesInterface is notified.
  179.      */
  180.     private function triggerRememberMe(GuardAuthenticatorInterface $guardAuthenticatorRequest $requestTokenInterface $tokenResponse $response null)
  181.     {
  182.         if (null === $this->rememberMeServices) {
  183.             if (null !== $this->logger) {
  184.                 $this->logger->debug('Remember me skipped: it is not configured for the firewall.', ['authenticator' => \get_class($guardAuthenticator)]);
  185.             }
  186.             return;
  187.         }
  188.         if (!$guardAuthenticator->supportsRememberMe()) {
  189.             if (null !== $this->logger) {
  190.                 $this->logger->debug('Remember me skipped: your authenticator does not support it.', ['authenticator' => \get_class($guardAuthenticator)]);
  191.             }
  192.             return;
  193.         }
  194.         if (!$response instanceof Response) {
  195.             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_class($guardAuthenticator)));
  196.         }
  197.         $this->rememberMeServices->loginSuccess($request$response$token);
  198.     }
  199. }