summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Core/Authentication/AuthenticationManagerInterface.php2
-rw-r--r--Core/Authentication/Token/RememberMeToken.php10
-rw-r--r--Http/Firewall/RememberMeListener.php77
-rw-r--r--Http/RememberMe/AbstractRememberMeServices.php (renamed from Http/RememberMe/RememberMeServices.php)110
-rw-r--r--Http/RememberMe/PersistentTokenBasedRememberMeServices.php74
-rw-r--r--Http/RememberMe/RememberMeServicesInterface.php59
-rw-r--r--Http/RememberMe/TokenBasedRememberMeServices.php10
7 files changed, 164 insertions, 178 deletions
diff --git a/Core/Authentication/AuthenticationManagerInterface.php b/Core/Authentication/AuthenticationManagerInterface.php
index 5f407f2..36cdc92 100644
--- a/Core/Authentication/AuthenticationManagerInterface.php
+++ b/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/Core/Authentication/Token/RememberMeToken.php b/Core/Authentication/Token/RememberMeToken.php
index 038198a..7978427 100644
--- a/Core/Authentication/Token/RememberMeToken.php
+++ b/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/Http/Firewall/RememberMeListener.php b/Http/Firewall/RememberMeListener.php
index 6b23679..f0755db 100644
--- a/Http/Firewall/RememberMeListener.php
+++ b/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;
}
- try {
- if (null === $token = $this->rememberMeServices->autoLogin($event->get('request'))) {
- return;
- }
-
- 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.');
- }
+ $request = $event->get('request');
+ if (null === $token = $this->rememberMeServices->autoLogin($request)) {
+ return;
+ }
- $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()
- );
- }
+ try {
+ $token = $this->authenticationManager->authenticate($token);
+ $this->securityContext->setToken($token);
- $this->lastState = $failed;
+ if (null !== $this->eventDispatcher) {
+ $this->eventDispatcher->notify(new Event($this, 'security.interactive_login', array('request' => $request, 'token' => $token)));
}
- } 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.');
}
-
- // silently ignore everything except a cookie theft exception
- if ($cookieInvalid instanceof CookieTheftException) {
- throw $cookieInvalid;
+ } 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()
+ );
}
- }
- }
-
- /**
- * 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);
+ $this->rememberMeServices->loginFail($request);
}
-
- return $response;
}
} \ No newline at end of file
diff --git a/Http/RememberMe/RememberMeServices.php b/Http/RememberMe/AbstractRememberMeServices.php
index e0ed52b..daf68f5 100644
--- a/Http/RememberMe/RememberMeServices.php
+++ b/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 <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 (null !== $this->logger) {
- $this->logger->debug('Remember-me cookie accepted.');
+ 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());
+ }
}
- return $token;
+ $this->cancelCookie($request);
+
+ 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 (!$token->getUser() instanceof UserInterface) {
+ if (null !== $this->logger) {
+ $this->logger->debug('Remember-me ignores token since it does not contain an UserInterface implementation.');
}
- if (!$this->isRememberMeRequested($request)) {
- if (null !== $this->logger) {
- $this->logger->debug('Remember-me was not requested.');
- }
-
- return;
- }
+ return;
+ }
+ if (!$this->isRememberMeRequested($request)) {
if (null !== $this->logger) {
- $this->logger->debug('Remember-me was requested; setting cookie.');
+ $this->logger->debug('Remember-me was not requested.');
}
- } else if (null !== $this->logger) {
- $this->logger->debug('Re-newing remember-me token; setting cookie.');
+
+ 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/Http/RememberMe/PersistentTokenBasedRememberMeServices.php b/Http/RememberMe/PersistentTokenBasedRememberMeServices.php
index 351ad03..f2a0249 100644
--- a/Http/RememberMe/PersistentTokenBasedRememberMeServices.php
+++ b/Http/RememberMe/PersistentTokenBasedRememberMeServices.php
@@ -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 = $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()
- )
- );
- }
+ $series = $this->generateRandomValue();
+ $tokenValue = $this->generateRandomValue();
+
+ $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'],
@@ -128,18 +130,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
*
* @return string
@@ -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/Http/RememberMe/RememberMeServicesInterface.php b/Http/RememberMe/RememberMeServicesInterface.php
index b038a0d..c740d28 100644
--- a/Http/RememberMe/RememberMeServicesInterface.php
+++ b/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 <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.
- *
+ * 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, Response $response);
+ function loginFail(Request $request);
/**
- * 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 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/Http/RememberMe/TokenBasedRememberMeServices.php b/Http/RememberMe/TokenBasedRememberMeServices.php
index 206e10b..0fd5c41 100644
--- a/Http/RememberMe/TokenBasedRememberMeServices.php
+++ b/Http/RememberMe/TokenBasedRememberMeServices.php
@@ -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());
}
}