From 3d976388130e359750ccbe8a88819d8df0278784 Mon Sep 17 00:00:00 2001 From: "Johannes M. Schmitt" Date: Thu, 10 Mar 2011 21:27:42 +0100 Subject: [PATCH] [Security] refactored remember-me code --- .../Resources/config/security_rememberme.xml | 6 + .../SecurityBundle/ResponseListener.php | 24 ++ .../AuthenticationManagerInterface.php | 2 +- .../Authentication/Token/RememberMeToken.php | 10 +- .../Http/Firewall/RememberMeListener.php | 81 ++----- ...ces.php => AbstractRememberMeServices.php} | 116 +++++---- ...PersistentTokenBasedRememberMeServices.php | 72 +++--- .../RememberMeServicesInterface.php | 65 ++++-- .../TokenBasedRememberMeServices.php | 10 +- .../Token/RememerMeTokenTest.php | 7 - .../Http/Firewall/RememberMeListenerTest.php | 220 +----------------- ...php => AbstractRememberMeServicesTest.php} | 59 ++--- ...istentTokenBasedRememberMeServicesTest.php | 142 +++-------- .../TokenBasedRememberMeServicesTest.php | 67 ++---- 14 files changed, 275 insertions(+), 606 deletions(-) create mode 100644 src/Symfony/Bundle/SecurityBundle/ResponseListener.php rename src/Symfony/Component/Security/Http/RememberMe/{RememberMeServices.php => AbstractRememberMeServices.php} (69%) rename tests/Symfony/Tests/Component/Security/Http/RememberMe/{RememberMeServicesTest.php => AbstractRememberMeServicesTest.php} (83%) diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/config/security_rememberme.xml b/src/Symfony/Bundle/SecurityBundle/Resources/config/security_rememberme.xml index 55c87df404..e47c59fc18 100644 --- a/src/Symfony/Bundle/SecurityBundle/Resources/config/security_rememberme.xml +++ b/src/Symfony/Bundle/SecurityBundle/Resources/config/security_rememberme.xml @@ -12,6 +12,8 @@ Symfony\Component\Security\Http\RememberMe\PersistentTokenBasedRememberMeServices Symfony\Component\Security\Http\RememberMe\TokenBasedRememberMeServices + + Symfony\Bundle\SecurityBundle\ResponseListener @@ -47,6 +49,10 @@ parent="security.authentication.rememberme.services.abstract" abstract="true"> + + + + \ No newline at end of file diff --git a/src/Symfony/Bundle/SecurityBundle/ResponseListener.php b/src/Symfony/Bundle/SecurityBundle/ResponseListener.php new file mode 100644 index 0000000000..dfebf6b425 --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/ResponseListener.php @@ -0,0 +1,24 @@ + + */ +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)); + } +} \ No newline at end of file diff --git a/src/Symfony/Component/Security/Core/Authentication/AuthenticationManagerInterface.php b/src/Symfony/Component/Security/Core/Authentication/AuthenticationManagerInterface.php index 5f407f2828..36cdc92e74 100644 --- a/src/Symfony/Component/Security/Core/Authentication/AuthenticationManagerInterface.php +++ b/src/Symfony/Component/Security/Core/Authentication/AuthenticationManagerInterface.php @@ -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 */ diff --git a/src/Symfony/Component/Security/Core/Authentication/Token/RememberMeToken.php b/src/Symfony/Component/Security/Core/Authentication/Token/RememberMeToken.php index 038198a4a5..79784271fe 100644 --- a/src/Symfony/Component/Security/Core/Authentication/Token/RememberMeToken.php +++ b/src/Symfony/Component/Security/Core/Authentication/Token/RememberMeToken.php @@ -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 ''; diff --git a/src/Symfony/Component/Security/Http/Firewall/RememberMeListener.php b/src/Symfony/Component/Security/Http/Firewall/RememberMeListener.php index 6b23679af3..f0755db2f8 100644 --- a/src/Symfony/Component/Security/Http/Firewall/RememberMeListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/RememberMeListener.php @@ -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; - } } \ No newline at end of file diff --git a/src/Symfony/Component/Security/Http/RememberMe/RememberMeServices.php b/src/Symfony/Component/Security/Http/RememberMe/AbstractRememberMeServices.php similarity index 69% rename from src/Symfony/Component/Security/Http/RememberMe/RememberMeServices.php rename to src/Symfony/Component/Security/Http/RememberMe/AbstractRememberMeServices.php index e0ed52b478..daf68f558f 100644 --- a/src/Symfony/Component/Security/Http/RememberMe/RememberMeServices.php +++ b/src/Symfony/Component/Security/Http/RememberMe/AbstractRememberMeServices.php @@ -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 */ -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'])); } /** diff --git a/src/Symfony/Component/Security/Http/RememberMe/PersistentTokenBasedRememberMeServices.php b/src/Symfony/Component/Security/Http/RememberMe/PersistentTokenBasedRememberMeServices.php index 351ad0348b..f2a024939c 100644 --- a/src/Symfony/Component/Security/Http/RememberMe/PersistentTokenBasedRememberMeServices.php +++ b/src/Symfony/Component/Security/Http/RememberMe/PersistentTokenBasedRememberMeServices.php @@ -28,7 +28,7 @@ use Symfony\Component\Security\Core\Authentication\Token\RememberMeToken; * * @author Johannes M. Schmitt */ -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)); } -} +} \ No newline at end of file diff --git a/src/Symfony/Component/Security/Http/RememberMe/RememberMeServicesInterface.php b/src/Symfony/Component/Security/Http/RememberMe/RememberMeServicesInterface.php index b038a0dc98..c740d28d81 100644 --- a/src/Symfony/Component/Security/Http/RememberMe/RememberMeServicesInterface.php +++ b/src/Symfony/Component/Security/Http/RememberMe/RememberMeServicesInterface.php @@ -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 */ 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); } \ No newline at end of file diff --git a/src/Symfony/Component/Security/Http/RememberMe/TokenBasedRememberMeServices.php b/src/Symfony/Component/Security/Http/RememberMe/TokenBasedRememberMeServices.php index 206e10b91c..0fd5c418bb 100644 --- a/src/Symfony/Component/Security/Http/RememberMe/TokenBasedRememberMeServices.php +++ b/src/Symfony/Component/Security/Http/RememberMe/TokenBasedRememberMeServices.php @@ -25,7 +25,7 @@ use Symfony\Component\Security\Core\User\UserInterface; * * @author Johannes M. Schmitt */ -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()); } } diff --git a/tests/Symfony/Tests/Component/Security/Core/Authentication/Token/RememerMeTokenTest.php b/tests/Symfony/Tests/Component/Security/Core/Authentication/Token/RememerMeTokenTest.php index 1a614519c9..3453615f70 100644 --- a/tests/Symfony/Tests/Component/Security/Core/Authentication/Token/RememerMeTokenTest.php +++ b/tests/Symfony/Tests/Component/Security/Core/Authentication/Token/RememerMeTokenTest.php @@ -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'); diff --git a/tests/Symfony/Tests/Component/Security/Http/Firewall/RememberMeListenerTest.php b/tests/Symfony/Tests/Component/Security/Http/Firewall/RememberMeListenerTest.php index 87c965c1c7..bb8720cae8 100644 --- a/tests/Symfony/Tests/Component/Security/Http/Firewall/RememberMeListenerTest.php +++ b/tests/Symfony/Tests/Component/Security/Http/Firewall/RememberMeListenerTest.php @@ -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() diff --git a/tests/Symfony/Tests/Component/Security/Http/RememberMe/RememberMeServicesTest.php b/tests/Symfony/Tests/Component/Security/Http/RememberMe/AbstractRememberMeServicesTest.php similarity index 83% rename from tests/Symfony/Tests/Component/Security/Http/RememberMe/RememberMeServicesTest.php rename to tests/Symfony/Tests/Component/Security/Http/RememberMe/AbstractRememberMeServicesTest.php index 68246fceee..96531a0d6c 100644 --- a/tests/Symfony/Tests/Component/Security/Http/RememberMe/RememberMeServicesTest.php +++ b/tests/Symfony/Tests/Component/Security/Http/RememberMe/AbstractRememberMeServicesTest.php @@ -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 )); } diff --git a/tests/Symfony/Tests/Component/Security/Http/RememberMe/PersistentTokenBasedRememberMeServicesTest.php b/tests/Symfony/Tests/Component/Security/Http/RememberMe/PersistentTokenBasedRememberMeServicesTest.php index bdaa053660..687a5352f5 100644 --- a/tests/Symfony/Tests/Component/Security/Http/RememberMe/PersistentTokenBasedRememberMeServicesTest.php +++ b/tests/Symfony/Tests/Component/Security/Http/RememberMe/PersistentTokenBasedRememberMeServicesTest.php @@ -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() diff --git a/tests/Symfony/Tests/Component/Security/Http/RememberMe/TokenBasedRememberMeServicesTest.php b/tests/Symfony/Tests/Component/Security/Http/RememberMe/TokenBasedRememberMeServicesTest.php index daaad7d091..f9e0044ae3 100644 --- a/tests/Symfony/Tests/Component/Security/Http/RememberMe/TokenBasedRememberMeServicesTest.php +++ b/tests/Symfony/Tests/Component/Security/Http/RememberMe/TokenBasedRememberMeServicesTest.php @@ -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));