[Security] Fix logout

This commit is contained in:
MatTheCat 2018-05-11 20:46:08 +02:00 committed by Nicolas Grekas
parent b7feafcf58
commit 9e88eb5aa9
12 changed files with 119 additions and 23 deletions

View File

@ -222,13 +222,14 @@ class SecurityExtension extends Extension
$mapDef = $container->getDefinition('security.firewall.map');
$map = $authenticationProviders = array();
foreach ($firewalls as $name => $firewall) {
list($matcher, $listeners, $exceptionListener) = $this->createFirewall($container, $name, $firewall, $authenticationProviders, $providerIds);
list($matcher, $listeners, $exceptionListener, $logoutListener) = $this->createFirewall($container, $name, $firewall, $authenticationProviders, $providerIds);
$contextId = 'security.firewall.map.context.'.$name;
$context = $container->setDefinition($contextId, new DefinitionDecorator('security.firewall.context'));
$context
->replaceArgument(0, $listeners)
->replaceArgument(1, $exceptionListener)
->replaceArgument(2, $logoutListener)
;
$map[$contextId] = $matcher;
}
@ -259,7 +260,7 @@ class SecurityExtension extends Extension
// Security disabled?
if (false === $firewall['security']) {
return array($matcher, array(), null);
return array($matcher, array(), null, null);
}
// Provider id (take the first registered provider if none defined)
@ -286,15 +287,15 @@ class SecurityExtension extends Extension
}
// Logout listener
$logoutListenerId = null;
if (isset($firewall['logout'])) {
$listenerId = 'security.logout_listener.'.$id;
$listener = $container->setDefinition($listenerId, new DefinitionDecorator('security.logout_listener'));
$listener->replaceArgument(3, array(
$logoutListenerId = 'security.logout_listener.'.$id;
$logoutListener = $container->setDefinition($logoutListenerId, new DefinitionDecorator('security.logout_listener'));
$logoutListener->replaceArgument(3, array(
'csrf_parameter' => $firewall['logout']['csrf_parameter'],
'intention' => $firewall['logout']['csrf_token_id'],
'logout_path' => $firewall['logout']['path'],
));
$listeners[] = new Reference($listenerId);
// add logout success handler
if (isset($firewall['logout']['success_handler'])) {
@ -304,16 +305,16 @@ class SecurityExtension extends Extension
$logoutSuccessHandler = $container->setDefinition($logoutSuccessHandlerId, new DefinitionDecorator('security.logout.success_handler'));
$logoutSuccessHandler->replaceArgument(1, $firewall['logout']['target']);
}
$listener->replaceArgument(2, new Reference($logoutSuccessHandlerId));
$logoutListener->replaceArgument(2, new Reference($logoutSuccessHandlerId));
// add CSRF provider
if (isset($firewall['logout']['csrf_token_generator'])) {
$listener->addArgument(new Reference($firewall['logout']['csrf_token_generator']));
$logoutListener->addArgument(new Reference($firewall['logout']['csrf_token_generator']));
}
// add session logout handler
if (true === $firewall['logout']['invalidate_session'] && false === $firewall['stateless']) {
$listener->addMethodCall('addHandler', array(new Reference('security.logout.handler.session')));
$logoutListener->addMethodCall('addHandler', array(new Reference('security.logout.handler.session')));
}
// add cookie logout handler
@ -322,12 +323,12 @@ class SecurityExtension extends Extension
$cookieHandler = $container->setDefinition($cookieHandlerId, new DefinitionDecorator('security.logout.handler.cookie_clearing'));
$cookieHandler->addArgument($firewall['logout']['delete_cookies']);
$listener->addMethodCall('addHandler', array(new Reference($cookieHandlerId)));
$logoutListener->addMethodCall('addHandler', array(new Reference($cookieHandlerId)));
}
// add custom handlers
foreach ($firewall['logout']['handlers'] as $handlerId) {
$listener->addMethodCall('addHandler', array(new Reference($handlerId)));
$logoutListener->addMethodCall('addHandler', array(new Reference($handlerId)));
}
// register with LogoutUrlGenerator
@ -362,7 +363,7 @@ class SecurityExtension extends Extension
// Exception listener
$exceptionListener = new Reference($this->createExceptionListener($container, $firewall, $id, $configuredEntryPoint ?: $defaultEntryPoint, $firewall['stateless']));
return array($matcher, $listeners, $exceptionListener);
return array($matcher, $listeners, $exceptionListener, null !== $logoutListenerId ? new Reference($logoutListenerId) : null);
}
private function createContextListener($container, $contextKey)

View File

@ -150,6 +150,7 @@
<service id="security.firewall.context" class="%security.firewall.context.class%" abstract="true">
<argument type="collection" />
<argument type="service" id="security.exception_listener" />
<argument /> <!-- LogoutListener -->
</service>
<service id="security.logout_url_generator" class="Symfony\Component\Security\Http\Logout\LogoutUrlGenerator" public="false">

View File

@ -12,6 +12,7 @@
namespace Symfony\Bundle\SecurityBundle\Security;
use Symfony\Component\Security\Http\Firewall\ExceptionListener;
use Symfony\Component\Security\Http\Firewall\LogoutListener;
/**
* This is a wrapper around the actual firewall configuration which allows us
@ -23,15 +24,17 @@ class FirewallContext
{
private $listeners;
private $exceptionListener;
private $logoutListener;
public function __construct(array $listeners, ExceptionListener $exceptionListener = null)
public function __construct(array $listeners, ExceptionListener $exceptionListener = null, LogoutListener $logoutListener = null)
{
$this->listeners = $listeners;
$this->exceptionListener = $exceptionListener;
$this->logoutListener = $logoutListener;
}
public function getContext()
{
return array($this->listeners, $this->exceptionListener);
return array($this->listeners, $this->exceptionListener, $this->logoutListener);
}
}

View File

@ -41,6 +41,6 @@ class FirewallMap implements FirewallMapInterface
}
}
return array(array(), null);
return array(array(), null, null);
}
}

View File

@ -76,7 +76,6 @@ abstract class CompleteConfigurationTest extends TestCase
array(),
array(
'security.channel_listener',
'security.logout_listener.secure',
'security.authentication.listener.x509.secure',
'security.authentication.listener.remote_user.secure',
'security.authentication.listener.form.secure',

View File

@ -0,0 +1,34 @@
<?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\Bundle\SecurityBundle\Tests\Functional;
class LogoutTest extends WebTestCase
{
public function testSessionLessRememberMeLogout()
{
$client = $this->createClient(array('test_case' => 'RememberMeLogout', 'root_config' => 'config.yml'));
$client->request('POST', '/login', array(
'_username' => 'johannes',
'_password' => 'test',
));
$cookieJar = $client->getCookieJar();
$cookieJar->expire(session_name());
$this->assertNotNull($cookieJar->get('REMEMBERME'));
$client->request('GET', '/logout');
$this->assertNull($cookieJar->get('REMEMBERME'));
}
}

View File

@ -0,0 +1,18 @@
<?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.
*/
use Symfony\Bundle\SecurityBundle\SecurityBundle;
use Symfony\Bundle\FrameworkBundle\FrameworkBundle;
return array(
new FrameworkBundle(),
new SecurityBundle(),
);

View File

@ -0,0 +1,25 @@
imports:
- { resource: ./../config/framework.yml }
security:
encoders:
Symfony\Component\Security\Core\User\User: plaintext
providers:
in_memory:
memory:
users:
johannes: { password: test, roles: [ROLE_USER] }
firewalls:
default:
form_login:
check_path: login
remember_me: true
require_previous_session: false
remember_me:
always_remember_me: true
key: key
logout: ~
anonymous: ~
stateless: true

View File

@ -0,0 +1,5 @@
login:
path: /login
logout:
path: /logout

View File

@ -18,7 +18,7 @@
"require": {
"php": ">=5.3.9",
"ext-xml": "*",
"symfony/security": "~2.7.38|~2.8.31",
"symfony/security": "~2.7.47|~2.8.40",
"symfony/security-acl": "~2.7",
"symfony/http-kernel": "~2.7"
},

View File

@ -47,20 +47,29 @@ class Firewall implements EventSubscriberInterface
}
// register listeners for this firewall
list($listeners, $exceptionListener) = $this->map->getListeners($event->getRequest());
$listeners = $this->map->getListeners($event->getRequest());
$authenticationListeners = $listeners[0];
$exceptionListener = $listeners[1];
$logoutListener = isset($listeners[2]) ? $listeners[2] : null;
if (null !== $exceptionListener) {
$this->exceptionListeners[$event->getRequest()] = $exceptionListener;
$exceptionListener->register($this->dispatcher);
}
// initiate the listener chain
foreach ($listeners as $listener) {
foreach ($authenticationListeners as $listener) {
$listener->handle($event);
if ($event->hasResponse()) {
break;
}
}
if (null !== $logoutListener) {
$logoutListener->handle($event);
}
}
public function onKernelFinishRequest(FinishRequestEvent $event)

View File

@ -14,6 +14,7 @@ namespace Symfony\Component\Security\Http;
use Symfony\Component\HttpFoundation\RequestMatcherInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Http\Firewall\ExceptionListener;
use Symfony\Component\Security\Http\Firewall\LogoutListener;
/**
* FirewallMap allows configuration of different firewalls for specific parts
@ -25,9 +26,9 @@ class FirewallMap implements FirewallMapInterface
{
private $map = array();
public function add(RequestMatcherInterface $requestMatcher = null, array $listeners = array(), ExceptionListener $exceptionListener = null)
public function add(RequestMatcherInterface $requestMatcher = null, array $listeners = array(), ExceptionListener $exceptionListener = null, LogoutListener $logoutListener = null)
{
$this->map[] = array($requestMatcher, $listeners, $exceptionListener);
$this->map[] = array($requestMatcher, $listeners, $exceptionListener, $logoutListener);
}
/**
@ -37,10 +38,10 @@ class FirewallMap implements FirewallMapInterface
{
foreach ($this->map as $elements) {
if (null === $elements[0] || $elements[0]->matches($request)) {
return array($elements[1], $elements[2]);
return array($elements[1], $elements[2], $elements[3]);
}
}
return array(array(), null);
return array(array(), null, null);
}
}