diff --git a/DependencyInjection/Security/Factory/AbstractFactory.php b/DependencyInjection/Security/Factory/AbstractFactory.php index 6a34fe7e..1d2121ff 100644 --- a/DependencyInjection/Security/Factory/AbstractFactory.php +++ b/DependencyInjection/Security/Factory/AbstractFactory.php @@ -14,7 +14,6 @@ use Symfony\Component\Config\Definition\Builder\NodeDefinition; use Symfony\Component\DependencyInjection\ChildDefinition; use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Reference; /** * AbstractFactory is the base class for all classes inheriting from diff --git a/LICENSE b/LICENSE index 00837045..0138f8f0 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2023 Fabien Potencier +Copyright (c) 2004-present Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Resources/config/security.php b/Resources/config/security.php index b25de431..5f4e693b 100644 --- a/Resources/config/security.php +++ b/Resources/config/security.php @@ -83,7 +83,7 @@ service_locator([ 'security.token_storage' => service('security.token_storage'), 'security.authorization_checker' => service('security.authorization_checker'), - 'security.user_authenticator' => service('security.user_authenticator')->ignoreOnInvalid(), + 'security.authenticator.managers_locator' => service('security.authenticator.managers_locator')->ignoreOnInvalid(), 'request_stack' => service('request_stack'), 'security.firewall.map' => service('security.firewall.map'), 'security.user_checker' => service('security.user_checker'), diff --git a/Security.php b/Security.php index d78cfc0e..94e0e05e 100644 --- a/Security.php +++ b/Security.php @@ -69,7 +69,7 @@ public function login(UserInterface $user, string $authenticatorName = null, str $authenticator = $this->getAuthenticator($authenticatorName, $firewallName); $this->container->get('security.user_checker')->checkPreAuth($user); - $this->container->get('security.user_authenticator')->authenticateUser($user, $authenticator, $request); + $this->container->get('security.authenticator.managers_locator')->get($firewallName)->authenticateUser($user, $authenticator, $request); } /** diff --git a/Tests/CacheWarmer/ExpressionCacheWarmerTest.php b/Tests/CacheWarmer/ExpressionCacheWarmerTest.php index 53b16fcd..d32e2d5d 100644 --- a/Tests/CacheWarmer/ExpressionCacheWarmerTest.php +++ b/Tests/CacheWarmer/ExpressionCacheWarmerTest.php @@ -14,6 +14,7 @@ use PHPUnit\Framework\TestCase; use Symfony\Bundle\SecurityBundle\CacheWarmer\ExpressionCacheWarmer; use Symfony\Component\ExpressionLanguage\Expression; +use Symfony\Component\ExpressionLanguage\ParsedExpression; use Symfony\Component\Security\Core\Authorization\ExpressionLanguage; class ExpressionCacheWarmerTest extends TestCase @@ -22,13 +23,21 @@ public function testWarmUp() { $expressions = [new Expression('A'), new Expression('B')]; + $series = [ + [$expressions[0], ['token', 'user', 'object', 'subject', 'role_names', 'request', 'trust_resolver']], + [$expressions[1], ['token', 'user', 'object', 'subject', 'role_names', 'request', 'trust_resolver']], + ]; + $expressionLang = $this->createMock(ExpressionLanguage::class); $expressionLang->expects($this->exactly(2)) ->method('parse') - ->withConsecutive( - [$expressions[0], ['token', 'user', 'object', 'subject', 'role_names', 'request', 'trust_resolver']], - [$expressions[1], ['token', 'user', 'object', 'subject', 'role_names', 'request', 'trust_resolver']] - ); + ->willReturnCallback(function (...$args) use (&$series) { + $expectedArgs = array_shift($series); + $this->assertSame($expectedArgs, $args); + + return $this->createMock(ParsedExpression::class); + }) + ; (new ExpressionCacheWarmer($expressions, $expressionLang))->warmUp(''); } diff --git a/Tests/DataCollector/SecurityDataCollectorTest.php b/Tests/DataCollector/SecurityDataCollectorTest.php index b5178dcd..abc27188 100644 --- a/Tests/DataCollector/SecurityDataCollectorTest.php +++ b/Tests/DataCollector/SecurityDataCollectorTest.php @@ -24,6 +24,7 @@ use Symfony\Component\HttpKernel\HttpKernelInterface; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage; use Symfony\Component\Security\Core\Authentication\Token\SwitchUserToken; +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; use Symfony\Component\Security\Core\Authorization\TraceableAccessDecisionManager; use Symfony\Component\Security\Core\Authorization\Voter\TraceableVoter; @@ -32,6 +33,7 @@ use Symfony\Component\Security\Core\User\InMemoryUser; use Symfony\Component\Security\Http\FirewallMapInterface; use Symfony\Component\Security\Http\Logout\LogoutUrlGenerator; +use Symfony\Component\VarDumper\Caster\ClassStub; use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; class SecurityDataCollectorTest extends TestCase @@ -219,17 +221,36 @@ public function testGetListeners() $this->assertSame(1, $listenerCalled); } - public function providerCollectDecisionLog(): \Generator + public function testCollectCollectsDecisionLogWhenStrategyIsAffirmative() { - $voter1 = $this->getMockBuilder(VoterInterface::class)->getMockForAbstractClass(); - $voter2 = $this->getMockBuilder(VoterInterface::class)->getMockForAbstractClass(); + $voter1 = new DummyVoter(); + $voter2 = new DummyVoter(); - $eventDispatcher = $this->getMockBuilder(EventDispatcherInterface::class)->getMockForAbstractClass(); - $decoratedVoter1 = new TraceableVoter($voter1, $eventDispatcher); + $decoratedVoter1 = new TraceableVoter($voter1, new class() implements EventDispatcherInterface { + public function dispatch(object $event, string $eventName = null): object + { + return new \stdClass(); + } + }); - yield [ - MainConfiguration::STRATEGY_AFFIRMATIVE, - [[ + $strategy = MainConfiguration::STRATEGY_AFFIRMATIVE; + + $accessDecisionManager = $this->createMock(TraceableAccessDecisionManager::class); + + $accessDecisionManager + ->method('getStrategy') + ->willReturn($strategy); + + $accessDecisionManager + ->method('getVoters') + ->willReturn([ + $decoratedVoter1, + $decoratedVoter1, + ]); + + $accessDecisionManager + ->method('getDecisionLog') + ->willReturn([[ 'attributes' => ['view'], 'object' => new \stdClass(), 'result' => true, @@ -237,23 +258,74 @@ public function providerCollectDecisionLog(): \Generator ['voter' => $voter1, 'attributes' => ['view'], 'vote' => VoterInterface::ACCESS_ABSTAIN], ['voter' => $voter2, 'attributes' => ['view'], 'vote' => VoterInterface::ACCESS_ABSTAIN], ], - ]], - [$decoratedVoter1, $decoratedVoter1], - [$voter1::class, $voter2::class], - [[ - 'attributes' => ['view'], - 'object' => new \stdClass(), - 'result' => true, - 'voter_details' => [ - ['class' => $voter1::class, 'attributes' => ['view'], 'vote' => VoterInterface::ACCESS_ABSTAIN], - ['class' => $voter2::class, 'attributes' => ['view'], 'vote' => VoterInterface::ACCESS_ABSTAIN], - ], - ]], + ]]); + + $dataCollector = new SecurityDataCollector(null, null, null, $accessDecisionManager, null, null, true); + + $dataCollector->collect(new Request(), new Response()); + + $actualDecisionLog = $dataCollector->getAccessDecisionLog(); + + $expectedDecisionLog = [[ + 'attributes' => ['view'], + 'object' => new \stdClass(), + 'result' => true, + 'voter_details' => [ + ['class' => $voter1::class, 'attributes' => ['view'], 'vote' => VoterInterface::ACCESS_ABSTAIN], + ['class' => $voter2::class, 'attributes' => ['view'], 'vote' => VoterInterface::ACCESS_ABSTAIN], + ], + ]]; + + $this->assertEquals($actualDecisionLog, $expectedDecisionLog, 'Wrong value returned by getAccessDecisionLog'); + + $actualVoterClasses = array_map(static function (ClassStub $classStub): string { + return (string) $classStub; + }, $dataCollector->getVoters()); + + $expectedVoterClasses = [ + $voter1::class, + $voter2::class, ]; - yield [ - MainConfiguration::STRATEGY_UNANIMOUS, - [ + $this->assertSame( + $actualVoterClasses, + $expectedVoterClasses, + 'Wrong value returned by getVoters' + ); + + $this->assertSame($dataCollector->getVoterStrategy(), $strategy, 'Wrong value returned by getVoterStrategy'); + } + + public function testCollectCollectsDecisionLogWhenStrategyIsUnanimous() + { + $voter1 = new DummyVoter(); + $voter2 = new DummyVoter(); + + $decoratedVoter1 = new TraceableVoter($voter1, new class() implements EventDispatcherInterface { + public function dispatch(object $event, string $eventName = null): object + { + return new \stdClass(); + } + }); + + $strategy = MainConfiguration::STRATEGY_UNANIMOUS; + + $accessDecisionManager = $this->createMock(TraceableAccessDecisionManager::class); + + $accessDecisionManager + ->method('getStrategy') + ->willReturn($strategy); + + $accessDecisionManager + ->method('getVoters') + ->willReturn([ + $decoratedVoter1, + $decoratedVoter1, + ]); + + $accessDecisionManager + ->method('getDecisionLog') + ->willReturn([ [ 'attributes' => ['view', 'edit'], 'object' => new \stdClass(), @@ -274,82 +346,58 @@ public function providerCollectDecisionLog(): \Generator ['voter' => $voter2, 'attributes' => ['update'], 'vote' => VoterInterface::ACCESS_GRANTED], ], ], - ], - [$decoratedVoter1, $decoratedVoter1], - [$voter1::class, $voter2::class], + ]); + + $dataCollector = new SecurityDataCollector(null, null, null, $accessDecisionManager, null, null, true); + + $dataCollector->collect(new Request(), new Response()); + + $actualDecisionLog = $dataCollector->getAccessDecisionLog(); + + $expectedDecisionLog = [ [ - [ - 'attributes' => ['view', 'edit'], - 'object' => new \stdClass(), - 'result' => false, - 'voter_details' => [ - ['class' => $voter1::class, 'attributes' => ['view'], 'vote' => VoterInterface::ACCESS_DENIED], - ['class' => $voter1::class, 'attributes' => ['edit'], 'vote' => VoterInterface::ACCESS_DENIED], - ['class' => $voter2::class, 'attributes' => ['view'], 'vote' => VoterInterface::ACCESS_GRANTED], - ['class' => $voter2::class, 'attributes' => ['edit'], 'vote' => VoterInterface::ACCESS_GRANTED], - ], + 'attributes' => ['view', 'edit'], + 'object' => new \stdClass(), + 'result' => false, + 'voter_details' => [ + ['class' => $voter1::class, 'attributes' => ['view'], 'vote' => VoterInterface::ACCESS_DENIED], + ['class' => $voter1::class, 'attributes' => ['edit'], 'vote' => VoterInterface::ACCESS_DENIED], + ['class' => $voter2::class, 'attributes' => ['view'], 'vote' => VoterInterface::ACCESS_GRANTED], + ['class' => $voter2::class, 'attributes' => ['edit'], 'vote' => VoterInterface::ACCESS_GRANTED], ], - [ - 'attributes' => ['update'], - 'object' => new \stdClass(), - 'result' => true, - 'voter_details' => [ - ['class' => $voter1::class, 'attributes' => ['update'], 'vote' => VoterInterface::ACCESS_GRANTED], - ['class' => $voter2::class, 'attributes' => ['update'], 'vote' => VoterInterface::ACCESS_GRANTED], - ], + ], + [ + 'attributes' => ['update'], + 'object' => new \stdClass(), + 'result' => true, + 'voter_details' => [ + ['class' => $voter1::class, 'attributes' => ['update'], 'vote' => VoterInterface::ACCESS_GRANTED], + ['class' => $voter2::class, 'attributes' => ['update'], 'vote' => VoterInterface::ACCESS_GRANTED], ], ], ]; - } - - /** - * Test the returned data when AccessDecisionManager is a TraceableAccessDecisionManager. - * - * @param string $strategy strategy returned by the AccessDecisionManager - * @param array $voters voters returned by AccessDecisionManager - * @param array $decisionLog log of the votes and final decisions from AccessDecisionManager - * @param array $expectedVoterClasses expected voter classes returned by the collector - * @param array $expectedDecisionLog expected decision log returned by the collector - * - * @dataProvider providerCollectDecisionLog - */ - public function testCollectDecisionLog(string $strategy, array $decisionLog, array $voters, array $expectedVoterClasses, array $expectedDecisionLog) - { - $accessDecisionManager = $this - ->getMockBuilder(TraceableAccessDecisionManager::class) - ->disableOriginalConstructor() - ->setMethods(['getStrategy', 'getVoters', 'getDecisionLog']) - ->getMock(); - $accessDecisionManager - ->expects($this->any()) - ->method('getStrategy') - ->willReturn($strategy); + $this->assertEquals($actualDecisionLog, $expectedDecisionLog, 'Wrong value returned by getAccessDecisionLog'); - $accessDecisionManager - ->expects($this->any()) - ->method('getVoters') - ->willReturn($voters); + $actualVoterClasses = array_map(static function (ClassStub $classStub): string { + return (string) $classStub; + }, $dataCollector->getVoters()); - $accessDecisionManager - ->expects($this->any()) - ->method('getDecisionLog') - ->willReturn($decisionLog); - - $dataCollector = new SecurityDataCollector(null, null, null, $accessDecisionManager, null, null, true); - $dataCollector->collect(new Request(), new Response()); - - $this->assertEquals($dataCollector->getAccessDecisionLog(), $expectedDecisionLog, 'Wrong value returned by getAccessDecisionLog'); + $expectedVoterClasses = [ + $voter1::class, + $voter2::class, + ]; $this->assertSame( - array_map(function ($classStub) { return (string) $classStub; }, $dataCollector->getVoters()), + $actualVoterClasses, $expectedVoterClasses, 'Wrong value returned by getVoters' ); + $this->assertSame($dataCollector->getVoterStrategy(), $strategy, 'Wrong value returned by getVoterStrategy'); } - public function provideRoles() + public static function provideRoles() { return [ // Basic roles @@ -380,3 +428,10 @@ private function getRoleHierarchy() ]); } } + +final class DummyVoter implements VoterInterface +{ + public function vote(TokenInterface $token, mixed $subject, array $attributes): int + { + } +} diff --git a/Tests/DependencyInjection/Compiler/RegisterGlobalSecurityEventListenersPassTest.php b/Tests/DependencyInjection/Compiler/RegisterGlobalSecurityEventListenersPassTest.php index cecf1b04..4ec5149f 100644 --- a/Tests/DependencyInjection/Compiler/RegisterGlobalSecurityEventListenersPassTest.php +++ b/Tests/DependencyInjection/Compiler/RegisterGlobalSecurityEventListenersPassTest.php @@ -69,7 +69,7 @@ public function testEventIsPropagated(string $configuredEvent, string $registere ]); } - public function providePropagatedEvents(): array + public static function providePropagatedEvents(): array { return [ [CheckPassportEvent::class, CheckPassportEvent::class], diff --git a/Tests/DependencyInjection/CompleteConfigurationTest.php b/Tests/DependencyInjection/CompleteConfigurationTestCase.php similarity index 99% rename from Tests/DependencyInjection/CompleteConfigurationTest.php rename to Tests/DependencyInjection/CompleteConfigurationTestCase.php index 2909627f..4181bac5 100644 --- a/Tests/DependencyInjection/CompleteConfigurationTest.php +++ b/Tests/DependencyInjection/CompleteConfigurationTestCase.php @@ -34,7 +34,7 @@ use Symfony\Component\Security\Http\Authenticator\Passport\Badge\CsrfTokenBadge; use Symfony\Component\Security\Http\Authenticator\Passport\Badge\RememberMeBadge; -abstract class CompleteConfigurationTest extends TestCase +abstract class CompleteConfigurationTestCase extends TestCase { abstract protected function getLoader(ContainerBuilder $container); diff --git a/Tests/DependencyInjection/PhpCompleteConfigurationTest.php b/Tests/DependencyInjection/PhpCompleteConfigurationTest.php index 846d01d5..c4297c7b 100644 --- a/Tests/DependencyInjection/PhpCompleteConfigurationTest.php +++ b/Tests/DependencyInjection/PhpCompleteConfigurationTest.php @@ -15,7 +15,7 @@ use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Loader\PhpFileLoader; -class PhpCompleteConfigurationTest extends CompleteConfigurationTest +class PhpCompleteConfigurationTest extends CompleteConfigurationTestCase { protected function getLoader(ContainerBuilder $container) { diff --git a/Tests/DependencyInjection/Security/Factory/AbstractFactoryTest.php b/Tests/DependencyInjection/Security/Factory/AbstractFactoryTest.php index a0d9353b..be300e75 100644 --- a/Tests/DependencyInjection/Security/Factory/AbstractFactoryTest.php +++ b/Tests/DependencyInjection/Security/Factory/AbstractFactoryTest.php @@ -60,7 +60,7 @@ public function testDefaultFailureHandler($serviceId, $defaultHandlerInjection) } } - public function getFailureHandlers() + public static function getFailureHandlers() { return [ [null, true], @@ -107,7 +107,7 @@ public function testDefaultSuccessHandler($serviceId, $defaultHandlerInjection) } } - public function getSuccessHandlers() + public static function getSuccessHandlers() { return [ [null, true], diff --git a/Tests/DependencyInjection/SecurityExtensionTest.php b/Tests/DependencyInjection/SecurityExtensionTest.php index bcfb169e..faa31b5f 100644 --- a/Tests/DependencyInjection/SecurityExtensionTest.php +++ b/Tests/DependencyInjection/SecurityExtensionTest.php @@ -306,7 +306,7 @@ public function testRegisterAccessControlWithRequestMatcherAndAdditionalOptionsT $container->compile(); } - public function provideAdditionalRequestMatcherConstraints() + public static function provideAdditionalRequestMatcherConstraints() { yield 'Invalid configuration with path' => [['path' => '^/url']]; yield 'Invalid configuration with host' => [['host' => 'example.com']]; @@ -606,7 +606,7 @@ public function sessionConfigurationProvider() ]; } - public function acceptableIpsProvider(): iterable + public static function acceptableIpsProvider(): iterable { yield [['127.0.0.1']]; yield ['127.0.0.1']; @@ -711,7 +711,7 @@ public function testEntryPointRequired(array $firewall, $messageRegex) $container->compile(); } - public function provideEntryPointRequiredData() + public static function provideEntryPointRequiredData() { // more than one entry point available and not explicitly set yield [ @@ -742,7 +742,7 @@ public function testConfigureCustomAuthenticator(array $firewall, array $expecte $this->assertEquals($expectedAuthenticators, array_map('strval', $container->getDefinition('security.authenticator.manager.main')->getArgument(0))); } - public function provideConfigureCustomAuthenticatorData() + public static function provideConfigureCustomAuthenticatorData() { yield [ ['custom_authenticator' => TestAuthenticator::class], @@ -819,7 +819,7 @@ public function testUserCheckerWithAuthenticatorManager(array $config, string $e $this->assertEquals($expectedUserCheckerClass, $container->findDefinition($userCheckerId)->getClass()); } - public function provideUserCheckerConfig() + public static function provideUserCheckerConfig() { yield [[], InMemoryUserChecker::class]; yield [['user_checker' => TestUserChecker::class], TestUserChecker::class]; diff --git a/Tests/DependencyInjection/XmlCompleteConfigurationTest.php b/Tests/DependencyInjection/XmlCompleteConfigurationTest.php index 00006a75..eccfabef 100644 --- a/Tests/DependencyInjection/XmlCompleteConfigurationTest.php +++ b/Tests/DependencyInjection/XmlCompleteConfigurationTest.php @@ -15,7 +15,7 @@ use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; -class XmlCompleteConfigurationTest extends CompleteConfigurationTest +class XmlCompleteConfigurationTest extends CompleteConfigurationTestCase { protected function getLoader(ContainerBuilder $container) { diff --git a/Tests/DependencyInjection/YamlCompleteConfigurationTest.php b/Tests/DependencyInjection/YamlCompleteConfigurationTest.php index 8e3954a0..4fd2d44e 100644 --- a/Tests/DependencyInjection/YamlCompleteConfigurationTest.php +++ b/Tests/DependencyInjection/YamlCompleteConfigurationTest.php @@ -15,7 +15,7 @@ use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; -class YamlCompleteConfigurationTest extends CompleteConfigurationTest +class YamlCompleteConfigurationTest extends CompleteConfigurationTestCase { protected function getLoader(ContainerBuilder $container) { diff --git a/Tests/EventListener/VoteListenerTest.php b/Tests/EventListener/VoteListenerTest.php index 3406e165..d262effa 100644 --- a/Tests/EventListener/VoteListenerTest.php +++ b/Tests/EventListener/VoteListenerTest.php @@ -26,7 +26,7 @@ public function testOnVoterVote() $traceableAccessDecisionManager = $this ->getMockBuilder(TraceableAccessDecisionManager::class) ->disableOriginalConstructor() - ->setMethods(['addVoterVote']) + ->onlyMethods(['addVoterVote']) ->getMock(); $traceableAccessDecisionManager diff --git a/Tests/Functional/AccessTokenTest.php b/Tests/Functional/AccessTokenTest.php index 01b20573..91afad75 100644 --- a/Tests/Functional/AccessTokenTest.php +++ b/Tests/Functional/AccessTokenTest.php @@ -113,12 +113,12 @@ public function testCustomMissingFormEncodedBodyShouldFail() $this->assertSame(401, $response->getStatusCode()); } - public function defaultFormEncodedBodyFailureData(): iterable + public static function defaultFormEncodedBodyFailureData(): iterable { yield [['access_token' => 'INVALID_ACCESS_TOKEN'], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']]; } - public function customFormEncodedBodyFailure(): iterable + public static function customFormEncodedBodyFailure(): iterable { yield [['secured_token' => 'INVALID_ACCESS_TOKEN'], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']]; } @@ -211,24 +211,24 @@ public function testCustomMissingHeaderAccessTokenShouldFail(array $headers) $this->assertSame(401, $response->getStatusCode()); } - public function defaultHeaderAccessTokenFailureData(): iterable + public static function defaultHeaderAccessTokenFailureData(): iterable { yield [['HTTP_AUTHORIZATION' => 'Bearer INVALID_ACCESS_TOKEN']]; } - public function defaultMissingHeaderAccessTokenFailData(): iterable + public static function defaultMissingHeaderAccessTokenFailData(): iterable { yield [['HTTP_AUTHORIZATION' => 'JWT INVALID_TOKEN_TYPE']]; yield [['HTTP_X_FOO' => 'Missing-Header']]; yield [['HTTP_X_AUTH_TOKEN' => 'this is not a token']]; } - public function customHeaderAccessTokenFailure(): iterable + public static function customHeaderAccessTokenFailure(): iterable { yield [['HTTP_X_AUTH_TOKEN' => 'INVALID_ACCESS_TOKEN'], 500]; } - public function customMissingHeaderAccessTokenShouldFail(): iterable + public static function customMissingHeaderAccessTokenShouldFail(): iterable { yield [[]]; yield [['HTTP_AUTHORIZATION' => 'Bearer this is not a token']]; @@ -306,12 +306,12 @@ public function testCustomMissingQueryAccessTokenShouldFail() $this->assertSame(401, $response->getStatusCode()); } - public function defaultQueryAccessTokenFailureData(): iterable + public static function defaultQueryAccessTokenFailureData(): iterable { yield ['/foo?access_token=INVALID_ACCESS_TOKEN']; } - public function customQueryAccessTokenFailure(): iterable + public static function customQueryAccessTokenFailure(): iterable { yield ['/foo?protection_token=INVALID_ACCESS_TOKEN']; } diff --git a/Tests/Functional/AuthenticatorTest.php b/Tests/Functional/AuthenticatorTest.php index 970a9dc9..ca99dbf3 100644 --- a/Tests/Functional/AuthenticatorTest.php +++ b/Tests/Functional/AuthenticatorTest.php @@ -60,7 +60,7 @@ public function testWithoutUserProvider($email) $this->assertJsonStringEqualsJsonString('{"email":"'.$email.'"}', $client->getResponse()->getContent()); } - public function provideEmails() + public static function provideEmails() { yield ['jane@example.org', true]; yield ['john@example.org', false]; @@ -84,7 +84,7 @@ public function testLoginUsersWithMultipleFirewalls(string $username, string $fi $this->assertEquals('Welcome '.$username.'!', $client->getResponse()->getContent()); } - public function provideEmailsWithFirewalls() + public static function provideEmailsWithFirewalls() { yield ['jane@example.org', 'main']; yield ['john@example.org', 'custom']; diff --git a/Tests/Functional/Bundle/AccessTokenBundle/Security/Http/JsonAuthenticationSuccessHandler.php b/Tests/Functional/Bundle/AccessTokenBundle/Security/Http/JsonAuthenticationSuccessHandler.php index fd08a4ad..d6148158 100644 --- a/Tests/Functional/Bundle/AccessTokenBundle/Security/Http/JsonAuthenticationSuccessHandler.php +++ b/Tests/Functional/Bundle/AccessTokenBundle/Security/Http/JsonAuthenticationSuccessHandler.php @@ -19,7 +19,7 @@ class JsonAuthenticationSuccessHandler implements AuthenticationSuccessHandlerInterface { - public function onAuthenticationSuccess(Request $request, TokenInterface $token): Response + public function onAuthenticationSuccess(Request $request, TokenInterface $token): ?Response { return new JsonResponse(['message' => sprintf('Good game @%s!', $token->getUserIdentifier())]); } diff --git a/Tests/Functional/Bundle/JsonLoginBundle/Security/Http/JsonAuthenticationSuccessHandler.php b/Tests/Functional/Bundle/JsonLoginBundle/Security/Http/JsonAuthenticationSuccessHandler.php index 4aabaacd..b7dd3fd3 100644 --- a/Tests/Functional/Bundle/JsonLoginBundle/Security/Http/JsonAuthenticationSuccessHandler.php +++ b/Tests/Functional/Bundle/JsonLoginBundle/Security/Http/JsonAuthenticationSuccessHandler.php @@ -19,7 +19,7 @@ class JsonAuthenticationSuccessHandler implements AuthenticationSuccessHandlerInterface { - public function onAuthenticationSuccess(Request $request, TokenInterface $token): Response + public function onAuthenticationSuccess(Request $request, TokenInterface $token): ?Response { return new JsonResponse(['message' => sprintf('Good game @%s!', $token->getUserIdentifier())]); } diff --git a/Tests/Functional/Bundle/LoginLink/TestCustomLoginLinkSuccessHandler.php b/Tests/Functional/Bundle/LoginLink/TestCustomLoginLinkSuccessHandler.php index 0cbc159e..06997641 100644 --- a/Tests/Functional/Bundle/LoginLink/TestCustomLoginLinkSuccessHandler.php +++ b/Tests/Functional/Bundle/LoginLink/TestCustomLoginLinkSuccessHandler.php @@ -19,7 +19,7 @@ class TestCustomLoginLinkSuccessHandler implements AuthenticationSuccessHandlerInterface { - public function onAuthenticationSuccess(Request $request, TokenInterface $token): Response + public function onAuthenticationSuccess(Request $request, TokenInterface $token): ?Response { return new JsonResponse(['message' => sprintf('Welcome %s!', $token->getUserIdentifier())]); } diff --git a/Tests/Functional/CsrfFormLoginTest.php b/Tests/Functional/CsrfFormLoginTest.php index 72a4b7bb..25aa0131 100644 --- a/Tests/Functional/CsrfFormLoginTest.php +++ b/Tests/Functional/CsrfFormLoginTest.php @@ -122,7 +122,7 @@ public function testFormLoginRedirectsToProtectedResourceAfterLogin($options) $this->assertStringContainsString('You\'re browsing to path "/protected-resource".', $text); } - public function provideClientOptions() + public static function provideClientOptions() { yield [['test_case' => 'CsrfFormLogin', 'root_config' => 'config.yml']]; yield [['test_case' => 'CsrfFormLogin', 'root_config' => 'routes_as_path.yml']]; diff --git a/Tests/Functional/FormLoginTest.php b/Tests/Functional/FormLoginTest.php index 78b7f614..583c0dd2 100644 --- a/Tests/Functional/FormLoginTest.php +++ b/Tests/Functional/FormLoginTest.php @@ -147,7 +147,7 @@ public function testLoginThrottling() } } - public function provideClientOptions() + public static function provideClientOptions() { yield [['test_case' => 'StandardFormLogin', 'root_config' => 'base_config.yml']]; yield [['test_case' => 'StandardFormLogin', 'root_config' => 'routes_as_path.yml']]; diff --git a/Tests/Functional/LocalizedRoutesAsPathTest.php b/Tests/Functional/LocalizedRoutesAsPathTest.php index 0c356662..99ba311a 100644 --- a/Tests/Functional/LocalizedRoutesAsPathTest.php +++ b/Tests/Functional/LocalizedRoutesAsPathTest.php @@ -36,6 +36,7 @@ public function testLoginLogoutProcedure(string $locale) /** * @group issue-32995 + * * @dataProvider getLocales */ public function testLoginFailureWithLocalizedFailurePath(string $locale) @@ -73,7 +74,7 @@ public function testAccessRestrictedResourceWithForward(string $locale) $this->assertCount(1, $crawler->selectButton('login'), (string) $client->getResponse()); } - public function getLocales() + public static function getLocales() { yield ['en']; yield ['de']; diff --git a/Tests/Functional/RememberMeCookieTest.php b/Tests/Functional/RememberMeCookieTest.php index 0bd2711f..d91b321b 100644 --- a/Tests/Functional/RememberMeCookieTest.php +++ b/Tests/Functional/RememberMeCookieTest.php @@ -31,7 +31,7 @@ public function testSessionRememberMeSecureCookieFlagAuto($https, $expectedSecur $this->assertSame($expectedSecureFlag, $cookies['']['/']['REMEMBERME']->isSecure()); } - public function getSessionRememberMeSecureCookieFlagAutoHttpsMap() + public static function getSessionRememberMeSecureCookieFlagAutoHttpsMap() { return [ [true, true], diff --git a/Tests/Functional/RememberMeTest.php b/Tests/Functional/RememberMeTest.php index 0ccc17d9..2fff3a9e 100644 --- a/Tests/Functional/RememberMeTest.php +++ b/Tests/Functional/RememberMeTest.php @@ -93,7 +93,7 @@ public function testSessionLessRememberMeLogout() $this->assertNull($cookieJar->get('REMEMBERME')); } - public function provideConfigs() + public static function provideConfigs() { yield [['root_config' => 'config_session.yml']]; yield [['root_config' => 'config_persistent.yml']]; diff --git a/Tests/Functional/SecurityRoutingIntegrationTest.php b/Tests/Functional/SecurityRoutingIntegrationTest.php index 7c1f3dc0..36280125 100644 --- a/Tests/Functional/SecurityRoutingIntegrationTest.php +++ b/Tests/Functional/SecurityRoutingIntegrationTest.php @@ -151,7 +151,7 @@ private function assertRestricted($client, $path) $this->assertEquals(302, $client->getResponse()->getStatusCode()); } - public function provideConfigs() + public static function provideConfigs() { yield [['test_case' => 'StandardFormLogin', 'root_config' => 'base_config.yml']]; yield [['test_case' => 'StandardFormLogin', 'root_config' => 'routes_as_path.yml']]; diff --git a/Tests/Functional/SecurityTest.php b/Tests/Functional/SecurityTest.php index 91eaefa4..f2d6733f 100644 --- a/Tests/Functional/SecurityTest.php +++ b/Tests/Functional/SecurityTest.php @@ -76,7 +76,7 @@ public function testUserWillBeMarkedAsChangedIfRolesHasChanged(UserInterface $us $this->assertEquals(302, $client->getResponse()->getStatusCode()); } - public function userWillBeMarkedAsChangedIfRolesHasChangedProvider() + public static function userWillBeMarkedAsChangedIfRolesHasChangedProvider() { return [ [ diff --git a/Tests/Functional/SwitchUserTest.php b/Tests/Functional/SwitchUserTest.php index 8b884801..f3768472 100644 --- a/Tests/Functional/SwitchUserTest.php +++ b/Tests/Functional/SwitchUserTest.php @@ -63,7 +63,7 @@ public function testSwitchUserStateless() $this->assertSame('dunglas', $client->getProfile()->getCollector('security')->getUser()); } - public function getTestParameters() + public static function getTestParameters() { return [ 'unauthorized_user_cannot_switch' => ['user_cannot_switch_1', 'user_cannot_switch_1', 'user_cannot_switch_1', 403], diff --git a/Tests/Functional/app/SecurityHelper/config.yml b/Tests/Functional/app/SecurityHelper/config.yml index c6934ba0..3837eb5d 100644 --- a/Tests/Functional/app/SecurityHelper/config.yml +++ b/Tests/Functional/app/SecurityHelper/config.yml @@ -40,7 +40,16 @@ security: custom_authenticators: - 'Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\AuthenticatorBundle\ApiAuthenticator' provider: main + second: + pattern: ^/second + form_login: + check_path: /second/login/check + custom_authenticators: + - 'Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\AuthenticatorBundle\ApiAuthenticator' + provider: main access_control: - { path: '^/main/login/check$', roles: IS_AUTHENTICATED_FULLY } - { path: '^/main/logged-in$', roles: IS_AUTHENTICATED_FULLY } + - { path: '^/second/login/check$', roles: IS_AUTHENTICATED_FULLY } + - { path: '^/second/logged-in$', roles: IS_AUTHENTICATED_FULLY } diff --git a/Tests/Functional/app/SecurityHelper/routing.yml b/Tests/Functional/app/SecurityHelper/routing.yml index 1f74d275..8487bbec 100644 --- a/Tests/Functional/app/SecurityHelper/routing.yml +++ b/Tests/Functional/app/SecurityHelper/routing.yml @@ -9,3 +9,11 @@ logged-in: force-logout: path: /main/force-logout controller: Symfony\Bundle\SecurityBundle\Tests\Functional\LogoutController::logout + +second-force-login: + path: /second/force-login + controller: Symfony\Bundle\SecurityBundle\Tests\Functional\ForceLoginController::welcome + +second-logged-in: + path: /second/logged-in + controller: Symfony\Bundle\SecurityBundle\Tests\Functional\LoggedInController diff --git a/Tests/SecurityTest.php b/Tests/SecurityTest.php index b69467cb..8e1e6156 100644 --- a/Tests/SecurityTest.php +++ b/Tests/SecurityTest.php @@ -74,7 +74,7 @@ public function testGetUser($userInToken, $expectedUser) $this->assertSame($expectedUser, $security->getUser()); } - public function getUserTests() + public static function getUserTests() { yield [null, null]; @@ -115,7 +115,7 @@ public function testGetFirewallConfig(Request $request, ?FirewallConfig $expecte $this->assertSame($expectedFirewallConfig, $security->getFirewallConfig($request)); } - public function getFirewallConfigTests() + public static function getFirewallConfigTests() { $request = new Request(); @@ -141,7 +141,7 @@ public function testLogin() ->willReturnMap([ ['request_stack', $requestStack], ['security.firewall.map', $firewallMap], - ['security.user_authenticator', $userAuthenticator], + ['security.authenticator.managers_locator', new ServiceLocator(['main' => fn () => $userAuthenticator])], ['security.user_checker', $userChecker], ]) ; diff --git a/composer.json b/composer.json index 0272b632..cae9c763 100644 --- a/composer.json +++ b/composer.json @@ -27,7 +27,7 @@ "symfony/password-hasher": "^5.4|^6.0", "symfony/security-core": "^6.2", "symfony/security-csrf": "^5.4|^6.0", - "symfony/security-http": "^6.2.6" + "symfony/security-http": "^6.2.10" }, "require-dev": { "doctrine/annotations": "^1.10.4|^2",