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

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