diff --git a/src/Symfony/Bridge/Twig/CHANGELOG.md b/src/Symfony/Bridge/Twig/CHANGELOG.md index c4df599be1..887e9acf34 100644 --- a/src/Symfony/Bridge/Twig/CHANGELOG.md +++ b/src/Symfony/Bridge/Twig/CHANGELOG.md @@ -4,6 +4,7 @@ CHANGELOG 2.7.0 ----- + * added LogoutUrlExtension (provides `logout_url` and `logout_path`) * added an HttpFoundation extension (provides the `absolute_url` and the `relative_path` functions) 2.5.0 diff --git a/src/Symfony/Bridge/Twig/Extension/LogoutUrlExtension.php b/src/Symfony/Bridge/Twig/Extension/LogoutUrlExtension.php new file mode 100644 index 0000000000..7fc278758e --- /dev/null +++ b/src/Symfony/Bridge/Twig/Extension/LogoutUrlExtension.php @@ -0,0 +1,73 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Twig\Extension; + +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; +use Symfony\Component\Security\Http\Logout\LogoutUrlGenerator; + +/** + * LogoutUrlHelper provides generator functions for the logout URL to Twig. + * + * @author Jeremy Mikola + */ +class LogoutUrlExtension extends \Twig_Extension +{ + private $generator; + + public function __construct(LogoutUrlGenerator $generator) + { + $this->generator = $generator; + } + + /** + * {@inheritdoc} + */ + public function getFunctions() + { + return array( + new \Twig_SimpleFunction('logout_url', array($this, 'getLogoutUrl')), + new \Twig_SimpleFunction('logout_path', array($this, 'getLogoutPath')), + ); + } + + /** + * Generates the relative logout URL for the firewall. + * + * @param string|null $key The firewall key or null to use the current firewall key + * + * @return string The relative logout URL + */ + public function getLogoutPath($key = null) + { + return $this->generator->getLogoutPath($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 absolute logout URL + */ + public function getLogoutUrl($key = null) + { + return $this->generator->getLogoutUrl($key, UrlGeneratorInterface::ABSOLUTE_URL); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'logout_url'; + } +} diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php index e322a6aa44..106083643b 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php @@ -337,9 +337,9 @@ class SecurityExtension extends Extension $listener->addMethodCall('addHandler', array(new Reference($handlerId))); } - // register with LogoutUrlHelper + // register with LogoutUrlGenerator $container - ->getDefinition('templating.helper.logout_url') + ->getDefinition('security.logout_url_generator') ->addMethodCall('registerListener', array( $id, $firewall['logout']['path'], diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml b/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml index 3d02095bba..ca47065a78 100644 --- a/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml +++ b/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml @@ -152,6 +152,12 @@ + + + + + + diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/config/templating_php.xml b/src/Symfony/Bundle/SecurityBundle/Resources/config/templating_php.xml index 02ffa59582..b10d060d8a 100644 --- a/src/Symfony/Bundle/SecurityBundle/Resources/config/templating_php.xml +++ b/src/Symfony/Bundle/SecurityBundle/Resources/config/templating_php.xml @@ -12,9 +12,7 @@ - - - + diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/config/templating_twig.xml b/src/Symfony/Bundle/SecurityBundle/Resources/config/templating_twig.xml index 582dad7874..9fe6c4adde 100644 --- a/src/Symfony/Bundle/SecurityBundle/Resources/config/templating_twig.xml +++ b/src/Symfony/Bundle/SecurityBundle/Resources/config/templating_twig.xml @@ -5,14 +5,14 @@ xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> - Symfony\Bundle\SecurityBundle\Twig\Extension\LogoutUrlExtension + Symfony\Bridge\Twig\Extension\LogoutUrlExtension Symfony\Bridge\Twig\Extension\SecurityExtension - + diff --git a/src/Symfony/Bundle/SecurityBundle/Templating/Helper/LogoutUrlHelper.php b/src/Symfony/Bundle/SecurityBundle/Templating/Helper/LogoutUrlHelper.php index 8ef5ee7a42..4476d562fd 100644 --- a/src/Symfony/Bundle/SecurityBundle/Templating/Helper/LogoutUrlHelper.php +++ b/src/Symfony/Bundle/SecurityBundle/Templating/Helper/LogoutUrlHelper.php @@ -12,12 +12,11 @@ namespace Symfony\Bundle\SecurityBundle\Templating\Helper; use Symfony\Component\DependencyInjection\ContainerInterface; -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\Http\Logout\LogoutUrlGenerator; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; -use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface; +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Component\Templating\Helper\Helper; /** @@ -35,45 +34,21 @@ class LogoutUrlHelper extends Helper /** * Constructor. * - * @param ContainerInterface|RequestStack $requestStack A ContainerInterface instance or RequestStack - * @param UrlGeneratorInterface $router The router service - * @param TokenStorageInterface|null $tokenStorage The token storage service + * @param ContainerInterface|LogoutUrlGenerator $generator A ContainerInterface or LogoutUrlGenerator instance + * @param UrlGeneratorInterface|null $router The router service + * @param TokenStorageInterface|null $tokenStorage The token storage service * * @deprecated Passing a ContainerInterface as a first argument is deprecated since 2.7 and will be removed in 3.0. + * @deprecated Passing a second and third argument is deprecated since 2.7 and will be removed in 3.0. */ - public function __construct($requestStack, UrlGeneratorInterface $router, TokenStorageInterface $tokenStorage = null) + public function __construct($generator, UrlGeneratorInterface $router = null, TokenStorageInterface $tokenStorage = null) { if ($requestStack instanceof ContainerInterface) { - $this->requestStack = $requestStack->get('request_stack'); + $this->generator = $container->get('security.logout_url_generator'); trigger_error('The '.__CLASS__.' constructor will require a RequestStack instead of a ContainerInterface instance in 3.0.', E_USER_DEPRECATED); - } elseif ($requestStack instanceof RequestStack) { - $this->requestStack = $requestStack; } else { - throw new \InvalidArgumentException(sprintf('%s takes either a RequestStack or a ContainerInterface object as its first argument.', __METHOD__)); + $this->generator = $generator; } - - $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); } /** @@ -85,7 +60,7 @@ class LogoutUrlHelper extends Helper */ public function getLogoutPath($key) { - return $this->generateLogoutUrl($key, UrlGeneratorInterface::ABSOLUTE_PATH); + return $this->generator->getLogoutPath($key, UrlGeneratorInterface::ABSOLUTE_PATH); } /** @@ -97,54 +72,7 @@ class LogoutUrlHelper extends Helper */ public function getLogoutUrl($key) { - 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]) { - $request = $this->requestStack->getCurrentRequest(); - - $url = UrlGeneratorInterface::ABSOLUTE_URL === $referenceType ? $request->getUriForPath($logoutPath) : $request->getBasePath().$logoutPath; - - if (!empty($parameters)) { - $url .= '?'.http_build_query($parameters); - } - } else { - $url = $this->router->generate($logoutPath, $parameters, $referenceType); - } - - return $url; + return $this->generator->getLogoutUrl($key, UrlGeneratorInterface::ABSOLUTE_URL); } /** diff --git a/src/Symfony/Bundle/SecurityBundle/Twig/Extension/LogoutUrlExtension.php b/src/Symfony/Bundle/SecurityBundle/Twig/Extension/LogoutUrlExtension.php index 3473f97cad..206ab539ef 100644 --- a/src/Symfony/Bundle/SecurityBundle/Twig/Extension/LogoutUrlExtension.php +++ b/src/Symfony/Bundle/SecurityBundle/Twig/Extension/LogoutUrlExtension.php @@ -11,12 +11,16 @@ namespace Symfony\Bundle\SecurityBundle\Twig\Extension; +trigger_error('The '.__NAMESPACE__.'\LogoutUrlExtension class is deprecated since version 2.5 and will be removed in 3.0. Use Symfony\Bridge\Twig\Extension\LogoutUrlExtension instead.', E_USER_DEPRECATED); + use Symfony\Bundle\SecurityBundle\Templating\Helper\LogoutUrlHelper; /** * LogoutUrlHelper provides generator functions for the logout URL to Twig. * * @author Jeremy Mikola + * + * @deprecated since version 2.7, to be removed in 3.0. Use Symfony\Bridge\Twig\Extension\LogoutUrlExtension instead. */ class LogoutUrlExtension extends \Twig_Extension { diff --git a/src/Symfony/Component/Security/CHANGELOG.md b/src/Symfony/Component/Security/CHANGELOG.md index 677c1853b6..c44668eba7 100644 --- a/src/Symfony/Component/Security/CHANGELOG.md +++ b/src/Symfony/Component/Security/CHANGELOG.md @@ -4,7 +4,8 @@ CHANGELOG 2.7.0 ----- -* Added the triggering of the `Symfony\Component\Security\Http\SecurityEvents::INTERACTIVE_LOGIN` in `Symfony\Component\Security\Http\Firewall\SimplePreAuthenticationListener` + * added LogoutUrlGenerator + * added the triggering of the `Symfony\Component\Security\Http\SecurityEvents::INTERACTIVE_LOGIN` in `Symfony\Component\Security\Http\Firewall\SimplePreAuthenticationListener` 2.6.0 ----- diff --git a/src/Symfony/Component/Security/Http/Logout/LogoutUrlGenerator.php b/src/Symfony/Component/Security/Http/Logout/LogoutUrlGenerator.php new file mode 100644 index 0000000000..298c22445c --- /dev/null +++ b/src/Symfony/Component/Security/Http/Logout/LogoutUrlGenerator.php @@ -0,0 +1,139 @@ + + * + * 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 + * @author Jeremy Mikola + */ +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; + } +}