2011-01-25 19:28:26 +00:00
< ? php
/*
* This file is part of the Symfony package .
*
2011-03-06 11:40:06 +00:00
* ( c ) Fabien Potencier < fabien @ symfony . com >
2011-01-25 19:28:26 +00:00
*
* For the full copyright and license information , please view the LICENSE
* file that was distributed with this source code .
*/
2012-04-02 04:52:14 +01:00
namespace Symfony\Component\Security\Http\RememberMe ;
use Symfony\Component\HttpFoundation\Cookie ;
use Symfony\Component\HttpFoundation\Request ;
use Symfony\Component\HttpFoundation\Response ;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface ;
use Symfony\Component\Security\Core\Exception\AuthenticationException ;
use Symfony\Component\Security\Core\User\UserInterface ;
2011-01-25 19:28:26 +00:00
/**
* Concrete implementation of the RememberMeServicesInterface providing
* remember - me capabilities without requiring a TokenProvider .
*
* @ author Johannes M . Schmitt < schmittjoh @ gmail . com >
*/
2011-03-10 20:27:42 +00:00
class TokenBasedRememberMeServices extends AbstractRememberMeServices
2011-01-25 19:28:26 +00:00
{
/**
* { @ inheritDoc }
*/
protected function processAutoLoginCookie ( array $cookieParts , Request $request )
{
if ( count ( $cookieParts ) !== 4 ) {
throw new AuthenticationException ( 'The cookie is invalid.' );
}
list ( $class , $username , $expires , $hash ) = $cookieParts ;
if ( false === $username = base64_decode ( $username , true )) {
throw new AuthenticationException ( '$username contains a character from outside the base64 alphabet.' );
}
try {
$user = $this -> getUserProvider ( $class ) -> loadUserByUsername ( $username );
} catch ( \Exception $ex ) {
if ( ! $ex instanceof AuthenticationException ) {
$ex = new AuthenticationException ( $ex -> getMessage (), null , $ex -> getCode (), $ex );
}
throw $ex ;
}
2011-03-07 17:17:46 +00:00
if ( ! $user instanceof UserInterface ) {
throw new \RuntimeException ( sprintf ( 'The UserProviderInterface implementation must return an instance of UserInterface, but returned "%s".' , get_class ( $user )));
2011-01-25 19:28:26 +00:00
}
if ( true !== $this -> compareHashes ( $hash , $this -> generateCookieHash ( $class , $username , $expires , $user -> getPassword ()))) {
throw new AuthenticationException ( 'The cookie\'s hash is invalid.' );
}
if ( $expires < time ()) {
throw new AuthenticationException ( 'The cookie has expired.' );
}
2011-03-10 20:27:42 +00:00
return $user ;
2011-01-25 19:28:26 +00:00
}
/**
* Compares two hashes using a constant - time algorithm to avoid ( remote )
* timing attacks .
*
* This is the same implementation as used in the BasePasswordEncoder .
*
* @ param string $hash1 The first hash
* @ param string $hash2 The second hash
*
* @ return Boolean true if the two hashes are the same , false otherwise
*/
2011-03-07 17:17:46 +00:00
private function compareHashes ( $hash1 , $hash2 )
2011-01-25 19:28:26 +00:00
{
if ( strlen ( $hash1 ) !== $c = strlen ( $hash2 )) {
return false ;
}
$result = 0 ;
for ( $i = 0 ; $i < $c ; $i ++ ) {
$result |= ord ( $hash1 [ $i ]) ^ ord ( $hash2 [ $i ]);
}
return 0 === $result ;
}
/**
* { @ inheritDoc }
*/
protected function onLoginSuccess ( Request $request , Response $response , TokenInterface $token )
{
$user = $token -> getUser ();
$expires = time () + $this -> options [ 'lifetime' ];
$value = $this -> generateCookieValue ( get_class ( $user ), $user -> getUsername (), $expires , $user -> getPassword ());
$response -> headers -> setCookie (
new Cookie (
$this -> options [ 'name' ],
$value ,
$expires ,
$this -> options [ 'path' ],
$this -> options [ 'domain' ],
$this -> options [ 'secure' ],
$this -> options [ 'httponly' ]
)
);
}
/**
2011-03-01 17:55:12 +00:00
* Generates the cookie value .
*
* @ param string $class
* @ param string $username The username
* @ param integer $expires The unixtime when the cookie expires
* @ param string $password The encoded password
2011-01-25 19:28:26 +00:00
*
* @ throws \RuntimeException if username contains invalid chars
2011-03-01 17:55:12 +00:00
*
2011-01-25 19:28:26 +00:00
* @ return string
*/
protected function generateCookieValue ( $class , $username , $expires , $password )
{
return $this -> encodeCookie ( array (
$class ,
base64_encode ( $username ),
$expires ,
$this -> generateCookieHash ( $class , $username , $expires , $password )
));
}
/**
* Generates a hash for the cookie to ensure it is not being tempered with
*
2012-05-18 18:41:48 +01:00
* @ param string $class
2012-05-15 21:19:31 +01:00
* @ param string $username The username
* @ param integer $expires The unixtime when the cookie expires
* @ param string $password The encoded password
2011-12-13 07:50:54 +00:00
*
2011-01-25 19:28:26 +00:00
* @ throws \RuntimeException when the private key is empty
2011-12-13 07:50:54 +00:00
*
2011-01-25 19:28:26 +00:00
* @ return string
*/
protected function generateCookieHash ( $class , $username , $expires , $password )
{
2011-03-10 20:27:42 +00:00
return hash ( 'sha256' , $class . $username . $expires . $password . $this -> getKey ());
2011-01-25 19:28:26 +00:00
}
}