summaryrefslogtreecommitdiffstats
path: root/Http/Firewall
diff options
context:
space:
mode:
Diffstat (limited to 'Http/Firewall')
-rw-r--r--Http/Firewall/AbstractAuthenticationListener.php12
-rw-r--r--Http/Firewall/AccessListener.php4
-rw-r--r--Http/Firewall/ChannelListener.php4
-rw-r--r--Http/Firewall/ContextListener.php21
-rw-r--r--Http/Firewall/ExceptionListener.php17
-rw-r--r--Http/Firewall/LogoutListener.php68
-rw-r--r--Http/Firewall/RememberMeListener.php18
-rw-r--r--Http/Firewall/SwitchUserListener.php16
8 files changed, 108 insertions, 52 deletions
diff --git a/Http/Firewall/AbstractAuthenticationListener.php b/Http/Firewall/AbstractAuthenticationListener.php
index 9452e29..6d95314 100644
--- a/Http/Firewall/AbstractAuthenticationListener.php
+++ b/Http/Firewall/AbstractAuthenticationListener.php
@@ -133,7 +133,7 @@ abstract class AbstractAuthenticationListener implements ListenerInterface
try {
if (!$request->hasPreviousSession()) {
- throw new SessionUnavailableException('Your session has timed-out, or you have disabled cookies.');
+ throw new SessionUnavailableException('Your session has timed out, or you have disabled cookies.');
}
if (null === $returnValue = $this->attemptAuthentication($request)) {
@@ -192,7 +192,9 @@ abstract class AbstractAuthenticationListener implements ListenerInterface
$this->securityContext->setToken(null);
if (null !== $this->failureHandler) {
- return $this->failureHandler->onAuthenticationFailure($request, $failed);
+ if (null !== $response = $this->failureHandler->onAuthenticationFailure($request, $failed)) {
+ return $response;
+ }
}
if (null === $this->options['failure_path']) {
@@ -236,9 +238,11 @@ abstract class AbstractAuthenticationListener implements ListenerInterface
$this->dispatcher->dispatch(SecurityEvents::INTERACTIVE_LOGIN, $loginEvent);
}
+ $response = null;
if (null !== $this->successHandler) {
$response = $this->successHandler->onAuthenticationSuccess($request, $token);
- } else {
+ }
+ if (null === $response) {
$response = $this->httpUtils->createRedirectResponse($request, $this->determineTargetUrl($request));
}
@@ -273,7 +277,7 @@ abstract class AbstractAuthenticationListener implements ListenerInterface
return $targetUrl;
}
- if ($this->options['use_referer'] && $targetUrl = $request->headers->get('Referer')) {
+ if ($this->options['use_referer'] && ($targetUrl = $request->headers->get('Referer')) && $targetUrl !== $request->getUriForPath($this->options['login_path'])) {
return $targetUrl;
}
diff --git a/Http/Firewall/AccessListener.php b/Http/Firewall/AccessListener.php
index 877b6c3..3e2d3a5 100644
--- a/Http/Firewall/AccessListener.php
+++ b/Http/Firewall/AccessListener.php
@@ -13,7 +13,7 @@ namespace Symfony\Component\Security\Http\Firewall;
use Symfony\Component\Security\Core\SecurityContextInterface;
use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface;
-use Symfony\Component\Security\Http\AccessMap;
+use Symfony\Component\Security\Http\AccessMapInterface;
use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
use Symfony\Component\HttpKernel\Log\LoggerInterface;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
@@ -33,7 +33,7 @@ class AccessListener implements ListenerInterface
private $authManager;
private $logger;
- public function __construct(SecurityContextInterface $context, AccessDecisionManagerInterface $accessDecisionManager, AccessMap $map, AuthenticationManagerInterface $authManager, LoggerInterface $logger = null)
+ public function __construct(SecurityContextInterface $context, AccessDecisionManagerInterface $accessDecisionManager, AccessMapInterface $map, AuthenticationManagerInterface $authManager, LoggerInterface $logger = null)
{
$this->context = $context;
$this->accessDecisionManager = $accessDecisionManager;
diff --git a/Http/Firewall/ChannelListener.php b/Http/Firewall/ChannelListener.php
index 847753f..9b0f8c6 100644
--- a/Http/Firewall/ChannelListener.php
+++ b/Http/Firewall/ChannelListener.php
@@ -11,7 +11,7 @@
namespace Symfony\Component\Security\Http\Firewall;
-use Symfony\Component\Security\Http\AccessMap;
+use Symfony\Component\Security\Http\AccessMapInterface;
use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface;
use Symfony\Component\HttpKernel\Log\LoggerInterface;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
@@ -28,7 +28,7 @@ class ChannelListener implements ListenerInterface
private $authenticationEntryPoint;
private $logger;
- public function __construct(AccessMap $map, AuthenticationEntryPointInterface $authenticationEntryPoint, LoggerInterface $logger = null)
+ public function __construct(AccessMapInterface $map, AuthenticationEntryPointInterface $authenticationEntryPoint, LoggerInterface $logger = null)
{
$this->map = $map;
$this->authenticationEntryPoint = $authenticationEntryPoint;
diff --git a/Http/Firewall/ContextListener.php b/Http/Firewall/ContextListener.php
index 52dea56..423ccb2 100644
--- a/Http/Firewall/ContextListener.php
+++ b/Http/Firewall/ContextListener.php
@@ -22,6 +22,7 @@ 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;
/**
@@ -43,6 +44,12 @@ class ContextListener implements ListenerInterface
throw new \InvalidArgumentException('$contextKey must not be empty.');
}
+ foreach ($userProviders as $userProvider) {
+ if (!$userProvider instanceof UserProviderInterface) {
+ throw new \InvalidArgumentException(sprintf('User provider "%s" must implement "Symfony\Component\Security\Core\User\UserProviderInterface".', get_class($userProvider)));
+ }
+ }
+
$this->context = $context;
$this->userProviders = $userProviders;
$this->contextKey = $contextKey;
@@ -96,19 +103,19 @@ class ContextListener implements ListenerInterface
return;
}
- if (null === $token = $this->context->getToken()) {
- return;
+ if (null !== $this->logger) {
+ $this->logger->debug('Write SecurityContext in the session');
}
- if (null === $token || $token instanceof AnonymousToken) {
+ if (null === $session = $event->getRequest()->getSession()) {
return;
}
- if (null !== $this->logger) {
- $this->logger->debug('Write SecurityContext in the session');
+ if ((null === $token = $this->context->getToken()) || ($token instanceof AnonymousToken)) {
+ $session->remove('_security_'.$this->contextKey);
+ } else {
+ $session->set('_security_'.$this->contextKey, serialize($token));
}
-
- $event->getRequest()->getSession()->set('_security_'.$this->contextKey, serialize($token));
}
/**
diff --git a/Http/Firewall/ExceptionListener.php b/Http/Firewall/ExceptionListener.php
index f61df81..05e7d14 100644
--- a/Http/Firewall/ExceptionListener.php
+++ b/Http/Firewall/ExceptionListener.php
@@ -15,12 +15,12 @@ use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Http\Authorization\AccessDeniedHandlerInterface;
use Symfony\Component\Security\Core\SecurityContextInterface;
use Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolverInterface;
-use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface;
use Symfony\Component\Security\Core\Exception\AccountStatusException;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
use Symfony\Component\Security\Core\Exception\InsufficientAuthenticationException;
+use Symfony\Component\Security\Core\Exception\LogoutException;
use Symfony\Component\Security\Http\HttpUtils;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Log\LoggerInterface;
@@ -98,7 +98,7 @@ class ExceptionListener
$token = $this->context->getToken();
if (!$this->authenticationTrustResolver->isFullFledged($token)) {
if (null !== $this->logger) {
- $this->logger->debug('Access denied (user is not fully authenticated); redirecting to authentication entry point');
+ $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()));
}
try {
@@ -110,7 +110,7 @@ class ExceptionListener
}
} else {
if (null !== $this->logger) {
- $this->logger->debug('Access is denied (and user is neither anonymous, nor remember-me)');
+ $this->logger->debug(sprintf('Access is denied (and user is neither anonymous, nor remember-me) by "%s" at line %s', $exception->getFile(), $exception->getLine()));
}
try {
@@ -141,6 +141,14 @@ class ExceptionListener
return;
}
}
+ } elseif ($exception instanceof LogoutException) {
+ if (null !== $this->logger) {
+ $this->logger->info(sprintf('Logout exception occurred; wrapping with AccessDeniedHttpException (%s)', $exception->getMessage()));
+ }
+
+ $event->setException(new AccessDeniedHttpException($exception->getMessage(), $exception));
+
+ return;
} else {
return;
}
@@ -160,10 +168,9 @@ class ExceptionListener
$this->setTargetPath($request);
- if ($authException instanceof AccountStatusException && ($token = $this->context->getToken()) instanceof UsernamePasswordToken) {
+ if ($authException instanceof AccountStatusException) {
// remove the security token to prevent infinite redirect loops
$this->context->setToken(null);
- $request->getSession()->remove('_security_'.$token->getProviderKey());
}
return $this->authenticationEntryPoint->start($request, $authException);
diff --git a/Http/Firewall/LogoutListener.php b/Http/Firewall/LogoutListener.php
index 07244f5..d145fa1 100644
--- a/Http/Firewall/LogoutListener.php
+++ b/Http/Firewall/LogoutListener.php
@@ -11,13 +11,15 @@
namespace Symfony\Component\Security\Http\Firewall;
-use Symfony\Component\Security\Http\Logout\LogoutSuccessHandlerInterface;
-
-use Symfony\Component\Security\Http\Logout\LogoutHandlerInterface;
-use Symfony\Component\Security\Core\SecurityContextInterface;
-use Symfony\Component\Security\Http\HttpUtils;
+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\Exception\LogoutException;
+use Symfony\Component\Security\Http\HttpUtils;
+use Symfony\Component\Security\Http\Logout\LogoutHandlerInterface;
+use Symfony\Component\Security\Http\Logout\LogoutSuccessHandlerInterface;
/**
* LogoutListener logout users.
@@ -27,28 +29,33 @@ use Symfony\Component\HttpKernel\Event\GetResponseEvent;
class LogoutListener implements ListenerInterface
{
private $securityContext;
- private $logoutPath;
- private $targetUrl;
+ private $options;
private $handlers;
private $successHandler;
private $httpUtils;
+ private $csrfProvider;
/**
* Constructor
*
* @param SecurityContextInterface $securityContext
* @param HttpUtils $httpUtils An HttpUtilsInterface instance
- * @param string $logoutPath The path that starts the logout process
- * @param string $targetUrl The URL to redirect to after logout
- * @param LogoutSuccessHandlerInterface $successHandler
+ * @param array $options An array of options to process a logout attempt
+ * @param LogoutSuccessHandlerInterface $successHandler A LogoutSuccessHandlerInterface instance
+ * @param CsrfProviderInterface $csrfProvider A CsrfProviderInterface instance
*/
- public function __construct(SecurityContextInterface $securityContext, HttpUtils $httpUtils, $logoutPath, $targetUrl = '/', LogoutSuccessHandlerInterface $successHandler = null)
+ public function __construct(SecurityContextInterface $securityContext, HttpUtils $httpUtils, array $options = array(), LogoutSuccessHandlerInterface $successHandler = null, CsrfProviderInterface $csrfProvider = null)
{
$this->securityContext = $securityContext;
$this->httpUtils = $httpUtils;
- $this->logoutPath = $logoutPath;
- $this->targetUrl = $targetUrl;
+ $this->options = array_merge(array(
+ 'csrf_parameter' => '_csrf_token',
+ 'intention' => 'logout',
+ 'logout_path' => '/logout',
+ 'target_url' => '/',
+ ), $options);
$this->successHandler = $successHandler;
+ $this->csrfProvider = $csrfProvider;
$this->handlers = array();
}
@@ -56,8 +63,6 @@ class LogoutListener implements ListenerInterface
* Adds a logout handler
*
* @param LogoutHandlerInterface $handler
- *
- * @return void
*/
public function addHandler(LogoutHandlerInterface $handler)
{
@@ -67,16 +72,29 @@ class LogoutListener implements ListenerInterface
/**
* Performs the logout if requested
*
+ * If a CsrfProviderInterface instance is available, it will be used to
+ * validate the request.
+ *
* @param GetResponseEvent $event A GetResponseEvent instance
+ * @throws InvalidCsrfTokenException if the CSRF token is invalid
+ * @throws RuntimeException if the LogoutSuccessHandlerInterface instance does not return a response
*/
public function handle(GetResponseEvent $event)
{
$request = $event->getRequest();
- if (!$this->httpUtils->checkRequestPath($request, $this->logoutPath)) {
+ if (!$this->requiresLogout($request)) {
return;
}
+ if (null !== $this->csrfProvider) {
+ $csrfToken = $request->get($this->options['csrf_parameter'], null, true);
+
+ if (false === $this->csrfProvider->isCsrfTokenValid($this->options['intention'], $csrfToken)) {
+ throw new LogoutException('Invalid CSRF token.');
+ }
+ }
+
if (null !== $this->successHandler) {
$response = $this->successHandler->onLogoutSuccess($request);
@@ -84,7 +102,7 @@ class LogoutListener implements ListenerInterface
throw new \RuntimeException('Logout Success Handler did not return a Response.');
}
} else {
- $response = $this->httpUtils->createRedirectResponse($request, $this->targetUrl);
+ $response = $this->httpUtils->createRedirectResponse($request, $this->options['target_url']);
}
// handle multiple logout attempts gracefully
@@ -98,4 +116,20 @@ class LogoutListener implements ListenerInterface
$event->setResponse($response);
}
+
+ /**
+ * Whether this request is asking for logout.
+ *
+ * The default implementation only processed requests to a specific path,
+ * but a subclass could change this to logout requests where
+ * certain parameters is present.
+ *
+ * @param Request $request
+ *
+ * @return Boolean
+ */
+ protected function requiresLogout(Request $request)
+ {
+ return $this->httpUtils->checkRequestPath($request, $this->options['logout_path']);
+ }
}
diff --git a/Http/Firewall/RememberMeListener.php b/Http/Firewall/RememberMeListener.php
index ccda113..3614f79 100644
--- a/Http/Firewall/RememberMeListener.php
+++ b/Http/Firewall/RememberMeListener.php
@@ -1,5 +1,14 @@
<?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\HttpKernel\Log\LoggerInterface;
@@ -12,15 +21,6 @@ use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
use Symfony\Component\Security\Http\SecurityEvents;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
-/*
- * This file is part of the Symfony framework.
- *
- * (c) Fabien Potencier <fabien@symfony.com>
- *
- * This source file is subject to the MIT license that is bundled
- * with this source code in the file LICENSE.
- */
-
/**
* RememberMeListener implements authentication capabilities via a cookie
*
diff --git a/Http/Firewall/SwitchUserListener.php b/Http/Firewall/SwitchUserListener.php
index 9780860..7f0aa78 100644
--- a/Http/Firewall/SwitchUserListener.php
+++ b/Http/Firewall/SwitchUserListener.php
@@ -86,9 +86,7 @@ class SwitchUserListener implements ListenerInterface
try {
$this->securityContext->setToken($this->attemptSwitchUser($request));
} catch (AuthenticationException $e) {
- if (null !== $this->logger) {
- $this->logger->warn(sprintf('Switch User failed: "%s"', $e->getMessage()));
- }
+ throw new \LogicException(sprintf('Switch User failed: "%s"', $e->getMessage()));
}
}
@@ -108,8 +106,14 @@ class SwitchUserListener implements ListenerInterface
private function attemptSwitchUser(Request $request)
{
$token = $this->securityContext->getToken();
- if (false !== $this->getOriginalToken($token)) {
- throw new \LogicException(sprintf('You are already switched to "%s" user.', $token->getUsername()));
+ $originalToken = $this->getOriginalToken($token);
+
+ if (false !== $originalToken) {
+ if ($token->getUsername() === $request->get($this->usernameParameter)) {
+ return $token;
+ } else {
+ throw new \LogicException(sprintf('You are already switched to "%s" user.', $token->getUsername()));
+ }
}
if (false === $this->accessDecisionManager->decide($token, array($this->role))) {
@@ -148,7 +152,7 @@ class SwitchUserListener implements ListenerInterface
private function attemptExitUser(Request $request)
{
if (false === $original = $this->getOriginalToken($this->securityContext->getToken())) {
- throw new AuthenticationCredentialsNotFoundException(sprintf('Could not find original Token object.'));
+ throw new AuthenticationCredentialsNotFoundException('Could not find original Token object.');
}
if (null !== $this->dispatcher) {