diff options
Diffstat (limited to 'Http')
23 files changed, 126 insertions, 81 deletions
diff --git a/Http/Authentication/DefaultAuthenticationFailureHandler.php b/Http/Authentication/DefaultAuthenticationFailureHandler.php index 61d77a8..64f84f0 100644 --- a/Http/Authentication/DefaultAuthenticationFailureHandler.php +++ b/Http/Authentication/DefaultAuthenticationFailureHandler.php @@ -13,7 +13,7 @@ namespace Symfony\Component\Security\Http\Authentication; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\HttpKernelInterface; -use Symfony\Component\HttpKernel\Log\LoggerInterface; +use Psr\Log\LoggerInterface; use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Core\SecurityContextInterface; use Symfony\Component\Security\Http\HttpUtils; @@ -50,9 +50,10 @@ class DefaultAuthenticationFailureHandler implements AuthenticationFailureHandle $this->logger = $logger; $this->options = array_merge(array( - 'failure_path' => null, - 'failure_forward' => false, - 'login_path' => '/login', + 'failure_path' => null, + 'failure_forward' => false, + 'login_path' => '/login', + 'failure_path_parameter' => '_failure_path' ), $options); } @@ -61,6 +62,10 @@ class DefaultAuthenticationFailureHandler implements AuthenticationFailureHandle */ public function onAuthenticationFailure(Request $request, AuthenticationException $exception) { + if ($failureUrl = $request->get($this->options['failure_path_parameter'], null, true)) { + $this->options['failure_path'] = $failureUrl; + } + if (null === $this->options['failure_path']) { $this->options['failure_path'] = $this->options['login_path']; } diff --git a/Http/EntryPoint/BasicAuthenticationEntryPoint.php b/Http/EntryPoint/BasicAuthenticationEntryPoint.php index 6ba3872..44ece5e 100644 --- a/Http/EntryPoint/BasicAuthenticationEntryPoint.php +++ b/Http/EntryPoint/BasicAuthenticationEntryPoint.php @@ -34,7 +34,7 @@ class BasicAuthenticationEntryPoint implements AuthenticationEntryPointInterface { $response = new Response(); $response->headers->set('WWW-Authenticate', sprintf('Basic realm="%s"', $this->realmName)); - $response->setStatusCode(401, $authException ? $authException->getMessage() : null); + $response->setStatusCode(401); return $response; } diff --git a/Http/EntryPoint/DigestAuthenticationEntryPoint.php b/Http/EntryPoint/DigestAuthenticationEntryPoint.php index ec92419..1131b58 100644 --- a/Http/EntryPoint/DigestAuthenticationEntryPoint.php +++ b/Http/EntryPoint/DigestAuthenticationEntryPoint.php @@ -16,7 +16,7 @@ use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface use Symfony\Component\Security\Core\Exception\NonceExpiredException; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpKernel\Log\LoggerInterface; +use Psr\Log\LoggerInterface; /** * DigestAuthenticationEntryPoint starts an HTTP Digest authentication. @@ -57,7 +57,7 @@ class DigestAuthenticationEntryPoint implements AuthenticationEntryPointInterfac $response = new Response(); $response->headers->set('WWW-Authenticate', $authenticateHeader); - $response->setStatusCode(401, $authException ? $authException->getMessage() : null); + $response->setStatusCode(401); return $response; } diff --git a/Http/Firewall.php b/Http/Firewall.php index a590fd9..e083fdb 100644 --- a/Http/Firewall.php +++ b/Http/Firewall.php @@ -12,8 +12,10 @@ namespace Symfony\Component\Security\Http; use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpKernel\KernelEvents; use Symfony\Component\HttpKernel\Event\GetResponseEvent; use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; /** * Firewall uses a FirewallMap to register security listeners for the given @@ -25,7 +27,7 @@ use Symfony\Component\EventDispatcher\EventDispatcherInterface; * * @author Fabien Potencier <fabien@symfony.com> */ -class Firewall +class Firewall implements EventSubscriberInterface { private $map; private $dispatcher; @@ -68,4 +70,9 @@ class Firewall } } } + + public static function getSubscribedEvents() + { + return array(KernelEvents::REQUEST => array('onKernelRequest', 8)); + } } diff --git a/Http/Firewall/AbstractAuthenticationListener.php b/Http/Firewall/AbstractAuthenticationListener.php index 410fb73..80f47f7 100644 --- a/Http/Firewall/AbstractAuthenticationListener.php +++ b/Http/Firewall/AbstractAuthenticationListener.php @@ -19,7 +19,7 @@ use Symfony\Component\Security\Core\SecurityContextInterface; use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface; use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Core\Exception\SessionUnavailableException; -use Symfony\Component\HttpKernel\Log\LoggerInterface; +use Psr\Log\LoggerInterface; use Symfony\Component\HttpKernel\Event\GetResponseEvent; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; @@ -75,6 +75,8 @@ abstract class AbstractAuthenticationListener implements ListenerInterface * successful, or failed authentication attempt * @param LoggerInterface $logger A LoggerInterface instance * @param EventDispatcherInterface $dispatcher An EventDispatcherInterface instance + * + * @throws \InvalidArgumentException */ public function __construct(SecurityContextInterface $securityContext, AuthenticationManagerInterface $authenticationManager, SessionAuthenticationStrategyInterface $sessionStrategy, HttpUtils $httpUtils, $providerKey, AuthenticationSuccessHandlerInterface $successHandler, AuthenticationFailureHandlerInterface $failureHandler, array $options = array(), LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null) { @@ -110,6 +112,9 @@ abstract class AbstractAuthenticationListener implements ListenerInterface * Handles form based authentication. * * @param GetResponseEvent $event A GetResponseEvent instance + * + * @throws \RuntimeException + * @throws SessionUnavailableException */ final public function handle(GetResponseEvent $event) { diff --git a/Http/Firewall/AbstractPreAuthenticatedListener.php b/Http/Firewall/AbstractPreAuthenticatedListener.php index 66041be..d7e46cf 100644 --- a/Http/Firewall/AbstractPreAuthenticatedListener.php +++ b/Http/Firewall/AbstractPreAuthenticatedListener.php @@ -18,7 +18,7 @@ use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Http\Event\InteractiveLoginEvent; use Symfony\Component\Security\Http\SecurityEvents; use Symfony\Component\HttpKernel\Event\GetResponseEvent; -use Symfony\Component\HttpKernel\Log\LoggerInterface; +use Psr\Log\LoggerInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\EventDispatcher\EventDispatcherInterface; diff --git a/Http/Firewall/AccessListener.php b/Http/Firewall/AccessListener.php index 3e2d3a5..c3894ef 100644 --- a/Http/Firewall/AccessListener.php +++ b/Http/Firewall/AccessListener.php @@ -15,7 +15,7 @@ use Symfony\Component\Security\Core\SecurityContextInterface; use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface; use Symfony\Component\Security\Http\AccessMapInterface; use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface; -use Symfony\Component\HttpKernel\Log\LoggerInterface; +use Psr\Log\LoggerInterface; use Symfony\Component\HttpKernel\Event\GetResponseEvent; use Symfony\Component\Security\Core\Exception\AuthenticationCredentialsNotFoundException; use Symfony\Component\Security\Core\Exception\AccessDeniedException; @@ -46,6 +46,9 @@ class AccessListener implements ListenerInterface * Handles access authorization. * * @param GetResponseEvent $event A GetResponseEvent instance + * + * @throws AccessDeniedException + * @throws AuthenticationCredentialsNotFoundException */ public function handle(GetResponseEvent $event) { diff --git a/Http/Firewall/AnonymousAuthenticationListener.php b/Http/Firewall/AnonymousAuthenticationListener.php index d00865d..af2213b 100644 --- a/Http/Firewall/AnonymousAuthenticationListener.php +++ b/Http/Firewall/AnonymousAuthenticationListener.php @@ -12,7 +12,7 @@ namespace Symfony\Component\Security\Http\Firewall; use Symfony\Component\Security\Core\SecurityContextInterface; -use Symfony\Component\HttpKernel\Log\LoggerInterface; +use Psr\Log\LoggerInterface; use Symfony\Component\HttpKernel\Event\GetResponseEvent; use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken; diff --git a/Http/Firewall/BasicAuthenticationListener.php b/Http/Firewall/BasicAuthenticationListener.php index e331179..5b1c8b3 100644 --- a/Http/Firewall/BasicAuthenticationListener.php +++ b/Http/Firewall/BasicAuthenticationListener.php @@ -14,7 +14,7 @@ namespace Symfony\Component\Security\Http\Firewall; use Symfony\Component\Security\Core\SecurityContextInterface; use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface; use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface; -use Symfony\Component\HttpKernel\Log\LoggerInterface; +use Psr\Log\LoggerInterface; use Symfony\Component\HttpKernel\Event\GetResponseEvent; use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; use Symfony\Component\Security\Core\Exception\AuthenticationException; diff --git a/Http/Firewall/ChannelListener.php b/Http/Firewall/ChannelListener.php index 9b0f8c6..9e4a6ee 100644 --- a/Http/Firewall/ChannelListener.php +++ b/Http/Firewall/ChannelListener.php @@ -13,7 +13,7 @@ namespace Symfony\Component\Security\Http\Firewall; use Symfony\Component\Security\Http\AccessMapInterface; use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface; -use Symfony\Component\HttpKernel\Log\LoggerInterface; +use Psr\Log\LoggerInterface; use Symfony\Component\HttpKernel\Event\GetResponseEvent; /** diff --git a/Http/Firewall/ContextListener.php b/Http/Firewall/ContextListener.php index c24c879..8c71876 100644 --- a/Http/Firewall/ContextListener.php +++ b/Http/Firewall/ContextListener.php @@ -12,7 +12,7 @@ namespace Symfony\Component\Security\Http\Firewall; use Symfony\Component\HttpKernel\HttpKernelInterface; -use Symfony\Component\HttpKernel\Log\LoggerInterface; +use Psr\Log\LoggerInterface; use Symfony\Component\HttpKernel\Event\GetResponseEvent; use Symfony\Component\HttpKernel\Event\FilterResponseEvent; use Symfony\Component\HttpKernel\KernelEvents; @@ -88,7 +88,7 @@ class ContextListener implements ListenerInterface $token = $this->refreshUser($token); } elseif (null !== $token) { if (null !== $this->logger) { - $this->logger->warn(sprintf('Session includes a "%s" where a security token is expected', is_object($token) ? get_class($token) : gettype($token))); + $this->logger->warning(sprintf('Session includes a "%s" where a security token is expected', is_object($token) ? get_class($token) : gettype($token))); } $token = null; @@ -138,6 +138,8 @@ class ContextListener implements ListenerInterface * @param TokenInterface $token * * @return TokenInterface|null + * + * @throws \RuntimeException */ private function refreshUser(TokenInterface $token) { @@ -163,7 +165,7 @@ class ContextListener implements ListenerInterface // let's try the next user provider } catch (UsernameNotFoundException $notFound) { if (null !== $this->logger) { - $this->logger->warn(sprintf('Username "%s" could not be found.', $user->getUsername())); + $this->logger->warning(sprintf('Username "%s" could not be found.', $user->getUsername())); } return null; diff --git a/Http/Firewall/DigestAuthenticationListener.php b/Http/Firewall/DigestAuthenticationListener.php index 2bc4aa5..7ab3dcf 100644 --- a/Http/Firewall/DigestAuthenticationListener.php +++ b/Http/Firewall/DigestAuthenticationListener.php @@ -14,7 +14,7 @@ namespace Symfony\Component\Security\Http\Firewall; use Symfony\Component\Security\Core\SecurityContextInterface; use Symfony\Component\Security\Core\User\UserProviderInterface; use Symfony\Component\Security\Http\EntryPoint\DigestAuthenticationEntryPoint; -use Symfony\Component\HttpKernel\Log\LoggerInterface; +use Psr\Log\LoggerInterface; use Symfony\Component\HttpKernel\Event\GetResponseEvent; use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; use Symfony\Component\Security\Core\Exception\BadCredentialsException; @@ -54,6 +54,8 @@ class DigestAuthenticationListener implements ListenerInterface * Handles digest authentication. * * @param GetResponseEvent $event A GetResponseEvent instance + * + * @throws AuthenticationServiceException */ public function handle(GetResponseEvent $event) { diff --git a/Http/Firewall/ExceptionListener.php b/Http/Firewall/ExceptionListener.php index f134f9c..0f81d1b 100644 --- a/Http/Firewall/ExceptionListener.php +++ b/Http/Firewall/ExceptionListener.php @@ -23,7 +23,7 @@ use Symfony\Component\Security\Core\Exception\InsufficientAuthenticationExceptio use Symfony\Component\Security\Core\Exception\LogoutException; use Symfony\Component\Security\Http\HttpUtils; use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpKernel\Log\LoggerInterface; +use Psr\Log\LoggerInterface; use Symfony\Component\HttpKernel\HttpKernelInterface; use Symfony\Component\HttpKernel\KernelEvents; use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent; @@ -106,7 +106,9 @@ class ExceptionListener } try { - $response = $this->startAuthentication($request, new InsufficientAuthenticationException('Full authentication is required to access this resource.', $token, 0, $exception)); + $insufficientAuthenticationException = new InsufficientAuthenticationException('Full authentication is required to access this resource.', 0, $exception); + $insufficientAuthenticationException->setToken($token); + $response = $this->startAuthentication($request, $insufficientAuthenticationException); } catch (\Exception $e) { $event->setException($e); @@ -134,7 +136,7 @@ class ExceptionListener } } catch (\Exception $e) { if (null !== $this->logger) { - $this->logger->err(sprintf('Exception thrown when handling an exception (%s: %s)', get_class($e), $e->getMessage())); + $this->logger->error(sprintf('Exception thrown when handling an exception (%s: %s)', get_class($e), $e->getMessage())); } $event->setException(new \RuntimeException('Exception thrown when handling an exception.', 0, $e)); diff --git a/Http/Firewall/LogoutListener.php b/Http/Firewall/LogoutListener.php index 32a0511..ca2f439 100644 --- a/Http/Firewall/LogoutListener.php +++ b/Http/Firewall/LogoutListener.php @@ -75,8 +75,10 @@ class LogoutListener implements ListenerInterface * validate the request. * * @param GetResponseEvent $event A GetResponseEvent instance + * * @throws InvalidCsrfTokenException if the CSRF token is invalid - * @throws RuntimeException if the LogoutSuccessHandlerInterface instance does not return a response + * @throws \RuntimeException if the LogoutSuccessHandlerInterface instance does not return a response + * @throws LogoutException */ public function handle(GetResponseEvent $event) { diff --git a/Http/Firewall/RememberMeListener.php b/Http/Firewall/RememberMeListener.php index 3614f79..5a856e2 100644 --- a/Http/Firewall/RememberMeListener.php +++ b/Http/Firewall/RememberMeListener.php @@ -11,7 +11,7 @@ namespace Symfony\Component\Security\Http\Firewall; -use Symfony\Component\HttpKernel\Log\LoggerInterface; +use Psr\Log\LoggerInterface; use Symfony\Component\HttpKernel\Event\GetResponseEvent; use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface; use Symfony\Component\Security\Core\Exception\AuthenticationException; @@ -82,7 +82,7 @@ class RememberMeListener implements ListenerInterface } } catch (AuthenticationException $failed) { if (null !== $this->logger) { - $this->logger->warn( + $this->logger->warning( 'SecurityContext not populated with remember-me token as the' .' AuthenticationManager rejected the AuthenticationToken returned' .' by the RememberMeServices: '.$failed->getMessage() diff --git a/Http/Firewall/SwitchUserListener.php b/Http/Firewall/SwitchUserListener.php index 7f0aa78..bad6b2b 100644 --- a/Http/Firewall/SwitchUserListener.php +++ b/Http/Firewall/SwitchUserListener.php @@ -16,7 +16,7 @@ use Symfony\Component\Security\Core\SecurityContextInterface; use Symfony\Component\Security\Core\User\UserProviderInterface; use Symfony\Component\Security\Core\User\UserCheckerInterface; use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface; -use Symfony\Component\HttpKernel\Log\LoggerInterface; +use Psr\Log\LoggerInterface; use Symfony\Component\HttpKernel\Event\GetResponseEvent; use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\HttpFoundation\RedirectResponse; @@ -71,6 +71,8 @@ class SwitchUserListener implements ListenerInterface * Handles digest authentication. * * @param GetResponseEvent $event A GetResponseEvent instance + * + * @throws \LogicException */ public function handle(GetResponseEvent $event) { @@ -102,6 +104,9 @@ class SwitchUserListener implements ListenerInterface * @param Request $request A Request instance * * @return TokenInterface|null The new TokenInterface if successfully switched, null otherwise + * + * @throws \LogicException + * @throws AccessDeniedException */ private function attemptSwitchUser(Request $request) { @@ -148,6 +153,8 @@ class SwitchUserListener implements ListenerInterface * @param Request $request A Request instance * * @return TokenInterface The original TokenInterface instance + * + * @throws AuthenticationCredentialsNotFoundException */ private function attemptExitUser(Request $request) { diff --git a/Http/Firewall/UsernamePasswordFormAuthenticationListener.php b/Http/Firewall/UsernamePasswordFormAuthenticationListener.php index 22330a8..81c2b37 100644 --- a/Http/Firewall/UsernamePasswordFormAuthenticationListener.php +++ b/Http/Firewall/UsernamePasswordFormAuthenticationListener.php @@ -13,7 +13,7 @@ namespace Symfony\Component\Security\Http\Firewall; use Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface; use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpKernel\Log\LoggerInterface; +use Psr\Log\LoggerInterface; use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface; use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface; use Symfony\Component\Security\Http\Session\SessionAuthenticationStrategyInterface; @@ -55,7 +55,7 @@ class UsernamePasswordFormAuthenticationListener extends AbstractAuthenticationL */ protected function requiresAuthentication(Request $request) { - if ($this->options['post_only'] && !$request->isMethod('post')) { + if ($this->options['post_only'] && !$request->isMethod('POST')) { return false; } @@ -67,14 +67,6 @@ class UsernamePasswordFormAuthenticationListener extends AbstractAuthenticationL */ protected function attemptAuthentication(Request $request) { - if ($this->options['post_only'] && 'post' !== strtolower($request->getMethod())) { - if (null !== $this->logger) { - $this->logger->debug(sprintf('Authentication method not supported: %s.', $request->getMethod())); - } - - return null; - } - if (null !== $this->csrfProvider) { $csrfToken = $request->get($this->options['csrf_parameter'], null, true); @@ -83,8 +75,13 @@ class UsernamePasswordFormAuthenticationListener extends AbstractAuthenticationL } } - $username = trim($request->get($this->options['username_parameter'], null, true)); - $password = $request->get($this->options['password_parameter'], null, true); + if ($this->options['post_only']) { + $username = trim($request->request->get($this->options['username_parameter'], null, true)); + $password = $request->request->get($this->options['password_parameter'], null, true); + } else { + $username = trim($request->get($this->options['username_parameter'], null, true)); + $password = $request->get($this->options['password_parameter'], null, true); + } $request->getSession()->set(SecurityContextInterface::LAST_USERNAME, $username); diff --git a/Http/Firewall/X509AuthenticationListener.php b/Http/Firewall/X509AuthenticationListener.php index 2f4b78a..0b5a6ae 100644 --- a/Http/Firewall/X509AuthenticationListener.php +++ b/Http/Firewall/X509AuthenticationListener.php @@ -13,7 +13,7 @@ namespace Symfony\Component\Security\Http\Firewall; use Symfony\Component\Security\Core\SecurityContextInterface; use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface; -use Symfony\Component\HttpKernel\Log\LoggerInterface; +use Psr\Log\LoggerInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Security\Core\Exception\BadCredentialsException; use Symfony\Component\EventDispatcher\EventDispatcherInterface; diff --git a/Http/HttpUtils.php b/Http/HttpUtils.php index 7e6679e..0453520 100644 --- a/Http/HttpUtils.php +++ b/Http/HttpUtils.php @@ -35,7 +35,7 @@ class HttpUtils * Constructor. * * @param UrlGeneratorInterface $urlGenerator A UrlGeneratorInterface instance - * @param UrlMatcherInterface|RequestMatcherInterface $matcher The Url or Request matcher + * @param UrlMatcherInterface|RequestMatcherInterface $urlMatcher The Url or Request matcher */ public function __construct(UrlGeneratorInterface $urlGenerator = null, $urlMatcher = null) { @@ -71,8 +71,8 @@ class HttpUtils public function createRequest(Request $request, $path) { $newRequest = $request::create($this->generateUri($request, $path), 'get', array(), $request->cookies->all(), array(), $request->server->all()); - if ($session = $request->getSession()) { - $newRequest->setSession($session); + if ($request->hasSession()) { + $newRequest->setSession($request->getSession()); } if ($request->attributes->has(SecurityContextInterface::AUTHENTICATION_ERROR)) { @@ -136,16 +136,11 @@ class HttpUtils return $request->getUriForPath($path); } - return $this->generateUrl($path, $request->attributes->all(), true); - } - - private function generateUrl($route, array $attributes = array(), $absolute = false) - { if (null === $this->urlGenerator) { throw new \LogicException('You must provide a UrlGeneratorInterface instance to be able to use routes.'); } - $url = $this->urlGenerator->generate($route, $attributes, $absolute); + $url = $this->urlGenerator->generate($path, $request->attributes->all(), UrlGeneratorInterface::ABSOLUTE_URL); // unnecessary query string parameters must be removed from url // (ie. query parameters that are presents in $attributes) diff --git a/Http/RememberMe/AbstractRememberMeServices.php b/Http/RememberMe/AbstractRememberMeServices.php index 4f7c5b9..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,7 +91,9 @@ abstract class AbstractRememberMeServices implements RememberMeServicesInterface * * @param Request $request * - * @return TokenInterface + * @return TokenInterface|null + * + * @throws CookieTheftException */ final public function autoLogin(Request $request) { @@ -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) { @@ -172,6 +176,9 @@ abstract class AbstractRememberMeServices implements RememberMeServicesInterface */ 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); } diff --git a/Http/RememberMe/PersistentTokenBasedRememberMeServices.php b/Http/RememberMe/PersistentTokenBasedRememberMeServices.php index 8944672..6500bfd 100644 --- a/Http/RememberMe/PersistentTokenBasedRememberMeServices.php +++ b/Http/RememberMe/PersistentTokenBasedRememberMeServices.php @@ -19,6 +19,8 @@ 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; +use Psr\Log\LoggerInterface; /** * Concrete implementation of the RememberMeServicesInterface which needs @@ -30,6 +32,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 +64,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 +91,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 +99,7 @@ class PersistentTokenBasedRememberMeServices extends AbstractRememberMeServices } $series = $persistentToken->getSeries(); - $tokenValue = $this->generateRandomValue(); + $tokenValue = base64_encode($this->secureRandom->nextBytes(64)); $this->tokenProvider->updateToken($series, $tokenValue, new \DateTime()); $request->attributes->set(self::COOKIE_ATTR_NAME, new Cookie( @@ -101,8 +121,8 @@ class PersistentTokenBasedRememberMeServices extends AbstractRememberMeServices */ protected function onLoginSuccess(Request $request, Response $response, TokenInterface $token) { - $series = $this->generateRandomValue(); - $tokenValue = $this->generateRandomValue(); + $series = base64_encode($this->secureRandom->nextBytes(64)); + $tokenValue = base64_encode($this->secureRandom->nextBytes(64)); $this->tokenProvider->createNewToken( new PersistentToken( @@ -126,26 +146,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/ResponseListener.php b/Http/RememberMe/ResponseListener.php index 6cbdcb3..03c71c7 100644 --- a/Http/RememberMe/ResponseListener.php +++ b/Http/RememberMe/ResponseListener.php @@ -12,13 +12,15 @@ 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 +class ResponseListener implements EventSubscriberInterface { public function onKernelResponse(FilterResponseEvent $event) { @@ -29,4 +31,9 @@ class ResponseListener $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; |