vendor/symfony/src/Symfony/Component/Security/Http/RememberMe/AbstractRememberMeServices.php
changeset 0 7f95f8617b0b
equal deleted inserted replaced
-1:000000000000 0:7f95f8617b0b
       
     1 <?php
       
     2 
       
     3 namespace Symfony\Component\Security\Http\RememberMe;
       
     4 
       
     5 use Symfony\Component\Security\Core\Exception\AuthenticationException;
       
     6 use Symfony\Component\Security\Core\User\UserInterface;
       
     7 use Symfony\Component\Security\Core\Authentication\Token\RememberMeToken;
       
     8 use Symfony\Component\Security\Http\Logout\LogoutHandlerInterface;
       
     9 use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
       
    10 use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
       
    11 use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
       
    12 use Symfony\Component\Security\Core\Exception\CookieTheftException;
       
    13 use Symfony\Component\Security\Core\User\UserProviderInterface;
       
    14 use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
       
    15 use Symfony\Component\HttpFoundation\Response;
       
    16 use Symfony\Component\HttpFoundation\Request;
       
    17 use Symfony\Component\HttpFoundation\Cookie;
       
    18 use Symfony\Component\HttpKernel\Log\LoggerInterface;
       
    19 
       
    20 /*
       
    21  * This file is part of the Symfony package.
       
    22  *
       
    23  * (c) Fabien Potencier <fabien@symfony.com>
       
    24  *
       
    25  * For the full copyright and license information, please view the LICENSE
       
    26  * file that was distributed with this source code.
       
    27  */
       
    28 
       
    29 /**
       
    30  * Base class implementing the RememberMeServicesInterface
       
    31  *
       
    32  * @author Johannes M. Schmitt <schmittjoh@gmail.com>
       
    33  */
       
    34 abstract class AbstractRememberMeServices implements RememberMeServicesInterface, LogoutHandlerInterface
       
    35 {
       
    36     const COOKIE_DELIMITER = ':';
       
    37 
       
    38     protected $logger;
       
    39     protected $options;
       
    40     private $providerKey;
       
    41     private $key;
       
    42     private $userProviders;
       
    43 
       
    44     /**
       
    45      * Constructor
       
    46      *
       
    47      * @param array           $userProviders
       
    48      * @param string          $key
       
    49      * @param string          $providerKey
       
    50      * @param array           $options
       
    51      * @param LoggerInterface $logger
       
    52      */
       
    53     public function __construct(array $userProviders, $key, $providerKey, array $options = array(), LoggerInterface $logger = null)
       
    54     {
       
    55         if (empty($key)) {
       
    56             throw new \InvalidArgumentException('$key must not be empty.');
       
    57         }
       
    58         if (empty($providerKey)) {
       
    59             throw new \InvalidArgumentException('$providerKey must not be empty.');
       
    60         }
       
    61         if (0 === count($userProviders)) {
       
    62             throw new \InvalidArgumentException('You must provide at least one user provider.');
       
    63         }
       
    64 
       
    65         $this->userProviders = $userProviders;
       
    66         $this->key = $key;
       
    67         $this->providerKey = $providerKey;
       
    68         $this->options = $options;
       
    69         $this->logger = $logger;
       
    70     }
       
    71 
       
    72     /**
       
    73      * Returns the parameter that is used for checking whether remember-me
       
    74      * services have been requested.
       
    75      *
       
    76      * @return string
       
    77      */
       
    78     public function getRememberMeParameter()
       
    79     {
       
    80         return $this->options['remember_me_parameter'];
       
    81     }
       
    82 
       
    83     public function getKey()
       
    84     {
       
    85         return $this->key;
       
    86     }
       
    87 
       
    88     /**
       
    89      * Implementation of RememberMeServicesInterface. Detects whether a remember-me
       
    90      * cookie was set, decodes it, and hands it to subclasses for further processing.
       
    91      *
       
    92      * @param Request $request
       
    93      * @return TokenInterface
       
    94      */
       
    95     public final function autoLogin(Request $request)
       
    96     {
       
    97         if (null === $cookie = $request->cookies->get($this->options['name'])) {
       
    98             return;
       
    99         }
       
   100 
       
   101         if (null !== $this->logger) {
       
   102             $this->logger->debug('Remember-me cookie detected.');
       
   103         }
       
   104 
       
   105         $cookieParts = $this->decodeCookie($cookie);
       
   106 
       
   107         try {
       
   108             $user = $this->processAutoLoginCookie($cookieParts, $request);
       
   109 
       
   110             if (!$user instanceof UserInterface) {
       
   111                 throw new \RuntimeException('processAutoLoginCookie() must return a UserInterface implementation.');
       
   112             }
       
   113 
       
   114             if (null !== $this->logger) {
       
   115                 $this->logger->info('Remember-me cookie accepted.');
       
   116             }
       
   117 
       
   118             return new RememberMeToken($user, $this->providerKey, $this->key);
       
   119         } catch (CookieTheftException $theft) {
       
   120             $this->cancelCookie($request);
       
   121 
       
   122             throw $theft;
       
   123         } catch (UsernameNotFoundException $notFound) {
       
   124             if (null !== $this->logger) {
       
   125                 $this->logger->info('User for remember-me cookie not found.');
       
   126             }
       
   127         } catch (UnsupportedUserException $unSupported) {
       
   128             if (null !== $this->logger) {
       
   129                 $this->logger->warn('User class for remember-me cookie not supported.');
       
   130             }
       
   131         } catch (AuthenticationException $invalid) {
       
   132             if (null !== $this->logger) {
       
   133                 $this->logger->debug('Remember-Me authentication failed: '.$invalid->getMessage());
       
   134             }
       
   135         }
       
   136 
       
   137         $this->cancelCookie($request);
       
   138 
       
   139         return null;
       
   140     }
       
   141 
       
   142     /**
       
   143      * Implementation for LogoutHandlerInterface. Deletes the cookie.
       
   144      *
       
   145      * @param Request        $request
       
   146      * @param Response       $response
       
   147      * @param TokenInterface $token
       
   148      * @return void
       
   149      */
       
   150     public function logout(Request $request, Response $response, TokenInterface $token)
       
   151     {
       
   152         $this->cancelCookie($request);
       
   153     }
       
   154 
       
   155     /**
       
   156      * Implementation for RememberMeServicesInterface. Deletes the cookie when
       
   157      * an attempted authentication fails.
       
   158      *
       
   159      * @param Request $request
       
   160      * @return void
       
   161      */
       
   162     public final function loginFail(Request $request)
       
   163     {
       
   164         $this->cancelCookie($request);
       
   165         $this->onLoginFail($request);
       
   166     }
       
   167 
       
   168     /**
       
   169      * Implementation for RememberMeServicesInterface. This is called when an
       
   170      * authentication is successful.
       
   171      *
       
   172      * @param Request        $request
       
   173      * @param Response       $response
       
   174      * @param TokenInterface $token    The token that resulted in a successful authentication
       
   175      * @return void
       
   176      */
       
   177     public final function loginSuccess(Request $request, Response $response, TokenInterface $token)
       
   178     {
       
   179         if (!$token->getUser() instanceof UserInterface) {
       
   180             if (null !== $this->logger) {
       
   181                 $this->logger->debug('Remember-me ignores token since it does not contain an UserInterface implementation.');
       
   182             }
       
   183 
       
   184             return;
       
   185         }
       
   186 
       
   187         if (!$this->isRememberMeRequested($request)) {
       
   188             if (null !== $this->logger) {
       
   189                 $this->logger->debug('Remember-me was not requested.');
       
   190             }
       
   191 
       
   192             return;
       
   193         }
       
   194 
       
   195         if (null !== $this->logger) {
       
   196             $this->logger->debug('Remember-me was requested; setting cookie.');
       
   197         }
       
   198 
       
   199         $this->onLoginSuccess($request, $response, $token);
       
   200     }
       
   201 
       
   202     /**
       
   203      * Subclasses should validate the cookie and do any additional processing
       
   204      * that is required. This is called from autoLogin().
       
   205      *
       
   206      * @param array   $cookieParts
       
   207      * @param Request $request
       
   208      * @return TokenInterface
       
   209      */
       
   210     abstract protected function processAutoLoginCookie(array $cookieParts, Request $request);
       
   211 
       
   212     protected function onLoginFail(Request $request)
       
   213     {
       
   214     }
       
   215 
       
   216     /**
       
   217      * This is called after a user has been logged in successfully, and has
       
   218      * requested remember-me capabilities. The implementation usually sets a
       
   219      * cookie and possibly stores a persistent record of it.
       
   220      *
       
   221      * @param Request        $request
       
   222      * @param Response       $response
       
   223      * @param TokenInterface $token
       
   224      * @return void
       
   225      */
       
   226     abstract protected function onLoginSuccess(Request $request, Response $response, TokenInterface $token);
       
   227 
       
   228     protected final function getUserProvider($class)
       
   229     {
       
   230         foreach ($this->userProviders as $provider) {
       
   231             if ($provider->supportsClass($class)) {
       
   232                 return $provider;
       
   233             }
       
   234         }
       
   235 
       
   236         throw new UnsupportedUserException(sprintf('There is no user provider that supports class "%s".', $class));
       
   237     }
       
   238 
       
   239     /**
       
   240      * Decodes the raw cookie value
       
   241      *
       
   242      * @param string $rawCookie
       
   243      * @return array
       
   244      */
       
   245     protected function decodeCookie($rawCookie)
       
   246     {
       
   247         return explode(self::COOKIE_DELIMITER, base64_decode($rawCookie));
       
   248     }
       
   249 
       
   250     /**
       
   251      * Encodes the cookie parts
       
   252      *
       
   253      * @param array $cookieParts
       
   254      * @return string
       
   255      */
       
   256     protected function encodeCookie(array $cookieParts)
       
   257     {
       
   258         return base64_encode(implode(self::COOKIE_DELIMITER, $cookieParts));
       
   259     }
       
   260 
       
   261     /**
       
   262      * Deletes the remember-me cookie
       
   263      *
       
   264      * @param Request $request
       
   265      * @return void
       
   266      */
       
   267     protected function cancelCookie(Request $request)
       
   268     {
       
   269         if (null !== $this->logger) {
       
   270             $this->logger->debug(sprintf('Clearing remember-me cookie "%s"', $this->options['name']));
       
   271         }
       
   272 
       
   273         $request->attributes->set(self::COOKIE_ATTR_NAME, new Cookie($this->options['name'], null, 1, $this->options['path'], $this->options['domain']));
       
   274     }
       
   275 
       
   276     /**
       
   277      * Checks whether remember-me capabilities where requested
       
   278      *
       
   279      * @param Request $request
       
   280      * @return Boolean
       
   281      */
       
   282     protected function isRememberMeRequested(Request $request)
       
   283     {
       
   284         if (true === $this->options['always_remember_me']) {
       
   285             return true;
       
   286         }
       
   287 
       
   288         $parameter = $request->request->get($this->options['remember_me_parameter'], null, true);
       
   289 
       
   290         if ($parameter === null && null !== $this->logger) {
       
   291             $this->logger->debug(sprintf('Did not send remember-me cookie (remember-me parameter "%s" was not sent).', $this->options['remember_me_parameter']));
       
   292         }
       
   293 
       
   294         return $parameter === 'true' || $parameter === 'on' || $parameter === '1' || $parameter === 'yes';
       
   295     }
       
   296 }