From 510e5061124a423822138735b45d1267de4a3753 Mon Sep 17 00:00:00 2001 From: matlec Date: Mon, 2 Jun 2025 09:09:50 +0200 Subject: [PATCH] [Security] Deprecate callable firewall listeners --- UPGRADE-7.4.md | 7 +++ .../Bundle/SecurityBundle/CHANGELOG.md | 1 + .../Debug/TraceableFirewallListener.php | 7 ++- .../Security/FirewallContext.php | 3 +- .../Security/LazyFirewallContext.php | 34 +++++++++-- .../SecurityDataCollectorTest.php | 20 +++++-- .../Debug/TraceableFirewallListenerTest.php | 18 +++++- .../Bundle/SecurityBundle/composer.json | 1 + .../Component/Security/Http/CHANGELOG.md | 6 ++ .../Component/Security/Http/Firewall.php | 7 ++- .../Http/Firewall/AbstractListener.php | 5 ++ .../Tests/Firewall/AccessListenerTest.php | 25 +++++--- .../Tests/Firewall/ChannelListenerTest.php | 20 +++---- .../Tests/Firewall/ContextListenerTest.php | 17 +++--- .../Tests/Firewall/LogoutListenerTest.php | 30 ++++------ .../Tests/Firewall/SwitchUserListenerTest.php | 47 +++++++++------ .../Security/Http/Tests/FirewallTest.php | 59 +++++++++++++++---- 17 files changed, 215 insertions(+), 92 deletions(-) diff --git a/UPGRADE-7.4.md b/UPGRADE-7.4.md index 859fd57d0afa6..d3f628bc16b54 100644 --- a/UPGRADE-7.4.md +++ b/UPGRADE-7.4.md @@ -27,3 +27,10 @@ HttpFoundation -------------- * Deprecate using `Request::sendHeaders()` after headers have already been sent; use a `StreamedResponse` instead + +Security +-------- + + * Deprecate callable firewall listeners, extend `AbstractListener` or implement `FirewallListenerInterface` instead + * Deprecate `AbstractListener::__invoke` + * Deprecate `LazyFirewallContext::__invoke()` diff --git a/src/Symfony/Bundle/SecurityBundle/CHANGELOG.md b/src/Symfony/Bundle/SecurityBundle/CHANGELOG.md index 1d69d1888c6f7..73754eddb83a5 100644 --- a/src/Symfony/Bundle/SecurityBundle/CHANGELOG.md +++ b/src/Symfony/Bundle/SecurityBundle/CHANGELOG.md @@ -22,6 +22,7 @@ CHANGELOG ) { } ``` + * Deprecate `LazyFirewallContext::__invoke()` 7.3 --- diff --git a/src/Symfony/Bundle/SecurityBundle/Debug/TraceableFirewallListener.php b/src/Symfony/Bundle/SecurityBundle/Debug/TraceableFirewallListener.php index 45f4f498344b1..f3a8ca22b46ff 100644 --- a/src/Symfony/Bundle/SecurityBundle/Debug/TraceableFirewallListener.php +++ b/src/Symfony/Bundle/SecurityBundle/Debug/TraceableFirewallListener.php @@ -16,6 +16,7 @@ use Symfony\Bundle\SecurityBundle\Security\LazyFirewallContext; use Symfony\Component\HttpKernel\Event\RequestEvent; use Symfony\Component\Security\Http\Authenticator\Debug\TraceableAuthenticatorManagerListener; +use Symfony\Component\Security\Http\Firewall\AbstractListener; use Symfony\Component\Security\Http\Firewall\FirewallListenerInterface; use Symfony\Contracts\Service\ResetInterface; @@ -88,7 +89,11 @@ protected function callListeners(RequestEvent $event, iterable $listeners): void } foreach ($requestListeners as $listener) { - $listener($event); + if (!$listener instanceof FirewallListenerInterface) { + $listener($event); + } elseif (false !== $listener->supports($event->getRequest())) { + $listener->authenticate($event); + } if ($event->hasResponse()) { break; diff --git a/src/Symfony/Bundle/SecurityBundle/Security/FirewallContext.php b/src/Symfony/Bundle/SecurityBundle/Security/FirewallContext.php index 63648bd67510e..1da8913906f01 100644 --- a/src/Symfony/Bundle/SecurityBundle/Security/FirewallContext.php +++ b/src/Symfony/Bundle/SecurityBundle/Security/FirewallContext.php @@ -12,6 +12,7 @@ namespace Symfony\Bundle\SecurityBundle\Security; use Symfony\Component\Security\Http\Firewall\ExceptionListener; +use Symfony\Component\Security\Http\Firewall\FirewallListenerInterface; use Symfony\Component\Security\Http\Firewall\LogoutListener; /** @@ -39,7 +40,7 @@ public function getConfig(): ?FirewallConfig } /** - * @return iterable + * @return iterable */ public function getListeners(): iterable { diff --git a/src/Symfony/Bundle/SecurityBundle/Security/LazyFirewallContext.php b/src/Symfony/Bundle/SecurityBundle/Security/LazyFirewallContext.php index 6835762315415..09526fde6c5cd 100644 --- a/src/Symfony/Bundle/SecurityBundle/Security/LazyFirewallContext.php +++ b/src/Symfony/Bundle/SecurityBundle/Security/LazyFirewallContext.php @@ -11,9 +11,11 @@ namespace Symfony\Bundle\SecurityBundle\Security; +use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Event\RequestEvent; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage; use Symfony\Component\Security\Http\Event\LazyResponseEvent; +use Symfony\Component\Security\Http\Firewall\AbstractListener; use Symfony\Component\Security\Http\Firewall\ExceptionListener; use Symfony\Component\Security\Http\Firewall\FirewallListenerInterface; use Symfony\Component\Security\Http\Firewall\LogoutListener; @@ -23,7 +25,7 @@ * * @author Nicolas Grekas */ -class LazyFirewallContext extends FirewallContext +class LazyFirewallContext extends FirewallContext implements FirewallListenerInterface { public function __construct( iterable $listeners, @@ -40,19 +42,26 @@ public function getListeners(): iterable return [$this]; } - public function __invoke(RequestEvent $event): void + public function supports(Request $request): ?bool + { + return true; + } + + public function authenticate(RequestEvent $event): void { $listeners = []; $request = $event->getRequest(); $lazy = $request->isMethodCacheable(); foreach (parent::getListeners() as $listener) { - if (!$lazy || !$listener instanceof FirewallListenerInterface) { + if (!$listener instanceof FirewallListenerInterface) { + trigger_deprecation('symfony/security-http', '7.4', 'Using a callable as firewall listener is deprecated, extend "%s" or implement "%s" instead.', AbstractListener::class, FirewallListenerInterface::class); + $listeners[] = $listener; - $lazy = $lazy && $listener instanceof FirewallListenerInterface; + $lazy = false; } elseif (false !== $supports = $listener->supports($request)) { $listeners[] = [$listener, 'authenticate']; - $lazy = null === $supports; + $lazy = $lazy && null === $supports; } } @@ -75,4 +84,19 @@ public function __invoke(RequestEvent $event): void } }); } + + public static function getPriority(): int + { + return 0; + } + + /** + * @deprecated since Symfony 7.4, to be removed in 8.0 + */ + public function __invoke(RequestEvent $event): void + { + trigger_deprecation('symfony/security-bundle', '7.4', 'The "%s()" method is deprecated since Symfony 7.4 and will be removed in 8.0.', __METHOD__); + + $this->authenticate($event); + } } diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DataCollector/SecurityDataCollectorTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DataCollector/SecurityDataCollectorTest.php index 5528c9b7a8fc7..053bf25f5485c 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DataCollector/SecurityDataCollectorTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DataCollector/SecurityDataCollectorTest.php @@ -32,6 +32,8 @@ use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface; use Symfony\Component\Security\Core\Role\RoleHierarchy; use Symfony\Component\Security\Core\User\InMemoryUser; +use Symfony\Component\Security\Http\Firewall\AbstractListener; +use Symfony\Component\Security\Http\Firewall\FirewallListenerInterface; use Symfony\Component\Security\Http\FirewallMapInterface; use Symfony\Component\Security\Http\Logout\LogoutUrlGenerator; use Symfony\Component\VarDumper\Caster\ClassStub; @@ -193,8 +195,18 @@ public function testGetListeners() $request = new Request(); $event = new RequestEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MAIN_REQUEST); $event->setResponse($response = new Response()); - $listener = function ($e) use ($event, &$listenerCalled) { - $listenerCalled += $e === $event; + $listener = new class extends AbstractListener { + public int $callCount = 0; + + public function supports(Request $request): ?bool + { + return true; + } + + public function authenticate(RequestEvent $event): void + { + ++$this->callCount; + } }; $firewallMap = $this ->getMockBuilder(FirewallMap::class) @@ -217,9 +229,9 @@ public function testGetListeners() $collector = new SecurityDataCollector(null, null, null, null, $firewallMap, $firewall, true); $collector->collect($request, $response); - $this->assertNotEmpty($collected = $collector->getListeners()[0]); + $this->assertCount(1, $collector->getListeners()); $collector->lateCollect(); - $this->assertSame(1, $listenerCalled); + $this->assertSame(1, $listener->callCount); } public function testCollectCollectsDecisionLogWhenStrategyIsAffirmative() diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Debug/TraceableFirewallListenerTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Debug/TraceableFirewallListenerTest.php index 4ab483a28f38a..db6e8a0e548c8 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Debug/TraceableFirewallListenerTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Debug/TraceableFirewallListenerTest.php @@ -29,7 +29,9 @@ use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge; use Symfony\Component\Security\Http\Authenticator\Passport\Passport; use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport; +use Symfony\Component\Security\Http\Firewall\AbstractListener; use Symfony\Component\Security\Http\Firewall\AuthenticatorManagerListener; +use Symfony\Component\Security\Http\Firewall\FirewallListenerInterface; use Symfony\Component\Security\Http\Logout\LogoutUrlGenerator; /** @@ -41,9 +43,19 @@ public function testOnKernelRequestRecordsListeners() { $request = new Request(); $event = new RequestEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MAIN_REQUEST); - $event->setResponse($response = new Response()); - $listener = function ($e) use ($event, &$listenerCalled) { - $listenerCalled += $e === $event; + $event->setResponse(new Response()); + $listener = new class extends AbstractListener { + public int $callCount = 0; + + public function supports(Request $request): ?bool + { + return true; + } + + public function authenticate(RequestEvent $event): void + { + ++$this->callCount; + } }; $firewallMap = $this->createMock(FirewallMap::class); $firewallMap diff --git a/src/Symfony/Bundle/SecurityBundle/composer.json b/src/Symfony/Bundle/SecurityBundle/composer.json index 66bc512f1d1ff..cbad87a62861c 100644 --- a/src/Symfony/Bundle/SecurityBundle/composer.json +++ b/src/Symfony/Bundle/SecurityBundle/composer.json @@ -22,6 +22,7 @@ "symfony/clock": "^6.4|^7.0|^8.0", "symfony/config": "^7.3|^8.0", "symfony/dependency-injection": "^6.4.11|^7.1.4|^8.0", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/event-dispatcher": "^6.4|^7.0|^8.0", "symfony/http-kernel": "^6.4|^7.0|^8.0", "symfony/http-foundation": "^6.4|^7.0|^8.0", diff --git a/src/Symfony/Component/Security/Http/CHANGELOG.md b/src/Symfony/Component/Security/Http/CHANGELOG.md index 275180ff87b3b..6c485dc6e5450 100644 --- a/src/Symfony/Component/Security/Http/CHANGELOG.md +++ b/src/Symfony/Component/Security/Http/CHANGELOG.md @@ -1,6 +1,12 @@ CHANGELOG ========= +7.4 +--- + + * Deprecate callable firewall listeners, extend `AbstractListener` or implement `FirewallListenerInterface` instead + * Deprecate `AbstractListener::__invoke` + 7.3 --- diff --git a/src/Symfony/Component/Security/Http/Firewall.php b/src/Symfony/Component/Security/Http/Firewall.php index da616e86ccc99..5aa2948d22bc5 100644 --- a/src/Symfony/Component/Security/Http/Firewall.php +++ b/src/Symfony/Component/Security/Http/Firewall.php @@ -16,6 +16,7 @@ use Symfony\Component\HttpKernel\Event\FinishRequestEvent; use Symfony\Component\HttpKernel\Event\RequestEvent; use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\Security\Http\Firewall\AbstractListener; use Symfony\Component\Security\Http\Firewall\ExceptionListener; use Symfony\Component\Security\Http\Firewall\FirewallListenerInterface; use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; @@ -123,6 +124,8 @@ protected function callListeners(RequestEvent $event, iterable $listeners) { foreach ($listeners as $listener) { if (!$listener instanceof FirewallListenerInterface) { + trigger_deprecation('symfony/security-http', '7.4', 'Using a callable as firewall listener is deprecated, extend "%s" or implement "%s" instead.', AbstractListener::class, FirewallListenerInterface::class); + $listener($event); } elseif (false !== $listener->supports($event->getRequest())) { $listener->authenticate($event); @@ -134,8 +137,8 @@ protected function callListeners(RequestEvent $event, iterable $listeners) } } - private function getListenerPriority(object $logoutListener): int + private function getListenerPriority(object $listener): int { - return $logoutListener instanceof FirewallListenerInterface ? $logoutListener->getPriority() : 0; + return $listener instanceof FirewallListenerInterface ? $listener->getPriority() : 0; } } diff --git a/src/Symfony/Component/Security/Http/Firewall/AbstractListener.php b/src/Symfony/Component/Security/Http/Firewall/AbstractListener.php index b5349e5e552cc..b30614defd215 100644 --- a/src/Symfony/Component/Security/Http/Firewall/AbstractListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/AbstractListener.php @@ -20,8 +20,13 @@ */ abstract class AbstractListener implements FirewallListenerInterface { + /** + * @deprecated since Symfony 7.4, to be removed in 8.0 + */ final public function __invoke(RequestEvent $event): void { + trigger_deprecation('symfony/security-http', '7.4', 'The "%s()" method is deprecated since Symfony 7.4 and will be removed in 8.0.', __METHOD__); + if (false !== $this->supports($event->getRequest())) { $this->authenticate($event); } diff --git a/src/Symfony/Component/Security/Http/Tests/Firewall/AccessListenerTest.php b/src/Symfony/Component/Security/Http/Tests/Firewall/AccessListenerTest.php index 83df93d36169f..82ecbcb88b1a2 100644 --- a/src/Symfony/Component/Security/Http/Tests/Firewall/AccessListenerTest.php +++ b/src/Symfony/Component/Security/Http/Tests/Firewall/AccessListenerTest.php @@ -68,7 +68,8 @@ public function testHandleWhenTheAccessDecisionManagerDecidesToRefuseAccess() $this->expectException(AccessDeniedException::class); - $listener(new RequestEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MAIN_REQUEST)); + $this->assertTrue($listener->supports($request)); + $listener->authenticate(new RequestEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MAIN_REQUEST)); } public function testHandleWhenThereIsNoAccessMapEntryMatchingTheRequest() @@ -95,7 +96,8 @@ public function testHandleWhenThereIsNoAccessMapEntryMatchingTheRequest() $accessMap ); - $listener(new RequestEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MAIN_REQUEST)); + $this->assertNull($listener->supports($request)); + $listener->authenticate(new RequestEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MAIN_REQUEST)); } public function testHandleWhenAccessMapReturnsEmptyAttributes() @@ -124,7 +126,8 @@ public function testHandleWhenAccessMapReturnsEmptyAttributes() $event = new RequestEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MAIN_REQUEST); - $listener(new LazyResponseEvent($event)); + $this->assertNull($listener->supports($request)); + $listener->authenticate(new LazyResponseEvent($event)); } public function testHandleWhenTheSecurityTokenStorageHasNoToken() @@ -154,7 +157,8 @@ public function testHandleWhenTheSecurityTokenStorageHasNoToken() $this->expectException(AccessDeniedException::class); - $listener(new RequestEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MAIN_REQUEST)); + $this->assertTrue($listener->supports($request)); + $listener->authenticate(new RequestEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MAIN_REQUEST)); } public function testHandleWhenPublicAccessIsAllowed() @@ -182,7 +186,8 @@ public function testHandleWhenPublicAccessIsAllowed() false ); - $listener(new RequestEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MAIN_REQUEST)); + $this->assertNull($listener->supports($request)); + $listener->authenticate(new RequestEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MAIN_REQUEST)); } public function testHandleWhenPublicAccessWhileAuthenticated() @@ -212,7 +217,8 @@ public function testHandleWhenPublicAccessWhileAuthenticated() false ); - $listener(new RequestEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MAIN_REQUEST)); + $this->assertNull($listener->supports($request)); + $listener->authenticate(new RequestEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MAIN_REQUEST)); } public function testHandleMWithultipleAttributesShouldBeHandledAsAnd() @@ -246,7 +252,8 @@ public function testHandleMWithultipleAttributesShouldBeHandledAsAnd() $accessMap ); - $listener(new RequestEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MAIN_REQUEST)); + $this->assertTrue($listener->supports($request)); + $listener->authenticate(new RequestEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MAIN_REQUEST)); } public function testLazyPublicPagesShouldNotAccessTokenStorage() @@ -263,7 +270,9 @@ public function testLazyPublicPagesShouldNotAccessTokenStorage() ; $listener = new AccessListener($tokenStorage, $this->createMock(AccessDecisionManagerInterface::class), $accessMap, false); - $listener(new LazyResponseEvent(new RequestEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MAIN_REQUEST))); + + $this->assertNull($listener->supports($request)); + $listener->authenticate(new LazyResponseEvent(new RequestEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MAIN_REQUEST))); } public function testConstructWithTrueExceptionOnNoToken() diff --git a/src/Symfony/Component/Security/Http/Tests/Firewall/ChannelListenerTest.php b/src/Symfony/Component/Security/Http/Tests/Firewall/ChannelListenerTest.php index 06c4c6d0e3422..5a4be3feb1eae 100644 --- a/src/Symfony/Component/Security/Http/Tests/Firewall/ChannelListenerTest.php +++ b/src/Symfony/Component/Security/Http/Tests/Firewall/ChannelListenerTest.php @@ -39,12 +39,8 @@ public function testHandleWithNotSecuredRequestAndHttpChannel() ->willReturn([[], 'http']) ; - $event = new RequestEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MAIN_REQUEST); - $listener = new ChannelListener($accessMap); - $listener($event); - - $this->assertNull($event->getResponse()); + $this->assertFalse($listener->supports($request)); } public function testHandleWithSecuredRequestAndHttpsChannel() @@ -64,12 +60,8 @@ public function testHandleWithSecuredRequestAndHttpsChannel() ->willReturn([[], 'https']) ; - $event = new RequestEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MAIN_REQUEST); - $listener = new ChannelListener($accessMap); - $listener($event); - - $this->assertNull($event->getResponse()); + $this->assertFalse($listener->supports($request)); } public function testHandleWithNotSecuredRequestAndHttpsChannel() @@ -92,7 +84,9 @@ public function testHandleWithNotSecuredRequestAndHttpsChannel() $event = new RequestEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MAIN_REQUEST); $listener = new ChannelListener($accessMap); - $listener($event); + $this->assertTrue($listener->supports($request)); + + $listener->authenticate($event); $response = $event->getResponse(); $this->assertInstanceOf(RedirectResponse::class, $response); @@ -119,7 +113,9 @@ public function testHandleWithSecuredRequestAndHttpChannel() $event = new RequestEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MAIN_REQUEST); $listener = new ChannelListener($accessMap); - $listener($event); + $this->assertTrue($listener->supports($request)); + + $listener->authenticate($event); $response = $event->getResponse(); $this->assertInstanceOf(RedirectResponse::class, $response); diff --git a/src/Symfony/Component/Security/Http/Tests/Firewall/ContextListenerTest.php b/src/Symfony/Component/Security/Http/Tests/Firewall/ContextListenerTest.php index 585fca8af10ff..03d45722822b5 100644 --- a/src/Symfony/Component/Security/Http/Tests/Firewall/ContextListenerTest.php +++ b/src/Symfony/Component/Security/Http/Tests/Firewall/ContextListenerTest.php @@ -179,7 +179,7 @@ public function testInvalidTokenInSession($token) ->with(null); $listener = new ContextListener($tokenStorage, [], 'key123'); - $listener(new RequestEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MAIN_REQUEST)); + $listener->authenticate(new RequestEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MAIN_REQUEST)); } public static function provideInvalidToken() @@ -203,7 +203,7 @@ public function testHandleAddsKernelResponseListener() ->method('addListener') ->with(KernelEvents::RESPONSE, $listener->onKernelResponse(...)); - $listener(new RequestEvent($this->createMock(HttpKernelInterface::class), new Request(), HttpKernelInterface::MAIN_REQUEST)); + $listener->authenticate(new RequestEvent($this->createMock(HttpKernelInterface::class), new Request(), HttpKernelInterface::MAIN_REQUEST)); } public function testOnKernelResponseListenerRemovesItself() @@ -236,7 +236,7 @@ public function testHandleRemovesTokenIfNoPreviousSessionWasFound() $tokenStorage->expects($this->once())->method('setToken')->with(null); $listener = new ContextListener($tokenStorage, [], 'key123'); - $listener(new RequestEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MAIN_REQUEST)); + $listener->authenticate(new RequestEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MAIN_REQUEST)); } public function testIfTokenIsDeauthenticated() @@ -262,7 +262,7 @@ public function testTokenIsNotDeauthenticatedOnUserChangeIfNotAnInstanceOfAbstra $request->cookies->set('MOCKSESSID', true); $listener = new ContextListener($tokenStorage, [new NotSupportingUserProvider(true), new NotSupportingUserProvider(false), new SupportingUserProvider($refreshedUser)], 'context_key'); - $listener(new RequestEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MAIN_REQUEST)); + $listener->authenticate(new RequestEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MAIN_REQUEST)); $this->assertInstanceOf(CustomToken::class, $tokenStorage->getToken()); $this->assertSame($refreshedUser, $tokenStorage->getToken()->getUser()); @@ -270,7 +270,6 @@ public function testTokenIsNotDeauthenticatedOnUserChangeIfNotAnInstanceOfAbstra public function testIfTokenIsNotDeauthenticated() { - $tokenStorage = new TokenStorage(); $badRefreshedUser = new InMemoryUser('foobar', 'baz'); $goodRefreshedUser = new InMemoryUser('foobar', 'bar'); $tokenStorage = $this->handleEventWithPreviousSession([new SupportingUserProvider($badRefreshedUser), new SupportingUserProvider($goodRefreshedUser)], $goodRefreshedUser); @@ -326,7 +325,7 @@ public function testWithPreviousNotStartedSession() $tokenStorage = new TokenStorage(); $listener = new ContextListener($tokenStorage, [], 'context_key', null, null, null, $tokenStorage->getToken(...)); - $listener(new RequestEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MAIN_REQUEST)); + $listener->authenticate(new RequestEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MAIN_REQUEST)); $this->assertSame($usageIndex, $session->getUsageIndex()); } @@ -348,7 +347,7 @@ public function testSessionIsNotReported() $tokenStorage = new TokenStorage(); $listener = new ContextListener($tokenStorage, [], 'context_key', null, null, null, $tokenStorage->getToken(...)); - $listener(new RequestEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MAIN_REQUEST)); + $listener->authenticate(new RequestEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MAIN_REQUEST)); $listener->onKernelResponse(new ResponseEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MAIN_REQUEST, new Response())); } @@ -370,7 +369,7 @@ public function testOnKernelResponseRemoveListener() $listener = new ContextListener($tokenStorage, [], 'session', null, $dispatcher, null, $tokenStorage->getToken(...)); $this->assertSame([], $dispatcher->getListeners()); - $listener(new RequestEvent($httpKernel, $request, HttpKernelInterface::MAIN_REQUEST)); + $listener->authenticate(new RequestEvent($httpKernel, $request, HttpKernelInterface::MAIN_REQUEST)); $this->assertNotEmpty($dispatcher->getListeners()); $listener->onKernelResponse(new ResponseEvent($httpKernel, $request, HttpKernelInterface::MAIN_REQUEST, new Response())); @@ -468,7 +467,7 @@ private function handleEventWithPreviousSession($userProviders, ?UserInterface $ $listener = new ContextListener($tokenStorage, $userProviders, 'context_key', null, null, null, $sessionTrackerEnabler); - $listener(new RequestEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MAIN_REQUEST)); + $listener->authenticate(new RequestEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MAIN_REQUEST)); if (null !== $user) { ++$usageIndex; diff --git a/src/Symfony/Component/Security/Http/Tests/Firewall/LogoutListenerTest.php b/src/Symfony/Component/Security/Http/Tests/Firewall/LogoutListenerTest.php index c7cdc7abd216a..acdeccfb5e11f 100644 --- a/src/Symfony/Component/Security/Http/Tests/Firewall/LogoutListenerTest.php +++ b/src/Symfony/Component/Security/Http/Tests/Firewall/LogoutListenerTest.php @@ -29,13 +29,7 @@ class LogoutListenerTest extends TestCase { public function testHandleUnmatchedPath() { - $dispatcher = $this->getEventDispatcher(); - [$listener, , $httpUtils, $options] = $this->getListener($dispatcher); - - $logoutEventDispatched = false; - $dispatcher->addListener(LogoutEvent::class, function () use (&$logoutEventDispatched) { - $logoutEventDispatched = true; - }); + [$listener, , $httpUtils, $options] = $this->getListener(); $request = new Request(); @@ -44,9 +38,7 @@ public function testHandleUnmatchedPath() ->with($request, $options['logout_path']) ->willReturn(false); - $listener(new RequestEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MAIN_REQUEST)); - - $this->assertFalse($logoutEventDispatched, 'LogoutEvent should not have been dispatched.'); + $this->assertFalse($listener->supports($request)); } public function testHandleMatchedPathWithCsrfValidation() @@ -75,7 +67,7 @@ public function testHandleMatchedPathWithCsrfValidation() $tokenStorage->expects($this->once()) ->method('getToken') - ->willReturn($token = $this->getToken()); + ->willReturn($this->getToken()); $tokenStorage->expects($this->once()) ->method('setToken') @@ -83,7 +75,8 @@ public function testHandleMatchedPathWithCsrfValidation() $event = new RequestEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MAIN_REQUEST); - $listener($event); + $this->assertTrue($listener->supports($request)); + $listener->authenticate($event); $this->assertSame($response, $event->getResponse()); } @@ -107,7 +100,7 @@ public function testHandleMatchedPathWithoutCsrfValidation() $tokenStorage->expects($this->once()) ->method('getToken') - ->willReturn($token = $this->getToken()); + ->willReturn($this->getToken()); $tokenStorage->expects($this->once()) ->method('setToken') @@ -115,7 +108,8 @@ public function testHandleMatchedPathWithoutCsrfValidation() $event = new RequestEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MAIN_REQUEST); - $listener($event); + $this->assertTrue($listener->supports($request)); + $listener->authenticate($event); $this->assertSame($response, $event->getResponse()); } @@ -133,7 +127,8 @@ public function testNoResponseSet() $this->expectException(\RuntimeException::class); - $listener(new RequestEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MAIN_REQUEST)); + $this->assertTrue($listener->supports($request)); + $listener->authenticate(new RequestEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MAIN_REQUEST)); } /** @@ -161,7 +156,8 @@ public function testCsrfValidationFails($invalidToken) $this->expectException(LogoutException::class); - $listener(new RequestEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MAIN_REQUEST)); + $this->assertTrue($listener->supports($request)); + $listener->authenticate(new RequestEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MAIN_REQUEST)); } public static function provideInvalidCsrfTokens(): array @@ -188,7 +184,7 @@ private function getHttpUtils() return $this->createMock(HttpUtils::class); } - private function getListener($eventDispatcher = null, $tokenManager = null) + private function getListener($eventDispatcher = null, $tokenManager = null): array { $listener = new LogoutListener( $tokenStorage = $this->getTokenStorage(), diff --git a/src/Symfony/Component/Security/Http/Tests/Firewall/SwitchUserListenerTest.php b/src/Symfony/Component/Security/Http/Tests/Firewall/SwitchUserListenerTest.php index 114d0db979e46..0c012ab338db7 100644 --- a/src/Symfony/Component/Security/Http/Tests/Firewall/SwitchUserListenerTest.php +++ b/src/Symfony/Component/Security/Http/Tests/Firewall/SwitchUserListenerTest.php @@ -61,10 +61,7 @@ public function testFirewallNameIsRequired() public function testEventIsIgnoredIfUsernameIsNotPassedWithTheRequest() { $listener = new SwitchUserListener($this->tokenStorage, $this->userProvider, $this->userChecker, 'provider123', $this->accessDecisionManager); - $listener($this->event); - - $this->assertNull($this->event->getResponse()); - $this->assertNull($this->tokenStorage->getToken()); + $this->assertFalse($listener->supports($this->event->getRequest())); } public function testExitUserThrowsAuthenticationExceptionIfNoCurrentToken() @@ -75,7 +72,8 @@ public function testExitUserThrowsAuthenticationExceptionIfNoCurrentToken() $this->expectException(AuthenticationCredentialsNotFoundException::class); - $listener($this->event); + $this->assertTrue($listener->supports($this->event->getRequest())); + $listener->authenticate($this->event); } public function testExitUserThrowsAuthenticationExceptionIfOriginalTokenCannotBeFound() @@ -89,7 +87,8 @@ public function testExitUserThrowsAuthenticationExceptionIfOriginalTokenCannotBe $this->expectException(AuthenticationCredentialsNotFoundException::class); - $listener($this->event); + $this->assertTrue($listener->supports($this->event->getRequest())); + $listener->authenticate($this->event); } public function testExitUserUpdatesToken() @@ -100,7 +99,8 @@ public function testExitUserUpdatesToken() $this->request->query->set('_switch_user', SwitchUserListener::EXIT_VALUE); $listener = new SwitchUserListener($this->tokenStorage, $this->userProvider, $this->userChecker, 'provider123', $this->accessDecisionManager); - $listener($this->event); + $this->assertTrue($listener->supports($this->event->getRequest())); + $listener->authenticate($this->event); $this->assertSame([], $this->request->query->all()); $this->assertSame('', $this->request->server->get('QUERY_STRING')); @@ -134,7 +134,8 @@ public function testExitUserDispatchesEventWithRefreshedUser() ; $listener = new SwitchUserListener($this->tokenStorage, $userProvider, $this->userChecker, 'provider123', $this->accessDecisionManager, null, '_switch_user', 'ROLE_ALLOWED_TO_SWITCH', $dispatcher); - $listener($this->event); + $this->assertTrue($listener->supports($this->event->getRequest())); + $listener->authenticate($this->event); } public function testSwitchUserIsDisallowed() @@ -153,7 +154,8 @@ public function testSwitchUserIsDisallowed() $this->expectException(AccessDeniedException::class); - $listener($this->event); + $this->assertTrue($listener->supports($this->event->getRequest())); + $listener->authenticate($this->event); } public function testSwitchUserTurnsAuthenticationExceptionTo403() @@ -170,7 +172,8 @@ public function testSwitchUserTurnsAuthenticationExceptionTo403() $this->expectException(AccessDeniedException::class); - $listener($this->event); + $this->assertTrue($listener->supports($this->event->getRequest())); + $listener->authenticate($this->event); } public function testSwitchUser() @@ -188,7 +191,8 @@ public function testSwitchUser() ->method('checkPostAuth')->with($this->callback(fn ($user) => 'kuba' === $user->getUserIdentifier()), $token); $listener = new SwitchUserListener($this->tokenStorage, $this->userProvider, $this->userChecker, 'provider123', $this->accessDecisionManager); - $listener($this->event); + $this->assertTrue($listener->supports($this->event->getRequest())); + $listener->authenticate($this->event); $this->assertSame([], $this->request->query->all()); $this->assertSame('', $this->request->server->get('QUERY_STRING')); @@ -217,7 +221,8 @@ public function testSwitchUserAlreadySwitched() ->method('checkPostAuth')->with($targetsUser); $listener = new SwitchUserListener($tokenStorage, $this->userProvider, $this->userChecker, 'provider123', $this->accessDecisionManager, null, '_switch_user', 'ROLE_ALLOWED_TO_SWITCH', null, false); - $listener($this->event); + $this->assertTrue($listener->supports($this->event->getRequest())); + $listener->authenticate($this->event); $this->assertSame([], $this->request->query->all()); $this->assertSame('', $this->request->server->get('QUERY_STRING')); @@ -243,7 +248,8 @@ public function testSwitchUserWorksWithFalsyUsernames() ->method('checkPostAuth')->with($this->callback(fn ($argUser) => $user->isEqualTo($argUser))); $listener = new SwitchUserListener($this->tokenStorage, $this->userProvider, $this->userChecker, 'provider123', $this->accessDecisionManager); - $listener($this->event); + $this->assertTrue($listener->supports($this->event->getRequest())); + $listener->authenticate($this->event); $this->assertSame([], $this->request->query->all()); $this->assertSame('', $this->request->server->get('QUERY_STRING')); @@ -270,7 +276,8 @@ public function testSwitchUserKeepsOtherQueryStringParameters() ->method('checkPostAuth')->with($targetsUser); $listener = new SwitchUserListener($this->tokenStorage, $this->userProvider, $this->userChecker, 'provider123', $this->accessDecisionManager); - $listener($this->event); + $this->assertTrue($listener->supports($this->event->getRequest())); + $listener->authenticate($this->event); $this->assertSame('page=3§ion=2', $this->request->server->get('QUERY_STRING')); $this->assertInstanceOf(UsernamePasswordToken::class, $this->tokenStorage->getToken()); @@ -308,7 +315,8 @@ public function testSwitchUserWithReplacedToken() ); $listener = new SwitchUserListener($this->tokenStorage, $this->userProvider, $this->userChecker, 'provider123', $this->accessDecisionManager, null, '_switch_user', 'ROLE_ALLOWED_TO_SWITCH', $dispatcher); - $listener($this->event); + $this->assertTrue($listener->supports($this->event->getRequest())); + $listener->authenticate($this->event); $this->assertSame($replacedToken, $this->tokenStorage->getToken()); } @@ -321,7 +329,8 @@ public function testSwitchUserThrowsAuthenticationExceptionIfNoCurrentToken() $this->expectException(AuthenticationCredentialsNotFoundException::class); - $listener($this->event); + $this->assertTrue($listener->supports($this->event->getRequest())); + $listener->authenticate($this->event); } public function testSwitchUserStateless() @@ -340,7 +349,8 @@ public function testSwitchUserStateless() ->method('checkPostAuth')->with($targetsUser); $listener = new SwitchUserListener($this->tokenStorage, $this->userProvider, $this->userChecker, 'provider123', $this->accessDecisionManager, null, '_switch_user', 'ROLE_ALLOWED_TO_SWITCH', null, true); - $listener($this->event); + $this->assertTrue($listener->supports($this->event->getRequest())); + $listener->authenticate($this->event); $this->assertInstanceOf(UsernamePasswordToken::class, $this->tokenStorage->getToken()); $this->assertFalse($this->event->hasResponse()); @@ -371,6 +381,7 @@ public function testSwitchUserRefreshesOriginalToken() ; $listener = new SwitchUserListener($this->tokenStorage, $userProvider, $this->userChecker, 'provider123', $this->accessDecisionManager, null, '_switch_user', 'ROLE_ALLOWED_TO_SWITCH', $dispatcher); - $listener($this->event); + $this->assertTrue($listener->supports($this->event->getRequest())); + $listener->authenticate($this->event); } } diff --git a/src/Symfony/Component/Security/Http/Tests/FirewallTest.php b/src/Symfony/Component/Security/Http/Tests/FirewallTest.php index 89040f3875f2b..bfa9bebdd0b32 100644 --- a/src/Symfony/Component/Security/Http/Tests/FirewallTest.php +++ b/src/Symfony/Component/Security/Http/Tests/FirewallTest.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Security\Http\Tests; use PHPUnit\Framework\TestCase; +use Symfony\Bridge\PhpUnit\ExpectUserDeprecationMessageTrait; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; @@ -25,6 +26,8 @@ class FirewallTest extends TestCase { + use ExpectUserDeprecationMessageTrait; + public function testOnKernelRequestRegistersExceptionListener() { $dispatcher = $this->createMock(EventDispatcherInterface::class); @@ -54,21 +57,25 @@ public function testOnKernelRequestRegistersExceptionListener() public function testOnKernelRequestStopsWhenThereIsAResponse() { - $called = []; + $listener = new class extends AbstractListener { + public int $callCount = 0; - $first = function () use (&$called) { - $called[] = 1; - }; + public function supports(Request $request): ?bool + { + return true; + } - $second = function () use (&$called) { - $called[] = 2; + public function authenticate(RequestEvent $event): void + { + ++$this->callCount; + } }; $map = $this->createMock(FirewallMapInterface::class); $map ->expects($this->once()) ->method('getListeners') - ->willReturn([[$first, $second], null, null]) + ->willReturn([[$listener, $listener], null, null]) ; $event = new RequestEvent($this->createMock(HttpKernelInterface::class), new Request(), HttpKernelInterface::MAIN_REQUEST); @@ -77,7 +84,7 @@ public function testOnKernelRequestStopsWhenThereIsAResponse() $firewall = new Firewall($map, $this->createMock(EventDispatcherInterface::class)); $firewall->onKernelRequest($event); - $this->assertSame([1], $called); + $this->assertSame(1, $listener->callCount); } public function testOnKernelRequestWithSubRequest() @@ -100,11 +107,10 @@ public function testOnKernelRequestWithSubRequest() $this->assertFalse($event->hasResponse()); } - public function testListenersAreCalled() + public function testFirewallListenersAreCalled() { $calledListeners = []; - $callableListener = static function() use(&$calledListeners) { $calledListeners[] = 'callableListener'; }; $firewallListener = new class($calledListeners) implements FirewallListenerInterface { public function __construct(private array &$calledListeners) {} @@ -144,14 +150,43 @@ public function authenticate(RequestEvent $event): void ->expects($this->once()) ->method('getListeners') ->with($this->equalTo($request)) - ->willReturn([[$callableListener, $firewallListener, $callableFirewallListener], null, null]) + ->willReturn([[$firewallListener, $callableFirewallListener], null, null]) + ; + + $event = new RequestEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MAIN_REQUEST); + + $firewall = new Firewall($map, $this->createMock(EventDispatcherInterface::class)); + $firewall->onKernelRequest($event); + + $this->assertSame(['firewallListener', 'callableFirewallListener'], $calledListeners); + } + + /** + * @group legacy + */ + public function testCallableListenersAreCalled() + { + $calledListeners = []; + + $callableListener = static function() use(&$calledListeners) { $calledListeners[] = 'callableListener'; }; + + $request = $this->createMock(Request::class); + + $map = $this->createMock(FirewallMapInterface::class); + $map + ->expects($this->once()) + ->method('getListeners') + ->with($this->equalTo($request)) + ->willReturn([[$callableListener], null, null]) ; $event = new RequestEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MAIN_REQUEST); $firewall = new Firewall($map, $this->createMock(EventDispatcherInterface::class)); + + $this->expectUserDeprecationMessage('Since symfony/security-http 7.4: Using a callable as firewall listener is deprecated, extend "Symfony\Component\Security\Http\Firewall\AbstractListener" or implement "Symfony\Component\Security\Http\Firewall\FirewallListenerInterface" instead.'); $firewall->onKernelRequest($event); - $this->assertSame(['callableListener', 'firewallListener', 'callableFirewallListener'], $calledListeners); + $this->assertSame(['callableListener'], $calledListeners); } } pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy