"Fossies" - the Fresh Open Source Software Archive

Member "Symfony/vendor/symfony/symfony/src/Symfony/Component/Security/Http/Firewall/AbstractAuthenticationListener.php" (30 Mar 2020, 9313 Bytes) of package /linux/www/Symfony_Standard_Vendors_3.4.39.tgz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) PHP source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "AbstractAuthenticationListener.php" see the Fossies "Dox" file reference documentation.

    1 <?php
    2 
    3 /*
    4  * This file is part of the Symfony package.
    5  *
    6  * (c) Fabien Potencier <fabien@symfony.com>
    7  *
    8  * For the full copyright and license information, please view the LICENSE
    9  * file that was distributed with this source code.
   10  */
   11 
   12 namespace Symfony\Component\Security\Http\Firewall;
   13 
   14 use Psr\Log\LoggerInterface;
   15 use Symfony\Component\EventDispatcher\EventDispatcherInterface;
   16 use Symfony\Component\HttpFoundation\Request;
   17 use Symfony\Component\HttpFoundation\Response;
   18 use Symfony\Component\HttpKernel\Event\GetResponseEvent;
   19 use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
   20 use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
   21 use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
   22 use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
   23 use Symfony\Component\Security\Core\Exception\AuthenticationException;
   24 use Symfony\Component\Security\Core\Exception\SessionUnavailableException;
   25 use Symfony\Component\Security\Core\Security;
   26 use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface;
   27 use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface;
   28 use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
   29 use Symfony\Component\Security\Http\HttpUtils;
   30 use Symfony\Component\Security\Http\RememberMe\RememberMeServicesInterface;
   31 use Symfony\Component\Security\Http\SecurityEvents;
   32 use Symfony\Component\Security\Http\Session\SessionAuthenticationStrategyInterface;
   33 
   34 /**
   35  * The AbstractAuthenticationListener is the preferred base class for all
   36  * browser-/HTTP-based authentication requests.
   37  *
   38  * Subclasses likely have to implement the following:
   39  * - an TokenInterface to hold authentication related data
   40  * - an AuthenticationProvider to perform the actual authentication of the
   41  *   token, retrieve the UserInterface implementation from a database, and
   42  *   perform the specific account checks using the UserChecker
   43  *
   44  * By default, this listener only is active for a specific path, e.g.
   45  * /login_check. If you want to change this behavior, you can overwrite the
   46  * requiresAuthentication() method.
   47  *
   48  * @author Fabien Potencier <fabien@symfony.com>
   49  * @author Johannes M. Schmitt <schmittjoh@gmail.com>
   50  */
   51 abstract class AbstractAuthenticationListener implements ListenerInterface
   52 {
   53     protected $options;
   54     protected $logger;
   55     protected $authenticationManager;
   56     protected $providerKey;
   57     protected $httpUtils;
   58 
   59     private $tokenStorage;
   60     private $sessionStrategy;
   61     private $dispatcher;
   62     private $successHandler;
   63     private $failureHandler;
   64     private $rememberMeServices;
   65 
   66     /**
   67      * @param TokenStorageInterface          $tokenStorage          A TokenStorageInterface instance
   68      * @param AuthenticationManagerInterface $authenticationManager An AuthenticationManagerInterface instance
   69      * @param HttpUtils                      $httpUtils             An HttpUtils instance
   70      * @param string                         $providerKey
   71      * @param array                          $options               An array of options for the processing of a
   72      *                                                              successful, or failed authentication attempt
   73      * @param LoggerInterface|null           $logger                A LoggerInterface instance
   74      * @param EventDispatcherInterface|null  $dispatcher            An EventDispatcherInterface instance
   75      *
   76      * @throws \InvalidArgumentException
   77      */
   78     public function __construct(TokenStorageInterface $tokenStorage, AuthenticationManagerInterface $authenticationManager, SessionAuthenticationStrategyInterface $sessionStrategy, HttpUtils $httpUtils, $providerKey, AuthenticationSuccessHandlerInterface $successHandler, AuthenticationFailureHandlerInterface $failureHandler, array $options = [], LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null)
   79     {
   80         if (empty($providerKey)) {
   81             throw new \InvalidArgumentException('$providerKey must not be empty.');
   82         }
   83 
   84         $this->tokenStorage = $tokenStorage;
   85         $this->authenticationManager = $authenticationManager;
   86         $this->sessionStrategy = $sessionStrategy;
   87         $this->providerKey = $providerKey;
   88         $this->successHandler = $successHandler;
   89         $this->failureHandler = $failureHandler;
   90         $this->options = array_merge([
   91             'check_path' => '/login_check',
   92             'login_path' => '/login',
   93             'always_use_default_target_path' => false,
   94             'default_target_path' => '/',
   95             'target_path_parameter' => '_target_path',
   96             'use_referer' => false,
   97             'failure_path' => null,
   98             'failure_forward' => false,
   99             'require_previous_session' => true,
  100         ], $options);
  101         $this->logger = $logger;
  102         $this->dispatcher = $dispatcher;
  103         $this->httpUtils = $httpUtils;
  104     }
  105 
  106     /**
  107      * Sets the RememberMeServices implementation to use.
  108      */
  109     public function setRememberMeServices(RememberMeServicesInterface $rememberMeServices)
  110     {
  111         $this->rememberMeServices = $rememberMeServices;
  112     }
  113 
  114     /**
  115      * Handles form based authentication.
  116      *
  117      * @throws \RuntimeException
  118      * @throws SessionUnavailableException
  119      */
  120     final public function handle(GetResponseEvent $event)
  121     {
  122         $request = $event->getRequest();
  123 
  124         if (!$this->requiresAuthentication($request)) {
  125             return;
  126         }
  127 
  128         if (!$request->hasSession()) {
  129             throw new \RuntimeException('This authentication method requires a session.');
  130         }
  131 
  132         try {
  133             if ($this->options['require_previous_session'] && !$request->hasPreviousSession()) {
  134                 throw new SessionUnavailableException('Your session has timed out, or you have disabled cookies.');
  135             }
  136 
  137             if (null === $returnValue = $this->attemptAuthentication($request)) {
  138                 return;
  139             }
  140 
  141             if ($returnValue instanceof TokenInterface) {
  142                 $this->sessionStrategy->onAuthentication($request, $returnValue);
  143 
  144                 $response = $this->onSuccess($request, $returnValue);
  145             } elseif ($returnValue instanceof Response) {
  146                 $response = $returnValue;
  147             } else {
  148                 throw new \RuntimeException('attemptAuthentication() must either return a Response, an implementation of TokenInterface, or null.');
  149             }
  150         } catch (AuthenticationException $e) {
  151             $response = $this->onFailure($request, $e);
  152         }
  153 
  154         $event->setResponse($response);
  155     }
  156 
  157     /**
  158      * Whether this request requires authentication.
  159      *
  160      * The default implementation only processes requests to a specific path,
  161      * but a subclass could change this to only authenticate requests where a
  162      * certain parameters is present.
  163      *
  164      * @return bool
  165      */
  166     protected function requiresAuthentication(Request $request)
  167     {
  168         return $this->httpUtils->checkRequestPath($request, $this->options['check_path']);
  169     }
  170 
  171     /**
  172      * Performs authentication.
  173      *
  174      * @return TokenInterface|Response|null The authenticated token, null if full authentication is not possible, or a Response
  175      *
  176      * @throws AuthenticationException if the authentication fails
  177      */
  178     abstract protected function attemptAuthentication(Request $request);
  179 
  180     private function onFailure(Request $request, AuthenticationException $failed)
  181     {
  182         if (null !== $this->logger) {
  183             $this->logger->info('Authentication request failed.', ['exception' => $failed]);
  184         }
  185 
  186         $token = $this->tokenStorage->getToken();
  187         if ($token instanceof UsernamePasswordToken && $this->providerKey === $token->getProviderKey()) {
  188             $this->tokenStorage->setToken(null);
  189         }
  190 
  191         $response = $this->failureHandler->onAuthenticationFailure($request, $failed);
  192 
  193         if (!$response instanceof Response) {
  194             throw new \RuntimeException('Authentication Failure Handler did not return a Response.');
  195         }
  196 
  197         return $response;
  198     }
  199 
  200     private function onSuccess(Request $request, TokenInterface $token)
  201     {
  202         if (null !== $this->logger) {
  203             $this->logger->info('User has been authenticated successfully.', ['username' => $token->getUsername()]);
  204         }
  205 
  206         $this->tokenStorage->setToken($token);
  207 
  208         $session = $request->getSession();
  209         $session->remove(Security::AUTHENTICATION_ERROR);
  210         $session->remove(Security::LAST_USERNAME);
  211 
  212         if (null !== $this->dispatcher) {
  213             $loginEvent = new InteractiveLoginEvent($request, $token);
  214             $this->dispatcher->dispatch(SecurityEvents::INTERACTIVE_LOGIN, $loginEvent);
  215         }
  216 
  217         $response = $this->successHandler->onAuthenticationSuccess($request, $token);
  218 
  219         if (!$response instanceof Response) {
  220             throw new \RuntimeException('Authentication Success Handler did not return a Response.');
  221         }
  222 
  223         if (null !== $this->rememberMeServices) {
  224             $this->rememberMeServices->loginSuccess($request, $response, $token);
  225         }
  226 
  227         return $response;
  228     }
  229 }