[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 2.7.0
----- -----
* added LogoutUrlExtension (provides `logout_url` and `logout_path`)
* added an HttpFoundation extension (provides the `absolute_url` and the `relative_path` functions) * added an HttpFoundation extension (provides the `absolute_url` and the `relative_path` functions)
2.5.0 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))); $listener->addMethodCall('addHandler', array(new Reference($handlerId)));
} }
// register with LogoutUrlHelper // register with LogoutUrlGenerator
$container $container
->getDefinition('templating.helper.logout_url') ->getDefinition('security.logout_url_generator')
->addMethodCall('registerListener', array( ->addMethodCall('registerListener', array(
$id, $id,
$firewall['logout']['path'], $firewall['logout']['path'],

View File

@ -152,6 +152,12 @@
<argument type="service" id="security.exception_listener" /> <argument type="service" id="security.exception_listener" />
</service> </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 --> <!-- 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" 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" /> <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> <services>
<service id="templating.helper.logout_url" class="%templating.helper.logout_url.class%"> <service id="templating.helper.logout_url" class="%templating.helper.logout_url.class%">
<tag name="templating.helper" alias="logout_url" /> <tag name="templating.helper" alias="logout_url" />
<argument type="service" id="request_stack" /> <argument type="service" id="security.logout_url_generator" />
<argument type="service" id="router" />
<argument type="service" id="security.token_storage" />
</service> </service>
<service id="templating.helper.security" class="%templating.helper.security.class%"> <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"> xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<parameters> <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> <parameter key="twig.extension.security.class">Symfony\Bridge\Twig\Extension\SecurityExtension</parameter>
</parameters> </parameters>
<services> <services>
<service id="twig.extension.logout_url" class="%twig.extension.logout_url.class%" public="false"> <service id="twig.extension.logout_url" class="%twig.extension.logout_url.class%" public="false">
<tag name="twig.extension" /> <tag name="twig.extension" />
<argument type="service" id="templating.helper.logout_url" /> <argument type="service" id="security.logout_url_generator" />
</service> </service>
<service id="twig.extension.security" class="%twig.extension.security.class%" public="false"> <service id="twig.extension.security" class="%twig.extension.security.class%" public="false">

View File

@ -12,12 +12,11 @@
namespace Symfony\Bundle\SecurityBundle\Templating\Helper; namespace Symfony\Bundle\SecurityBundle\Templating\Helper;
use Symfony\Component\DependencyInjection\ContainerInterface; 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\HttpFoundation\RequestStack;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface; 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\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface; use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Templating\Helper\Helper; use Symfony\Component\Templating\Helper\Helper;
/** /**
@ -35,45 +34,21 @@ class LogoutUrlHelper extends Helper
/** /**
* Constructor. * Constructor.
* *
* @param ContainerInterface|RequestStack $requestStack A ContainerInterface instance or RequestStack * @param ContainerInterface|LogoutUrlGenerator $generator A ContainerInterface or LogoutUrlGenerator instance
* @param UrlGeneratorInterface $router The router service * @param UrlGeneratorInterface|null $router The router service
* @param TokenStorageInterface|null $tokenStorage The token storage 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 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) { 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); 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 { } 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) 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) public function getLogoutUrl($key)
{ {
return $this->generateLogoutUrl($key, UrlGeneratorInterface::ABSOLUTE_URL); return $this->generator->getLogoutUrl($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;
} }
/** /**

View File

@ -11,12 +11,16 @@
namespace Symfony\Bundle\SecurityBundle\Twig\Extension; 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; use Symfony\Bundle\SecurityBundle\Templating\Helper\LogoutUrlHelper;
/** /**
* LogoutUrlHelper provides generator functions for the logout URL to Twig. * LogoutUrlHelper provides generator functions for the logout URL to Twig.
* *
* @author Jeremy Mikola <jmikola@gmail.com> * @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 class LogoutUrlExtension extends \Twig_Extension
{ {

View File

@ -4,7 +4,8 @@ CHANGELOG
2.7.0 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 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;
}
}