[Security] refactored remember-me code
This commit is contained in:
parent
129d7c7c5f
commit
3d97638813
@ -12,6 +12,8 @@
|
||||
|
||||
<parameter key="security.authentication.rememberme.services.persistent.class">Symfony\Component\Security\Http\RememberMe\PersistentTokenBasedRememberMeServices</parameter>
|
||||
<parameter key="security.authentication.rememberme.services.simplehash.class">Symfony\Component\Security\Http\RememberMe\TokenBasedRememberMeServices</parameter>
|
||||
|
||||
<parameter key="security.rememberme.response_listener.class">Symfony\Bundle\SecurityBundle\ResponseListener</parameter>
|
||||
</parameters>
|
||||
|
||||
<services>
|
||||
@ -47,6 +49,10 @@
|
||||
parent="security.authentication.rememberme.services.abstract"
|
||||
abstract="true">
|
||||
</service>
|
||||
|
||||
<service id="security.rememberme.response_listener" class="%security.rememberme.response_listener.class%">
|
||||
<tag name="kernel.listener" method="handle" event="core.response"/>
|
||||
</service>
|
||||
</services>
|
||||
|
||||
</container>
|
24
src/Symfony/Bundle/SecurityBundle/ResponseListener.php
Normal file
24
src/Symfony/Bundle/SecurityBundle/ResponseListener.php
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Bundle\SecurityBundle;
|
||||
|
||||
use Symfony\Component\EventDispatcher\EventInterface;
|
||||
use Symfony\Component\Security\Http\RememberMe\RememberMeServicesInterface;
|
||||
|
||||
/**
|
||||
* Adds remember-me cookies to the Response.
|
||||
*
|
||||
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
|
||||
*/
|
||||
class ResponseListener
|
||||
{
|
||||
public function handle(EventInterface $event)
|
||||
{
|
||||
$request = $event->get('request');
|
||||
if (!$request->attributes->has(RememberMeServicesInterface::COOKIE_ATTR_NAME)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$event->get('response')->headers->setCookie($request->attributes->get(RememberMeServicesInterface::COOKIE_ATTR_NAME));
|
||||
}
|
||||
}
|
@ -27,7 +27,7 @@ interface AuthenticationManagerInterface
|
||||
*
|
||||
* @param TokenInterface $token The TokenInterface instance to authenticate
|
||||
*
|
||||
* @return TokenInterface An authenticated TokenInterface instance
|
||||
* @return TokenInterface An authenticated TokenInterface instance, never null
|
||||
*
|
||||
* @throws AuthenticationException if the authentication fails
|
||||
*/
|
||||
|
@ -11,7 +11,6 @@
|
||||
|
||||
namespace Symfony\Component\Security\Core\Authentication\Token;
|
||||
|
||||
use Symfony\Component\Security\Core\Authentication\RememberMe\PersistentTokenInterface;
|
||||
use Symfony\Component\Security\Core\User\UserInterface;
|
||||
|
||||
/**
|
||||
@ -23,7 +22,6 @@ class RememberMeToken extends AbstractToken
|
||||
{
|
||||
private $key;
|
||||
private $providerKey;
|
||||
private $persistentToken;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
@ -32,7 +30,7 @@ class RememberMeToken extends AbstractToken
|
||||
* @param string $providerKey
|
||||
* @param string $key
|
||||
*/
|
||||
public function __construct(UserInterface $user, $providerKey, $key, PersistentTokenInterface $persistentToken = null) {
|
||||
public function __construct(UserInterface $user, $providerKey, $key) {
|
||||
parent::__construct($user->getRoles());
|
||||
|
||||
if (empty($key)) {
|
||||
@ -45,7 +43,6 @@ class RememberMeToken extends AbstractToken
|
||||
|
||||
$this->providerKey = $providerKey;
|
||||
$this->key = $key;
|
||||
$this->persistentToken = $persistentToken;
|
||||
|
||||
$this->setUser($user);
|
||||
parent::setAuthenticated(true);
|
||||
@ -70,11 +67,6 @@ class RememberMeToken extends AbstractToken
|
||||
return $this->key;
|
||||
}
|
||||
|
||||
public function getPersistentToken()
|
||||
{
|
||||
return $this->persistentToken;
|
||||
}
|
||||
|
||||
public function getCredentials()
|
||||
{
|
||||
return '';
|
||||
|
@ -35,7 +35,6 @@ class RememberMeListener implements ListenerInterface
|
||||
private $rememberMeServices;
|
||||
private $authenticationManager;
|
||||
private $logger;
|
||||
private $lastState;
|
||||
private $eventDispatcher;
|
||||
|
||||
/**
|
||||
@ -63,7 +62,6 @@ class RememberMeListener implements ListenerInterface
|
||||
public function register(EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$dispatcher->connect('core.security', array($this, 'checkCookies'), 0);
|
||||
$dispatcher->connect('core.response', array($this, 'updateCookies'), 0);
|
||||
|
||||
$this->eventDispatcher = $dispatcher;
|
||||
}
|
||||
@ -73,7 +71,6 @@ class RememberMeListener implements ListenerInterface
|
||||
*/
|
||||
public function unregister(EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$dispatcher->disconnect('core.response', array($this, 'updateCookies'));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -83,74 +80,36 @@ class RememberMeListener implements ListenerInterface
|
||||
*/
|
||||
public function checkCookies(EventInterface $event)
|
||||
{
|
||||
$this->lastState = null;
|
||||
|
||||
if (null !== $this->securityContext->getToken()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$request = $event->get('request');
|
||||
if (null === $token = $this->rememberMeServices->autoLogin($request)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (null === $token = $this->rememberMeServices->autoLogin($event->get('request'))) {
|
||||
return;
|
||||
$token = $this->authenticationManager->authenticate($token);
|
||||
$this->securityContext->setToken($token);
|
||||
|
||||
if (null !== $this->eventDispatcher) {
|
||||
$this->eventDispatcher->notify(new Event($this, 'security.interactive_login', array('request' => $request, 'token' => $token)));
|
||||
}
|
||||
|
||||
try {
|
||||
if (null === $token = $this->authenticationManager->authenticate($token)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->securityContext->setToken($token);
|
||||
|
||||
if (null !== $this->eventDispatcher) {
|
||||
$this->eventDispatcher->notify(new Event($this, 'security.interactive_login', array('request' => $event->get('request'), 'token' => $token)));
|
||||
}
|
||||
|
||||
if (null !== $this->logger) {
|
||||
$this->logger->debug('SecurityContext populated with remember-me token.');
|
||||
}
|
||||
|
||||
$this->lastState = $token;
|
||||
} catch (AuthenticationException $failed) {
|
||||
if (null !== $this->logger) {
|
||||
$this->logger->debug(
|
||||
'SecurityContext not populated with remember-me token as the'
|
||||
.' AuthenticationManager rejected the AuthenticationToken returned'
|
||||
.' by the RememberMeServices: '.$failed->getMessage()
|
||||
);
|
||||
}
|
||||
|
||||
$this->lastState = $failed;
|
||||
}
|
||||
} catch (AuthenticationException $cookieInvalid) {
|
||||
$this->lastState = $cookieInvalid;
|
||||
|
||||
if (null !== $this->logger) {
|
||||
$this->logger->debug('The presented cookie was invalid: '.$cookieInvalid->getMessage());
|
||||
$this->logger->debug('SecurityContext populated with remember-me token.');
|
||||
}
|
||||
} catch (AuthenticationException $failed) {
|
||||
if (null !== $this->logger) {
|
||||
$this->logger->debug(
|
||||
'SecurityContext not populated with remember-me token as the'
|
||||
.' AuthenticationManager rejected the AuthenticationToken returned'
|
||||
.' by the RememberMeServices: '.$failed->getMessage()
|
||||
);
|
||||
}
|
||||
|
||||
// silently ignore everything except a cookie theft exception
|
||||
if ($cookieInvalid instanceof CookieTheftException) {
|
||||
throw $cookieInvalid;
|
||||
}
|
||||
$this->rememberMeServices->loginFail($request);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update cookies
|
||||
* @param Event $event
|
||||
*/
|
||||
public function updateCookies(EventInterface $event, Response $response)
|
||||
{
|
||||
if (HttpKernelInterface::MASTER_REQUEST !== $event->get('request_type')) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
if ($this->lastState instanceof TokenInterface) {
|
||||
$this->rememberMeServices->loginSuccess($event->get('request'), $response, $this->lastState);
|
||||
} else if ($this->lastState instanceof AuthenticationException) {
|
||||
$this->rememberMeServices->loginFail($event->get('request'), $response);
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
@ -2,14 +2,19 @@
|
||||
|
||||
namespace Symfony\Component\Security\Http\RememberMe;
|
||||
|
||||
use Symfony\Component\Security\Core\Exception\AuthenticationException;
|
||||
use Symfony\Component\Security\Core\User\UserInterface;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\RememberMeToken;
|
||||
use Symfony\Component\Security\Http\Logout\LogoutHandlerInterface;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
|
||||
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
|
||||
use Symfony\Component\Security\Core\Exception\CookieTheftException;
|
||||
use Symfony\Component\Security\Core\User\UserProviderInterface;
|
||||
use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Cookie;
|
||||
use Symfony\Component\HttpKernel\Log\LoggerInterface;
|
||||
|
||||
/*
|
||||
@ -26,14 +31,14 @@ use Symfony\Component\HttpKernel\Log\LoggerInterface;
|
||||
*
|
||||
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
|
||||
*/
|
||||
abstract class RememberMeServices implements RememberMeServicesInterface, LogoutHandlerInterface
|
||||
abstract class AbstractRememberMeServices implements RememberMeServicesInterface, LogoutHandlerInterface
|
||||
{
|
||||
const COOKIE_DELIMITER = ':';
|
||||
|
||||
protected $options;
|
||||
protected $logger;
|
||||
protected $providerKey;
|
||||
protected $key;
|
||||
protected $options;
|
||||
private $providerKey;
|
||||
private $key;
|
||||
private $userProviders;
|
||||
|
||||
/**
|
||||
@ -73,6 +78,11 @@ abstract class RememberMeServices implements RememberMeServicesInterface, Logout
|
||||
return $this->options['remember_me_parameter'];
|
||||
}
|
||||
|
||||
public function getKey()
|
||||
{
|
||||
return $this->key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of RememberMeServicesInterface. Detects whether a remember-me
|
||||
* cookie was set, decodes it, and hands it to subclasses for further processing.
|
||||
@ -91,17 +101,40 @@ abstract class RememberMeServices implements RememberMeServicesInterface, Logout
|
||||
}
|
||||
|
||||
$cookieParts = $this->decodeCookie($cookie);
|
||||
$token = $this->processAutoLoginCookie($cookieParts, $request);
|
||||
|
||||
if (!$token instanceof TokenInterface) {
|
||||
throw new \RuntimeException('processAutoLoginCookie() must return a TokenInterface implementation.');
|
||||
try {
|
||||
$user = $this->processAutoLoginCookie($cookieParts, $request);
|
||||
|
||||
if (!$user instanceof UserInterface) {
|
||||
throw new \RuntimeException('processAutoLoginCookie() must return a UserInterface implementation.');
|
||||
}
|
||||
|
||||
if (null !== $this->logger) {
|
||||
$this->logger->debug('Remember-me cookie accepted.');
|
||||
}
|
||||
|
||||
return new RememberMeToken($user, $this->providerKey, $this->key);
|
||||
} catch (CookieTheftException $theft) {
|
||||
$this->cancelCookie($request);
|
||||
|
||||
throw $theft;
|
||||
} catch (UsernameNotFoundException $notFound) {
|
||||
if (null !== $this->logger) {
|
||||
$this->logger->debug('User for remember-me cookie not found.');
|
||||
}
|
||||
} catch (UnsupportedUserException $unSupported) {
|
||||
if (null !== $this->logger) {
|
||||
$this->logger->debug('User class for remember-me cookie not supported.');
|
||||
}
|
||||
} catch (AuthenticationException $invalid) {
|
||||
if (null !== $this->logger) {
|
||||
$this->logger->debug('Remember-Me authentication failed: '.$invalid->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
if (null !== $this->logger) {
|
||||
$this->logger->debug('Remember-me cookie accepted.');
|
||||
}
|
||||
$this->cancelCookie($request);
|
||||
|
||||
return $token;
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -114,7 +147,7 @@ abstract class RememberMeServices implements RememberMeServicesInterface, Logout
|
||||
*/
|
||||
public function logout(Request $request, Response $response, TokenInterface $token)
|
||||
{
|
||||
$this->cancelCookie($response);
|
||||
$this->cancelCookie($request);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -122,12 +155,11 @@ abstract class RememberMeServices implements RememberMeServicesInterface, Logout
|
||||
* an attempted authentication fails.
|
||||
*
|
||||
* @param Request $request
|
||||
* @param Response $response
|
||||
* @return void
|
||||
*/
|
||||
public function loginFail(Request $request, Response $response)
|
||||
public final function loginFail(Request $request)
|
||||
{
|
||||
$this->cancelCookie($response);
|
||||
$this->cancelCookie($request);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -141,28 +173,24 @@ abstract class RememberMeServices implements RememberMeServicesInterface, Logout
|
||||
*/
|
||||
public final function loginSuccess(Request $request, Response $response, TokenInterface $token)
|
||||
{
|
||||
if (!$token instanceof RememberMeToken) {
|
||||
if (!$token->getUser() instanceof UserInterface) {
|
||||
if (null !== $this->logger) {
|
||||
$this->logger->debug('Remember-me ignores token since it does not contain an UserInterface implementation.');
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$this->isRememberMeRequested($request)) {
|
||||
if (null !== $this->logger) {
|
||||
$this->logger->debug('Remember-me was not requested.');
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$token->getUser() instanceof UserInterface) {
|
||||
if (null !== $this->logger) {
|
||||
$this->logger->debug('Remember-me was requested; setting cookie.');
|
||||
$this->logger->debug('Remember-me ignores token since it does not contain an UserInterface implementation.');
|
||||
}
|
||||
} else if (null !== $this->logger) {
|
||||
$this->logger->debug('Re-newing remember-me token; setting cookie.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$this->isRememberMeRequested($request)) {
|
||||
if (null !== $this->logger) {
|
||||
$this->logger->debug('Remember-me was not requested.');
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (null !== $this->logger) {
|
||||
$this->logger->debug('Remember-me was requested; setting cookie.');
|
||||
}
|
||||
|
||||
$this->onLoginSuccess($request, $response, $token);
|
||||
@ -178,6 +206,10 @@ abstract class RememberMeServices implements RememberMeServicesInterface, Logout
|
||||
*/
|
||||
abstract protected function processAutoLoginCookie(array $cookieParts, Request $request);
|
||||
|
||||
protected function onLoginFail(Request $request)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* This is called after a user has been logged in successfully, and has
|
||||
* requested remember-me capabilities. The implementation usually sets a
|
||||
@ -190,7 +222,7 @@ abstract class RememberMeServices implements RememberMeServicesInterface, Logout
|
||||
*/
|
||||
abstract protected function onLoginSuccess(Request $request, Response $response, TokenInterface $token);
|
||||
|
||||
protected function getUserProvider($class)
|
||||
protected final function getUserProvider($class)
|
||||
{
|
||||
foreach ($this->userProviders as $provider) {
|
||||
if ($provider->supportsClass($class)) {
|
||||
@ -198,7 +230,7 @@ abstract class RememberMeServices implements RememberMeServicesInterface, Logout
|
||||
}
|
||||
}
|
||||
|
||||
throw new \RuntimeException(sprintf('There is no user provider that supports class "%s".', $class));
|
||||
throw new UnsupportedUserException(sprintf('There is no user provider that supports class "%s".', $class));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -226,16 +258,16 @@ abstract class RememberMeServices implements RememberMeServicesInterface, Logout
|
||||
/**
|
||||
* Deletes the remember-me cookie
|
||||
*
|
||||
* @param Response $response
|
||||
* @param Request $request
|
||||
* @return void
|
||||
*/
|
||||
protected function cancelCookie(Response $response)
|
||||
protected function cancelCookie(Request $request)
|
||||
{
|
||||
if (null !== $this->logger) {
|
||||
$this->logger->debug(sprintf('Clearing remember-me cookie "%s"', $this->options['name']));
|
||||
}
|
||||
|
||||
$response->headers->clearCookie($this->options['name'], $this->options['path'], $this->options['domain']);
|
||||
$request->attributes->set(self::COOKIE_ATTR_NAME, new Cookie($this->options['name'], null, 1, $this->options['path'], $this->options['domain']));
|
||||
}
|
||||
|
||||
/**
|
@ -28,7 +28,7 @@ use Symfony\Component\Security\Core\Authentication\Token\RememberMeToken;
|
||||
*
|
||||
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
|
||||
*/
|
||||
class PersistentTokenBasedRememberMeServices extends RememberMeServices
|
||||
class PersistentTokenBasedRememberMeServices extends AbstractRememberMeServices
|
||||
{
|
||||
private $tokenProvider;
|
||||
|
||||
@ -80,9 +80,22 @@ class PersistentTokenBasedRememberMeServices extends RememberMeServices
|
||||
throw new AuthenticationException('The cookie has expired.');
|
||||
}
|
||||
|
||||
$user = $this->getUserProvider($persistentToken->getClass())->loadUserByUsername($persistentToken->getUsername());
|
||||
$series = $persistentToken->getSeries();
|
||||
$tokenValue = $this->generateRandomValue();
|
||||
$this->tokenProvider->updateToken($series, $tokenValue, new \DateTime());
|
||||
$request->attributes->set(self::COOKIE_ATTR_NAME,
|
||||
new Cookie(
|
||||
$this->options['name'],
|
||||
$this->encodeCookie(array($series, $tokenValue)),
|
||||
time() + $this->options['lifetime'],
|
||||
$this->options['path'],
|
||||
$this->options['domain'],
|
||||
$this->options['secure'],
|
||||
$this->options['httponly']
|
||||
)
|
||||
);
|
||||
|
||||
return new RememberMeToken($user, $this->providerKey, $this->key, $persistentToken);
|
||||
return $this->getUserProvider($persistentToken->getClass())->loadUserByUsername($persistentToken->getUsername());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -90,34 +103,23 @@ class PersistentTokenBasedRememberMeServices extends RememberMeServices
|
||||
*/
|
||||
protected function onLoginSuccess(Request $request, Response $response, TokenInterface $token)
|
||||
{
|
||||
if ($token instanceof RememberMeToken) {
|
||||
if (null === $persistentToken = $token->getPersistentToken()) {
|
||||
throw new \RuntimeException('RememberMeToken must contain a PersistentTokenInterface implementation when used as login.');
|
||||
}
|
||||
$series = $this->generateRandomValue();
|
||||
$tokenValue = $this->generateRandomValue();
|
||||
|
||||
$series = $persistentToken->getSeries();
|
||||
$tokenValue = $this->generateRandomValue();
|
||||
|
||||
$this->tokenProvider->updateToken($series, $tokenValue, new \DateTime());
|
||||
} else {
|
||||
$series = $this->generateRandomValue();
|
||||
$tokenValue = $this->generateRandomValue();
|
||||
|
||||
$this->tokenProvider->createNewToken(
|
||||
new PersistentToken(
|
||||
get_class($user = $token->getUser()),
|
||||
$user->getUsername(),
|
||||
$series,
|
||||
$tokenValue,
|
||||
new \DateTime()
|
||||
)
|
||||
);
|
||||
}
|
||||
$this->tokenProvider->createNewToken(
|
||||
new PersistentToken(
|
||||
get_class($user = $token->getUser()),
|
||||
$user->getUsername(),
|
||||
$series,
|
||||
$tokenValue,
|
||||
new \DateTime()
|
||||
)
|
||||
);
|
||||
|
||||
$response->headers->setCookie(
|
||||
new Cookie(
|
||||
$this->options['name'],
|
||||
$this->generateCookieValue($series, $tokenValue),
|
||||
$this->encodeCookie(array($series, $tokenValue)),
|
||||
time() + $this->options['lifetime'],
|
||||
$this->options['path'],
|
||||
$this->options['domain'],
|
||||
@ -127,18 +129,6 @@ class PersistentTokenBasedRememberMeServices extends RememberMeServices
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the value for the cookie
|
||||
*
|
||||
* @param string $series
|
||||
* @param string $tokenValue
|
||||
* @return string
|
||||
*/
|
||||
protected function generateCookieValue($series, $tokenValue)
|
||||
{
|
||||
return $this->encodeCookie(array($series, $tokenValue));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a cryptographically strong random value
|
||||
*
|
||||
@ -147,7 +137,7 @@ class PersistentTokenBasedRememberMeServices extends RememberMeServices
|
||||
protected function generateRandomValue()
|
||||
{
|
||||
if (function_exists('openssl_random_pseudo_bytes')) {
|
||||
$bytes = openssl_random_pseudo_bytes(32, $strong);
|
||||
$bytes = openssl_random_pseudo_bytes(64, $strong);
|
||||
|
||||
if (true === $strong && false !== $bytes) {
|
||||
return base64_encode($bytes);
|
||||
@ -158,6 +148,6 @@ class PersistentTokenBasedRememberMeServices extends RememberMeServices
|
||||
$this->logger->warn('Could not produce a cryptographically strong random value. Please install/update the OpenSSL extension.');
|
||||
}
|
||||
|
||||
return base64_encode(hash('sha256', uniqid(mt_rand(), true), true));
|
||||
return base64_encode(hash('sha512', uniqid(mt_rand(), true), true));
|
||||
}
|
||||
}
|
||||
}
|
@ -17,50 +17,67 @@ use Symfony\Component\HttpFoundation\Request;
|
||||
/**
|
||||
* Interface that needs to be implemented by classes which provide remember-me
|
||||
* capabilities.
|
||||
*
|
||||
*
|
||||
* We provide two implementations out-of-the-box:
|
||||
* - TokenBasedRememberMeServices (does not require a TokenProvider)
|
||||
* - PersistentTokenBasedRememberMeServices (requires a TokenProvider)
|
||||
*
|
||||
*
|
||||
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
|
||||
*/
|
||||
interface RememberMeServicesInterface
|
||||
{
|
||||
/**
|
||||
* This method will be called whenever the SecurityContext does not contain
|
||||
* an TokenInterface object and the framework wishes to provide an implementation
|
||||
* with an opportunity to authenticate the request using remember-me capabilities.
|
||||
*
|
||||
* This attribute name can be used by the implementation if it needs to set
|
||||
* a cookie on the Request when there is no actual Response, yet.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const COOKIE_ATTR_NAME = '_security_remember_me_cookie';
|
||||
|
||||
/**
|
||||
* This method will be called whenever the SecurityContext does not contain
|
||||
* an TokenInterface object and the framework wishes to provide an implementation
|
||||
* with an opportunity to authenticate the request using remember-me capabilities.
|
||||
*
|
||||
* No attempt whatsoever is made to determine whether the browser has requested
|
||||
* remember-me services or presented a valid cookie. Any and all such determinations
|
||||
* are left to the implementation of this method.
|
||||
*
|
||||
* are left to the implementation of this method.
|
||||
*
|
||||
* If a browser has presented an unauthorised cookie for whatever reason,
|
||||
* make sure to throw an AuthenticationException as this will consequentially
|
||||
* make sure to throw an AuthenticationException as this will consequentially
|
||||
* result in a call to loginFail() and therefore an invalidation of the cookie.
|
||||
*
|
||||
*
|
||||
* @param Request $request
|
||||
* @return TokenInterface
|
||||
*/
|
||||
function autoLogin(Request $request);
|
||||
|
||||
/**
|
||||
* Called whenever an authentication attempt was made, but the credentials
|
||||
* supplied by the user were missing or otherwise invalid.
|
||||
*
|
||||
* This method needs to take care of invalidating the cookie.
|
||||
*/
|
||||
function loginFail(Request $request, Response $response);
|
||||
|
||||
/**
|
||||
* Called whenever authentication attempt is successful (e.g. a form login).
|
||||
*
|
||||
* An implementation may always set a remember-me cookie in the Response,
|
||||
* although this is not recommended.
|
||||
*
|
||||
* Instead, implementations should typically look for a request parameter
|
||||
* Called whenever an interactive authentication attempt was made, but the
|
||||
* credentials supplied by the user were missing or otherwise invalid.
|
||||
*
|
||||
* This method needs to take care of invalidating the cookie.
|
||||
*
|
||||
* @param Request $request
|
||||
* @return void
|
||||
*/
|
||||
function loginFail(Request $request);
|
||||
|
||||
/**
|
||||
* Called whenever an interactive authentication attempt is successful
|
||||
* (e.g. a form login).
|
||||
*
|
||||
* An implementation may always set a remember-me cookie in the Response,
|
||||
* although this is not recommended.
|
||||
*
|
||||
* Instead, implementations should typically look for a request parameter
|
||||
* (such as a HTTP POST parameter) that indicates the browser has explicitly
|
||||
* requested for the authentication to be remembered.
|
||||
*
|
||||
* @param Request $request
|
||||
* @param Response $response
|
||||
* @param TokenInterface $token
|
||||
* @return void
|
||||
*/
|
||||
function loginSuccess(Request $request, Response $response, TokenInterface $token);
|
||||
}
|
@ -25,7 +25,7 @@ use Symfony\Component\Security\Core\User\UserInterface;
|
||||
*
|
||||
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
|
||||
*/
|
||||
class TokenBasedRememberMeServices extends RememberMeServices
|
||||
class TokenBasedRememberMeServices extends AbstractRememberMeServices
|
||||
{
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
@ -62,7 +62,7 @@ class TokenBasedRememberMeServices extends RememberMeServices
|
||||
throw new AuthenticationException('The cookie has expired.');
|
||||
}
|
||||
|
||||
return new RememberMeToken($user, $this->providerKey, $this->key);
|
||||
return $user;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -95,10 +95,6 @@ class TokenBasedRememberMeServices extends RememberMeServices
|
||||
*/
|
||||
protected function onLoginSuccess(Request $request, Response $response, TokenInterface $token)
|
||||
{
|
||||
if ($token instanceof RememberMeToken) {
|
||||
return;
|
||||
}
|
||||
|
||||
$user = $token->getUser();
|
||||
$expires = time() + $this->options['lifetime'];
|
||||
$value = $this->generateCookieValue(get_class($user), $user->getUsername(), $expires, $user->getPassword());
|
||||
@ -150,6 +146,6 @@ class TokenBasedRememberMeServices extends RememberMeServices
|
||||
*/
|
||||
protected function generateCookieHash($class, $username, $expires, $password)
|
||||
{
|
||||
return hash('sha256', $class.$username.$expires.$password.$this->key);
|
||||
return hash('sha256', $class.$username.$expires.$password.$this->getKey());
|
||||
}
|
||||
}
|
||||
|
@ -60,13 +60,6 @@ class RememberMeTokenTest extends \PHPUnit_Framework_TestCase
|
||||
);
|
||||
}
|
||||
|
||||
public function testPersistentToken()
|
||||
{
|
||||
$token = new RememberMeToken($this->getUser(), 'fookey', 'foo', $persistentToken = $this->getMock('Symfony\Component\Security\Core\Authentication\RememberMe\PersistentTokenInterface'));
|
||||
|
||||
$this->assertSame($persistentToken, $token->getPersistentToken());
|
||||
}
|
||||
|
||||
protected function getUser($roles = array('ROLE_FOO'))
|
||||
{
|
||||
$user = $this->getMock('Symfony\Component\Security\Core\User\UserInterface');
|
||||
|
@ -21,11 +21,6 @@ class RememberMeListenerTest extends \PHPUnit_Framework_TestCase
|
||||
->method('connect')
|
||||
->with($this->equalTo('core.security'))
|
||||
;
|
||||
$dispatcher
|
||||
->expects($this->at(1))
|
||||
->method('connect')
|
||||
->with($this->equalTo('core.response'))
|
||||
;
|
||||
|
||||
$listener->register($dispatcher);
|
||||
}
|
||||
@ -45,9 +40,7 @@ class RememberMeListenerTest extends \PHPUnit_Framework_TestCase
|
||||
->method('setToken')
|
||||
;
|
||||
|
||||
$this->assertNull($this->getLastState($listener));
|
||||
$this->assertNull($listener->checkCookies($this->getEvent()));
|
||||
$this->assertNull($this->getLastState($listener));
|
||||
}
|
||||
|
||||
public function testCheckCookiesDoesNothingWhenNoCookieIsSet()
|
||||
@ -74,105 +67,7 @@ class RememberMeListenerTest extends \PHPUnit_Framework_TestCase
|
||||
->will($this->returnValue(new Request()))
|
||||
;
|
||||
|
||||
$this->assertNull($this->getLastState($listener));
|
||||
$this->assertNull($listener->checkCookies($event));
|
||||
$this->assertNull($this->getLastState($listener));
|
||||
}
|
||||
|
||||
public function testCheckCookiesIgnoresAuthenticationExceptionThrownByTheRememberMeServicesImplementation()
|
||||
{
|
||||
list($listener, $context, $service,,) = $this->getListener();
|
||||
|
||||
$context
|
||||
->expects($this->once())
|
||||
->method('getToken')
|
||||
->will($this->returnValue(null))
|
||||
;
|
||||
|
||||
$exception = new AuthenticationException('cookie invalid.');
|
||||
$service
|
||||
->expects($this->once())
|
||||
->method('autoLogin')
|
||||
->will($this->throwException($exception))
|
||||
;
|
||||
|
||||
$event = $this->getEvent();
|
||||
$event
|
||||
->expects($this->once())
|
||||
->method('get')
|
||||
->with('request')
|
||||
->will($this->returnValue(new Request()))
|
||||
;
|
||||
|
||||
$this->assertNull($this->getLastState($listener));
|
||||
$this->assertNull($listener->checkCookies($event));
|
||||
$this->assertSame($exception, $this->getLastState($listener));
|
||||
}
|
||||
|
||||
public function testCheckCookiesThrowsCookieTheftExceptionIfThrownByTheRememberMeServicesImplementation()
|
||||
{
|
||||
list($listener, $context, $service,,) = $this->getListener();
|
||||
|
||||
$context
|
||||
->expects($this->once())
|
||||
->method('getToken')
|
||||
->will($this->returnValue(null))
|
||||
;
|
||||
|
||||
$exception = new CookieTheftException('cookie was stolen.');
|
||||
$service
|
||||
->expects($this->once())
|
||||
->method('autoLogin')
|
||||
->will($this->throwException($exception))
|
||||
;
|
||||
|
||||
$event = $this->getEvent();
|
||||
$event
|
||||
->expects($this->once())
|
||||
->method('get')
|
||||
->with('request')
|
||||
->will($this->returnValue(new Request()))
|
||||
;
|
||||
|
||||
try {
|
||||
$listener->checkCookies($event);
|
||||
}
|
||||
catch (CookieTheftException $theft) {
|
||||
$this->assertSame($theft, $this->getLastState($listener));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->fail('Expected CookieTheftException was not thrown.');
|
||||
}
|
||||
|
||||
public function testCheckCookiesAuthenticationManagerDoesNotChangeListenerStateWhenTokenIsNotSupported()
|
||||
{
|
||||
list($listener, $context, $service, $manager,) = $this->getListener();
|
||||
|
||||
$context
|
||||
->expects($this->once())
|
||||
->method('getToken')
|
||||
->will($this->returnValue(null))
|
||||
;
|
||||
|
||||
$service
|
||||
->expects($this->once())
|
||||
->method('autoLogin')
|
||||
->will($this->returnValue($this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface')))
|
||||
;
|
||||
|
||||
$event = $this->getEvent();
|
||||
$event
|
||||
->expects($this->once())
|
||||
->method('get')
|
||||
->with('request')
|
||||
->will($this->returnValue(new Request()))
|
||||
;
|
||||
|
||||
$this->assertNull($this->getLastState($listener));
|
||||
$this->assertNull($listener->checkCookies($event));
|
||||
$this->assertNull($this->getLastState($listener));
|
||||
}
|
||||
|
||||
public function testCheckCookiesIgnoresAuthenticationExceptionThrownByAuthenticationManagerImplementation()
|
||||
@ -191,6 +86,11 @@ class RememberMeListenerTest extends \PHPUnit_Framework_TestCase
|
||||
->will($this->returnValue($this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface')))
|
||||
;
|
||||
|
||||
$service
|
||||
->expects($this->once())
|
||||
->method('loginFail')
|
||||
;
|
||||
|
||||
$exception = new AuthenticationException('Authentication failed.');
|
||||
$manager
|
||||
->expects($this->once())
|
||||
@ -206,9 +106,7 @@ class RememberMeListenerTest extends \PHPUnit_Framework_TestCase
|
||||
->will($this->returnValue(new Request()))
|
||||
;
|
||||
|
||||
$this->assertNull($this->getLastState($listener));
|
||||
$this->assertNull($listener->checkCookies($event));
|
||||
$this->assertSame($exception, $this->getLastState($listener));
|
||||
$listener->checkCookies($event);
|
||||
}
|
||||
|
||||
public function testCheckCookies()
|
||||
@ -248,111 +146,7 @@ class RememberMeListenerTest extends \PHPUnit_Framework_TestCase
|
||||
->will($this->returnValue(new Request()))
|
||||
;
|
||||
|
||||
$this->assertNull($this->getLastState($listener));
|
||||
$this->assertNull($listener->checkCookies($event));
|
||||
$this->assertSame($token, $this->getLastState($listener));
|
||||
}
|
||||
|
||||
public function testUpdateCookiesIgnoresAnythingButMasterRequests()
|
||||
{
|
||||
list($listener,,,,) = $this->getListener();
|
||||
|
||||
$event = $this->getEvent();
|
||||
$event
|
||||
->expects($this->once())
|
||||
->method('get')
|
||||
->with($this->equalTo('request_type'))
|
||||
->will($this->returnValue('foo'))
|
||||
;
|
||||
|
||||
$response = $this->getMock('Symfony\Component\HttpFoundation\Response');
|
||||
|
||||
$this->assertSame($response, $listener->updateCookies($event, $response));
|
||||
}
|
||||
|
||||
public function testUpdateCookiesCallsLoginSuccessOnRememberMeServicesImplementationWhenAuthenticationWasSuccessful()
|
||||
{
|
||||
list($listener,, $service,,) = $this->getListener();
|
||||
|
||||
$request = new Request();
|
||||
$response = new Response();
|
||||
|
||||
$token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface');
|
||||
$this->setLastState($listener, $token);
|
||||
|
||||
$event = $this->getEvent();
|
||||
$event
|
||||
->expects($this->at(0))
|
||||
->method('get')
|
||||
->with('request_type')
|
||||
->will($this->returnValue(HttpKernelInterface::MASTER_REQUEST))
|
||||
;
|
||||
$event
|
||||
->expects($this->at(1))
|
||||
->method('get')
|
||||
->with('request')
|
||||
->will($this->returnValue($request))
|
||||
;
|
||||
|
||||
$service
|
||||
->expects($this->once())
|
||||
->method('loginSuccess')
|
||||
->with($this->equalTo($request), $this->equalTo($response), $this->equalTo($token))
|
||||
->will($this->returnValue(null))
|
||||
;
|
||||
|
||||
$this->assertSame($response, $listener->updateCookies($event, $response));
|
||||
}
|
||||
|
||||
public function testUpdateCookiesCallsLoginFailOnRememberMeServicesImplementationWhenAuthenticationWasNotSuccessful()
|
||||
{
|
||||
list($listener,, $service,,) = $this->getListener();
|
||||
|
||||
$request = new Request();
|
||||
$response = new Response();
|
||||
|
||||
$exception = new AuthenticationException('foo');
|
||||
$this->setLastState($listener, $exception);
|
||||
|
||||
$event = $this->getEvent();
|
||||
$event
|
||||
->expects($this->at(0))
|
||||
->method('get')
|
||||
->with('request_type')
|
||||
->will($this->returnValue(HttpKernelInterface::MASTER_REQUEST))
|
||||
;
|
||||
$event
|
||||
->expects($this->at(1))
|
||||
->method('get')
|
||||
->with('request')
|
||||
->will($this->returnValue($request))
|
||||
;
|
||||
|
||||
$service
|
||||
->expects($this->once())
|
||||
->method('loginFail')
|
||||
->with($this->equalTo($request), $this->equalTo($response))
|
||||
->will($this->returnValue(null))
|
||||
;
|
||||
|
||||
$this->assertSame($response, $listener->updateCookies($event, $response));
|
||||
}
|
||||
|
||||
protected function setLastState($listener, $state)
|
||||
{
|
||||
$r = new \ReflectionObject($listener);
|
||||
$p = $r->getProperty('lastState');
|
||||
$p->setAccessible(true);
|
||||
$p->setValue($listener, $state);
|
||||
}
|
||||
|
||||
protected function getLastState($listener)
|
||||
{
|
||||
$r = new \ReflectionObject($listener);
|
||||
$p = $r->getProperty('lastState');
|
||||
$p->setAccessible(true);
|
||||
|
||||
return $p->getValue($listener);
|
||||
$listener->checkCookies($event);
|
||||
}
|
||||
|
||||
protected function getEvent()
|
||||
|
@ -2,10 +2,11 @@
|
||||
|
||||
namespace Symfony\Tests\Component\Security\Http\RememberMe;
|
||||
|
||||
use Symfony\Component\Security\Http\RememberMe\RememberMeServicesInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class RememberMeServicesTest extends \PHPUnit_Framework_TestCase
|
||||
class AbstractRememberMeServicesTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testGetRememberMeParameter()
|
||||
{
|
||||
@ -14,6 +15,12 @@ class RememberMeServicesTest extends \PHPUnit_Framework_TestCase
|
||||
$this->assertEquals('foo', $service->getRememberMeParameter());
|
||||
}
|
||||
|
||||
public function testGetKey()
|
||||
{
|
||||
$service = $this->getService();
|
||||
$this->assertEquals('fookey', $service->getKey());
|
||||
}
|
||||
|
||||
public function testAutoLoginReturnsNullWhenNoCookie()
|
||||
{
|
||||
$service = $this->getService(null, array('name' => 'foo'));
|
||||
@ -23,9 +30,8 @@ class RememberMeServicesTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
/**
|
||||
* @expectedException \RuntimeException
|
||||
* @expectedMessage processAutoLoginCookie() must return a TokenInterface implementation.
|
||||
*/
|
||||
public function testAutoLoginThrowsExceptionWhenImplementationDoesNotReturnTokenInterface()
|
||||
public function testAutoLoginThrowsExceptionWhenImplementationDoesNotReturnUserInterface()
|
||||
{
|
||||
$service = $this->getService(null, array('name' => 'foo'));
|
||||
$request = new Request;
|
||||
@ -46,17 +52,24 @@ class RememberMeServicesTest extends \PHPUnit_Framework_TestCase
|
||||
$request = new Request();
|
||||
$request->cookies->set('foo', 'foo');
|
||||
|
||||
$token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface');
|
||||
$user = $this->getMock('Symfony\Component\Security\Core\User\UserInterface');
|
||||
$user
|
||||
->expects($this->once())
|
||||
->method('getRoles')
|
||||
->will($this->returnValue(array()))
|
||||
;
|
||||
|
||||
$service
|
||||
->expects($this->once())
|
||||
->method('processAutoLoginCookie')
|
||||
->will($this->returnValue($token))
|
||||
->will($this->returnValue($user))
|
||||
;
|
||||
|
||||
$returnedToken = $service->autoLogin($request);
|
||||
|
||||
$this->assertSame($token, $returnedToken);
|
||||
$this->assertSame($user, $returnedToken->getUser());
|
||||
$this->assertSame('fookey', $returnedToken->getKey());
|
||||
$this->assertSame('fookey', $returnedToken->getProviderKey());
|
||||
}
|
||||
|
||||
public function testLogout()
|
||||
@ -66,24 +79,19 @@ class RememberMeServicesTest extends \PHPUnit_Framework_TestCase
|
||||
$response = new Response();
|
||||
$token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface');
|
||||
|
||||
$this->assertFalse($response->headers->hasCookie('foo'));
|
||||
|
||||
$service->logout($request, $response, $token);
|
||||
|
||||
$this->assertTrue($response->headers->getCookie('foo')->isCleared());
|
||||
$this->assertTrue($request->attributes->get(RememberMeServicesInterface::COOKIE_ATTR_NAME)->isCleared());
|
||||
}
|
||||
|
||||
public function testLoginFail()
|
||||
{
|
||||
$service = $this->getService(null, array('name' => 'foo', 'path' => null, 'domain' => null));
|
||||
$request = new Request();
|
||||
$response = new Response();
|
||||
|
||||
$this->assertFalse($response->headers->hasCookie('foo'));
|
||||
$service->loginFail($request);
|
||||
|
||||
$service->loginFail($request, $response);
|
||||
|
||||
$this->assertTrue($response->headers->getCookie('foo')->isCleared());
|
||||
$this->assertTrue($request->attributes->get(RememberMeServicesInterface::COOKIE_ATTR_NAME)->isCleared());
|
||||
}
|
||||
|
||||
public function testLoginSuccessIsNotProcessedWhenTokenDoesNotContainUserInterfaceImplementation()
|
||||
@ -192,34 +200,13 @@ class RememberMeServicesTest extends \PHPUnit_Framework_TestCase
|
||||
);
|
||||
}
|
||||
|
||||
public function testLoginSuccessRenewsRememberMeCookie()
|
||||
{
|
||||
$service = $this->getService();
|
||||
|
||||
$token = $this->getMock(
|
||||
'Symfony\Component\Security\Core\Authentication\Token\RememberMeToken',
|
||||
array(),
|
||||
array(),
|
||||
'NonFunctionalRememberMeTokenMockClass',
|
||||
false
|
||||
);
|
||||
|
||||
$service
|
||||
->expects($this->once())
|
||||
->method('onLoginSuccess')
|
||||
->will($this->returnValue(null))
|
||||
;
|
||||
|
||||
$service->loginSuccess(new Request(), new Response(), $token);
|
||||
}
|
||||
|
||||
protected function getService($userProvider = null, $options = array(), $logger = null)
|
||||
{
|
||||
if (null === $userProvider) {
|
||||
$userProvider = $this->getProvider();
|
||||
}
|
||||
|
||||
return $this->getMockForAbstractClass('Symfony\Component\Security\Http\RememberMe\RememberMeServices', array(
|
||||
return $this->getMockForAbstractClass('Symfony\Component\Security\Http\RememberMe\AbstractRememberMeServices', array(
|
||||
array($userProvider), 'fookey', 'fookey', $options, $logger
|
||||
));
|
||||
}
|
@ -2,6 +2,8 @@
|
||||
|
||||
namespace Symfony\Tests\Component\Security\Http\RememberMe;
|
||||
|
||||
use Symfony\Component\Security\Http\RememberMe\RememberMeServicesInterface;
|
||||
|
||||
use Symfony\Component\Security\Core\Authentication\Token\RememberMeToken;
|
||||
use Symfony\Component\HttpFoundation\HeaderBag;
|
||||
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
|
||||
@ -21,26 +23,20 @@ class PersistentTokenBasedRememberMeServicesTest extends \PHPUnit_Framework_Test
|
||||
$this->assertNull($service->autoLogin(new Request()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Symfony\Component\Security\Core\Exception\AuthenticationException
|
||||
* @expectedMessage The cookie is invalid.
|
||||
*/
|
||||
public function testAutoLoginThrowsExceptionOnInvalidCookie()
|
||||
{
|
||||
$service = $this->getService(null, array('name' => 'foo', 'always_remember_me' => false, 'remember_me_parameter' => 'foo'));
|
||||
$service = $this->getService(null, array('name' => 'foo', 'path' => null, 'domain' => null, 'always_remember_me' => false, 'remember_me_parameter' => 'foo'));
|
||||
$request = new Request;
|
||||
$request->request->set('foo', 'true');
|
||||
$request->cookies->set('foo', 'foo');
|
||||
|
||||
$service->autoLogin($request);
|
||||
$this->assertNull($service->autoLogin($request));
|
||||
$this->assertTrue($request->attributes->get(RememberMeServicesInterface::COOKIE_ATTR_NAME)->isCleared());
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Symfony\Component\Security\Core\Exception\TokenNotFoundException
|
||||
*/
|
||||
public function testAutoLoginThrowsExceptionOnNonExistentToken()
|
||||
{
|
||||
$service = $this->getService(null, array('name' => 'foo', 'always_remember_me' => false, 'remember_me_parameter' => 'foo'));
|
||||
$service = $this->getService(null, array('name' => 'foo', 'path' => null, 'domain' => null, 'always_remember_me' => false, 'remember_me_parameter' => 'foo'));
|
||||
$request = new Request;
|
||||
$request->request->set('foo', 'true');
|
||||
$request->cookies->set('foo', $this->encodeCookie(array(
|
||||
@ -56,16 +52,14 @@ class PersistentTokenBasedRememberMeServicesTest extends \PHPUnit_Framework_Test
|
||||
;
|
||||
$service->setTokenProvider($tokenProvider);
|
||||
|
||||
$service->autoLogin($request);
|
||||
$this->assertNull($service->autoLogin($request));
|
||||
$this->assertTrue($request->attributes->get(RememberMeServicesInterface::COOKIE_ATTR_NAME)->isCleared());
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Symfony\Component\Security\Core\Exception\UsernameNotFoundException
|
||||
*/
|
||||
public function testAutoLoginThrowsExceptionOnNonExistentUser()
|
||||
public function testAutoLoginReturnsNullOnNonExistentUser()
|
||||
{
|
||||
$userProvider = $this->getProvider();
|
||||
$service = $this->getService($userProvider, array('name' => 'foo', 'always_remember_me' => true, 'lifetime' => 3600));
|
||||
$service = $this->getService($userProvider, array('name' => 'foo', 'path' => null, 'domain' => null, 'always_remember_me' => true, 'lifetime' => 3600, 'secure' => false, 'httponly' => false));
|
||||
$request = new Request;
|
||||
$request->cookies->set('foo', $this->encodeCookie(array('fooseries', 'foovalue')));
|
||||
|
||||
@ -83,13 +77,14 @@ class PersistentTokenBasedRememberMeServicesTest extends \PHPUnit_Framework_Test
|
||||
->will($this->throwException(new UsernameNotFoundException('user not found')))
|
||||
;
|
||||
|
||||
$service->autoLogin($request);
|
||||
$this->assertNull($service->autoLogin($request));
|
||||
$this->assertTrue($request->attributes->has(RememberMeServicesInterface::COOKIE_ATTR_NAME));
|
||||
}
|
||||
|
||||
public function testAutoLoginThrowsExceptionOnStolenCookieAndRemovesItFromThePersistentBackend()
|
||||
{
|
||||
$userProvider = $this->getProvider();
|
||||
$service = $this->getService($userProvider, array('name' => 'foo', 'always_remember_me' => true));
|
||||
$service = $this->getService($userProvider, array('name' => 'foo', 'path' => null, 'domain' => null, 'always_remember_me' => true));
|
||||
$request = new Request;
|
||||
$request->cookies->set('foo', $this->encodeCookie(array('fooseries', 'foovalue')));
|
||||
|
||||
@ -111,20 +106,15 @@ class PersistentTokenBasedRememberMeServicesTest extends \PHPUnit_Framework_Test
|
||||
|
||||
try {
|
||||
$service->autoLogin($request);
|
||||
} catch (CookieTheftException $theft) {
|
||||
return;
|
||||
}
|
||||
$this->fail('Expected CookieTheftException was not thrown.');
|
||||
} catch (CookieTheftException $theft) { }
|
||||
|
||||
$this->fail('Expected CookieTheftException was not thrown.');
|
||||
$this->assertTrue($request->attributes->has(RememberMeServicesInterface::COOKIE_ATTR_NAME));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Symfony\Component\Security\Core\Exception\AuthenticationException
|
||||
* @expectedMessage The cookie has expired.
|
||||
*/
|
||||
public function testAutoLoginDoesNotAcceptAnExpiredCookie()
|
||||
{
|
||||
$service = $this->getService(null, array('name' => 'foo', 'always_remember_me' => true, 'lifetime' => 3600));
|
||||
$service = $this->getService(null, array('name' => 'foo', 'path' => null, 'domain' => null, 'always_remember_me' => true, 'lifetime' => 3600));
|
||||
$request = new Request;
|
||||
$request->cookies->set('foo', $this->encodeCookie(array('fooseries', 'foovalue')));
|
||||
|
||||
@ -133,11 +123,12 @@ class PersistentTokenBasedRememberMeServicesTest extends \PHPUnit_Framework_Test
|
||||
->expects($this->once())
|
||||
->method('loadTokenBySeries')
|
||||
->with($this->equalTo('fooseries'))
|
||||
->will($this->returnValue(new PersistentToken('fooclass', 'username', 'fooseries', 'newFooValue', new \DateTime('yesterday'))))
|
||||
->will($this->returnValue(new PersistentToken('fooclass', 'username', 'fooseries', 'foovalue', new \DateTime('yesterday'))))
|
||||
;
|
||||
$service->setTokenProvider($tokenProvider);
|
||||
|
||||
$service->autoLogin($request);
|
||||
$this->assertNull($service->autoLogin($request));
|
||||
$this->assertTrue($request->attributes->has(RememberMeServicesInterface::COOKIE_ATTR_NAME));
|
||||
}
|
||||
|
||||
public function testAutoLogin()
|
||||
@ -157,7 +148,7 @@ class PersistentTokenBasedRememberMeServicesTest extends \PHPUnit_Framework_Test
|
||||
->will($this->returnValue($user))
|
||||
;
|
||||
|
||||
$service = $this->getService($userProvider, array('name' => 'foo', 'always_remember_me' => true, 'lifetime' => 3600));
|
||||
$service = $this->getService($userProvider, array('name' => 'foo', 'path' => null, 'domain' => null, 'secure' => false, 'httponly' => false, 'always_remember_me' => true, 'lifetime' => 3600));
|
||||
$request = new Request;
|
||||
$request->cookies->set('foo', $this->encodeCookie(array('fooseries', 'foovalue')));
|
||||
|
||||
@ -173,9 +164,9 @@ class PersistentTokenBasedRememberMeServicesTest extends \PHPUnit_Framework_Test
|
||||
$returnedToken = $service->autoLogin($request);
|
||||
|
||||
$this->assertInstanceOf('Symfony\Component\Security\Core\Authentication\Token\RememberMeToken', $returnedToken);
|
||||
$this->assertInstanceOf('Symfony\Component\Security\Core\Authentication\RememberMe\PersistentTokenInterface', $returnedToken->getPersistentToken());
|
||||
$this->assertSame($user, $returnedToken->getUser());
|
||||
$this->assertEquals('fookey', $returnedToken->getKey());
|
||||
$this->assertTrue($request->attributes->has(RememberMeServicesInterface::COOKIE_ATTR_NAME));
|
||||
}
|
||||
|
||||
public function testLogout()
|
||||
@ -195,11 +186,9 @@ class PersistentTokenBasedRememberMeServicesTest extends \PHPUnit_Framework_Test
|
||||
;
|
||||
$service->setTokenProvider($tokenProvider);
|
||||
|
||||
$this->assertFalse($response->headers->hasCookie('foo'));
|
||||
|
||||
$service->logout($request, $response, $token);
|
||||
|
||||
$cookie = $response->headers->getCookie('foo');
|
||||
$cookie = $request->attributes->get(RememberMeServicesInterface::COOKIE_ATTR_NAME);
|
||||
$this->assertTrue($cookie->isCleared());
|
||||
$this->assertEquals('/foo', $cookie->getPath());
|
||||
$this->assertEquals('foodomain.foo', $cookie->getDomain());
|
||||
@ -219,10 +208,9 @@ class PersistentTokenBasedRememberMeServicesTest extends \PHPUnit_Framework_Test
|
||||
;
|
||||
$service->setTokenProvider($tokenProvider);
|
||||
|
||||
$this->assertFalse($response->headers->hasCookie('foo'));
|
||||
$service->logout($request, $response, $token);
|
||||
|
||||
$cookie = $response->headers->getCookie('foo');
|
||||
$cookie = $request->attributes->get(RememberMeServicesInterface::COOKIE_ATTR_NAME);
|
||||
$this->assertTrue($cookie->isCleared());
|
||||
$this->assertNull($cookie->getPath());
|
||||
$this->assertNull($cookie->getDomain());
|
||||
@ -243,93 +231,19 @@ class PersistentTokenBasedRememberMeServicesTest extends \PHPUnit_Framework_Test
|
||||
;
|
||||
$service->setTokenProvider($tokenProvider);
|
||||
|
||||
$this->assertFalse($response->headers->hasCookie('foo'));
|
||||
|
||||
$service->logout($request, $response, $token);
|
||||
|
||||
$this->assertTrue($response->headers->getCookie('foo')->isCleared());
|
||||
$this->assertTrue($request->attributes->get(RememberMeServicesInterface::COOKIE_ATTR_NAME)->isCleared());
|
||||
}
|
||||
|
||||
public function testLoginFail()
|
||||
{
|
||||
$service = $this->getService(null, array('name' => 'foo', 'path' => null, 'domain' => null));
|
||||
$request = new Request();
|
||||
$response = new Response();
|
||||
|
||||
$this->assertFalse($response->headers->hasCookie('foo'));
|
||||
|
||||
$service->loginFail($request, $response);
|
||||
|
||||
$this->assertTrue($response->headers->getCookie('foo')->isCleared());
|
||||
}
|
||||
|
||||
public function testLoginSuccessRenewsRememberMeTokenWhenUsedForLogin()
|
||||
{
|
||||
$service = $this->getService(null, array('name' => 'foo', 'domain' => 'myfoodomain.foo', 'path' => '/foo/path', 'secure' => true, 'httponly' => true, 'lifetime' => 3600));
|
||||
$request = new Request;
|
||||
$response = new Response;
|
||||
|
||||
$user = $this->getMock('Symfony\Component\Security\Core\User\UserInterface');
|
||||
$user
|
||||
->expects($this->once())
|
||||
->method('getRoles')
|
||||
->will($this->returnValue(array('ROLE_FOO')))
|
||||
;
|
||||
|
||||
$token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\RememberMeToken', array(), array($user, 'fookey', 'fookey'));
|
||||
$token
|
||||
->expects($this->once())
|
||||
->method('getPersistentToken')
|
||||
->will($this->returnValue(new PersistentToken('fooclass', 'foouser', 'fooseries', 'foovalue', new \DateTime())))
|
||||
;
|
||||
|
||||
$tokenProvider = $this->getMock('Symfony\Component\Security\Core\Authentication\RememberMe\TokenProviderInterface');
|
||||
$tokenProvider
|
||||
->expects($this->once())
|
||||
->method('updateToken')
|
||||
->with($this->equalTo('fooseries'))
|
||||
->will($this->returnValue(null))
|
||||
;
|
||||
$service->setTokenProvider($tokenProvider);
|
||||
|
||||
$this->assertFalse($response->headers->hasCookie('foo'));
|
||||
|
||||
$service->loginSuccess($request, $response, $token);
|
||||
|
||||
$cookie = $response->headers->getCookie('foo');
|
||||
$this->assertFalse($cookie->isCleared());
|
||||
$this->assertTrue($cookie->isSecure());
|
||||
$this->assertTrue($cookie->isHttpOnly());
|
||||
$this->assertTrue($cookie->getExpire() > time() + 3590 && $cookie->getExpire() < time() + 3610);
|
||||
$this->assertEquals('myfoodomain.foo', $cookie->getDomain());
|
||||
$this->assertEquals('/foo/path', $cookie->getPath());
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException RuntimeException
|
||||
* @expectedMessage RememberMeToken must contain a PersistentTokenInterface implementation when used as login.
|
||||
*/
|
||||
public function testLoginSuccessThrowsExceptionWhenRememberMeTokenDoesNotContainPersistentTokenImplementation()
|
||||
{
|
||||
$service = $this->getService(null, array('always_remember_me' => true, 'name' => 'foo'));
|
||||
$request = new Request;
|
||||
$response = new Response;
|
||||
|
||||
$user = $this->getMock('Symfony\Component\Security\Core\User\UserInterface');
|
||||
$user
|
||||
->expects($this->once())
|
||||
->method('getRoles')
|
||||
->will($this->returnValue(array('ROLE_FOO')))
|
||||
;
|
||||
|
||||
$token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\RememberMeToken', array(), array($user, 'fookey', 'fookey'));
|
||||
$token
|
||||
->expects($this->once())
|
||||
->method('getPersistentToken')
|
||||
->will($this->returnValue(null))
|
||||
;
|
||||
|
||||
$service->loginSuccess($request, $response, $token);
|
||||
$this->assertFalse($request->attributes->has(RememberMeServicesInterface::COOKIE_ATTR_NAME));
|
||||
$service->loginFail($request);
|
||||
$this->assertTrue($request->attributes->get(RememberMeServicesInterface::COOKIE_ATTR_NAME)->isCleared());
|
||||
}
|
||||
|
||||
public function testLoginSuccessSetsCookieWhenLoggedInWithNonRememberMeTokenInterfaceImplementation()
|
||||
|
@ -2,6 +2,8 @@
|
||||
|
||||
namespace Symfony\Tests\Component\Security\Http\RememberMe;
|
||||
|
||||
use Symfony\Component\Security\Http\RememberMe\RememberMeServicesInterface;
|
||||
|
||||
use Symfony\Component\Security\Core\Authentication\Token\RememberMeToken;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\Token;
|
||||
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
|
||||
@ -21,27 +23,21 @@ class TokenBasedRememberMeServicesTest extends \PHPUnit_Framework_TestCase
|
||||
$this->assertNull($service->autoLogin(new Request()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Symfony\Component\Security\Core\Exception\AuthenticationException
|
||||
* @expectedMessage The cookie is invalid.
|
||||
*/
|
||||
public function testAutoLoginThrowsExceptionOnInvalidCookie()
|
||||
{
|
||||
$service = $this->getService(null, array('name' => 'foo', 'always_remember_me' => false, 'remember_me_parameter' => 'foo'));
|
||||
$service = $this->getService(null, array('name' => 'foo', 'path' => null, 'domain' => null, 'always_remember_me' => false, 'remember_me_parameter' => 'foo'));
|
||||
$request = new Request;
|
||||
$request->request->set('foo', 'true');
|
||||
$request->cookies->set('foo', 'foo');
|
||||
|
||||
$service->autoLogin($request);
|
||||
$this->assertNull($service->autoLogin($request));
|
||||
$this->assertTrue($request->attributes->get(RememberMeServicesInterface::COOKIE_ATTR_NAME)->isCleared());
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Symfony\Component\Security\Core\Exception\UsernameNotFoundException
|
||||
*/
|
||||
public function testAutoLoginThrowsExceptionOnNonExistentUser()
|
||||
{
|
||||
$userProvider = $this->getProvider();
|
||||
$service = $this->getService($userProvider, array('name' => 'foo', 'always_remember_me' => true, 'lifetime' => 3600));
|
||||
$service = $this->getService($userProvider, array('name' => 'foo', 'path' => null, 'domain' => null, 'always_remember_me' => true, 'lifetime' => 3600));
|
||||
$request = new Request;
|
||||
$request->cookies->set('foo', $this->getCookie('fooclass', 'foouser', time()+3600, 'foopass'));
|
||||
|
||||
@ -51,17 +47,14 @@ class TokenBasedRememberMeServicesTest extends \PHPUnit_Framework_TestCase
|
||||
->will($this->throwException(new UsernameNotFoundException('user not found')))
|
||||
;
|
||||
|
||||
$service->autoLogin($request);
|
||||
$this->assertNull($service->autoLogin($request));
|
||||
$this->assertTrue($request->attributes->get(RememberMeServicesInterface::COOKIE_ATTR_NAME)->isCleared());
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Symfony\Component\Security\Core\Exception\AuthenticationException
|
||||
* @expectedMessage The cookie's hash is invalid.
|
||||
*/
|
||||
public function testAutoLoginDoesNotAcceptCookieWithInvalidHash()
|
||||
{
|
||||
$userProvider = $this->getProvider();
|
||||
$service = $this->getService($userProvider, array('name' => 'foo', 'always_remember_me' => true, 'lifetime' => 3600));
|
||||
$service = $this->getService($userProvider, array('name' => 'foo', 'path' => null, 'domain' => null, 'always_remember_me' => true, 'lifetime' => 3600));
|
||||
$request = new Request;
|
||||
$request->cookies->set('foo', base64_encode('class:'.base64_encode('foouser').':123456789:fooHash'));
|
||||
|
||||
@ -79,17 +72,14 @@ class TokenBasedRememberMeServicesTest extends \PHPUnit_Framework_TestCase
|
||||
->will($this->returnValue($user))
|
||||
;
|
||||
|
||||
$service->autoLogin($request);
|
||||
$this->assertNull($service->autoLogin($request));
|
||||
$this->assertTrue($request->attributes->get(RememberMeServicesInterface::COOKIE_ATTR_NAME)->isCleared());
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Symfony\Component\Security\Core\Exception\AuthenticationException
|
||||
* @expectedMessage The cookie has expired.
|
||||
*/
|
||||
public function testAutoLoginDoesNotAcceptAnExpiredCookie()
|
||||
{
|
||||
$userProvider = $this->getProvider();
|
||||
$service = $this->getService($userProvider, array('name' => 'foo', 'always_remember_me' => true, 'lifetime' => 3600));
|
||||
$service = $this->getService($userProvider, array('name' => 'foo', 'path' => null, 'domain' => null, 'always_remember_me' => true, 'lifetime' => 3600));
|
||||
$request = new Request;
|
||||
$request->cookies->set('foo', $this->getCookie('fooclass', 'foouser', time() - 1, 'foopass'));
|
||||
|
||||
@ -107,7 +97,8 @@ class TokenBasedRememberMeServicesTest extends \PHPUnit_Framework_TestCase
|
||||
->will($this->returnValue($user))
|
||||
;
|
||||
|
||||
$service->autoLogin($request);
|
||||
$this->assertNull($service->autoLogin($request));
|
||||
$this->assertTrue($request->attributes->get(RememberMeServicesInterface::COOKIE_ATTR_NAME)->isCleared());
|
||||
}
|
||||
|
||||
public function testAutoLogin()
|
||||
@ -150,11 +141,9 @@ class TokenBasedRememberMeServicesTest extends \PHPUnit_Framework_TestCase
|
||||
$response = new Response();
|
||||
$token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface');
|
||||
|
||||
$this->assertFalse($response->headers->hasCookie('foo'));
|
||||
|
||||
$service->logout($request, $response, $token);
|
||||
|
||||
$cookie = $response->headers->getCookie('foo');
|
||||
$cookie = $request->attributes->get(RememberMeServicesInterface::COOKIE_ATTR_NAME);
|
||||
$this->assertTrue($cookie->isCleared());
|
||||
$this->assertNull($cookie->getPath());
|
||||
$this->assertNull($cookie->getDomain());
|
||||
@ -166,38 +155,14 @@ class TokenBasedRememberMeServicesTest extends \PHPUnit_Framework_TestCase
|
||||
$request = new Request();
|
||||
$response = new Response();
|
||||
|
||||
$this->assertFalse($response->headers->hasCookie('foo'));
|
||||
|
||||
$service->loginFail($request, $response);
|
||||
|
||||
$cookie = $response->headers->getCookie('foo');
|
||||
$cookie = $request->attributes->get(RememberMeServicesInterface::COOKIE_ATTR_NAME);
|
||||
$this->assertTrue($cookie->isCleared());
|
||||
$this->assertEquals('/foo', $cookie->getPath());
|
||||
$this->assertEquals('foodomain.foo', $cookie->getDomain());
|
||||
}
|
||||
|
||||
public function testLoginSuccessDoesNotRenewRememberMeToken()
|
||||
{
|
||||
$service = $this->getService(null, array('name' => 'foo', 'domain' => 'myfoodomain.foo', 'path' => '/foo/path', 'secure' => true, 'httponly' => true, 'lifetime' => 3600));
|
||||
$request = new Request;
|
||||
$response = new Response;
|
||||
|
||||
$user = $this->getMock('Symfony\Component\Security\Core\User\UserInterface');
|
||||
$user
|
||||
->expects($this->once())
|
||||
->method('getRoles')
|
||||
->will($this->returnValue(array('ROLE_FOO')))
|
||||
;
|
||||
|
||||
$token = new RememberMeToken($user, 'fookey', 'foo');
|
||||
|
||||
$this->assertFalse($response->headers->hasCookie('foo'));
|
||||
|
||||
$service->loginSuccess($request, $response, $token);
|
||||
|
||||
$this->assertFalse($response->headers->hasCookie('foo'));
|
||||
}
|
||||
|
||||
public function testLoginSuccessIgnoresTokensWhichDoNotContainAnUserInterfaceImplementation()
|
||||
{
|
||||
$service = $this->getService(null, array('name' => 'foo', 'always_remember_me' => true));
|
||||
|
Reference in New Issue
Block a user