Skip to content

Commit

Permalink
[SecurityBundle] decouple the logout PHP helper and Twig extension
Browse files Browse the repository at this point in the history
  • Loading branch information
fabpot committed Jan 21, 2015
1 parent 497fdd4 commit f089dd4
Show file tree
Hide file tree
Showing 10 changed files with 241 additions and 91 deletions.
1 change: 1 addition & 0 deletions src/Symfony/Bridge/Twig/CHANGELOG.md
Expand Up @@ -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
Expand Down
73 changes: 73 additions & 0 deletions src/Symfony/Bridge/Twig/Extension/LogoutUrlExtension.php
@@ -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';
}
}
Expand Up @@ -337,9 +337,9 @@ private function createFirewall(ContainerBuilder $container, $id, $firewall, &$a
$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'],
Expand Down
Expand Up @@ -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" />
Expand Down
Expand Up @@ -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%">
Expand Down
Expand Up @@ -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">
Expand Down
Expand Up @@ -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;

/**
Expand All @@ -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->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->generator = $generator;
}

$this->listeners[$key] = array($logoutPath, $csrfTokenId, $csrfParameter, $csrfTokenManager);
}

/**
Expand All @@ -85,7 +60,7 @@ public function registerListener($key, $logoutPath, $csrfTokenId, $csrfParameter
*/
public function getLogoutPath($key)
{
return $this->generateLogoutUrl($key, UrlGeneratorInterface::ABSOLUTE_PATH);
return $this->generator->getLogoutPath($key, UrlGeneratorInterface::ABSOLUTE_PATH);
}

/**
Expand All @@ -97,54 +72,7 @@ public function getLogoutPath($key)
*/
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);
}

/**
Expand Down
Expand Up @@ -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
{
Expand Down
3 changes: 2 additions & 1 deletion src/Symfony/Component/Security/CHANGELOG.md
Expand Up @@ -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
-----
Expand Down

0 comments on commit f089dd4

Please sign in to comment.