diff options
author | Fabien Potencier <fabien.potencier@gmail.com> | 2011-06-20 07:08:46 +0200 |
---|---|---|
committer | Fabien Potencier <fabien.potencier@gmail.com> | 2011-06-22 14:47:19 +0200 |
commit | 100bc023abb4c1908b0d468dda6869e47494e355 (patch) | |
tree | 2780db9d5ab963bd0c8216622c647771f343fc64 /Http | |
parent | 2ebfd97612d3d5ef4fc6f632b27a242eed8a1f0f (diff) | |
download | symfony-security-100bc023abb4c1908b0d468dda6869e47494e355.zip symfony-security-100bc023abb4c1908b0d468dda6869e47494e355.tar.gz symfony-security-100bc023abb4c1908b0d468dda6869e47494e355.tar.bz2 |
[Security] added an HttpUtils class to manage logic related to Requests and Responses
This change removes the need for the {_locale} hack.
Now, all paths in the Security component can be:
* An absolute path (/login)
* An absolute URL (http://symfony.com/login)
* A route name (login)
So, if you want to use a path that includes a global parameter (like _locale),
use a route instead of a path.
Diffstat (limited to 'Http')
-rw-r--r-- | Http/EntryPoint/FormAuthenticationEntryPoint.php | 13 | ||||
-rw-r--r-- | Http/Firewall/AbstractAuthenticationListener.php | 22 | ||||
-rw-r--r-- | Http/Firewall/ExceptionListener.php | 9 | ||||
-rw-r--r-- | Http/Firewall/LogoutListener.php | 17 | ||||
-rw-r--r-- | Http/Firewall/UsernamePasswordFormAuthenticationListener.php | 5 | ||||
-rw-r--r-- | Http/HttpUtils.php | 99 |
6 files changed, 135 insertions, 30 deletions
diff --git a/Http/EntryPoint/FormAuthenticationEntryPoint.php b/Http/EntryPoint/FormAuthenticationEntryPoint.php index 6301606..1a35784 100644 --- a/Http/EntryPoint/FormAuthenticationEntryPoint.php +++ b/Http/EntryPoint/FormAuthenticationEntryPoint.php @@ -12,10 +12,9 @@ namespace Symfony\Component\Security\Http\EntryPoint; use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface; +use Symfony\Component\Security\Http\HttpUtils; use Symfony\Component\HttpKernel\HttpKernelInterface; /** @@ -28,17 +27,20 @@ class FormAuthenticationEntryPoint implements AuthenticationEntryPointInterface private $loginPath; private $useForward; private $httpKernel; + private $httpUtils; /** * Constructor * * @param HttpKernelInterface $kernel + * @param HttpUtils $httpUtils An HttpUtils instance * @param string $loginPath The path to the login form * @param Boolean $useForward Whether to forward or redirect to the login form */ - public function __construct(HttpKernelInterface $kernel, $loginPath, $useForward = false) + public function __construct(HttpKernelInterface $kernel, HttpUtils $httpUtils, $loginPath, $useForward = false) { $this->httpKernel = $kernel; + $this->httpUtils = $httpUtils; $this->loginPath = $loginPath; $this->useForward = (Boolean) $useForward; } @@ -48,13 +50,12 @@ class FormAuthenticationEntryPoint implements AuthenticationEntryPointInterface */ public function start(Request $request, AuthenticationException $authException = null) { - $path = str_replace('{_locale}', $request->getSession()->getLocale(), $this->loginPath); if ($this->useForward) { - $subRequest = Request::create($path, 'get', array(), $request->cookies->all(), array(), $request->server->all()); + $path = $this->httpUtils->createRequest($request, $this->loginPath); return $this->httpKernel->handle($subRequest, HttpKernelInterface::SUB_REQUEST); } - return new RedirectResponse(0 !== strpos($path, 'http') ? $request->getUriForPath($path) : $path, 302); + return $this->httpUtils->createRedirectResponse($request, $this->loginPath); } } diff --git a/Http/Firewall/AbstractAuthenticationListener.php b/Http/Firewall/AbstractAuthenticationListener.php index 4cbd6dd..52aff57 100644 --- a/Http/Firewall/AbstractAuthenticationListener.php +++ b/Http/Firewall/AbstractAuthenticationListener.php @@ -24,11 +24,11 @@ use Symfony\Component\HttpKernel\HttpKernelInterface; use Symfony\Component\HttpKernel\Event\GetResponseEvent; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\Security\Http\Event\InteractiveLoginEvent; use Symfony\Component\Security\Http\SecurityEvents; +use Symfony\Component\Security\Http\HttpUtils; /** * The AbstractAuthenticationListener is the preferred base class for all @@ -59,6 +59,7 @@ abstract class AbstractAuthenticationListener implements ListenerInterface private $successHandler; private $failureHandler; private $rememberMeServices; + private $httpUtils; /** * Constructor. @@ -66,6 +67,7 @@ abstract class AbstractAuthenticationListener implements ListenerInterface * @param SecurityContextInterface $securityContext A SecurityContext instance * @param AuthenticationManagerInterface $authenticationManager An AuthenticationManagerInterface instance * @param SessionAuthenticationStrategyInterface $sessionStrategy + * @param HttpUtils $httpUtils An HttpUtilsInterface instance * @param string $providerKey * @param array $options An array of options for the processing of a * successful, or failed authentication attempt @@ -74,7 +76,7 @@ abstract class AbstractAuthenticationListener implements ListenerInterface * @param LoggerInterface $logger A LoggerInterface instance * @param EventDispatcherInterface $dispatcher An EventDispatcherInterface instance */ - public function __construct(SecurityContextInterface $securityContext, AuthenticationManagerInterface $authenticationManager, SessionAuthenticationStrategyInterface $sessionStrategy, $providerKey, array $options = array(), AuthenticationSuccessHandlerInterface $successHandler = null, AuthenticationFailureHandlerInterface $failureHandler = null, LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null) + public function __construct(SecurityContextInterface $securityContext, AuthenticationManagerInterface $authenticationManager, SessionAuthenticationStrategyInterface $sessionStrategy, HttpUtils $httpUtils, $providerKey, array $options = array(), AuthenticationSuccessHandlerInterface $successHandler = null, AuthenticationFailureHandlerInterface $failureHandler = null, LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null) { if (empty($providerKey)) { throw new \InvalidArgumentException('$providerKey must not be empty.'); @@ -98,6 +100,7 @@ abstract class AbstractAuthenticationListener implements ListenerInterface ), $options); $this->logger = $logger; $this->dispatcher = $dispatcher; + $this->httpUtils = $httpUtils; } /** @@ -165,7 +168,7 @@ abstract class AbstractAuthenticationListener implements ListenerInterface */ protected function requiresAuthentication(Request $request) { - return str_replace('{_locale}', $request->getSession()->getLocale(), $this->options['check_path']) === $request->getPathInfo(); + return $this->httpUtils->checkRequestPath($request, $this->options['check_path']); } /** @@ -195,26 +198,24 @@ abstract class AbstractAuthenticationListener implements ListenerInterface $this->options['failure_path'] = $this->options['login_path']; } - $path = str_replace('{_locale}', $request->getSession()->getLocale(), $this->options['failure_path']); - if ($this->options['failure_forward']) { if (null !== $this->logger) { - $this->logger->debug(sprintf('Forwarding to %s', $path)); + $this->logger->debug(sprintf('Forwarding to %s', $this->options['failure_path'])); } - $subRequest = Request::create($path, 'get', array(), $request->cookies->all(), array(), $request->server->all()); + $subRequest = $this->httpUtils->createRequest($request, $this->options['failure_path']); $subRequest->attributes->set(SecurityContextInterface::AUTHENTICATION_ERROR, $failed); return $event->getKernel()->handle($subRequest, HttpKernelInterface::SUB_REQUEST); } if (null !== $this->logger) { - $this->logger->debug(sprintf('Redirecting to %s', $path)); + $this->logger->debug(sprintf('Redirecting to %s', $this->options['failure_path'])); } $request->getSession()->set(SecurityContextInterface::AUTHENTICATION_ERROR, $failed); - return new RedirectResponse(0 !== strpos($path, 'http') ? $request->getUriForPath($path) : $path, 302); + return $this->httpUtils->createRedirectResponse($request, $this->options['failure_path']); } private function onSuccess(GetResponseEvent $event, Request $request, TokenInterface $token) @@ -237,8 +238,7 @@ abstract class AbstractAuthenticationListener implements ListenerInterface if (null !== $this->successHandler) { $response = $this->successHandler->onAuthenticationSuccess($request, $token); } else { - $path = str_replace('{_locale}', $session->getLocale(), $this->determineTargetUrl($request)); - $response = new RedirectResponse(0 !== strpos($path, 'http') ? $request->getUriForPath($path) : $path, 302); + $response = $this->httpUtils->createRedirectResponse($request, $this->determineTargetUrl($request)); } if (null !== $this->rememberMeServices) { diff --git a/Http/Firewall/ExceptionListener.php b/Http/Firewall/ExceptionListener.php index 5242815..737d644 100644 --- a/Http/Firewall/ExceptionListener.php +++ b/Http/Firewall/ExceptionListener.php @@ -16,12 +16,13 @@ use Symfony\Component\Security\Http\Authorization\AccessDeniedHandlerInterface; use Symfony\Component\Security\Core\SecurityContextInterface; use Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolverInterface; use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface; -use Symfony\Component\HttpKernel\Log\LoggerInterface; use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Core\Exception\AccessDeniedException; use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken; use Symfony\Component\Security\Core\Exception\InsufficientAuthenticationException; +use Symfony\Component\Security\Http\HttpUtils; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Log\LoggerInterface; use Symfony\Component\HttpKernel\HttpKernelInterface; use Symfony\Component\HttpKernel\KernelEvents; use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent; @@ -41,11 +42,13 @@ class ExceptionListener private $authenticationTrustResolver; private $errorPage; private $logger; + private $httpUtils; - public function __construct(SecurityContextInterface $context, AuthenticationTrustResolverInterface $trustResolver, AuthenticationEntryPointInterface $authenticationEntryPoint = null, $errorPage = null, AccessDeniedHandlerInterface $accessDeniedHandler = null, LoggerInterface $logger = null) + public function __construct(SecurityContextInterface $context, AuthenticationTrustResolverInterface $trustResolver, HttpUtils $httpUtils, AuthenticationEntryPointInterface $authenticationEntryPoint = null, $errorPage = null, AccessDeniedHandlerInterface $accessDeniedHandler = null, LoggerInterface $logger = null) { $this->context = $context; $this->accessDeniedHandler = $accessDeniedHandler; + $this->httpUtils = $httpUtils; $this->authenticationEntryPoint = $authenticationEntryPoint; $this->authenticationTrustResolver = $trustResolver; $this->errorPage = $errorPage; @@ -115,7 +118,7 @@ class ExceptionListener return; } - $subRequest = Request::create($this->errorPage, 'get', array(), $request->cookies->all(), array(), $request->server->all()); + $subRequest = $this->httpUtils->createRequest($request, $this->errorPage); $subRequest->attributes->set(SecurityContextInterface::ACCESS_DENIED_ERROR, $exception); $response = $event->getKernel()->handle($subRequest, HttpKernelInterface::SUB_REQUEST, true); diff --git a/Http/Firewall/LogoutListener.php b/Http/Firewall/LogoutListener.php index e1a5f3d..06454a3 100644 --- a/Http/Firewall/LogoutListener.php +++ b/Http/Firewall/LogoutListener.php @@ -15,6 +15,7 @@ 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\HttpFoundation\Response; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpKernel\Event\GetResponseEvent; @@ -31,18 +32,21 @@ class LogoutListener implements ListenerInterface private $targetUrl; private $handlers; private $successHandler; + private $httpUtils; /** * Constructor * * @param SecurityContextInterface $securityContext - * @param string $logoutPath The path that starts the logout process - * @param string $targetUrl The URL to redirect to after logout + * @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 */ - public function __construct(SecurityContextInterface $securityContext, $logoutPath, $targetUrl = '/', LogoutSuccessHandlerInterface $successHandler = null) + public function __construct(SecurityContextInterface $securityContext, HttpUtils $httpUtils, $logoutPath, $targetUrl = '/', LogoutSuccessHandlerInterface $successHandler = null) { $this->securityContext = $securityContext; + $this->httpUtils = $httpUtils; $this->logoutPath = $logoutPath; $this->targetUrl = $targetUrl; $this->successHandler = $successHandler; @@ -69,10 +73,7 @@ class LogoutListener implements ListenerInterface { $request = $event->getRequest(); - $logoutPath = str_replace('{_locale}', $request->getSession()->getLocale(), $this->logoutPath); - $targetUrl = str_replace('{_locale}', $request->getSession()->getLocale(), $this->targetUrl); - - if ($logoutPath !== $request->getPathInfo()) { + if (!$this->httpUtils->checkRequestPath($request, $this->logoutPath)) { return; } @@ -83,7 +84,7 @@ class LogoutListener implements ListenerInterface throw new \RuntimeException('Logout Success Handler did not return a Response.'); } } else { - $response = new RedirectResponse(0 !== strpos($targetUrl, 'http') ? $request->getUriForPath($targetUrl) : $targetUrl, 302); + $response = $this->httpUtils->createRedirectResponse($request, $this->targetUrl); } // handle multiple logout attempts gracefully diff --git a/Http/Firewall/UsernamePasswordFormAuthenticationListener.php b/Http/Firewall/UsernamePasswordFormAuthenticationListener.php index a6b6968..bd2cec1 100644 --- a/Http/Firewall/UsernamePasswordFormAuthenticationListener.php +++ b/Http/Firewall/UsernamePasswordFormAuthenticationListener.php @@ -17,6 +17,7 @@ use Symfony\Component\HttpKernel\Log\LoggerInterface; 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\UsernamePasswordToken; use Symfony\Component\Security\Core\Exception\InvalidCsrfTokenException; @@ -36,9 +37,9 @@ class UsernamePasswordFormAuthenticationListener extends AbstractAuthenticationL /** * {@inheritdoc} */ - public function __construct(SecurityContextInterface $securityContext, AuthenticationManagerInterface $authenticationManager, SessionAuthenticationStrategyInterface $sessionStrategy, $providerKey, array $options = array(), AuthenticationSuccessHandlerInterface $successHandler = null, AuthenticationFailureHandlerInterface $failureHandler = null, LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null, CsrfProviderInterface $csrfProvider = null) + public function __construct(SecurityContextInterface $securityContext, AuthenticationManagerInterface $authenticationManager, SessionAuthenticationStrategyInterface $sessionStrategy, HttpUtils $httpUtils, $providerKey, array $options = array(), AuthenticationSuccessHandlerInterface $successHandler = null, AuthenticationFailureHandlerInterface $failureHandler = null, LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null, CsrfProviderInterface $csrfProvider = null) { - parent::__construct($securityContext, $authenticationManager, $sessionStrategy, $providerKey, array_merge(array( + parent::__construct($securityContext, $authenticationManager, $sessionStrategy, $httpUtils, $providerKey, array_merge(array( 'username_parameter' => '_username', 'password_parameter' => '_password', 'csrf_parameter' => '_csrf_token', diff --git a/Http/HttpUtils.php b/Http/HttpUtils.php new file mode 100644 index 0000000..6b674aa --- /dev/null +++ b/Http/HttpUtils.php @@ -0,0 +1,99 @@ +<?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; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\RedirectResponse; +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; + +/** + * Encapsulates the logic needed to create sub-requests, redirect the user, and match URLs. + * + * @author Fabien Potencier <fabien@symfony.com> + */ +class HttpUtils +{ + private $urlGenerator; + + /** + * Constructor. + * + * @param UrlGeneratorInterface $urlGenerator An UrlGeneratorInterface instance + */ + public function __construct(UrlGeneratorInterface $urlGenerator = null) + { + $this->urlGenerator = $urlGenerator; + } + + /** + * Creates a redirect Response. + * + * @param Request $request A Request instance + * @param string $path A path (an absolute path (/foo), an absolute URL (http://...), or a route name (foo)) + * @param integer $status The status code + * + * @return Response A RedirectResponse instance + */ + public function createRedirectResponse(Request $request, $path, $status = 302) + { + if (0 === strpos($path, '/')) { + $path = $request->getUriForPath($path); + } elseif (0 !== strpos($path, 'http')) { + $path = $this->generateUrl($path, true); + } + + return new RedirectResponse($path, 302); + } + + /** + * Creates a Request. + * + * @param Request $request The current Request instance + * @param string $path A path (an absolute path (/foo), an absolute URL (http://...), or a route name (foo)) + * + * @return Request A Request instance + */ + public function createRequest(Request $request, $path) + { + if ($path && '/' !== $path[0] && 0 !== strpos($path, 'http')) { + $path = $this->generateUrl($path, true); + } + + return Request::create($path, 'get', array(), $request->cookies->all(), array(), $request->server->all()); + } + + /** + * Checks that a given path matches the Request. + * + * @param Request $request A Request instance + * @param string $path A path (an absolute path (/foo), an absolute URL (http://...), or a route name (foo)) + * + * @return Boolean true if the path is the same as the one from the Request, false otherwise + */ + public function checkRequestPath(Request $request, $path) + { + if ('/' !== $path[0]) { + $path = preg_replace('#'.preg_quote($request->getBaseUrl(), '#').'#', '', $this->generateUrl($path)); + } + + return $path === $request->getPathInfo(); + } + + private function generateUrl($route, $absolute = false) + { + if (null === $this->urlGenerator) { + throw new \LogicException('You must provide a UrlGeneratorInterface instance to be able to use routes.'); + } + + return $this->urlGenerator->generate($route, array(), $absolute); + } +} |