Skip to content

[Security] Deprecate LogoutListener being returned as 3rd element by FirewallMapInterface::getListeners #43674

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/Symfony/Bundle/SecurityBundle/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ CHANGELOG
* Display the roles of the logged-in user in the Web Debug Toolbar
* Add the `security.access_decision_manager.strategy_service` option
* Deprecate not configuring explicitly a provider for custom_authenticators when there is more than one registered provider
* Deprecate `FirewallContext::getListeners`, use `FirewallContext::getFirewallListeners` and `FirewallContext::getExceptionListener` instead

5.3
---
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,7 @@ private function createFirewalls(array $config, ContainerBuilder $container)

$configId = 'security.firewall.map.config.'.$name;

[$matcher, $listeners, $exceptionListener, $logoutListener] = $this->createFirewall($container, $name, $firewall, $authenticationProviders, $providerIds, $configId);
[$matcher, $listeners, $exceptionListener] = $this->createFirewall($container, $name, $firewall, $authenticationProviders, $providerIds, $configId);

$contextId = 'security.firewall.map.context.'.$name;
$isLazy = !$firewall['stateless'] && (!empty($firewall['anonymous']['lazy']) || $firewall['lazy']);
Expand All @@ -329,8 +329,7 @@ private function createFirewalls(array $config, ContainerBuilder $container)
$context
->replaceArgument(0, new IteratorArgument($listeners))
->replaceArgument(1, $exceptionListener)
->replaceArgument(2, $logoutListener)
->replaceArgument(3, new Reference($configId))
->replaceArgument(2, new Reference($configId))
;

$contextRefs[$contextId] = new Reference($contextId);
Expand Down Expand Up @@ -450,6 +449,7 @@ private function createFirewall(ContainerBuilder $container, string $id, array $
'csrf_token_id' => $firewall['logout']['csrf_token_id'],
'logout_path' => $firewall['logout']['path'],
]);
$listeners[] = new Reference($logoutListenerId);

// add default logout listener
if (isset($firewall['logout']['success_handler'])) {
Expand Down Expand Up @@ -599,7 +599,7 @@ private function createFirewall(ContainerBuilder $container, string $id, array $
$config->replaceArgument(10, $listenerKeys);
$config->replaceArgument(11, $firewall['switch_user'] ?? null);

return [$matcher, $listeners, $exceptionListener, null !== $logoutListenerId ? new Reference($logoutListenerId) : null];
return [$matcher, $listeners, $exceptionListener];
}

private function createContextListener(ContainerBuilder $container, string $contextKey, ?string $firewallEventDispatcherId)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,6 @@
->args([
[],
service('security.exception_listener'),
abstract_arg('LogoutListener'),
abstract_arg('FirewallConfig'),
])

Expand All @@ -205,7 +204,6 @@
->args([
[],
service('security.exception_listener'),
abstract_arg('LogoutListener'),
abstract_arg('FirewallConfig'),
service('security.untracked_token_storage'),
])
Expand Down
63 changes: 57 additions & 6 deletions src/Symfony/Bundle/SecurityBundle/Security/FirewallContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,22 @@ class FirewallContext
{
private $listeners;
private $exceptionListener;
private $logoutListener;
private $config;
private $logoutListener = null;

/**
* @param iterable<mixed, callable> $listeners
*/
public function __construct(iterable $listeners, ExceptionListener $exceptionListener = null, LogoutListener $logoutListener = null, FirewallConfig $config = null)
public function __construct(iterable $listeners, ExceptionListener $exceptionListener = null, /*FirewallConfig*/ $config = null)
{
if ($config instanceof LogoutListener) {
trigger_deprecation('symfony/security-bundle', '5.4', 'Passing the LogoutListener as third argument is deprecated, add it to $listeners instead.', __METHOD__);
$this->logoutListener = $config;
$config = \func_num_args() > 3 ? func_get_arg(3) : null;
}

$this->listeners = $listeners;
$this->exceptionListener = $exceptionListener;
$this->logoutListener = $logoutListener;
$this->config = $config;
}

Expand All @@ -45,19 +50,65 @@ public function getConfig()

/**
* @return iterable<mixed, callable>
*
* @deprecated since Symfony 5.4, use "getFirewallListeners()" instead
*/
public function getListeners(): iterable
{
return $this->listeners;
if (0 === \func_num_args() || func_get_arg(0)) {
trigger_deprecation('symfony/security-bundle', '5.4', 'The %s() method is deprecated, use getFirewallListeners() instead.', __METHOD__);
}

// Ensure LogoutListener is not included
foreach ($this->listeners as $listener) {
if (!($listener instanceof LogoutListener)) {
yield $listener;
}
}
}

/**
* @return iterable<mixed, callable>
*/
public function getFirewallListeners(): iterable
{
$containedLogoutListener = false;
foreach ($this->listeners as $listener) {
yield $listener;
$containedLogoutListener |= $listener instanceof LogoutListener;
}

// Ensure the LogoutListener is contained
if (null !== $this->logoutListener && !$containedLogoutListener) {
yield $this->logoutListener;
}
}

public function getExceptionListener()
public function getExceptionListener(): ?ExceptionListener
{
return $this->exceptionListener;
}

/**
* @deprecated since Symfony 5.4, use "getFirewallListeners()" instead
*/
public function getLogoutListener()
{
return $this->logoutListener;
if (0 === \func_num_args() || func_get_arg(0)) {
trigger_deprecation('symfony/security-bundle', '5.4', 'The %s() method is deprecated, use getFirewallListeners() instead.', __METHOD__);
}

if (null !== $this->logoutListener) {
return $this->logoutListener;
}

// Return LogoutListener from listeners list
foreach ($this->listeners as $listener) {
if ($listener instanceof LogoutListener) {
return $listener;
}
}

return null;
}
}
32 changes: 31 additions & 1 deletion src/Symfony/Bundle/SecurityBundle/Security/FirewallMap.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

use Psr\Container\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Http\Firewall\ExceptionListener;
use Symfony\Component\Security\Http\FirewallMapInterface;

/**
Expand All @@ -33,15 +34,44 @@ public function __construct(ContainerInterface $container, iterable $map)
$this->map = $map;
}

/**
* {@inheritdoc}
*/
public function getListeners(Request $request)
{
if (2 > \func_num_args() || func_get_arg(1)) {
trigger_deprecation('symfony/security-bundle', '5.4', 'The %s() method is deprecated, use getFirewallListeners() or "getExceptionListener()" instead.', __METHOD__);
}

$context = $this->getFirewallContext($request);

if (null === $context) {
return [[], null, null];
}

return [$context->getListeners(), $context->getExceptionListener(), $context->getLogoutListener()];
return [$context->getListeners(false), $context->getExceptionListener(), $context->getLogoutListener(false)];
}

public function getFirewallListeners(Request $request): iterable
{
$context = $this->getFirewallContext($request);

if (null === $context) {
return [];
}

return $context->getFirewallListeners();
}

public function getExceptionListener(Request $request): ?ExceptionListener
{
$context = $this->getFirewallContext($request);

if (null === $context) {
return null;
}

return $context->getExceptionListener();
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
use Symfony\Component\Security\Http\Event\LazyResponseEvent;
use Symfony\Component\Security\Http\Firewall\ExceptionListener;
use Symfony\Component\Security\Http\Firewall\FirewallListenerInterface;
use Symfony\Component\Security\Http\Firewall\LogoutListener;

/**
* Lazily calls authentication listeners when actually required by the access listener.
Expand All @@ -27,13 +26,33 @@ class LazyFirewallContext extends FirewallContext
{
private $tokenStorage;

public function __construct(iterable $listeners, ?ExceptionListener $exceptionListener, ?LogoutListener $logoutListener, ?FirewallConfig $config, TokenStorage $tokenStorage)
/**
* @param iterable<mixed, callable> $listeners
*/
public function __construct(iterable $listeners, ?ExceptionListener $exceptionListener, /*?FirewallConfig*/ $config, /*TokenStorage*/ $tokenStorage)
{
parent::__construct($listeners, $exceptionListener, $logoutListener, $config);
$arguments = \func_get_args();

if (\count($arguments) > 4 && $arguments[4] instanceof TokenStorage) {
trigger_deprecation('symfony/security-bundle', '5.4', 'Passing the LogoutListener as third argument is deprecated, add it to $listeners instead.', __METHOD__);

$logoutListener = $arguments[2];
$config = $arguments[3];
$this->tokenStorage = $arguments[4];

parent::__construct($listeners, $exceptionListener, $logoutListener, $config);

return;
}

parent::__construct($listeners, $exceptionListener, $config);

$this->tokenStorage = $tokenStorage;
}

/**
* {@inheritdoc}
*/
public function getListeners(): iterable
{
return [$this];
Expand All @@ -45,7 +64,7 @@ public function __invoke(RequestEvent $event)
$request = $event->getRequest();
$lazy = $request->isMethodCacheable();

foreach (parent::getListeners() as $listener) {
foreach (parent::getListeners(false) as $listener) {
if (!$lazy || !$listener instanceof FirewallListenerInterface) {
$listeners[] = $listener;
$lazy = $lazy && $listener instanceof FirewallListenerInterface;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ public function testFirewalls()
$arguments = $contextDef->getArguments();
$listeners[] = array_map('strval', $arguments[0]->getValues());

$configDef = $container->getDefinition((string) $arguments[3]);
$configDef = $container->getDefinition((string) $arguments[2]);
$configs[] = array_values($configDef->getArguments());
}

Expand Down Expand Up @@ -208,6 +208,7 @@ public function testFirewalls()
'security.channel_listener',
'security.firewall.authenticator.secure',
'security.authentication.switchuser_listener.secure',
'security.logout_listener.secure',
'security.access_listener',
],
[
Expand Down Expand Up @@ -241,7 +242,7 @@ public function testLegacyFirewalls()
$arguments = $contextDef->getArguments();
$listeners[] = array_map('strval', $arguments[0]->getValues());

$configDef = $container->getDefinition((string) $arguments[3]);
$configDef = $container->getDefinition((string) $arguments[2]);
$configs[] = array_values($configDef->getArguments());
}

Expand Down Expand Up @@ -337,6 +338,7 @@ public function testLegacyFirewalls()
'security.authentication.listener.rememberme.secure',
'security.authentication.listener.anonymous.secure',
'security.authentication.switchuser_listener.secure',
'security.logout_listener.secure',
'security.access_listener',
],
[
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,15 @@ public function testGetters()
$config = new FirewallConfig('main', 'user_checker', 'request_matcher');
$exceptionListener = $this->getExceptionListenerMock();
$logoutListener = $this->getLogoutListenerMock();
$listeners = [function () {}];
$listeners = [function () {}, $logoutListener];
$listenersMinusLogoutListener = [function () {}];

$context = new FirewallContext($listeners, $exceptionListener, $logoutListener, $config);
$context = new FirewallContext($listeners, $exceptionListener, $config);

$this->assertEquals($listeners, $context->getListeners());
$this->assertEquals($listenersMinusLogoutListener, iterator_to_array($context->getListeners(false)));
$this->assertEquals($exceptionListener, $context->getExceptionListener());
$this->assertEquals($logoutListener, $context->getLogoutListener());
$this->assertEquals($logoutListener, $context->getLogoutListener(false));
$this->assertEquals($listeners, iterator_to_array($context->getFirewallListeners()));
$this->assertEquals($config, $context->getConfig());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public function testGetListenersWithEmptyMap()

$firewallMap = new FirewallMap($container, $map);

$this->assertEquals([[], null, null], $firewallMap->getListeners($request));
$this->assertEquals([], $firewallMap->getFirewallListeners($request));
$this->assertNull($firewallMap->getFirewallConfig($request));
$this->assertFalse($request->attributes->has(self::ATTRIBUTE_FIREWALL_CONTEXT));
}
Expand All @@ -51,11 +51,14 @@ public function testGetListenersWithInvalidParameter()

$firewallMap = new FirewallMap($container, $map);

$this->assertEquals([[], null, null], $firewallMap->getListeners($request));
$this->assertEquals([], $firewallMap->getFirewallListeners($request));
$this->assertNull($firewallMap->getFirewallConfig($request));
$this->assertFalse($request->attributes->has(self::ATTRIBUTE_FIREWALL_CONTEXT));
}

/**
* @group legacy
*/
public function testGetListeners()
{
$request = new Request();
Expand All @@ -67,9 +70,10 @@ public function testGetListeners()

$listener = function () {};
$firewallContext->expects($this->once())->method('getListeners')->willReturn([$listener]);
$firewallContext->expects($this->once())->method('getFirewallListeners')->willReturn([$listener]);

$exceptionListener = $this->createMock(ExceptionListener::class);
$firewallContext->expects($this->once())->method('getExceptionListener')->willReturn($exceptionListener);
$firewallContext->expects($this->exactly(2))->method('getExceptionListener')->willReturn($exceptionListener);

$logoutListener = $this->createMock(LogoutListener::class);
$firewallContext->expects($this->once())->method('getLogoutListener')->willReturn($logoutListener);
Expand All @@ -81,11 +85,13 @@ public function testGetListeners()
->willReturn(true);

$container = $this->createMock(Container::class);
$container->expects($this->exactly(2))->method('get')->willReturn($firewallContext);
$container->expects($this->exactly(4))->method('get')->willReturn($firewallContext);

$firewallMap = new FirewallMap($container, ['security.firewall.map.context.foo' => $matcher]);

$this->assertEquals([[$listener], $exceptionListener, $logoutListener], $firewallMap->getListeners($request));
$this->assertEquals([[$listener], $exceptionListener, $logoutListener], $firewallMap->getListeners($request, false));
$this->assertEquals([$listener], $firewallMap->getFirewallListeners($request));
$this->assertEquals($exceptionListener, $firewallMap->getExceptionListener($request));
$this->assertEquals($firewallConfig, $firewallMap->getFirewallConfig($request));
$this->assertEquals('security.firewall.map.context.foo', $request->attributes->get(self::ATTRIBUTE_FIREWALL_CONTEXT));
}
Expand Down
1 change: 1 addition & 0 deletions src/Symfony/Component/Security/Http/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ CHANGELOG
* Deprecate `CookieClearingLogoutHandler`, `SessionLogoutHandler` and `CsrfTokenClearingLogoutHandler`.
Use `CookieClearingLogoutListener`, `SessionLogoutListener` and `CsrfTokenClearingLogoutListener` instead
* Deprecate `PassportInterface`, `UserPassportInterface` and `PassportTrait`, use `Passport` instead
* Deprecate `FirewallMapInterface::getListeners`, use `FirewallMapInterface::getFirewallListeners` and `FirewallMapInterface::getExceptionListener` instead

5.3
---
Expand Down
2 changes: 1 addition & 1 deletion src/Symfony/Component/Security/Http/Firewall.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public function onKernelRequest(RequestEvent $event)
}

// register listeners for this firewall
$listeners = $this->map->getListeners($event->getRequest());
$listeners = $this->map->getListeners($event->getRequest(), false);

$authenticationListeners = $listeners[0];
$exceptionListener = $listeners[1];
Expand Down
Loading