diff options
Diffstat (limited to 'Http/RememberMe')
-rw-r--r-- | Http/RememberMe/AbstractRememberMeServices.php | 27 | ||||
-rw-r--r-- | Http/RememberMe/PersistentTokenBasedRememberMeServices.php | 55 | ||||
-rw-r--r-- | Http/RememberMe/RememberMeServicesInterface.php | 6 | ||||
-rw-r--r-- | Http/RememberMe/ResponseListener.php | 39 | ||||
-rw-r--r-- | Http/RememberMe/TokenBasedRememberMeServices.php | 2 |
5 files changed, 89 insertions, 40 deletions
diff --git a/Http/RememberMe/AbstractRememberMeServices.php b/Http/RememberMe/AbstractRememberMeServices.php index d61a6ce..ae61dd7 100644 --- a/Http/RememberMe/AbstractRememberMeServices.php +++ b/Http/RememberMe/AbstractRememberMeServices.php @@ -22,7 +22,7 @@ use Symfony\Component\Security\Core\Exception\CookieTheftException; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Cookie; -use Symfony\Component\HttpKernel\Log\LoggerInterface; +use Psr\Log\LoggerInterface; /** * Base class implementing the RememberMeServicesInterface @@ -47,6 +47,8 @@ abstract class AbstractRememberMeServices implements RememberMeServicesInterface * @param string $providerKey * @param array $options * @param LoggerInterface $logger + * + * @throws \InvalidArgumentException */ public function __construct(array $userProviders, $key, $providerKey, array $options = array(), LoggerInterface $logger = null) { @@ -89,9 +91,11 @@ abstract class AbstractRememberMeServices implements RememberMeServicesInterface * * @param Request $request * - * @return TokenInterface + * @return TokenInterface|null + * + * @throws CookieTheftException */ - public final function autoLogin(Request $request) + final public function autoLogin(Request $request) { if (null === $cookie = $request->cookies->get($this->options['name'])) { return; @@ -125,7 +129,7 @@ abstract class AbstractRememberMeServices implements RememberMeServicesInterface } } catch (UnsupportedUserException $unSupported) { if (null !== $this->logger) { - $this->logger->warn('User class for remember-me cookie not supported.'); + $this->logger->warning('User class for remember-me cookie not supported.'); } } catch (AuthenticationException $invalid) { if (null !== $this->logger) { @@ -156,7 +160,7 @@ abstract class AbstractRememberMeServices implements RememberMeServicesInterface * * @param Request $request */ - public final function loginFail(Request $request) + final public function loginFail(Request $request) { $this->cancelCookie($request); $this->onLoginFail($request); @@ -170,8 +174,11 @@ abstract class AbstractRememberMeServices implements RememberMeServicesInterface * @param Response $response * @param TokenInterface $token The token that resulted in a successful authentication */ - public final function loginSuccess(Request $request, Response $response, TokenInterface $token) + final public function loginSuccess(Request $request, Response $response, TokenInterface $token) { + // Make sure any old remember-me cookies are cancelled + $this->cancelCookie($request); + if (!$token->getUser() instanceof UserInterface) { if (null !== $this->logger) { $this->logger->debug('Remember-me ignores token since it does not contain a UserInterface implementation.'); @@ -192,6 +199,12 @@ abstract class AbstractRememberMeServices implements RememberMeServicesInterface $this->logger->debug('Remember-me was requested; setting cookie.'); } + // Remove attribute from request that sets a NULL cookie. + // It was set by $this->cancelCookie() + // (cancelCookie does other things too for some RememberMeServices + // so we should still call it at the start of this method) + $request->attributes->remove(self::COOKIE_ATTR_NAME); + $this->onLoginSuccess($request, $response, $token); } @@ -221,7 +234,7 @@ abstract class AbstractRememberMeServices implements RememberMeServicesInterface */ abstract protected function onLoginSuccess(Request $request, Response $response, TokenInterface $token); - protected final function getUserProvider($class) + final protected function getUserProvider($class) { foreach ($this->userProviders as $provider) { if ($provider->supportsClass($class)) { diff --git a/Http/RememberMe/PersistentTokenBasedRememberMeServices.php b/Http/RememberMe/PersistentTokenBasedRememberMeServices.php index 8944672..9f4013d 100644 --- a/Http/RememberMe/PersistentTokenBasedRememberMeServices.php +++ b/Http/RememberMe/PersistentTokenBasedRememberMeServices.php @@ -19,6 +19,7 @@ use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Core\Exception\CookieTheftException; use Symfony\Component\Security\Core\Authentication\RememberMe\PersistentToken; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; +use Symfony\Component\Security\Core\Util\SecureRandomInterface; /** * Concrete implementation of the RememberMeServicesInterface which needs @@ -30,6 +31,24 @@ use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; class PersistentTokenBasedRememberMeServices extends AbstractRememberMeServices { private $tokenProvider; + private $secureRandom; + + /** + * Constructor. + * + * @param array $userProviders + * @param string $key + * @param string $providerKey + * @param array $options + * @param LoggerInterface $logger + * @param SecureRandomInterface $secureRandom + */ + public function __construct(array $userProviders, $key, $providerKey, array $options = array(), LoggerInterface $logger = null, SecureRandomInterface $secureRandom) + { + parent::__construct($userProviders, $key, $providerKey, $options, $logger); + + $this->secureRandom = $secureRandom; + } /** * Sets the token provider @@ -44,10 +63,12 @@ class PersistentTokenBasedRememberMeServices extends AbstractRememberMeServices /** * {@inheritDoc} */ - public function logout(Request $request, Response $response, TokenInterface $token) + protected function cancelCookie(Request $request) { - parent::logout($request, $response, $token); + // Delete cookie on the client + parent::cancelCookie($request); + // Delete cookie from the tokenProvider if (null !== ($cookie = $request->cookies->get($this->options['name'])) && count($parts = $this->decodeCookie($cookie)) === 2 ) { @@ -69,8 +90,6 @@ class PersistentTokenBasedRememberMeServices extends AbstractRememberMeServices $persistentToken = $this->tokenProvider->loadTokenBySeries($series); if ($persistentToken->getTokenValue() !== $tokenValue) { - $this->tokenProvider->deleteTokenBySeries($series); - throw new CookieTheftException('This token was already used. The account is possibly compromised.'); } @@ -79,7 +98,7 @@ class PersistentTokenBasedRememberMeServices extends AbstractRememberMeServices } $series = $persistentToken->getSeries(); - $tokenValue = $this->generateRandomValue(); + $tokenValue = $this->secureRandom->nextBytes(64); $this->tokenProvider->updateToken($series, $tokenValue, new \DateTime()); $request->attributes->set(self::COOKIE_ATTR_NAME, new Cookie( @@ -101,8 +120,8 @@ class PersistentTokenBasedRememberMeServices extends AbstractRememberMeServices */ protected function onLoginSuccess(Request $request, Response $response, TokenInterface $token) { - $series = $this->generateRandomValue(); - $tokenValue = $this->generateRandomValue(); + $series = $this->secureRandom->nextBytes(64); + $tokenValue = $this->secureRandom->nextBytes(64); $this->tokenProvider->createNewToken( new PersistentToken( @@ -126,26 +145,4 @@ class PersistentTokenBasedRememberMeServices extends AbstractRememberMeServices ) ); } - - /** - * Generates a cryptographically strong random value - * - * @return string - */ - protected function generateRandomValue() - { - if (function_exists('openssl_random_pseudo_bytes')) { - $bytes = openssl_random_pseudo_bytes(64, $strong); - - if (true === $strong && false !== $bytes) { - return base64_encode($bytes); - } - } - - if (null !== $this->logger) { - $this->logger->warn('Could not produce a cryptographically strong random value. Please install/update the OpenSSL extension.'); - } - - return base64_encode(hash('sha512', uniqid(mt_rand(), true), true)); - } } diff --git a/Http/RememberMe/RememberMeServicesInterface.php b/Http/RememberMe/RememberMeServicesInterface.php index 0497c69..a00dcef 100644 --- a/Http/RememberMe/RememberMeServicesInterface.php +++ b/Http/RememberMe/RememberMeServicesInterface.php @@ -51,7 +51,7 @@ interface RememberMeServicesInterface * * @return TokenInterface */ - function autoLogin(Request $request); + public function autoLogin(Request $request); /** * Called whenever an interactive authentication attempt was made, but the @@ -61,7 +61,7 @@ interface RememberMeServicesInterface * * @param Request $request */ - function loginFail(Request $request); + public function loginFail(Request $request); /** * Called whenever an interactive authentication attempt is successful @@ -78,5 +78,5 @@ interface RememberMeServicesInterface * @param Response $response * @param TokenInterface $token */ - function loginSuccess(Request $request, Response $response, TokenInterface $token); + public function loginSuccess(Request $request, Response $response, TokenInterface $token); } diff --git a/Http/RememberMe/ResponseListener.php b/Http/RememberMe/ResponseListener.php new file mode 100644 index 0000000..03c71c7 --- /dev/null +++ b/Http/RememberMe/ResponseListener.php @@ -0,0 +1,39 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Security\Http\RememberMe; + +use Symfony\Component\HttpKernel\Event\FilterResponseEvent; +use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +/** + * Adds remember-me cookies to the Response. + * + * @author Johannes M. Schmitt <schmittjoh@gmail.com> + */ +class ResponseListener implements EventSubscriberInterface +{ + public function onKernelResponse(FilterResponseEvent $event) + { + $request = $event->getRequest(); + $response = $event->getResponse(); + + if ($request->attributes->has(RememberMeServicesInterface::COOKIE_ATTR_NAME)) { + $response->headers->setCookie($request->attributes->get(RememberMeServicesInterface::COOKIE_ATTR_NAME)); + } + } + + public static function getSubscribedEvents() + { + return array(KernelEvents::RESPONSE => 'onKernelResponse'); + } +} diff --git a/Http/RememberMe/TokenBasedRememberMeServices.php b/Http/RememberMe/TokenBasedRememberMeServices.php index bd828f2..5a66fe4 100644 --- a/Http/RememberMe/TokenBasedRememberMeServices.php +++ b/Http/RememberMe/TokenBasedRememberMeServices.php @@ -43,7 +43,7 @@ class TokenBasedRememberMeServices extends AbstractRememberMeServices $user = $this->getUserProvider($class)->loadUserByUsername($username); } catch (\Exception $ex) { if (!$ex instanceof AuthenticationException) { - $ex = new AuthenticationException($ex->getMessage(), null, $ex->getCode(), $ex); + $ex = new AuthenticationException($ex->getMessage(), $ex->getCode(), $ex); } throw $ex; |