summaryrefslogtreecommitdiffstats
path: root/Http
diff options
context:
space:
mode:
Diffstat (limited to 'Http')
-rw-r--r--Http/.gitignore3
-rw-r--r--Http/AccessMap.php6
-rw-r--r--Http/Authentication/AuthenticationUtils.php88
-rw-r--r--Http/Authentication/CustomAuthenticationFailureHandler.php45
-rw-r--r--Http/Authentication/CustomAuthenticationSuccessHandler.php49
-rw-r--r--Http/Authentication/DefaultAuthenticationFailureHandler.php44
-rw-r--r--Http/Authentication/DefaultAuthenticationSuccessHandler.php38
-rw-r--r--Http/Authentication/SimpleAuthenticationHandler.php106
-rw-r--r--Http/EntryPoint/DigestAuthenticationEntryPoint.php2
-rw-r--r--Http/Firewall.php28
-rw-r--r--Http/Firewall/AbstractAuthenticationListener.php33
-rw-r--r--Http/Firewall/AbstractPreAuthenticatedListener.php30
-rw-r--r--Http/Firewall/AccessListener.php14
-rw-r--r--Http/Firewall/AnonymousAuthenticationListener.php31
-rw-r--r--Http/Firewall/BasicAuthenticationListener.php20
-rw-r--r--Http/Firewall/ChannelListener.php6
-rw-r--r--Http/Firewall/ContextListener.php57
-rw-r--r--Http/Firewall/DigestAuthenticationListener.php29
-rw-r--r--Http/Firewall/ExceptionListener.php45
-rw-r--r--Http/Firewall/LogoutListener.php42
-rw-r--r--Http/Firewall/RememberMeListener.php42
-rw-r--r--Http/Firewall/RemoteUserAuthenticationListener.php49
-rw-r--r--Http/Firewall/SimpleFormAuthenticationListener.php125
-rw-r--r--Http/Firewall/SimplePreAuthenticationListener.php126
-rw-r--r--Http/Firewall/SwitchUserListener.php27
-rw-r--r--Http/Firewall/UsernamePasswordFormAuthenticationListener.php30
-rw-r--r--Http/Firewall/X509AuthenticationListener.php6
-rw-r--r--Http/HttpUtils.php16
-rw-r--r--Http/LICENSE19
-rw-r--r--Http/Logout/LogoutUrlGenerator.php139
-rw-r--r--Http/README.md23
-rw-r--r--Http/RememberMe/AbstractRememberMeServices.php6
-rw-r--r--Http/RememberMe/PersistentTokenBasedRememberMeServices.php1
-rw-r--r--Http/RememberMe/RememberMeServicesInterface.php4
-rw-r--r--Http/RememberMe/ResponseListener.php3
-rw-r--r--Http/RememberMe/TokenBasedRememberMeServices.php30
-rw-r--r--Http/Session/SessionAuthenticationStrategyInterface.php2
-rw-r--r--Http/Tests/AccessMapTest.php51
-rw-r--r--Http/Tests/Authentication/DefaultAuthenticationFailureHandlerTest.php180
-rw-r--r--Http/Tests/Authentication/DefaultAuthenticationSuccessHandlerTest.php169
-rw-r--r--Http/Tests/Authentication/SimpleAuthenticationHandlerTest.php194
-rw-r--r--Http/Tests/EntryPoint/BasicAuthenticationEntryPointTest.php43
-rw-r--r--Http/Tests/EntryPoint/DigestAuthenticationEntryPointTest.php56
-rw-r--r--Http/Tests/EntryPoint/FormAuthenticationEntryPointTest.php67
-rw-r--r--Http/Tests/EntryPoint/RetryAuthenticationEntryPointTest.php64
-rw-r--r--Http/Tests/Firewall/AbstractPreAuthenticatedListenerTest.php252
-rw-r--r--Http/Tests/Firewall/AccessListenerTest.php208
-rw-r--r--Http/Tests/Firewall/AnonymousAuthenticationListenerTest.php87
-rw-r--r--Http/Tests/Firewall/BasicAuthenticationListenerTest.php249
-rw-r--r--Http/Tests/Firewall/ChannelListenerTest.php180
-rw-r--r--Http/Tests/Firewall/ContextListenerTest.php267
-rw-r--r--Http/Tests/Firewall/DigestDataTest.php181
-rw-r--r--Http/Tests/Firewall/ExceptionListenerTest.php184
-rw-r--r--Http/Tests/Firewall/LogoutListenerTest.php235
-rw-r--r--Http/Tests/Firewall/RememberMeListenerTest.php352
-rw-r--r--Http/Tests/Firewall/RemoteUserAuthenticationListenerTest.php91
-rw-r--r--Http/Tests/Firewall/SimplePreAuthenticationListenerTest.php128
-rw-r--r--Http/Tests/Firewall/SwitchUserListenerTest.php260
-rw-r--r--Http/Tests/Firewall/X509AuthenticationListenerTest.php123
-rw-r--r--Http/Tests/FirewallMapTest.php117
-rw-r--r--Http/Tests/FirewallTest.php108
-rw-r--r--Http/Tests/HttpUtilsTest.php267
-rw-r--r--Http/Tests/Logout/CookieClearingLogoutHandlerTest.php49
-rw-r--r--Http/Tests/Logout/DefaultLogoutSuccessHandlerTest.php34
-rw-r--r--Http/Tests/Logout/SessionLogoutHandlerTest.php40
-rw-r--r--Http/Tests/RememberMe/AbstractRememberMeServicesTest.php311
-rw-r--r--Http/Tests/RememberMe/PersistentTokenBasedRememberMeServicesTest.php330
-rw-r--r--Http/Tests/RememberMe/ResponseListenerTest.php102
-rw-r--r--Http/Tests/RememberMe/TokenBasedRememberMeServicesTest.php285
-rw-r--r--Http/Tests/Session/SessionAuthenticationStrategyTest.php90
-rw-r--r--Http/composer.json43
-rw-r--r--Http/phpunit.xml.dist33
72 files changed, 6538 insertions, 256 deletions
diff --git a/Http/.gitignore b/Http/.gitignore
new file mode 100644
index 0000000..c49a5d8
--- /dev/null
+++ b/Http/.gitignore
@@ -0,0 +1,3 @@
+vendor/
+composer.lock
+phpunit.xml
diff --git a/Http/AccessMap.php b/Http/AccessMap.php
index 14fcbe1..116874a 100644
--- a/Http/AccessMap.php
+++ b/Http/AccessMap.php
@@ -28,12 +28,12 @@ class AccessMap implements AccessMapInterface
* Constructor.
*
* @param RequestMatcherInterface $requestMatcher A RequestMatcherInterface instance
- * @param array $roles An array of roles needed to access the resource
+ * @param array $attributes An array of attributes to pass to the access decision manager (like roles)
* @param string|null $channel The channel to enforce (http, https, or null)
*/
- public function add(RequestMatcherInterface $requestMatcher, array $roles = array(), $channel = null)
+ public function add(RequestMatcherInterface $requestMatcher, array $attributes = array(), $channel = null)
{
- $this->map[] = array($requestMatcher, $roles, $channel);
+ $this->map[] = array($requestMatcher, $attributes, $channel);
}
/**
diff --git a/Http/Authentication/AuthenticationUtils.php b/Http/Authentication/AuthenticationUtils.php
new file mode 100644
index 0000000..4d5c71a
--- /dev/null
+++ b/Http/Authentication/AuthenticationUtils.php
@@ -0,0 +1,88 @@
+<?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\Authentication;
+
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\RequestStack;
+use Symfony\Component\Security\Core\Exception\AuthenticationException;
+use Symfony\Component\Security\Core\Security;
+
+/**
+ * Extracts Security Errors from Request.
+ *
+ * @author Boris Vujicic <boris.vujicic@gmail.com>
+ */
+class AuthenticationUtils
+{
+ /**
+ * @var RequestStack
+ */
+ private $requestStack;
+
+ /**
+ * @param RequestStack $requestStack
+ */
+ public function __construct(RequestStack $requestStack)
+ {
+ $this->requestStack = $requestStack;
+ }
+
+ /**
+ * @param bool $clearSession
+ *
+ * @return AuthenticationException|null
+ */
+ public function getLastAuthenticationError($clearSession = true)
+ {
+ $request = $this->getRequest();
+ $session = $request->getSession();
+ $authenticationException = null;
+
+ if ($request->attributes->has(Security::AUTHENTICATION_ERROR)) {
+ $authenticationException = $request->attributes->get(Security::AUTHENTICATION_ERROR);
+ } elseif ($session !== null && $session->has(Security::AUTHENTICATION_ERROR)) {
+ $authenticationException = $session->get(Security::AUTHENTICATION_ERROR);
+
+ if ($clearSession) {
+ $session->remove(Security::AUTHENTICATION_ERROR);
+ }
+ }
+
+ return $authenticationException;
+ }
+
+ /**
+ * @return string
+ */
+ public function getLastUsername()
+ {
+ $session = $this->getRequest()->getSession();
+
+ return null === $session ? '' : $session->get(Security::LAST_USERNAME);
+ }
+
+ /**
+ * @return Request
+ *
+ * @throws \LogicException
+ */
+ private function getRequest()
+ {
+ $request = $this->requestStack->getCurrentRequest();
+
+ if (null === $request) {
+ throw new \LogicException('Request should exist so it can be processed for error.');
+ }
+
+ return $request;
+ }
+}
diff --git a/Http/Authentication/CustomAuthenticationFailureHandler.php b/Http/Authentication/CustomAuthenticationFailureHandler.php
new file mode 100644
index 0000000..36d4a78
--- /dev/null
+++ b/Http/Authentication/CustomAuthenticationFailureHandler.php
@@ -0,0 +1,45 @@
+<?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\Authentication;
+
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\Security\Core\Exception\AuthenticationException;
+
+/**
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class CustomAuthenticationFailureHandler implements AuthenticationFailureHandlerInterface
+{
+ private $handler;
+
+ /**
+ * Constructor.
+ *
+ * @param AuthenticationFailureHandlerInterface $handler An AuthenticationFailureHandlerInterface instance
+ * @param array $options Options for processing a successful authentication attempt
+ */
+ public function __construct(AuthenticationFailureHandlerInterface $handler, array $options)
+ {
+ $this->handler = $handler;
+ if (method_exists($handler, 'setOptions')) {
+ $this->handler->setOptions($options);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
+ {
+ return $this->handler->onAuthenticationFailure($request, $exception);
+ }
+}
diff --git a/Http/Authentication/CustomAuthenticationSuccessHandler.php b/Http/Authentication/CustomAuthenticationSuccessHandler.php
new file mode 100644
index 0000000..2d1b26e
--- /dev/null
+++ b/Http/Authentication/CustomAuthenticationSuccessHandler.php
@@ -0,0 +1,49 @@
+<?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\Authentication;
+
+use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class CustomAuthenticationSuccessHandler implements AuthenticationSuccessHandlerInterface
+{
+ private $handler;
+
+ /**
+ * Constructor.
+ *
+ * @param AuthenticationSuccessHandlerInterface $handler An AuthenticationSuccessHandlerInterface instance
+ * @param array $options Options for processing a successful authentication attempt
+ * @param string $providerKey The provider key
+ */
+ public function __construct(AuthenticationSuccessHandlerInterface $handler, array $options, $providerKey)
+ {
+ $this->handler = $handler;
+ if (method_exists($handler, 'setOptions')) {
+ $this->handler->setOptions($options);
+ }
+ if (method_exists($handler, 'setProviderKey')) {
+ $this->handler->setProviderKey($providerKey);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function onAuthenticationSuccess(Request $request, TokenInterface $token)
+ {
+ return $this->handler->onAuthenticationSuccess($request, $token);
+ }
+}
diff --git a/Http/Authentication/DefaultAuthenticationFailureHandler.php b/Http/Authentication/DefaultAuthenticationFailureHandler.php
index b3c5c4d..f8004d6 100644
--- a/Http/Authentication/DefaultAuthenticationFailureHandler.php
+++ b/Http/Authentication/DefaultAuthenticationFailureHandler.php
@@ -15,7 +15,7 @@ use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Psr\Log\LoggerInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
-use Symfony\Component\Security\Core\SecurityContextInterface;
+use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Http\HttpUtils;
/**
@@ -34,6 +34,12 @@ class DefaultAuthenticationFailureHandler implements AuthenticationFailureHandle
protected $httpUtils;
protected $logger;
protected $options;
+ protected $defaultOptions = array(
+ 'failure_path' => null,
+ 'failure_forward' => false,
+ 'login_path' => '/login',
+ 'failure_path_parameter' => '_failure_path',
+ );
/**
* Constructor.
@@ -43,18 +49,32 @@ class DefaultAuthenticationFailureHandler implements AuthenticationFailureHandle
* @param array $options Options for processing a failed authentication attempt.
* @param LoggerInterface $logger Optional logger
*/
- public function __construct(HttpKernelInterface $httpKernel, HttpUtils $httpUtils, array $options, LoggerInterface $logger = null)
+ public function __construct(HttpKernelInterface $httpKernel, HttpUtils $httpUtils, array $options = array(), LoggerInterface $logger = null)
{
$this->httpKernel = $httpKernel;
$this->httpUtils = $httpUtils;
$this->logger = $logger;
+ $this->setOptions($options);
+ }
- $this->options = array_merge(array(
- 'failure_path' => null,
- 'failure_forward' => false,
- 'login_path' => '/login',
- 'failure_path_parameter' => '_failure_path',
- ), $options);
+ /**
+ * Gets the options.
+ *
+ * @return array An array of options
+ */
+ public function getOptions()
+ {
+ return $this->options;
+ }
+
+ /**
+ * Sets the options.
+ *
+ * @param array $options An array of options
+ */
+ public function setOptions(array $options)
+ {
+ $this->options = array_merge($this->defaultOptions, $options);
}
/**
@@ -72,20 +92,20 @@ class DefaultAuthenticationFailureHandler implements AuthenticationFailureHandle
if ($this->options['failure_forward']) {
if (null !== $this->logger) {
- $this->logger->debug(sprintf('Forwarding to %s', $this->options['failure_path']));
+ $this->logger->debug('Authentication failure, forward triggered.', array('failure_path' => $this->options['failure_path']));
}
$subRequest = $this->httpUtils->createRequest($request, $this->options['failure_path']);
- $subRequest->attributes->set(SecurityContextInterface::AUTHENTICATION_ERROR, $exception);
+ $subRequest->attributes->set(Security::AUTHENTICATION_ERROR, $exception);
return $this->httpKernel->handle($subRequest, HttpKernelInterface::SUB_REQUEST);
}
if (null !== $this->logger) {
- $this->logger->debug(sprintf('Redirecting to %s', $this->options['failure_path']));
+ $this->logger->debug('Authentication failure, redirect triggered.', array('failure_path' => $this->options['failure_path']));
}
- $request->getSession()->set(SecurityContextInterface::AUTHENTICATION_ERROR, $exception);
+ $request->getSession()->set(Security::AUTHENTICATION_ERROR, $exception);
return $this->httpUtils->createRedirectResponse($request, $this->options['failure_path']);
}
diff --git a/Http/Authentication/DefaultAuthenticationSuccessHandler.php b/Http/Authentication/DefaultAuthenticationSuccessHandler.php
index 591a28d..5fa7071 100644
--- a/Http/Authentication/DefaultAuthenticationSuccessHandler.php
+++ b/Http/Authentication/DefaultAuthenticationSuccessHandler.php
@@ -27,6 +27,13 @@ class DefaultAuthenticationSuccessHandler implements AuthenticationSuccessHandle
protected $httpUtils;
protected $options;
protected $providerKey;
+ protected $defaultOptions = array(
+ 'always_use_default_target_path' => false,
+ 'default_target_path' => '/',
+ 'login_path' => '/login',
+ 'target_path_parameter' => '_target_path',
+ 'use_referer' => false,
+ );
/**
* Constructor.
@@ -34,17 +41,10 @@ class DefaultAuthenticationSuccessHandler implements AuthenticationSuccessHandle
* @param HttpUtils $httpUtils
* @param array $options Options for processing a successful authentication attempt.
*/
- public function __construct(HttpUtils $httpUtils, array $options)
+ public function __construct(HttpUtils $httpUtils, array $options = array())
{
$this->httpUtils = $httpUtils;
-
- $this->options = array_merge(array(
- 'always_use_default_target_path' => false,
- 'default_target_path' => '/',
- 'login_path' => '/login',
- 'target_path_parameter' => '_target_path',
- 'use_referer' => false,
- ), $options);
+ $this->setOptions($options);
}
/**
@@ -56,6 +56,26 @@ class DefaultAuthenticationSuccessHandler implements AuthenticationSuccessHandle
}
/**
+ * Gets the options.
+ *
+ * @return array An array of options
+ */
+ public function getOptions()
+ {
+ return $this->options;
+ }
+
+ /**
+ * Sets the options.
+ *
+ * @param array $options An array of options
+ */
+ public function setOptions(array $options)
+ {
+ $this->options = array_merge($this->defaultOptions, $options);
+ }
+
+ /**
* Get the provider key.
*
* @return string
diff --git a/Http/Authentication/SimpleAuthenticationHandler.php b/Http/Authentication/SimpleAuthenticationHandler.php
new file mode 100644
index 0000000..c5c43f2
--- /dev/null
+++ b/Http/Authentication/SimpleAuthenticationHandler.php
@@ -0,0 +1,106 @@
+<?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\Authentication;
+
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+use Psr\Log\LoggerInterface;
+use Symfony\Component\Security\Core\Exception\AuthenticationException;
+use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
+use Symfony\Component\Security\Core\Authentication\SimpleAuthenticatorInterface;
+
+/**
+ * Class to proxy authentication success/failure handlers.
+ *
+ * Events are sent to the SimpleAuthenticatorInterface if it implements
+ * the right interface, otherwise (or if it fails to return a Response)
+ * the default handlers are triggered.
+ *
+ * @author Jordi Boggiano <j.boggiano@seld.be>
+ */
+class SimpleAuthenticationHandler implements AuthenticationFailureHandlerInterface, AuthenticationSuccessHandlerInterface
+{
+ protected $successHandler;
+ protected $failureHandler;
+ protected $simpleAuthenticator;
+ protected $logger;
+
+ /**
+ * Constructor.
+ *
+ * @param SimpleAuthenticatorInterface $authenticator SimpleAuthenticatorInterface instance
+ * @param AuthenticationSuccessHandlerInterface $successHandler Default success handler
+ * @param AuthenticationFailureHandlerInterface $failureHandler Default failure handler
+ * @param LoggerInterface $logger Optional logger
+ */
+ public function __construct(SimpleAuthenticatorInterface $authenticator, AuthenticationSuccessHandlerInterface $successHandler, AuthenticationFailureHandlerInterface $failureHandler, LoggerInterface $logger = null)
+ {
+ $this->simpleAuthenticator = $authenticator;
+ $this->successHandler = $successHandler;
+ $this->failureHandler = $failureHandler;
+ $this->logger = $logger;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function onAuthenticationSuccess(Request $request, TokenInterface $token)
+ {
+ if ($this->simpleAuthenticator instanceof AuthenticationSuccessHandlerInterface) {
+ if ($this->logger) {
+ $this->logger->debug('Selected an authentication success handler.', array('handler' => get_class($this->simpleAuthenticator)));
+ }
+
+ $response = $this->simpleAuthenticator->onAuthenticationSuccess($request, $token);
+ if ($response instanceof Response) {
+ return $response;
+ }
+
+ if (null !== $response) {
+ throw new \UnexpectedValueException(sprintf('The %s::onAuthenticationSuccess method must return null to use the default success handler, or a Response object', get_class($this->simpleAuthenticator)));
+ }
+ }
+
+ if ($this->logger) {
+ $this->logger->debug('Fallback to the default authentication success handler.');
+ }
+
+ return $this->successHandler->onAuthenticationSuccess($request, $token);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
+ {
+ if ($this->simpleAuthenticator instanceof AuthenticationFailureHandlerInterface) {
+ if ($this->logger) {
+ $this->logger->debug('Selected an authentication failure handler.', array('handler' => get_class($this->simpleAuthenticator)));
+ }
+
+ $response = $this->simpleAuthenticator->onAuthenticationFailure($request, $exception);
+ if ($response instanceof Response) {
+ return $response;
+ }
+
+ if (null !== $response) {
+ throw new \UnexpectedValueException(sprintf('The %s::onAuthenticationFailure method must return null to use the default failure handler, or a Response object', get_class($this->simpleAuthenticator)));
+ }
+ }
+
+ if ($this->logger) {
+ $this->logger->debug('Fallback to the default authentication failure handler.');
+ }
+
+ return $this->failureHandler->onAuthenticationFailure($request, $exception);
+ }
+}
diff --git a/Http/EntryPoint/DigestAuthenticationEntryPoint.php b/Http/EntryPoint/DigestAuthenticationEntryPoint.php
index 5a7aa1a..89f80ad 100644
--- a/Http/EntryPoint/DigestAuthenticationEntryPoint.php
+++ b/Http/EntryPoint/DigestAuthenticationEntryPoint.php
@@ -54,7 +54,7 @@ class DigestAuthenticationEntryPoint implements AuthenticationEntryPointInterfac
}
if (null !== $this->logger) {
- $this->logger->debug(sprintf('WWW-Authenticate header sent to user agent: "%s"', $authenticateHeader));
+ $this->logger->debug('WWW-Authenticate header sent.', array('header' => $authenticateHeader));
}
$response = new Response();
diff --git a/Http/Firewall.php b/Http/Firewall.php
index 7499c6f..7bad47a 100644
--- a/Http/Firewall.php
+++ b/Http/Firewall.php
@@ -11,9 +11,9 @@
namespace Symfony\Component\Security\Http;
-use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
+use Symfony\Component\HttpKernel\Event\FinishRequestEvent;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
@@ -31,6 +31,7 @@ class Firewall implements EventSubscriberInterface
{
private $map;
private $dispatcher;
+ private $exceptionListeners;
/**
* Constructor.
@@ -42,6 +43,7 @@ class Firewall implements EventSubscriberInterface
{
$this->map = $map;
$this->dispatcher = $dispatcher;
+ $this->exceptionListeners = new \SplObjectStorage();
}
/**
@@ -51,14 +53,15 @@ class Firewall implements EventSubscriberInterface
*/
public function onKernelRequest(GetResponseEvent $event)
{
- if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) {
+ if (!$event->isMasterRequest()) {
return;
}
// register listeners for this firewall
- list($listeners, $exception) = $this->map->getListeners($event->getRequest());
- if (null !== $exception) {
- $exception->register($this->dispatcher);
+ list($listeners, $exceptionListener) = $this->map->getListeners($event->getRequest());
+ if (null !== $exceptionListener) {
+ $this->exceptionListeners[$event->getRequest()] = $exceptionListener;
+ $exceptionListener->register($this->dispatcher);
}
// initiate the listener chain
@@ -71,11 +74,24 @@ class Firewall implements EventSubscriberInterface
}
}
+ public function onKernelFinishRequest(FinishRequestEvent $event)
+ {
+ $request = $event->getRequest();
+
+ if (isset($this->exceptionListeners[$request])) {
+ $this->exceptionListeners[$request]->unregister($this->dispatcher);
+ unset($this->exceptionListeners[$request]);
+ }
+ }
+
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents()
{
- return array(KernelEvents::REQUEST => array('onKernelRequest', 8));
+ return array(
+ KernelEvents::REQUEST => array('onKernelRequest', 8),
+ KernelEvents::FINISH_REQUEST => 'onKernelFinishRequest',
+ );
}
}
diff --git a/Http/Firewall/AbstractAuthenticationListener.php b/Http/Firewall/AbstractAuthenticationListener.php
index f71089f..09a4f55 100644
--- a/Http/Firewall/AbstractAuthenticationListener.php
+++ b/Http/Firewall/AbstractAuthenticationListener.php
@@ -15,8 +15,9 @@ use Symfony\Component\Security\Http\Session\SessionAuthenticationStrategyInterfa
use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface;
use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface;
use Symfony\Component\Security\Http\RememberMe\RememberMeServicesInterface;
-use Symfony\Component\Security\Core\SecurityContextInterface;
+use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
+use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Exception\SessionUnavailableException;
@@ -55,7 +56,7 @@ abstract class AbstractAuthenticationListener implements ListenerInterface
protected $providerKey;
protected $httpUtils;
- private $securityContext;
+ private $tokenStorage;
private $sessionStrategy;
private $dispatcher;
private $successHandler;
@@ -65,7 +66,7 @@ abstract class AbstractAuthenticationListener implements ListenerInterface
/**
* Constructor.
*
- * @param SecurityContextInterface $securityContext A SecurityContext instance
+ * @param TokenStorageInterface $tokenStorage A TokenStorageInterface instance
* @param AuthenticationManagerInterface $authenticationManager An AuthenticationManagerInterface instance
* @param SessionAuthenticationStrategyInterface $sessionStrategy
* @param HttpUtils $httpUtils An HttpUtilsInterface instance
@@ -79,13 +80,13 @@ abstract class AbstractAuthenticationListener implements ListenerInterface
*
* @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)
+ public function __construct(TokenStorageInterface $tokenStorage, AuthenticationManagerInterface $authenticationManager, SessionAuthenticationStrategyInterface $sessionStrategy, HttpUtils $httpUtils, $providerKey, AuthenticationSuccessHandlerInterface $successHandler, AuthenticationFailureHandlerInterface $failureHandler, array $options = array(), LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null)
{
if (empty($providerKey)) {
throw new \InvalidArgumentException('$providerKey must not be empty.');
}
- $this->securityContext = $securityContext;
+ $this->tokenStorage = $tokenStorage;
$this->authenticationManager = $authenticationManager;
$this->sessionStrategy = $sessionStrategy;
$this->providerKey = $providerKey;
@@ -149,14 +150,14 @@ abstract class AbstractAuthenticationListener implements ListenerInterface
if ($returnValue instanceof TokenInterface) {
$this->sessionStrategy->onAuthentication($request, $returnValue);
- $response = $this->onSuccess($event, $request, $returnValue);
+ $response = $this->onSuccess($request, $returnValue);
} elseif ($returnValue instanceof Response) {
$response = $returnValue;
} else {
throw new \RuntimeException('attemptAuthentication() must either return a Response, an implementation of TokenInterface, or null.');
}
} catch (AuthenticationException $e) {
- $response = $this->onFailure($event, $request, $e);
+ $response = $this->onFailure($request, $e);
}
$event->setResponse($response);
@@ -189,15 +190,15 @@ abstract class AbstractAuthenticationListener implements ListenerInterface
*/
abstract protected function attemptAuthentication(Request $request);
- private function onFailure(GetResponseEvent $event, Request $request, AuthenticationException $failed)
+ private function onFailure(Request $request, AuthenticationException $failed)
{
if (null !== $this->logger) {
- $this->logger->info(sprintf('Authentication request failed: %s', $failed->getMessage()));
+ $this->logger->info('Authentication request failed.', array('exception' => $failed));
}
- $token = $this->securityContext->getToken();
+ $token = $this->tokenStorage->getToken();
if ($token instanceof UsernamePasswordToken && $this->providerKey === $token->getProviderKey()) {
- $this->securityContext->setToken(null);
+ $this->tokenStorage->setToken(null);
}
$response = $this->failureHandler->onAuthenticationFailure($request, $failed);
@@ -209,17 +210,17 @@ abstract class AbstractAuthenticationListener implements ListenerInterface
return $response;
}
- private function onSuccess(GetResponseEvent $event, Request $request, TokenInterface $token)
+ private function onSuccess(Request $request, TokenInterface $token)
{
if (null !== $this->logger) {
- $this->logger->info(sprintf('User "%s" has been authenticated successfully', $token->getUsername()));
+ $this->logger->info('User has been authenticated successfully.', array('username' => $token->getUsername()));
}
- $this->securityContext->setToken($token);
+ $this->tokenStorage->setToken($token);
$session = $request->getSession();
- $session->remove(SecurityContextInterface::AUTHENTICATION_ERROR);
- $session->remove(SecurityContextInterface::LAST_USERNAME);
+ $session->remove(Security::AUTHENTICATION_ERROR);
+ $session->remove(Security::LAST_USERNAME);
if (null !== $this->dispatcher) {
$loginEvent = new InteractiveLoginEvent($request, $token);
diff --git a/Http/Firewall/AbstractPreAuthenticatedListener.php b/Http/Firewall/AbstractPreAuthenticatedListener.php
index 9973683..b793310 100644
--- a/Http/Firewall/AbstractPreAuthenticatedListener.php
+++ b/Http/Firewall/AbstractPreAuthenticatedListener.php
@@ -11,9 +11,9 @@
namespace Symfony\Component\Security\Http\Firewall;
-use Symfony\Component\Security\Core\SecurityContextInterface;
use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
use Symfony\Component\Security\Core\Authentication\Token\PreAuthenticatedToken;
+use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
use Symfony\Component\Security\Http\SecurityEvents;
@@ -33,14 +33,14 @@ use Symfony\Component\Security\Core\Exception\BadCredentialsException;
abstract class AbstractPreAuthenticatedListener implements ListenerInterface
{
protected $logger;
- private $securityContext;
+ private $tokenStorage;
private $authenticationManager;
private $providerKey;
private $dispatcher;
- public function __construct(SecurityContextInterface $securityContext, AuthenticationManagerInterface $authenticationManager, $providerKey, LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null)
+ public function __construct(TokenStorageInterface $tokenStorage, AuthenticationManagerInterface $authenticationManager, $providerKey, LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null)
{
- $this->securityContext = $securityContext;
+ $this->tokenStorage = $tokenStorage;
$this->authenticationManager = $authenticationManager;
$this->providerKey = $providerKey;
$this->logger = $logger;
@@ -56,10 +56,6 @@ abstract class AbstractPreAuthenticatedListener implements ListenerInterface
{
$request = $event->getRequest();
- if (null !== $this->logger) {
- $this->logger->debug(sprintf('Checking secure context token: %s', $this->securityContext->getToken()));
- }
-
try {
list($user, $credentials) = $this->getPreAuthenticatedData($request);
} catch (BadCredentialsException $e) {
@@ -68,23 +64,27 @@ abstract class AbstractPreAuthenticatedListener implements ListenerInterface
return;
}
- if (null !== $token = $this->securityContext->getToken()) {
+ if (null !== $this->logger) {
+ $this->logger->debug('Checking current security token.', array('token' => (string) $this->tokenStorage->getToken()));
+ }
+
+ if (null !== $token = $this->tokenStorage->getToken()) {
if ($token instanceof PreAuthenticatedToken && $this->providerKey == $token->getProviderKey() && $token->isAuthenticated() && $token->getUsername() === $user) {
return;
}
}
if (null !== $this->logger) {
- $this->logger->debug(sprintf('Trying to pre-authenticate user "%s"', $user));
+ $this->logger->debug('Trying to pre-authenticate user.', array('username' => (string) $user));
}
try {
$token = $this->authenticationManager->authenticate(new PreAuthenticatedToken($user, $credentials, $this->providerKey));
if (null !== $this->logger) {
- $this->logger->info(sprintf('Authentication success: %s', $token));
+ $this->logger->info('Pre-authentication successful.', array('token' => (string) $token));
}
- $this->securityContext->setToken($token);
+ $this->tokenStorage->setToken($token);
if (null !== $this->dispatcher) {
$loginEvent = new InteractiveLoginEvent($request, $token);
@@ -102,12 +102,12 @@ abstract class AbstractPreAuthenticatedListener implements ListenerInterface
*/
private function clearToken(AuthenticationException $exception)
{
- $token = $this->securityContext->getToken();
+ $token = $this->tokenStorage->getToken();
if ($token instanceof PreAuthenticatedToken && $this->providerKey === $token->getProviderKey()) {
- $this->securityContext->setToken(null);
+ $this->tokenStorage->setToken(null);
if (null !== $this->logger) {
- $this->logger->info(sprintf('Cleared security context due to exception: %s', $exception->getMessage()));
+ $this->logger->info('Cleared security token due to an exception.', array('exception' => $exception));
}
}
}
diff --git a/Http/Firewall/AccessListener.php b/Http/Firewall/AccessListener.php
index ecb6a09..c234317 100644
--- a/Http/Firewall/AccessListener.php
+++ b/Http/Firewall/AccessListener.php
@@ -11,10 +11,10 @@
namespace Symfony\Component\Security\Http\Firewall;
-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\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\Security\Core\Exception\AuthenticationCredentialsNotFoundException;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
@@ -26,14 +26,14 @@ use Symfony\Component\Security\Core\Exception\AccessDeniedException;
*/
class AccessListener implements ListenerInterface
{
- private $context;
+ private $tokenStorage;
private $accessDecisionManager;
private $map;
private $authManager;
- public function __construct(SecurityContextInterface $context, AccessDecisionManagerInterface $accessDecisionManager, AccessMapInterface $map, AuthenticationManagerInterface $authManager)
+ public function __construct(TokenStorageInterface $tokenStorage, AccessDecisionManagerInterface $accessDecisionManager, AccessMapInterface $map, AuthenticationManagerInterface $authManager)
{
- $this->context = $context;
+ $this->tokenStorage = $tokenStorage;
$this->accessDecisionManager = $accessDecisionManager;
$this->map = $map;
$this->authManager = $authManager;
@@ -49,8 +49,8 @@ class AccessListener implements ListenerInterface
*/
public function handle(GetResponseEvent $event)
{
- if (null === $token = $this->context->getToken()) {
- throw new AuthenticationCredentialsNotFoundException('A Token was not found in the SecurityContext.');
+ if (null === $token = $this->tokenStorage->getToken()) {
+ throw new AuthenticationCredentialsNotFoundException('A Token was not found in the TokenStorage.');
}
$request = $event->getRequest();
@@ -63,7 +63,7 @@ class AccessListener implements ListenerInterface
if (!$token->isAuthenticated()) {
$token = $this->authManager->authenticate($token);
- $this->context->setToken($token);
+ $this->tokenStorage->setToken($token);
}
if (!$this->accessDecisionManager->decide($token, $attributes, $request)) {
diff --git a/Http/Firewall/AnonymousAuthenticationListener.php b/Http/Firewall/AnonymousAuthenticationListener.php
index 446dfec..f7feee8 100644
--- a/Http/Firewall/AnonymousAuthenticationListener.php
+++ b/Http/Firewall/AnonymousAuthenticationListener.php
@@ -11,7 +11,9 @@
namespace Symfony\Component\Security\Http\Firewall;
-use Symfony\Component\Security\Core\SecurityContextInterface;
+use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
+use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
+use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken;
@@ -24,14 +26,16 @@ use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken;
*/
class AnonymousAuthenticationListener implements ListenerInterface
{
- private $context;
+ private $tokenStorage;
private $key;
+ private $authenticationManager;
private $logger;
- public function __construct(SecurityContextInterface $context, $key, LoggerInterface $logger = null)
+ public function __construct(TokenStorageInterface $tokenStorage, $key, LoggerInterface $logger = null, AuthenticationManagerInterface $authenticationManager = null)
{
- $this->context = $context;
+ $this->tokenStorage = $tokenStorage;
$this->key = $key;
+ $this->authenticationManager = $authenticationManager;
$this->logger = $logger;
}
@@ -42,14 +46,25 @@ class AnonymousAuthenticationListener implements ListenerInterface
*/
public function handle(GetResponseEvent $event)
{
- if (null !== $this->context->getToken()) {
+ if (null !== $this->tokenStorage->getToken()) {
return;
}
- $this->context->setToken(new AnonymousToken($this->key, 'anon.', array()));
+ try {
+ $token = new AnonymousToken($this->key, 'anon.', array());
+ if (null !== $this->authenticationManager) {
+ $token = $this->authenticationManager->authenticate($token);
+ }
- if (null !== $this->logger) {
- $this->logger->info('Populated SecurityContext with an anonymous Token');
+ $this->tokenStorage->setToken($token);
+
+ if (null !== $this->logger) {
+ $this->logger->info('Populated the TokenStorage with an anonymous Token.');
+ }
+ } catch (AuthenticationException $failed) {
+ if (null !== $this->logger) {
+ $this->logger->info('Anonymous authentication failed.', array('exception' => $failed));
+ }
}
}
}
diff --git a/Http/Firewall/BasicAuthenticationListener.php b/Http/Firewall/BasicAuthenticationListener.php
index eed9838..ebe96ea 100644
--- a/Http/Firewall/BasicAuthenticationListener.php
+++ b/Http/Firewall/BasicAuthenticationListener.php
@@ -11,8 +11,8 @@
namespace Symfony\Component\Security\Http\Firewall;
-use Symfony\Component\Security\Core\SecurityContextInterface;
use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
+use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
@@ -26,20 +26,20 @@ use Symfony\Component\Security\Core\Exception\AuthenticationException;
*/
class BasicAuthenticationListener implements ListenerInterface
{
- private $securityContext;
+ private $tokenStorage;
private $authenticationManager;
private $providerKey;
private $authenticationEntryPoint;
private $logger;
private $ignoreFailure;
- public function __construct(SecurityContextInterface $securityContext, AuthenticationManagerInterface $authenticationManager, $providerKey, AuthenticationEntryPointInterface $authenticationEntryPoint, LoggerInterface $logger = null)
+ public function __construct(TokenStorageInterface $tokenStorage, AuthenticationManagerInterface $authenticationManager, $providerKey, AuthenticationEntryPointInterface $authenticationEntryPoint, LoggerInterface $logger = null)
{
if (empty($providerKey)) {
throw new \InvalidArgumentException('$providerKey must not be empty.');
}
- $this->securityContext = $securityContext;
+ $this->tokenStorage = $tokenStorage;
$this->authenticationManager = $authenticationManager;
$this->providerKey = $providerKey;
$this->authenticationEntryPoint = $authenticationEntryPoint;
@@ -60,27 +60,27 @@ class BasicAuthenticationListener implements ListenerInterface
return;
}
- if (null !== $token = $this->securityContext->getToken()) {
+ if (null !== $token = $this->tokenStorage->getToken()) {
if ($token instanceof UsernamePasswordToken && $token->isAuthenticated() && $token->getUsername() === $username) {
return;
}
}
if (null !== $this->logger) {
- $this->logger->info(sprintf('Basic Authentication Authorization header found for user "%s"', $username));
+ $this->logger->info('Basic authentication Authorization header found for user.', array('username' => $username));
}
try {
$token = $this->authenticationManager->authenticate(new UsernamePasswordToken($username, $request->headers->get('PHP_AUTH_PW'), $this->providerKey));
- $this->securityContext->setToken($token);
+ $this->tokenStorage->setToken($token);
} catch (AuthenticationException $e) {
- $token = $this->securityContext->getToken();
+ $token = $this->tokenStorage->getToken();
if ($token instanceof UsernamePasswordToken && $this->providerKey === $token->getProviderKey()) {
- $this->securityContext->setToken(null);
+ $this->tokenStorage->setToken(null);
}
if (null !== $this->logger) {
- $this->logger->info(sprintf('Authentication request failed for user "%s": %s', $username, $e->getMessage()));
+ $this->logger->info('Basic authentication failed for user.', array('username' => $username, 'exception' => $e));
}
if ($this->ignoreFailure) {
diff --git a/Http/Firewall/ChannelListener.php b/Http/Firewall/ChannelListener.php
index 9e4a6ee..637a7f5 100644
--- a/Http/Firewall/ChannelListener.php
+++ b/Http/Firewall/ChannelListener.php
@@ -44,11 +44,11 @@ class ChannelListener implements ListenerInterface
{
$request = $event->getRequest();
- list($attributes, $channel) = $this->map->getPatterns($request);
+ list(, $channel) = $this->map->getPatterns($request);
if ('https' === $channel && !$request->isSecure()) {
if (null !== $this->logger) {
- $this->logger->info('Redirecting to HTTPS');
+ $this->logger->info('Redirecting to HTTPS.');
}
$response = $this->authenticationEntryPoint->start($request);
@@ -60,7 +60,7 @@ class ChannelListener implements ListenerInterface
if ('http' === $channel && $request->isSecure()) {
if (null !== $this->logger) {
- $this->logger->info('Redirecting to HTTP');
+ $this->logger->info('Redirecting to HTTP.');
}
$response = $this->authenticationEntryPoint->start($request);
diff --git a/Http/Firewall/ContextListener.php b/Http/Firewall/ContextListener.php
index 43ad31d..9ac37cd 100644
--- a/Http/Firewall/ContextListener.php
+++ b/Http/Firewall/ContextListener.php
@@ -11,16 +11,15 @@
namespace Symfony\Component\Security\Http\Firewall;
-use Symfony\Component\HttpKernel\HttpKernelInterface;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken;
+use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
-use Symfony\Component\Security\Core\SecurityContextInterface;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
@@ -33,14 +32,15 @@ use Symfony\Component\EventDispatcher\EventDispatcherInterface;
*/
class ContextListener implements ListenerInterface
{
- private $context;
+ private $tokenStorage;
private $contextKey;
+ private $sessionKey;
private $logger;
private $userProviders;
private $dispatcher;
private $registered;
- public function __construct(SecurityContextInterface $context, array $userProviders, $contextKey, LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null)
+ public function __construct(TokenStorageInterface $tokenStorage, array $userProviders, $contextKey, LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null)
{
if (empty($contextKey)) {
throw new \InvalidArgumentException('$contextKey must not be empty.');
@@ -52,21 +52,22 @@ class ContextListener implements ListenerInterface
}
}
- $this->context = $context;
+ $this->tokenStorage = $tokenStorage;
$this->userProviders = $userProviders;
$this->contextKey = $contextKey;
+ $this->sessionKey = '_security_'.$contextKey;
$this->logger = $logger;
$this->dispatcher = $dispatcher;
}
/**
- * Reads the SecurityContext from the session.
+ * Reads the Security Token from the session.
*
* @param GetResponseEvent $event A GetResponseEvent instance
*/
public function handle(GetResponseEvent $event)
{
- if (!$this->registered && null !== $this->dispatcher && HttpKernelInterface::MASTER_REQUEST === $event->getRequestType()) {
+ if (!$this->registered && null !== $this->dispatcher && $event->isMasterRequest()) {
$this->dispatcher->addListener(KernelEvents::RESPONSE, array($this, 'onKernelResponse'));
$this->registered = true;
}
@@ -74,8 +75,8 @@ class ContextListener implements ListenerInterface
$request = $event->getRequest();
$session = $request->hasPreviousSession() ? $request->getSession() : null;
- if (null === $session || null === $token = $session->get('_security_'.$this->contextKey)) {
- $this->context->setToken(null);
+ if (null === $session || null === $token = $session->get($this->sessionKey)) {
+ $this->tokenStorage->setToken(null);
return;
}
@@ -83,30 +84,30 @@ class ContextListener implements ListenerInterface
$token = unserialize($token);
if (null !== $this->logger) {
- $this->logger->debug('Read SecurityContext from the session');
+ $this->logger->debug('Read existing security token from the session.', array('key' => $this->sessionKey));
}
if ($token instanceof TokenInterface) {
$token = $this->refreshUser($token);
} elseif (null !== $token) {
if (null !== $this->logger) {
- $this->logger->warning(sprintf('Session includes a "%s" where a security token is expected', is_object($token) ? get_class($token) : gettype($token)));
+ $this->logger->warning('Expected a security token from the session, got something else.', array('key' => $this->sessionKey, 'received' => $token));
}
$token = null;
}
- $this->context->setToken($token);
+ $this->tokenStorage->setToken($token);
}
/**
- * Writes the SecurityContext to the session.
+ * Writes the security token into the session.
*
* @param FilterResponseEvent $event A FilterResponseEvent instance
*/
public function onKernelResponse(FilterResponseEvent $event)
{
- if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) {
+ if (!$event->isMasterRequest()) {
return;
}
@@ -117,23 +118,19 @@ class ContextListener implements ListenerInterface
$this->dispatcher->removeListener(KernelEvents::RESPONSE, array($this, 'onKernelResponse'));
$this->registered = false;
- if (null !== $this->logger) {
- $this->logger->debug('Write SecurityContext in the session');
- }
-
$request = $event->getRequest();
$session = $request->getSession();
- if (null === $session) {
- return;
- }
-
- if ((null === $token = $this->context->getToken()) || ($token instanceof AnonymousToken)) {
+ if ((null === $token = $this->tokenStorage->getToken()) || ($token instanceof AnonymousToken)) {
if ($request->hasPreviousSession()) {
- $session->remove('_security_'.$this->contextKey);
+ $session->remove($this->sessionKey);
}
} else {
- $session->set('_security_'.$this->contextKey, serialize($token));
+ $session->set($this->sessionKey, serialize($token));
+
+ if (null !== $this->logger) {
+ $this->logger->debug('Stored the security token in the session.', array('key' => $this->sessionKey));
+ }
}
}
@@ -146,24 +143,20 @@ class ContextListener implements ListenerInterface
*
* @throws \RuntimeException
*/
- private function refreshUser(TokenInterface $token)
+ protected function refreshUser(TokenInterface $token)
{
$user = $token->getUser();
if (!$user instanceof UserInterface) {
return $token;
}
- if (null !== $this->logger) {
- $this->logger->debug(sprintf('Reloading user from user provider.'));
- }
-
foreach ($this->userProviders as $provider) {
try {
$refreshedUser = $provider->refreshUser($user);
$token->setUser($refreshedUser);
if (null !== $this->logger) {
- $this->logger->debug(sprintf('Username "%s" was reloaded from user provider.', $refreshedUser->getUsername()));
+ $this->logger->debug('User was reloaded from a user provider.', array('username' => $refreshedUser->getUsername(), 'provider' => get_class($provider)));
}
return $token;
@@ -171,7 +164,7 @@ class ContextListener implements ListenerInterface
// let's try the next user provider
} catch (UsernameNotFoundException $e) {
if (null !== $this->logger) {
- $this->logger->warning(sprintf('Username "%s" could not be found.', $e->getUsername()));
+ $this->logger->warning('Username could not be found in the selected user provider.', array('username' => $e->getUsername(), 'provider' => get_class($provider)));
}
return;
diff --git a/Http/Firewall/DigestAuthenticationListener.php b/Http/Firewall/DigestAuthenticationListener.php
index a88250b..15b71ef 100644
--- a/Http/Firewall/DigestAuthenticationListener.php
+++ b/Http/Firewall/DigestAuthenticationListener.php
@@ -11,12 +11,12 @@
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 Psr\Log\LoggerInterface;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
+use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
use Symfony\Component\Security\Core\Exception\AuthenticationServiceException;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
@@ -31,19 +31,19 @@ use Symfony\Component\Security\Core\Exception\AuthenticationException;
*/
class DigestAuthenticationListener implements ListenerInterface
{
- private $securityContext;
+ private $tokenStorage;
private $provider;
private $providerKey;
private $authenticationEntryPoint;
private $logger;
- public function __construct(SecurityContextInterface $securityContext, UserProviderInterface $provider, $providerKey, DigestAuthenticationEntryPoint $authenticationEntryPoint, LoggerInterface $logger = null)
+ public function __construct(TokenStorageInterface $tokenStorage, UserProviderInterface $provider, $providerKey, DigestAuthenticationEntryPoint $authenticationEntryPoint, LoggerInterface $logger = null)
{
if (empty($providerKey)) {
throw new \InvalidArgumentException('$providerKey must not be empty.');
}
- $this->securityContext = $securityContext;
+ $this->tokenStorage = $tokenStorage;
$this->provider = $provider;
$this->providerKey = $providerKey;
$this->authenticationEntryPoint = $authenticationEntryPoint;
@@ -67,14 +67,14 @@ class DigestAuthenticationListener implements ListenerInterface
$digestAuth = new DigestData($header);
- if (null !== $token = $this->securityContext->getToken()) {
+ if (null !== $token = $this->tokenStorage->getToken()) {
if ($token instanceof UsernamePasswordToken && $token->isAuthenticated() && $token->getUsername() === $digestAuth->getUsername()) {
return;
}
}
if (null !== $this->logger) {
- $this->logger->debug(sprintf('Digest Authorization header received from user agent: %s', $header));
+ $this->logger->debug('Digest Authorization header received from user agent.', array('header' => $header));
}
try {
@@ -89,7 +89,7 @@ class DigestAuthenticationListener implements ListenerInterface
$user = $this->provider->loadUserByUsername($digestAuth->getUsername());
if (null === $user) {
- throw new AuthenticationServiceException('AuthenticationDao returned null, which is an interface contract violation');
+ throw new AuthenticationServiceException('Digest User provider returned null, which is an interface contract violation');
}
$serverDigestMd5 = $digestAuth->calculateServerDigest($user->getPassword(), $request->getMethod());
@@ -101,7 +101,7 @@ class DigestAuthenticationListener implements ListenerInterface
if ($serverDigestMd5 !== $digestAuth->getResponse()) {
if (null !== $this->logger) {
- $this->logger->debug(sprintf('Expected response: "%s" but received: "%s"; is AuthenticationDao returning clear text passwords?', $serverDigestMd5, $digestAuth->getResponse()));
+ $this->logger->debug('Unexpected response from the DigestAuth received; is the header returning a clear text passwords?', array('expected' => $serverDigestMd5, 'received' => $digestAuth->getResponse()));
}
$this->fail($event, $request, new BadCredentialsException('Incorrect response'));
@@ -116,21 +116,21 @@ class DigestAuthenticationListener implements ListenerInterface
}
if (null !== $this->logger) {
- $this->logger->info(sprintf('Authentication success for user "%s" with response "%s"', $digestAuth->getUsername(), $digestAuth->getResponse()));
+ $this->logger->info('Digest authentication successful.', array('username' => $digestAuth->getUsername(), 'received' => $digestAuth->getResponse()));
}
- $this->securityContext->setToken(new UsernamePasswordToken($user, $user->getPassword(), $this->providerKey));
+ $this->tokenStorage->setToken(new UsernamePasswordToken($user, $user->getPassword(), $this->providerKey));
}
private function fail(GetResponseEvent $event, Request $request, AuthenticationException $authException)
{
- $token = $this->securityContext->getToken();
+ $token = $this->tokenStorage->getToken();
if ($token instanceof UsernamePasswordToken && $this->providerKey === $token->getProviderKey()) {
- $this->securityContext->setToken(null);
+ $this->tokenStorage->setToken(null);
}
if (null !== $this->logger) {
- $this->logger->info($authException);
+ $this->logger->info('Digest authentication failed.', array('exception' => $authException));
}
$event->setResponse($this->authenticationEntryPoint->start($request, $authException));
@@ -139,7 +139,7 @@ class DigestAuthenticationListener implements ListenerInterface
class DigestData
{
- private $elements;
+ private $elements = array();
private $header;
private $nonceExpiryTime;
@@ -147,7 +147,6 @@ class DigestData
{
$this->header = $header;
preg_match_all('/(\w+)=("((?:[^"\\\\]|\\\\.)+)"|([^\s,$]+))/', $header, $matches, PREG_SET_ORDER);
- $this->elements = array();
foreach ($matches as $match) {
if (isset($match[1]) && isset($match[3])) {
$this->elements[$match[1]] = isset($match[4]) ? $match[4] : $match[3];
diff --git a/Http/Firewall/ExceptionListener.php b/Http/Firewall/ExceptionListener.php
index 8553c75..a1cae2a 100644
--- a/Http/Firewall/ExceptionListener.php
+++ b/Http/Firewall/ExceptionListener.php
@@ -13,8 +13,9 @@ namespace Symfony\Component\Security\Http\Firewall;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Http\Authorization\AccessDeniedHandlerInterface;
-use Symfony\Component\Security\Core\SecurityContextInterface;
+use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolverInterface;
+use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface;
use Symfony\Component\Security\Core\Exception\AccountStatusException;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
@@ -38,7 +39,7 @@ use Symfony\Component\EventDispatcher\EventDispatcherInterface;
*/
class ExceptionListener
{
- private $context;
+ private $tokenStorage;
private $providerKey;
private $accessDeniedHandler;
private $authenticationEntryPoint;
@@ -48,9 +49,9 @@ class ExceptionListener
private $httpUtils;
private $stateless;
- public function __construct(SecurityContextInterface $context, AuthenticationTrustResolverInterface $trustResolver, HttpUtils $httpUtils, $providerKey, AuthenticationEntryPointInterface $authenticationEntryPoint = null, $errorPage = null, AccessDeniedHandlerInterface $accessDeniedHandler = null, LoggerInterface $logger = null, $stateless = false)
+ public function __construct(TokenStorageInterface $tokenStorage, AuthenticationTrustResolverInterface $trustResolver, HttpUtils $httpUtils, $providerKey, AuthenticationEntryPointInterface $authenticationEntryPoint = null, $errorPage = null, AccessDeniedHandlerInterface $accessDeniedHandler = null, LoggerInterface $logger = null, $stateless = false)
{
- $this->context = $context;
+ $this->tokenStorage = $tokenStorage;
$this->accessDeniedHandler = $accessDeniedHandler;
$this->httpUtils = $httpUtils;
$this->providerKey = $providerKey;
@@ -72,16 +73,22 @@ class ExceptionListener
}
/**
+ * Unregisters the dispatcher.
+ *
+ * @param EventDispatcherInterface $dispatcher An EventDispatcherInterface instance
+ */
+ public function unregister(EventDispatcherInterface $dispatcher)
+ {
+ $dispatcher->removeListener(KernelEvents::EXCEPTION, array($this, 'onKernelException'));
+ }
+
+ /**
* Handles security related exceptions.
*
* @param GetResponseForExceptionEvent $event An GetResponseForExceptionEvent instance
*/
public function onKernelException(GetResponseForExceptionEvent $event)
{
- // we need to remove ourselves as the exception listener can be
- // different depending on the Request
- $event->getDispatcher()->removeListener(KernelEvents::EXCEPTION, array($this, 'onKernelException'));
-
$exception = $event->getException();
do {
if ($exception instanceof AuthenticationException) {
@@ -97,7 +104,7 @@ class ExceptionListener
private function handleAuthenticationException(GetResponseForExceptionEvent $event, AuthenticationException $exception)
{
if (null !== $this->logger) {
- $this->logger->info(sprintf('Authentication exception occurred; redirecting to authentication entry point (%s)', $exception->getMessage()));
+ $this->logger->info('An AuthenticationException was thrown; redirecting to authentication entry point.', array('exception' => $exception));
}
try {
@@ -111,10 +118,10 @@ class ExceptionListener
{
$event->setException(new AccessDeniedHttpException($exception->getMessage(), $exception));
- $token = $this->context->getToken();
+ $token = $this->tokenStorage->getToken();
if (!$this->authenticationTrustResolver->isFullFledged($token)) {
if (null !== $this->logger) {
- $this->logger->debug(sprintf('Access is denied (user is not fully authenticated) by "%s" at line %s; redirecting to authentication entry point', $exception->getFile(), $exception->getLine()));
+ $this->logger->debug('Access denied, the user is not fully authenticated; redirecting to authentication entry point.', array('exception' => $exception));
}
try {
@@ -130,7 +137,7 @@ class ExceptionListener
}
if (null !== $this->logger) {
- $this->logger->debug(sprintf('Access is denied (and user is neither anonymous, nor remember-me) by "%s" at line %s', $exception->getFile(), $exception->getLine()));
+ $this->logger->debug('Access denied, the user is neither anonymous, nor remember-me.', array('exception' => $exception));
}
try {
@@ -142,13 +149,13 @@ class ExceptionListener
}
} elseif (null !== $this->errorPage) {
$subRequest = $this->httpUtils->createRequest($event->getRequest(), $this->errorPage);
- $subRequest->attributes->set(SecurityContextInterface::ACCESS_DENIED_ERROR, $exception);
+ $subRequest->attributes->set(Security::ACCESS_DENIED_ERROR, $exception);
$event->setResponse($event->getKernel()->handle($subRequest, HttpKernelInterface::SUB_REQUEST, true));
}
} catch (\Exception $e) {
if (null !== $this->logger) {
- $this->logger->error(sprintf('Exception thrown when handling an exception (%s: %s)', get_class($e), $e->getMessage()));
+ $this->logger->error('An exception was thrown when handling an AccessDeniedException.', array('exception' => $e));
}
$event->setException(new \RuntimeException('Exception thrown when handling an exception.', 0, $e));
@@ -158,7 +165,7 @@ class ExceptionListener
private function handleLogoutException(LogoutException $exception)
{
if (null !== $this->logger) {
- $this->logger->info(sprintf('Logout exception occurred; wrapping with AccessDeniedHttpException (%s)', $exception->getMessage()));
+ $this->logger->info('A LogoutException was thrown.', array('exception' => $exception));
}
}
@@ -177,7 +184,7 @@ class ExceptionListener
}
if (null !== $this->logger) {
- $this->logger->debug('Calling Authentication entry point');
+ $this->logger->debug('Calling Authentication entry point.');
}
if (!$this->stateless) {
@@ -186,7 +193,11 @@ class ExceptionListener
if ($authException instanceof AccountStatusException) {
// remove the security token to prevent infinite redirect loops
- $this->context->setToken(null);
+ $this->tokenStorage->setToken(null);
+
+ if (null !== $this->logger) {
+ $this->logger->info('The security token was removed due to an AccountStatusException.', array('exception' => $authException));
+ }
}
return $this->authenticationEntryPoint->start($request, $authException);
diff --git a/Http/Firewall/LogoutListener.php b/Http/Firewall/LogoutListener.php
index 5a68670..96f5685 100644
--- a/Http/Firewall/LogoutListener.php
+++ b/Http/Firewall/LogoutListener.php
@@ -11,12 +11,16 @@
namespace Symfony\Component\Security\Http\Firewall;
+use Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderAdapter;
use Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
-use Symfony\Component\Security\Core\SecurityContextInterface;
+use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
+use Symfony\Component\Security\Core\Exception\InvalidArgumentException;
use Symfony\Component\Security\Core\Exception\LogoutException;
+use Symfony\Component\Security\Csrf\CsrfToken;
+use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
use Symfony\Component\Security\Http\HttpUtils;
use Symfony\Component\Security\Http\Logout\LogoutHandlerInterface;
use Symfony\Component\Security\Http\Logout\LogoutSuccessHandlerInterface;
@@ -28,25 +32,31 @@ use Symfony\Component\Security\Http\Logout\LogoutSuccessHandlerInterface;
*/
class LogoutListener implements ListenerInterface
{
- private $securityContext;
+ private $tokenStorage;
private $options;
private $handlers;
private $successHandler;
private $httpUtils;
- private $csrfProvider;
+ private $csrfTokenManager;
/**
* Constructor.
*
- * @param SecurityContextInterface $securityContext
- * @param HttpUtils $httpUtils An HttpUtilsInterface instance
- * @param LogoutSuccessHandlerInterface $successHandler A LogoutSuccessHandlerInterface instance
- * @param array $options An array of options to process a logout attempt
- * @param CsrfProviderInterface $csrfProvider A CsrfProviderInterface instance
+ * @param TokenStorageInterface $tokenStorage
+ * @param HttpUtils $httpUtils An HttpUtilsInterface instance
+ * @param LogoutSuccessHandlerInterface $successHandler A LogoutSuccessHandlerInterface instance
+ * @param array $options An array of options to process a logout attempt
+ * @param CsrfTokenManagerInterface $csrfTokenManager A CsrfTokenManagerInterface instance
*/
- public function __construct(SecurityContextInterface $securityContext, HttpUtils $httpUtils, LogoutSuccessHandlerInterface $successHandler, array $options = array(), CsrfProviderInterface $csrfProvider = null)
+ public function __construct(TokenStorageInterface $tokenStorage, HttpUtils $httpUtils, LogoutSuccessHandlerInterface $successHandler, array $options = array(), $csrfTokenManager = null)
{
- $this->securityContext = $securityContext;
+ if ($csrfTokenManager instanceof CsrfProviderInterface) {
+ $csrfTokenManager = new CsrfProviderAdapter($csrfTokenManager);
+ } elseif (null !== $csrfTokenManager && !$csrfTokenManager instanceof CsrfTokenManagerInterface) {
+ throw new InvalidArgumentException('The CSRF token manager should be an instance of CsrfProviderInterface or CsrfTokenManagerInterface.');
+ }
+
+ $this->tokenStorage = $tokenStorage;
$this->httpUtils = $httpUtils;
$this->options = array_merge(array(
'csrf_parameter' => '_csrf_token',
@@ -54,7 +64,7 @@ class LogoutListener implements ListenerInterface
'logout_path' => '/logout',
), $options);
$this->successHandler = $successHandler;
- $this->csrfProvider = $csrfProvider;
+ $this->csrfTokenManager = $csrfTokenManager;
$this->handlers = array();
}
@@ -71,7 +81,7 @@ class LogoutListener implements ListenerInterface
/**
* Performs the logout if requested.
*
- * If a CsrfProviderInterface instance is available, it will be used to
+ * If a CsrfTokenManagerInterface instance is available, it will be used to
* validate the request.
*
* @param GetResponseEvent $event A GetResponseEvent instance
@@ -87,10 +97,10 @@ class LogoutListener implements ListenerInterface
return;
}
- if (null !== $this->csrfProvider) {
+ if (null !== $this->csrfTokenManager) {
$csrfToken = $request->get($this->options['csrf_parameter'], null, true);
- if (false === $this->csrfProvider->isCsrfTokenValid($this->options['intention'], $csrfToken)) {
+ if (false === $this->csrfTokenManager->isTokenValid(new CsrfToken($this->options['intention'], $csrfToken))) {
throw new LogoutException('Invalid CSRF token.');
}
}
@@ -101,13 +111,13 @@ class LogoutListener implements ListenerInterface
}
// handle multiple logout attempts gracefully
- if ($token = $this->securityContext->getToken()) {
+ if ($token = $this->tokenStorage->getToken()) {
foreach ($this->handlers as $handler) {
$handler->logout($request, $response, $token);
}
}
- $this->securityContext->setToken(null);
+ $this->tokenStorage->setToken(null);
$event->setResponse($response);
}
diff --git a/Http/Firewall/RememberMeListener.php b/Http/Firewall/RememberMeListener.php
index 942e537..ccadf94 100644
--- a/Http/Firewall/RememberMeListener.php
+++ b/Http/Firewall/RememberMeListener.php
@@ -14,12 +14,13 @@ namespace Symfony\Component\Security\Http\Firewall;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
+use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
-use Symfony\Component\Security\Core\SecurityContextInterface;
use Symfony\Component\Security\Http\RememberMe\RememberMeServicesInterface;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
use Symfony\Component\Security\Http\SecurityEvents;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
+use Symfony\Component\Security\Http\Session\SessionAuthenticationStrategyInterface;
/**
* RememberMeListener implements authentication capabilities via a cookie.
@@ -28,28 +29,34 @@ use Symfony\Component\EventDispatcher\EventDispatcherInterface;
*/
class RememberMeListener implements ListenerInterface
{
- private $securityContext;
+ private $tokenStorage;
private $rememberMeServices;
private $authenticationManager;
private $logger;
private $dispatcher;
+ private $catchExceptions = true;
+ private $sessionStrategy;
/**
* Constructor.
*
- * @param SecurityContextInterface $securityContext
- * @param RememberMeServicesInterface $rememberMeServices
- * @param AuthenticationManagerInterface $authenticationManager
- * @param LoggerInterface $logger
- * @param EventDispatcherInterface $dispatcher
+ * @param TokenStorageInterface $tokenStorage
+ * @param RememberMeServicesInterface $rememberMeServices
+ * @param AuthenticationManagerInterface $authenticationManager
+ * @param LoggerInterface $logger
+ * @param EventDispatcherInterface $dispatcher
+ * @param bool $catchExceptions
+ * @param SessionAuthenticationStrategyInterface $sessionStrategy
*/
- public function __construct(SecurityContextInterface $securityContext, RememberMeServicesInterface $rememberMeServices, AuthenticationManagerInterface $authenticationManager, LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null)
+ public function __construct(TokenStorageInterface $tokenStorage, RememberMeServicesInterface $rememberMeServices, AuthenticationManagerInterface $authenticationManager, LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null, $catchExceptions = true, SessionAuthenticationStrategyInterface $sessionStrategy = null)
{
- $this->securityContext = $securityContext;
+ $this->tokenStorage = $tokenStorage;
$this->rememberMeServices = $rememberMeServices;
$this->authenticationManager = $authenticationManager;
$this->logger = $logger;
$this->dispatcher = $dispatcher;
+ $this->catchExceptions = $catchExceptions;
+ $this->sessionStrategy = $sessionStrategy;
}
/**
@@ -59,7 +66,7 @@ class RememberMeListener implements ListenerInterface
*/
public function handle(GetResponseEvent $event)
{
- if (null !== $this->securityContext->getToken()) {
+ if (null !== $this->tokenStorage->getToken()) {
return;
}
@@ -70,7 +77,10 @@ class RememberMeListener implements ListenerInterface
try {
$token = $this->authenticationManager->authenticate($token);
- $this->securityContext->setToken($token);
+ if (null !== $this->sessionStrategy && $request->hasSession() && $request->getSession()->isStarted()) {
+ $this->sessionStrategy->onAuthentication($request, $token);
+ }
+ $this->tokenStorage->setToken($token);
if (null !== $this->dispatcher) {
$loginEvent = new InteractiveLoginEvent($request, $token);
@@ -78,18 +88,22 @@ class RememberMeListener implements ListenerInterface
}
if (null !== $this->logger) {
- $this->logger->debug('SecurityContext populated with remember-me token.');
+ $this->logger->debug('Populated the token storage with a remember-me token.');
}
} catch (AuthenticationException $e) {
if (null !== $this->logger) {
$this->logger->warning(
- 'SecurityContext not populated with remember-me token as the'
+ 'The token storage was not populated with remember-me token as the'
.' AuthenticationManager rejected the AuthenticationToken returned'
- .' by the RememberMeServices: '.$e->getMessage()
+ .' by the RememberMeServices.', array('exception' => $e)
);
}
$this->rememberMeServices->loginFail($request);
+
+ if (!$this->catchExceptions) {
+ throw $e;
+ }
}
}
}
diff --git a/Http/Firewall/RemoteUserAuthenticationListener.php b/Http/Firewall/RemoteUserAuthenticationListener.php
new file mode 100644
index 0000000..c42badf
--- /dev/null
+++ b/Http/Firewall/RemoteUserAuthenticationListener.php
@@ -0,0 +1,49 @@
+<?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\Firewall;
+
+use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
+use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
+use Psr\Log\LoggerInterface;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\Security\Core\Exception\BadCredentialsException;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
+
+/**
+ * REMOTE_USER authentication listener.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ * @author Maxime Douailin <maxime.douailin@gmail.com>
+ */
+class RemoteUserAuthenticationListener extends AbstractPreAuthenticatedListener
+{
+ private $userKey;
+
+ public function __construct(TokenStorageInterface $tokenStorage, AuthenticationManagerInterface $authenticationManager, $providerKey, $userKey = 'REMOTE_USER', LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null)
+ {
+ parent::__construct($tokenStorage, $authenticationManager, $providerKey, $logger, $dispatcher);
+
+ $this->userKey = $userKey;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getPreAuthenticatedData(Request $request)
+ {
+ if (!$request->server->has($this->userKey)) {
+ throw new BadCredentialsException(sprintf('User key was not found: %s', $this->userKey));
+ }
+
+ return array($request->server->get($this->userKey), null);
+ }
+}
diff --git a/Http/Firewall/SimpleFormAuthenticationListener.php b/Http/Firewall/SimpleFormAuthenticationListener.php
new file mode 100644
index 0000000..4733b6a
--- /dev/null
+++ b/Http/Firewall/SimpleFormAuthenticationListener.php
@@ -0,0 +1,125 @@
+<?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\Firewall;
+
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
+use Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderAdapter;
+use Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\Security\Core\Exception\InvalidArgumentException;
+use Symfony\Component\Security\Core\Exception\InvalidCsrfTokenException;
+use Symfony\Component\Security\Csrf\CsrfToken;
+use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
+use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface;
+use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface;
+use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
+use Symfony\Component\Security\Core\Authentication\SimpleFormAuthenticatorInterface;
+use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
+use Symfony\Component\Security\Core\Security;
+use Symfony\Component\Security\Http\HttpUtils;
+use Symfony\Component\Security\Http\Session\SessionAuthenticationStrategyInterface;
+use Psr\Log\LoggerInterface;
+
+/**
+ * @author Jordi Boggiano <j.boggiano@seld.be>
+ */
+class SimpleFormAuthenticationListener extends AbstractAuthenticationListener
+{
+ private $simpleAuthenticator;
+ private $csrfTokenManager;
+
+ /**
+ * Constructor.
+ *
+ * @param TokenStorageInterface $tokenStorage A TokenStorageInterface instance
+ * @param AuthenticationManagerInterface $authenticationManager An AuthenticationManagerInterface instance
+ * @param SessionAuthenticationStrategyInterface $sessionStrategy
+ * @param HttpUtils $httpUtils An HttpUtilsInterface instance
+ * @param string $providerKey
+ * @param AuthenticationSuccessHandlerInterface $successHandler
+ * @param AuthenticationFailureHandlerInterface $failureHandler
+ * @param array $options An array of options for the processing of a
+ * successful, or failed authentication attempt
+ * @param LoggerInterface $logger A LoggerInterface instance
+ * @param EventDispatcherInterface $dispatcher An EventDispatcherInterface instance
+ * @param CsrfTokenManagerInterface $csrfTokenManager A CsrfTokenManagerInterface instance
+ * @param SimpleFormAuthenticatorInterface $simpleAuthenticator A SimpleFormAuthenticatorInterface instance
+ *
+ * @throws \InvalidArgumentException In case no simple authenticator is provided
+ * @throws InvalidArgumentException In case an invalid CSRF token manager is passed
+ */
+ public function __construct(TokenStorageInterface $tokenStorage, AuthenticationManagerInterface $authenticationManager, SessionAuthenticationStrategyInterface $sessionStrategy, HttpUtils $httpUtils, $providerKey, AuthenticationSuccessHandlerInterface $successHandler, AuthenticationFailureHandlerInterface $failureHandler, array $options = array(), LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null, $csrfTokenManager = null, SimpleFormAuthenticatorInterface $simpleAuthenticator = null)
+ {
+ if (!$simpleAuthenticator) {
+ throw new \InvalidArgumentException('Missing simple authenticator');
+ }
+
+ if ($csrfTokenManager instanceof CsrfProviderInterface) {
+ $csrfTokenManager = new CsrfProviderAdapter($csrfTokenManager);
+ } elseif (null !== $csrfTokenManager && !$csrfTokenManager instanceof CsrfTokenManagerInterface) {
+ throw new InvalidArgumentException('The CSRF token manager should be an instance of CsrfProviderInterface or CsrfTokenManagerInterface.');
+ }
+
+ $this->simpleAuthenticator = $simpleAuthenticator;
+ $this->csrfTokenManager = $csrfTokenManager;
+
+ $options = array_merge(array(
+ 'username_parameter' => '_username',
+ 'password_parameter' => '_password',
+ 'csrf_parameter' => '_csrf_token',
+ 'intention' => 'authenticate',
+ 'post_only' => true,
+ ), $options);
+
+ parent::__construct($tokenStorage, $authenticationManager, $sessionStrategy, $httpUtils, $providerKey, $successHandler, $failureHandler, $options, $logger, $dispatcher);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function requiresAuthentication(Request $request)
+ {
+ if ($this->options['post_only'] && !$request->isMethod('POST')) {
+ return false;
+ }
+
+ return parent::requiresAuthentication($request);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function attemptAuthentication(Request $request)
+ {
+ if (null !== $this->csrfTokenManager) {
+ $csrfToken = $request->get($this->options['csrf_parameter'], null, true);
+
+ if (false === $this->csrfTokenManager->isTokenValid(new CsrfToken($this->options['intention'], $csrfToken))) {
+ throw new InvalidCsrfTokenException('Invalid CSRF token.');
+ }
+ }
+
+ 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(Security::LAST_USERNAME, $username);
+
+ $token = $this->simpleAuthenticator->createToken($request, $username, $password, $this->providerKey);
+
+ return $this->authenticationManager->authenticate($token);
+ }
+}
diff --git a/Http/Firewall/SimplePreAuthenticationListener.php b/Http/Firewall/SimplePreAuthenticationListener.php
new file mode 100644
index 0000000..8f1f6fd
--- /dev/null
+++ b/Http/Firewall/SimplePreAuthenticationListener.php
@@ -0,0 +1,126 @@
+<?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\Firewall;
+
+use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
+use Psr\Log\LoggerInterface;
+use Symfony\Component\HttpKernel\Event\GetResponseEvent;
+use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\Security\Core\Authentication\SimplePreAuthenticatorInterface;
+use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken;
+use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
+use Symfony\Component\Security\Core\Exception\AuthenticationException;
+use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface;
+use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface;
+use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
+use Symfony\Component\Security\Http\SecurityEvents;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
+
+/**
+ * SimplePreAuthenticationListener implements simple proxying to an authenticator.
+ *
+ * @author Jordi Boggiano <j.boggiano@seld.be>
+ */
+class SimplePreAuthenticationListener implements ListenerInterface
+{
+ private $tokenStorage;
+ private $authenticationManager;
+ private $providerKey;
+ private $simpleAuthenticator;
+ private $logger;
+ private $dispatcher;
+
+ /**
+ * Constructor.
+ *
+ * @param TokenStorageInterface $tokenStorage A TokenStorageInterface instance
+ * @param AuthenticationManagerInterface $authenticationManager An AuthenticationManagerInterface instance
+ * @param string $providerKey
+ * @param SimplePreAuthenticatorInterface $simpleAuthenticator A SimplePreAuthenticatorInterface instance
+ * @param LoggerInterface $logger A LoggerInterface instance
+ * @param EventDispatcherInterface $dispatcher An EventDispatcherInterface instance
+ */
+ public function __construct(TokenStorageInterface $tokenStorage, AuthenticationManagerInterface $authenticationManager, $providerKey, SimplePreAuthenticatorInterface $simpleAuthenticator, LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null)
+ {
+ if (empty($providerKey)) {
+ throw new \InvalidArgumentException('$providerKey must not be empty.');
+ }
+
+ $this->tokenStorage = $tokenStorage;
+ $this->authenticationManager = $authenticationManager;
+ $this->providerKey = $providerKey;
+ $this->simpleAuthenticator = $simpleAuthenticator;
+ $this->logger = $logger;
+ $this->dispatcher = $dispatcher;
+ }
+
+ /**
+ * Handles basic authentication.
+ *
+ * @param GetResponseEvent $event A GetResponseEvent instance
+ */
+ public function handle(GetResponseEvent $event)
+ {
+ $request = $event->getRequest();
+
+ if (null !== $this->logger) {
+ $this->logger->info('Attempting SimplePreAuthentication.', array('key' => $this->providerKey, 'authenticator' => get_class($this->simpleAuthenticator)));
+ }
+
+ if (null !== $this->tokenStorage->getToken() && !$this->tokenStorage->getToken() instanceof AnonymousToken) {
+ return;
+ }
+
+ try {
+ $token = $this->simpleAuthenticator->createToken($request, $this->providerKey);
+
+ // allow null to be returned to skip authentication
+ if (null === $token) {
+ return;
+ }
+
+ $token = $this->authenticationManager->authenticate($token);
+ $this->tokenStorage->setToken($token);
+
+ if (null !== $this->dispatcher) {
+ $loginEvent = new InteractiveLoginEvent($request, $token);
+ $this->dispatcher->dispatch(SecurityEvents::INTERACTIVE_LOGIN, $loginEvent);
+ }
+ } catch (AuthenticationException $e) {
+ $this->tokenStorage->setToken(null);
+
+ if (null !== $this->logger) {
+ $this->logger->info('SimplePreAuthentication request failed.', array('exception' => $e, 'authenticator' => get_class($this->simpleAuthenticator)));
+ }
+
+ if ($this->simpleAuthenticator instanceof AuthenticationFailureHandlerInterface) {
+ $response = $this->simpleAuthenticator->onAuthenticationFailure($request, $e);
+ if ($response instanceof Response) {
+ $event->setResponse($response);
+ } elseif (null !== $response) {
+ throw new \UnexpectedValueException(sprintf('The %s::onAuthenticationFailure method must return null or a Response object', get_class($this->simpleAuthenticator)));
+ }
+ }
+
+ return;
+ }
+
+ if ($this->simpleAuthenticator instanceof AuthenticationSuccessHandlerInterface) {
+ $response = $this->simpleAuthenticator->onAuthenticationSuccess($request, $token);
+ if ($response instanceof Response) {
+ $event->setResponse($response);
+ } elseif (null !== $response) {
+ throw new \UnexpectedValueException(sprintf('The %s::onAuthenticationSuccess method must return null or a Response object', get_class($this->simpleAuthenticator)));
+ }
+ }
+ }
+}
diff --git a/Http/Firewall/SwitchUserListener.php b/Http/Firewall/SwitchUserListener.php
index 79b715a..7c068fe 100644
--- a/Http/Firewall/SwitchUserListener.php
+++ b/Http/Firewall/SwitchUserListener.php
@@ -12,7 +12,6 @@
namespace Symfony\Component\Security\Http\Firewall;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
-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;
@@ -23,6 +22,7 @@ use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Role\SwitchUserRole;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
+use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationCredentialsNotFoundException;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Http\Event\SwitchUserEvent;
@@ -37,7 +37,7 @@ use Symfony\Component\EventDispatcher\EventDispatcherInterface;
*/
class SwitchUserListener implements ListenerInterface
{
- private $securityContext;
+ private $tokenStorage;
private $provider;
private $userChecker;
private $providerKey;
@@ -47,16 +47,13 @@ class SwitchUserListener implements ListenerInterface
private $logger;
private $dispatcher;
- /**
- * Constructor.
- */
- public function __construct(SecurityContextInterface $securityContext, UserProviderInterface $provider, UserCheckerInterface $userChecker, $providerKey, AccessDecisionManagerInterface $accessDecisionManager, LoggerInterface $logger = null, $usernameParameter = '_switch_user', $role = 'ROLE_ALLOWED_TO_SWITCH', EventDispatcherInterface $dispatcher = null)
+ public function __construct(TokenStorageInterface $tokenStorage, UserProviderInterface $provider, UserCheckerInterface $userChecker, $providerKey, AccessDecisionManagerInterface $accessDecisionManager, LoggerInterface $logger = null, $usernameParameter = '_switch_user', $role = 'ROLE_ALLOWED_TO_SWITCH', EventDispatcherInterface $dispatcher = null)
{
if (empty($providerKey)) {
throw new \InvalidArgumentException('$providerKey must not be empty.');
}
- $this->securityContext = $securityContext;
+ $this->tokenStorage = $tokenStorage;
$this->provider = $provider;
$this->userChecker = $userChecker;
$this->providerKey = $providerKey;
@@ -83,16 +80,18 @@ class SwitchUserListener implements ListenerInterface
}
if ('_exit' === $request->get($this->usernameParameter)) {
- $this->securityContext->setToken($this->attemptExitUser($request));
+ $this->tokenStorage->setToken($this->attemptExitUser($request));
} else {
try {
- $this->securityContext->setToken($this->attemptSwitchUser($request));
+ $this->tokenStorage->setToken($this->attemptSwitchUser($request));
} catch (AuthenticationException $e) {
throw new \LogicException(sprintf('Switch User failed: "%s"', $e->getMessage()));
}
}
- $request->server->set('QUERY_STRING', '');
+ $request->query->remove($this->usernameParameter);
+ $request->server->set('QUERY_STRING', http_build_query($request->query->all()));
+
$response = new RedirectResponse($request->getUri(), 302);
$event->setResponse($response);
@@ -110,7 +109,7 @@ class SwitchUserListener implements ListenerInterface
*/
private function attemptSwitchUser(Request $request)
{
- $token = $this->securityContext->getToken();
+ $token = $this->tokenStorage->getToken();
$originalToken = $this->getOriginalToken($token);
if (false !== $originalToken) {
@@ -128,14 +127,14 @@ class SwitchUserListener implements ListenerInterface
$username = $request->get($this->usernameParameter);
if (null !== $this->logger) {
- $this->logger->info(sprintf('Attempt to switch to user "%s"', $username));
+ $this->logger->info('Attempting to switch to user.', array('username' => $username));
}
$user = $this->provider->loadUserByUsername($username);
$this->userChecker->checkPostAuth($user);
$roles = $user->getRoles();
- $roles[] = new SwitchUserRole('ROLE_PREVIOUS_ADMIN', $this->securityContext->getToken());
+ $roles[] = new SwitchUserRole('ROLE_PREVIOUS_ADMIN', $this->tokenStorage->getToken());
$token = new UsernamePasswordToken($user, $user->getPassword(), $this->providerKey, $roles);
@@ -158,7 +157,7 @@ class SwitchUserListener implements ListenerInterface
*/
private function attemptExitUser(Request $request)
{
- if (false === $original = $this->getOriginalToken($this->securityContext->getToken())) {
+ if (false === $original = $this->getOriginalToken($this->tokenStorage->getToken())) {
throw new AuthenticationCredentialsNotFoundException('Could not find original Token object.');
}
diff --git a/Http/Firewall/UsernamePasswordFormAuthenticationListener.php b/Http/Firewall/UsernamePasswordFormAuthenticationListener.php
index 2147817..07ab85a 100644
--- a/Http/Firewall/UsernamePasswordFormAuthenticationListener.php
+++ b/Http/Firewall/UsernamePasswordFormAuthenticationListener.php
@@ -11,17 +11,22 @@
namespace Symfony\Component\Security\Http\Firewall;
+use Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderAdapter;
use Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface;
use Symfony\Component\HttpFoundation\Request;
use Psr\Log\LoggerInterface;
+use Symfony\Component\Security\Csrf\CsrfToken;
+use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface;
use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface;
use Symfony\Component\Security\Http\Session\SessionAuthenticationStrategyInterface;
use Symfony\Component\Security\Http\HttpUtils;
use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
+use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
+use Symfony\Component\Security\Core\Exception\InvalidArgumentException;
use Symfony\Component\Security\Core\Exception\InvalidCsrfTokenException;
-use Symfony\Component\Security\Core\SecurityContextInterface;
+use Symfony\Component\Security\Core\Security;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
/**
@@ -32,14 +37,17 @@ use Symfony\Component\EventDispatcher\EventDispatcherInterface;
*/
class UsernamePasswordFormAuthenticationListener extends AbstractAuthenticationListener
{
- private $csrfProvider;
+ private $csrfTokenManager;
- /**
- * {@inheritdoc}
- */
- 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, CsrfProviderInterface $csrfProvider = null)
+ public function __construct(TokenStorageInterface $tokenStorage, AuthenticationManagerInterface $authenticationManager, SessionAuthenticationStrategyInterface $sessionStrategy, HttpUtils $httpUtils, $providerKey, AuthenticationSuccessHandlerInterface $successHandler, AuthenticationFailureHandlerInterface $failureHandler, array $options = array(), LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null, $csrfTokenManager = null)
{
- parent::__construct($securityContext, $authenticationManager, $sessionStrategy, $httpUtils, $providerKey, $successHandler, $failureHandler, array_merge(array(
+ if ($csrfTokenManager instanceof CsrfProviderInterface) {
+ $csrfTokenManager = new CsrfProviderAdapter($csrfTokenManager);
+ } elseif (null !== $csrfTokenManager && !$csrfTokenManager instanceof CsrfTokenManagerInterface) {
+ throw new InvalidArgumentException('The CSRF token manager should be an instance of CsrfProviderInterface or CsrfTokenManagerInterface.');
+ }
+
+ parent::__construct($tokenStorage, $authenticationManager, $sessionStrategy, $httpUtils, $providerKey, $successHandler, $failureHandler, array_merge(array(
'username_parameter' => '_username',
'password_parameter' => '_password',
'csrf_parameter' => '_csrf_token',
@@ -47,7 +55,7 @@ class UsernamePasswordFormAuthenticationListener extends AbstractAuthenticationL
'post_only' => true,
), $options), $logger, $dispatcher);
- $this->csrfProvider = $csrfProvider;
+ $this->csrfTokenManager = $csrfTokenManager;
}
/**
@@ -67,10 +75,10 @@ class UsernamePasswordFormAuthenticationListener extends AbstractAuthenticationL
*/
protected function attemptAuthentication(Request $request)
{
- if (null !== $this->csrfProvider) {
+ if (null !== $this->csrfTokenManager) {
$csrfToken = $request->get($this->options['csrf_parameter'], null, true);
- if (false === $this->csrfProvider->isCsrfTokenValid($this->options['intention'], $csrfToken)) {
+ if (false === $this->csrfTokenManager->isTokenValid(new CsrfToken($this->options['intention'], $csrfToken))) {
throw new InvalidCsrfTokenException('Invalid CSRF token.');
}
}
@@ -83,7 +91,7 @@ class UsernamePasswordFormAuthenticationListener extends AbstractAuthenticationL
$password = $request->get($this->options['password_parameter'], null, true);
}
- $request->getSession()->set(SecurityContextInterface::LAST_USERNAME, $username);
+ $request->getSession()->set(Security::LAST_USERNAME, $username);
return $this->authenticationManager->authenticate(new UsernamePasswordToken($username, $password, $this->providerKey));
}
diff --git a/Http/Firewall/X509AuthenticationListener.php b/Http/Firewall/X509AuthenticationListener.php
index 9c07be1..326c9af 100644
--- a/Http/Firewall/X509AuthenticationListener.php
+++ b/Http/Firewall/X509AuthenticationListener.php
@@ -11,8 +11,8 @@
namespace Symfony\Component\Security\Http\Firewall;
-use Symfony\Component\Security\Core\SecurityContextInterface;
use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
+use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
@@ -28,9 +28,9 @@ class X509AuthenticationListener extends AbstractPreAuthenticatedListener
private $userKey;
private $credentialKey;
- public function __construct(SecurityContextInterface $securityContext, AuthenticationManagerInterface $authenticationManager, $providerKey, $userKey = 'SSL_CLIENT_S_DN_Email', $credentialKey = 'SSL_CLIENT_S_DN', LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null)
+ public function __construct(TokenStorageInterface $tokenStorage, AuthenticationManagerInterface $authenticationManager, $providerKey, $userKey = 'SSL_CLIENT_S_DN_Email', $credentialKey = 'SSL_CLIENT_S_DN', LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null)
{
- parent::__construct($securityContext, $authenticationManager, $providerKey, $logger, $dispatcher);
+ parent::__construct($tokenStorage, $authenticationManager, $providerKey, $logger, $dispatcher);
$this->userKey = $userKey;
$this->credentialKey = $credentialKey;
diff --git a/Http/HttpUtils.php b/Http/HttpUtils.php
index 0f47809..ed737a2 100644
--- a/Http/HttpUtils.php
+++ b/Http/HttpUtils.php
@@ -11,7 +11,6 @@
namespace Symfony\Component\Security\Http;
-use Symfony\Component\Security\Core\SecurityContextInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Routing\Matcher\UrlMatcherInterface;
@@ -19,6 +18,7 @@ use Symfony\Component\Routing\Matcher\RequestMatcherInterface;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Routing\Exception\MethodNotAllowedException;
use Symfony\Component\Routing\Exception\ResourceNotFoundException;
+use Symfony\Component\Security\Core\Security;
/**
* Encapsulates the logic needed to create sub-requests, redirect the user, and match URLs.
@@ -71,19 +71,19 @@ class HttpUtils
*/
public function createRequest(Request $request, $path)
{
- $newRequest = $request::create($this->generateUri($request, $path), 'get', array(), $request->cookies->all(), array(), $request->server->all());
+ $newRequest = Request::create($this->generateUri($request, $path), 'get', array(), $request->cookies->all(), array(), $request->server->all());
if ($request->hasSession()) {
$newRequest->setSession($request->getSession());
}
- if ($request->attributes->has(SecurityContextInterface::AUTHENTICATION_ERROR)) {
- $newRequest->attributes->set(SecurityContextInterface::AUTHENTICATION_ERROR, $request->attributes->get(SecurityContextInterface::AUTHENTICATION_ERROR));
+ if ($request->attributes->has(Security::AUTHENTICATION_ERROR)) {
+ $newRequest->attributes->set(Security::AUTHENTICATION_ERROR, $request->attributes->get(Security::AUTHENTICATION_ERROR));
}
- if ($request->attributes->has(SecurityContextInterface::ACCESS_DENIED_ERROR)) {
- $newRequest->attributes->set(SecurityContextInterface::ACCESS_DENIED_ERROR, $request->attributes->get(SecurityContextInterface::ACCESS_DENIED_ERROR));
+ if ($request->attributes->has(Security::ACCESS_DENIED_ERROR)) {
+ $newRequest->attributes->set(Security::ACCESS_DENIED_ERROR, $request->attributes->get(Security::ACCESS_DENIED_ERROR));
}
- if ($request->attributes->has(SecurityContextInterface::LAST_USERNAME)) {
- $newRequest->attributes->set(SecurityContextInterface::LAST_USERNAME, $request->attributes->get(SecurityContextInterface::LAST_USERNAME));
+ if ($request->attributes->has(Security::LAST_USERNAME)) {
+ $newRequest->attributes->set(Security::LAST_USERNAME, $request->attributes->get(Security::LAST_USERNAME));
}
return $newRequest;
diff --git a/Http/LICENSE b/Http/LICENSE
new file mode 100644
index 0000000..43028bc
--- /dev/null
+++ b/Http/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2004-2015 Fabien Potencier
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/Http/Logout/LogoutUrlGenerator.php b/Http/Logout/LogoutUrlGenerator.php
new file mode 100644
index 0000000..298c224
--- /dev/null
+++ b/Http/Logout/LogoutUrlGenerator.php
@@ -0,0 +1,139 @@
+<?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\Logout;
+
+use Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderAdapter;
+use Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface;
+use Symfony\Component\HttpFoundation\RequestStack;
+use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
+use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
+use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
+
+/**
+ * Provides generator functions for the logout URL.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ * @author Jeremy Mikola <jmikola@gmail.com>
+ */
+class LogoutUrlGenerator
+{
+ private $requestStack;
+ private $router;
+ private $tokenStorage;
+ private $listeners = array();
+
+ public function __construct(RequestStack $requestStack = null, UrlGeneratorInterface $router = null, TokenStorageInterface $tokenStorage = null)
+ {
+ $this->requestStack = $requestStack;
+ $this->router = $router;
+ $this->tokenStorage = $tokenStorage;
+ }
+
+ /**
+ * Registers a firewall's LogoutListener, allowing its URL to be generated.
+ *
+ * @param string $key The firewall key
+ * @param string $logoutPath The path that starts the logout process
+ * @param string $csrfTokenId The ID of the CSRF token
+ * @param string $csrfParameter The CSRF token parameter name
+ * @param CsrfTokenManagerInterface $csrfTokenManager A CsrfTokenManagerInterface instance
+ */
+ public function registerListener($key, $logoutPath, $csrfTokenId, $csrfParameter, $csrfTokenManager = null)
+ {
+ if ($csrfTokenManager instanceof CsrfProviderInterface) {
+ $csrfTokenManager = new CsrfProviderAdapter($csrfTokenManager);
+ } elseif (null !== $csrfTokenManager && !$csrfTokenManager instanceof CsrfTokenManagerInterface) {
+ throw new \InvalidArgumentException('The CSRF token manager should be an instance of CsrfProviderInterface or CsrfTokenManagerInterface.');
+ }
+
+ $this->listeners[$key] = array($logoutPath, $csrfTokenId, $csrfParameter, $csrfTokenManager);
+ }
+
+ /**
+ * Generates the absolute logout path for the firewall.
+ *
+ * @param string|null $key The firewall key or null to use the current firewall key
+ *
+ * @return string The logout path
+ */
+ public function getLogoutPath($key = null)
+ {
+ return $this->generateLogoutUrl($key, UrlGeneratorInterface::ABSOLUTE_PATH);
+ }
+
+ /**
+ * Generates the absolute logout URL for the firewall.
+ *
+ * @param string|null $key The firewall key or null to use the current firewall key
+ *
+ * @return string The logout URL
+ */
+ public function getLogoutUrl($key = null)
+ {
+ return $this->generateLogoutUrl($key, UrlGeneratorInterface::ABSOLUTE_URL);
+ }
+
+ /**
+ * Generates the logout URL for the firewall.
+ *
+ * @param string|null $key The firewall key or null to use the current firewall key
+ * @param bool|string $referenceType The type of reference (one of the constants in UrlGeneratorInterface)
+ *
+ * @return string The logout URL
+ *
+ * @throws \InvalidArgumentException if no LogoutListener is registered for the key or the key could not be found automatically.
+ */
+ private function generateLogoutUrl($key, $referenceType)
+ {
+ // Fetch the current provider key from token, if possible
+ if (null === $key && null !== $this->tokenStorage) {
+ $token = $this->tokenStorage->getToken();
+ if (null !== $token && method_exists($token, 'getProviderKey')) {
+ $key = $token->getProviderKey();
+ }
+ }
+
+ if (null === $key) {
+ throw new \InvalidArgumentException('Unable to find the current firewall LogoutListener, please provide the provider key manually.');
+ }
+
+ if (!array_key_exists($key, $this->listeners)) {
+ throw new \InvalidArgumentException(sprintf('No LogoutListener found for firewall key "%s".', $key));
+ }
+
+ list($logoutPath, $csrfTokenId, $csrfParameter, $csrfTokenManager) = $this->listeners[$key];
+
+ $parameters = null !== $csrfTokenManager ? array($csrfParameter => (string) $csrfTokenManager->getToken($csrfTokenId)) : array();
+
+ if ('/' === $logoutPath[0]) {
+ if (!$this->requestStack) {
+ throw new \LogicException('Unable to generate the logout URL without a RequestStack.');
+ }
+
+ $request = $this->requestStack->getCurrentRequest();
+
+ $url = UrlGeneratorInterface::ABSOLUTE_URL === $referenceType ? $request->getUriForPath($logoutPath) : $request->getBasePath().$logoutPath;
+
+ if (!empty($parameters)) {
+ $url .= '?'.http_build_query($parameters);
+ }
+ } else {
+ if (!$this->router) {
+ throw new \LogicException('Unable to generate the logout URL without a Router.');
+ }
+
+ $url = $this->router->generate($logoutPath, $parameters, $referenceType);
+ }
+
+ return $url;
+ }
+}
diff --git a/Http/README.md b/Http/README.md
new file mode 100644
index 0000000..7619bfc
--- /dev/null
+++ b/Http/README.md
@@ -0,0 +1,23 @@
+Security Component - HTTP Integration
+=====================================
+
+Security provides an infrastructure for sophisticated authorization systems,
+which makes it possible to easily separate the actual authorization logic from
+so called user providers that hold the users credentials. It is inspired by
+the Java Spring framework.
+
+Resources
+---------
+
+Documentation:
+
+https://symfony.com/doc/2.7/book/security.html
+
+Tests
+-----
+
+You can run the unit tests with the following command:
+
+ $ cd path/to/Symfony/Component/Security/Http/
+ $ composer.phar install --dev
+ $ phpunit
diff --git a/Http/RememberMe/AbstractRememberMeServices.php b/Http/RememberMe/AbstractRememberMeServices.php
index be22a1d..cd8640d 100644
--- a/Http/RememberMe/AbstractRememberMeServices.php
+++ b/Http/RememberMe/AbstractRememberMeServices.php
@@ -140,7 +140,7 @@ abstract class AbstractRememberMeServices implements RememberMeServicesInterface
}
} catch (AuthenticationException $e) {
if (null !== $this->logger) {
- $this->logger->debug('Remember-Me authentication failed: '.$e->getMessage());
+ $this->logger->debug('Remember-Me authentication failed.', array('exception' => $e));
}
}
@@ -293,7 +293,7 @@ abstract class AbstractRememberMeServices implements RememberMeServicesInterface
protected function cancelCookie(Request $request)
{
if (null !== $this->logger) {
- $this->logger->debug(sprintf('Clearing remember-me cookie "%s"', $this->options['name']));
+ $this->logger->debug('Clearing remember-me cookie.', array('name' => $this->options['name']));
}
$request->attributes->set(self::COOKIE_ATTR_NAME, new Cookie($this->options['name'], null, 1, $this->options['path'], $this->options['domain'], $this->options['secure'], $this->options['httponly']));
@@ -315,7 +315,7 @@ abstract class AbstractRememberMeServices implements RememberMeServicesInterface
$parameter = $request->get($this->options['remember_me_parameter'], null, true);
if (null === $parameter && null !== $this->logger) {
- $this->logger->debug(sprintf('Did not send remember-me cookie (remember-me parameter "%s" was not sent).', $this->options['remember_me_parameter']));
+ $this->logger->debug('Did not send remember-me cookie.', array('parameter' => $this->options['remember_me_parameter']));
}
return $parameter === 'true' || $parameter === 'on' || $parameter === '1' || $parameter === 'yes';
diff --git a/Http/RememberMe/PersistentTokenBasedRememberMeServices.php b/Http/RememberMe/PersistentTokenBasedRememberMeServices.php
index f800668..4fb7e09 100644
--- a/Http/RememberMe/PersistentTokenBasedRememberMeServices.php
+++ b/Http/RememberMe/PersistentTokenBasedRememberMeServices.php
@@ -98,7 +98,6 @@ class PersistentTokenBasedRememberMeServices extends AbstractRememberMeServices
throw new AuthenticationException('The cookie has expired.');
}
- $series = $persistentToken->getSeries();
$tokenValue = base64_encode($this->secureRandom->nextBytes(64));
$this->tokenProvider->updateToken($series, $tokenValue, new \DateTime());
$request->attributes->set(self::COOKIE_ATTR_NAME,
diff --git a/Http/RememberMe/RememberMeServicesInterface.php b/Http/RememberMe/RememberMeServicesInterface.php
index 7adb827..5750a8c 100644
--- a/Http/RememberMe/RememberMeServicesInterface.php
+++ b/Http/RememberMe/RememberMeServicesInterface.php
@@ -36,8 +36,8 @@ interface RememberMeServicesInterface
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
+ * This method will be called whenever the TokenStorage does not contain
+ * a 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
diff --git a/Http/RememberMe/ResponseListener.php b/Http/RememberMe/ResponseListener.php
index ec5f006..4149fb6 100644
--- a/Http/RememberMe/ResponseListener.php
+++ b/Http/RememberMe/ResponseListener.php
@@ -13,7 +13,6 @@ namespace Symfony\Component\Security\Http\RememberMe;
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
-use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
@@ -28,7 +27,7 @@ class ResponseListener implements EventSubscriberInterface
*/
public function onKernelResponse(FilterResponseEvent $event)
{
- if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) {
+ if (!$event->isMasterRequest()) {
return;
}
diff --git a/Http/RememberMe/TokenBasedRememberMeServices.php b/Http/RememberMe/TokenBasedRememberMeServices.php
index de662fb..d68ada5 100644
--- a/Http/RememberMe/TokenBasedRememberMeServices.php
+++ b/Http/RememberMe/TokenBasedRememberMeServices.php
@@ -17,6 +17,7 @@ use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\User\UserInterface;
+use Symfony\Component\Security\Core\Util\StringUtils;
/**
* Concrete implementation of the RememberMeServicesInterface providing
@@ -53,7 +54,7 @@ class TokenBasedRememberMeServices extends AbstractRememberMeServices
throw new \RuntimeException(sprintf('The UserProviderInterface implementation must return an instance of UserInterface, but returned "%s".', get_class($user)));
}
- if (true !== $this->compareHashes($hash, $this->generateCookieHash($class, $username, $expires, $user->getPassword()))) {
+ if (true !== StringUtils::equals($this->generateCookieHash($class, $username, $expires, $user->getPassword()), $hash)) {
throw new AuthenticationException('The cookie\'s hash is invalid.');
}
@@ -65,31 +66,6 @@ class TokenBasedRememberMeServices extends AbstractRememberMeServices
}
/**
- * Compares two hashes using a constant-time algorithm to avoid (remote)
- * timing attacks.
- *
- * This is the same implementation as used in the BasePasswordEncoder.
- *
- * @param string $hash1 The first hash
- * @param string $hash2 The second hash
- *
- * @return bool true if the two hashes are the same, false otherwise
- */
- private function compareHashes($hash1, $hash2)
- {
- if (strlen($hash1) !== $c = strlen($hash2)) {
- return false;
- }
-
- $result = 0;
- for ($i = 0; $i < $c; ++$i) {
- $result |= ord($hash1[$i]) ^ ord($hash2[$i]);
- }
-
- return 0 === $result;
- }
-
- /**
* {@inheritdoc}
*/
protected function onLoginSuccess(Request $request, Response $response, TokenInterface $token)
@@ -145,6 +121,6 @@ class TokenBasedRememberMeServices extends AbstractRememberMeServices
*/
protected function generateCookieHash($class, $username, $expires, $password)
{
- return hash('sha256', $class.$username.$expires.$password.$this->getKey());
+ return hash_hmac('sha256', $class.$username.$expires.$password, $this->getKey());
}
}
diff --git a/Http/Session/SessionAuthenticationStrategyInterface.php b/Http/Session/SessionAuthenticationStrategyInterface.php
index 9cb95d8..dd0c381 100644
--- a/Http/Session/SessionAuthenticationStrategyInterface.php
+++ b/Http/Session/SessionAuthenticationStrategyInterface.php
@@ -27,7 +27,7 @@ interface SessionAuthenticationStrategyInterface
/**
* This performs any necessary changes to the session.
*
- * This method is called before the SecurityContext is populated with a
+ * This method is called before the TokenStorage is populated with a
* Token, and only by classes inheriting from AbstractAuthenticationListener.
*
* @param Request $request
diff --git a/Http/Tests/AccessMapTest.php b/Http/Tests/AccessMapTest.php
new file mode 100644
index 0000000..d8ab7aa
--- /dev/null
+++ b/Http/Tests/AccessMapTest.php
@@ -0,0 +1,51 @@
+<?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\Tests;
+
+use Symfony\Component\Security\Http\AccessMap;
+
+class AccessMapTest extends \PHPUnit_Framework_TestCase
+{
+ public function testReturnsFirstMatchedPattern()
+ {
+ $request = $this->getMock('Symfony\Component\HttpFoundation\Request');
+ $requestMatcher1 = $this->getRequestMatcher($request, false);
+ $requestMatcher2 = $this->getRequestMatcher($request, true);
+
+ $map = new AccessMap();
+ $map->add($requestMatcher1, array('ROLE_ADMIN'), 'http');
+ $map->add($requestMatcher2, array('ROLE_USER'), 'https');
+
+ $this->assertSame(array(array('ROLE_USER'), 'https'), $map->getPatterns($request));
+ }
+
+ public function testReturnsEmptyPatternIfNoneMatched()
+ {
+ $request = $this->getMock('Symfony\Component\HttpFoundation\Request');
+ $requestMatcher = $this->getRequestMatcher($request, false);
+
+ $map = new AccessMap();
+ $map->add($requestMatcher, array('ROLE_USER'), 'https');
+
+ $this->assertSame(array(null, null), $map->getPatterns($request));
+ }
+
+ private function getRequestMatcher($request, $matches)
+ {
+ $requestMatcher = $this->getMock('Symfony\Component\HttpFoundation\RequestMatcherInterface');
+ $requestMatcher->expects($this->once())
+ ->method('matches')->with($request)
+ ->will($this->returnValue($matches));
+
+ return $requestMatcher;
+ }
+}
diff --git a/Http/Tests/Authentication/DefaultAuthenticationFailureHandlerTest.php b/Http/Tests/Authentication/DefaultAuthenticationFailureHandlerTest.php
new file mode 100644
index 0000000..82b5533
--- /dev/null
+++ b/Http/Tests/Authentication/DefaultAuthenticationFailureHandlerTest.php
@@ -0,0 +1,180 @@
+<?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\Tests\Authentication;
+
+use Symfony\Component\Security\Http\Authentication\DefaultAuthenticationFailureHandler;
+use Symfony\Component\Security\Core\Security;
+use Symfony\Component\HttpKernel\HttpKernelInterface;
+
+class DefaultAuthenticationFailureHandlerTest extends \PHPUnit_Framework_TestCase
+{
+ private $httpKernel = null;
+
+ private $httpUtils = null;
+
+ private $logger = null;
+
+ private $request = null;
+
+ private $session = null;
+
+ private $exception = null;
+
+ protected function setUp()
+ {
+ $this->httpKernel = $this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface');
+ $this->httpUtils = $this->getMock('Symfony\Component\Security\Http\HttpUtils');
+ $this->logger = $this->getMock('Psr\Log\LoggerInterface');
+
+ $this->session = $this->getMock('Symfony\Component\HttpFoundation\Session\SessionInterface');
+ $this->request = $this->getMock('Symfony\Component\HttpFoundation\Request');
+ $this->request->expects($this->any())->method('getSession')->will($this->returnValue($this->session));
+ $this->exception = $this->getMock('Symfony\Component\Security\Core\Exception\AuthenticationException', array('getMessage'));
+ }
+
+ public function testForward()
+ {
+ $options = array('failure_forward' => true);
+
+ $subRequest = $this->getRequest();
+ $subRequest->attributes->expects($this->once())
+ ->method('set')->with(Security::AUTHENTICATION_ERROR, $this->exception);
+ $this->httpUtils->expects($this->once())
+ ->method('createRequest')->with($this->request, '/login')
+ ->will($this->returnValue($subRequest));
+
+ $response = $this->getMock('Symfony\Component\HttpFoundation\Response');
+ $this->httpKernel->expects($this->once())
+ ->method('handle')->with($subRequest, HttpKernelInterface::SUB_REQUEST)
+ ->will($this->returnValue($response));
+
+ $handler = new DefaultAuthenticationFailureHandler($this->httpKernel, $this->httpUtils, $options, $this->logger);
+ $result = $handler->onAuthenticationFailure($this->request, $this->exception);
+
+ $this->assertSame($response, $result);
+ }
+
+ public function testRedirect()
+ {
+ $response = $this->getMock('Symfony\Component\HttpFoundation\Response');
+ $this->httpUtils->expects($this->once())
+ ->method('createRedirectResponse')->with($this->request, '/login')
+ ->will($this->returnValue($response));
+
+ $handler = new DefaultAuthenticationFailureHandler($this->httpKernel, $this->httpUtils, array(), $this->logger);
+ $result = $handler->onAuthenticationFailure($this->request, $this->exception);
+
+ $this->assertSame($response, $result);
+ }
+
+ public function testExceptionIsPersistedInSession()
+ {
+ $this->session->expects($this->once())
+ ->method('set')->with(Security::AUTHENTICATION_ERROR, $this->exception);
+
+ $handler = new DefaultAuthenticationFailureHandler($this->httpKernel, $this->httpUtils, array(), $this->logger);
+ $handler->onAuthenticationFailure($this->request, $this->exception);
+ }
+
+ public function testExceptionIsPassedInRequestOnForward()
+ {
+ $options = array('failure_forward' => true);
+
+ $subRequest = $this->getRequest();
+ $subRequest->attributes->expects($this->once())
+ ->method('set')->with(Security::AUTHENTICATION_ERROR, $this->exception);
+
+ $this->httpUtils->expects($this->once())
+ ->method('createRequest')->with($this->request, '/login')
+ ->will($this->returnValue($subRequest));
+
+ $this->session->expects($this->never())->method('set');
+
+ $handler = new DefaultAuthenticationFailureHandler($this->httpKernel, $this->httpUtils, $options, $this->logger);
+ $handler->onAuthenticationFailure($this->request, $this->exception);
+ }
+
+ public function testRedirectIsLogged()
+ {
+ $this->logger
+ ->expects($this->once())
+ ->method('debug')
+ ->with('Authentication failure, redirect triggered.', array('failure_path' => '/login'));
+
+ $handler = new DefaultAuthenticationFailureHandler($this->httpKernel, $this->httpUtils, array(), $this->logger);
+ $handler->onAuthenticationFailure($this->request, $this->exception);
+ }
+
+ public function testForwardIsLogged()
+ {
+ $options = array('failure_forward' => true);
+
+ $this->httpUtils->expects($this->once())
+ ->method('createRequest')->with($this->request, '/login')
+ ->will($this->returnValue($this->getRequest()));
+
+ $this->logger
+ ->expects($this->once())
+ ->method('debug')
+ ->with('Authentication failure, forward triggered.', array('failure_path' => '/login'));
+
+ $handler = new DefaultAuthenticationFailureHandler($this->httpKernel, $this->httpUtils, $options, $this->logger);
+ $handler->onAuthenticationFailure($this->request, $this->exception);
+ }
+
+ public function testFailurePathCanBeOverwritten()
+ {
+ $options = array('failure_path' => '/auth/login');
+
+ $this->httpUtils->expects($this->once())
+ ->method('createRedirectResponse')->with($this->request, '/auth/login');
+
+ $handler = new DefaultAuthenticationFailureHandler($this->httpKernel, $this->httpUtils, $options, $this->logger);
+ $handler->onAuthenticationFailure($this->request, $this->exception);
+ }
+
+ public function testFailurePathCanBeOverwrittenWithRequest()
+ {
+ $this->request->expects($this->once())
+ ->method('get')->with('_failure_path', null, true)
+ ->will($this->returnValue('/auth/login'));
+
+ $this->httpUtils->expects($this->once())
+ ->method('createRedirectResponse')->with($this->request, '/auth/login');
+
+ $handler = new DefaultAuthenticationFailureHandler($this->httpKernel, $this->httpUtils, array(), $this->logger);
+ $handler->onAuthenticationFailure($this->request, $this->exception);
+ }
+
+ public function testFailurePathParameterCanBeOverwritten()
+ {
+ $options = array('failure_path_parameter' => '_my_failure_path');
+
+ $this->request->expects($this->once())
+ ->method('get')->with('_my_failure_path', null, true)
+ ->will($this->returnValue('/auth/login'));
+
+ $this->httpUtils->expects($this->once())
+ ->method('createRedirectResponse')->with($this->request, '/auth/login');
+
+ $handler = new DefaultAuthenticationFailureHandler($this->httpKernel, $this->httpUtils, $options, $this->logger);
+ $handler->onAuthenticationFailure($this->request, $this->exception);
+ }
+
+ private function getRequest()
+ {
+ $request = $this->getMock('Symfony\Component\HttpFoundation\Request');
+ $request->attributes = $this->getMock('Symfony\Component\HttpFoundation\ParameterBag');
+
+ return $request;
+ }
+}
diff --git a/Http/Tests/Authentication/DefaultAuthenticationSuccessHandlerTest.php b/Http/Tests/Authentication/DefaultAuthenticationSuccessHandlerTest.php
new file mode 100644
index 0000000..4d1847d
--- /dev/null
+++ b/Http/Tests/Authentication/DefaultAuthenticationSuccessHandlerTest.php
@@ -0,0 +1,169 @@
+<?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\Tests\Authentication;
+
+use Symfony\Component\Security\Http\Authentication\DefaultAuthenticationSuccessHandler;
+
+class DefaultAuthenticationSuccessHandlerTest extends \PHPUnit_Framework_TestCase
+{
+ private $httpUtils = null;
+
+ private $request = null;
+
+ private $token = null;
+
+ protected function setUp()
+ {
+ $this->httpUtils = $this->getMock('Symfony\Component\Security\Http\HttpUtils');
+ $this->request = $this->getMock('Symfony\Component\HttpFoundation\Request');
+ $this->request->headers = $this->getMock('Symfony\Component\HttpFoundation\HeaderBag');
+ $this->token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface');
+ }
+
+ public function testRequestIsRedirected()
+ {
+ $response = $this->expectRedirectResponse('/');
+
+ $handler = new DefaultAuthenticationSuccessHandler($this->httpUtils, array());
+ $result = $handler->onAuthenticationSuccess($this->request, $this->token);
+
+ $this->assertSame($response, $result);
+ }
+
+ public function testDefaultTargetPathCanBeForced()
+ {
+ $options = array(
+ 'always_use_default_target_path' => true,
+ 'default_target_path' => '/dashboard',
+ );
+
+ $response = $this->expectRedirectResponse('/dashboard');
+
+ $handler = new DefaultAuthenticationSuccessHandler($this->httpUtils, $options);
+ $result = $handler->onAuthenticationSuccess($this->request, $this->token);
+
+ $this->assertSame($response, $result);
+ }
+
+ public function testTargetPathIsPassedWithRequest()
+ {
+ $this->request->expects($this->once())
+ ->method('get')->with('_target_path')
+ ->will($this->returnValue('/dashboard'));
+
+ $response = $this->expectRedirectResponse('/dashboard');
+
+ $handler = new DefaultAuthenticationSuccessHandler($this->httpUtils, array());
+ $result = $handler->onAuthenticationSuccess($this->request, $this->token);
+
+ $this->assertSame($response, $result);
+ }
+
+ public function testTargetPathParameterIsCustomised()
+ {
+ $options = array('target_path_parameter' => '_my_target_path');
+
+ $this->request->expects($this->once())
+ ->method('get')->with('_my_target_path')
+ ->will($this->returnValue('/dashboard'));
+
+ $response = $this->expectRedirectResponse('/dashboard');
+
+ $handler = new DefaultAuthenticationSuccessHandler($this->httpUtils, $options);
+ $result = $handler->onAuthenticationSuccess($this->request, $this->token);
+
+ $this->assertSame($response, $result);
+ }
+
+ public function testTargetPathIsTakenFromTheSession()
+ {
+ $session = $this->getMock('Symfony\Component\HttpFoundation\Session\SessionInterface');
+ $session->expects($this->once())
+ ->method('get')->with('_security.admin.target_path')
+ ->will($this->returnValue('/admin/dashboard'));
+ $session->expects($this->once())
+ ->method('remove')->with('_security.admin.target_path');
+
+ $this->request->expects($this->any())
+ ->method('getSession')
+ ->will($this->returnValue($session));
+
+ $response = $this->expectRedirectResponse('/admin/dashboard');
+
+ $handler = new DefaultAuthenticationSuccessHandler($this->httpUtils, array());
+ $handler->setProviderKey('admin');
+
+ $result = $handler->onAuthenticationSuccess($this->request, $this->token);
+
+ $this->assertSame($response, $result);
+ }
+
+ public function testTargetPathIsPassedAsReferer()
+ {
+ $options = array('use_referer' => true);
+
+ $this->request->headers->expects($this->once())
+ ->method('get')->with('Referer')
+ ->will($this->returnValue('/dashboard'));
+
+ $response = $this->expectRedirectResponse('/dashboard');
+
+ $handler = new DefaultAuthenticationSuccessHandler($this->httpUtils, $options);
+ $result = $handler->onAuthenticationSuccess($this->request, $this->token);
+
+ $this->assertSame($response, $result);
+ }
+
+ public function testRefererHasToBeDifferentThatLoginUrl()
+ {
+ $options = array('use_referer' => true);
+
+ $this->request->headers->expects($this->any())
+ ->method('get')->with('Referer')
+ ->will($this->returnValue('/login'));
+
+ $this->httpUtils->expects($this->once())
+ ->method('generateUri')->with($this->request, '/login')
+ ->will($this->returnValue('/login'));
+
+ $response = $this->expectRedirectResponse('/');
+
+ $handler = new DefaultAuthenticationSuccessHandler($this->httpUtils, $options);
+ $result = $handler->onAuthenticationSuccess($this->request, $this->token);
+
+ $this->assertSame($response, $result);
+ }
+
+ public function testRefererTargetPathIsIgnoredByDefault()
+ {
+ $this->request->headers->expects($this->never())->method('get');
+
+ $response = $this->expectRedirectResponse('/');
+
+ $handler = new DefaultAuthenticationSuccessHandler($this->httpUtils, array());
+ $result = $handler->onAuthenticationSuccess($this->request, $this->token);
+
+ $this->assertSame($response, $result);
+ }
+
+ private function expectRedirectResponse($path)
+ {
+ $response = $this->getMock('Symfony\Component\HttpFoundation\Response');
+
+ $this->httpUtils->expects($this->once())
+ ->method('createRedirectResponse')
+ ->with($this->request, $path)
+ ->will($this->returnValue($response));
+
+ return $response;
+ }
+}
diff --git a/Http/Tests/Authentication/SimpleAuthenticationHandlerTest.php b/Http/Tests/Authentication/SimpleAuthenticationHandlerTest.php
new file mode 100644
index 0000000..6e79b07
--- /dev/null
+++ b/Http/Tests/Authentication/SimpleAuthenticationHandlerTest.php
@@ -0,0 +1,194 @@
+<?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\Tests;
+
+use Symfony\Component\Security\Core\Authentication\SimpleAuthenticatorInterface;
+use Symfony\Component\Security\Core\Exception\AuthenticationException;
+use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface;
+use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface;
+use Symfony\Component\Security\Http\Authentication\SimpleAuthenticationHandler;
+
+class SimpleAuthenticationHandlerTest extends \PHPUnit_Framework_TestCase
+{
+ private $successHandler;
+
+ private $failureHandler;
+
+ private $request;
+
+ private $token;
+
+ private $authenticationException;
+
+ private $response;
+
+ protected function setUp()
+ {
+ $this->successHandler = $this->getMock('Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface');
+ $this->failureHandler = $this->getMock('Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface');
+
+ $this->request = $this->getMock('Symfony\Component\HttpFoundation\Request');
+ $this->token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface');
+ // No methods are invoked on the exception; we just assert on its class
+ $this->authenticationException = new AuthenticationException();
+
+ $this->response = $this->getMock('Symfony\Component\HttpFoundation\Response');
+ }
+
+ public function testOnAuthenticationSuccessFallsBackToDefaultHandlerIfSimpleIsNotASuccessHandler()
+ {
+ $authenticator = $this->getMock('Symfony\Component\Security\Core\Authentication\SimpleAuthenticatorInterface');
+
+ $this->successHandler->expects($this->once())
+ ->method('onAuthenticationSuccess')
+ ->with($this->request, $this->token)
+ ->will($this->returnValue($this->response));
+
+ $handler = new SimpleAuthenticationHandler($authenticator, $this->successHandler, $this->failureHandler);
+ $result = $handler->onAuthenticationSuccess($this->request, $this->token);
+
+ $this->assertSame($this->response, $result);
+ }
+
+ public function testOnAuthenticationSuccessCallsSimpleAuthenticator()
+ {
+ $this->successHandler->expects($this->never())
+ ->method('onAuthenticationSuccess');
+
+ $authenticator = $this->getMockForAbstractClass('Symfony\Component\Security\Http\Tests\TestSuccessHandlerInterface');
+ $authenticator->expects($this->once())
+ ->method('onAuthenticationSuccess')
+ ->with($this->request, $this->token)
+ ->will($this->returnValue($this->response));
+
+ $handler = new SimpleAuthenticationHandler($authenticator, $this->successHandler, $this->failureHandler);
+ $result = $handler->onAuthenticationSuccess($this->request, $this->token);
+
+ $this->assertSame($this->response, $result);
+ }
+
+ /**
+ * @expectedException \UnexpectedValueException
+ * @expectedExceptionMessage onAuthenticationSuccess method must return null to use the default success handler, or a Response object
+ */
+ public function testOnAuthenticationSuccessThrowsAnExceptionIfNonResponseIsReturned()
+ {
+ $this->successHandler->expects($this->never())
+ ->method('onAuthenticationSuccess');
+
+ $authenticator = $this->getMockForAbstractClass('Symfony\Component\Security\Http\Tests\TestSuccessHandlerInterface');
+ $authenticator->expects($this->once())
+ ->method('onAuthenticationSuccess')
+ ->with($this->request, $this->token)
+ ->will($this->returnValue(new \stdClass()));
+
+ $handler = new SimpleAuthenticationHandler($authenticator, $this->successHandler, $this->failureHandler);
+ $handler->onAuthenticationSuccess($this->request, $this->token);
+ }
+
+ public function testOnAuthenticationSuccessFallsBackToDefaultHandlerIfNullIsReturned()
+ {
+ $this->successHandler->expects($this->once())
+ ->method('onAuthenticationSuccess')
+ ->with($this->request, $this->token)
+ ->will($this->returnValue($this->response));
+
+ $authenticator = $this->getMockForAbstractClass('Symfony\Component\Security\Http\Tests\TestSuccessHandlerInterface');
+ $authenticator->expects($this->once())
+ ->method('onAuthenticationSuccess')
+ ->with($this->request, $this->token)
+ ->will($this->returnValue(null));
+
+ $handler = new SimpleAuthenticationHandler($authenticator, $this->successHandler, $this->failureHandler);
+ $result = $handler->onAuthenticationSuccess($this->request, $this->token);
+
+ $this->assertSame($this->response, $result);
+ }
+
+ public function testOnAuthenticationFailureFallsBackToDefaultHandlerIfSimpleIsNotAFailureHandler()
+ {
+ $authenticator = $this->getMock('Symfony\Component\Security\Core\Authentication\SimpleAuthenticatorInterface');
+
+ $this->failureHandler->expects($this->once())
+ ->method('onAuthenticationFailure')
+ ->with($this->request, $this->authenticationException)
+ ->will($this->returnValue($this->response));
+
+ $handler = new SimpleAuthenticationHandler($authenticator, $this->successHandler, $this->failureHandler);
+ $result = $handler->onAuthenticationFailure($this->request, $this->authenticationException);
+
+ $this->assertSame($this->response, $result);
+ }
+
+ public function testOnAuthenticationFailureCallsSimpleAuthenticator()
+ {
+ $this->failureHandler->expects($this->never())
+ ->method('onAuthenticationFailure');
+
+ $authenticator = $this->getMockForAbstractClass('Symfony\Component\Security\Http\Tests\TestFailureHandlerInterface');
+ $authenticator->expects($this->once())
+ ->method('onAuthenticationFailure')
+ ->with($this->request, $this->authenticationException)
+ ->will($this->returnValue($this->response));
+
+ $handler = new SimpleAuthenticationHandler($authenticator, $this->successHandler, $this->failureHandler);
+ $result = $handler->onAuthenticationFailure($this->request, $this->authenticationException);
+
+ $this->assertSame($this->response, $result);
+ }
+
+ /**
+ * @expectedException \UnexpectedValueException
+ * @expectedExceptionMessage onAuthenticationFailure method must return null to use the default failure handler, or a Response object
+ */
+ public function testOnAuthenticationFailureThrowsAnExceptionIfNonResponseIsReturned()
+ {
+ $this->failureHandler->expects($this->never())
+ ->method('onAuthenticationFailure');
+
+ $authenticator = $this->getMockForAbstractClass('Symfony\Component\Security\Http\Tests\TestFailureHandlerInterface');
+ $authenticator->expects($this->once())
+ ->method('onAuthenticationFailure')
+ ->with($this->request, $this->authenticationException)
+ ->will($this->returnValue(new \stdClass()));
+
+ $handler = new SimpleAuthenticationHandler($authenticator, $this->successHandler, $this->failureHandler);
+ $handler->onAuthenticationFailure($this->request, $this->authenticationException);
+ }
+
+ public function testOnAuthenticationFailureFallsBackToDefaultHandlerIfNullIsReturned()
+ {
+ $this->failureHandler->expects($this->once())
+ ->method('onAuthenticationFailure')
+ ->with($this->request, $this->authenticationException)
+ ->will($this->returnValue($this->response));
+
+ $authenticator = $this->getMockForAbstractClass('Symfony\Component\Security\Http\Tests\TestFailureHandlerInterface');
+ $authenticator->expects($this->once())
+ ->method('onAuthenticationFailure')
+ ->with($this->request, $this->authenticationException)
+ ->will($this->returnValue(null));
+
+ $handler = new SimpleAuthenticationHandler($authenticator, $this->successHandler, $this->failureHandler);
+ $result = $handler->onAuthenticationFailure($this->request, $this->authenticationException);
+
+ $this->assertSame($this->response, $result);
+ }
+}
+
+interface TestSuccessHandlerInterface extends AuthenticationSuccessHandlerInterface, SimpleAuthenticatorInterface
+{
+}
+
+interface TestFailureHandlerInterface extends AuthenticationFailureHandlerInterface, SimpleAuthenticatorInterface
+{
+}
diff --git a/Http/Tests/EntryPoint/BasicAuthenticationEntryPointTest.php b/Http/Tests/EntryPoint/BasicAuthenticationEntryPointTest.php
new file mode 100644
index 0000000..ca5922c
--- /dev/null
+++ b/Http/Tests/EntryPoint/BasicAuthenticationEntryPointTest.php
@@ -0,0 +1,43 @@
+<?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\Tests\EntryPoint;
+
+use Symfony\Component\Security\Http\EntryPoint\BasicAuthenticationEntryPoint;
+use Symfony\Component\Security\Core\Exception\AuthenticationException;
+
+class BasicAuthenticationEntryPointTest extends \PHPUnit_Framework_TestCase
+{
+ public function testStart()
+ {
+ $request = $this->getMock('Symfony\Component\HttpFoundation\Request');
+
+ $authException = new AuthenticationException('The exception message');
+
+ $entryPoint = new BasicAuthenticationEntryPoint('TheRealmName');
+ $response = $entryPoint->start($request, $authException);
+
+ $this->assertEquals('Basic realm="TheRealmName"', $response->headers->get('WWW-Authenticate'));
+ $this->assertEquals(401, $response->getStatusCode());
+ }
+
+ public function testStartWithoutAuthException()
+ {
+ $request = $this->getMock('Symfony\Component\HttpFoundation\Request');
+
+ $entryPoint = new BasicAuthenticationEntryPoint('TheRealmName');
+
+ $response = $entryPoint->start($request);
+
+ $this->assertEquals('Basic realm="TheRealmName"', $response->headers->get('WWW-Authenticate'));
+ $this->assertEquals(401, $response->getStatusCode());
+ }
+}
diff --git a/Http/Tests/EntryPoint/DigestAuthenticationEntryPointTest.php b/Http/Tests/EntryPoint/DigestAuthenticationEntryPointTest.php
new file mode 100644
index 0000000..181e340
--- /dev/null
+++ b/Http/Tests/EntryPoint/DigestAuthenticationEntryPointTest.php
@@ -0,0 +1,56 @@
+<?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\Tests\EntryPoint;
+
+use Symfony\Component\Security\Http\EntryPoint\DigestAuthenticationEntryPoint;
+use Symfony\Component\Security\Core\Exception\AuthenticationException;
+use Symfony\Component\Security\Core\Exception\NonceExpiredException;
+
+class DigestAuthenticationEntryPointTest extends \PHPUnit_Framework_TestCase
+{
+ public function testStart()
+ {
+ $request = $this->getMock('Symfony\Component\HttpFoundation\Request');
+
+ $authenticationException = new AuthenticationException('TheAuthenticationExceptionMessage');
+
+ $entryPoint = new DigestAuthenticationEntryPoint('TheRealmName', 'TheKey');
+ $response = $entryPoint->start($request, $authenticationException);
+
+ $this->assertEquals(401, $response->getStatusCode());
+ $this->assertRegExp('/^Digest realm="TheRealmName", qop="auth", nonce="[a-zA-Z0-9\/+]+={0,2}"$/', $response->headers->get('WWW-Authenticate'));
+ }
+
+ public function testStartWithNoException()
+ {
+ $request = $this->getMock('Symfony\Component\HttpFoundation\Request');
+
+ $entryPoint = new DigestAuthenticationEntryPoint('TheRealmName', 'TheKey');
+ $response = $entryPoint->start($request);
+
+ $this->assertEquals(401, $response->getStatusCode());
+ $this->assertRegExp('/^Digest realm="TheRealmName", qop="auth", nonce="[a-zA-Z0-9\/+]+={0,2}"$/', $response->headers->get('WWW-Authenticate'));
+ }
+
+ public function testStartWithNonceExpiredException()
+ {
+ $request = $this->getMock('Symfony\Component\HttpFoundation\Request');
+
+ $nonceExpiredException = new NonceExpiredException('TheNonceExpiredExceptionMessage');
+
+ $entryPoint = new DigestAuthenticationEntryPoint('TheRealmName', 'TheKey');
+ $response = $entryPoint->start($request, $nonceExpiredException);
+
+ $this->assertEquals(401, $response->getStatusCode());
+ $this->assertRegExp('/^Digest realm="TheRealmName", qop="auth", nonce="[a-zA-Z0-9\/+]+={0,2}", stale="true"$/', $response->headers->get('WWW-Authenticate'));
+ }
+}
diff --git a/Http/Tests/EntryPoint/FormAuthenticationEntryPointTest.php b/Http/Tests/EntryPoint/FormAuthenticationEntryPointTest.php
new file mode 100644
index 0000000..3acb9c2
--- /dev/null
+++ b/Http/Tests/EntryPoint/FormAuthenticationEntryPointTest.php
@@ -0,0 +1,67 @@
+<?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\Tests\EntryPoint;
+
+use Symfony\Component\Security\Http\EntryPoint\FormAuthenticationEntryPoint;
+use Symfony\Component\HttpKernel\HttpKernelInterface;
+
+class FormAuthenticationEntryPointTest extends \PHPUnit_Framework_TestCase
+{
+ public function testStart()
+ {
+ $request = $this->getMock('Symfony\Component\HttpFoundation\Request', array(), array(), '', false, false);
+ $response = $this->getMock('Symfony\Component\HttpFoundation\Response');
+
+ $httpKernel = $this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface');
+ $httpUtils = $this->getMock('Symfony\Component\Security\Http\HttpUtils');
+ $httpUtils
+ ->expects($this->once())
+ ->method('createRedirectResponse')
+ ->with($this->equalTo($request), $this->equalTo('/the/login/path'))
+ ->will($this->returnValue($response))
+ ;
+
+ $entryPoint = new FormAuthenticationEntryPoint($httpKernel, $httpUtils, '/the/login/path', false);
+
+ $this->assertEquals($response, $entryPoint->start($request));
+ }
+
+ public function testStartWithUseForward()
+ {
+ $request = $this->getMock('Symfony\Component\HttpFoundation\Request', array(), array(), '', false, false);
+ $subRequest = $this->getMock('Symfony\Component\HttpFoundation\Request', array(), array(), '', false, false);
+ $response = new \Symfony\Component\HttpFoundation\Response('', 200);
+
+ $httpUtils = $this->getMock('Symfony\Component\Security\Http\HttpUtils');
+ $httpUtils
+ ->expects($this->once())
+ ->method('createRequest')
+ ->with($this->equalTo($request), $this->equalTo('/the/login/path'))
+ ->will($this->returnValue($subRequest))
+ ;
+
+ $httpKernel = $this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface');
+ $httpKernel
+ ->expects($this->once())
+ ->method('handle')
+ ->with($this->equalTo($subRequest), $this->equalTo(HttpKernelInterface::SUB_REQUEST))
+ ->will($this->returnValue($response))
+ ;
+
+ $entryPoint = new FormAuthenticationEntryPoint($httpKernel, $httpUtils, '/the/login/path', true);
+
+ $entryPointResponse = $entryPoint->start($request);
+
+ $this->assertEquals($response, $entryPointResponse);
+ $this->assertEquals(401, $entryPointResponse->headers->get('X-Status-Code'));
+ }
+}
diff --git a/Http/Tests/EntryPoint/RetryAuthenticationEntryPointTest.php b/Http/Tests/EntryPoint/RetryAuthenticationEntryPointTest.php
new file mode 100644
index 0000000..ff5fbc2
--- /dev/null
+++ b/Http/Tests/EntryPoint/RetryAuthenticationEntryPointTest.php
@@ -0,0 +1,64 @@
+<?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\Tests\EntryPoint;
+
+use Symfony\Component\Security\Http\EntryPoint\RetryAuthenticationEntryPoint;
+use Symfony\Component\HttpFoundation\Request;
+
+class RetryAuthenticationEntryPointTest extends \PHPUnit_Framework_TestCase
+{
+ /**
+ * @dataProvider dataForStart
+ */
+ public function testStart($httpPort, $httpsPort, $request, $expectedUrl)
+ {
+ $entryPoint = new RetryAuthenticationEntryPoint($httpPort, $httpsPort);
+ $response = $entryPoint->start($request);
+
+ $this->assertInstanceOf('Symfony\Component\HttpFoundation\RedirectResponse', $response);
+ $this->assertEquals($expectedUrl, $response->headers->get('Location'));
+ }
+
+ public function dataForStart()
+ {
+ if (!class_exists('Symfony\Component\HttpFoundation\Request')) {
+ return array(array());
+ }
+
+ return array(
+ array(
+ 80,
+ 443,
+ Request::create('http://localhost/foo/bar?baz=bat'),
+ 'https://localhost/foo/bar?baz=bat',
+ ),
+ array(
+ 80,
+ 443,
+ Request::create('https://localhost/foo/bar?baz=bat'),
+ 'http://localhost/foo/bar?baz=bat',
+ ),
+ array(
+ 80,
+ 123,
+ Request::create('http://localhost/foo/bar?baz=bat'),
+ 'https://localhost:123/foo/bar?baz=bat',
+ ),
+ array(
+ 8080,
+ 443,
+ Request::create('https://localhost/foo/bar?baz=bat'),
+ 'http://localhost:8080/foo/bar?baz=bat',
+ ),
+ );
+ }
+}
diff --git a/Http/Tests/Firewall/AbstractPreAuthenticatedListenerTest.php b/Http/Tests/Firewall/AbstractPreAuthenticatedListenerTest.php
new file mode 100644
index 0000000..61f086a
--- /dev/null
+++ b/Http/Tests/Firewall/AbstractPreAuthenticatedListenerTest.php
@@ -0,0 +1,252 @@
+<?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\Tests\Firewall;
+
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\Security\Core\Authentication\Token\PreAuthenticatedToken;
+use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
+use Symfony\Component\Security\Core\Exception\AuthenticationException;
+
+class AbstractPreAuthenticatedListenerTest extends \PHPUnit_Framework_TestCase
+{
+ public function testHandleWithValidValues()
+ {
+ $userCredentials = array('TheUser', 'TheCredentials');
+
+ $request = new Request(array(), array(), array(), array(), array(), array());
+
+ $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface');
+
+ $tokenStorage = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface');
+ $tokenStorage
+ ->expects($this->any())
+ ->method('getToken')
+ ->will($this->returnValue(null))
+ ;
+ $tokenStorage
+ ->expects($this->once())
+ ->method('setToken')
+ ->with($this->equalTo($token))
+ ;
+
+ $authenticationManager = $this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface');
+ $authenticationManager
+ ->expects($this->once())
+ ->method('authenticate')
+ ->with($this->isInstanceOf('Symfony\Component\Security\Core\Authentication\Token\PreAuthenticatedToken'))
+ ->will($this->returnValue($token))
+ ;
+
+ $listener = $this->getMockForAbstractClass('Symfony\Component\Security\Http\Firewall\AbstractPreAuthenticatedListener', array(
+ $tokenStorage,
+ $authenticationManager,
+ 'TheProviderKey',
+ ));
+ $listener
+ ->expects($this->once())
+ ->method('getPreAuthenticatedData')
+ ->will($this->returnValue($userCredentials));
+
+ $event = $this->getMock('Symfony\Component\HttpKernel\Event\GetResponseEvent', array(), array(), '', false);
+ $event
+ ->expects($this->any())
+ ->method('getRequest')
+ ->will($this->returnValue($request))
+ ;
+
+ $listener->handle($event);
+ }
+
+ public function testHandleWhenAuthenticationFails()
+ {
+ $userCredentials = array('TheUser', 'TheCredentials');
+
+ $request = new Request(array(), array(), array(), array(), array(), array());
+
+ $tokenStorage = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface');
+ $tokenStorage
+ ->expects($this->any())
+ ->method('getToken')
+ ->will($this->returnValue(null))
+ ;
+ $tokenStorage
+ ->expects($this->never())
+ ->method('setToken')
+ ;
+
+ $exception = new AuthenticationException('Authentication failed.');
+ $authenticationManager = $this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface');
+ $authenticationManager
+ ->expects($this->once())
+ ->method('authenticate')
+ ->with($this->isInstanceOf('Symfony\Component\Security\Core\Authentication\Token\PreAuthenticatedToken'))
+ ->will($this->throwException($exception))
+ ;
+
+ $listener = $this->getMockForAbstractClass('Symfony\Component\Security\Http\Firewall\AbstractPreAuthenticatedListener', array(
+ $tokenStorage,
+ $authenticationManager,
+ 'TheProviderKey',
+ ));
+ $listener
+ ->expects($this->once())
+ ->method('getPreAuthenticatedData')
+ ->will($this->returnValue($userCredentials));
+
+ $event = $this->getMock('Symfony\Component\HttpKernel\Event\GetResponseEvent', array(), array(), '', false);
+ $event
+ ->expects($this->any())
+ ->method('getRequest')
+ ->will($this->returnValue($request))
+ ;
+
+ $listener->handle($event);
+ }
+
+ public function testHandleWhenAuthenticationFailsWithDifferentToken()
+ {
+ $userCredentials = array('TheUser', 'TheCredentials');
+
+ $token = new UsernamePasswordToken('TheUsername', 'ThePassword', 'TheProviderKey', array('ROLE_FOO'));
+
+ $request = new Request(array(), array(), array(), array(), array(), array());
+
+ $tokenStorage = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface');
+ $tokenStorage
+ ->expects($this->any())
+ ->method('getToken')
+ ->will($this->returnValue($token))
+ ;
+ $tokenStorage
+ ->expects($this->never())
+ ->method('setToken')
+ ;
+
+ $exception = new AuthenticationException('Authentication failed.');
+ $authenticationManager = $this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface');
+ $authenticationManager
+ ->expects($this->once())
+ ->method('authenticate')
+ ->with($this->isInstanceOf('Symfony\Component\Security\Core\Authentication\Token\PreAuthenticatedToken'))
+ ->will($this->throwException($exception))
+ ;
+
+ $listener = $this->getMockForAbstractClass('Symfony\Component\Security\Http\Firewall\AbstractPreAuthenticatedListener', array(
+ $tokenStorage,
+ $authenticationManager,
+ 'TheProviderKey',
+ ));
+ $listener
+ ->expects($this->once())
+ ->method('getPreAuthenticatedData')
+ ->will($this->returnValue($userCredentials));
+
+ $event = $this->getMock('Symfony\Component\HttpKernel\Event\GetResponseEvent', array(), array(), '', false);
+ $event
+ ->expects($this->any())
+ ->method('getRequest')
+ ->will($this->returnValue($request))
+ ;
+
+ $listener->handle($event);
+ }
+
+ public function testHandleWithASimilarAuthenticatedToken()
+ {
+ $userCredentials = array('TheUser', 'TheCredentials');
+
+ $request = new Request(array(), array(), array(), array(), array(), array());
+
+ $token = new PreAuthenticatedToken('TheUser', 'TheCredentials', 'TheProviderKey', array('ROLE_FOO'));
+
+ $tokenStorage = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface');
+ $tokenStorage
+ ->expects($this->any())
+ ->method('getToken')
+ ->will($this->returnValue($token))
+ ;
+
+ $authenticationManager = $this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface');
+ $authenticationManager
+ ->expects($this->never())
+ ->method('authenticate')
+ ;
+
+ $listener = $this->getMockForAbstractClass('Symfony\Component\Security\Http\Firewall\AbstractPreAuthenticatedListener', array(
+ $tokenStorage,
+ $authenticationManager,
+ 'TheProviderKey',
+ ));
+ $listener
+ ->expects($this->once())
+ ->method('getPreAuthenticatedData')
+ ->will($this->returnValue($userCredentials));
+
+ $event = $this->getMock('Symfony\Component\HttpKernel\Event\GetResponseEvent', array(), array(), '', false);
+ $event
+ ->expects($this->any())
+ ->method('getRequest')
+ ->will($this->returnValue($request))
+ ;
+
+ $listener->handle($event);
+ }
+
+ public function testHandleWithAnInvalidSimilarToken()
+ {
+ $userCredentials = array('TheUser', 'TheCredentials');
+
+ $request = new Request(array(), array(), array(), array(), array(), array());
+
+ $token = new PreAuthenticatedToken('AnotherUser', 'TheCredentials', 'TheProviderKey', array('ROLE_FOO'));
+
+ $tokenStorage = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface');
+ $tokenStorage
+ ->expects($this->any())
+ ->method('getToken')
+ ->will($this->returnValue($token))
+ ;
+ $tokenStorage
+ ->expects($this->once())
+ ->method('setToken')
+ ->with($this->equalTo(null))
+ ;
+
+ $exception = new AuthenticationException('Authentication failed.');
+ $authenticationManager = $this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface');
+ $authenticationManager
+ ->expects($this->once())
+ ->method('authenticate')
+ ->with($this->isInstanceOf('Symfony\Component\Security\Core\Authentication\Token\PreAuthenticatedToken'))
+ ->will($this->throwException($exception))
+ ;
+
+ $listener = $this->getMockForAbstractClass('Symfony\Component\Security\Http\Firewall\AbstractPreAuthenticatedListener', array(
+ $tokenStorage,
+ $authenticationManager,
+ 'TheProviderKey',
+ ));
+ $listener
+ ->expects($this->once())
+ ->method('getPreAuthenticatedData')
+ ->will($this->returnValue($userCredentials));
+
+ $event = $this->getMock('Symfony\Component\HttpKernel\Event\GetResponseEvent', array(), array(), '', false);
+ $event
+ ->expects($this->any())
+ ->method('getRequest')
+ ->will($this->returnValue($request))
+ ;
+
+ $listener->handle($event);
+ }
+}
diff --git a/Http/Tests/Firewall/AccessListenerTest.php b/Http/Tests/Firewall/AccessListenerTest.php
new file mode 100644
index 0000000..af9d565
--- /dev/null
+++ b/Http/Tests/Firewall/AccessListenerTest.php
@@ -0,0 +1,208 @@
+<?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\Tests\Firewall;
+
+use Symfony\Component\Security\Http\Firewall\AccessListener;
+
+class AccessListenerTest extends \PHPUnit_Framework_TestCase
+{
+ /**
+ * @expectedException \Symfony\Component\Security\Core\Exception\AccessDeniedException
+ */
+ public function testHandleWhenTheAccessDecisionManagerDecidesToRefuseAccess()
+ {
+ $request = $this->getMock('Symfony\Component\HttpFoundation\Request', array(), array(), '', false, false);
+
+ $accessMap = $this->getMock('Symfony\Component\Security\Http\AccessMapInterface');
+ $accessMap
+ ->expects($this->any())
+ ->method('getPatterns')
+ ->with($this->equalTo($request))
+ ->will($this->returnValue(array(array('foo' => 'bar'), null)))
+ ;
+
+ $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface');
+ $token
+ ->expects($this->any())
+ ->method('isAuthenticated')
+ ->will($this->returnValue(true))
+ ;
+
+ $tokenStorage = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface');
+ $tokenStorage
+ ->expects($this->any())
+ ->method('getToken')
+ ->will($this->returnValue($token))
+ ;
+
+ $accessDecisionManager = $this->getMock('Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface');
+ $accessDecisionManager
+ ->expects($this->once())
+ ->method('decide')
+ ->with($this->equalTo($token), $this->equalTo(array('foo' => 'bar')), $this->equalTo($request))
+ ->will($this->returnValue(false))
+ ;
+
+ $listener = new AccessListener(
+ $tokenStorage,
+ $accessDecisionManager,
+ $accessMap,
+ $this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface')
+ );
+
+ $event = $this->getMock('Symfony\Component\HttpKernel\Event\GetResponseEvent', array(), array(), '', false);
+ $event
+ ->expects($this->any())
+ ->method('getRequest')
+ ->will($this->returnValue($request))
+ ;
+
+ $listener->handle($event);
+ }
+
+ public function testHandleWhenTheTokenIsNotAuthenticated()
+ {
+ $request = $this->getMock('Symfony\Component\HttpFoundation\Request', array(), array(), '', false, false);
+
+ $accessMap = $this->getMock('Symfony\Component\Security\Http\AccessMapInterface');
+ $accessMap
+ ->expects($this->any())
+ ->method('getPatterns')
+ ->with($this->equalTo($request))
+ ->will($this->returnValue(array(array('foo' => 'bar'), null)))
+ ;
+
+ $notAuthenticatedToken = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface');
+ $notAuthenticatedToken
+ ->expects($this->any())
+ ->method('isAuthenticated')
+ ->will($this->returnValue(false))
+ ;
+
+ $authenticatedToken = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface');
+ $authenticatedToken
+ ->expects($this->any())
+ ->method('isAuthenticated')
+ ->will($this->returnValue(true))
+ ;
+
+ $authManager = $this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface');
+ $authManager
+ ->expects($this->once())
+ ->method('authenticate')
+ ->with($this->equalTo($notAuthenticatedToken))
+ ->will($this->returnValue($authenticatedToken))
+ ;
+
+ $tokenStorage = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface');
+ $tokenStorage
+ ->expects($this->any())
+ ->method('getToken')
+ ->will($this->returnValue($notAuthenticatedToken))
+ ;
+ $tokenStorage
+ ->expects($this->once())
+ ->method('setToken')
+ ->with($this->equalTo($authenticatedToken))
+ ;
+
+ $accessDecisionManager = $this->getMock('Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface');
+ $accessDecisionManager
+ ->expects($this->once())
+ ->method('decide')
+ ->with($this->equalTo($authenticatedToken), $this->equalTo(array('foo' => 'bar')), $this->equalTo($request))
+ ->will($this->returnValue(true))
+ ;
+
+ $listener = new AccessListener(
+ $tokenStorage,
+ $accessDecisionManager,
+ $accessMap,
+ $authManager
+ );
+
+ $event = $this->getMock('Symfony\Component\HttpKernel\Event\GetResponseEvent', array(), array(), '', false);
+ $event
+ ->expects($this->any())
+ ->method('getRequest')
+ ->will($this->returnValue($request))
+ ;
+
+ $listener->handle($event);
+ }
+
+ public function testHandleWhenThereIsNoAccessMapEntryMatchingTheRequest()
+ {
+ $request = $this->getMock('Symfony\Component\HttpFoundation\Request', array(), array(), '', false, false);
+
+ $accessMap = $this->getMock('Symfony\Component\Security\Http\AccessMapInterface');
+ $accessMap
+ ->expects($this->any())
+ ->method('getPatterns')
+ ->with($this->equalTo($request))
+ ->will($this->returnValue(array(null, null)))
+ ;
+
+ $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface');
+ $token
+ ->expects($this->never())
+ ->method('isAuthenticated')
+ ;
+
+ $tokenStorage = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface');
+ $tokenStorage
+ ->expects($this->any())
+ ->method('getToken')
+ ->will($this->returnValue($token))
+ ;
+
+ $listener = new AccessListener(
+ $tokenStorage,
+ $this->getMock('Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface'),
+ $accessMap,
+ $this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface')
+ );
+
+ $event = $this->getMock('Symfony\Component\HttpKernel\Event\GetResponseEvent', array(), array(), '', false);
+ $event
+ ->expects($this->any())
+ ->method('getRequest')
+ ->will($this->returnValue($request))
+ ;
+
+ $listener->handle($event);
+ }
+
+ /**
+ * @expectedException \Symfony\Component\Security\Core\Exception\AuthenticationCredentialsNotFoundException
+ */
+ public function testHandleWhenTheSecurityTokenStorageHasNoToken()
+ {
+ $tokenStorage = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface');
+ $tokenStorage
+ ->expects($this->any())
+ ->method('getToken')
+ ->will($this->returnValue(null))
+ ;
+
+ $listener = new AccessListener(
+ $tokenStorage,
+ $this->getMock('Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface'),
+ $this->getMock('Symfony\Component\Security\Http\AccessMapInterface'),
+ $this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface')
+ );
+
+ $event = $this->getMock('Symfony\Component\HttpKernel\Event\GetResponseEvent', array(), array(), '', false);
+
+ $listener->handle($event);
+ }
+}
diff --git a/Http/Tests/Firewall/AnonymousAuthenticationListenerTest.php b/Http/Tests/Firewall/AnonymousAuthenticationListenerTest.php
new file mode 100644
index 0000000..3450c1e
--- /dev/null
+++ b/Http/Tests/Firewall/AnonymousAuthenticationListenerTest.php
@@ -0,0 +1,87 @@
+<?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\Tests\Firewall;
+
+use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken;
+use Symfony\Component\Security\Http\Firewall\AnonymousAuthenticationListener;
+
+class AnonymousAuthenticationListenerTest extends \PHPUnit_Framework_TestCase
+{
+ public function testHandleWithTokenStorageHavingAToken()
+ {
+ $tokenStorage = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface');
+ $tokenStorage
+ ->expects($this->any())
+ ->method('getToken')
+ ->will($this->returnValue($this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface')))
+ ;
+ $tokenStorage
+ ->expects($this->never())
+ ->method('setToken')
+ ;
+
+ $authenticationManager = $this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface');
+ $authenticationManager
+ ->expects($this->never())
+ ->method('authenticate')
+ ;
+
+ $listener = new AnonymousAuthenticationListener($tokenStorage, 'TheKey', null, $authenticationManager);
+ $listener->handle($this->getMock('Symfony\Component\HttpKernel\Event\GetResponseEvent', array(), array(), '', false));
+ }
+
+ public function testHandleWithTokenStorageHavingNoToken()
+ {
+ $tokenStorage = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface');
+ $tokenStorage
+ ->expects($this->any())
+ ->method('getToken')
+ ->will($this->returnValue(null))
+ ;
+
+ $anonymousToken = new AnonymousToken('TheKey', 'anon.', array());
+
+ $authenticationManager = $this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface');
+ $authenticationManager
+ ->expects($this->once())
+ ->method('authenticate')
+ ->with($this->callback(function ($token) {
+ return 'TheKey' === $token->getKey();
+ }))
+ ->will($this->returnValue($anonymousToken))
+ ;
+
+ $tokenStorage
+ ->expects($this->once())
+ ->method('setToken')
+ ->with($anonymousToken)
+ ;
+
+ $listener = new AnonymousAuthenticationListener($tokenStorage, 'TheKey', null, $authenticationManager);
+ $listener->handle($this->getMock('Symfony\Component\HttpKernel\Event\GetResponseEvent', array(), array(), '', false));
+ }
+
+ public function testHandledEventIsLogged()
+ {
+ $tokenStorage = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface');
+ $logger = $this->getMock('Psr\Log\LoggerInterface');
+ $logger->expects($this->once())
+ ->method('info')
+ ->with('Populated the TokenStorage with an anonymous Token.')
+ ;
+
+ $authenticationManager = $this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface');
+
+ $listener = new AnonymousAuthenticationListener($tokenStorage, 'TheKey', $logger, $authenticationManager);
+ $listener->handle($this->getMock('Symfony\Component\HttpKernel\Event\GetResponseEvent', array(), array(), '', false));
+ }
+}
diff --git a/Http/Tests/Firewall/BasicAuthenticationListenerTest.php b/Http/Tests/Firewall/BasicAuthenticationListenerTest.php
new file mode 100644
index 0000000..8901cb2
--- /dev/null
+++ b/Http/Tests/Firewall/BasicAuthenticationListenerTest.php
@@ -0,0 +1,249 @@
+<?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\Tests\Firewall;
+
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\Security\Core\Authentication\Token\PreAuthenticatedToken;
+use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
+use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\Security\Http\Firewall\BasicAuthenticationListener;
+use Symfony\Component\Security\Core\Authentication\AuthenticationProviderManager;
+
+class BasicAuthenticationListenerTest extends \PHPUnit_Framework_TestCase
+{
+ public function testHandleWithValidUsernameAndPasswordServerParameters()
+ {
+ $request = new Request(array(), array(), array(), array(), array(), array(
+ 'PHP_AUTH_USER' => 'TheUsername',
+ 'PHP_AUTH_PW' => 'ThePassword',
+ ));
+
+ $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface');
+
+ $tokenStorage = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface');
+ $tokenStorage
+ ->expects($this->any())
+ ->method('getToken')
+ ->will($this->returnValue(null))
+ ;
+ $tokenStorage
+ ->expects($this->once())
+ ->method('setToken')
+ ->with($this->equalTo($token))
+ ;
+
+ $authenticationManager = $this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface');
+ $authenticationManager
+ ->expects($this->once())
+ ->method('authenticate')
+ ->with($this->isInstanceOf('Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken'))
+ ->will($this->returnValue($token))
+ ;
+
+ $listener = new BasicAuthenticationListener(
+ $tokenStorage,
+ $authenticationManager,
+ 'TheProviderKey',
+ $this->getMock('Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface')
+ );
+
+ $event = $this->getMock('Symfony\Component\HttpKernel\Event\GetResponseEvent', array(), array(), '', false);
+ $event
+ ->expects($this->any())
+ ->method('getRequest')
+ ->will($this->returnValue($request))
+ ;
+
+ $listener->handle($event);
+ }
+
+ public function testHandleWhenAuthenticationFails()
+ {
+ $request = new Request(array(), array(), array(), array(), array(), array(
+ 'PHP_AUTH_USER' => 'TheUsername',
+ 'PHP_AUTH_PW' => 'ThePassword',
+ ));
+
+ $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface');
+
+ $tokenStorage = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface');
+ $tokenStorage
+ ->expects($this->any())
+ ->method('getToken')
+ ->will($this->returnValue(null))
+ ;
+ $tokenStorage
+ ->expects($this->never())
+ ->method('setToken')
+ ;
+
+ $response = new Response();
+
+ $authenticationEntryPoint = $this->getMock('Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface');
+ $authenticationEntryPoint
+ ->expects($this->any())
+ ->method('start')
+ ->with($this->equalTo($request), $this->isInstanceOf('Symfony\Component\Security\Core\Exception\AuthenticationException'))
+ ->will($this->returnValue($response))
+ ;
+
+ $listener = new BasicAuthenticationListener(
+ $tokenStorage,
+ new AuthenticationProviderManager(array($this->getMock('Symfony\Component\Security\Core\Authentication\Provider\AuthenticationProviderInterface'))),
+ 'TheProviderKey',
+ $authenticationEntryPoint
+ );
+
+ $event = $this->getMock('Symfony\Component\HttpKernel\Event\GetResponseEvent', array(), array(), '', false);
+ $event
+ ->expects($this->any())
+ ->method('getRequest')
+ ->will($this->returnValue($request))
+ ;
+ $event
+ ->expects($this->once())
+ ->method('setResponse')
+ ->with($this->equalTo($response))
+ ;
+
+ $listener->handle($event);
+ }
+
+ public function testHandleWithNoUsernameServerParameter()
+ {
+ $request = new Request();
+
+ $tokenStorage = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface');
+ $tokenStorage
+ ->expects($this->never())
+ ->method('getToken')
+ ;
+
+ $listener = new BasicAuthenticationListener(
+ $tokenStorage,
+ $this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface'),
+ 'TheProviderKey',
+ $this->getMock('Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface')
+ );
+
+ $event = $this->getMock('Symfony\Component\HttpKernel\Event\GetResponseEvent', array(), array(), '', false);
+ $event
+ ->expects($this->any())
+ ->method('getRequest')
+ ->will($this->returnValue($request))
+ ;
+
+ $listener->handle($event);
+ }
+
+ public function testHandleWithASimilarAuthenticatedToken()
+ {
+ $request = new Request(array(), array(), array(), array(), array(), array('PHP_AUTH_USER' => 'TheUsername'));
+
+ $token = new UsernamePasswordToken('TheUsername', 'ThePassword', 'TheProviderKey', array('ROLE_FOO'));
+
+ $tokenStorage = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface');
+ $tokenStorage
+ ->expects($this->any())
+ ->method('getToken')
+ ->will($this->returnValue($token))
+ ;
+
+ $authenticationManager = $this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface');
+ $authenticationManager
+ ->expects($this->never())
+ ->method('authenticate')
+ ;
+
+ $listener = new BasicAuthenticationListener(
+ $tokenStorage,
+ $authenticationManager,
+ 'TheProviderKey',
+ $this->getMock('Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface')
+ );
+
+ $event = $this->getMock('Symfony\Component\HttpKernel\Event\GetResponseEvent', array(), array(), '', false);
+ $event
+ ->expects($this->any())
+ ->method('getRequest')
+ ->will($this->returnValue($request))
+ ;
+
+ $listener->handle($event);
+ }
+
+ /**
+ * @expectedException \InvalidArgumentException
+ * @expectedExceptionMessage $providerKey must not be empty
+ */
+ public function testItRequiresProviderKey()
+ {
+ new BasicAuthenticationListener(
+ $this->getMock('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface'),
+ $this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface'),
+ '',
+ $this->getMock('Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface')
+ );
+ }
+
+ public function testHandleWithADifferentAuthenticatedToken()
+ {
+ $request = new Request(array(), array(), array(), array(), array(), array(
+ 'PHP_AUTH_USER' => 'TheUsername',
+ 'PHP_AUTH_PW' => 'ThePassword',
+ ));
+
+ $token = new PreAuthenticatedToken('TheUser', 'TheCredentials', 'TheProviderKey', array('ROLE_FOO'));
+
+ $tokenStorage = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface');
+ $tokenStorage
+ ->expects($this->any())
+ ->method('getToken')
+ ->will($this->returnValue($token))
+ ;
+ $tokenStorage
+ ->expects($this->never())
+ ->method('setToken')
+ ;
+
+ $response = new Response();
+
+ $authenticationEntryPoint = $this->getMock('Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface');
+ $authenticationEntryPoint
+ ->expects($this->any())
+ ->method('start')
+ ->with($this->equalTo($request), $this->isInstanceOf('Symfony\Component\Security\Core\Exception\AuthenticationException'))
+ ->will($this->returnValue($response))
+ ;
+
+ $listener = new BasicAuthenticationListener(
+ $tokenStorage,
+ new AuthenticationProviderManager(array($this->getMock('Symfony\Component\Security\Core\Authentication\Provider\AuthenticationProviderInterface'))),
+ 'TheProviderKey',
+ $authenticationEntryPoint
+ );
+
+ $event = $this->getMock('Symfony\Component\HttpKernel\Event\GetResponseEvent', array(), array(), '', false);
+ $event
+ ->expects($this->any())
+ ->method('getRequest')
+ ->will($this->returnValue($request))
+ ;
+ $event
+ ->expects($this->once())
+ ->method('setResponse')
+ ->with($this->equalTo($response))
+ ;
+
+ $listener->handle($event);
+ }
+}
diff --git a/Http/Tests/Firewall/ChannelListenerTest.php b/Http/Tests/Firewall/ChannelListenerTest.php
new file mode 100644
index 0000000..1465224
--- /dev/null
+++ b/Http/Tests/Firewall/ChannelListenerTest.php
@@ -0,0 +1,180 @@
+<?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\Tests\Firewall;
+
+use Symfony\Component\Security\Http\Firewall\ChannelListener;
+use Symfony\Component\HttpFoundation\Response;
+
+class ChannelListenerTest extends \PHPUnit_Framework_TestCase
+{
+ public function testHandleWithNotSecuredRequestAndHttpChannel()
+ {
+ $request = $this->getMock('Symfony\Component\HttpFoundation\Request', array(), array(), '', false, false);
+ $request
+ ->expects($this->any())
+ ->method('isSecure')
+ ->will($this->returnValue(false))
+ ;
+
+ $accessMap = $this->getMock('Symfony\Component\Security\Http\AccessMapInterface');
+ $accessMap
+ ->expects($this->any())
+ ->method('getPatterns')
+ ->with($this->equalTo($request))
+ ->will($this->returnValue(array(array(), 'http')))
+ ;
+
+ $entryPoint = $this->getMock('Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface');
+ $entryPoint
+ ->expects($this->never())
+ ->method('start')
+ ;
+
+ $event = $this->getMock('Symfony\Component\HttpKernel\Event\GetResponseEvent', array(), array(), '', false);
+ $event
+ ->expects($this->any())
+ ->method('getRequest')
+ ->will($this->returnValue($request))
+ ;
+ $event
+ ->expects($this->never())
+ ->method('setResponse')
+ ;
+
+ $listener = new ChannelListener($accessMap, $entryPoint);
+ $listener->handle($event);
+ }
+
+ public function testHandleWithSecuredRequestAndHttpsChannel()
+ {
+ $request = $this->getMock('Symfony\Component\HttpFoundation\Request', array(), array(), '', false, false);
+ $request
+ ->expects($this->any())
+ ->method('isSecure')
+ ->will($this->returnValue(true))
+ ;
+
+ $accessMap = $this->getMock('Symfony\Component\Security\Http\AccessMapInterface');
+ $accessMap
+ ->expects($this->any())
+ ->method('getPatterns')
+ ->with($this->equalTo($request))
+ ->will($this->returnValue(array(array(), 'https')))
+ ;
+
+ $entryPoint = $this->getMock('Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface');
+ $entryPoint
+ ->expects($this->never())
+ ->method('start')
+ ;
+
+ $event = $this->getMock('Symfony\Component\HttpKernel\Event\GetResponseEvent', array(), array(), '', false);
+ $event
+ ->expects($this->any())
+ ->method('getRequest')
+ ->will($this->returnValue($request))
+ ;
+ $event
+ ->expects($this->never())
+ ->method('setResponse')
+ ;
+
+ $listener = new ChannelListener($accessMap, $entryPoint);
+ $listener->handle($event);
+ }
+
+ public function testHandleWithNotSecuredRequestAndHttpsChannel()
+ {
+ $request = $this->getMock('Symfony\Component\HttpFoundation\Request', array(), array(), '', false, false);
+ $request
+ ->expects($this->any())
+ ->method('isSecure')
+ ->will($this->returnValue(false))
+ ;
+
+ $response = new Response();
+
+ $accessMap = $this->getMock('Symfony\Component\Security\Http\AccessMapInterface');
+ $accessMap
+ ->expects($this->any())
+ ->method('getPatterns')
+ ->with($this->equalTo($request))
+ ->will($this->returnValue(array(array(), 'https')))
+ ;
+
+ $entryPoint = $this->getMock('Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface');
+ $entryPoint
+ ->expects($this->once())
+ ->method('start')
+ ->with($this->equalTo($request))
+ ->will($this->returnValue($response))
+ ;
+
+ $event = $this->getMock('Symfony\Component\HttpKernel\Event\GetResponseEvent', array(), array(), '', false);
+ $event
+ ->expects($this->any())
+ ->method('getRequest')
+ ->will($this->returnValue($request))
+ ;
+ $event
+ ->expects($this->once())
+ ->method('setResponse')
+ ->with($this->equalTo($response))
+ ;
+
+ $listener = new ChannelListener($accessMap, $entryPoint);
+ $listener->handle($event);
+ }
+
+ public function testHandleWithSecuredRequestAndHttpChannel()
+ {
+ $request = $this->getMock('Symfony\Component\HttpFoundation\Request', array(), array(), '', false, false);
+ $request
+ ->expects($this->any())
+ ->method('isSecure')
+ ->will($this->returnValue(true))
+ ;
+
+ $response = new Response();
+
+ $accessMap = $this->getMock('Symfony\Component\Security\Http\AccessMapInterface');
+ $accessMap
+ ->expects($this->any())
+ ->method('getPatterns')
+ ->with($this->equalTo($request))
+ ->will($this->returnValue(array(array(), 'http')))
+ ;
+
+ $entryPoint = $this->getMock('Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface');
+ $entryPoint
+ ->expects($this->once())
+ ->method('start')
+ ->with($this->equalTo($request))
+ ->will($this->returnValue($response))
+ ;
+
+ $event = $this->getMock('Symfony\Component\HttpKernel\Event\GetResponseEvent', array(), array(), '', false);
+ $event
+ ->expects($this->any())
+ ->method('getRequest')
+ ->will($this->returnValue($request))
+ ;
+ $event
+ ->expects($this->once())
+ ->method('setResponse')
+ ->with($this->equalTo($response))
+ ;
+
+ $listener = new ChannelListener($accessMap, $entryPoint);
+ $listener->handle($event);
+ }
+}
diff --git a/Http/Tests/Firewall/ContextListenerTest.php b/Http/Tests/Firewall/ContextListenerTest.php
new file mode 100644
index 0000000..ae1199a
--- /dev/null
+++ b/Http/Tests/Firewall/ContextListenerTest.php
@@ -0,0 +1,267 @@
+<?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\Tests\Firewall;
+
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\HttpFoundation\Session\Session;
+use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage;
+use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
+use Symfony\Component\HttpKernel\HttpKernelInterface;
+use Symfony\Component\HttpKernel\KernelEvents;
+use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage;
+use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
+use Symfony\Component\Security\Http\Firewall\ContextListener;
+use Symfony\Component\EventDispatcher\EventDispatcher;
+
+class ContextListenerTest extends \PHPUnit_Framework_TestCase
+{
+ /**
+ * @expectedException \InvalidArgumentException
+ * @expectedExceptionMessage $contextKey must not be empty
+ */
+ public function testItRequiresContextKey()
+ {
+ new ContextListener(
+ $this->getMock('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface'),
+ array(),
+ ''
+ );
+ }
+
+ /**
+ * @expectedException \InvalidArgumentException
+ * @expectedExceptionMessage User provider "stdClass" must implement "Symfony\Component\Security\Core\User\UserProviderInterface
+ */
+ public function testUserProvidersNeedToImplementAnInterface()
+ {
+ new ContextListener(
+ $this->getMock('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface'),
+ array(new \stdClass()),
+ 'key123'
+ );
+ }
+
+ public function testOnKernelResponseWillAddSession()
+ {
+ $session = $this->runSessionOnKernelResponse(
+ new UsernamePasswordToken('test1', 'pass1', 'phpunit'),
+ null
+ );
+
+ $token = unserialize($session->get('_security_session'));
+ $this->assertInstanceOf('Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken', $token);
+ $this->assertEquals('test1', $token->getUsername());
+ }
+
+ public function testOnKernelResponseWillReplaceSession()
+ {
+ $session = $this->runSessionOnKernelResponse(
+ new UsernamePasswordToken('test1', 'pass1', 'phpunit'),
+ 'C:10:"serialized"'
+ );
+
+ $token = unserialize($session->get('_security_session'));
+ $this->assertInstanceOf('Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken', $token);
+ $this->assertEquals('test1', $token->getUsername());
+ }
+
+ public function testOnKernelResponseWillRemoveSession()
+ {
+ $session = $this->runSessionOnKernelResponse(
+ null,
+ 'C:10:"serialized"'
+ );
+
+ $this->assertFalse($session->has('_security_session'));
+ }
+
+ public function testOnKernelResponseWithoutSession()
+ {
+ $tokenStorage = new TokenStorage();
+ $tokenStorage->setToken(new UsernamePasswordToken('test1', 'pass1', 'phpunit'));
+ $request = new Request();
+ $session = new Session(new MockArraySessionStorage());
+ $request->setSession($session);
+
+ $event = new FilterResponseEvent(
+ $this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface'),
+ $request,
+ HttpKernelInterface::MASTER_REQUEST,
+ new Response()
+ );
+
+ $listener = new ContextListener($tokenStorage, array(), 'session', null, new EventDispatcher());
+ $listener->onKernelResponse($event);
+
+ $this->assertTrue($session->isStarted());
+ }
+
+ public function testOnKernelResponseWithoutSessionNorToken()
+ {
+ $request = new Request();
+ $session = new Session(new MockArraySessionStorage());
+ $request->setSession($session);
+
+ $event = new FilterResponseEvent(
+ $this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface'),
+ $request,
+ HttpKernelInterface::MASTER_REQUEST,
+ new Response()
+ );
+
+ $listener = new ContextListener(new TokenStorage(), array(), 'session', null, new EventDispatcher());
+ $listener->onKernelResponse($event);
+
+ $this->assertFalse($session->isStarted());
+ }
+
+ /**
+ * @dataProvider provideInvalidToken
+ */
+ public function testInvalidTokenInSession($token)
+ {
+ $tokenStorage = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface');
+ $event = $this->getMockBuilder('Symfony\Component\HttpKernel\Event\GetResponseEvent')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $request = $this->getMock('Symfony\Component\HttpFoundation\Request');
+ $session = $this->getMock('Symfony\Component\HttpFoundation\Session\SessionInterface');
+
+ $event->expects($this->any())
+ ->method('getRequest')
+ ->will($this->returnValue($request));
+ $request->expects($this->any())
+ ->method('hasPreviousSession')
+ ->will($this->returnValue(true));
+ $request->expects($this->any())
+ ->method('getSession')
+ ->will($this->returnValue($session));
+ $session->expects($this->any())
+ ->method('get')
+ ->with('_security_key123')
+ ->will($this->returnValue($token));
+ $tokenStorage->expects($this->once())
+ ->method('setToken')
+ ->with(null);
+
+ $listener = new ContextListener($tokenStorage, array(), 'key123');
+ $listener->handle($event);
+ }
+
+ public function provideInvalidToken()
+ {
+ return array(
+ array(serialize(new \__PHP_Incomplete_Class())),
+ array(serialize(null)),
+ array(null),
+ );
+ }
+
+ public function testHandleAddsKernelResponseListener()
+ {
+ $tokenStorage = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface');
+ $dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface');
+ $event = $this->getMockBuilder('Symfony\Component\HttpKernel\Event\GetResponseEvent')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $listener = new ContextListener($tokenStorage, array(), 'key123', null, $dispatcher);
+
+ $event->expects($this->any())
+ ->method('isMasterRequest')
+ ->will($this->returnValue(true));
+ $event->expects($this->any())
+ ->method('getRequest')
+ ->will($this->returnValue($this->getMock('Symfony\Component\HttpFoundation\Request')));
+
+ $dispatcher->expects($this->once())
+ ->method('addListener')
+ ->with(KernelEvents::RESPONSE, array($listener, 'onKernelResponse'));
+
+ $listener->handle($event);
+ }
+
+ public function testOnKernelResponseListenerRemovesItself()
+ {
+ $tokenStorage = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface');
+ $dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface');
+ $event = $this->getMockBuilder('Symfony\Component\HttpKernel\Event\FilterResponseEvent')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $listener = new ContextListener($tokenStorage, array(), 'key123', null, $dispatcher);
+
+ $request = $this->getMock('Symfony\Component\HttpFoundation\Request');
+ $request->expects($this->any())
+ ->method('hasSession')
+ ->will($this->returnValue(true));
+
+ $event->expects($this->any())
+ ->method('isMasterRequest')
+ ->will($this->returnValue(true));
+ $event->expects($this->any())
+ ->method('getRequest')
+ ->will($this->returnValue($request));
+
+ $dispatcher->expects($this->once())
+ ->method('removeListener')
+ ->with(KernelEvents::RESPONSE, array($listener, 'onKernelResponse'));
+
+ $listener->onKernelResponse($event);
+ }
+
+ public function testHandleRemovesTokenIfNoPreviousSessionWasFound()
+ {
+ $request = $this->getMock('Symfony\Component\HttpFoundation\Request');
+ $request->expects($this->any())->method('hasPreviousSession')->will($this->returnValue(false));
+
+ $event = $this->getMockBuilder('Symfony\Component\HttpKernel\Event\GetResponseEvent')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $event->expects($this->any())->method('getRequest')->will($this->returnValue($request));
+
+ $tokenStorage = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface');
+ $tokenStorage->expects($this->once())->method('setToken')->with(null);
+
+ $listener = new ContextListener($tokenStorage, array(), 'key123');
+ $listener->handle($event);
+ }
+
+ protected function runSessionOnKernelResponse($newToken, $original = null)
+ {
+ $session = new Session(new MockArraySessionStorage());
+
+ if ($original !== null) {
+ $session->set('_security_session', $original);
+ }
+
+ $tokenStorage = new TokenStorage();
+ $tokenStorage->setToken($newToken);
+
+ $request = new Request();
+ $request->setSession($session);
+ $request->cookies->set('MOCKSESSID', true);
+
+ $event = new FilterResponseEvent(
+ $this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface'),
+ $request,
+ HttpKernelInterface::MASTER_REQUEST,
+ new Response()
+ );
+
+ $listener = new ContextListener($tokenStorage, array(), 'session', null, new EventDispatcher());
+ $listener->onKernelResponse($event);
+
+ return $session;
+ }
+}
diff --git a/Http/Tests/Firewall/DigestDataTest.php b/Http/Tests/Firewall/DigestDataTest.php
new file mode 100644
index 0000000..a7c8d49
--- /dev/null
+++ b/Http/Tests/Firewall/DigestDataTest.php
@@ -0,0 +1,181 @@
+<?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\Tests\Firewall;
+
+use Symfony\Component\Security\Http\Firewall\DigestData;
+
+class DigestDataTest extends \PHPUnit_Framework_TestCase
+{
+ public function testGetResponse()
+ {
+ $digestAuth = new DigestData(
+ 'username="user", realm="Welcome, robot!", '.
+ 'nonce="MTM0NzMyMTgyMy42NzkzOmRlZjM4NmIzOGNjMjE0OWJiNDU0MDAxNzJmYmM1MmZl", '.
+ 'uri="/path/info?p1=5&p2=5", cnonce="MDIwODkz", nc=00000001, qop="auth", '.
+ 'response="b52938fc9e6d7c01be7702ece9031b42"'
+ );
+
+ $this->assertEquals('b52938fc9e6d7c01be7702ece9031b42', $digestAuth->getResponse());
+ }
+
+ public function testGetUsername()
+ {
+ $digestAuth = new DigestData(
+ 'username="user", realm="Welcome, robot!", '.
+ 'nonce="MTM0NzMyMTgyMy42NzkzOmRlZjM4NmIzOGNjMjE0OWJiNDU0MDAxNzJmYmM1MmZl", '.
+ 'uri="/path/info?p1=5&p2=5", cnonce="MDIwODkz", nc=00000001, qop="auth", '.
+ 'response="b52938fc9e6d7c01be7702ece9031b42"'
+ );
+
+ $this->assertEquals('user', $digestAuth->getUsername());
+ }
+
+ public function testGetUsernameWithQuote()
+ {
+ $digestAuth = new DigestData(
+ 'username="\"user\"", realm="Welcome, robot!", '.
+ 'nonce="MTM0NzMyMTgyMy42NzkzOmRlZjM4NmIzOGNjMjE0OWJiNDU0MDAxNzJmYmM1MmZl", '.
+ 'uri="/path/info?p1=5&p2=5", cnonce="MDIwODkz", nc=00000001, qop="auth", '.
+ 'response="b52938fc9e6d7c01be7702ece9031b42"'
+ );
+
+ $this->assertEquals('"user"', $digestAuth->getUsername());
+ }
+
+ public function testGetUsernameWithQuoteAndEscape()
+ {
+ $digestAuth = new DigestData(
+ 'username="\"u\\\\\"ser\"", realm="Welcome, robot!", '.
+ 'nonce="MTM0NzMyMTgyMy42NzkzOmRlZjM4NmIzOGNjMjE0OWJiNDU0MDAxNzJmYmM1MmZl", '.
+ 'uri="/path/info?p1=5&p2=5", cnonce="MDIwODkz", nc=00000001, qop="auth", '.
+ 'response="b52938fc9e6d7c01be7702ece9031b42"'
+ );
+
+ $this->assertEquals('"u\\"ser"', $digestAuth->getUsername());
+ }
+
+ public function testGetUsernameWithSingleQuote()
+ {
+ $digestAuth = new DigestData(
+ 'username="\"u\'ser\"", realm="Welcome, robot!", '.
+ 'nonce="MTM0NzMyMTgyMy42NzkzOmRlZjM4NmIzOGNjMjE0OWJiNDU0MDAxNzJmYmM1MmZl", '.
+ 'uri="/path/info?p1=5&p2=5", cnonce="MDIwODkz", nc=00000001, qop="auth", '.
+ 'response="b52938fc9e6d7c01be7702ece9031b42"'
+ );
+
+ $this->assertEquals('"u\'ser"', $digestAuth->getUsername());
+ }
+
+ public function testGetUsernameWithSingleQuoteAndEscape()
+ {
+ $digestAuth = new DigestData(
+ 'username="\"u\\\'ser\"", realm="Welcome, robot!", '.
+ 'nonce="MTM0NzMyMTgyMy42NzkzOmRlZjM4NmIzOGNjMjE0OWJiNDU0MDAxNzJmYmM1MmZl", '.
+ 'uri="/path/info?p1=5&p2=5", cnonce="MDIwODkz", nc=00000001, qop="auth", '.
+ 'response="b52938fc9e6d7c01be7702ece9031b42"'
+ );
+
+ $this->assertEquals('"u\\\'ser"', $digestAuth->getUsername());
+ }
+
+ public function testGetUsernameWithEscape()
+ {
+ $digestAuth = new DigestData(
+ 'username="\"u\\ser\"", realm="Welcome, robot!", '.
+ 'nonce="MTM0NzMyMTgyMy42NzkzOmRlZjM4NmIzOGNjMjE0OWJiNDU0MDAxNzJmYmM1MmZl", '.
+ 'uri="/path/info?p1=5&p2=5", cnonce="MDIwODkz", nc=00000001, qop="auth", '.
+ 'response="b52938fc9e6d7c01be7702ece9031b42"'
+ );
+
+ $this->assertEquals('"u\\ser"', $digestAuth->getUsername());
+ }
+
+ public function testValidateAndDecode()
+ {
+ $time = microtime(true);
+ $key = 'ThisIsAKey';
+ $nonce = base64_encode($time.':'.md5($time.':'.$key));
+
+ $digestAuth = new DigestData(
+ 'username="user", realm="Welcome, robot!", nonce="'.$nonce.'", '.
+ 'uri="/path/info?p1=5&p2=5", cnonce="MDIwODkz", nc=00000001, qop="auth", '.
+ 'response="b52938fc9e6d7c01be7702ece9031b42"'
+ );
+
+ try {
+ $digestAuth->validateAndDecode($key, 'Welcome, robot!');
+ } catch (\Exception $e) {
+ $this->fail(sprintf('testValidateAndDecode fail with message: %s', $e->getMessage()));
+ }
+ }
+
+ public function testCalculateServerDigest()
+ {
+ $this->calculateServerDigest('user', 'Welcome, robot!', 'pass,word=password', 'ThisIsAKey', '00000001', 'MDIwODkz', 'auth', 'GET', '/path/info?p1=5&p2=5');
+ }
+
+ public function testCalculateServerDigestWithQuote()
+ {
+ $this->calculateServerDigest('\"user\"', 'Welcome, \"robot\"!', 'pass,word=password', 'ThisIsAKey', '00000001', 'MDIwODkz', 'auth', 'GET', '/path/info?p1=5&p2=5');
+ }
+
+ public function testCalculateServerDigestWithQuoteAndEscape()
+ {
+ $this->calculateServerDigest('\"u\\\\\"ser\"', 'Welcome, \"robot\"!', 'pass,word=password', 'ThisIsAKey', '00000001', 'MDIwODkz', 'auth', 'GET', '/path/info?p1=5&p2=5');
+ }
+
+ public function testCalculateServerDigestEscape()
+ {
+ $this->calculateServerDigest('\"u\\ser\"', 'Welcome, \"robot\"!', 'pass,word=password', 'ThisIsAKey', '00000001', 'MDIwODkz', 'auth', 'GET', '/path/info?p1=5&p2=5');
+ $this->calculateServerDigest('\"u\\ser\\\\\"', 'Welcome, \"robot\"!', 'pass,word=password', 'ThisIsAKey', '00000001', 'MDIwODkz', 'auth', 'GET', '/path/info?p1=5&p2=5');
+ }
+
+ public function testIsNonceExpired()
+ {
+ $time = microtime(true) + 10;
+ $key = 'ThisIsAKey';
+ $nonce = base64_encode($time.':'.md5($time.':'.$key));
+
+ $digestAuth = new DigestData(
+ 'username="user", realm="Welcome, robot!", nonce="'.$nonce.'", '.
+ 'uri="/path/info?p1=5&p2=5", cnonce="MDIwODkz", nc=00000001, qop="auth", '.
+ 'response="b52938fc9e6d7c01be7702ece9031b42"'
+ );
+
+ $digestAuth->validateAndDecode($key, 'Welcome, robot!');
+
+ $this->assertFalse($digestAuth->isNonceExpired());
+ }
+
+ protected function setUp()
+ {
+ class_exists('Symfony\Component\Security\Http\Firewall\DigestAuthenticationListener', true);
+ }
+
+ private function calculateServerDigest($username, $realm, $password, $key, $nc, $cnonce, $qop, $method, $uri)
+ {
+ $time = microtime(true);
+ $nonce = base64_encode($time.':'.md5($time.':'.$key));
+
+ $response = md5(
+ md5($username.':'.$realm.':'.$password).':'.$nonce.':'.$nc.':'.$cnonce.':'.$qop.':'.md5($method.':'.$uri)
+ );
+
+ $digest = sprintf('username="%s", realm="%s", nonce="%s", uri="%s", cnonce="%s", nc=%s, qop="%s", response="%s"',
+ $username, $realm, $nonce, $uri, $cnonce, $nc, $qop, $response
+ );
+
+ $digestAuth = new DigestData($digest);
+
+ $this->assertEquals($digestAuth->getResponse(), $digestAuth->calculateServerDigest($password, $method));
+ }
+}
diff --git a/Http/Tests/Firewall/ExceptionListenerTest.php b/Http/Tests/Firewall/ExceptionListenerTest.php
new file mode 100644
index 0000000..3d409e5
--- /dev/null
+++ b/Http/Tests/Firewall/ExceptionListenerTest.php
@@ -0,0 +1,184 @@
+<?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\Tests\Firewall;
+
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
+use Symfony\Component\HttpKernel\HttpKernelInterface;
+use Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolverInterface;
+use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
+use Symfony\Component\Security\Core\Exception\AccessDeniedException;
+use Symfony\Component\Security\Core\Exception\AuthenticationException;
+use Symfony\Component\Security\Http\Authorization\AccessDeniedHandlerInterface;
+use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface;
+use Symfony\Component\Security\Http\Firewall\ExceptionListener;
+use Symfony\Component\Security\Http\HttpUtils;
+
+class ExceptionListenerTest extends \PHPUnit_Framework_TestCase
+{
+ /**
+ * @dataProvider getAuthenticationExceptionProvider
+ */
+ public function testAuthenticationExceptionWithoutEntryPoint(\Exception $exception, \Exception $eventException = null)
+ {
+ $event = $this->createEvent($exception);
+
+ $listener = $this->createExceptionListener();
+ $listener->onKernelException($event);
+
+ $this->assertNull($event->getResponse());
+ $this->assertSame(null === $eventException ? $exception : $eventException, $event->getException());
+ }
+
+ /**
+ * @dataProvider getAuthenticationExceptionProvider
+ */
+ public function testAuthenticationExceptionWithEntryPoint(\Exception $exception, \Exception $eventException = null)
+ {
+ $event = $this->createEvent($exception = new AuthenticationException());
+
+ $listener = $this->createExceptionListener(null, null, null, $this->createEntryPoint());
+ $listener->onKernelException($event);
+
+ $this->assertEquals('OK', $event->getResponse()->getContent());
+ $this->assertSame($exception, $event->getException());
+ }
+
+ public function getAuthenticationExceptionProvider()
+ {
+ return array(
+ array(new AuthenticationException()),
+ array(new \LogicException('random', 0, $e = new AuthenticationException()), $e),
+ array(new \LogicException('random', 0, $e = new AuthenticationException('embed', 0, new AuthenticationException())), $e),
+ array(new \LogicException('random', 0, $e = new AuthenticationException('embed', 0, new AccessDeniedException())), $e),
+ array(new AuthenticationException('random', 0, new \LogicException())),
+ );
+ }
+
+ /**
+ * @dataProvider getAccessDeniedExceptionProvider
+ */
+ public function testAccessDeniedExceptionFullFledgedAndWithoutAccessDeniedHandlerAndWithoutErrorPage(\Exception $exception, \Exception $eventException = null)
+ {
+ $event = $this->createEvent($exception);
+
+ $listener = $this->createExceptionListener(null, $this->createTrustResolver(true));
+ $listener->onKernelException($event);
+
+ $this->assertNull($event->getResponse());
+ $this->assertSame(null === $eventException ? $exception : $eventException, $event->getException()->getPrevious());
+ }
+
+ /**
+ * @dataProvider getAccessDeniedExceptionProvider
+ */
+ public function testAccessDeniedExceptionFullFledgedAndWithoutAccessDeniedHandlerAndWithErrorPage(\Exception $exception, \Exception $eventException = null)
+ {
+ $kernel = $this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface');
+ $kernel->expects($this->once())->method('handle')->will($this->returnValue(new Response('error')));
+
+ $event = $this->createEvent($exception, $kernel);
+
+ $httpUtils = $this->getMock('Symfony\Component\Security\Http\HttpUtils');
+ $httpUtils->expects($this->once())->method('createRequest')->will($this->returnValue(Request::create('/error')));
+
+ $listener = $this->createExceptionListener(null, $this->createTrustResolver(true), $httpUtils, null, '/error');
+ $listener->onKernelException($event);
+
+ $this->assertEquals('error', $event->getResponse()->getContent());
+ $this->assertSame(null === $eventException ? $exception : $eventException, $event->getException()->getPrevious());
+ }
+
+ /**
+ * @dataProvider getAccessDeniedExceptionProvider
+ */
+ public function testAccessDeniedExceptionFullFledgedAndWithAccessDeniedHandlerAndWithoutErrorPage(\Exception $exception, \Exception $eventException = null)
+ {
+ $event = $this->createEvent($exception);
+
+ $accessDeniedHandler = $this->getMock('Symfony\Component\Security\Http\Authorization\AccessDeniedHandlerInterface');
+ $accessDeniedHandler->expects($this->once())->method('handle')->will($this->returnValue(new Response('error')));
+
+ $listener = $this->createExceptionListener(null, $this->createTrustResolver(true), null, null, null, $accessDeniedHandler);
+ $listener->onKernelException($event);
+
+ $this->assertEquals('error', $event->getResponse()->getContent());
+ $this->assertSame(null === $eventException ? $exception : $eventException, $event->getException()->getPrevious());
+ }
+
+ /**
+ * @dataProvider getAccessDeniedExceptionProvider
+ */
+ public function testAccessDeniedExceptionNotFullFledged(\Exception $exception, \Exception $eventException = null)
+ {
+ $event = $this->createEvent($exception);
+
+ $tokenStorage = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface');
+ $tokenStorage->expects($this->once())->method('getToken')->will($this->returnValue($this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface')));
+
+ $listener = $this->createExceptionListener($tokenStorage, $this->createTrustResolver(false), null, $this->createEntryPoint());
+ $listener->onKernelException($event);
+
+ $this->assertEquals('OK', $event->getResponse()->getContent());
+ $this->assertSame(null === $eventException ? $exception : $eventException, $event->getException()->getPrevious());
+ }
+
+ public function getAccessDeniedExceptionProvider()
+ {
+ return array(
+ array(new AccessDeniedException()),
+ array(new \LogicException('random', 0, $e = new AccessDeniedException()), $e),
+ array(new \LogicException('random', 0, $e = new AccessDeniedException('embed', new AccessDeniedException())), $e),
+ array(new \LogicException('random', 0, $e = new AccessDeniedException('embed', new AuthenticationException())), $e),
+ array(new AccessDeniedException('random', new \LogicException())),
+ );
+ }
+
+ private function createEntryPoint()
+ {
+ $entryPoint = $this->getMock('Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface');
+ $entryPoint->expects($this->once())->method('start')->will($this->returnValue(new Response('OK')));
+
+ return $entryPoint;
+ }
+
+ private function createTrustResolver($fullFledged)
+ {
+ $trustResolver = $this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolverInterface');
+ $trustResolver->expects($this->once())->method('isFullFledged')->will($this->returnValue($fullFledged));
+
+ return $trustResolver;
+ }
+
+ private function createEvent(\Exception $exception, $kernel = null)
+ {
+ if (null === $kernel) {
+ $kernel = $this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface');
+ }
+
+ return new GetResponseForExceptionEvent($kernel, Request::create('/'), HttpKernelInterface::MASTER_REQUEST, $exception);
+ }
+
+ private function createExceptionListener(TokenStorageInterface $tokenStorage = null, AuthenticationTrustResolverInterface $trustResolver = null, HttpUtils $httpUtils = null, AuthenticationEntryPointInterface $authenticationEntryPoint = null, $errorPage = null, AccessDeniedHandlerInterface $accessDeniedHandler = null)
+ {
+ return new ExceptionListener(
+ $tokenStorage ?: $this->getMock('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface'),
+ $trustResolver ?: $this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolverInterface'),
+ $httpUtils ?: $this->getMock('Symfony\Component\Security\Http\HttpUtils'),
+ 'key',
+ $authenticationEntryPoint,
+ $errorPage,
+ $accessDeniedHandler
+ );
+ }
+}
diff --git a/Http/Tests/Firewall/LogoutListenerTest.php b/Http/Tests/Firewall/LogoutListenerTest.php
new file mode 100644
index 0000000..15c996e
--- /dev/null
+++ b/Http/Tests/Firewall/LogoutListenerTest.php
@@ -0,0 +1,235 @@
+<?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\Tests\Firewall;
+
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\Security\Http\Firewall\LogoutListener;
+
+class LogoutListenerTest extends \PHPUnit_Framework_TestCase
+{
+ public function testHandleUnmatchedPath()
+ {
+ list($listener, $tokenStorage, $httpUtils, $options) = $this->getListener();
+
+ list($event, $request) = $this->getGetResponseEvent();
+
+ $event->expects($this->never())
+ ->method('setResponse');
+
+ $httpUtils->expects($this->once())
+ ->method('checkRequestPath')
+ ->with($request, $options['logout_path'])
+ ->will($this->returnValue(false));
+
+ $listener->handle($event);
+ }
+
+ public function testHandleMatchedPathWithSuccessHandlerAndCsrfValidation()
+ {
+ $successHandler = $this->getSuccessHandler();
+ $tokenManager = $this->getTokenManager();
+
+ list($listener, $tokenStorage, $httpUtils, $options) = $this->getListener($successHandler, $tokenManager);
+
+ list($event, $request) = $this->getGetResponseEvent();
+
+ $request->query->set('_csrf_token', 'token');
+
+ $httpUtils->expects($this->once())
+ ->method('checkRequestPath')
+ ->with($request, $options['logout_path'])
+ ->will($this->returnValue(true));
+
+ $tokenManager->expects($this->once())
+ ->method('isTokenValid')
+ ->will($this->returnValue(true));
+
+ $successHandler->expects($this->once())
+ ->method('onLogoutSuccess')
+ ->with($request)
+ ->will($this->returnValue($response = new Response()));
+
+ $tokenStorage->expects($this->once())
+ ->method('getToken')
+ ->will($this->returnValue($token = $this->getToken()));
+
+ $handler = $this->getHandler();
+ $handler->expects($this->once())
+ ->method('logout')
+ ->with($request, $response, $token);
+
+ $tokenStorage->expects($this->once())
+ ->method('setToken')
+ ->with(null);
+
+ $event->expects($this->once())
+ ->method('setResponse')
+ ->with($response);
+
+ $listener->addHandler($handler);
+
+ $listener->handle($event);
+ }
+
+ public function testHandleMatchedPathWithoutSuccessHandlerAndCsrfValidation()
+ {
+ $successHandler = $this->getSuccessHandler();
+
+ list($listener, $tokenStorage, $httpUtils, $options) = $this->getListener($successHandler);
+
+ list($event, $request) = $this->getGetResponseEvent();
+
+ $httpUtils->expects($this->once())
+ ->method('checkRequestPath')
+ ->with($request, $options['logout_path'])
+ ->will($this->returnValue(true));
+
+ $successHandler->expects($this->once())
+ ->method('onLogoutSuccess')
+ ->with($request)
+ ->will($this->returnValue($response = new Response()));
+
+ $tokenStorage->expects($this->once())
+ ->method('getToken')
+ ->will($this->returnValue($token = $this->getToken()));
+
+ $handler = $this->getHandler();
+ $handler->expects($this->once())
+ ->method('logout')
+ ->with($request, $response, $token);
+
+ $tokenStorage->expects($this->once())
+ ->method('setToken')
+ ->with(null);
+
+ $event->expects($this->once())
+ ->method('setResponse')
+ ->with($response);
+
+ $listener->addHandler($handler);
+
+ $listener->handle($event);
+ }
+
+ /**
+ * @expectedException \RuntimeException
+ */
+ public function testSuccessHandlerReturnsNonResponse()
+ {
+ $successHandler = $this->getSuccessHandler();
+
+ list($listener, $tokenStorage, $httpUtils, $options) = $this->getListener($successHandler);
+
+ list($event, $request) = $this->getGetResponseEvent();
+
+ $httpUtils->expects($this->once())
+ ->method('checkRequestPath')
+ ->with($request, $options['logout_path'])
+ ->will($this->returnValue(true));
+
+ $successHandler->expects($this->once())
+ ->method('onLogoutSuccess')
+ ->with($request)
+ ->will($this->returnValue(null));
+
+ $listener->handle($event);
+ }
+
+ /**
+ * @expectedException \Symfony\Component\Security\Core\Exception\LogoutException
+ */
+ public function testCsrfValidationFails()
+ {
+ $tokenManager = $this->getTokenManager();
+
+ list($listener, $tokenStorage, $httpUtils, $options) = $this->getListener(null, $tokenManager);
+
+ list($event, $request) = $this->getGetResponseEvent();
+
+ $request->query->set('_csrf_token', 'token');
+
+ $httpUtils->expects($this->once())
+ ->method('checkRequestPath')
+ ->with($request, $options['logout_path'])
+ ->will($this->returnValue(true));
+
+ $tokenManager->expects($this->once())
+ ->method('isTokenValid')
+ ->will($this->returnValue(false));
+
+ $listener->handle($event);
+ }
+
+ private function getTokenManager()
+ {
+ return $this->getMock('Symfony\Component\Security\Csrf\CsrfTokenManagerInterface');
+ }
+
+ private function getTokenStorage()
+ {
+ return $this->getMock('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface');
+ }
+
+ private function getGetResponseEvent()
+ {
+ $event = $this->getMockBuilder('Symfony\Component\HttpKernel\Event\GetResponseEvent')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $event->expects($this->any())
+ ->method('getRequest')
+ ->will($this->returnValue($request = new Request()));
+
+ return array($event, $request);
+ }
+
+ private function getHandler()
+ {
+ return $this->getMock('Symfony\Component\Security\Http\Logout\LogoutHandlerInterface');
+ }
+
+ private function getHttpUtils()
+ {
+ return $this->getMockBuilder('Symfony\Component\Security\Http\HttpUtils')
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ private function getListener($successHandler = null, $tokenManager = null)
+ {
+ $listener = new LogoutListener(
+ $tokenStorage = $this->getTokenStorage(),
+ $httpUtils = $this->getHttpUtils(),
+ $successHandler ?: $this->getSuccessHandler(),
+ $options = array(
+ 'csrf_parameter' => '_csrf_token',
+ 'intention' => 'logout',
+ 'logout_path' => '/logout',
+ 'target_url' => '/',
+ ),
+ $tokenManager
+ );
+
+ return array($listener, $tokenStorage, $httpUtils, $options);
+ }
+
+ private function getSuccessHandler()
+ {
+ return $this->getMock('Symfony\Component\Security\Http\Logout\LogoutSuccessHandlerInterface');
+ }
+
+ private function getToken()
+ {
+ return $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface');
+ }
+}
diff --git a/Http/Tests/Firewall/RememberMeListenerTest.php b/Http/Tests/Firewall/RememberMeListenerTest.php
new file mode 100644
index 0000000..b16d55b
--- /dev/null
+++ b/Http/Tests/Firewall/RememberMeListenerTest.php
@@ -0,0 +1,352 @@
+<?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\Tests\Firewall;
+
+use Symfony\Component\Security\Core\Exception\AuthenticationException;
+use Symfony\Component\Security\Http\Firewall\RememberMeListener;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\Security\Http\SecurityEvents;
+
+class RememberMeListenerTest extends \PHPUnit_Framework_TestCase
+{
+ public function testOnCoreSecurityDoesNotTryToPopulateNonEmptyTokenStorage()
+ {
+ list($listener, $tokenStorage) = $this->getListener();
+
+ $tokenStorage
+ ->expects($this->once())
+ ->method('getToken')
+ ->will($this->returnValue($this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface')))
+ ;
+
+ $tokenStorage
+ ->expects($this->never())
+ ->method('setToken')
+ ;
+
+ $this->assertNull($listener->handle($this->getGetResponseEvent()));
+ }
+
+ public function testOnCoreSecurityDoesNothingWhenNoCookieIsSet()
+ {
+ list($listener, $tokenStorage, $service) = $this->getListener();
+
+ $tokenStorage
+ ->expects($this->once())
+ ->method('getToken')
+ ->will($this->returnValue(null))
+ ;
+
+ $service
+ ->expects($this->once())
+ ->method('autoLogin')
+ ->will($this->returnValue(null))
+ ;
+
+ $event = $this->getGetResponseEvent();
+ $event
+ ->expects($this->once())
+ ->method('getRequest')
+ ->will($this->returnValue(new Request()))
+ ;
+
+ $this->assertNull($listener->handle($event));
+ }
+
+ public function testOnCoreSecurityIgnoresAuthenticationExceptionThrownByAuthenticationManagerImplementation()
+ {
+ list($listener, $tokenStorage, $service, $manager) = $this->getListener();
+
+ $tokenStorage
+ ->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')))
+ ;
+
+ $service
+ ->expects($this->once())
+ ->method('loginFail')
+ ;
+
+ $exception = new AuthenticationException('Authentication failed.');
+ $manager
+ ->expects($this->once())
+ ->method('authenticate')
+ ->will($this->throwException($exception))
+ ;
+
+ $event = $this->getGetResponseEvent();
+ $event
+ ->expects($this->once())
+ ->method('getRequest')
+ ->will($this->returnValue(new Request()))
+ ;
+
+ $listener->handle($event);
+ }
+
+ /**
+ * @expectedException Symfony\Component\Security\Core\Exception\AuthenticationException
+ * @expectedExceptionMessage Authentication failed.
+ */
+ public function testOnCoreSecurityIgnoresAuthenticationOptionallyRethrowsExceptionThrownAuthenticationManagerImplementation()
+ {
+ list($listener, $tokenStorage, $service, $manager) = $this->getListener(false, false);
+
+ $tokenStorage
+ ->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')))
+ ;
+
+ $service
+ ->expects($this->once())
+ ->method('loginFail')
+ ;
+
+ $exception = new AuthenticationException('Authentication failed.');
+ $manager
+ ->expects($this->once())
+ ->method('authenticate')
+ ->will($this->throwException($exception))
+ ;
+
+ $event = $this->getGetResponseEvent();
+ $event
+ ->expects($this->once())
+ ->method('getRequest')
+ ->will($this->returnValue(new Request()))
+ ;
+
+ $listener->handle($event);
+ }
+
+ public function testOnCoreSecurity()
+ {
+ list($listener, $tokenStorage, $service, $manager) = $this->getListener();
+
+ $tokenStorage
+ ->expects($this->once())
+ ->method('getToken')
+ ->will($this->returnValue(null))
+ ;
+
+ $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface');
+ $service
+ ->expects($this->once())
+ ->method('autoLogin')
+ ->will($this->returnValue($token))
+ ;
+
+ $tokenStorage
+ ->expects($this->once())
+ ->method('setToken')
+ ->with($this->equalTo($token))
+ ;
+
+ $manager
+ ->expects($this->once())
+ ->method('authenticate')
+ ->will($this->returnValue($token))
+ ;
+
+ $event = $this->getGetResponseEvent();
+ $event
+ ->expects($this->once())
+ ->method('getRequest')
+ ->will($this->returnValue(new Request()))
+ ;
+
+ $listener->handle($event);
+ }
+
+ public function testSessionStrategy()
+ {
+ list($listener, $tokenStorage, $service, $manager, , $dispatcher, $sessionStrategy) = $this->getListener(false, true, true);
+
+ $tokenStorage
+ ->expects($this->once())
+ ->method('getToken')
+ ->will($this->returnValue(null))
+ ;
+
+ $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface');
+ $service
+ ->expects($this->once())
+ ->method('autoLogin')
+ ->will($this->returnValue($token))
+ ;
+
+ $tokenStorage
+ ->expects($this->once())
+ ->method('setToken')
+ ->with($this->equalTo($token))
+ ;
+
+ $manager
+ ->expects($this->once())
+ ->method('authenticate')
+ ->will($this->returnValue($token))
+ ;
+
+ $session = $this->getMock('\Symfony\Component\HttpFoundation\Session\SessionInterface');
+ $session
+ ->expects($this->once())
+ ->method('isStarted')
+ ->will($this->returnValue(true))
+ ;
+
+ $request = $this->getMock('\Symfony\Component\HttpFoundation\Request');
+ $request
+ ->expects($this->once())
+ ->method('hasSession')
+ ->will($this->returnValue(true))
+ ;
+
+ $request
+ ->expects($this->once())
+ ->method('getSession')
+ ->will($this->returnValue($session))
+ ;
+
+ $event = $this->getGetResponseEvent();
+ $event
+ ->expects($this->once())
+ ->method('getRequest')
+ ->will($this->returnValue($request))
+ ;
+
+ $sessionStrategy
+ ->expects($this->once())
+ ->method('onAuthentication')
+ ->will($this->returnValue(null))
+ ;
+
+ $listener->handle($event);
+ }
+
+ public function testOnCoreSecurityInteractiveLoginEventIsDispatchedIfDispatcherIsPresent()
+ {
+ list($listener, $tokenStorage, $service, $manager, , $dispatcher) = $this->getListener(true);
+
+ $tokenStorage
+ ->expects($this->once())
+ ->method('getToken')
+ ->will($this->returnValue(null))
+ ;
+
+ $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface');
+ $service
+ ->expects($this->once())
+ ->method('autoLogin')
+ ->will($this->returnValue($token))
+ ;
+
+ $tokenStorage
+ ->expects($this->once())
+ ->method('setToken')
+ ->with($this->equalTo($token))
+ ;
+
+ $manager
+ ->expects($this->once())
+ ->method('authenticate')
+ ->will($this->returnValue($token))
+ ;
+
+ $event = $this->getGetResponseEvent();
+ $request = new Request();
+ $event
+ ->expects($this->once())
+ ->method('getRequest')
+ ->will($this->returnValue($request))
+ ;
+
+ $dispatcher
+ ->expects($this->once())
+ ->method('dispatch')
+ ->with(
+ SecurityEvents::INTERACTIVE_LOGIN,
+ $this->isInstanceOf('Symfony\Component\Security\Http\Event\InteractiveLoginEvent')
+ )
+ ;
+
+ $listener->handle($event);
+ }
+
+ protected function getGetResponseEvent()
+ {
+ return $this->getMock('Symfony\Component\HttpKernel\Event\GetResponseEvent', array(), array(), '', false);
+ }
+
+ protected function getFilterResponseEvent()
+ {
+ return $this->getMock('Symfony\Component\HttpKernel\Event\FilterResponseEvent', array(), array(), '', false);
+ }
+
+ protected function getListener($withDispatcher = false, $catchExceptions = true, $withSessionStrategy = false)
+ {
+ $listener = new RememberMeListener(
+ $tokenStorage = $this->getTokenStorage(),
+ $service = $this->getService(),
+ $manager = $this->getManager(),
+ $logger = $this->getLogger(),
+ $dispatcher = ($withDispatcher ? $this->getDispatcher() : null),
+ $catchExceptions,
+ $sessionStrategy = ($withSessionStrategy ? $this->getSessionStrategy() : null)
+ );
+
+ return array($listener, $tokenStorage, $service, $manager, $logger, $dispatcher, $sessionStrategy);
+ }
+
+ protected function getLogger()
+ {
+ return $this->getMock('Psr\Log\LoggerInterface');
+ }
+
+ protected function getManager()
+ {
+ return $this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface');
+ }
+
+ protected function getService()
+ {
+ return $this->getMock('Symfony\Component\Security\Http\RememberMe\RememberMeServicesInterface');
+ }
+
+ protected function getTokenStorage()
+ {
+ return $this->getMock('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface');
+ }
+
+ protected function getDispatcher()
+ {
+ return $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface');
+ }
+
+ private function getSessionStrategy()
+ {
+ return $this->getMock('\Symfony\Component\Security\Http\Session\SessionAuthenticationStrategyInterface');
+ }
+}
diff --git a/Http/Tests/Firewall/RemoteUserAuthenticationListenerTest.php b/Http/Tests/Firewall/RemoteUserAuthenticationListenerTest.php
new file mode 100644
index 0000000..dad7aad
--- /dev/null
+++ b/Http/Tests/Firewall/RemoteUserAuthenticationListenerTest.php
@@ -0,0 +1,91 @@
+<?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\Tests\Firewall;
+
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\Security\Http\Firewall\RemoteUserAuthenticationListener;
+
+class RemoteUserAuthenticationListenerTest extends \PHPUnit_Framework_TestCase
+{
+ public function testGetPreAuthenticatedData()
+ {
+ $serverVars = array(
+ 'REMOTE_USER' => 'TheUser',
+ );
+
+ $request = new Request(array(), array(), array(), array(), array(), $serverVars);
+
+ $tokenStorage = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface');
+
+ $authenticationManager = $this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface');
+
+ $listener = new RemoteUserAuthenticationListener(
+ $tokenStorage,
+ $authenticationManager,
+ 'TheProviderKey'
+ );
+
+ $method = new \ReflectionMethod($listener, 'getPreAuthenticatedData');
+ $method->setAccessible(true);
+
+ $result = $method->invokeArgs($listener, array($request));
+ $this->assertSame($result, array('TheUser', null));
+ }
+
+ /**
+ * @expectedException \Symfony\Component\Security\Core\Exception\BadCredentialsException
+ */
+ public function testGetPreAuthenticatedDataNoUser()
+ {
+ $request = new Request(array(), array(), array(), array(), array(), array());
+
+ $tokenStorage = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface');
+
+ $authenticationManager = $this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface');
+
+ $listener = new RemoteUserAuthenticationListener(
+ $tokenStorage,
+ $authenticationManager,
+ 'TheProviderKey'
+ );
+
+ $method = new \ReflectionMethod($listener, 'getPreAuthenticatedData');
+ $method->setAccessible(true);
+
+ $result = $method->invokeArgs($listener, array($request));
+ }
+
+ public function testGetPreAuthenticatedDataWithDifferentKeys()
+ {
+ $userCredentials = array('TheUser', null);
+
+ $request = new Request(array(), array(), array(), array(), array(), array(
+ 'TheUserKey' => 'TheUser',
+ ));
+ $tokenStorage = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface');
+
+ $authenticationManager = $this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface');
+
+ $listener = new RemoteUserAuthenticationListener(
+ $tokenStorage,
+ $authenticationManager,
+ 'TheProviderKey',
+ 'TheUserKey'
+ );
+
+ $method = new \ReflectionMethod($listener, 'getPreAuthenticatedData');
+ $method->setAccessible(true);
+
+ $result = $method->invokeArgs($listener, array($request));
+ $this->assertSame($result, $userCredentials);
+ }
+}
diff --git a/Http/Tests/Firewall/SimplePreAuthenticationListenerTest.php b/Http/Tests/Firewall/SimplePreAuthenticationListenerTest.php
new file mode 100644
index 0000000..0a1286c
--- /dev/null
+++ b/Http/Tests/Firewall/SimplePreAuthenticationListenerTest.php
@@ -0,0 +1,128 @@
+<?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\Tests\Firewall;
+
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\Security\Core\Exception\AuthenticationException;
+use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
+use Symfony\Component\Security\Http\Firewall\SimplePreAuthenticationListener;
+use Symfony\Component\Security\Http\SecurityEvents;
+
+class SimplePreAuthenticationListenerTest extends \PHPUnit_Framework_TestCase
+{
+ private $authenticationManager;
+ private $dispatcher;
+ private $event;
+ private $logger;
+ private $request;
+ private $tokenStorage;
+ private $token;
+
+ public function testHandle()
+ {
+ $this->tokenStorage
+ ->expects($this->once())
+ ->method('setToken')
+ ->with($this->equalTo($this->token))
+ ;
+
+ $this->authenticationManager
+ ->expects($this->once())
+ ->method('authenticate')
+ ->with($this->equalTo($this->token))
+ ->will($this->returnValue($this->token))
+ ;
+
+ $simpleAuthenticator = $this->getMock('Symfony\Component\Security\Core\Authentication\SimplePreAuthenticatorInterface');
+ $simpleAuthenticator
+ ->expects($this->once())
+ ->method('createToken')
+ ->with($this->equalTo($this->request), $this->equalTo('secured_area'))
+ ->will($this->returnValue($this->token))
+ ;
+
+ $loginEvent = new InteractiveLoginEvent($this->request, $this->token);
+
+ $this->dispatcher
+ ->expects($this->once())
+ ->method('dispatch')
+ ->with($this->equalTo(SecurityEvents::INTERACTIVE_LOGIN), $this->equalTo($loginEvent))
+ ;
+
+ $listener = new SimplePreAuthenticationListener($this->tokenStorage, $this->authenticationManager, 'secured_area', $simpleAuthenticator, $this->logger, $this->dispatcher);
+
+ $listener->handle($this->event);
+ }
+
+ public function testHandlecatchAuthenticationException()
+ {
+ $exception = new AuthenticationException('Authentication failed.');
+
+ $this->authenticationManager
+ ->expects($this->once())
+ ->method('authenticate')
+ ->with($this->equalTo($this->token))
+ ->will($this->throwException($exception))
+ ;
+
+ $this->tokenStorage->expects($this->once())
+ ->method('setToken')
+ ->with($this->equalTo(null))
+ ;
+
+ $simpleAuthenticator = $this->getMock('Symfony\Component\Security\Core\Authentication\SimplePreAuthenticatorInterface');
+ $simpleAuthenticator
+ ->expects($this->once())
+ ->method('createToken')
+ ->with($this->equalTo($this->request), $this->equalTo('secured_area'))
+ ->will($this->returnValue($this->token))
+ ;
+
+ $listener = new SimplePreAuthenticationListener($this->tokenStorage, $this->authenticationManager, 'secured_area', $simpleAuthenticator, $this->logger, $this->dispatcher);
+
+ $listener->handle($this->event);
+ }
+
+ protected function setUp()
+ {
+ $this->authenticationManager = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\AuthenticationProviderManager')
+ ->disableOriginalConstructor()
+ ->getMock()
+ ;
+
+ $this->dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface');
+
+ $this->request = new Request(array(), array(), array(), array(), array(), array());
+
+ $this->event = $this->getMock('Symfony\Component\HttpKernel\Event\GetResponseEvent', array(), array(), '', false);
+ $this->event
+ ->expects($this->any())
+ ->method('getRequest')
+ ->will($this->returnValue($this->request))
+ ;
+
+ $this->logger = $this->getMock('Psr\Log\LoggerInterface');
+ $this->tokenStorage = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface');
+ $this->token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface');
+ }
+
+ protected function tearDown()
+ {
+ $this->authenticationManager = null;
+ $this->dispatcher = null;
+ $this->event = null;
+ $this->logger = null;
+ $this->request = null;
+ $this->tokenStorage = null;
+ $this->token = null;
+ }
+}
diff --git a/Http/Tests/Firewall/SwitchUserListenerTest.php b/Http/Tests/Firewall/SwitchUserListenerTest.php
new file mode 100644
index 0000000..f43b564
--- /dev/null
+++ b/Http/Tests/Firewall/SwitchUserListenerTest.php
@@ -0,0 +1,260 @@
+<?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\Tests\Firewall;
+
+use Symfony\Component\Security\Http\Event\SwitchUserEvent;
+use Symfony\Component\Security\Http\Firewall\SwitchUserListener;
+use Symfony\Component\Security\Http\SecurityEvents;
+
+class SwitchUserListenerTest extends \PHPUnit_Framework_TestCase
+{
+ private $tokenStorage;
+
+ private $userProvider;
+
+ private $userChecker;
+
+ private $accessDecisionManager;
+
+ private $request;
+
+ private $event;
+
+ protected function setUp()
+ {
+ $this->tokenStorage = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface');
+ $this->userProvider = $this->getMock('Symfony\Component\Security\Core\User\UserProviderInterface');
+ $this->userChecker = $this->getMock('Symfony\Component\Security\Core\User\UserCheckerInterface');
+ $this->accessDecisionManager = $this->getMock('Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface');
+ $this->request = $this->getMock('Symfony\Component\HttpFoundation\Request');
+ $this->request->query = $this->getMock('Symfony\Component\HttpFoundation\ParameterBag');
+ $this->request->server = $this->getMock('Symfony\Component\HttpFoundation\ServerBag');
+ $this->event = $this->getEvent($this->request);
+ }
+
+ /**
+ * @expectedException \InvalidArgumentException
+ * @expectedExceptionMessage $providerKey must not be empty
+ */
+ public function testProviderKeyIsRequired()
+ {
+ new SwitchUserListener($this->tokenStorage, $this->userProvider, $this->userChecker, '', $this->accessDecisionManager);
+ }
+
+ public function testEventIsIgnoredIfUsernameIsNotPassedWithTheRequest()
+ {
+ $this->request->expects($this->any())->method('get')->with('_switch_user')->will($this->returnValue(null));
+
+ $this->event->expects($this->never())->method('setResponse');
+ $this->tokenStorage->expects($this->never())->method('setToken');
+
+ $listener = new SwitchUserListener($this->tokenStorage, $this->userProvider, $this->userChecker, 'provider123', $this->accessDecisionManager);
+ $listener->handle($this->event);
+ }
+
+ /**
+ * @expectedException \Symfony\Component\Security\Core\Exception\AuthenticationCredentialsNotFoundException
+ */
+ public function testExitUserThrowsAuthenticationExceptionIfOriginalTokenCannotBeFound()
+ {
+ $token = $this->getToken(array($this->getMock('Symfony\Component\Security\Core\Role\RoleInterface')));
+
+ $this->tokenStorage->expects($this->any())->method('getToken')->will($this->returnValue($token));
+ $this->request->expects($this->any())->method('get')->with('_switch_user')->will($this->returnValue('_exit'));
+
+ $listener = new SwitchUserListener($this->tokenStorage, $this->userProvider, $this->userChecker, 'provider123', $this->accessDecisionManager);
+ $listener->handle($this->event);
+ }
+
+ public function testExitUserUpdatesToken()
+ {
+ $originalToken = $this->getToken();
+ $role = $this->getMockBuilder('Symfony\Component\Security\Core\Role\SwitchUserRole')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $role->expects($this->any())->method('getSource')->will($this->returnValue($originalToken));
+
+ $this->tokenStorage->expects($this->any())
+ ->method('getToken')
+ ->will($this->returnValue($this->getToken(array($role))));
+
+ $this->request->expects($this->any())->method('get')->with('_switch_user')->will($this->returnValue('_exit'));
+ $this->request->expects($this->any())->method('getUri')->will($this->returnValue('/'));
+ $this->request->query->expects($this->once())->method('remove', '_switch_user');
+ $this->request->query->expects($this->any())->method('all')->will($this->returnValue(array()));
+ $this->request->server->expects($this->once())->method('set')->with('QUERY_STRING', '');
+
+ $this->tokenStorage->expects($this->once())
+ ->method('setToken')->with($originalToken);
+ $this->event->expects($this->once())
+ ->method('setResponse')->with($this->isInstanceOf('Symfony\Component\HttpFoundation\RedirectResponse'));
+
+ $listener = new SwitchUserListener($this->tokenStorage, $this->userProvider, $this->userChecker, 'provider123', $this->accessDecisionManager);
+ $listener->handle($this->event);
+ }
+
+ public function testExitUserDispatchesEventWithRefreshedUser()
+ {
+ $originalUser = $this->getMock('Symfony\Component\Security\Core\User\UserInterface');
+ $refreshedUser = $this->getMock('Symfony\Component\Security\Core\User\UserInterface');
+ $this
+ ->userProvider
+ ->expects($this->any())
+ ->method('refreshUser')
+ ->with($originalUser)
+ ->willReturn($refreshedUser);
+ $originalToken = $this->getToken();
+ $originalToken
+ ->expects($this->any())
+ ->method('getUser')
+ ->willReturn($originalUser);
+ $role = $this
+ ->getMockBuilder('Symfony\Component\Security\Core\Role\SwitchUserRole')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $role->expects($this->any())->method('getSource')->willReturn($originalToken);
+ $this
+ ->tokenStorage
+ ->expects($this->any())
+ ->method('getToken')
+ ->willReturn($this->getToken(array($role)));
+ $this
+ ->request
+ ->expects($this->any())
+ ->method('get')
+ ->with('_switch_user')
+ ->willReturn('_exit');
+ $this
+ ->request
+ ->expects($this->any())
+ ->method('getUri')
+ ->willReturn('/');
+ $this
+ ->request
+ ->query
+ ->expects($this->any())
+ ->method('all')
+ ->will($this->returnValue(array()));
+
+ $dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface');
+ $dispatcher
+ ->expects($this->once())
+ ->method('dispatch')
+ ->with(SecurityEvents::SWITCH_USER, $this->callback(function (SwitchUserEvent $event) use ($refreshedUser) {
+ return $event->getTargetUser() === $refreshedUser;
+ }))
+ ;
+
+ $listener = new SwitchUserListener($this->tokenStorage, $this->userProvider, $this->userChecker, 'provider123', $this->accessDecisionManager, null, '_switch_user', 'ROLE_ALLOWED_TO_SWITCH', $dispatcher);
+ $listener->handle($this->event);
+ }
+
+ /**
+ * @expectedException \Symfony\Component\Security\Core\Exception\AccessDeniedException
+ */
+ public function testSwitchUserIsDisallowed()
+ {
+ $token = $this->getToken(array($this->getMock('Symfony\Component\Security\Core\Role\RoleInterface')));
+
+ $this->tokenStorage->expects($this->any())->method('getToken')->will($this->returnValue($token));
+ $this->request->expects($this->any())->method('get')->with('_switch_user')->will($this->returnValue('kuba'));
+
+ $this->accessDecisionManager->expects($this->once())
+ ->method('decide')->with($token, array('ROLE_ALLOWED_TO_SWITCH'))
+ ->will($this->returnValue(false));
+
+ $listener = new SwitchUserListener($this->tokenStorage, $this->userProvider, $this->userChecker, 'provider123', $this->accessDecisionManager);
+ $listener->handle($this->event);
+ }
+
+ public function testSwitchUser()
+ {
+ $token = $this->getToken(array($this->getMock('Symfony\Component\Security\Core\Role\RoleInterface')));
+ $user = $this->getMock('Symfony\Component\Security\Core\User\UserInterface');
+ $user->expects($this->any())->method('getRoles')->will($this->returnValue(array()));
+
+ $this->tokenStorage->expects($this->any())->method('getToken')->will($this->returnValue($token));
+ $this->request->expects($this->any())->method('get')->with('_switch_user')->will($this->returnValue('kuba'));
+ $this->request->query->expects($this->once())->method('remove', '_switch_user');
+ $this->request->query->expects($this->any())->method('all')->will($this->returnValue(array()));
+
+ $this->request->expects($this->any())->method('getUri')->will($this->returnValue('/'));
+ $this->request->server->expects($this->once())->method('set')->with('QUERY_STRING', '');
+
+ $this->accessDecisionManager->expects($this->once())
+ ->method('decide')->with($token, array('ROLE_ALLOWED_TO_SWITCH'))
+ ->will($this->returnValue(true));
+
+ $this->userProvider->expects($this->once())
+ ->method('loadUserByUsername')->with('kuba')
+ ->will($this->returnValue($user));
+ $this->userChecker->expects($this->once())
+ ->method('checkPostAuth')->with($user);
+ $this->tokenStorage->expects($this->once())
+ ->method('setToken')->with($this->isInstanceOf('Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken'));
+
+ $listener = new SwitchUserListener($this->tokenStorage, $this->userProvider, $this->userChecker, 'provider123', $this->accessDecisionManager);
+ $listener->handle($this->event);
+ }
+
+ public function testSwitchUserKeepsOtherQueryStringParameters()
+ {
+ $token = $this->getToken(array($this->getMock('Symfony\Component\Security\Core\Role\RoleInterface')));
+ $user = $this->getMock('Symfony\Component\Security\Core\User\UserInterface');
+ $user->expects($this->any())->method('getRoles')->will($this->returnValue(array()));
+
+ $this->tokenStorage->expects($this->any())->method('getToken')->will($this->returnValue($token));
+ $this->request->expects($this->any())->method('get')->with('_switch_user')->will($this->returnValue('kuba'));
+ $this->request->query->expects($this->once())->method('remove', '_switch_user');
+ $this->request->query->expects($this->any())->method('all')->will($this->returnValue(array('page' => 3, 'section' => 2)));
+ $this->request->expects($this->any())->method('getUri')->will($this->returnValue('/'));
+ $this->request->server->expects($this->once())->method('set')->with('QUERY_STRING', 'page=3&section=2');
+
+ $this->accessDecisionManager->expects($this->once())
+ ->method('decide')->with($token, array('ROLE_ALLOWED_TO_SWITCH'))
+ ->will($this->returnValue(true));
+
+ $this->userProvider->expects($this->once())
+ ->method('loadUserByUsername')->with('kuba')
+ ->will($this->returnValue($user));
+ $this->userChecker->expects($this->once())
+ ->method('checkPostAuth')->with($user);
+ $this->tokenStorage->expects($this->once())
+ ->method('setToken')->with($this->isInstanceOf('Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken'));
+
+ $listener = new SwitchUserListener($this->tokenStorage, $this->userProvider, $this->userChecker, 'provider123', $this->accessDecisionManager);
+ $listener->handle($this->event);
+ }
+
+ private function getEvent($request)
+ {
+ $event = $this->getMockBuilder('Symfony\Component\HttpKernel\Event\GetResponseEvent')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $event->expects($this->any())
+ ->method('getRequest')
+ ->will($this->returnValue($request));
+
+ return $event;
+ }
+
+ private function getToken(array $roles = array())
+ {
+ $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface');
+ $token->expects($this->any())
+ ->method('getRoles')
+ ->will($this->returnValue($roles));
+
+ return $token;
+ }
+}
diff --git a/Http/Tests/Firewall/X509AuthenticationListenerTest.php b/Http/Tests/Firewall/X509AuthenticationListenerTest.php
new file mode 100644
index 0000000..66690d9
--- /dev/null
+++ b/Http/Tests/Firewall/X509AuthenticationListenerTest.php
@@ -0,0 +1,123 @@
+<?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\Tests\Firewall;
+
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\Security\Http\Firewall\X509AuthenticationListener;
+
+class X509AuthenticationListenerTest extends \PHPUnit_Framework_TestCase
+{
+ /**
+ * @dataProvider dataProviderGetPreAuthenticatedData
+ */
+ public function testGetPreAuthenticatedData($user, $credentials)
+ {
+ $serverVars = array();
+ if ('' !== $user) {
+ $serverVars['SSL_CLIENT_S_DN_Email'] = $user;
+ }
+ if ('' !== $credentials) {
+ $serverVars['SSL_CLIENT_S_DN'] = $credentials;
+ }
+
+ $request = new Request(array(), array(), array(), array(), array(), $serverVars);
+
+ $tokenStorage = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface');
+
+ $authenticationManager = $this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface');
+
+ $listener = new X509AuthenticationListener($tokenStorage, $authenticationManager, 'TheProviderKey');
+
+ $method = new \ReflectionMethod($listener, 'getPreAuthenticatedData');
+ $method->setAccessible(true);
+
+ $result = $method->invokeArgs($listener, array($request));
+ $this->assertSame($result, array($user, $credentials));
+ }
+
+ public static function dataProviderGetPreAuthenticatedData()
+ {
+ return array(
+ 'validValues' => array('TheUser', 'TheCredentials'),
+ 'noCredentials' => array('TheUser', ''),
+ );
+ }
+
+ /**
+ * @dataProvider dataProviderGetPreAuthenticatedDataNoUser
+ */
+ public function testGetPreAuthenticatedDataNoUser($emailAddress)
+ {
+ $credentials = 'CN=Sample certificate DN/emailAddress='.$emailAddress;
+ $request = new Request(array(), array(), array(), array(), array(), array('SSL_CLIENT_S_DN' => $credentials));
+
+ $tokenStorage = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface');
+
+ $authenticationManager = $this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface');
+
+ $listener = new X509AuthenticationListener($tokenStorage, $authenticationManager, 'TheProviderKey');
+
+ $method = new \ReflectionMethod($listener, 'getPreAuthenticatedData');
+ $method->setAccessible(true);
+
+ $result = $method->invokeArgs($listener, array($request));
+ $this->assertSame($result, array($emailAddress, $credentials));
+ }
+
+ public static function dataProviderGetPreAuthenticatedDataNoUser()
+ {
+ return array(
+ 'basicEmailAddress' => array('cert@example.com'),
+ 'emailAddressWithPlusSign' => array('cert+something@example.com'),
+ );
+ }
+
+ /**
+ * @expectedException \Symfony\Component\Security\Core\Exception\BadCredentialsException
+ */
+ public function testGetPreAuthenticatedDataNoData()
+ {
+ $request = new Request(array(), array(), array(), array(), array(), array());
+
+ $tokenStorage = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface');
+
+ $authenticationManager = $this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface');
+
+ $listener = new X509AuthenticationListener($tokenStorage, $authenticationManager, 'TheProviderKey');
+
+ $method = new \ReflectionMethod($listener, 'getPreAuthenticatedData');
+ $method->setAccessible(true);
+
+ $result = $method->invokeArgs($listener, array($request));
+ }
+
+ public function testGetPreAuthenticatedDataWithDifferentKeys()
+ {
+ $userCredentials = array('TheUser', 'TheCredentials');
+
+ $request = new Request(array(), array(), array(), array(), array(), array(
+ 'TheUserKey' => 'TheUser',
+ 'TheCredentialsKey' => 'TheCredentials',
+ ));
+ $tokenStorage = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface');
+
+ $authenticationManager = $this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface');
+
+ $listener = new X509AuthenticationListener($tokenStorage, $authenticationManager, 'TheProviderKey', 'TheUserKey', 'TheCredentialsKey');
+
+ $method = new \ReflectionMethod($listener, 'getPreAuthenticatedData');
+ $method->setAccessible(true);
+
+ $result = $method->invokeArgs($listener, array($request));
+ $this->assertSame($result, $userCredentials);
+ }
+}
diff --git a/Http/Tests/FirewallMapTest.php b/Http/Tests/FirewallMapTest.php
new file mode 100644
index 0000000..85a57ab
--- /dev/null
+++ b/Http/Tests/FirewallMapTest.php
@@ -0,0 +1,117 @@
+<?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\Tests;
+
+use Symfony\Component\Security\Http\FirewallMap;
+use Symfony\Component\HttpFoundation\Request;
+
+class FirewallMapTest extends \PHPUnit_Framework_TestCase
+{
+ public function testGetListeners()
+ {
+ $map = new FirewallMap();
+
+ $request = new Request();
+
+ $notMatchingMatcher = $this->getMock('Symfony\Component\HttpFoundation\RequestMatcher');
+ $notMatchingMatcher
+ ->expects($this->once())
+ ->method('matches')
+ ->with($this->equalTo($request))
+ ->will($this->returnValue(false))
+ ;
+
+ $map->add($notMatchingMatcher, array($this->getMock('Symfony\Component\Security\Http\Firewall\ListenerInterface')));
+
+ $matchingMatcher = $this->getMock('Symfony\Component\HttpFoundation\RequestMatcher');
+ $matchingMatcher
+ ->expects($this->once())
+ ->method('matches')
+ ->with($this->equalTo($request))
+ ->will($this->returnValue(true))
+ ;
+ $theListener = $this->getMock('Symfony\Component\Security\Http\Firewall\ListenerInterface');
+ $theException = $this->getMock('Symfony\Component\Security\Http\Firewall\ExceptionListener', array(), array(), '', false);
+
+ $map->add($matchingMatcher, array($theListener), $theException);
+
+ $tooLateMatcher = $this->getMock('Symfony\Component\HttpFoundation\RequestMatcher');
+ $tooLateMatcher
+ ->expects($this->never())
+ ->method('matches')
+ ;
+
+ $map->add($tooLateMatcher, array($this->getMock('Symfony\Component\Security\Http\Firewall\ListenerInterface')));
+
+ list($listeners, $exception) = $map->getListeners($request);
+
+ $this->assertEquals(array($theListener), $listeners);
+ $this->assertEquals($theException, $exception);
+ }
+
+ public function testGetListenersWithAnEntryHavingNoRequestMatcher()
+ {
+ $map = new FirewallMap();
+
+ $request = new Request();
+
+ $notMatchingMatcher = $this->getMock('Symfony\Component\HttpFoundation\RequestMatcher');
+ $notMatchingMatcher
+ ->expects($this->once())
+ ->method('matches')
+ ->with($this->equalTo($request))
+ ->will($this->returnValue(false))
+ ;
+
+ $map->add($notMatchingMatcher, array($this->getMock('Symfony\Component\Security\Http\Firewall\ListenerInterface')));
+
+ $theListener = $this->getMock('Symfony\Component\Security\Http\Firewall\ListenerInterface');
+ $theException = $this->getMock('Symfony\Component\Security\Http\Firewall\ExceptionListener', array(), array(), '', false);
+
+ $map->add(null, array($theListener), $theException);
+
+ $tooLateMatcher = $this->getMock('Symfony\Component\HttpFoundation\RequestMatcher');
+ $tooLateMatcher
+ ->expects($this->never())
+ ->method('matches')
+ ;
+
+ $map->add($tooLateMatcher, array($this->getMock('Symfony\Component\Security\Http\Firewall\ListenerInterface')));
+
+ list($listeners, $exception) = $map->getListeners($request);
+
+ $this->assertEquals(array($theListener), $listeners);
+ $this->assertEquals($theException, $exception);
+ }
+
+ public function testGetListenersWithNoMatchingEntry()
+ {
+ $map = new FirewallMap();
+
+ $request = new Request();
+
+ $notMatchingMatcher = $this->getMock('Symfony\Component\HttpFoundation\RequestMatcher');
+ $notMatchingMatcher
+ ->expects($this->once())
+ ->method('matches')
+ ->with($this->equalTo($request))
+ ->will($this->returnValue(false))
+ ;
+
+ $map->add($notMatchingMatcher, array($this->getMock('Symfony\Component\Security\Http\Firewall\ListenerInterface')));
+
+ list($listeners, $exception) = $map->getListeners($request);
+
+ $this->assertEquals(array(), $listeners);
+ $this->assertNull($exception);
+ }
+}
diff --git a/Http/Tests/FirewallTest.php b/Http/Tests/FirewallTest.php
new file mode 100644
index 0000000..9994737
--- /dev/null
+++ b/Http/Tests/FirewallTest.php
@@ -0,0 +1,108 @@
+<?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\Tests;
+
+use Symfony\Component\Security\Http\Firewall;
+use Symfony\Component\HttpKernel\Event\GetResponseEvent;
+use Symfony\Component\HttpKernel\HttpKernelInterface;
+
+class FirewallTest extends \PHPUnit_Framework_TestCase
+{
+ public function testOnKernelRequestRegistersExceptionListener()
+ {
+ $dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface');
+
+ $listener = $this->getMock('Symfony\Component\Security\Http\Firewall\ExceptionListener', array(), array(), '', false);
+ $listener
+ ->expects($this->once())
+ ->method('register')
+ ->with($this->equalTo($dispatcher))
+ ;
+
+ $request = $this->getMock('Symfony\Component\HttpFoundation\Request', array(), array(), '', false, false);
+
+ $map = $this->getMock('Symfony\Component\Security\Http\FirewallMapInterface');
+ $map
+ ->expects($this->once())
+ ->method('getListeners')
+ ->with($this->equalTo($request))
+ ->will($this->returnValue(array(array(), $listener)))
+ ;
+
+ $event = new GetResponseEvent($this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface'), $request, HttpKernelInterface::MASTER_REQUEST);
+
+ $firewall = new Firewall($map, $dispatcher);
+ $firewall->onKernelRequest($event);
+ }
+
+ public function testOnKernelRequestStopsWhenThereIsAResponse()
+ {
+ $response = $this->getMock('Symfony\Component\HttpFoundation\Response');
+
+ $first = $this->getMock('Symfony\Component\Security\Http\Firewall\ListenerInterface');
+ $first
+ ->expects($this->once())
+ ->method('handle')
+ ;
+
+ $second = $this->getMock('Symfony\Component\Security\Http\Firewall\ListenerInterface');
+ $second
+ ->expects($this->never())
+ ->method('handle')
+ ;
+
+ $map = $this->getMock('Symfony\Component\Security\Http\FirewallMapInterface');
+ $map
+ ->expects($this->once())
+ ->method('getListeners')
+ ->will($this->returnValue(array(array($first, $second), null)))
+ ;
+
+ $event = $this->getMock(
+ 'Symfony\Component\HttpKernel\Event\GetResponseEvent',
+ array('hasResponse'),
+ array(
+ $this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface'),
+ $this->getMock('Symfony\Component\HttpFoundation\Request', array(), array(), '', false, false),
+ HttpKernelInterface::MASTER_REQUEST,
+ )
+ );
+ $event
+ ->expects($this->once())
+ ->method('hasResponse')
+ ->will($this->returnValue(true))
+ ;
+
+ $firewall = new Firewall($map, $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface'));
+ $firewall->onKernelRequest($event);
+ }
+
+ public function testOnKernelRequestWithSubRequest()
+ {
+ $map = $this->getMock('Symfony\Component\Security\Http\FirewallMapInterface');
+ $map
+ ->expects($this->never())
+ ->method('getListeners')
+ ;
+
+ $event = new GetResponseEvent(
+ $this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface'),
+ $this->getMock('Symfony\Component\HttpFoundation\Request'),
+ HttpKernelInterface::SUB_REQUEST
+ );
+
+ $firewall = new Firewall($map, $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface'));
+ $firewall->onKernelRequest($event);
+
+ $this->assertFalse($event->hasResponse());
+ }
+}
diff --git a/Http/Tests/HttpUtilsTest.php b/Http/Tests/HttpUtilsTest.php
new file mode 100644
index 0000000..45a0281
--- /dev/null
+++ b/Http/Tests/HttpUtilsTest.php
@@ -0,0 +1,267 @@
+<?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\Tests;
+
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\Routing\Exception\MethodNotAllowedException;
+use Symfony\Component\Routing\Exception\ResourceNotFoundException;
+use Symfony\Component\Security\Core\Security;
+use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
+use Symfony\Component\Security\Http\HttpUtils;
+
+class HttpUtilsTest extends \PHPUnit_Framework_TestCase
+{
+ public function testCreateRedirectResponseWithPath()
+ {
+ $utils = new HttpUtils($this->getUrlGenerator());
+ $response = $utils->createRedirectResponse($this->getRequest(), '/foobar');
+
+ $this->assertTrue($response->isRedirect('http://localhost/foobar'));
+ $this->assertEquals(302, $response->getStatusCode());
+ }
+
+ public function testCreateRedirectResponseWithAbsoluteUrl()
+ {
+ $utils = new HttpUtils($this->getUrlGenerator());
+ $response = $utils->createRedirectResponse($this->getRequest(), 'http://symfony.com/');
+
+ $this->assertTrue($response->isRedirect('http://symfony.com/'));
+ }
+
+ public function testCreateRedirectResponseWithRouteName()
+ {
+ $utils = new HttpUtils($urlGenerator = $this->getMock('Symfony\Component\Routing\Generator\UrlGeneratorInterface'));
+
+ $urlGenerator
+ ->expects($this->any())
+ ->method('generate')
+ ->with('foobar', array(), UrlGeneratorInterface::ABSOLUTE_URL)
+ ->will($this->returnValue('http://localhost/foo/bar'))
+ ;
+ $urlGenerator
+ ->expects($this->any())
+ ->method('getContext')
+ ->will($this->returnValue($this->getMock('Symfony\Component\Routing\RequestContext')))
+ ;
+
+ $response = $utils->createRedirectResponse($this->getRequest(), 'foobar');
+
+ $this->assertTrue($response->isRedirect('http://localhost/foo/bar'));
+ }
+
+ public function testCreateRequestWithPath()
+ {
+ $request = $this->getRequest();
+ $request->server->set('Foo', 'bar');
+
+ $utils = new HttpUtils($this->getUrlGenerator());
+ $subRequest = $utils->createRequest($request, '/foobar');
+
+ $this->assertEquals('GET', $subRequest->getMethod());
+ $this->assertEquals('/foobar', $subRequest->getPathInfo());
+ $this->assertEquals('bar', $subRequest->server->get('Foo'));
+ }
+
+ public function testCreateRequestWithRouteName()
+ {
+ $utils = new HttpUtils($urlGenerator = $this->getMock('Symfony\Component\Routing\Generator\UrlGeneratorInterface'));
+
+ $urlGenerator
+ ->expects($this->once())
+ ->method('generate')
+ ->will($this->returnValue('/foo/bar'))
+ ;
+ $urlGenerator
+ ->expects($this->any())
+ ->method('getContext')
+ ->will($this->returnValue($this->getMock('Symfony\Component\Routing\RequestContext')))
+ ;
+
+ $subRequest = $utils->createRequest($this->getRequest(), 'foobar');
+
+ $this->assertEquals('/foo/bar', $subRequest->getPathInfo());
+ }
+
+ public function testCreateRequestWithAbsoluteUrl()
+ {
+ $utils = new HttpUtils($this->getMock('Symfony\Component\Routing\Generator\UrlGeneratorInterface'));
+ $subRequest = $utils->createRequest($this->getRequest(), 'http://symfony.com/');
+
+ $this->assertEquals('/', $subRequest->getPathInfo());
+ }
+
+ public function testCreateRequestPassesSessionToTheNewRequest()
+ {
+ $request = $this->getRequest();
+ $request->setSession($session = $this->getMock('Symfony\Component\HttpFoundation\Session\SessionInterface'));
+
+ $utils = new HttpUtils($this->getUrlGenerator());
+ $subRequest = $utils->createRequest($request, '/foobar');
+
+ $this->assertSame($session, $subRequest->getSession());
+ }
+
+ /**
+ * @dataProvider provideSecurityContextAttributes
+ */
+ public function testCreateRequestPassesSecurityContextAttributesToTheNewRequest($attribute)
+ {
+ $request = $this->getRequest();
+ $request->attributes->set($attribute, 'foo');
+
+ $utils = new HttpUtils($this->getUrlGenerator());
+ $subRequest = $utils->createRequest($request, '/foobar');
+
+ $this->assertSame('foo', $subRequest->attributes->get($attribute));
+ }
+
+ public function provideSecurityContextAttributes()
+ {
+ return array(
+ array(Security::AUTHENTICATION_ERROR),
+ array(Security::ACCESS_DENIED_ERROR),
+ array(Security::LAST_USERNAME),
+ );
+ }
+
+ public function testCheckRequestPath()
+ {
+ $utils = new HttpUtils($this->getUrlGenerator());
+
+ $this->assertTrue($utils->checkRequestPath($this->getRequest(), '/'));
+ $this->assertFalse($utils->checkRequestPath($this->getRequest(), '/foo'));
+ $this->assertTrue($utils->checkRequestPath($this->getRequest('/foo%20bar'), '/foo bar'));
+ // Plus must not decoded to space
+ $this->assertTrue($utils->checkRequestPath($this->getRequest('/foo+bar'), '/foo+bar'));
+ // Checking unicode
+ $this->assertTrue($utils->checkRequestPath($this->getRequest(urlencode('/вход')), '/вход'));
+ }
+
+ public function testCheckRequestPathWithUrlMatcherAndResourceNotFound()
+ {
+ $urlMatcher = $this->getMock('Symfony\Component\Routing\Matcher\UrlMatcherInterface');
+ $urlMatcher
+ ->expects($this->any())
+ ->method('match')
+ ->with('/')
+ ->will($this->throwException(new ResourceNotFoundException()))
+ ;
+
+ $utils = new HttpUtils(null, $urlMatcher);
+ $this->assertFalse($utils->checkRequestPath($this->getRequest(), 'foobar'));
+ }
+
+ public function testCheckRequestPathWithUrlMatcherAndMethodNotAllowed()
+ {
+ $request = $this->getRequest();
+ $urlMatcher = $this->getMock('Symfony\Component\Routing\Matcher\RequestMatcherInterface');
+ $urlMatcher
+ ->expects($this->any())
+ ->method('matchRequest')
+ ->with($request)
+ ->will($this->throwException(new MethodNotAllowedException(array())))
+ ;
+
+ $utils = new HttpUtils(null, $urlMatcher);
+ $this->assertFalse($utils->checkRequestPath($request, 'foobar'));
+ }
+
+ public function testCheckRequestPathWithUrlMatcherAndResourceFoundByUrl()
+ {
+ $urlMatcher = $this->getMock('Symfony\Component\Routing\Matcher\UrlMatcherInterface');
+ $urlMatcher
+ ->expects($this->any())
+ ->method('match')
+ ->with('/foo/bar')
+ ->will($this->returnValue(array('_route' => 'foobar')))
+ ;
+
+ $utils = new HttpUtils(null, $urlMatcher);
+ $this->assertTrue($utils->checkRequestPath($this->getRequest('/foo/bar'), 'foobar'));
+ }
+
+ public function testCheckRequestPathWithUrlMatcherAndResourceFoundByRequest()
+ {
+ $request = $this->getRequest();
+ $urlMatcher = $this->getMock('Symfony\Component\Routing\Matcher\RequestMatcherInterface');
+ $urlMatcher
+ ->expects($this->any())
+ ->method('matchRequest')
+ ->with($request)
+ ->will($this->returnValue(array('_route' => 'foobar')))
+ ;
+
+ $utils = new HttpUtils(null, $urlMatcher);
+ $this->assertTrue($utils->checkRequestPath($request, 'foobar'));
+ }
+
+ /**
+ * @expectedException \RuntimeException
+ */
+ public function testCheckRequestPathWithUrlMatcherLoadingException()
+ {
+ $urlMatcher = $this->getMock('Symfony\Component\Routing\Matcher\UrlMatcherInterface');
+ $urlMatcher
+ ->expects($this->any())
+ ->method('match')
+ ->will($this->throwException(new \RuntimeException()))
+ ;
+
+ $utils = new HttpUtils(null, $urlMatcher);
+ $utils->checkRequestPath($this->getRequest(), 'foobar');
+ }
+
+ /**
+ * @expectedException \InvalidArgumentException
+ * @expectedExceptionMessage Matcher must either implement UrlMatcherInterface or RequestMatcherInterface
+ */
+ public function testUrlMatcher()
+ {
+ new HttpUtils($this->getUrlGenerator(), new \stdClass());
+ }
+
+ public function testGenerateUriRemovesQueryString()
+ {
+ $utils = new HttpUtils($this->getUrlGenerator('/foo/bar'));
+ $this->assertEquals('/foo/bar', $utils->generateUri(new Request(), 'route_name'));
+
+ $utils = new HttpUtils($this->getUrlGenerator('/foo/bar?param=value'));
+ $this->assertEquals('/foo/bar', $utils->generateUri(new Request(), 'route_name'));
+ }
+
+ /**
+ * @expectedException \LogicException
+ * @expectedExceptionMessage You must provide a UrlGeneratorInterface instance to be able to use routes.
+ */
+ public function testUrlGeneratorIsRequiredToGenerateUrl()
+ {
+ $utils = new HttpUtils();
+ $utils->generateUri(new Request(), 'route_name');
+ }
+
+ private function getUrlGenerator($generatedUrl = '/foo/bar')
+ {
+ $urlGenerator = $this->getMock('Symfony\Component\Routing\Generator\UrlGeneratorInterface');
+ $urlGenerator
+ ->expects($this->any())
+ ->method('generate')
+ ->will($this->returnValue($generatedUrl))
+ ;
+
+ return $urlGenerator;
+ }
+
+ private function getRequest($path = '/')
+ {
+ return Request::create($path, 'get');
+ }
+}
diff --git a/Http/Tests/Logout/CookieClearingLogoutHandlerTest.php b/Http/Tests/Logout/CookieClearingLogoutHandlerTest.php
new file mode 100644
index 0000000..8474504
--- /dev/null
+++ b/Http/Tests/Logout/CookieClearingLogoutHandlerTest.php
@@ -0,0 +1,49 @@
+<?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\Tests\Logout;
+
+use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\HttpFoundation\ResponseHeaderBag;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\Security\Http\Logout\CookieClearingLogoutHandler;
+
+class CookieClearingLogoutHandlerTest extends \PHPUnit_Framework_TestCase
+{
+ public function testLogout()
+ {
+ $request = new Request();
+ $response = new Response();
+ $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface');
+
+ $handler = new CookieClearingLogoutHandler(array('foo' => array('path' => '/foo', 'domain' => 'foo.foo'), 'foo2' => array('path' => null, 'domain' => null)));
+
+ $cookies = $response->headers->getCookies();
+ $this->assertCount(0, $cookies);
+
+ $handler->logout($request, $response, $token);
+
+ $cookies = $response->headers->getCookies(ResponseHeaderBag::COOKIES_ARRAY);
+ $this->assertCount(2, $cookies);
+
+ $cookie = $cookies['foo.foo']['/foo']['foo'];
+ $this->assertEquals('foo', $cookie->getName());
+ $this->assertEquals('/foo', $cookie->getPath());
+ $this->assertEquals('foo.foo', $cookie->getDomain());
+ $this->assertTrue($cookie->isCleared());
+
+ $cookie = $cookies['']['/']['foo2'];
+ $this->assertStringStartsWith('foo2', $cookie->getName());
+ $this->assertEquals('/', $cookie->getPath());
+ $this->assertNull($cookie->getDomain());
+ $this->assertTrue($cookie->isCleared());
+ }
+}
diff --git a/Http/Tests/Logout/DefaultLogoutSuccessHandlerTest.php b/Http/Tests/Logout/DefaultLogoutSuccessHandlerTest.php
new file mode 100644
index 0000000..381a48e
--- /dev/null
+++ b/Http/Tests/Logout/DefaultLogoutSuccessHandlerTest.php
@@ -0,0 +1,34 @@
+<?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\Tests\Logout;
+
+use Symfony\Component\Security\Http\Logout\DefaultLogoutSuccessHandler;
+
+class DefaultLogoutSuccessHandlerTest extends \PHPUnit_Framework_TestCase
+{
+ public function testLogout()
+ {
+ $request = $this->getMock('Symfony\Component\HttpFoundation\Request');
+ $response = $this->getMock('Symfony\Component\HttpFoundation\Response');
+
+ $httpUtils = $this->getMock('Symfony\Component\Security\Http\HttpUtils');
+ $httpUtils->expects($this->once())
+ ->method('createRedirectResponse')
+ ->with($request, '/dashboard')
+ ->will($this->returnValue($response));
+
+ $handler = new DefaultLogoutSuccessHandler($httpUtils, '/dashboard');
+ $result = $handler->onLogoutSuccess($request);
+
+ $this->assertSame($response, $result);
+ }
+}
diff --git a/Http/Tests/Logout/SessionLogoutHandlerTest.php b/Http/Tests/Logout/SessionLogoutHandlerTest.php
new file mode 100644
index 0000000..c342995
--- /dev/null
+++ b/Http/Tests/Logout/SessionLogoutHandlerTest.php
@@ -0,0 +1,40 @@
+<?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\Tests\Logout;
+
+use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\Security\Http\Logout\SessionLogoutHandler;
+
+class SessionLogoutHandlerTest extends \PHPUnit_Framework_TestCase
+{
+ public function testLogout()
+ {
+ $handler = new SessionLogoutHandler();
+
+ $request = $this->getMock('Symfony\Component\HttpFoundation\Request');
+ $response = new Response();
+ $session = $this->getMock('Symfony\Component\HttpFoundation\Session\Session', array(), array(), '', false);
+
+ $request
+ ->expects($this->once())
+ ->method('getSession')
+ ->will($this->returnValue($session))
+ ;
+
+ $session
+ ->expects($this->once())
+ ->method('invalidate')
+ ;
+
+ $handler->logout($request, $response, $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface'));
+ }
+}
diff --git a/Http/Tests/RememberMe/AbstractRememberMeServicesTest.php b/Http/Tests/RememberMe/AbstractRememberMeServicesTest.php
new file mode 100644
index 0000000..ddfaaeb
--- /dev/null
+++ b/Http/Tests/RememberMe/AbstractRememberMeServicesTest.php
@@ -0,0 +1,311 @@
+<?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\Tests\RememberMe;
+
+use Symfony\Component\Security\Http\RememberMe\RememberMeServicesInterface;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\Security\Http\RememberMe\AbstractRememberMeServices;
+
+class AbstractRememberMeServicesTest extends \PHPUnit_Framework_TestCase
+{
+ public function testGetRememberMeParameter()
+ {
+ $service = $this->getService(null, array('remember_me_parameter' => 'foo'));
+
+ $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', 'path' => null, 'domain' => null));
+
+ $this->assertNull($service->autoLogin(new Request()));
+ }
+
+ /**
+ * @expectedException \RuntimeException
+ */
+ public function testAutoLoginThrowsExceptionWhenImplementationDoesNotReturnUserInterface()
+ {
+ $service = $this->getService(null, array('name' => 'foo', 'path' => null, 'domain' => null));
+ $request = new Request();
+ $request->cookies->set('foo', 'foo');
+
+ $service
+ ->expects($this->once())
+ ->method('processAutoLoginCookie')
+ ->will($this->returnValue(null))
+ ;
+
+ $service->autoLogin($request);
+ }
+
+ public function testAutoLogin()
+ {
+ $service = $this->getService(null, array('name' => 'foo', 'path' => null, 'domain' => null));
+ $request = new Request();
+ $request->cookies->set('foo', 'foo');
+
+ $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($user))
+ ;
+
+ $returnedToken = $service->autoLogin($request);
+
+ $this->assertSame($user, $returnedToken->getUser());
+ $this->assertSame('fookey', $returnedToken->getKey());
+ $this->assertSame('fookey', $returnedToken->getProviderKey());
+ }
+
+ /**
+ * @dataProvider provideOptionsForLogout
+ */
+ public function testLogout(array $options)
+ {
+ $service = $this->getService(null, $options);
+ $request = new Request();
+ $response = new Response();
+ $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface');
+ $service->logout($request, $response, $token);
+ $cookie = $request->attributes->get(RememberMeServicesInterface::COOKIE_ATTR_NAME);
+ $this->assertInstanceOf('Symfony\Component\HttpFoundation\Cookie', $cookie);
+ $this->assertTrue($cookie->isCleared());
+ $this->assertSame($options['name'], $cookie->getName());
+ $this->assertSame($options['path'], $cookie->getPath());
+ $this->assertSame($options['domain'], $cookie->getDomain());
+ $this->assertSame($options['secure'], $cookie->isSecure());
+ $this->assertSame($options['httponly'], $cookie->isHttpOnly());
+ }
+
+ public function provideOptionsForLogout()
+ {
+ return array(
+ array(array('name' => 'foo', 'path' => '/', 'domain' => null, 'secure' => false, 'httponly' => true)),
+ array(array('name' => 'foo', 'path' => '/bar', 'domain' => 'baz.com', 'secure' => true, 'httponly' => false)),
+ );
+ }
+
+ public function testLoginFail()
+ {
+ $service = $this->getService(null, array('name' => 'foo', 'path' => null, 'domain' => null));
+ $request = new Request();
+
+ $service->loginFail($request);
+
+ $this->assertTrue($request->attributes->get(RememberMeServicesInterface::COOKIE_ATTR_NAME)->isCleared());
+ }
+
+ public function testLoginSuccessIsNotProcessedWhenTokenDoesNotContainUserInterfaceImplementation()
+ {
+ $service = $this->getService(null, array('name' => 'foo', 'always_remember_me' => true, 'path' => null, 'domain' => null));
+ $request = new Request();
+ $response = new Response();
+ $account = $this->getMock('Symfony\Component\Security\Core\User\UserInterface');
+ $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface');
+ $token
+ ->expects($this->once())
+ ->method('getUser')
+ ->will($this->returnValue('foo'))
+ ;
+
+ $service
+ ->expects($this->never())
+ ->method('onLoginSuccess')
+ ;
+
+ $this->assertFalse($request->request->has('foo'));
+
+ $service->loginSuccess($request, $response, $token);
+ }
+
+ public function testLoginSuccessIsNotProcessedWhenRememberMeIsNotRequested()
+ {
+ $service = $this->getService(null, array('name' => 'foo', 'always_remember_me' => false, 'remember_me_parameter' => 'foo', 'path' => null, 'domain' => null));
+ $request = new Request();
+ $response = new Response();
+ $account = $this->getMock('Symfony\Component\Security\Core\User\UserInterface');
+ $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface');
+ $token
+ ->expects($this->once())
+ ->method('getUser')
+ ->will($this->returnValue($account))
+ ;
+
+ $service
+ ->expects($this->never())
+ ->method('onLoginSuccess')
+ ->will($this->returnValue(null))
+ ;
+
+ $this->assertFalse($request->request->has('foo'));
+
+ $service->loginSuccess($request, $response, $token);
+ }
+
+ public function testLoginSuccessWhenRememberMeAlwaysIsTrue()
+ {
+ $service = $this->getService(null, array('name' => 'foo', 'always_remember_me' => true, 'path' => null, 'domain' => null));
+ $request = new Request();
+ $response = new Response();
+ $account = $this->getMock('Symfony\Component\Security\Core\User\UserInterface');
+ $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface');
+ $token
+ ->expects($this->once())
+ ->method('getUser')
+ ->will($this->returnValue($account))
+ ;
+
+ $service
+ ->expects($this->once())
+ ->method('onLoginSuccess')
+ ->will($this->returnValue(null))
+ ;
+
+ $service->loginSuccess($request, $response, $token);
+ }
+
+ /**
+ * @dataProvider getPositiveRememberMeParameterValues
+ */
+ public function testLoginSuccessWhenRememberMeParameterWithPathIsPositive($value)
+ {
+ $service = $this->getService(null, array('name' => 'foo', 'always_remember_me' => false, 'remember_me_parameter' => 'foo[bar]', 'path' => null, 'domain' => null));
+
+ $request = new Request();
+ $request->request->set('foo', array('bar' => $value));
+ $response = new Response();
+ $account = $this->getMock('Symfony\Component\Security\Core\User\UserInterface');
+ $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface');
+ $token
+ ->expects($this->once())
+ ->method('getUser')
+ ->will($this->returnValue($account))
+ ;
+
+ $service
+ ->expects($this->once())
+ ->method('onLoginSuccess')
+ ->will($this->returnValue(true))
+ ;
+
+ $service->loginSuccess($request, $response, $token);
+ }
+
+ /**
+ * @dataProvider getPositiveRememberMeParameterValues
+ */
+ public function testLoginSuccessWhenRememberMeParameterIsPositive($value)
+ {
+ $service = $this->getService(null, array('name' => 'foo', 'always_remember_me' => false, 'remember_me_parameter' => 'foo', 'path' => null, 'domain' => null));
+
+ $request = new Request();
+ $request->request->set('foo', $value);
+ $response = new Response();
+ $account = $this->getMock('Symfony\Component\Security\Core\User\UserInterface');
+ $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface');
+ $token
+ ->expects($this->once())
+ ->method('getUser')
+ ->will($this->returnValue($account))
+ ;
+
+ $service
+ ->expects($this->once())
+ ->method('onLoginSuccess')
+ ->will($this->returnValue(true))
+ ;
+
+ $service->loginSuccess($request, $response, $token);
+ }
+
+ public function getPositiveRememberMeParameterValues()
+ {
+ return array(
+ array('true'),
+ array('1'),
+ array('on'),
+ array('yes'),
+ );
+ }
+
+ public function testEncodeCookieAndDecodeCookieAreInvertible()
+ {
+ $cookieParts = array('aa', 'bb', 'cc');
+ $service = $this->getService();
+
+ $encoded = $this->callProtected($service, 'encodeCookie', array($cookieParts));
+ $this->assertInternalType('string', $encoded);
+
+ $decoded = $this->callProtected($service, 'decodeCookie', array($encoded));
+ $this->assertSame($cookieParts, $decoded);
+ }
+
+ /**
+ * @expectedException \InvalidArgumentException
+ * @expectedExceptionMessage cookie delimiter
+ */
+ public function testThereShouldBeNoCookieDelimiterInCookieParts()
+ {
+ $cookieParts = array('aa', 'b'.AbstractRememberMeServices::COOKIE_DELIMITER.'b', 'cc');
+ $service = $this->getService();
+
+ $this->callProtected($service, 'encodeCookie', array($cookieParts));
+ }
+
+ protected function getService($userProvider = null, $options = array(), $logger = null)
+ {
+ if (null === $userProvider) {
+ $userProvider = $this->getProvider();
+ }
+
+ return $this->getMockForAbstractClass('Symfony\Component\Security\Http\RememberMe\AbstractRememberMeServices', array(
+ array($userProvider), 'fookey', 'fookey', $options, $logger,
+ ));
+ }
+
+ protected function getProvider()
+ {
+ $provider = $this->getMock('Symfony\Component\Security\Core\User\UserProviderInterface');
+ $provider
+ ->expects($this->any())
+ ->method('supportsClass')
+ ->will($this->returnValue(true))
+ ;
+
+ return $provider;
+ }
+
+ private function callProtected($object, $method, array $args)
+ {
+ $reflection = new \ReflectionClass(get_class($object));
+ $reflectionMethod = $reflection->getMethod($method);
+ $reflectionMethod->setAccessible(true);
+
+ return $reflectionMethod->invokeArgs($object, $args);
+ }
+}
diff --git a/Http/Tests/RememberMe/PersistentTokenBasedRememberMeServicesTest.php b/Http/Tests/RememberMe/PersistentTokenBasedRememberMeServicesTest.php
new file mode 100644
index 0000000..cce8c4b
--- /dev/null
+++ b/Http/Tests/RememberMe/PersistentTokenBasedRememberMeServicesTest.php
@@ -0,0 +1,330 @@
+<?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\Tests\RememberMe;
+
+use Symfony\Component\Security\Http\RememberMe\RememberMeServicesInterface;
+use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
+use Symfony\Component\Security\Core\Authentication\RememberMe\PersistentToken;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\HttpFoundation\ResponseHeaderBag;
+use Symfony\Component\Security\Http\RememberMe\PersistentTokenBasedRememberMeServices;
+use Symfony\Component\Security\Core\Exception\TokenNotFoundException;
+use Symfony\Component\Security\Core\Exception\CookieTheftException;
+use Symfony\Component\Security\Core\Util\SecureRandom;
+
+class PersistentTokenBasedRememberMeServicesTest extends \PHPUnit_Framework_TestCase
+{
+ public function testAutoLoginReturnsNullWhenNoCookie()
+ {
+ $service = $this->getService(null, array('name' => 'foo'));
+
+ $this->assertNull($service->autoLogin(new Request()));
+ }
+
+ public function testAutoLoginThrowsExceptionOnInvalidCookie()
+ {
+ $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');
+
+ $this->assertNull($service->autoLogin($request));
+ $this->assertTrue($request->attributes->get(RememberMeServicesInterface::COOKIE_ATTR_NAME)->isCleared());
+ }
+
+ public function testAutoLoginThrowsExceptionOnNonExistentToken()
+ {
+ $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(
+ $series = 'fooseries',
+ $tokenValue = 'foovalue',
+ )));
+
+ $tokenProvider = $this->getMock('Symfony\Component\Security\Core\Authentication\RememberMe\TokenProviderInterface');
+ $tokenProvider
+ ->expects($this->once())
+ ->method('loadTokenBySeries')
+ ->will($this->throwException(new TokenNotFoundException('Token not found.')))
+ ;
+ $service->setTokenProvider($tokenProvider);
+
+ $this->assertNull($service->autoLogin($request));
+ $this->assertTrue($request->attributes->get(RememberMeServicesInterface::COOKIE_ATTR_NAME)->isCleared());
+ }
+
+ public function testAutoLoginReturnsNullOnNonExistentUser()
+ {
+ $userProvider = $this->getProvider();
+ $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')));
+
+ $tokenProvider = $this->getMock('Symfony\Component\Security\Core\Authentication\RememberMe\TokenProviderInterface');
+ $tokenProvider
+ ->expects($this->once())
+ ->method('loadTokenBySeries')
+ ->will($this->returnValue(new PersistentToken('fooclass', 'fooname', 'fooseries', 'foovalue', new \DateTime())))
+ ;
+ $service->setTokenProvider($tokenProvider);
+
+ $userProvider
+ ->expects($this->once())
+ ->method('loadUserByUsername')
+ ->will($this->throwException(new UsernameNotFoundException('user not found')))
+ ;
+
+ $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', 'path' => null, 'domain' => null, 'always_remember_me' => true));
+ $request = new Request();
+ $request->cookies->set('foo', $this->encodeCookie(array('fooseries', 'foovalue')));
+
+ $tokenProvider = $this->getMock('Symfony\Component\Security\Core\Authentication\RememberMe\TokenProviderInterface');
+ $service->setTokenProvider($tokenProvider);
+
+ $tokenProvider
+ ->expects($this->once())
+ ->method('loadTokenBySeries')
+ ->will($this->returnValue(new PersistentToken('fooclass', 'foouser', 'fooseries', 'anotherFooValue', new \DateTime())))
+ ;
+
+ $tokenProvider
+ ->expects($this->once())
+ ->method('deleteTokenBySeries')
+ ->with($this->equalTo('fooseries'))
+ ->will($this->returnValue(null))
+ ;
+
+ try {
+ $service->autoLogin($request);
+ $this->fail('Expected CookieTheftException was not thrown.');
+ } catch (CookieTheftException $e) {
+ }
+
+ $this->assertTrue($request->attributes->has(RememberMeServicesInterface::COOKIE_ATTR_NAME));
+ }
+
+ public function testAutoLoginDoesNotAcceptAnExpiredCookie()
+ {
+ $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')));
+
+ $tokenProvider = $this->getMock('Symfony\Component\Security\Core\Authentication\RememberMe\TokenProviderInterface');
+ $tokenProvider
+ ->expects($this->once())
+ ->method('loadTokenBySeries')
+ ->with($this->equalTo('fooseries'))
+ ->will($this->returnValue(new PersistentToken('fooclass', 'username', 'fooseries', 'foovalue', new \DateTime('yesterday'))))
+ ;
+ $service->setTokenProvider($tokenProvider);
+
+ $this->assertNull($service->autoLogin($request));
+ $this->assertTrue($request->attributes->has(RememberMeServicesInterface::COOKIE_ATTR_NAME));
+ }
+
+ public function testAutoLogin()
+ {
+ $user = $this->getMock('Symfony\Component\Security\Core\User\UserInterface');
+ $user
+ ->expects($this->once())
+ ->method('getRoles')
+ ->will($this->returnValue(array('ROLE_FOO')))
+ ;
+
+ $userProvider = $this->getProvider();
+ $userProvider
+ ->expects($this->once())
+ ->method('loadUserByUsername')
+ ->with($this->equalTo('foouser'))
+ ->will($this->returnValue($user))
+ ;
+
+ $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')));
+
+ $tokenProvider = $this->getMock('Symfony\Component\Security\Core\Authentication\RememberMe\TokenProviderInterface');
+ $tokenProvider
+ ->expects($this->once())
+ ->method('loadTokenBySeries')
+ ->with($this->equalTo('fooseries'))
+ ->will($this->returnValue(new PersistentToken('fooclass', 'foouser', 'fooseries', 'foovalue', new \DateTime())))
+ ;
+ $service->setTokenProvider($tokenProvider);
+
+ $returnedToken = $service->autoLogin($request);
+
+ $this->assertInstanceOf('Symfony\Component\Security\Core\Authentication\Token\RememberMeToken', $returnedToken);
+ $this->assertSame($user, $returnedToken->getUser());
+ $this->assertEquals('fookey', $returnedToken->getKey());
+ $this->assertTrue($request->attributes->has(RememberMeServicesInterface::COOKIE_ATTR_NAME));
+ }
+
+ public function testLogout()
+ {
+ $service = $this->getService(null, array('name' => 'foo', 'path' => '/foo', 'domain' => 'foodomain.foo', 'secure' => true, 'httponly' => false));
+ $request = new Request();
+ $request->cookies->set('foo', $this->encodeCookie(array('fooseries', 'foovalue')));
+ $response = new Response();
+ $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface');
+
+ $tokenProvider = $this->getMock('Symfony\Component\Security\Core\Authentication\RememberMe\TokenProviderInterface');
+ $tokenProvider
+ ->expects($this->once())
+ ->method('deleteTokenBySeries')
+ ->with($this->equalTo('fooseries'))
+ ->will($this->returnValue(null))
+ ;
+ $service->setTokenProvider($tokenProvider);
+
+ $service->logout($request, $response, $token);
+
+ $cookie = $request->attributes->get(RememberMeServicesInterface::COOKIE_ATTR_NAME);
+ $this->assertTrue($cookie->isCleared());
+ $this->assertEquals('/foo', $cookie->getPath());
+ $this->assertEquals('foodomain.foo', $cookie->getDomain());
+ $this->assertTrue($cookie->isSecure());
+ $this->assertFalse($cookie->isHttpOnly());
+ }
+
+ public function testLogoutSimplyIgnoresNonSetRequestCookie()
+ {
+ $service = $this->getService(null, array('name' => 'foo', 'path' => null, 'domain' => null));
+ $request = new Request();
+ $response = new Response();
+ $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface');
+
+ $tokenProvider = $this->getMock('Symfony\Component\Security\Core\Authentication\RememberMe\TokenProviderInterface');
+ $tokenProvider
+ ->expects($this->never())
+ ->method('deleteTokenBySeries')
+ ;
+ $service->setTokenProvider($tokenProvider);
+
+ $service->logout($request, $response, $token);
+
+ $cookie = $request->attributes->get(RememberMeServicesInterface::COOKIE_ATTR_NAME);
+ $this->assertTrue($cookie->isCleared());
+ $this->assertEquals('/', $cookie->getPath());
+ $this->assertNull($cookie->getDomain());
+ }
+
+ public function testLogoutSimplyIgnoresInvalidCookie()
+ {
+ $service = $this->getService(null, array('name' => 'foo', 'path' => null, 'domain' => null));
+ $request = new Request();
+ $request->cookies->set('foo', 'somefoovalue');
+ $response = new Response();
+ $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface');
+
+ $tokenProvider = $this->getMock('Symfony\Component\Security\Core\Authentication\RememberMe\TokenProviderInterface');
+ $tokenProvider
+ ->expects($this->never())
+ ->method('deleteTokenBySeries')
+ ;
+ $service->setTokenProvider($tokenProvider);
+
+ $service->logout($request, $response, $token);
+
+ $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();
+
+ $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()
+ {
+ $service = $this->getService(null, array('name' => 'foo', 'domain' => 'myfoodomain.foo', 'path' => '/foo/path', 'secure' => true, 'httponly' => true, 'lifetime' => 3600, 'always_remember_me' => true));
+ $request = new Request();
+ $response = new Response();
+
+ $account = $this->getMock('Symfony\Component\Security\Core\User\UserInterface');
+ $account
+ ->expects($this->once())
+ ->method('getUsername')
+ ->will($this->returnValue('foo'))
+ ;
+ $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface');
+ $token
+ ->expects($this->any())
+ ->method('getUser')
+ ->will($this->returnValue($account))
+ ;
+
+ $tokenProvider = $this->getMock('Symfony\Component\Security\Core\Authentication\RememberMe\TokenProviderInterface');
+ $tokenProvider
+ ->expects($this->once())
+ ->method('createNewToken')
+ ;
+ $service->setTokenProvider($tokenProvider);
+
+ $cookies = $response->headers->getCookies();
+ $this->assertCount(0, $cookies);
+
+ $service->loginSuccess($request, $response, $token);
+
+ $cookies = $response->headers->getCookies(ResponseHeaderBag::COOKIES_ARRAY);
+ $cookie = $cookies['myfoodomain.foo']['/foo/path']['foo'];
+ $this->assertFalse($cookie->isCleared());
+ $this->assertTrue($cookie->isSecure());
+ $this->assertTrue($cookie->isHttpOnly());
+ $this->assertTrue($cookie->getExpiresTime() > time() + 3590 && $cookie->getExpiresTime() < time() + 3610);
+ $this->assertEquals('myfoodomain.foo', $cookie->getDomain());
+ $this->assertEquals('/foo/path', $cookie->getPath());
+ }
+
+ protected function encodeCookie(array $parts)
+ {
+ $service = $this->getService();
+ $r = new \ReflectionMethod($service, 'encodeCookie');
+ $r->setAccessible(true);
+
+ return $r->invoke($service, $parts);
+ }
+
+ protected function getService($userProvider = null, $options = array(), $logger = null)
+ {
+ if (null === $userProvider) {
+ $userProvider = $this->getProvider();
+ }
+
+ return new PersistentTokenBasedRememberMeServices(array($userProvider), 'fookey', 'fookey', $options, $logger, new SecureRandom(sys_get_temp_dir().'/_sf2.seed'));
+ }
+
+ protected function getProvider()
+ {
+ $provider = $this->getMock('Symfony\Component\Security\Core\User\UserProviderInterface');
+ $provider
+ ->expects($this->any())
+ ->method('supportsClass')
+ ->will($this->returnValue(true))
+ ;
+
+ return $provider;
+ }
+}
diff --git a/Http/Tests/RememberMe/ResponseListenerTest.php b/Http/Tests/RememberMe/ResponseListenerTest.php
new file mode 100644
index 0000000..78de8e4
--- /dev/null
+++ b/Http/Tests/RememberMe/ResponseListenerTest.php
@@ -0,0 +1,102 @@
+<?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\Tests\RememberMe;
+
+use Symfony\Component\HttpKernel\HttpKernelInterface;
+use Symfony\Component\Security\Http\RememberMe\ResponseListener;
+use Symfony\Component\Security\Http\RememberMe\RememberMeServicesInterface;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Cookie;
+use Symfony\Component\HttpKernel\KernelEvents;
+
+class ResponseListenerTest extends \PHPUnit_Framework_TestCase
+{
+ public function testRememberMeCookieIsSentWithResponse()
+ {
+ $cookie = new Cookie('rememberme');
+
+ $request = $this->getRequest(array(
+ RememberMeServicesInterface::COOKIE_ATTR_NAME => $cookie,
+ ));
+
+ $response = $this->getResponse();
+ $response->headers->expects($this->once())->method('setCookie')->with($cookie);
+
+ $listener = new ResponseListener();
+ $listener->onKernelResponse($this->getEvent($request, $response));
+ }
+
+ public function testRememberMeCookieIsNotSendWithResponseForSubRequests()
+ {
+ $cookie = new Cookie('rememberme');
+
+ $request = $this->getRequest(array(
+ RememberMeServicesInterface::COOKIE_ATTR_NAME => $cookie,
+ ));
+
+ $response = $this->getResponse();
+ $response->headers->expects($this->never())->method('setCookie');
+
+ $listener = new ResponseListener();
+ $listener->onKernelResponse($this->getEvent($request, $response, HttpKernelInterface::SUB_REQUEST));
+ }
+
+ public function testRememberMeCookieIsNotSendWithResponse()
+ {
+ $request = $this->getRequest();
+
+ $response = $this->getResponse();
+ $response->headers->expects($this->never())->method('setCookie');
+
+ $listener = new ResponseListener();
+ $listener->onKernelResponse($this->getEvent($request, $response));
+ }
+
+ public function testItSubscribesToTheOnKernelResponseEvent()
+ {
+ $listener = new ResponseListener();
+
+ $this->assertSame(array(KernelEvents::RESPONSE => 'onKernelResponse'), ResponseListener::getSubscribedEvents());
+ }
+
+ private function getRequest(array $attributes = array())
+ {
+ $request = new Request();
+
+ foreach ($attributes as $name => $value) {
+ $request->attributes->set($name, $value);
+ }
+
+ return $request;
+ }
+
+ private function getResponse()
+ {
+ $response = $this->getMock('Symfony\Component\HttpFoundation\Response');
+ $response->headers = $this->getMock('Symfony\Component\HttpFoundation\ResponseHeaderBag');
+
+ return $response;
+ }
+
+ private function getEvent($request, $response, $type = HttpKernelInterface::MASTER_REQUEST)
+ {
+ $event = $this->getMockBuilder('Symfony\Component\HttpKernel\Event\FilterResponseEvent')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $event->expects($this->any())->method('getRequest')->will($this->returnValue($request));
+ $event->expects($this->any())->method('isMasterRequest')->will($this->returnValue($type === HttpKernelInterface::MASTER_REQUEST));
+ $event->expects($this->any())->method('getResponse')->will($this->returnValue($response));
+
+ return $event;
+ }
+}
diff --git a/Http/Tests/RememberMe/TokenBasedRememberMeServicesTest.php b/Http/Tests/RememberMe/TokenBasedRememberMeServicesTest.php
new file mode 100644
index 0000000..c92ef13
--- /dev/null
+++ b/Http/Tests/RememberMe/TokenBasedRememberMeServicesTest.php
@@ -0,0 +1,285 @@
+<?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\Tests\RememberMe;
+
+use Symfony\Component\Security\Http\RememberMe\RememberMeServicesInterface;
+use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\HttpFoundation\ResponseHeaderBag;
+use Symfony\Component\Security\Http\RememberMe\TokenBasedRememberMeServices;
+
+class TokenBasedRememberMeServicesTest extends \PHPUnit_Framework_TestCase
+{
+ public function testAutoLoginReturnsNullWhenNoCookie()
+ {
+ $service = $this->getService(null, array('name' => 'foo'));
+
+ $this->assertNull($service->autoLogin(new Request()));
+ }
+
+ public function testAutoLoginThrowsExceptionOnInvalidCookie()
+ {
+ $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');
+
+ $this->assertNull($service->autoLogin($request));
+ $this->assertTrue($request->attributes->get(RememberMeServicesInterface::COOKIE_ATTR_NAME)->isCleared());
+ }
+
+ public function testAutoLoginThrowsExceptionOnNonExistentUser()
+ {
+ $userProvider = $this->getProvider();
+ $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'));
+
+ $userProvider
+ ->expects($this->once())
+ ->method('loadUserByUsername')
+ ->will($this->throwException(new UsernameNotFoundException('user not found')))
+ ;
+
+ $this->assertNull($service->autoLogin($request));
+ $this->assertTrue($request->attributes->get(RememberMeServicesInterface::COOKIE_ATTR_NAME)->isCleared());
+ }
+
+ public function testAutoLoginDoesNotAcceptCookieWithInvalidHash()
+ {
+ $userProvider = $this->getProvider();
+ $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'));
+
+ $user = $this->getMock('Symfony\Component\Security\Core\User\UserInterface');
+ $user
+ ->expects($this->once())
+ ->method('getPassword')
+ ->will($this->returnValue('foopass'))
+ ;
+
+ $userProvider
+ ->expects($this->once())
+ ->method('loadUserByUsername')
+ ->with($this->equalTo('foouser'))
+ ->will($this->returnValue($user))
+ ;
+
+ $this->assertNull($service->autoLogin($request));
+ $this->assertTrue($request->attributes->get(RememberMeServicesInterface::COOKIE_ATTR_NAME)->isCleared());
+ }
+
+ public function testAutoLoginDoesNotAcceptAnExpiredCookie()
+ {
+ $userProvider = $this->getProvider();
+ $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'));
+
+ $user = $this->getMock('Symfony\Component\Security\Core\User\UserInterface');
+ $user
+ ->expects($this->once())
+ ->method('getPassword')
+ ->will($this->returnValue('foopass'))
+ ;
+
+ $userProvider
+ ->expects($this->once())
+ ->method('loadUserByUsername')
+ ->with($this->equalTo('foouser'))
+ ->will($this->returnValue($user))
+ ;
+
+ $this->assertNull($service->autoLogin($request));
+ $this->assertTrue($request->attributes->get(RememberMeServicesInterface::COOKIE_ATTR_NAME)->isCleared());
+ }
+
+ /**
+ * @dataProvider provideUsernamesForAutoLogin
+ *
+ * @param string $username
+ */
+ public function testAutoLogin($username)
+ {
+ $user = $this->getMock('Symfony\Component\Security\Core\User\UserInterface');
+ $user
+ ->expects($this->once())
+ ->method('getRoles')
+ ->will($this->returnValue(array('ROLE_FOO')))
+ ;
+ $user
+ ->expects($this->once())
+ ->method('getPassword')
+ ->will($this->returnValue('foopass'))
+ ;
+
+ $userProvider = $this->getProvider();
+ $userProvider
+ ->expects($this->once())
+ ->method('loadUserByUsername')
+ ->with($this->equalTo($username))
+ ->will($this->returnValue($user))
+ ;
+
+ $service = $this->getService($userProvider, array('name' => 'foo', 'always_remember_me' => true, 'lifetime' => 3600));
+ $request = new Request();
+ $request->cookies->set('foo', $this->getCookie('fooclass', $username, time() + 3600, 'foopass'));
+
+ $returnedToken = $service->autoLogin($request);
+
+ $this->assertInstanceOf('Symfony\Component\Security\Core\Authentication\Token\RememberMeToken', $returnedToken);
+ $this->assertSame($user, $returnedToken->getUser());
+ $this->assertEquals('fookey', $returnedToken->getKey());
+ }
+
+ public function provideUsernamesForAutoLogin()
+ {
+ return array(
+ array('foouser', 'Simple username'),
+ array('foo'.TokenBasedRememberMeServices::COOKIE_DELIMITER.'user', 'Username might contain the delimiter'),
+ );
+ }
+
+ public function testLogout()
+ {
+ $service = $this->getService(null, array('name' => 'foo', 'path' => null, 'domain' => null, 'secure' => true, 'httponly' => false));
+ $request = new Request();
+ $response = new Response();
+ $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface');
+
+ $service->logout($request, $response, $token);
+
+ $cookie = $request->attributes->get(RememberMeServicesInterface::COOKIE_ATTR_NAME);
+ $this->assertTrue($cookie->isCleared());
+ $this->assertEquals('/', $cookie->getPath());
+ $this->assertNull($cookie->getDomain());
+ $this->assertTrue($cookie->isSecure());
+ $this->assertFalse($cookie->isHttpOnly());
+ }
+
+ public function testLoginFail()
+ {
+ $service = $this->getService(null, array('name' => 'foo', 'path' => '/foo', 'domain' => 'foodomain.foo'));
+ $request = new Request();
+ $response = new Response();
+
+ $service->loginFail($request, $response);
+
+ $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 testLoginSuccessIgnoresTokensWhichDoNotContainAnUserInterfaceImplementation()
+ {
+ $service = $this->getService(null, array('name' => 'foo', 'always_remember_me' => true, 'path' => null, 'domain' => null));
+ $request = new Request();
+ $response = new Response();
+ $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface');
+ $token
+ ->expects($this->once())
+ ->method('getUser')
+ ->will($this->returnValue('foo'))
+ ;
+
+ $cookies = $response->headers->getCookies();
+ $this->assertCount(0, $cookies);
+
+ $service->loginSuccess($request, $response, $token);
+
+ $cookies = $response->headers->getCookies();
+ $this->assertCount(0, $cookies);
+ }
+
+ public function testLoginSuccess()
+ {
+ $service = $this->getService(null, array('name' => 'foo', 'domain' => 'myfoodomain.foo', 'path' => '/foo/path', 'secure' => true, 'httponly' => true, 'lifetime' => 3600, 'always_remember_me' => true));
+ $request = new Request();
+ $response = new Response();
+
+ $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface');
+ $user = $this->getMock('Symfony\Component\Security\Core\User\UserInterface');
+ $user
+ ->expects($this->once())
+ ->method('getPassword')
+ ->will($this->returnValue('foopass'))
+ ;
+ $user
+ ->expects($this->once())
+ ->method('getUsername')
+ ->will($this->returnValue('foouser'))
+ ;
+ $token
+ ->expects($this->atLeastOnce())
+ ->method('getUser')
+ ->will($this->returnValue($user))
+ ;
+
+ $cookies = $response->headers->getCookies();
+ $this->assertCount(0, $cookies);
+
+ $service->loginSuccess($request, $response, $token);
+
+ $cookies = $response->headers->getCookies(ResponseHeaderBag::COOKIES_ARRAY);
+ $cookie = $cookies['myfoodomain.foo']['/foo/path']['foo'];
+ $this->assertFalse($cookie->isCleared());
+ $this->assertTrue($cookie->isSecure());
+ $this->assertTrue($cookie->isHttpOnly());
+ $this->assertTrue($cookie->getExpiresTime() > time() + 3590 && $cookie->getExpiresTime() < time() + 3610);
+ $this->assertEquals('myfoodomain.foo', $cookie->getDomain());
+ $this->assertEquals('/foo/path', $cookie->getPath());
+ }
+
+ protected function getCookie($class, $username, $expires, $password)
+ {
+ $service = $this->getService();
+ $r = new \ReflectionMethod($service, 'generateCookieValue');
+ $r->setAccessible(true);
+
+ return $r->invoke($service, $class, $username, $expires, $password);
+ }
+
+ protected function encodeCookie(array $parts)
+ {
+ $service = $this->getService();
+ $r = new \ReflectionMethod($service, 'encodeCookie');
+ $r->setAccessible(true);
+
+ return $r->invoke($service, $parts);
+ }
+
+ protected function getService($userProvider = null, $options = array(), $logger = null)
+ {
+ if (null === $userProvider) {
+ $userProvider = $this->getProvider();
+ }
+
+ $service = new TokenBasedRememberMeServices(array($userProvider), 'fookey', 'fookey', $options, $logger);
+
+ return $service;
+ }
+
+ protected function getProvider()
+ {
+ $provider = $this->getMock('Symfony\Component\Security\Core\User\UserProviderInterface');
+ $provider
+ ->expects($this->any())
+ ->method('supportsClass')
+ ->will($this->returnValue(true))
+ ;
+
+ return $provider;
+ }
+}
diff --git a/Http/Tests/Session/SessionAuthenticationStrategyTest.php b/Http/Tests/Session/SessionAuthenticationStrategyTest.php
new file mode 100644
index 0000000..4aef4b2
--- /dev/null
+++ b/Http/Tests/Session/SessionAuthenticationStrategyTest.php
@@ -0,0 +1,90 @@
+<?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\Tests\Session;
+
+use Symfony\Component\Security\Http\Session\SessionAuthenticationStrategy;
+
+class SessionAuthenticationStrategyTest extends \PHPUnit_Framework_TestCase
+{
+ public function testSessionIsNotChanged()
+ {
+ $request = $this->getRequest();
+ $request->expects($this->never())->method('getSession');
+
+ $strategy = new SessionAuthenticationStrategy(SessionAuthenticationStrategy::NONE);
+ $strategy->onAuthentication($request, $this->getToken());
+ }
+
+ /**
+ * @expectedException \RuntimeException
+ * @expectedExceptionMessage Invalid session authentication strategy "foo"
+ */
+ public function testUnsupportedStrategy()
+ {
+ $request = $this->getRequest();
+ $request->expects($this->never())->method('getSession');
+
+ $strategy = new SessionAuthenticationStrategy('foo');
+ $strategy->onAuthentication($request, $this->getToken());
+ }
+
+ public function testSessionIsMigrated()
+ {
+ if (PHP_VERSION_ID >= 50400 && PHP_VERSION_ID < 50411) {
+ $this->markTestSkipped('We cannot destroy the old session on PHP 5.4.0 - 5.4.10.');
+ }
+
+ $session = $this->getMock('Symfony\Component\HttpFoundation\Session\SessionInterface');
+ $session->expects($this->once())->method('migrate')->with($this->equalTo(true));
+
+ $strategy = new SessionAuthenticationStrategy(SessionAuthenticationStrategy::MIGRATE);
+ $strategy->onAuthentication($this->getRequest($session), $this->getToken());
+ }
+
+ public function testSessionIsMigratedWithPhp54Workaround()
+ {
+ if (PHP_VERSION_ID < 50400 || PHP_VERSION_ID >= 50411) {
+ $this->markTestSkipped('This PHP version is not affected.');
+ }
+
+ $session = $this->getMock('Symfony\Component\HttpFoundation\Session\SessionInterface');
+ $session->expects($this->once())->method('migrate')->with($this->equalTo(false));
+
+ $strategy = new SessionAuthenticationStrategy(SessionAuthenticationStrategy::MIGRATE);
+ $strategy->onAuthentication($this->getRequest($session), $this->getToken());
+ }
+
+ public function testSessionIsInvalidated()
+ {
+ $session = $this->getMock('Symfony\Component\HttpFoundation\Session\SessionInterface');
+ $session->expects($this->once())->method('invalidate');
+
+ $strategy = new SessionAuthenticationStrategy(SessionAuthenticationStrategy::INVALIDATE);
+ $strategy->onAuthentication($this->getRequest($session), $this->getToken());
+ }
+
+ private function getRequest($session = null)
+ {
+ $request = $this->getMock('Symfony\Component\HttpFoundation\Request');
+
+ if (null !== $session) {
+ $request->expects($this->any())->method('getSession')->will($this->returnValue($session));
+ }
+
+ return $request;
+ }
+
+ private function getToken()
+ {
+ return $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface');
+ }
+}
diff --git a/Http/composer.json b/Http/composer.json
new file mode 100644
index 0000000..1d40882
--- /dev/null
+++ b/Http/composer.json
@@ -0,0 +1,43 @@
+{
+ "name": "symfony/security-http",
+ "type": "library",
+ "description": "Symfony Security Component - HTTP Integration",
+ "keywords": [],
+ "homepage": "https://symfony.com",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "require": {
+ "php": ">=5.3.9",
+ "symfony/security-core": "~2.6",
+ "symfony/event-dispatcher": "~2.1",
+ "symfony/http-foundation": "~2.4",
+ "symfony/http-kernel": "~2.4"
+ },
+ "require-dev": {
+ "symfony/routing": "~2.2",
+ "symfony/security-csrf": "~2.4",
+ "psr/log": "~1.0"
+ },
+ "suggest": {
+ "symfony/security-csrf": "For using tokens to protect authentication/logout attempts",
+ "symfony/routing": "For using the HttpUtils class to create sub-requests, redirect the user, and match URLs"
+ },
+ "autoload": {
+ "psr-4": { "Symfony\\Component\\Security\\Http\\": "" }
+ },
+ "minimum-stability": "dev",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.7-dev"
+ }
+ }
+}
diff --git a/Http/phpunit.xml.dist b/Http/phpunit.xml.dist
new file mode 100644
index 0000000..49b36f2
--- /dev/null
+++ b/Http/phpunit.xml.dist
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<phpunit backupGlobals="false"
+ backupStaticAttributes="false"
+ colors="true"
+ convertErrorsToExceptions="true"
+ convertNoticesToExceptions="true"
+ convertWarningsToExceptions="true"
+ processIsolation="false"
+ stopOnFailure="false"
+ syntaxCheck="false"
+ bootstrap="vendor/autoload.php"
+>
+ <php>
+ <ini name="error_reporting" value="-1" />
+ </php>
+
+ <testsuites>
+ <testsuite name="Symfony Security Component HTTP Test Suite">
+ <directory>./Tests/</directory>
+ </testsuite>
+ </testsuites>
+
+ <filter>
+ <whitelist>
+ <directory>./</directory>
+ <exclude>
+ <directory>./vendor</directory>
+ <directory>./Tests</directory>
+ </exclude>
+ </whitelist>
+ </filter>
+</phpunit>