vendor/symfony/src/Symfony/Component/Security/Http/RememberMe/TokenBasedRememberMeServices.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\HttpFoundation\Cookie;
       
     6 use Symfony\Component\HttpFoundation\Request;
       
     7 use Symfony\Component\HttpFoundation\Response;
       
     8 use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
       
     9 use Symfony\Component\Security\Core\Authentication\Token\RememberMeToken;
       
    10 use Symfony\Component\Security\Core\Exception\AuthenticationException;
       
    11 use Symfony\Component\Security\Core\User\UserInterface;
       
    12 
       
    13 /*
       
    14  * This file is part of the Symfony package.
       
    15  *
       
    16  * (c) Fabien Potencier <fabien@symfony.com>
       
    17  *
       
    18  * For the full copyright and license information, please view the LICENSE
       
    19  * file that was distributed with this source code.
       
    20  */
       
    21 
       
    22 /**
       
    23  * Concrete implementation of the RememberMeServicesInterface providing
       
    24  * remember-me capabilities without requiring a TokenProvider.
       
    25  *
       
    26  * @author Johannes M. Schmitt <schmittjoh@gmail.com>
       
    27  */
       
    28 class TokenBasedRememberMeServices extends AbstractRememberMeServices
       
    29 {
       
    30     /**
       
    31      * {@inheritDoc}
       
    32      */
       
    33     protected function processAutoLoginCookie(array $cookieParts, Request $request)
       
    34     {
       
    35         if (count($cookieParts) !== 4) {
       
    36             throw new AuthenticationException('The cookie is invalid.');
       
    37         }
       
    38 
       
    39         list($class, $username, $expires, $hash) = $cookieParts;
       
    40         if (false === $username = base64_decode($username, true)) {
       
    41             throw new AuthenticationException('$username contains a character from outside the base64 alphabet.');
       
    42         }
       
    43         try {
       
    44             $user = $this->getUserProvider($class)->loadUserByUsername($username);
       
    45         } catch (\Exception $ex) {
       
    46             if (!$ex instanceof AuthenticationException) {
       
    47                 $ex = new AuthenticationException($ex->getMessage(), null, $ex->getCode(), $ex);
       
    48             }
       
    49 
       
    50             throw $ex;
       
    51         }
       
    52 
       
    53         if (!$user instanceof UserInterface) {
       
    54             throw new \RuntimeException(sprintf('The UserProviderInterface implementation must return an instance of UserInterface, but returned "%s".', get_class($user)));
       
    55         }
       
    56 
       
    57         if (true !== $this->compareHashes($hash, $this->generateCookieHash($class, $username, $expires, $user->getPassword()))) {
       
    58             throw new AuthenticationException('The cookie\'s hash is invalid.');
       
    59         }
       
    60 
       
    61         if ($expires < time()) {
       
    62             throw new AuthenticationException('The cookie has expired.');
       
    63         }
       
    64 
       
    65         return $user;
       
    66     }
       
    67 
       
    68     /**
       
    69      * Compares two hashes using a constant-time algorithm to avoid (remote)
       
    70      * timing attacks.
       
    71      *
       
    72      * This is the same implementation as used in the BasePasswordEncoder.
       
    73      *
       
    74      * @param string $hash1 The first hash
       
    75      * @param string $hash2 The second hash
       
    76      *
       
    77      * @return Boolean true if the two hashes are the same, false otherwise
       
    78      */
       
    79     private function compareHashes($hash1, $hash2)
       
    80     {
       
    81         if (strlen($hash1) !== $c = strlen($hash2)) {
       
    82             return false;
       
    83         }
       
    84 
       
    85         $result = 0;
       
    86         for ($i = 0; $i < $c; $i++) {
       
    87             $result |= ord($hash1[$i]) ^ ord($hash2[$i]);
       
    88         }
       
    89 
       
    90         return 0 === $result;
       
    91     }
       
    92 
       
    93     /**
       
    94      * {@inheritDoc}
       
    95      */
       
    96     protected function onLoginSuccess(Request $request, Response $response, TokenInterface $token)
       
    97     {
       
    98         $user = $token->getUser();
       
    99         $expires = time() + $this->options['lifetime'];
       
   100         $value = $this->generateCookieValue(get_class($user), $user->getUsername(), $expires, $user->getPassword());
       
   101 
       
   102         $response->headers->setCookie(
       
   103             new Cookie(
       
   104                 $this->options['name'],
       
   105                 $value,
       
   106                 $expires,
       
   107                 $this->options['path'],
       
   108                 $this->options['domain'],
       
   109                 $this->options['secure'],
       
   110                 $this->options['httponly']
       
   111             )
       
   112         );
       
   113     }
       
   114 
       
   115     /**
       
   116      * Generates the cookie value.
       
   117      *
       
   118      * @param string  $class
       
   119      * @param string  $username The username
       
   120      * @param integer $expires  The unixtime when the cookie expires
       
   121      * @param string  $password The encoded password
       
   122      *
       
   123      * @throws \RuntimeException if username contains invalid chars
       
   124      *
       
   125      * @return string
       
   126      */
       
   127     protected function generateCookieValue($class, $username, $expires, $password)
       
   128     {
       
   129         return $this->encodeCookie(array(
       
   130             $class,
       
   131             base64_encode($username),
       
   132             $expires,
       
   133             $this->generateCookieHash($class, $username, $expires, $password)
       
   134         ));
       
   135     }
       
   136 
       
   137     /**
       
   138      * Generates a hash for the cookie to ensure it is not being tempered with
       
   139      *
       
   140      * @param string $class
       
   141      * @param string $username The username
       
   142      * @param integer $expires The unixtime when the cookie expires
       
   143      * @param string $password The encoded password
       
   144      * @throws \RuntimeException when the private key is empty
       
   145      * @return string
       
   146      */
       
   147     protected function generateCookieHash($class, $username, $expires, $password)
       
   148     {
       
   149         return hash('sha256', $class.$username.$expires.$password.$this->getKey());
       
   150     }
       
   151 }