[SecurityBundle] decouple the logout PHP helper and Twig extension

This commit is contained in:
Fabien Potencier 2015-01-21 01:16:45 +01:00
parent 497fdd4cc6
commit f089dd4dd6
10 changed files with 241 additions and 91 deletions

View File

@ -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

View File

@ -0,0 +1,73 @@
<?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\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 <jmikola@gmail.com>
*/
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';
}
}

View File

@ -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'],

View File

@ -152,6 +152,12 @@
<argument type="service" id="security.exception_listener" />
</service>
<service id="security.logout_url_generator" class="Symfony\Component\Security\Http\Logout\LogoutUrlGenerator" public="false">
<argument type="service" id="request_stack" on-invalid="null" />
<argument type="service" id="router" on-invalid="null" />
<argument type="service" id="security.token_storage" on-invalid="null" />
</service>
<!-- Provisioning -->
<service id="security.user.provider.in_memory" class="%security.user.provider.in_memory.class%" abstract="true" public="false" />
<service id="security.user.provider.in_memory.user" class="%security.user.provider.in_memory.user.class%" abstract="true" public="false" />

View File

@ -12,9 +12,7 @@
<services>
<service id="templating.helper.logout_url" class="%templating.helper.logout_url.class%">
<tag name="templating.helper" alias="logout_url" />
<argument type="service" id="request_stack" />
<argument type="service" id="router" />
<argument type="service" id="security.token_storage" />
<argument type="service" id="security.logout_url_generator" />
</service>
<service id="templating.helper.security" class="%templating.helper.security.class%">

View File

@ -5,14 +5,14 @@
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<parameters>
<parameter key="twig.extension.logout_url.class">Symfony\Bundle\SecurityBundle\Twig\Extension\LogoutUrlExtension</parameter>
<parameter key="twig.extension.logout_url.class">Symfony\Bridge\Twig\Extension\LogoutUrlExtension</parameter>
<parameter key="twig.extension.security.class">Symfony\Bridge\Twig\Extension\SecurityExtension</parameter>
</parameters>
<services>
<service id="twig.extension.logout_url" class="%twig.extension.logout_url.class%" public="false">
<tag name="twig.extension" />
<argument type="service" id="templating.helper.logout_url" />
<argument type="service" id="security.logout_url_generator" />
</service>
<service id="twig.extension.security" class="%twig.extension.security.class%" public="false">

View File

@ -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);
}
/**

View File

@ -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 <jmikola@gmail.com>
*
* @deprecated since version 2.7, to be removed in 3.0. Use Symfony\Bridge\Twig\Extension\LogoutUrlExtension instead.
*/
class LogoutUrlExtension extends \Twig_Extension
{

View File

@ -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
-----

View File

@ -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;
}
}