diff --git a/UPGRADE-5.3.md b/UPGRADE-5.3.md index 32615caf15b6f..a69a34ce62ff8 100644 --- a/UPGRADE-5.3.md +++ b/UPGRADE-5.3.md @@ -9,6 +9,7 @@ Asset DoctrineBridge -------------- + * Deprecate `UserLoaderInterface::loadUserByUsername()` in favor of `UserLoaderInterface::loadUserByIdentifier() * Remove `UuidV*Generator` classes DomCrawler @@ -178,6 +179,11 @@ Security } ``` + * Deprecate `UserInterface::getUsername()` in favor of `UserInterface::getUserIdentifier()` + * Deprecate `TokenInterface::getUsername()` in favor of `TokenInterface::getUserIdentifier()` + * Deprecate `UserProviderInterface::loadUserByUsername()` in favor of `UserProviderInterface::loadUserByIdentifier()` + * Deprecate `UsernameNotFoundException` in favor of `UserNotFoundException` and `getUsername()`/`setUsername()` in favor of `getUserIdentifier()`/`setUserIdentifier()` + * Deprecate `PersistentTokenInterface::getUsername()` in favor of `PersistentTokenInterface::getUserIdentifier()` * Deprecate calling `PasswordUpgraderInterface::upgradePassword()` with a `UserInterface` instance that does not implement `PasswordAuthenticatedUserInterface` * Deprecate calling methods `hashPassword()`, `isPasswordValid()` and `needsRehash()` on `UserPasswordHasherInterface` with a `UserInterface` instance that does not implement `PasswordAuthenticatedUserInterface` * Deprecate all classes in the `Core\Encoder\` sub-namespace, use the `PasswordHasher` component instead diff --git a/UPGRADE-6.0.md b/UPGRADE-6.0.md index 997029ae61a6a..728475eed3710 100644 --- a/UPGRADE-6.0.md +++ b/UPGRADE-6.0.md @@ -6,6 +6,11 @@ Asset * Removed `RemoteJsonManifestVersionStrategy`, use `JsonManifestVersionStrategy` instead. +DoctrineBridge +-------------- + + * Remove `UserLoaderInterface::loadUserByUsername()` in favor of `UserLoaderInterface::loadUserByIdentifier() + Config ------ @@ -262,6 +267,11 @@ Security } ``` + * Remove `UserInterface::getUsername()` in favor of `UserInterface::getUserIdentifier()` + * Remove `TokenInterface::getUsername()` in favor of `TokenInterface::getUserIdentifier()` + * Remove `UserProviderInterface::loadUserByUsername()` in favor of `UserProviderInterface::loadUserByIdentifier()` + * Remove `UsernameNotFoundException` in favor of `UserNotFoundException` and `getUsername()`/`setUsername()` in favor of `getUserIdentifier()`/`setUserIdentifier()` + * Remove `PersistentTokenInterface::getUsername()` in favor of `PersistentTokenInterface::getUserIdentifier()` * Calling `PasswordUpgraderInterface::upgradePassword()` with a `UserInterface` instance that does not implement `PasswordAuthenticatedUserInterface` now throws a `\TypeError`. * Calling methods `hashPassword()`, `isPasswordValid()` and `needsRehash()` on `UserPasswordHasherInterface` diff --git a/src/Symfony/Bridge/Doctrine/CHANGELOG.md b/src/Symfony/Bridge/Doctrine/CHANGELOG.md index 7afda2ec2d167..7b627edbfa33b 100644 --- a/src/Symfony/Bridge/Doctrine/CHANGELOG.md +++ b/src/Symfony/Bridge/Doctrine/CHANGELOG.md @@ -4,6 +4,7 @@ CHANGELOG 5.3 --- + * Deprecate `UserLoaderInterface::loadUserByUsername()` in favor of `UserLoaderInterface::loadUserByIdentifier() * Deprecate `DoctrineTestHelper` and `TestRepositoryFactory` * [BC BREAK] Remove `UuidV*Generator` classes * Add `UuidGenerator` diff --git a/src/Symfony/Bridge/Doctrine/Security/RememberMe/DoctrineTokenProvider.php b/src/Symfony/Bridge/Doctrine/Security/RememberMe/DoctrineTokenProvider.php index 4116a6c9c6cb8..d2ac616db7439 100644 --- a/src/Symfony/Bridge/Doctrine/Security/RememberMe/DoctrineTokenProvider.php +++ b/src/Symfony/Bridge/Doctrine/Security/RememberMe/DoctrineTokenProvider.php @@ -119,7 +119,8 @@ public function createNewToken(PersistentTokenInterface $token) .' VALUES (:class, :username, :series, :value, :lastUsed)'; $paramValues = [ 'class' => $token->getClass(), - 'username' => $token->getUsername(), + // @deprecated since 5.3, change to $token->getUserIdentifier() in 6.0 + 'username' => method_exists($token, 'getUserIdentifier') ? $token->getUserIdentifier() : $token->getUsername(), 'series' => $token->getSeries(), 'value' => $token->getTokenValue(), 'lastUsed' => $token->getLastUsed(), diff --git a/src/Symfony/Bridge/Doctrine/Security/User/EntityUserProvider.php b/src/Symfony/Bridge/Doctrine/Security/User/EntityUserProvider.php index e0174e6bbb1b2..f965b8173f1ac 100644 --- a/src/Symfony/Bridge/Doctrine/Security/User/EntityUserProvider.php +++ b/src/Symfony/Bridge/Doctrine/Security/User/EntityUserProvider.php @@ -16,7 +16,7 @@ use Doctrine\Persistence\ObjectManager; use Doctrine\Persistence\ObjectRepository; use Symfony\Component\Security\Core\Exception\UnsupportedUserException; -use Symfony\Component\Security\Core\Exception\UsernameNotFoundException; +use Symfony\Component\Security\Core\Exception\UserNotFoundException; use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface; use Symfony\Component\Security\Core\User\PasswordUpgraderInterface; use Symfony\Component\Security\Core\User\UserInterface; @@ -50,21 +50,35 @@ public function __construct(ManagerRegistry $registry, string $classOrAlias, str * {@inheritdoc} */ public function loadUserByUsername(string $username) + { + trigger_deprecation('symfony/doctrine-bridge', '5.3', 'Method "%s()" is deprecated, use loadUserByIdentifier() instead.', __METHOD__); + + return $this->loadUserByIdentifier($username); + } + + public function loadUserByIdentifier(string $identifier): UserInterface { $repository = $this->getRepository(); if (null !== $this->property) { - $user = $repository->findOneBy([$this->property => $username]); + $user = $repository->findOneBy([$this->property => $identifier]); } else { if (!$repository instanceof UserLoaderInterface) { throw new \InvalidArgumentException(sprintf('You must either make the "%s" entity Doctrine Repository ("%s") implement "Symfony\Bridge\Doctrine\Security\User\UserLoaderInterface" or set the "property" option in the corresponding entity provider configuration.', $this->classOrAlias, get_debug_type($repository))); } - $user = $repository->loadUserByUsername($username); + // @deprecated since 5.3, change to $repository->loadUserByIdentifier() in 6.0 + if (method_exists($repository, 'loadUserByIdentifier')) { + $user = $repository->loadUserByIdentifier($identifier); + } else { + trigger_deprecation('symfony/doctrine-bridge', '5.3', 'Not implementing method "loadUserByIdentifier()" in user loader "%s" is deprecated. This method will replace "loadUserByUsername()" in Symfony 6.0.', get_debug_type($repository)); + + $user = $repository->loadUserByUsername($identifier); + } } if (null === $user) { - $e = new UsernameNotFoundException(sprintf('User "%s" not found.', $username)); - $e->setUsername($username); + $e = new UserNotFoundException(sprintf('User "%s" not found.', $identifier)); + $e->setUserIdentifier($identifier); throw $e; } @@ -96,8 +110,8 @@ public function refreshUser(UserInterface $user) $refreshedUser = $repository->find($id); if (null === $refreshedUser) { - $e = new UsernameNotFoundException('User with id '.json_encode($id).' not found.'); - $e->setUsername(json_encode($id)); + $e = new UserNotFoundException('User with id '.json_encode($id).' not found.'); + $e->setUserIdentifier(json_encode($id)); throw $e; } diff --git a/src/Symfony/Bridge/Doctrine/Security/User/UserLoaderInterface.php b/src/Symfony/Bridge/Doctrine/Security/User/UserLoaderInterface.php index d996f71702291..b190eb249967a 100644 --- a/src/Symfony/Bridge/Doctrine/Security/User/UserLoaderInterface.php +++ b/src/Symfony/Bridge/Doctrine/Security/User/UserLoaderInterface.php @@ -22,16 +22,11 @@ * * @see UserInterface * + * @method UserInterface|null loadUserByIdentifier(string $identifier) loads the user for the given user identifier (e.g. username or email). + * This method must return null if the user is not found. + * * @author Michal Trojanowski */ interface UserLoaderInterface { - /** - * Loads the user for the given username. - * - * This method must return null if the user is not found. - * - * @return UserInterface|null - */ - public function loadUserByUsername(string $username); } diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/BaseUser.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/BaseUser.php index 416d5b20bf7b1..f03157637b256 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/BaseUser.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/BaseUser.php @@ -37,4 +37,9 @@ public function getUsername(): string { return $this->username; } + + public function getUserIdentifier(): string + { + return $this->username; + } } diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/User.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/User.php index f4ff7c2840ce7..0ffaf19883361 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/User.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/User.php @@ -53,6 +53,11 @@ public function getUsername(): string return $this->name; } + public function getUserIdentifier(): string + { + return $this->name; + } + public function eraseCredentials() { } diff --git a/src/Symfony/Bridge/Doctrine/Tests/Security/User/EntityUserProviderTest.php b/src/Symfony/Bridge/Doctrine/Tests/Security/User/EntityUserProviderTest.php index 216eee0e3f89c..20d1e487a23d2 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Security/User/EntityUserProviderTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Security/User/EntityUserProviderTest.php @@ -21,7 +21,7 @@ use Symfony\Bridge\Doctrine\Security\User\UserLoaderInterface; use Symfony\Bridge\Doctrine\Tests\DoctrineTestHelper; use Symfony\Bridge\Doctrine\Tests\Fixtures\User; -use Symfony\Component\Security\Core\Exception\UsernameNotFoundException; +use Symfony\Component\Security\Core\Exception\UserNotFoundException; use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface; use Symfony\Component\Security\Core\User\PasswordUpgraderInterface; use Symfony\Component\Security\Core\User\UserInterface; @@ -60,7 +60,7 @@ public function testLoadUserByUsername() $provider = new EntityUserProvider($this->getManager($em), 'Symfony\Bridge\Doctrine\Tests\Fixtures\User', 'name'); - $this->assertSame($user, $provider->loadUserByUsername('user1')); + $this->assertSame($user, $provider->loadUserByIdentifier('user1')); } public function testLoadUserByUsernameWithUserLoaderRepositoryAndWithoutProperty() @@ -70,7 +70,7 @@ public function testLoadUserByUsernameWithUserLoaderRepositoryAndWithoutProperty $repository = $this->createMock(UserLoaderRepository::class); $repository ->expects($this->once()) - ->method('loadUserByUsername') + ->method('loadUserByIdentifier') ->with('user1') ->willReturn($user); @@ -82,7 +82,7 @@ public function testLoadUserByUsernameWithUserLoaderRepositoryAndWithoutProperty ->willReturn($repository); $provider = new EntityUserProvider($this->getManager($em), 'Symfony\Bridge\Doctrine\Tests\Fixtures\User'); - $this->assertSame($user, $provider->loadUserByUsername('user1')); + $this->assertSame($user, $provider->loadUserByIdentifier('user1')); } public function testLoadUserByUsernameWithNonUserLoaderRepositoryAndWithoutProperty() @@ -98,7 +98,7 @@ public function testLoadUserByUsernameWithNonUserLoaderRepositoryAndWithoutPrope $em->flush(); $provider = new EntityUserProvider($this->getManager($em), 'Symfony\Bridge\Doctrine\Tests\Fixtures\User'); - $provider->loadUserByUsername('user1'); + $provider->loadUserByIdentifier('user1'); } public function testRefreshUserRequiresId() @@ -126,7 +126,7 @@ public function testRefreshInvalidUser() $provider = new EntityUserProvider($this->getManager($em), 'Symfony\Bridge\Doctrine\Tests\Fixtures\User', 'name'); $user2 = new User(1, 2, 'user2'); - $this->expectException(UsernameNotFoundException::class); + $this->expectException(UserNotFoundException::class); $this->expectExceptionMessage('User with id {"id1":1,"id2":2} not found'); $provider->refreshUser($user2); @@ -153,7 +153,7 @@ public function testLoadUserByUserNameShouldLoadUserWhenProperInterfaceProvided( { $repository = $this->createMock(UserLoaderRepository::class); $repository->expects($this->once()) - ->method('loadUserByUsername') + ->method('loadUserByIdentifier') ->with('name') ->willReturn( $this->createMock(UserInterface::class) @@ -164,7 +164,7 @@ public function testLoadUserByUserNameShouldLoadUserWhenProperInterfaceProvided( 'Symfony\Bridge\Doctrine\Tests\Fixtures\User' ); - $provider->loadUserByUsername('name'); + $provider->loadUserByIdentifier('name'); } public function testLoadUserByUserNameShouldDeclineInvalidInterface() @@ -177,7 +177,7 @@ public function testLoadUserByUserNameShouldDeclineInvalidInterface() 'Symfony\Bridge\Doctrine\Tests\Fixtures\User' ); - $provider->loadUserByUsername('name'); + $provider->loadUserByIdentifier('name'); } public function testPasswordUpgrades() @@ -231,6 +231,7 @@ private function createSchema($em) abstract class UserLoaderRepository implements ObjectRepository, UserLoaderInterface { + abstract public function loadUserByIdentifier(string $identifier): ?UserInterface; } abstract class PasswordUpgraderRepository implements ObjectRepository, PasswordUpgraderInterface diff --git a/src/Symfony/Bridge/Monolog/Processor/AbstractTokenProcessor.php b/src/Symfony/Bridge/Monolog/Processor/AbstractTokenProcessor.php index ed37c94b81c00..15919978857c3 100644 --- a/src/Symfony/Bridge/Monolog/Processor/AbstractTokenProcessor.php +++ b/src/Symfony/Bridge/Monolog/Processor/AbstractTokenProcessor.php @@ -42,10 +42,16 @@ public function __invoke(array $record): array if (null !== $token = $this->getToken()) { $record['extra'][$this->getKey()] = [ - 'username' => $token->getUsername(), 'authenticated' => $token->isAuthenticated(), 'roles' => $token->getRoleNames(), ]; + + // @deprecated since 5.3, change to $token->getUserIdentifier() in 6.0 + if (method_exists($token, 'getUserIdentifier')) { + $record['extra'][$this->getKey()]['username'] = $record['extra'][$this->getKey()]['user_identifier'] = $token->getUserIdentifier(); + } else { + $record['extra'][$this->getKey()]['username'] = $token->getUsername(); + } } return $record; diff --git a/src/Symfony/Bridge/Monolog/Tests/Processor/SwitchUserTokenProcessorTest.php b/src/Symfony/Bridge/Monolog/Tests/Processor/SwitchUserTokenProcessorTest.php index 7107993b9c849..7d9aaede008c4 100644 --- a/src/Symfony/Bridge/Monolog/Tests/Processor/SwitchUserTokenProcessorTest.php +++ b/src/Symfony/Bridge/Monolog/Tests/Processor/SwitchUserTokenProcessorTest.php @@ -37,11 +37,15 @@ public function testProcessor() $expected = [ 'impersonator_token' => [ - 'username' => 'original_user', 'authenticated' => true, 'roles' => ['ROLE_SUPER_ADMIN'], + 'username' => 'original_user', ], ]; - $this->assertSame($expected, $record['extra']); + if (method_exists($originalToken, 'getUserIdentifier')) { + $expected['impersonator_token']['user_identifier'] = 'original_user'; + } + + $this->assertEquals($expected, $record['extra']); } } diff --git a/src/Symfony/Bridge/Monolog/Tests/Processor/TokenProcessorTest.php b/src/Symfony/Bridge/Monolog/Tests/Processor/TokenProcessorTest.php index dcaf0f647e301..b9fa51658e0d4 100644 --- a/src/Symfony/Bridge/Monolog/Tests/Processor/TokenProcessorTest.php +++ b/src/Symfony/Bridge/Monolog/Tests/Processor/TokenProcessorTest.php @@ -23,8 +23,12 @@ */ class TokenProcessorTest extends TestCase { - public function testProcessor() + public function testLegacyProcessor() { + if (method_exists(UsernamePasswordToken::class, 'getUserIdentifier')) { + $this->markTestSkipped('This test requires symfony/security-core <5.3'); + } + $token = new UsernamePasswordToken('user', 'password', 'provider', ['ROLE_USER']); $tokenStorage = $this->createMock(TokenStorageInterface::class); $tokenStorage->method('getToken')->willReturn($token); @@ -38,4 +42,24 @@ public function testProcessor() $this->assertEquals($token->isAuthenticated(), $record['extra']['token']['authenticated']); $this->assertEquals(['ROLE_USER'], $record['extra']['token']['roles']); } + + public function testProcessor() + { + if (!method_exists(UsernamePasswordToken::class, 'getUserIdentifier')) { + $this->markTestSkipped('This test requires symfony/security-core 5.3+'); + } + + $token = new UsernamePasswordToken('user', 'password', 'provider', ['ROLE_USER']); + $tokenStorage = $this->createMock(TokenStorageInterface::class); + $tokenStorage->method('getToken')->willReturn($token); + + $processor = new TokenProcessor($tokenStorage); + $record = ['extra' => []]; + $record = $processor($record); + + $this->assertArrayHasKey('token', $record['extra']); + $this->assertEquals($token->getUserIdentifier(), $record['extra']['token']['user_identifier']); + $this->assertEquals($token->isAuthenticated(), $record['extra']['token']['authenticated']); + $this->assertEquals(['ROLE_USER'], $record['extra']['token']['roles']); + } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Controller/SecurityController.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Controller/SecurityController.php index 6bf27e1ca2d9d..540a5b088a6b1 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Controller/SecurityController.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Controller/SecurityController.php @@ -21,6 +21,6 @@ class SecurityController implements ContainerAwareInterface public function profileAction() { - return new Response('Welcome '.$this->container->get('security.token_storage')->getToken()->getUsername().'!'); + return new Response('Welcome '.$this->container->get('security.token_storage')->getToken()->getUserIdentifier().'!'); } } diff --git a/src/Symfony/Bundle/SecurityBundle/DataCollector/SecurityDataCollector.php b/src/Symfony/Bundle/SecurityBundle/DataCollector/SecurityDataCollector.php index 9bd7c005757bb..e346d34800413 100644 --- a/src/Symfony/Bundle/SecurityBundle/DataCollector/SecurityDataCollector.php +++ b/src/Symfony/Bundle/SecurityBundle/DataCollector/SecurityDataCollector.php @@ -97,7 +97,9 @@ public function collect(Request $request, Response $response, \Throwable $except $impersonatorUser = null; if ($token instanceof SwitchUserToken) { - $impersonatorUser = $token->getOriginalToken()->getUsername(); + $originalToken = $token->getOriginalToken(); + // @deprecated since 5.3, change to $originalToken->getUserIdentifier() in 6.0 + $impersonatorUser = method_exists($originalToken, 'getUserIdentifier') ? $originalToken->getUserIdentifier() : $originalToken->getUsername(); } if (null !== $this->roleHierarchy) { @@ -126,7 +128,8 @@ public function collect(Request $request, Response $response, \Throwable $except 'token' => $token, 'token_class' => $this->hasVarDumper ? new ClassStub(\get_class($token)) : \get_class($token), 'logout_url' => $logoutUrl, - 'user' => $token->getUsername(), + // @deprecated since 5.3, change to $token->getUserIdentifier() in 6.0 + 'user' => method_exists($token, 'getUserIdentifier') ? $token->getUserIdentifier() : $token->getUsername(), 'roles' => $assignedRoles, 'inherited_roles' => array_unique($inheritedRoles), 'supports_role_hierarchy' => null !== $this->roleHierarchy, diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/UserProvider/InMemoryFactory.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/UserProvider/InMemoryFactory.php index a19a9eb163b3f..ceb04e340c8ea 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/UserProvider/InMemoryFactory.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/UserProvider/InMemoryFactory.php @@ -48,8 +48,30 @@ public function addConfiguration(NodeDefinition $node) ->fixXmlConfig('user') ->children() ->arrayNode('users') - ->useAttributeAsKey('name') + ->useAttributeAsKey('identifier') ->normalizeKeys(false) + ->beforeNormalization() + ->always() + ->then(function ($v) { + $deprecation = false; + foreach ($v as $i => $child) { + if (!isset($child['name'])) { + continue; + } + + $deprecation = true; + + $v[$i]['identifier'] = $child['name']; + unset($v[$i]['name']); + } + + if ($deprecation) { + trigger_deprecation('symfony/security-bundle', '5.3', 'The "in_memory.user.name" option is deprecated, use "identifier" instead.'); + } + + return $v; + }) + ->end() ->prototype('array') ->children() ->scalarNode('password')->defaultNull()->end() diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/config/schema/security-1.0.xsd b/src/Symfony/Bundle/SecurityBundle/Resources/config/schema/security-1.0.xsd index 2196017ae99a9..f7b9790f4a810 100644 --- a/src/Symfony/Bundle/SecurityBundle/Resources/config/schema/security-1.0.xsd +++ b/src/Symfony/Bundle/SecurityBundle/Resources/config/schema/security-1.0.xsd @@ -136,7 +136,8 @@ - + + diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTest.php index a3c0cd6ffb0cf..f192ee614cbb6 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTest.php @@ -297,7 +297,7 @@ public function testAccess() } elseif (3 === $i) { $this->assertEquals('IS_AUTHENTICATED_ANONYMOUSLY', $attributes[0]); $expression = $container->getDefinition((string) $attributes[1])->getArgument(0); - $this->assertEquals("token.getUsername() matches '/^admin/'", $expression); + $this->assertEquals("token.getUserIdentifier() matches '/^admin/'", $expression); } } } diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/container1.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/container1.php index f551131f00639..6118929a36f69 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/container1.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/container1.php @@ -97,7 +97,7 @@ 'access_control' => [ ['path' => '/blog/524', 'role' => 'ROLE_USER', 'requires_channel' => 'https', 'methods' => ['get', 'POST'], 'port' => 8000], ['path' => '/blog/.*', 'role' => 'IS_AUTHENTICATED_ANONYMOUSLY'], - ['path' => '/blog/524', 'role' => 'IS_AUTHENTICATED_ANONYMOUSLY', 'allow_if' => "token.getUsername() matches '/^admin/'"], + ['path' => '/blog/524', 'role' => 'IS_AUTHENTICATED_ANONYMOUSLY', 'allow_if' => "token.getUserIdentifier() matches '/^admin/'"], ], 'role_hierarchy' => [ diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/legacy_encoders.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/legacy_encoders.php index 3c9e6104eecc3..d6206527e6180 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/legacy_encoders.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/legacy_encoders.php @@ -97,7 +97,7 @@ 'access_control' => [ ['path' => '/blog/524', 'role' => 'ROLE_USER', 'requires_channel' => 'https', 'methods' => ['get', 'POST'], 'port' => 8000], ['path' => '/blog/.*', 'role' => 'IS_AUTHENTICATED_ANONYMOUSLY'], - ['path' => '/blog/524', 'role' => 'IS_AUTHENTICATED_ANONYMOUSLY', 'allow_if' => "token.getUsername() matches '/^admin/'"], + ['path' => '/blog/524', 'role' => 'IS_AUTHENTICATED_ANONYMOUSLY', 'allow_if' => "token.getUserIdentifier() matches '/^admin/'"], ], 'role_hierarchy' => [ diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/access_decision_manager_customized_config.xml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/access_decision_manager_customized_config.xml index b58028b2fbfe3..012c8dac7b069 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/access_decision_manager_customized_config.xml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/access_decision_manager_customized_config.xml @@ -12,7 +12,7 @@ - + diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/access_decision_manager_default_strategy.xml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/access_decision_manager_default_strategy.xml index 5bffea64f5bf5..1011f45c4accc 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/access_decision_manager_default_strategy.xml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/access_decision_manager_default_strategy.xml @@ -10,7 +10,7 @@ - + diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/access_decision_manager_service.xml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/access_decision_manager_service.xml index 9f9f9d5a34e27..ebc208c057168 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/access_decision_manager_service.xml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/access_decision_manager_service.xml @@ -12,7 +12,7 @@ - + diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/access_decision_manager_service_and_strategy.xml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/access_decision_manager_service_and_strategy.xml index 06ee3435e5a7f..1f2133ffe02f1 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/access_decision_manager_service_and_strategy.xml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/access_decision_manager_service_and_strategy.xml @@ -12,7 +12,7 @@ - + diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/container1.xml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/container1.xml index 097a726db58d2..ed7afe5e833ee 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/container1.xml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/container1.xml @@ -25,20 +25,20 @@ - + - + - - + + @@ -78,6 +78,6 @@ - + diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/legacy_encoders.xml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/legacy_encoders.xml index 84d68cc4fd59b..a362a59a15b80 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/legacy_encoders.xml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/legacy_encoders.xml @@ -25,20 +25,20 @@ - + - + - - + + @@ -78,6 +78,6 @@ - + diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/no_custom_user_checker.xml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/no_custom_user_checker.xml index 9dd035b7c47e3..3c545ecedc0be 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/no_custom_user_checker.xml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/no_custom_user_checker.xml @@ -10,7 +10,7 @@ - + diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/container1.yml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/container1.yml index 0ac2c94b0680b..3eb50b91b7370 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/container1.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/container1.yml @@ -84,4 +84,4 @@ security: - path: /blog/.* role: IS_AUTHENTICATED_ANONYMOUSLY - - { path: /blog/524, role: IS_AUTHENTICATED_ANONYMOUSLY, allow_if: "token.getUsername() matches '/^admin/'" } + - { path: /blog/524, role: IS_AUTHENTICATED_ANONYMOUSLY, allow_if: "token.getUserIdentifier() matches '/^admin/'" } diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/legacy_encoders.yml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/legacy_encoders.yml index 03b9aaf6ef5b9..d80a99afcfca3 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/legacy_encoders.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/legacy_encoders.yml @@ -84,4 +84,4 @@ security: - path: /blog/.* role: IS_AUTHENTICATED_ANONYMOUSLY - - { path: /blog/524, role: IS_AUTHENTICATED_ANONYMOUSLY, allow_if: "token.getUsername() matches '/^admin/'" } + - { path: /blog/524, role: IS_AUTHENTICATED_ANONYMOUSLY, allow_if: "token.getUserIdentifier() matches '/^admin/'" } diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/AuthenticatorBundle/ProfileController.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/AuthenticatorBundle/ProfileController.php index 3e23d86e37483..3e1598ea593cd 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/AuthenticatorBundle/ProfileController.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/AuthenticatorBundle/ProfileController.php @@ -19,6 +19,6 @@ public function __invoke() { $this->denyAccessUnlessGranted('ROLE_USER'); - return $this->json(['email' => $this->getUser()->getUsername()]); + return $this->json(['email' => $this->getUser()->getUserIdentifier()]); } } diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/CsrfFormLoginBundle/Resources/views/Login/after_login.html.twig b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/CsrfFormLoginBundle/Resources/views/Login/after_login.html.twig index a117cb94f8778..9a9bfbc731397 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/CsrfFormLoginBundle/Resources/views/Login/after_login.html.twig +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/CsrfFormLoginBundle/Resources/views/Login/after_login.html.twig @@ -1,7 +1,7 @@ {% extends "base.html.twig" %} {% block body %} - Hello {{ app.user.username }}!

+ Hello {{ app.user.userIdentifier }}!

You're browsing to path "{{ app.request.pathInfo }}".

Log out. Log out. diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Resources/views/Login/after_login.html.twig b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Resources/views/Login/after_login.html.twig index 3f88aae903536..fd51df2a4383f 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Resources/views/Login/after_login.html.twig +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Resources/views/Login/after_login.html.twig @@ -1,7 +1,7 @@ {% extends "base.html.twig" %} {% block body %} - Hello {{ user.username }}!

+ Hello {{ user.userIdentifier }}!

You're browsing to path "{{ app.request.pathInfo }}". Log out. diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/GuardedBundle/AuthenticationController.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/GuardedBundle/AuthenticationController.php index b8af5cee433a3..21a2ea9e4b8f6 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/GuardedBundle/AuthenticationController.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/GuardedBundle/AuthenticationController.php @@ -33,6 +33,6 @@ public function profileAction(UserInterface $user = null) return new Response('Not logged in.'); } - return new Response('Username: '.$user->getUsername()); + return new Response('Username: '.$user->getUserIdentifier()); } } diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/JsonLoginBundle/Controller/TestController.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/JsonLoginBundle/Controller/TestController.php index cba75a1526ace..6bd571d15e217 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/JsonLoginBundle/Controller/TestController.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/JsonLoginBundle/Controller/TestController.php @@ -21,6 +21,6 @@ class TestController { public function loginCheckAction(UserInterface $user) { - return new JsonResponse(['message' => sprintf('Welcome @%s!', $user->getUsername())]); + return new JsonResponse(['message' => sprintf('Welcome @%s!', $user->getUserIdentifier())]); } } diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/JsonLoginBundle/Security/Http/JsonAuthenticationSuccessHandler.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/JsonLoginBundle/Security/Http/JsonAuthenticationSuccessHandler.php index a0300d4d78387..4aabaacd4889c 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/JsonLoginBundle/Security/Http/JsonAuthenticationSuccessHandler.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/JsonLoginBundle/Security/Http/JsonAuthenticationSuccessHandler.php @@ -21,6 +21,6 @@ class JsonAuthenticationSuccessHandler implements AuthenticationSuccessHandlerIn { public function onAuthenticationSuccess(Request $request, TokenInterface $token): Response { - return new JsonResponse(['message' => sprintf('Good game @%s!', $token->getUsername())]); + return new JsonResponse(['message' => sprintf('Good game @%s!', $token->getUserIdentifier())]); } } diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/LoginLink/TestCustomLoginLinkSuccessHandler.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/LoginLink/TestCustomLoginLinkSuccessHandler.php index 329a288584a77..a20866c8cfd91 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/LoginLink/TestCustomLoginLinkSuccessHandler.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/LoginLink/TestCustomLoginLinkSuccessHandler.php @@ -11,6 +11,6 @@ class TestCustomLoginLinkSuccessHandler implements AuthenticationSuccessHandlerI { public function onAuthenticationSuccess(Request $request, TokenInterface $token) { - return new JsonResponse(['message' => sprintf('Welcome %s!', $token->getUsername())]); + return new JsonResponse(['message' => sprintf('Welcome %s!', $token->getUserIdentifier())]); } } diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/SecuredPageBundle/Security/Core/User/ArrayUserProvider.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/SecuredPageBundle/Security/Core/User/ArrayUserProvider.php index 43ea364a525db..a5ca99a41b6b7 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/SecuredPageBundle/Security/Core/User/ArrayUserProvider.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/SecuredPageBundle/Security/Core/User/ArrayUserProvider.php @@ -4,7 +4,7 @@ use Symfony\Bundle\SecurityBundle\Tests\Functional\UserWithoutEquatable; use Symfony\Component\Security\Core\Exception\UnsupportedUserException; -use Symfony\Component\Security\Core\Exception\UsernameNotFoundException; +use Symfony\Component\Security\Core\Exception\UserNotFoundException; use Symfony\Component\Security\Core\User\InMemoryUser; use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Core\User\UserProviderInterface; @@ -16,7 +16,7 @@ class ArrayUserProvider implements UserProviderInterface public function addUser(UserInterface $user) { - $this->users[$user->getUsername()] = $user; + $this->users[$user->getUserIdentifier()] = $user; } public function setUser($username, UserInterface $user) @@ -31,11 +31,16 @@ public function getUser($username) public function loadUserByUsername($username) { - $user = $this->getUser($username); + return $this->loadUserByIdentifier($username); + } + + public function loadUserByIdentifier(string $identifier): UserInterface + { + $user = $this->getUser($identifier); if (null === $user) { - $e = new UsernameNotFoundException(sprintf('User "%s" not found.', $username)); - $e->setUsername($username); + $e = new UserNotFoundException(sprintf('User "%s" not found.', $identifier)); + $e->setUsername($identifier); throw $e; } @@ -49,10 +54,10 @@ public function refreshUser(UserInterface $user) throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', get_debug_type($user))); } - $storedUser = $this->getUser($user->getUsername()); + $storedUser = $this->getUser($user->getUserIdentifier()); $class = \get_class($storedUser); - return new $class($storedUser->getUsername(), $storedUser->getPassword(), $storedUser->getRoles(), $storedUser->isEnabled()); + return new $class($storedUser->getUserIdentifier(), $storedUser->getPassword(), $storedUser->getRoles(), $storedUser->isEnabled()); } public function supportsClass($class) diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/ClearRememberMeTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/ClearRememberMeTest.php index a917e66c572c9..66a6676375436 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/ClearRememberMeTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/ClearRememberMeTest.php @@ -51,7 +51,7 @@ class RememberMeFooController { public function __invoke(UserInterface $user) { - return new Response($user->getUsername()); + return new Response($user->getUserIdentifier()); } } @@ -66,7 +66,12 @@ public function __construct(InMemoryUserProvider $inner) public function loadUserByUsername($username) { - return $this->inner->loadUserByUsername($username); + return $this->loadUserByIdentifier($username); + } + + public function loadUserByIdentifier(string $identifier): UserInterface + { + return $this->inner->loadUserByIdentifier($identifier); } public function refreshUser(UserInterface $user) diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/LoginLinkAuthenticationTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/LoginLinkAuthenticationTest.php index f45ec58055ffe..2c767349287d8 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/LoginLinkAuthenticationTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/LoginLinkAuthenticationTest.php @@ -28,7 +28,7 @@ public function testLoginLinkSuccess() $this->markTestSkipped('Login link auth requires symfony/security-http:^5.2'); } - $client = $this->createClient(['test_case' => 'LoginLink', 'root_config' => 'config.yml']); + $client = $this->createClient(['test_case' => 'LoginLink', 'root_config' => 'config.yml', 'debug' => true]); // we need an active request that is under the firewall to use the linker $request = Request::create('/get-login-link'); diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/SecurityTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/SecurityTest.php index e6f06fa2ccaf3..9aae384648fa7 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/SecurityTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/SecurityTest.php @@ -106,7 +106,7 @@ public function __construct(?string $username, ?string $password, array $roles = public function __toString() { - return $this->getUsername(); + return $this->getUserIdentifier(); } /** @@ -141,6 +141,11 @@ public function getUsername() return $this->username; } + public function getUserIdentifier() + { + return $this->username; + } + /** * {@inheritdoc} */ diff --git a/src/Symfony/Component/Ldap/Security/CheckLdapCredentialsListener.php b/src/Symfony/Component/Ldap/Security/CheckLdapCredentialsListener.php index e1b79443ac039..e32793c3d3bf4 100644 --- a/src/Symfony/Component/Ldap/Security/CheckLdapCredentialsListener.php +++ b/src/Symfony/Component/Ldap/Security/CheckLdapCredentialsListener.php @@ -83,7 +83,8 @@ public function onCheckPassport(CheckPassportEvent $event) } else { throw new LogicException('Using the "query_string" config without using a "search_dn" and a "search_password" is not supported.'); } - $username = $ldap->escape($user->getUsername(), '', LdapInterface::ESCAPE_FILTER); + // @deprecated since 5.3, change to $user->getUserIdentifier() in 6.0 + $username = $ldap->escape(method_exists($user, 'getUserIdentifier') ? $user->getUserIdentifier() : $user->getUsername(), '', LdapInterface::ESCAPE_FILTER); $query = str_replace('{username}', $username, $ldapBadge->getQueryString()); $result = $ldap->query($ldapBadge->getDnString(), $query)->execute(); if (1 !== $result->count()) { @@ -92,7 +93,8 @@ public function onCheckPassport(CheckPassportEvent $event) $dn = $result[0]->getDn(); } else { - $username = $ldap->escape($user->getUsername(), '', LdapInterface::ESCAPE_DN); + // @deprecated since 5.3, change to $user->getUserIdentifier() in 6.0 + $username = $ldap->escape(method_exists($user, 'getUserIdentifier') ? $user->getUserIdentifier() : $user->getUsername(), '', LdapInterface::ESCAPE_DN); $dn = str_replace('{username}', $username, $ldapBadge->getDnString()); } diff --git a/src/Symfony/Component/Ldap/Security/LdapUser.php b/src/Symfony/Component/Ldap/Security/LdapUser.php index 7c6a2c261c02e..86d6b0c4ad19c 100644 --- a/src/Symfony/Component/Ldap/Security/LdapUser.php +++ b/src/Symfony/Component/Ldap/Security/LdapUser.php @@ -75,6 +75,13 @@ public function getSalt(): ?string * {@inheritdoc} */ public function getUsername(): string + { + trigger_deprecation('symfony/security-core', '5.3', 'Method "%s()" is deprecated and will be removed in 6.0, use getUserIdentifier() instead.', __METHOD__); + + return $this->username; + } + + public function getUserIdentifier(): string { return $this->username; } diff --git a/src/Symfony/Component/Ldap/Security/LdapUserProvider.php b/src/Symfony/Component/Ldap/Security/LdapUserProvider.php index 6b0b4304ba8f6..319d6c605c43c 100644 --- a/src/Symfony/Component/Ldap/Security/LdapUserProvider.php +++ b/src/Symfony/Component/Ldap/Security/LdapUserProvider.php @@ -17,7 +17,7 @@ use Symfony\Component\Ldap\LdapInterface; use Symfony\Component\Security\Core\Exception\InvalidArgumentException; use Symfony\Component\Security\Core\Exception\UnsupportedUserException; -use Symfony\Component\Security\Core\Exception\UsernameNotFoundException; +use Symfony\Component\Security\Core\Exception\UserNotFoundException; use Symfony\Component\Security\Core\User\PasswordUpgraderInterface; use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Core\User\UserProviderInterface; @@ -48,7 +48,7 @@ public function __construct(LdapInterface $ldap, string $baseDn, string $searchD } if (null === $filter) { - $filter = '({uid_key}={username})'; + $filter = '({uid_key}={user_identifier})'; } $this->ldap = $ldap; @@ -66,15 +66,22 @@ public function __construct(LdapInterface $ldap, string $baseDn, string $searchD * {@inheritdoc} */ public function loadUserByUsername(string $username) + { + trigger_deprecation('symfony/ldap', '5.3', 'Method "%s()" is deprecated, use loadUserByIdentifier() instead.', __METHOD__); + + return $this->loadUserByIdentifier($username); + } + + public function loadUserByIdentifier(string $identifier): UserInterface { try { $this->ldap->bind($this->searchDn, $this->searchPassword); - $username = $this->ldap->escape($username, '', LdapInterface::ESCAPE_FILTER); - $query = str_replace('{username}', $username, $this->defaultSearch); + $identifier = $this->ldap->escape($identifier, '', LdapInterface::ESCAPE_FILTER); + $query = str_replace(['{username}', '{user_identifier}'], $identifier, $this->defaultSearch); $search = $this->ldap->query($this->baseDn, $query); } catch (ConnectionException $e) { - $e = new UsernameNotFoundException(sprintf('User "%s" not found.', $username), 0, $e); - $e->setUsername($username); + $e = new UserNotFoundException(sprintf('User "%s" not found.', $identifier), 0, $e); + $e->setUserIdentifier($identifier); throw $e; } @@ -83,15 +90,15 @@ public function loadUserByUsername(string $username) $count = \count($entries); if (!$count) { - $e = new UsernameNotFoundException(sprintf('User "%s" not found.', $username)); - $e->setUsername($username); + $e = new UserNotFoundException(sprintf('User "%s" not found.', $identifier)); + $e->setUserIdentifier($identifier); throw $e; } if ($count > 1) { - $e = new UsernameNotFoundException('More than one user found.'); - $e->setUsername($username); + $e = new UserNotFoundException('More than one user found.'); + $e->setUserIdentifier($identifier); throw $e; } @@ -100,12 +107,12 @@ public function loadUserByUsername(string $username) try { if (null !== $this->uidKey) { - $username = $this->getAttributeValue($entry, $this->uidKey); + $identifier = $this->getAttributeValue($entry, $this->uidKey); } } catch (InvalidArgumentException $e) { } - return $this->loadUser($username, $entry); + return $this->loadUser($identifier, $entry); } /** @@ -117,7 +124,7 @@ public function refreshUser(UserInterface $user) throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', get_debug_type($user))); } - return new LdapUser($user->getEntry(), $user->getUsername(), $user->getPassword(), $user->getRoles(), $user->getExtraFields()); + return new LdapUser($user->getEntry(), $user->getUserIdentifier(), $user->getPassword(), $user->getRoles(), $user->getExtraFields()); } /** @@ -157,7 +164,7 @@ public function supportsClass(string $class) * * @return UserInterface */ - protected function loadUser(string $username, Entry $entry) + protected function loadUser(string $identifier, Entry $entry) { $password = null; $extraFields = []; @@ -170,7 +177,7 @@ protected function loadUser(string $username, Entry $entry) $extraFields[$field] = $this->getAttributeValue($entry, $field); } - return new LdapUser($entry, $username, $password, $this->defaultRoles, $extraFields); + return new LdapUser($entry, $identifier, $password, $this->defaultRoles, $extraFields); } private function getAttributeValue(Entry $entry, string $attribute) diff --git a/src/Symfony/Component/Ldap/Tests/Security/LdapUserProviderTest.php b/src/Symfony/Component/Ldap/Tests/Security/LdapUserProviderTest.php index f6cd4c7414c51..32f6d60d5df3b 100644 --- a/src/Symfony/Component/Ldap/Tests/Security/LdapUserProviderTest.php +++ b/src/Symfony/Component/Ldap/Tests/Security/LdapUserProviderTest.php @@ -20,7 +20,7 @@ use Symfony\Component\Ldap\Security\LdapUser; use Symfony\Component\Ldap\Security\LdapUserProvider; use Symfony\Component\Security\Core\Exception\InvalidArgumentException; -use Symfony\Component\Security\Core\Exception\UsernameNotFoundException; +use Symfony\Component\Security\Core\Exception\UserNotFoundException; /** * @requires extension ldap @@ -29,7 +29,7 @@ class LdapUserProviderTest extends TestCase { public function testLoadUserByUsernameFailsIfCantConnectToLdap() { - $this->expectException(UsernameNotFoundException::class); + $this->expectException(UserNotFoundException::class); $ldap = $this->createMock(LdapInterface::class); $ldap @@ -39,12 +39,12 @@ public function testLoadUserByUsernameFailsIfCantConnectToLdap() ; $provider = new LdapUserProvider($ldap, 'ou=MyBusiness,dc=symfony,dc=com'); - $provider->loadUserByUsername('foo'); + $provider->loadUserByIdentifier('foo'); } public function testLoadUserByUsernameFailsIfNoLdapEntries() { - $this->expectException(UsernameNotFoundException::class); + $this->expectException(UserNotFoundException::class); $result = $this->createMock(CollectionInterface::class); $query = $this->createMock(QueryInterface::class); @@ -71,12 +71,12 @@ public function testLoadUserByUsernameFailsIfNoLdapEntries() ; $provider = new LdapUserProvider($ldap, 'ou=MyBusiness,dc=symfony,dc=com'); - $provider->loadUserByUsername('foo'); + $provider->loadUserByIdentifier('foo'); } public function testLoadUserByUsernameFailsIfMoreThanOneLdapEntry() { - $this->expectException(UsernameNotFoundException::class); + $this->expectException(UserNotFoundException::class); $result = $this->createMock(CollectionInterface::class); $query = $this->createMock(QueryInterface::class); @@ -103,7 +103,7 @@ public function testLoadUserByUsernameFailsIfMoreThanOneLdapEntry() ; $provider = new LdapUserProvider($ldap, 'ou=MyBusiness,dc=symfony,dc=com'); - $provider->loadUserByUsername('foo'); + $provider->loadUserByIdentifier('foo'); } public function testLoadUserByUsernameFailsIfMoreThanOneLdapPasswordsInEntry() @@ -144,7 +144,7 @@ public function testLoadUserByUsernameFailsIfMoreThanOneLdapPasswordsInEntry() ; $provider = new LdapUserProvider($ldap, 'ou=MyBusiness,dc=symfony,dc=com', null, null, [], 'sAMAccountName', '({uid_key}={username})', 'userpassword'); - $this->assertInstanceOf(LdapUser::class, $provider->loadUserByUsername('foo')); + $this->assertInstanceOf(LdapUser::class, $provider->loadUserByIdentifier('foo')); } public function testLoadUserByUsernameShouldNotFailIfEntryHasNoUidKeyAttribute() @@ -180,7 +180,7 @@ public function testLoadUserByUsernameShouldNotFailIfEntryHasNoUidKeyAttribute() ; $provider = new LdapUserProvider($ldap, 'ou=MyBusiness,dc=symfony,dc=com', null, null, [], 'sAMAccountName', '({uid_key}={username})'); - $this->assertInstanceOf(LdapUser::class, $provider->loadUserByUsername('foo')); + $this->assertInstanceOf(LdapUser::class, $provider->loadUserByIdentifier('foo')); } public function testLoadUserByUsernameFailsIfEntryHasNoPasswordAttribute() @@ -218,7 +218,7 @@ public function testLoadUserByUsernameFailsIfEntryHasNoPasswordAttribute() ; $provider = new LdapUserProvider($ldap, 'ou=MyBusiness,dc=symfony,dc=com', null, null, [], 'sAMAccountName', '({uid_key}={username})', 'userpassword'); - $this->assertInstanceOf(LdapUser::class, $provider->loadUserByUsername('foo')); + $this->assertInstanceOf(LdapUser::class, $provider->loadUserByIdentifier('foo')); } public function testLoadUserByUsernameIsSuccessfulWithoutPasswordAttribute() @@ -254,7 +254,7 @@ public function testLoadUserByUsernameIsSuccessfulWithoutPasswordAttribute() ; $provider = new LdapUserProvider($ldap, 'ou=MyBusiness,dc=symfony,dc=com'); - $this->assertInstanceOf(LdapUser::class, $provider->loadUserByUsername('foo')); + $this->assertInstanceOf(LdapUser::class, $provider->loadUserByIdentifier('foo')); } public function testLoadUserByUsernameIsSuccessfulWithoutPasswordAttributeAndWrongCase() @@ -290,7 +290,7 @@ public function testLoadUserByUsernameIsSuccessfulWithoutPasswordAttributeAndWro ; $provider = new LdapUserProvider($ldap, 'ou=MyBusiness,dc=symfony,dc=com'); - $this->assertSame('foo', $provider->loadUserByUsername('Foo')->getUsername()); + $this->assertSame('foo', $provider->loadUserByIdentifier('Foo')->getUserIdentifier()); } public function testLoadUserByUsernameIsSuccessfulWithPasswordAttribute() @@ -330,7 +330,7 @@ public function testLoadUserByUsernameIsSuccessfulWithPasswordAttribute() ; $provider = new LdapUserProvider($ldap, 'ou=MyBusiness,dc=symfony,dc=com', null, null, [], 'sAMAccountName', '({uid_key}={username})', 'userpassword', ['email']); - $this->assertInstanceOf(LdapUser::class, $provider->loadUserByUsername('foo')); + $this->assertInstanceOf(LdapUser::class, $provider->loadUserByIdentifier('foo')); } public function testRefreshUserShouldReturnUserWithSameProperties() diff --git a/src/Symfony/Component/PasswordHasher/Tests/Hasher/PasswordHasherFactoryTest.php b/src/Symfony/Component/PasswordHasher/Tests/Hasher/PasswordHasherFactoryTest.php index 46d430f51a8c4..633e38e39eef0 100644 --- a/src/Symfony/Component/PasswordHasher/Tests/Hasher/PasswordHasherFactoryTest.php +++ b/src/Symfony/Component/PasswordHasher/Tests/Hasher/PasswordHasherFactoryTest.php @@ -196,6 +196,10 @@ public function getUsername(): string { } + public function getUserIdentifier(): string + { + } + public function eraseCredentials() { } diff --git a/src/Symfony/Component/Security/CHANGELOG.md b/src/Symfony/Component/Security/CHANGELOG.md index d3f7ca0322823..adfd240992712 100644 --- a/src/Symfony/Component/Security/CHANGELOG.md +++ b/src/Symfony/Component/Security/CHANGELOG.md @@ -4,6 +4,11 @@ CHANGELOG 5.3 --- + * Deprecate `PersistentTokenInterface::getUsername()` in favor of `PersistentTokenInterface::getUserIdentifier()` + * Deprecate `UsernameNotFoundException` in favor of `UserNotFoundException` and `getUsername()`/`setUsername()` in favor of `getUserIdentifier()`/`setUserIdentifier()` + * Deprecate `UserProviderInterface::loadUserByUsername()` in favor of `UserProviderInterface::loadUserByIdentifier()` + * Deprecate `TokenInterface::getUsername()` in favor of `TokenInterface::getUserIdentifier()` + * Deprecate `UserInterface::getUsername()` in favor of `getUserIdentifier()` * Add `PassportInterface:getBadges()`, implemented by `PassportTrait` * [BC BREAK] Remove method `checkIfCompletelyResolved()` from `PassportInterface`, checking that passport badges are resolved is up to `AuthenticatorManager` diff --git a/src/Symfony/Component/Security/Core/Authentication/AuthenticationProviderManager.php b/src/Symfony/Component/Security/Core/Authentication/AuthenticationProviderManager.php index c4099603ef59f..ddf098306d4b3 100644 --- a/src/Symfony/Component/Security/Core/Authentication/AuthenticationProviderManager.php +++ b/src/Symfony/Component/Security/Core/Authentication/AuthenticationProviderManager.php @@ -21,6 +21,7 @@ use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Core\Exception\BadCredentialsException; use Symfony\Component\Security\Core\Exception\ProviderNotFoundException; +use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; // Help opcache.preload discover always-needed symbols @@ -105,6 +106,11 @@ public function authenticate(TokenInterface $token) $this->eventDispatcher->dispatch(new AuthenticationSuccessEvent($result), AuthenticationEvents::AUTHENTICATION_SUCCESS); } + // @deprecated since 5.3 + if ($user = $result->getUser() instanceof UserInterface && !method_exists($result->getUser(), 'getUserIdentifier')) { + trigger_deprecation('symfony/security-core', '5.3', 'Not implementing method "getUserIdentifier(): string" in user class "%s" is deprecated. This method will replace "getUsername()" in Symfony 6.0.', get_debug_type($result->getUser())); + } + return $result; } diff --git a/src/Symfony/Component/Security/Core/Authentication/Provider/DaoAuthenticationProvider.php b/src/Symfony/Component/Security/Core/Authentication/Provider/DaoAuthenticationProvider.php index eca9357f63a67..4ef55664dc513 100644 --- a/src/Symfony/Component/Security/Core/Authentication/Provider/DaoAuthenticationProvider.php +++ b/src/Symfony/Component/Security/Core/Authentication/Provider/DaoAuthenticationProvider.php @@ -16,7 +16,7 @@ use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface; use Symfony\Component\Security\Core\Exception\AuthenticationServiceException; use Symfony\Component\Security\Core\Exception\BadCredentialsException; -use Symfony\Component\Security\Core\Exception\UsernameNotFoundException; +use Symfony\Component\Security\Core\Exception\UserNotFoundException; use Symfony\Component\Security\Core\User\LegacyPasswordAuthenticatedUserInterface; use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface; use Symfony\Component\Security\Core\User\PasswordUpgraderInterface; @@ -108,7 +108,7 @@ protected function checkAuthentication(UserInterface $user, UsernamePasswordToke /** * {@inheritdoc} */ - protected function retrieveUser(string $username, UsernamePasswordToken $token) + protected function retrieveUser(string $userIdentifier, UsernamePasswordToken $token) { $user = $token->getUser(); if ($user instanceof UserInterface) { @@ -116,15 +116,22 @@ protected function retrieveUser(string $username, UsernamePasswordToken $token) } try { - $user = $this->userProvider->loadUserByUsername($username); + // @deprecated since 5.3, change to $this->userProvider->loadUserByIdentifier() in 6.0 + if (method_exists($this->userProvider, 'loadUserByIdentifier')) { + $user = $this->userProvider->loadUserByIdentifier($userIdentifier); + } else { + trigger_deprecation('symfony/security-core', '5.3', 'Not implementing method "loadUserByIdentifier()" in user provider "%s" is deprecated. This method will replace "loadUserByUsername()" in Symfony 6.0.', get_debug_type($this->userProvider)); + + $user = $this->userProvider->loadUserByUsername($userIdentifier); + } if (!$user instanceof UserInterface) { throw new AuthenticationServiceException('The user provider must return a UserInterface object.'); } return $user; - } catch (UsernameNotFoundException $e) { - $e->setUsername($username); + } catch (UserNotFoundException $e) { + $e->setUserIdentifier($userIdentifier); throw $e; } catch (\Exception $e) { $e = new AuthenticationServiceException($e->getMessage(), 0, $e); diff --git a/src/Symfony/Component/Security/Core/Authentication/Provider/LdapBindAuthenticationProvider.php b/src/Symfony/Component/Security/Core/Authentication/Provider/LdapBindAuthenticationProvider.php index 3705ae8289a19..e9a3ab029413f 100644 --- a/src/Symfony/Component/Security/Core/Authentication/Provider/LdapBindAuthenticationProvider.php +++ b/src/Symfony/Component/Security/Core/Authentication/Provider/LdapBindAuthenticationProvider.php @@ -16,7 +16,7 @@ use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; use Symfony\Component\Security\Core\Exception\BadCredentialsException; use Symfony\Component\Security\Core\Exception\LogicException; -use Symfony\Component\Security\Core\Exception\UsernameNotFoundException; +use Symfony\Component\Security\Core\Exception\UserNotFoundException; use Symfony\Component\Security\Core\User\UserCheckerInterface; use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Core\User\UserProviderInterface; @@ -38,7 +38,7 @@ class LdapBindAuthenticationProvider extends UserAuthenticationProvider private $searchDn; private $searchPassword; - public function __construct(UserProviderInterface $userProvider, UserCheckerInterface $userChecker, string $providerKey, LdapInterface $ldap, string $dnString = '{username}', bool $hideUserNotFoundExceptions = true, string $searchDn = '', string $searchPassword = '') + public function __construct(UserProviderInterface $userProvider, UserCheckerInterface $userChecker, string $providerKey, LdapInterface $ldap, string $dnString = '{user_identifier}', bool $hideUserNotFoundExceptions = true, string $searchDn = '', string $searchPassword = '') { parent::__construct($userChecker, $providerKey, $hideUserNotFoundExceptions); @@ -50,7 +50,7 @@ public function __construct(UserProviderInterface $userProvider, UserCheckerInte } /** - * Set a query string to use in order to find a DN for the username. + * Set a query string to use in order to find a DN for the user identifier. */ public function setQueryString(string $queryString) { @@ -60,13 +60,20 @@ public function setQueryString(string $queryString) /** * {@inheritdoc} */ - protected function retrieveUser(string $username, UsernamePasswordToken $token) + protected function retrieveUser(string $userIdentifier, UsernamePasswordToken $token) { - if (AuthenticationProviderInterface::USERNAME_NONE_PROVIDED === $username) { - throw new UsernameNotFoundException('Username can not be null.'); + if (AuthenticationProviderInterface::USERNAME_NONE_PROVIDED === $userIdentifier) { + throw new UserNotFoundException('User identifier can not be null.'); } - return $this->userProvider->loadUserByUsername($username); + // @deprecated since 5.3, change to $this->userProvider->loadUserByIdentifier() in 6.0 + if (method_exists($this->userProvider, 'loadUserByIdentifier')) { + return $this->userProvider->loadUserByIdentifier($userIdentifier); + } else { + trigger_deprecation('symfony/security-core', '5.3', 'Not implementing method "loadUserByIdentifier()" in user provider "%s" is deprecated. This method will replace "loadUserByUsername()" in Symfony 6.0.', get_debug_type($this->userProvider)); + + return $this->userProvider->loadUserByUsername($userIdentifier); + } } /** @@ -74,7 +81,8 @@ protected function retrieveUser(string $username, UsernamePasswordToken $token) */ protected function checkAuthentication(UserInterface $user, UsernamePasswordToken $token) { - $username = $token->getUsername(); + // @deprecated since 5.3, change to $token->getUserIdentifier() in 6.0 + $userIdentifier = method_exists($token, 'getUserIdentifier') ? $token->getUserIdentifier() : $token->getUsername(); $password = $token->getCredentials(); if ('' === (string) $password) { @@ -88,8 +96,8 @@ protected function checkAuthentication(UserInterface $user, UsernamePasswordToke } else { throw new LogicException('Using the "query_string" config without using a "search_dn" and a "search_password" is not supported.'); } - $username = $this->ldap->escape($username, '', LdapInterface::ESCAPE_FILTER); - $query = str_replace('{username}', $username, $this->queryString); + $userIdentifier = $this->ldap->escape($userIdentifier, '', LdapInterface::ESCAPE_FILTER); + $query = str_replace(['{username}', '{user_identifier}'], $userIdentifier, $this->queryString); $result = $this->ldap->query($this->dnString, $query)->execute(); if (1 !== $result->count()) { throw new BadCredentialsException('The presented username is invalid.'); @@ -97,8 +105,8 @@ protected function checkAuthentication(UserInterface $user, UsernamePasswordToke $dn = $result[0]->getDn(); } else { - $username = $this->ldap->escape($username, '', LdapInterface::ESCAPE_DN); - $dn = str_replace('{username}', $username, $this->dnString); + $userIdentifier = $this->ldap->escape($userIdentifier, '', LdapInterface::ESCAPE_DN); + $dn = str_replace(['{username}', '{user_identifier}'], $userIdentifier, $this->dnString); } $this->ldap->bind($dn, $password); diff --git a/src/Symfony/Component/Security/Core/Authentication/Provider/PreAuthenticatedAuthenticationProvider.php b/src/Symfony/Component/Security/Core/Authentication/Provider/PreAuthenticatedAuthenticationProvider.php index c0612bc0b61c4..292b8b9fed0f2 100644 --- a/src/Symfony/Component/Security/Core/Authentication/Provider/PreAuthenticatedAuthenticationProvider.php +++ b/src/Symfony/Component/Security/Core/Authentication/Provider/PreAuthenticatedAuthenticationProvider.php @@ -24,7 +24,7 @@ * This authentication provider will not perform any checks on authentication * requests, as they should already be pre-authenticated. However, the * UserProviderInterface implementation may still throw a - * UsernameNotFoundException, for example. + * UserNotFoundException, for example. * * @author Fabien Potencier */ @@ -54,7 +54,15 @@ public function authenticate(TokenInterface $token) throw new BadCredentialsException('No pre-authenticated principal found in request.'); } - $user = $this->userProvider->loadUserByUsername($user); + $userIdentifier = method_exists($token, 'getUserIdentifier') ? $token->getUserIdentifier() : $token->getUsername(); + // @deprecated since 5.3, change to $this->userProvider->loadUserByIdentifier() in 6.0 + if (method_exists($this->userProvider, 'loadUserByIdentifier')) { + $user = $this->userProvider->loadUserByIdentifier($userIdentifier); + } else { + trigger_deprecation('symfony/security-core', '5.3', 'Not implementing method "loadUserByIdentifier()" in user provider "%s" is deprecated. This method will replace "loadUserByUsername()" in Symfony 6.0.', get_debug_type($this->userProvider)); + + $user = $this->userProvider->loadUserByUsername($userIdentifier); + } $this->userChecker->checkPostAuth($user); diff --git a/src/Symfony/Component/Security/Core/Authentication/Provider/RememberMeAuthenticationProvider.php b/src/Symfony/Component/Security/Core/Authentication/Provider/RememberMeAuthenticationProvider.php index 630064af447ba..8ee8109b80170 100644 --- a/src/Symfony/Component/Security/Core/Authentication/Provider/RememberMeAuthenticationProvider.php +++ b/src/Symfony/Component/Security/Core/Authentication/Provider/RememberMeAuthenticationProvider.php @@ -51,7 +51,7 @@ public function authenticate(TokenInterface $token) $user = $token->getUser(); - if (!$token->getUser() instanceof UserInterface) { + if (!$user instanceof UserInterface) { throw new LogicException(sprintf('Method "%s::getUser()" must return a "%s" instance, "%s" returned.', get_debug_type($token), UserInterface::class, get_debug_type($user))); } diff --git a/src/Symfony/Component/Security/Core/Authentication/Provider/UserAuthenticationProvider.php b/src/Symfony/Component/Security/Core/Authentication/Provider/UserAuthenticationProvider.php index 21c1787ea9d52..4dfff685a7706 100644 --- a/src/Symfony/Component/Security/Core/Authentication/Provider/UserAuthenticationProvider.php +++ b/src/Symfony/Component/Security/Core/Authentication/Provider/UserAuthenticationProvider.php @@ -17,7 +17,7 @@ use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Core\Exception\AuthenticationServiceException; use Symfony\Component\Security\Core\Exception\BadCredentialsException; -use Symfony\Component\Security\Core\Exception\UsernameNotFoundException; +use Symfony\Component\Security\Core\Exception\UserNotFoundException; use Symfony\Component\Security\Core\User\UserCheckerInterface; use Symfony\Component\Security\Core\User\UserInterface; @@ -55,18 +55,18 @@ public function authenticate(TokenInterface $token) throw new AuthenticationException('The token is not supported by this authentication provider.'); } - $username = $token->getUsername(); + $username = method_exists($token, 'getUserIdentifier') ? $token->getUserIdentifier() : $token->getUsername(); if ('' === $username || null === $username) { $username = AuthenticationProviderInterface::USERNAME_NONE_PROVIDED; } try { $user = $this->retrieveUser($username, $token); - } catch (UsernameNotFoundException $e) { + } catch (UserNotFoundException $e) { if ($this->hideUserNotFoundExceptions) { throw new BadCredentialsException('Bad credentials.', 0, $e); } - $e->setUsername($username); + $e->setUserIdentifier($username); throw $e; } diff --git a/src/Symfony/Component/Security/Core/Authentication/RememberMe/InMemoryTokenProvider.php b/src/Symfony/Component/Security/Core/Authentication/RememberMe/InMemoryTokenProvider.php index a1b30443f3573..571bbe02ef716 100644 --- a/src/Symfony/Component/Security/Core/Authentication/RememberMe/InMemoryTokenProvider.php +++ b/src/Symfony/Component/Security/Core/Authentication/RememberMe/InMemoryTokenProvider.php @@ -45,7 +45,7 @@ public function updateToken(string $series, string $tokenValue, \DateTime $lastU $token = new PersistentToken( $this->tokens[$series]->getClass(), - $this->tokens[$series]->getUsername(), + method_exists($this->tokens[$series], 'getUserIdentifier') ? $this->tokens[$series]->getUserIdentifier() : $this->tokens[$series]->getUsername(), $series, $tokenValue, $lastUsed diff --git a/src/Symfony/Component/Security/Core/Authentication/RememberMe/PersistentToken.php b/src/Symfony/Component/Security/Core/Authentication/RememberMe/PersistentToken.php index 1f0e485c50ef2..b8337adff57f3 100644 --- a/src/Symfony/Component/Security/Core/Authentication/RememberMe/PersistentToken.php +++ b/src/Symfony/Component/Security/Core/Authentication/RememberMe/PersistentToken.php @@ -19,18 +19,18 @@ final class PersistentToken implements PersistentTokenInterface { private $class; - private $username; + private $userIdentifier; private $series; private $tokenValue; private $lastUsed; - public function __construct(string $class, string $username, string $series, string $tokenValue, \DateTime $lastUsed) + public function __construct(string $class, string $userIdentifier, string $series, string $tokenValue, \DateTime $lastUsed) { if (empty($class)) { throw new \InvalidArgumentException('$class must not be empty.'); } - if ('' === $username) { - throw new \InvalidArgumentException('$username must not be empty.'); + if ('' === $userIdentifier) { + throw new \InvalidArgumentException('$userIdentifier must not be empty.'); } if (empty($series)) { throw new \InvalidArgumentException('$series must not be empty.'); @@ -40,7 +40,7 @@ public function __construct(string $class, string $username, string $series, str } $this->class = $class; - $this->username = $username; + $this->userIdentifier = $userIdentifier; $this->series = $series; $this->tokenValue = $tokenValue; $this->lastUsed = $lastUsed; @@ -59,7 +59,14 @@ public function getClass(): string */ public function getUsername(): string { - return $this->username; + trigger_deprecation('symfony/security-core', '5.3', 'Method "%s()" is deprecated, use getUserIdentifier() instead.', __METHOD__); + + return $this->userIdentifier; + } + + public function getUserIdentifier(): string + { + return $this->userIdentifier; } /** diff --git a/src/Symfony/Component/Security/Core/Authentication/RememberMe/PersistentTokenInterface.php b/src/Symfony/Component/Security/Core/Authentication/RememberMe/PersistentTokenInterface.php index ba31ffa6d022e..85c5bc385e580 100644 --- a/src/Symfony/Component/Security/Core/Authentication/RememberMe/PersistentTokenInterface.php +++ b/src/Symfony/Component/Security/Core/Authentication/RememberMe/PersistentTokenInterface.php @@ -15,6 +15,8 @@ * Interface to be implemented by persistent token classes (such as * Doctrine entities representing a remember-me token). * + * @method string getUserIdentifier() returns the identifier used to authenticate (e.g. their e-mailaddress or username) + * * @author Johannes M. Schmitt */ interface PersistentTokenInterface @@ -26,13 +28,6 @@ interface PersistentTokenInterface */ public function getClass(); - /** - * Returns the username. - * - * @return string - */ - public function getUsername(); - /** * Returns the series. * diff --git a/src/Symfony/Component/Security/Core/Authentication/Token/AbstractToken.php b/src/Symfony/Component/Security/Core/Authentication/Token/AbstractToken.php index 0083ae3957728..b7934137e607f 100644 --- a/src/Symfony/Component/Security/Core/Authentication/Token/AbstractToken.php +++ b/src/Symfony/Component/Security/Core/Authentication/Token/AbstractToken.php @@ -51,10 +51,32 @@ public function getRoleNames(): array /** * {@inheritdoc} */ - public function getUsername() + public function getUsername(/* $legacy = true */) { + if (1 === func_num_args() && false === func_get_arg(0)) { + return null; + } + + trigger_deprecation('symfony/security-core', '5.3', 'Method "%s()" is deprecated, use getUserIdentifier() instead.', __METHOD__); + + if ($this->user instanceof UserInterface) { + return method_exists($this->user, 'getUserIdentifier') ? $this->user->getUserIdentifier() : $this->user->getUsername(); + } + + return (string) $this->user; + } + + public function getUserIdentifier(): string + { + // method returns "null" in non-legacy mode if not overriden + $username = $this->getUsername(false); + if (null !== $username) { + trigger_deprecation('symfony/security-core', '5.3', 'Method "%s::getUsername()" is deprecated, override "getUserIdentifier()" instead.', get_debug_type($this)); + } + if ($this->user instanceof UserInterface) { - return $this->user->getUsername(); + // @deprecated since 5.3, change to $user->getUserIdentifier() in 6.0 + return method_exists($this->user, 'getUserIdentifier') ? $this->user->getUserIdentifier() : $this->user->getUsername(); } return (string) $this->user; @@ -234,7 +256,7 @@ public function __toString() $roles[] = $role; } - return sprintf('%s(user="%s", authenticated=%s, roles="%s")', $class, $this->getUsername(), json_encode($this->authenticated), implode(', ', $roles)); + return sprintf('%s(user="%s", authenticated=%s, roles="%s")', $class, $this->getUserIdentifier(), json_encode($this->authenticated), implode(', ', $roles)); } /** @@ -283,7 +305,11 @@ private function hasUserChanged(UserInterface $user): bool return true; } - if ($this->user->getUsername() !== $user->getUsername()) { + // @deprecated since Symfony 5.3, drop getUsername() in 6.0 + $userIdentifier = function ($user) { + return method_exists($user, 'getUserIdentifier') ? $user->getUserIdentifier() : $user->getUsername(); + }; + if ($userIdentifier($this->user) !== $userIdentifier($user)) { return true; } diff --git a/src/Symfony/Component/Security/Core/Authentication/Token/NullToken.php b/src/Symfony/Component/Security/Core/Authentication/Token/NullToken.php index 589ad1b47f9c7..5c8a1c24b1e12 100644 --- a/src/Symfony/Component/Security/Core/Authentication/Token/NullToken.php +++ b/src/Symfony/Component/Security/Core/Authentication/Token/NullToken.php @@ -42,6 +42,13 @@ public function setUser($user) } public function getUsername() + { + trigger_deprecation('symfony/security-core', '5.3', 'Method "%s()" is deprecated, use getUserIdentifier() instead.', __METHOD__); + + return ''; + } + + public function getUserIdentifier(): string { return ''; } diff --git a/src/Symfony/Component/Security/Core/Authentication/Token/Storage/TokenStorage.php b/src/Symfony/Component/Security/Core/Authentication/Token/Storage/TokenStorage.php index 850c05e752672..1fc30bfcd1473 100644 --- a/src/Symfony/Component/Security/Core/Authentication/Token/Storage/TokenStorage.php +++ b/src/Symfony/Component/Security/Core/Authentication/Token/Storage/TokenStorage.php @@ -48,6 +48,11 @@ public function setToken(TokenInterface $token = null) if ($token) { // ensure any initializer is called $this->getToken(); + + // @deprecated since 5.3 + if (!method_exists($token, 'getUserIdentifier')) { + trigger_deprecation('symfony/security-core', '5.3', 'Not implementing method "getUserIdentifier(): string" in token class "%s" is deprecated. This method will replace "getUsername()" in Symfony 6.0.', get_debug_type($token)); + } } $this->initializer = null; diff --git a/src/Symfony/Component/Security/Core/Authentication/Token/TokenInterface.php b/src/Symfony/Component/Security/Core/Authentication/Token/TokenInterface.php index ad48ec6455f6a..047f571ae4a20 100644 --- a/src/Symfony/Component/Security/Core/Authentication/Token/TokenInterface.php +++ b/src/Symfony/Component/Security/Core/Authentication/Token/TokenInterface.php @@ -16,6 +16,8 @@ /** * TokenInterface is the interface for the user authentication information. * + * @method string getUserIdentifier() returns the user identifier used during authentication (e.g. a user's e-mailaddress or username) + * * @author Fabien Potencier * @author Johannes M. Schmitt */ @@ -65,13 +67,6 @@ public function getUser(); */ public function setUser($user); - /** - * Returns the username. - * - * @return string - */ - public function getUsername(); - /** * Returns whether the user is authenticated or not. * diff --git a/src/Symfony/Component/Security/Core/Exception/UserNotFoundException.php b/src/Symfony/Component/Security/Core/Exception/UserNotFoundException.php new file mode 100644 index 0000000000000..d730f7d7719f5 --- /dev/null +++ b/src/Symfony/Component/Security/Core/Exception/UserNotFoundException.php @@ -0,0 +1,96 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Security\Core\Exception; + +/** + * UserNotFoundException is thrown if a User cannot be found for the given identifier. + * + * @author Fabien Potencier + * @author Alexander + */ +class UserNotFoundException extends AuthenticationException +{ + private $identifier; + + /** + * {@inheritdoc} + */ + public function getMessageKey() + { + return 'Username could not be found.'; + } + + /** + * Get the user identifier (e.g. username or e-mailaddress). + */ + public function getUserIdentifier(): string + { + return $this->identifier; + } + + /** + * @return string + * + * @deprecated + */ + public function getUsername() + { + trigger_deprecation('symfony/security-core', '5.3', 'Method "%s()" is deprecated, use getUserIdentifier() instead.', __METHOD__); + + return $this->identifier; + } + + /** + * Set the user identifier (e.g. username or e-mailaddress). + */ + public function setUserIdentifier(string $identifier): void + { + $this->identifier = $identifier; + } + + /** + * @deprecated + */ + public function setUsername(string $username) + { + trigger_deprecation('symfony/security-core', '5.3', 'Method "%s()" is deprecated, use getUserIdentifier() instead.', __METHOD__); + + $this->identifier = $username; + } + + /** + * {@inheritdoc} + */ + public function getMessageData() + { + return ['{{ username }}' => $this->identifier, '{{ user_identifier }}' => $this->identifier]; + } + + /** + * {@inheritdoc} + */ + public function __serialize(): array + { + return [$this->identifier, parent::__serialize()]; + } + + /** + * {@inheritdoc} + */ + public function __unserialize(array $data): void + { + [$this->identifier, $parentData] = $data; + $parentData = \is_array($parentData) ? $parentData : unserialize($parentData); + parent::__unserialize($parentData); + } +} +class_alias(UserNotFoundException::class, UsernameNotFoundException::class); diff --git a/src/Symfony/Component/Security/Core/Exception/UsernameNotFoundException.php b/src/Symfony/Component/Security/Core/Exception/UsernameNotFoundException.php index f46013236c12a..e0d2d4a2ef113 100644 --- a/src/Symfony/Component/Security/Core/Exception/UsernameNotFoundException.php +++ b/src/Symfony/Component/Security/Core/Exception/UsernameNotFoundException.php @@ -11,65 +11,15 @@ namespace Symfony\Component\Security\Core\Exception; -/** - * UsernameNotFoundException is thrown if a User cannot be found by its username. - * - * @author Fabien Potencier - * @author Alexander - */ -class UsernameNotFoundException extends AuthenticationException -{ - private $username; - - /** - * {@inheritdoc} - */ - public function getMessageKey() - { - return 'Username could not be found.'; - } - - /** - * Get the username. - * - * @return string - */ - public function getUsername() - { - return $this->username; - } - - /** - * Set the username. - */ - public function setUsername(string $username) - { - $this->username = $username; - } +trigger_deprecation('symfony/security-core', '5.3', 'The "%s" class is deprecated, use "%s" instead.', UsernameNotFoundException::class, UserNotFoundException::class); - /** - * {@inheritdoc} - */ - public function getMessageData() - { - return ['{{ username }}' => $this->username]; - } - - /** - * {@inheritdoc} - */ - public function __serialize(): array - { - return [$this->username, parent::__serialize()]; - } +class_exists(UserNotFoundException::class); +if (false) { /** - * {@inheritdoc} + * @deprecated since Symfony 5.3 to be removed in 6.0, use UserNotFoundException instead. */ - public function __unserialize(array $data): void + class UsernameNotFoundException extends AuthenticationException { - [$this->username, $parentData] = $data; - $parentData = \is_array($parentData) ? $parentData : unserialize($parentData); - parent::__unserialize($parentData); } } diff --git a/src/Symfony/Component/Security/Core/Tests/Authentication/AuthenticationProviderManagerTest.php b/src/Symfony/Component/Security/Core/Tests/Authentication/AuthenticationProviderManagerTest.php index db1e388703bb6..d41805bffb371 100644 --- a/src/Symfony/Component/Security/Core/Tests/Authentication/AuthenticationProviderManagerTest.php +++ b/src/Symfony/Component/Security/Core/Tests/Authentication/AuthenticationProviderManagerTest.php @@ -23,6 +23,7 @@ use Symfony\Component\Security\Core\Exception\AccountStatusException; use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Core\Exception\ProviderNotFoundException; +use Symfony\Component\Security\Core\User\InMemoryUser; class AuthenticationProviderManagerTest extends TestCase { @@ -90,9 +91,12 @@ public function testAuthenticateWhenProviderReturnsAuthenticationException() public function testAuthenticateWhenOneReturnsAuthenticationExceptionButNotAll() { + $expected = $this->createMock(TokenInterface::class); + $expected->expects($this->any())->method('getUser')->willReturn(new InMemoryUser('wouter', null)); + $manager = new AuthenticationProviderManager([ $this->getAuthenticationProvider(true, null, AuthenticationException::class), - $this->getAuthenticationProvider(true, $expected = $this->createMock(TokenInterface::class)), + $this->getAuthenticationProvider(true, $expected), ]); $token = $manager->authenticate($this->createMock(TokenInterface::class)); @@ -106,8 +110,10 @@ public function testAuthenticateReturnsTokenOfTheFirstMatchingProvider() ->expects($this->never()) ->method('supports') ; + $expected = $this->createMock(TokenInterface::class); + $expected->expects($this->any())->method('getUser')->willReturn(new InMemoryUser('wouter', null)); $manager = new AuthenticationProviderManager([ - $this->getAuthenticationProvider(true, $expected = $this->createMock(TokenInterface::class)), + $this->getAuthenticationProvider(true, $expected), $second, ]); diff --git a/src/Symfony/Component/Security/Core/Tests/Authentication/AuthenticationTrustResolverTest.php b/src/Symfony/Component/Security/Core/Tests/Authentication/AuthenticationTrustResolverTest.php index cd1924c63edf6..adb14975bc949 100644 --- a/src/Symfony/Component/Security/Core/Tests/Authentication/AuthenticationTrustResolverTest.php +++ b/src/Symfony/Component/Security/Core/Tests/Authentication/AuthenticationTrustResolverTest.php @@ -155,6 +155,10 @@ public function getUsername(): string { } + public function getUserIdentifier(): string + { + } + public function isAuthenticated(): bool { } diff --git a/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/DaoAuthenticationProviderTest.php b/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/DaoAuthenticationProviderTest.php index 46b5624bb215e..05340afa8537d 100644 --- a/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/DaoAuthenticationProviderTest.php +++ b/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/DaoAuthenticationProviderTest.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Security\Core\Tests\Authentication\Provider; use PHPUnit\Framework\TestCase; +use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; use Symfony\Component\PasswordHasher\Hasher\PasswordHasherFactoryInterface; use Symfony\Component\PasswordHasher\Hasher\PlaintextPasswordHasher; use Symfony\Component\PasswordHasher\PasswordHasherInterface; @@ -20,8 +21,9 @@ use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface; use Symfony\Component\Security\Core\Exception\AuthenticationServiceException; use Symfony\Component\Security\Core\Exception\BadCredentialsException; -use Symfony\Component\Security\Core\Exception\UsernameNotFoundException; +use Symfony\Component\Security\Core\Exception\UserNotFoundException; use Symfony\Component\Security\Core\User\InMemoryUser; +use Symfony\Component\Security\Core\User\InMemoryUserProvider; use Symfony\Component\Security\Core\User\PasswordUpgraderInterface; use Symfony\Component\Security\Core\User\UserCheckerInterface; use Symfony\Component\Security\Core\User\UserInterface; @@ -29,13 +31,25 @@ class DaoAuthenticationProviderTest extends TestCase { + use ExpectDeprecationTrait; + + /** + * @group legacy + */ public function testRetrieveUserWhenProviderDoesNotReturnAnUserInterface() { $this->expectException(AuthenticationServiceException::class); - $provider = $this->getProvider('fabien'); + $userProvider = $this->createMock(DaoAuthenticationProviderTest_UserProvider::class); + $userProvider->expects($this->once()) + ->method('loadUserByUsername') + ->willReturn('fabien') + ; + $provider = $this->getProvider(null, null, null, $userProvider); $method = new \ReflectionMethod($provider, 'retrieveUser'); $method->setAccessible(true); + $this->expectDeprecation('Since symfony/security-core 5.3: Not implementing method "loadUserByIdentifier()" in user provider "'.get_debug_type($userProvider).'" is deprecated. This method will replace "loadUserByUsername()" in Symfony 6.0.'); + $method->invoke($provider, 'fabien', $this->getSupportedToken()); } @@ -44,12 +58,8 @@ public function testRetrieveUserWhenProviderDoesNotReturnAnUserInterface() */ public function testRetrieveUserWhenUsernameIsNotFoundWithLegacyEncoderFactory() { - $this->expectException(UsernameNotFoundException::class); - $userProvider = $this->createMock(UserProviderInterface::class); - $userProvider->expects($this->once()) - ->method('loadUserByUsername') - ->willThrowException(new UsernameNotFoundException()) - ; + $this->expectException(UserNotFoundException::class); + $userProvider = new InMemoryUserProvider(); $provider = new DaoAuthenticationProvider($userProvider, $this->createMock(UserCheckerInterface::class), 'key', $this->createMock(EncoderFactoryInterface::class)); $method = new \ReflectionMethod($provider, 'retrieveUser'); @@ -60,12 +70,8 @@ public function testRetrieveUserWhenUsernameIsNotFoundWithLegacyEncoderFactory() public function testRetrieveUserWhenUsernameIsNotFound() { - $this->expectException(UsernameNotFoundException::class); - $userProvider = $this->createMock(UserProviderInterface::class); - $userProvider->expects($this->once()) - ->method('loadUserByUsername') - ->willThrowException(new UsernameNotFoundException()) - ; + $this->expectException(UserNotFoundException::class); + $userProvider = new InMemoryUserProvider(); $provider = new DaoAuthenticationProvider($userProvider, $this->createMock(UserCheckerInterface::class), 'key', $this->createMock(PasswordHasherFactoryInterface::class)); $method = new \ReflectionMethod($provider, 'retrieveUser'); @@ -77,9 +83,9 @@ public function testRetrieveUserWhenUsernameIsNotFound() public function testRetrieveUserWhenAnExceptionOccurs() { $this->expectException(AuthenticationServiceException::class); - $userProvider = $this->createMock(UserProviderInterface::class); + $userProvider = $this->createMock(InMemoryUserProvider::class); $userProvider->expects($this->once()) - ->method('loadUserByUsername') + ->method('loadUserByIdentifier') ->willThrowException(new \RuntimeException()) ; @@ -92,9 +98,9 @@ public function testRetrieveUserWhenAnExceptionOccurs() public function testRetrieveUserReturnsUserFromTokenOnReauthentication() { - $userProvider = $this->createMock(UserProviderInterface::class); + $userProvider = $this->createMock(InMemoryUserProvider::class); $userProvider->expects($this->never()) - ->method('loadUserByUsername') + ->method('loadUserByIdentifier') ; $user = new TestUser(); @@ -114,19 +120,13 @@ public function testRetrieveUserReturnsUserFromTokenOnReauthentication() public function testRetrieveUser() { - $user = new TestUser(); - - $userProvider = $this->createMock(UserProviderInterface::class); - $userProvider->expects($this->once()) - ->method('loadUserByUsername') - ->willReturn($user) - ; + $userProvider = new InMemoryUserProvider(['fabien' => []]); $provider = new DaoAuthenticationProvider($userProvider, $this->createMock(UserCheckerInterface::class), 'key', $this->createMock(PasswordHasherFactoryInterface::class)); $method = new \ReflectionMethod($provider, 'retrieveUser'); $method->setAccessible(true); - $this->assertSame($user, $method->invoke($provider, 'fabien', $this->getSupportedToken())); + $this->assertEquals('fabien', $method->invoke($provider, 'fabien', $this->getSupportedToken())->getUserIdentifier()); } public function testCheckAuthenticationWhenCredentialsAreEmpty() @@ -323,14 +323,16 @@ protected function getSupportedToken() return $mock; } - protected function getProvider($user = null, $userChecker = null, $passwordHasher = null) + protected function getProvider($user = null, $userChecker = null, $passwordHasher = null, $userProvider = null) { - $userProvider = $this->createMock(PasswordUpgraderProvider::class); - if (null !== $user) { - $userProvider->expects($this->once()) - ->method('loadUserByUsername') - ->willReturn($user) - ; + if (null === $userProvider) { + $userProvider = $this->createMock(PasswordUpgraderProvider::class); + if (null !== $user) { + $userProvider->expects($this->once()) + ->method('loadUserByIdentifier') + ->willReturn($user) + ; + } } if (null === $userChecker) { @@ -374,6 +376,11 @@ public function getUsername(): string return 'jane_doe'; } + public function getUserIdentifier(): string + { + return 'jane_doe'; + } + public function eraseCredentials() { } @@ -381,4 +388,10 @@ public function eraseCredentials() interface PasswordUpgraderProvider extends UserProviderInterface, PasswordUpgraderInterface { public function upgradePassword(UserInterface $user, string $newHashedPassword): void; + public function loadUserByIdentifier(string $identifier): UserInterface; +} + +interface DaoAuthenticationProviderTest_UserProvider extends UserProviderInterface +{ + public function loadUserByUsername($username); } diff --git a/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/LdapBindAuthenticationProviderTest.php b/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/LdapBindAuthenticationProviderTest.php index c47508449ffad..4507e6a9cfc72 100644 --- a/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/LdapBindAuthenticationProviderTest.php +++ b/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/LdapBindAuthenticationProviderTest.php @@ -21,6 +21,7 @@ use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; use Symfony\Component\Security\Core\Exception\BadCredentialsException; use Symfony\Component\Security\Core\User\InMemoryUser; +use Symfony\Component\Security\Core\User\InMemoryUserProvider; use Symfony\Component\Security\Core\User\UserCheckerInterface; use Symfony\Component\Security\Core\User\UserProviderInterface; @@ -81,10 +82,10 @@ public function testBindFailureShouldThrowAnException() public function testRetrieveUser() { - $userProvider = $this->createMock(UserProviderInterface::class); + $userProvider = $this->createMock(InMemoryUserProvider::class); $userProvider ->expects($this->once()) - ->method('loadUserByUsername') + ->method('loadUserByIdentifier') ->with('foo') ; $ldap = $this->createMock(LdapInterface::class); diff --git a/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/PreAuthenticatedAuthenticationProviderTest.php b/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/PreAuthenticatedAuthenticationProviderTest.php index a0d6041327f73..15c079b8c398e 100644 --- a/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/PreAuthenticatedAuthenticationProviderTest.php +++ b/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/PreAuthenticatedAuthenticationProviderTest.php @@ -18,6 +18,7 @@ use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Core\Exception\BadCredentialsException; use Symfony\Component\Security\Core\Exception\LockedException; +use Symfony\Component\Security\Core\User\InMemoryUserProvider; use Symfony\Component\Security\Core\User\UserCheckerInterface; use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Core\User\UserProviderInterface; @@ -120,10 +121,10 @@ protected function getSupportedToken($user = false, $credentials = false) protected function getProvider($user = null, $userChecker = null) { - $userProvider = $this->createMock(UserProviderInterface::class); + $userProvider = $this->createMock(InMemoryUserProvider::class); if (null !== $user) { $userProvider->expects($this->once()) - ->method('loadUserByUsername') + ->method('loadUserByIdentifier') ->willReturn($user) ; } diff --git a/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/UserAuthenticationProviderTest.php b/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/UserAuthenticationProviderTest.php index f006d37ed1818..92b71448d5c7d 100644 --- a/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/UserAuthenticationProviderTest.php +++ b/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/UserAuthenticationProviderTest.php @@ -21,7 +21,8 @@ use Symfony\Component\Security\Core\Exception\AuthenticationServiceException; use Symfony\Component\Security\Core\Exception\BadCredentialsException; use Symfony\Component\Security\Core\Exception\CredentialsExpiredException; -use Symfony\Component\Security\Core\Exception\UsernameNotFoundException; +use Symfony\Component\Security\Core\Exception\UserNotFoundException; +use Symfony\Component\Security\Core\User\InMemoryUser; use Symfony\Component\Security\Core\User\UserCheckerInterface; use Symfony\Component\Security\Core\User\UserInterface; @@ -46,11 +47,11 @@ public function testAuthenticateWhenTokenIsNotSupported() public function testAuthenticateWhenUsernameIsNotFound() { - $this->expectException(UsernameNotFoundException::class); + $this->expectException(UserNotFoundException::class); $provider = $this->getProvider(false, false); $provider->expects($this->once()) ->method('retrieveUser') - ->willThrowException(new UsernameNotFoundException()) + ->willThrowException(new UserNotFoundException()) ; $provider->authenticate($this->getSupportedToken()); @@ -62,7 +63,7 @@ public function testAuthenticateWhenUsernameIsNotFoundAndHideIsTrue() $provider = $this->getProvider(false, true); $provider->expects($this->once()) ->method('retrieveUser') - ->willThrowException(new UsernameNotFoundException()) + ->willThrowException(new UserNotFoundException()) ; $provider->authenticate($this->getSupportedToken()); @@ -194,7 +195,7 @@ public function testAuthenticatePreservesOriginalToken() ; $originalToken = $this->createMock(TokenInterface::class); - $token = new SwitchUserToken($this->createMock(UserInterface::class), 'foo', 'key', [], $originalToken); + $token = new SwitchUserToken(new InMemoryUser('wouter', null), 'foo', 'key', [], $originalToken); $token->setAttributes(['foo' => 'bar']); $authToken = $provider->authenticate($token); diff --git a/src/Symfony/Component/Security/Core/Tests/Authentication/RememberMe/PersistentTokenTest.php b/src/Symfony/Component/Security/Core/Tests/Authentication/RememberMe/PersistentTokenTest.php index 12c133f52df57..9df545a4c0c6e 100644 --- a/src/Symfony/Component/Security/Core/Tests/Authentication/RememberMe/PersistentTokenTest.php +++ b/src/Symfony/Component/Security/Core/Tests/Authentication/RememberMe/PersistentTokenTest.php @@ -12,19 +12,33 @@ namespace Symfony\Component\Security\Core\Tests\Authentication\RememberMe; use PHPUnit\Framework\TestCase; +use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; use Symfony\Component\Security\Core\Authentication\RememberMe\PersistentToken; class PersistentTokenTest extends TestCase { + use ExpectDeprecationTrait; + public function testConstructor() { $lastUsed = new \DateTime(); $token = new PersistentToken('fooclass', 'fooname', 'fooseries', 'footokenvalue', $lastUsed); $this->assertEquals('fooclass', $token->getClass()); - $this->assertEquals('fooname', $token->getUsername()); + $this->assertEquals('fooname', $token->getUserIdentifier()); $this->assertEquals('fooseries', $token->getSeries()); $this->assertEquals('footokenvalue', $token->getTokenValue()); $this->assertSame($lastUsed, $token->getLastUsed()); } + + /** + * @group legacy + */ + public function testLegacyGetUsername() + { + $token = new PersistentToken('fooclass', 'fooname', 'fooseries', 'footokenvalue', new \DateTime()); + + $this->expectDeprecation('Since symfony/security-core 5.3: Method "Symfony\Component\Security\Core\Authentication\RememberMe\PersistentToken::getUsername()" is deprecated, use getUserIdentifier() instead.'); + $this->assertEquals('fooname', $token->getUsername()); + } } diff --git a/src/Symfony/Component/Security/Core/Tests/Authentication/Token/AbstractTokenTest.php b/src/Symfony/Component/Security/Core/Tests/Authentication/Token/AbstractTokenTest.php index 98f84e1f1e077..dcf479c84e710 100644 --- a/src/Symfony/Component/Security/Core/Tests/Authentication/Token/AbstractTokenTest.php +++ b/src/Symfony/Component/Security/Core/Tests/Authentication/Token/AbstractTokenTest.php @@ -12,12 +12,19 @@ namespace Symfony\Component\Security\Core\Tests\Authentication\Token; use PHPUnit\Framework\TestCase; +use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; use Symfony\Component\Security\Core\Authentication\Token\AbstractToken; +use Symfony\Component\Security\Core\User\InMemoryUser; use Symfony\Component\Security\Core\User\UserInterface; class AbstractTokenTest extends TestCase { - public function testGetUsername() + use ExpectDeprecationTrait; + + /** + * @group legacy + */ + public function testLegacyGetUsername() { $token = new ConcreteToken(['ROLE_FOO']); $token->setUser('fabien'); @@ -26,10 +33,43 @@ public function testGetUsername() $token->setUser(new TestUser('fabien')); $this->assertEquals('fabien', $token->getUsername()); - $user = $this->createMock(UserInterface::class); - $user->expects($this->once())->method('getUsername')->willReturn('fabien'); - $token->setUser($user); + $legacyUser = new class implements UserInterface { + public function getUsername() + { + return 'fabien'; + } + + public function getRoles() + {} + + public function getPassword() + {} + + public function getSalt() + {} + + public function eraseCredentials() + {} + }; + $token->setUser($legacyUser); $this->assertEquals('fabien', $token->getUsername()); + + $token->setUser($legacyUser); + $this->assertEquals('fabien', $token->getUserIdentifier()); + } + + public function testGetUserIdentifier() + { + $token = new ConcreteToken(['ROLE_FOO']); + $token->setUser('fabien'); + $this->assertEquals('fabien', $token->getUserIdentifier()); + + $token->setUser(new TestUser('fabien')); + $this->assertEquals('fabien', $token->getUserIdentifier()); + + $user = new InMemoryUser('fabien', null); + $token->setUser($user); + $this->assertEquals('fabien', $token->getUserIdentifier()); } public function testEraseCredentials() @@ -106,10 +146,8 @@ public function testSetUser($user) public function getUsers() { - $user = $this->createMock(UserInterface::class); - return [ - [$user], + [new InMemoryUser('foo', null)], [new TestUser('foo')], ['foo'], ]; @@ -210,6 +248,11 @@ public function getUsername() return $this->name; } + public function getUserIdentifier() + { + return $this->name; + } + public function getPassword() { return '***'; diff --git a/src/Symfony/Component/Security/Core/Tests/Authentication/Token/Storage/UsageTrackingTokenStorageTest.php b/src/Symfony/Component/Security/Core/Tests/Authentication/Token/Storage/UsageTrackingTokenStorageTest.php index c5d2eaf543203..38806efa8a20d 100644 --- a/src/Symfony/Component/Security/Core/Tests/Authentication/Token/Storage/UsageTrackingTokenStorageTest.php +++ b/src/Symfony/Component/Security/Core/Tests/Authentication/Token/Storage/UsageTrackingTokenStorageTest.php @@ -16,6 +16,7 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\HttpFoundation\Session\SessionInterface; +use Symfony\Component\Security\Core\Authentication\Token\NullToken; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage; use Symfony\Component\Security\Core\Authentication\Token\Storage\UsageTrackingTokenStorage; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; @@ -46,7 +47,7 @@ public function testGetSetToken() $trackingStorage = new UsageTrackingTokenStorage($tokenStorage, $sessionLocator); $this->assertNull($trackingStorage->getToken()); - $token = $this->createMock(TokenInterface::class); + $token = new NullToken(); $trackingStorage->setToken($token); $this->assertSame($token, $trackingStorage->getToken()); diff --git a/src/Symfony/Component/Security/Core/Tests/Authentication/Token/SwitchUserTokenTest.php b/src/Symfony/Component/Security/Core/Tests/Authentication/Token/SwitchUserTokenTest.php index 8138f7659639b..477247e76da4f 100644 --- a/src/Symfony/Component/Security/Core/Tests/Authentication/Token/SwitchUserTokenTest.php +++ b/src/Symfony/Component/Security/Core/Tests/Authentication/Token/SwitchUserTokenTest.php @@ -26,7 +26,7 @@ public function testSerialize() $unserializedToken = unserialize(serialize($token)); $this->assertInstanceOf(SwitchUserToken::class, $unserializedToken); - $this->assertSame('admin', $unserializedToken->getUsername()); + $this->assertSame('admin', $unserializedToken->getUserIdentifier()); $this->assertSame('bar', $unserializedToken->getCredentials()); $this->assertSame('provider-key', $unserializedToken->getFirewallName()); $this->assertEquals(['ROLE_USER'], $unserializedToken->getRoleNames()); @@ -35,7 +35,7 @@ public function testSerialize() $unserializedOriginalToken = $unserializedToken->getOriginalToken(); $this->assertInstanceOf(UsernamePasswordToken::class, $unserializedOriginalToken); - $this->assertSame('user', $unserializedOriginalToken->getUsername()); + $this->assertSame('user', $unserializedOriginalToken->getUserIdentifier()); $this->assertSame('foo', $unserializedOriginalToken->getCredentials()); $this->assertSame('provider-key', $unserializedOriginalToken->getFirewallName()); $this->assertEquals(['ROLE_ADMIN', 'ROLE_ALLOWED_TO_SWITCH'], $unserializedOriginalToken->getRoleNames()); @@ -49,6 +49,11 @@ public function getUsername() return 'impersonated'; } + public function getUserIdentifier() + { + return 'impersonated'; + } + public function getPassword() { return null; @@ -92,7 +97,7 @@ public function testUnserializeOldToken() self::assertInstanceOf(SwitchUserToken::class, $token); self::assertInstanceOf(UsernamePasswordToken::class, $token->getOriginalToken()); - self::assertSame('john', $token->getUsername()); + self::assertSame('john', $token->getUserIdentifier()); self::assertSame(['foo' => 'bar'], $token->getCredentials()); self::assertSame('main', $token->getFirewallName()); self::assertEquals(['ROLE_USER'], $token->getRoleNames()); diff --git a/src/Symfony/Component/Security/Core/Tests/Encoder/EncoderFactoryTest.php b/src/Symfony/Component/Security/Core/Tests/Encoder/EncoderFactoryTest.php index 7b79986b826a6..3744e05bc0efa 100644 --- a/src/Symfony/Component/Security/Core/Tests/Encoder/EncoderFactoryTest.php +++ b/src/Symfony/Component/Security/Core/Tests/Encoder/EncoderFactoryTest.php @@ -213,6 +213,10 @@ public function getUsername(): string { } + public function getUserIdentifier(): string + { + } + public function eraseCredentials() { } diff --git a/src/Symfony/Component/Security/Core/Tests/Exception/UserNotFoundExceptionTest.php b/src/Symfony/Component/Security/Core/Tests/Exception/UserNotFoundExceptionTest.php new file mode 100644 index 0000000000000..559e62acd97d0 --- /dev/null +++ b/src/Symfony/Component/Security/Core/Tests/Exception/UserNotFoundExceptionTest.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Security\Core\Tests\Exception; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Security\Core\Exception\UserNotFoundException; +use Symfony\Component\Security\Core\Exception\UsernameNotFoundException; + +class UserNotFoundExceptionTest extends TestCase +{ + public function testGetMessageData() + { + $exception = new UserNotFoundException('Username could not be found.'); + $this->assertEquals(['{{ username }}' => null, '{{ user_identifier }}' => null], $exception->getMessageData()); + $exception->setUserIdentifier('username'); + $this->assertEquals(['{{ username }}' => 'username', '{{ user_identifier }}' => 'username'], $exception->getMessageData()); + } + + /** + * @group legacy + */ + public function testUsernameNotFoundException() + { + $exception = new UsernameNotFoundException(); + $this->assertInstanceOf(UserNotFoundException::class, $exception); + + $exception->setUsername('username'); + $this->assertEquals('username', $exception->getUserIdentifier()); + } +} diff --git a/src/Symfony/Component/Security/Core/Tests/Exception/UsernameNotFoundExceptionTest.php b/src/Symfony/Component/Security/Core/Tests/Exception/UsernameNotFoundExceptionTest.php deleted file mode 100644 index 8e256aac2ea11..0000000000000 --- a/src/Symfony/Component/Security/Core/Tests/Exception/UsernameNotFoundExceptionTest.php +++ /dev/null @@ -1,26 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Core\Tests\Exception; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\Security\Core\Exception\UsernameNotFoundException; - -class UsernameNotFoundExceptionTest extends TestCase -{ - public function testGetMessageData() - { - $exception = new UsernameNotFoundException('Username could not be found.'); - $this->assertEquals(['{{ username }}' => null], $exception->getMessageData()); - $exception->setUsername('username'); - $this->assertEquals(['{{ username }}' => 'username'], $exception->getMessageData()); - } -} diff --git a/src/Symfony/Component/Security/Core/Tests/User/ChainUserProviderTest.php b/src/Symfony/Component/Security/Core/Tests/User/ChainUserProviderTest.php index 74d0cc138ce6f..5a4770064fc2e 100644 --- a/src/Symfony/Component/Security/Core/Tests/User/ChainUserProviderTest.php +++ b/src/Symfony/Component/Security/Core/Tests/User/ChainUserProviderTest.php @@ -13,9 +13,10 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\Security\Core\Exception\UnsupportedUserException; -use Symfony\Component\Security\Core\Exception\UsernameNotFoundException; +use Symfony\Component\Security\Core\Exception\UserNotFoundException; use Symfony\Component\Security\Core\User\ChainUserProvider; use Symfony\Component\Security\Core\User\InMemoryUser; +use Symfony\Component\Security\Core\User\InMemoryUserProvider; use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface; use Symfony\Component\Security\Core\User\PasswordUpgraderInterface; use Symfony\Component\Security\Core\User\UserInterface; @@ -25,59 +26,59 @@ class ChainUserProviderTest extends TestCase { public function testLoadUserByUsername() { - $provider1 = $this->createMock(UserProviderInterface::class); + $provider1 = $this->createMock(InMemoryUserProvider::class); $provider1 ->expects($this->once()) - ->method('loadUserByUsername') + ->method('loadUserByIdentifier') ->with($this->equalTo('foo')) - ->willThrowException(new UsernameNotFoundException('not found')) + ->willThrowException(new UserNotFoundException('not found')) ; - $provider2 = $this->createMock(UserProviderInterface::class); + $provider2 = $this->createMock(InMemoryUserProvider::class); $provider2 ->expects($this->once()) - ->method('loadUserByUsername') + ->method('loadUserByIdentifier') ->with($this->equalTo('foo')) ->willReturn($account = $this->createMock(UserInterface::class)) ; $provider = new ChainUserProvider([$provider1, $provider2]); - $this->assertSame($account, $provider->loadUserByUsername('foo')); + $this->assertSame($account, $provider->loadUserByIdentifier('foo')); } - public function testLoadUserByUsernameThrowsUsernameNotFoundException() + public function testLoadUserByUsernameThrowsUserNotFoundException() { - $this->expectException(UsernameNotFoundException::class); - $provider1 = $this->createMock(UserProviderInterface::class); + $this->expectException(UserNotFoundException::class); + $provider1 = $this->createMock(InMemoryUserProvider::class); $provider1 ->expects($this->once()) - ->method('loadUserByUsername') + ->method('loadUserByIdentifier') ->with($this->equalTo('foo')) - ->willThrowException(new UsernameNotFoundException('not found')) + ->willThrowException(new UserNotFoundException('not found')) ; - $provider2 = $this->createMock(UserProviderInterface::class); + $provider2 = $this->createMock(InMemoryUserProvider::class); $provider2 ->expects($this->once()) - ->method('loadUserByUsername') + ->method('loadUserByIdentifier') ->with($this->equalTo('foo')) - ->willThrowException(new UsernameNotFoundException('not found')) + ->willThrowException(new UserNotFoundException('not found')) ; $provider = new ChainUserProvider([$provider1, $provider2]); - $provider->loadUserByUsername('foo'); + $provider->loadUserByIdentifier('foo'); } public function testRefreshUser() { - $provider1 = $this->createMock(UserProviderInterface::class); + $provider1 = $this->createMock(InMemoryUserProvider::class); $provider1 ->expects($this->once()) ->method('supportsClass') ->willReturn(false) ; - $provider2 = $this->createMock(UserProviderInterface::class); + $provider2 = $this->createMock(InMemoryUserProvider::class); $provider2 ->expects($this->once()) ->method('supportsClass') @@ -90,7 +91,7 @@ public function testRefreshUser() ->willThrowException(new UnsupportedUserException('unsupported')) ; - $provider3 = $this->createMock(UserProviderInterface::class); + $provider3 = $this->createMock(InMemoryUserProvider::class); $provider3 ->expects($this->once()) ->method('supportsClass') @@ -109,7 +110,7 @@ public function testRefreshUser() public function testRefreshUserAgain() { - $provider1 = $this->createMock(UserProviderInterface::class); + $provider1 = $this->createMock(InMemoryUserProvider::class); $provider1 ->expects($this->once()) ->method('supportsClass') @@ -119,10 +120,10 @@ public function testRefreshUserAgain() $provider1 ->expects($this->once()) ->method('refreshUser') - ->willThrowException(new UsernameNotFoundException('not found')) + ->willThrowException(new UserNotFoundException('not found')) ; - $provider2 = $this->createMock(UserProviderInterface::class); + $provider2 = $this->createMock(InMemoryUserProvider::class); $provider2 ->expects($this->once()) ->method('supportsClass') @@ -142,7 +143,7 @@ public function testRefreshUserAgain() public function testRefreshUserThrowsUnsupportedUserException() { $this->expectException(UnsupportedUserException::class); - $provider1 = $this->createMock(UserProviderInterface::class); + $provider1 = $this->createMock(InMemoryUserProvider::class); $provider1 ->expects($this->once()) ->method('supportsClass') @@ -155,7 +156,7 @@ public function testRefreshUserThrowsUnsupportedUserException() ->willThrowException(new UnsupportedUserException('unsupported')) ; - $provider2 = $this->createMock(UserProviderInterface::class); + $provider2 = $this->createMock(InMemoryUserProvider::class); $provider2 ->expects($this->once()) ->method('supportsClass') @@ -174,7 +175,7 @@ public function testRefreshUserThrowsUnsupportedUserException() public function testSupportsClass() { - $provider1 = $this->createMock(UserProviderInterface::class); + $provider1 = $this->createMock(InMemoryUserProvider::class); $provider1 ->expects($this->once()) ->method('supportsClass') @@ -182,7 +183,7 @@ public function testSupportsClass() ->willReturn(false) ; - $provider2 = $this->createMock(UserProviderInterface::class); + $provider2 = $this->createMock(InMemoryUserProvider::class); $provider2 ->expects($this->once()) ->method('supportsClass') @@ -196,7 +197,7 @@ public function testSupportsClass() public function testSupportsClassWhenNotSupported() { - $provider1 = $this->createMock(UserProviderInterface::class); + $provider1 = $this->createMock(InMemoryUserProvider::class); $provider1 ->expects($this->once()) ->method('supportsClass') @@ -204,7 +205,7 @@ public function testSupportsClassWhenNotSupported() ->willReturn(false) ; - $provider2 = $this->createMock(UserProviderInterface::class); + $provider2 = $this->createMock(InMemoryUserProvider::class); $provider2 ->expects($this->once()) ->method('supportsClass') @@ -218,7 +219,7 @@ public function testSupportsClassWhenNotSupported() public function testAcceptsTraversable() { - $provider1 = $this->createMock(UserProviderInterface::class); + $provider1 = $this->createMock(InMemoryUserProvider::class); $provider1 ->expects($this->once()) ->method('supportsClass') @@ -231,7 +232,7 @@ public function testAcceptsTraversable() ->willThrowException(new UnsupportedUserException('unsupported')) ; - $provider2 = $this->createMock(UserProviderInterface::class); + $provider2 = $this->createMock(InMemoryUserProvider::class); $provider2 ->expects($this->once()) ->method('supportsClass') diff --git a/src/Symfony/Component/Security/Core/Tests/User/InMemoryUserProviderTest.php b/src/Symfony/Component/Security/Core/Tests/User/InMemoryUserProviderTest.php index f9d27c8f844a3..d4d4964c7be59 100644 --- a/src/Symfony/Component/Security/Core/Tests/User/InMemoryUserProviderTest.php +++ b/src/Symfony/Component/Security/Core/Tests/User/InMemoryUserProviderTest.php @@ -13,7 +13,7 @@ use PHPUnit\Framework\TestCase; use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; -use Symfony\Component\Security\Core\Exception\UsernameNotFoundException; +use Symfony\Component\Security\Core\Exception\UserNotFoundException; use Symfony\Component\Security\Core\User\InMemoryUser; use Symfony\Component\Security\Core\User\InMemoryUserProvider; use Symfony\Component\Security\Core\User\User; @@ -26,7 +26,7 @@ public function testConstructor() { $provider = $this->createProvider(); - $user = $provider->loadUserByUsername('fabien'); + $user = $provider->loadUserByIdentifier('fabien'); $this->assertEquals('foo', $user->getPassword()); $this->assertEquals(['ROLE_USER'], $user->getRoles()); $this->assertFalse($user->isEnabled()); @@ -76,7 +76,7 @@ public function testCreateUser() $provider = new InMemoryUserProvider(); $provider->createUser(new InMemoryUser('fabien', 'foo')); - $user = $provider->loadUserByUsername('fabien'); + $user = $provider->loadUserByIdentifier('fabien'); $this->assertEquals('foo', $user->getPassword()); } @@ -90,8 +90,8 @@ public function testCreateUserAlreadyExist() public function testLoadUserByUsernameDoesNotExist() { - $this->expectException(UsernameNotFoundException::class); + $this->expectException(UserNotFoundException::class); $provider = new InMemoryUserProvider(); - $provider->loadUserByUsername('fabien'); + $provider->loadUserByIdentifier('fabien'); } } diff --git a/src/Symfony/Component/Security/Core/Tests/User/InMemoryUserTest.php b/src/Symfony/Component/Security/Core/Tests/User/InMemoryUserTest.php index 885d1f73c0690..a5496ef325b85 100644 --- a/src/Symfony/Component/Security/Core/Tests/User/InMemoryUserTest.php +++ b/src/Symfony/Component/Security/Core/Tests/User/InMemoryUserTest.php @@ -12,12 +12,15 @@ namespace Symfony\Component\Security\Core\Tests\User; use PHPUnit\Framework\TestCase; +use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; use Symfony\Component\Security\Core\User\EquatableInterface; use Symfony\Component\Security\Core\User\InMemoryUser; use Symfony\Component\Security\Core\User\UserInterface; class InMemoryUserTest extends TestCase { + use ExpectDeprecationTrait; + public function testConstructorException() { $this->expectException(\InvalidArgumentException::class); @@ -39,12 +42,23 @@ public function testGetPassword() $this->assertEquals('superpass', $user->getPassword()); } + /** + * @group legacy + */ public function testGetUsername() { $user = new InMemoryUser('fabien', 'superpass'); + + $this->expectDeprecation('Since symfony/security-core 5.3: Method "Symfony\Component\Security\Core\User\User::getUsername()" is deprecated, use getUserIdentifier() instead.'); $this->assertEquals('fabien', $user->getUsername()); } + public function testGetUserIdentifier() + { + $user = new InMemoryUser('fabien', 'superpass'); + $this->assertEquals('fabien', $user->getUserIdentifier()); + } + public function testGetSalt() { $user = new InMemoryUser('fabien', 'superpass'); diff --git a/src/Symfony/Component/Security/Core/Tests/User/UserTest.php b/src/Symfony/Component/Security/Core/Tests/User/UserTest.php index 143479de7940d..81b8705d1041f 100644 --- a/src/Symfony/Component/Security/Core/Tests/User/UserTest.php +++ b/src/Symfony/Component/Security/Core/Tests/User/UserTest.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Security\Core\Tests\User; use PHPUnit\Framework\TestCase; +use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; use Symfony\Component\Security\Core\User\EquatableInterface; use Symfony\Component\Security\Core\User\User; use Symfony\Component\Security\Core\User\UserInterface; @@ -21,6 +22,8 @@ */ class UserTest extends TestCase { + use ExpectDeprecationTrait; + public function testConstructorException() { $this->expectException(\InvalidArgumentException::class); @@ -42,12 +45,23 @@ public function testGetPassword() $this->assertEquals('superpass', $user->getPassword()); } + /** + * @group legacy + */ public function testGetUsername() { $user = new User('fabien', 'superpass'); + + $this->expectDeprecation('Since symfony/security-core 5.3: Method "Symfony\Component\Security\Core\User\User::getUsername()" is deprecated, use getUserIdentifier() instead.'); $this->assertEquals('fabien', $user->getUsername()); } + public function testGetUserIdentifier() + { + $user = new User('fabien', 'superpass'); + $this->assertEquals('fabien', $user->getUserIdentifier()); + } + public function testGetSalt() { $user = new User('fabien', 'superpass'); diff --git a/src/Symfony/Component/Security/Core/User/ChainUserProvider.php b/src/Symfony/Component/Security/Core/User/ChainUserProvider.php index fedcdb6a53c10..35207d62cc924 100644 --- a/src/Symfony/Component/Security/Core/User/ChainUserProvider.php +++ b/src/Symfony/Component/Security/Core/User/ChainUserProvider.php @@ -12,7 +12,7 @@ namespace Symfony\Component\Security\Core\User; use Symfony\Component\Security\Core\Exception\UnsupportedUserException; -use Symfony\Component\Security\Core\Exception\UsernameNotFoundException; +use Symfony\Component\Security\Core\Exception\UserNotFoundException; /** * Chain User Provider. @@ -50,17 +50,31 @@ public function getProviders() * {@inheritdoc} */ public function loadUserByUsername(string $username) + { + trigger_deprecation('symfony/security-core', '5.3', 'Method "%s()" is deprecated, use loadUserByIdentifier() instead.', __METHOD__); + + return $this->loadUserByIdentifier($username); + } + + public function loadUserByIdentifier(string $userIdentifier): UserInterface { foreach ($this->providers as $provider) { try { - return $provider->loadUserByUsername($username); - } catch (UsernameNotFoundException $e) { + // @deprecated since 5.3, change to $provider->loadUserByIdentifier() in 6.0 + if (!method_exists($provider, 'loadUserByIdentifier')) { + trigger_deprecation('symfony/security-core', '5.3', 'Not implementing method "loadUserByIdentifier()" in user provider "%s" is deprecated. This method will replace "loadUserByUsername()" in Symfony 6.0.', \get_debug_type($provider)); + + return $provider->loadUserByUsername($userIdentifier); + } + + return $provider->loadUserByIdentifier($userIdentifier); + } catch (UserNotFoundException $e) { // try next one } } - $ex = new UsernameNotFoundException(sprintf('There is no user with name "%s".', $username)); - $ex->setUsername($username); + $ex = new UserNotFoundException(sprintf('There is no user with identifier "%s".', $userIdentifier)); + $ex->setUserIdentifier($userIdentifier); throw $ex; } @@ -80,15 +94,17 @@ public function refreshUser(UserInterface $user) return $provider->refreshUser($user); } catch (UnsupportedUserException $e) { // try next one - } catch (UsernameNotFoundException $e) { + } catch (UserNotFoundException $e) { $supportedUserFound = true; // try next one } } if ($supportedUserFound) { - $e = new UsernameNotFoundException(sprintf('There is no user with name "%s".', $user->getUsername())); - $e->setUsername($user->getUsername()); + // @deprecated since 5.3, change to $user->getUserIdentifier() in 6.0 + $username = method_exists($user, 'getUserIdentifier') ? $user->getUserIdentifier() : $user->getUsername(); + $e = new UserNotFoundException(sprintf('There is no user with name "%s".', $username)); + $e->setUserIdentifier($username); throw $e; } else { throw new UnsupportedUserException(sprintf('There is no user provider for user "%s". Shouldn\'t the "supportsClass()" method of your user provider return true for this classname?', get_debug_type($user))); diff --git a/src/Symfony/Component/Security/Core/User/InMemoryUserProvider.php b/src/Symfony/Component/Security/Core/User/InMemoryUserProvider.php index c79f96e2542ee..2e9ea5a27f675 100644 --- a/src/Symfony/Component/Security/Core/User/InMemoryUserProvider.php +++ b/src/Symfony/Component/Security/Core/User/InMemoryUserProvider.php @@ -12,7 +12,7 @@ namespace Symfony\Component\Security\Core\User; use Symfony\Component\Security\Core\Exception\UnsupportedUserException; -use Symfony\Component\Security\Core\Exception\UsernameNotFoundException; +use Symfony\Component\Security\Core\Exception\UserNotFoundException; /** * InMemoryUserProvider is a simple non persistent user provider. @@ -51,11 +51,13 @@ public function __construct(array $users = []) */ public function createUser(UserInterface $user) { - if (isset($this->users[strtolower($user->getUsername())])) { + // @deprecated since 5.3, change to $user->getUserIdentifier() in 6.0 + $userIdentifier = strtolower(method_exists($user, 'getUserIdentifier') ? $user->getUserIdentifier() : $user->getUsername()); + if (isset($this->users[$userIdentifier])) { throw new \LogicException('Another user with the same username already exists.'); } - $this->users[strtolower($user->getUsername())] = $user; + $this->users[$userIdentifier] = $user; } /** @@ -63,9 +65,17 @@ public function createUser(UserInterface $user) */ public function loadUserByUsername(string $username) { - $user = $this->getUser($username); + trigger_deprecation('symfony/security-core', '5.3', 'Method "%s()" is deprecated, use loadUserByIdentifier() instead.', __METHOD__); - return new InMemoryUser($user->getUsername(), $user->getPassword(), $user->getRoles(), $user->isEnabled()); + return $this->loadUserByIdentifier($username); + } + + public function loadUserByIdentifier(string $identifier): UserInterface + { + $user = $this->getUser($identifier); + + // @deprecated since 5.3, change to $user->getUserIdentifier() in 6.0 + return new InMemoryUser(method_exists($user, 'getUserIdentifier') ? $user->getUserIdentifier() : $user->getUsername(), $user->getPassword(), $user->getRoles(), $user->isEnabled()); } /** @@ -77,7 +87,9 @@ public function refreshUser(UserInterface $user) throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', get_debug_type($user))); } - $storedUser = $this->getUser($user->getUsername()); + // @deprecated since 5.3, change to $user->getUserIdentifier() in 6.0 + $storedUser = $this->getUser(method_exists($user, 'getUserIdentifier') ? $user->getUserIdentifier() : $user->getUsername()); + $userIdentifier = method_exists($storedUser, 'getUserIdentifier') ? $storedUser->getUserIdentifier() : $storedUser->getUsername(); // @deprecated since Symfony 5.3 if (User::class === \get_class($user)) { @@ -91,10 +103,10 @@ public function refreshUser(UserInterface $user) $accountNonLocked = $storedUser->isAccountNonLocked(); } - return new User($storedUser->getUsername(), $storedUser->getPassword(), $storedUser->getRoles(), $storedUser->isEnabled(), $accountNonExpired, $credentialsNonExpired, $accountNonLocked); + return new User($userIdentifier, $storedUser->getPassword(), $storedUser->getRoles(), $storedUser->isEnabled(), $accountNonExpired, $credentialsNonExpired, $accountNonLocked); } - return new InMemoryUser($storedUser->getUsername(), $storedUser->getPassword(), $storedUser->getRoles(), $storedUser->isEnabled()); + return new InMemoryUser($userIdentifier, $storedUser->getPassword(), $storedUser->getRoles(), $storedUser->isEnabled()); } /** @@ -113,13 +125,13 @@ public function supportsClass(string $class) /** * Returns the user by given username. * - * @throws UsernameNotFoundException if user whose given username does not exist + * @throws UserNotFoundException if user whose given username does not exist */ private function getUser(string $username)/*: InMemoryUser */ { if (!isset($this->users[strtolower($username)])) { - $ex = new UsernameNotFoundException(sprintf('Username "%s" does not exist.', $username)); - $ex->setUsername($username); + $ex = new UserNotFoundException(sprintf('Username "%s" does not exist.', $username)); + $ex->setUserIdentifier($username); throw $ex; } diff --git a/src/Symfony/Component/Security/Core/User/MissingUserProvider.php b/src/Symfony/Component/Security/Core/User/MissingUserProvider.php index 605aad6dfc74b..02df05169e9b8 100644 --- a/src/Symfony/Component/Security/Core/User/MissingUserProvider.php +++ b/src/Symfony/Component/Security/Core/User/MissingUserProvider.php @@ -37,6 +37,11 @@ public function loadUserByUsername(string $username): UserInterface throw new \BadMethodCallException(); } + public function loadUserByIdentifier(string $identifier): UserInterface + { + throw new \BadMethodCallException(); + } + /** * {@inheritdoc} */ diff --git a/src/Symfony/Component/Security/Core/User/User.php b/src/Symfony/Component/Security/Core/User/User.php index 045f03bff2554..d583e5a8cbd4f 100644 --- a/src/Symfony/Component/Security/Core/User/User.php +++ b/src/Symfony/Component/Security/Core/User/User.php @@ -53,7 +53,7 @@ public function __construct(?string $username, ?string $password, array $roles = public function __toString(): string { - return $this->getUsername(); + return $this->getUserIdentifier(); } /** @@ -84,6 +84,16 @@ public function getSalt(): ?string * {@inheritdoc} */ public function getUsername(): string + { + trigger_deprecation('symfony/security-core', '5.3', 'Method "%s()" is deprecated, use getUserIdentifier() instead.', __METHOD__); + + return $this->username; + } + + /** + * Returns the identifier for this user (e.g. its username or e-mailaddress). + */ + public function getUserIdentifier(): string { return $this->username; } @@ -184,7 +194,7 @@ public function isEqualTo(UserInterface $user): bool return false; } - if ($this->getUsername() !== $user->getUsername()) { + if ($this->getUserIdentifier() !== $user->getUserIdentifier()) { return false; } diff --git a/src/Symfony/Component/Security/Core/User/UserInterface.php b/src/Symfony/Component/Security/Core/User/UserInterface.php index 47661de0b7317..6448ab519248d 100644 --- a/src/Symfony/Component/Security/Core/User/UserInterface.php +++ b/src/Symfony/Component/Security/Core/User/UserInterface.php @@ -26,6 +26,8 @@ * * @see UserProviderInterface * + * @method string getUserIdentifier() returns the identifier for this user (e.g. its username or e-mailaddress) + * * @author Fabien Potencier */ interface UserInterface @@ -69,13 +71,6 @@ public function getPassword(); */ public function getSalt(); - /** - * Returns the username used to authenticate the user. - * - * @return string The username - */ - public function getUsername(); - /** * Removes sensitive data from the user. * diff --git a/src/Symfony/Component/Security/Core/User/UserProviderInterface.php b/src/Symfony/Component/Security/Core/User/UserProviderInterface.php index 708a97f4be404..5ab67836c730f 100644 --- a/src/Symfony/Component/Security/Core/User/UserProviderInterface.php +++ b/src/Symfony/Component/Security/Core/User/UserProviderInterface.php @@ -12,16 +12,16 @@ namespace Symfony\Component\Security\Core\User; use Symfony\Component\Security\Core\Exception\UnsupportedUserException; -use Symfony\Component\Security\Core\Exception\UsernameNotFoundException; +use Symfony\Component\Security\Core\Exception\UserNotFoundException; /** * Represents a class that loads UserInterface objects from some source for the authentication system. * - * In a typical authentication configuration, a username (i.e. some unique - * user identifier) credential enters the system (via form login, or any - * method). The user provider that is configured with that authentication - * method is asked to load the UserInterface object for the given username - * (via loadUserByUsername) so that the rest of the process can continue. + * In a typical authentication configuration, a user identifier (e.g. a + * username or e-mailaddress) credential enters the system (via form login, or + * any method). The user provider that is configured with that authentication + * method is asked to load the UserInterface object for the given identifier (via + * loadUserByIdentifier) so that the rest of the process can continue. * * Internally, a user provider can load users from any source (databases, * configuration, web service). This is totally independent of how the authentication @@ -29,22 +29,13 @@ * * @see UserInterface * + * @method UserInterface loadUserByIdentifier(string $identifier) loads the user for the given user identifier (e.g. username or email). + * This method must throw UserNotFoundException if the user is not found. + * * @author Fabien Potencier */ interface UserProviderInterface { - /** - * Loads the user for the given username. - * - * This method must throw UsernameNotFoundException if the user is not - * found. - * - * @return UserInterface - * - * @throws UsernameNotFoundException if the user is not found - */ - public function loadUserByUsername(string $username); - /** * Refreshes the user. * @@ -55,8 +46,8 @@ public function loadUserByUsername(string $username); * * @return UserInterface * - * @throws UnsupportedUserException if the user is not supported - * @throws UsernameNotFoundException if the user is not found + * @throws UnsupportedUserException if the user is not supported + * @throws UserNotFoundException if the user is not found */ public function refreshUser(UserInterface $user); diff --git a/src/Symfony/Component/Security/Guard/Authenticator/GuardBridgeAuthenticator.php b/src/Symfony/Component/Security/Guard/Authenticator/GuardBridgeAuthenticator.php index 8e678227806fa..b3384111008df 100644 --- a/src/Symfony/Component/Security/Guard/Authenticator/GuardBridgeAuthenticator.php +++ b/src/Symfony/Component/Security/Guard/Authenticator/GuardBridgeAuthenticator.php @@ -15,7 +15,7 @@ use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Exception\AuthenticationException; -use Symfony\Component\Security\Core\Exception\UsernameNotFoundException; +use Symfony\Component\Security\Core\Exception\UserNotFoundException; use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface; use Symfony\Component\Security\Core\User\PasswordUpgraderInterface; use Symfony\Component\Security\Core\User\UserInterface; @@ -98,7 +98,7 @@ private function getUser($credentials): UserInterface $user = $this->guard->getUser($credentials, $this->userProvider); if (null === $user) { - throw new UsernameNotFoundException(sprintf('Null returned from "%s::getUser()".', get_debug_type($this->guard))); + throw new UserNotFoundException(sprintf('Null returned from "%s::getUser()".', get_debug_type($this->guard))); } if (!$user instanceof UserInterface) { diff --git a/src/Symfony/Component/Security/Guard/AuthenticatorInterface.php b/src/Symfony/Component/Security/Guard/AuthenticatorInterface.php index 36c1be3115855..870ba387822a3 100644 --- a/src/Symfony/Component/Security/Guard/AuthenticatorInterface.php +++ b/src/Symfony/Component/Security/Guard/AuthenticatorInterface.php @@ -70,7 +70,7 @@ public function getCredentials(Request $request); * The *credentials* are the return value from getCredentials() * * You may throw an AuthenticationException if you wish. If you return - * null, then a UsernameNotFoundException is thrown for you. + * null, then a UserNotFoundException is thrown for you. * * @param mixed $credentials * diff --git a/src/Symfony/Component/Security/Guard/Provider/GuardAuthenticationProvider.php b/src/Symfony/Component/Security/Guard/Provider/GuardAuthenticationProvider.php index 71d663e3dcf85..880d275f68e65 100644 --- a/src/Symfony/Component/Security/Guard/Provider/GuardAuthenticationProvider.php +++ b/src/Symfony/Component/Security/Guard/Provider/GuardAuthenticationProvider.php @@ -18,7 +18,7 @@ use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Core\Exception\AuthenticationExpiredException; use Symfony\Component\Security\Core\Exception\BadCredentialsException; -use Symfony\Component\Security\Core\Exception\UsernameNotFoundException; +use Symfony\Component\Security\Core\Exception\UserNotFoundException; use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface; use Symfony\Component\Security\Core\User\PasswordUpgraderInterface; use Symfony\Component\Security\Core\User\UserCheckerInterface; @@ -112,8 +112,9 @@ private function authenticateViaGuard(AuthenticatorInterface $guardAuthenticator $user = $guardAuthenticator->getUser($token->getCredentials(), $this->userProvider); if (null === $user) { - $e = new UsernameNotFoundException(sprintf('Null returned from "%s::getUser()".', get_debug_type($guardAuthenticator))); - $e->setUsername($token->getUsername()); + $e = new UserNotFoundException(sprintf('Null returned from "%s::getUser()".', get_debug_type($guardAuthenticator))); + // @deprecated since 5.3, change to $token->getUserIdentifier() in 6.0 + $e->setUserIdentifier(method_exists($token, 'getUserIdentifier') ? $token->getUserIdentifier() : $token->getUsername()); throw $e; } diff --git a/src/Symfony/Component/Security/Guard/Tests/Authenticator/GuardBridgeAuthenticatorTest.php b/src/Symfony/Component/Security/Guard/Tests/Authenticator/GuardBridgeAuthenticatorTest.php index 678e1f17ceb7c..24a4d71c509d5 100644 --- a/src/Symfony/Component/Security/Guard/Tests/Authenticator/GuardBridgeAuthenticatorTest.php +++ b/src/Symfony/Component/Security/Guard/Tests/Authenticator/GuardBridgeAuthenticatorTest.php @@ -15,7 +15,7 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Security\Core\Exception\AuthenticationException; -use Symfony\Component\Security\Core\Exception\UsernameNotFoundException; +use Symfony\Component\Security\Core\Exception\UserNotFoundException; use Symfony\Component\Security\Core\User\InMemoryUser; use Symfony\Component\Security\Core\User\UserProviderInterface; use Symfony\Component\Security\Guard\Authenticator\GuardBridgeAuthenticator; @@ -97,7 +97,7 @@ public function testAuthenticate() public function testAuthenticateNoUser() { - $this->expectException(UsernameNotFoundException::class); + $this->expectException(UserNotFoundException::class); $request = new Request(); diff --git a/src/Symfony/Component/Security/Http/Authentication/AuthenticatorManager.php b/src/Symfony/Component/Security/Http/Authentication/AuthenticatorManager.php index 501a9e2fb1a3c..b5e609c74196b 100644 --- a/src/Symfony/Component/Security/Http/Authentication/AuthenticatorManager.php +++ b/src/Symfony/Component/Security/Http/Authentication/AuthenticatorManager.php @@ -70,7 +70,8 @@ public function __construct(iterable $authenticators, TokenStorageInterface $tok public function authenticateUser(UserInterface $user, AuthenticatorInterface $authenticator, Request $request, array $badges = []): ?Response { // create an authenticated token for the User - $token = $authenticator->createAuthenticatedToken($passport = new SelfValidatingPassport(new UserBadge($user->getUsername(), function () use ($user) { return $user; }), $badges), $this->firewallName); + // @deprecated since 5.3, change to $user->getUserIdentifier() in 6.0 + $token = $authenticator->createAuthenticatedToken($passport = new SelfValidatingPassport(new UserBadge(method_exists($user, 'getUserIdentifier') ? $user->getUserIdentifier() : $user->getUsername(), function () use ($user) { return $user; }), $badges), $this->firewallName); // announce the authenticated token $token = $this->eventDispatcher->dispatch(new AuthenticationTokenCreatedEvent($token))->getAuthenticatedToken(); @@ -215,6 +216,11 @@ private function executeAuthenticator(AuthenticatorInterface $authenticator, Req private function handleAuthenticationSuccess(TokenInterface $authenticatedToken, PassportInterface $passport, Request $request, AuthenticatorInterface $authenticator): ?Response { + // @deprecated since 5.3 + if (!method_exists($authenticatedToken->getUser(), 'getUserIdentifier')) { + trigger_deprecation('symfony/security-core', '5.3', 'Not implementing method "getUserIdentifier(): string" in user class "%s" is deprecated. This method will replace "getUsername()" in Symfony 6.0.', get_debug_type($authenticatedToken->getUser())); + } + $this->tokenStorage->setToken($authenticatedToken); $response = $authenticator->onAuthenticationSuccess($request, $authenticatedToken, $this->firewallName); diff --git a/src/Symfony/Component/Security/Http/Authenticator/AbstractPreAuthenticatedAuthenticator.php b/src/Symfony/Component/Security/Http/Authenticator/AbstractPreAuthenticatedAuthenticator.php index 8ccd356ca1d09..cb8470f4e856c 100644 --- a/src/Symfony/Component/Security/Http/Authenticator/AbstractPreAuthenticatedAuthenticator.php +++ b/src/Symfony/Component/Security/Http/Authenticator/AbstractPreAuthenticatedAuthenticator.php @@ -87,9 +87,18 @@ public function supports(Request $request): ?bool public function authenticate(Request $request): PassportInterface { - return new SelfValidatingPassport(new UserBadge($request->attributes->get('_pre_authenticated_username'), function ($username) { - return $this->userProvider->loadUserByUsername($username); - }), [new PreAuthenticatedUserBadge()]); + // @deprecated since 5.3, change to $this->userProvider->loadUserByIdentifier() in 6.0 + $method = 'loadUserByIdentifier'; + if (!method_exists($this->userProvider, 'loadUserByIdentifier')) { + trigger_deprecation('symfony/security-core', '5.3', 'Not implementing method "loadUserByIdentifier()" in user provider "%s" is deprecated. This method will replace "loadUserByUsername()" in Symfony 6.0.', get_debug_type($this->userProvider)); + + $method = 'loadUserByUsername'; + } + + return new SelfValidatingPassport( + new UserBadge($request->attributes->get('_pre_authenticated_username'), [$this->userProvider, $method]), + [new PreAuthenticatedUserBadge()] + ); } public function createAuthenticatedToken(PassportInterface $passport, string $firewallName): TokenInterface diff --git a/src/Symfony/Component/Security/Http/Authenticator/AuthenticatorInterface.php b/src/Symfony/Component/Security/Http/Authenticator/AuthenticatorInterface.php index 0d7e9cb7bb6f4..d6ec043d15cfc 100644 --- a/src/Symfony/Component/Security/Http/Authenticator/AuthenticatorInterface.php +++ b/src/Symfony/Component/Security/Http/Authenticator/AuthenticatorInterface.php @@ -46,7 +46,7 @@ public function supports(Request $request): ?bool; * presented password and the CSRF token value. * * You may throw any AuthenticationException in this method in case of error (e.g. - * a UsernameNotFoundException when the user cannot be found). + * a UserNotFoundException when the user cannot be found). * * @throws AuthenticationException */ diff --git a/src/Symfony/Component/Security/Http/Authenticator/FormLoginAuthenticator.php b/src/Symfony/Component/Security/Http/Authenticator/FormLoginAuthenticator.php index 234b2dc0a3d5a..965cbfa4ec99b 100644 --- a/src/Symfony/Component/Security/Http/Authenticator/FormLoginAuthenticator.php +++ b/src/Symfony/Component/Security/Http/Authenticator/FormLoginAuthenticator.php @@ -84,14 +84,19 @@ public function authenticate(Request $request): PassportInterface { $credentials = $this->getCredentials($request); - $passport = new Passport(new UserBadge($credentials['username'], function ($username) { - $user = $this->userProvider->loadUserByUsername($username); - if (!$user instanceof UserInterface) { - throw new AuthenticationServiceException('The user provider must return a UserInterface object.'); - } - - return $user; - }), new PasswordCredentials($credentials['password']), [new RememberMeBadge()]); + // @deprecated since 5.3, change to $this->userProvider->loadUserByIdentifier() in 6.0 + $method = 'loadUserByIdentifier'; + if (!method_exists($this->userProvider, 'loadUserByIdentifier')) { + trigger_deprecation('symfony/security-core', '5.3', 'Not implementing method "loadUserByIdentifier()" in user provider "%s" is deprecated. This method will replace "loadUserByUsername()" in Symfony 6.0.', get_debug_type($this->userProvider)); + + $method = 'loadUserByUsername'; + } + + $passport = new Passport( + new UserBadge($credentials['username'], [$this->userProvider, $method]), + new PasswordCredentials($credentials['password']), + [new RememberMeBadge()] + ); if ($this->options['enable_csrf']) { $passport->addBadge(new CsrfTokenBadge($this->options['csrf_token_id'], $credentials['csrf_token'])); } diff --git a/src/Symfony/Component/Security/Http/Authenticator/HttpBasicAuthenticator.php b/src/Symfony/Component/Security/Http/Authenticator/HttpBasicAuthenticator.php index 179e1fdd1a94c..c184de8477747 100644 --- a/src/Symfony/Component/Security/Http/Authenticator/HttpBasicAuthenticator.php +++ b/src/Symfony/Component/Security/Http/Authenticator/HttpBasicAuthenticator.php @@ -67,14 +67,18 @@ public function authenticate(Request $request): PassportInterface $username = $request->headers->get('PHP_AUTH_USER'); $password = $request->headers->get('PHP_AUTH_PW', ''); - $passport = new Passport(new UserBadge($username, function ($username) { - $user = $this->userProvider->loadUserByUsername($username); - if (!$user instanceof UserInterface) { - throw new AuthenticationServiceException('The user provider must return a UserInterface object.'); - } - - return $user; - }), new PasswordCredentials($password)); + // @deprecated since 5.3, change to $this->userProvider->loadUserByIdentifier() in 6.0 + $method = 'loadUserByIdentifier'; + if (!method_exists($this->userProvider, 'loadUserByIdentifier')) { + trigger_deprecation('symfony/security-core', '5.3', 'Not implementing method "loadUserByIdentifier()" in user provider "%s" is deprecated. This method will replace "loadUserByUsername()" in Symfony 6.0.', get_debug_type($this->userProvider)); + + $method = 'loadUserByUsername'; + } + + $passport = new Passport( + new UserBadge($username, [$this->userProvider, $method]), + new PasswordCredentials($password) + ); if ($this->userProvider instanceof PasswordUpgraderInterface) { $passport->addBadge(new PasswordUpgradeBadge($password, $this->userProvider)); } diff --git a/src/Symfony/Component/Security/Http/Authenticator/JsonLoginAuthenticator.php b/src/Symfony/Component/Security/Http/Authenticator/JsonLoginAuthenticator.php index 69ef4d28f5969..8e5d3afd5c920 100644 --- a/src/Symfony/Component/Security/Http/Authenticator/JsonLoginAuthenticator.php +++ b/src/Symfony/Component/Security/Http/Authenticator/JsonLoginAuthenticator.php @@ -94,14 +94,18 @@ public function authenticate(Request $request): PassportInterface throw $e; } - $passport = new Passport(new UserBadge($credentials['username'], function ($username) { - $user = $this->userProvider->loadUserByUsername($username); - if (!$user instanceof UserInterface) { - throw new AuthenticationServiceException('The user provider must return a UserInterface object.'); - } + // @deprecated since 5.3, change to $this->userProvider->loadUserByIdentifier() in 6.0 + $method = 'loadUserByIdentifier'; + if (!method_exists($this->userProvider, 'loadUserByIdentifier')) { + trigger_deprecation('symfony/security-core', '5.3', 'Not implementing method "loadUserByIdentifier()" in user provider "%s" is deprecated. This method will replace "loadUserByUsername()" in Symfony 6.0.', get_debug_type($this->userProvider)); + + $method = 'loadUserByUsername'; + } - return $user; - }), new PasswordCredentials($credentials['password'])); + $passport = new Passport( + new UserBadge($credentials['username'], [$this->userProvider, $method]), + new PasswordCredentials($credentials['password']) + ); if ($this->userProvider instanceof PasswordUpgraderInterface) { $passport->addBadge(new PasswordUpgradeBadge($credentials['password'], $this->userProvider)); } diff --git a/src/Symfony/Component/Security/Http/Authenticator/Passport/Badge/UserBadge.php b/src/Symfony/Component/Security/Http/Authenticator/Passport/Badge/UserBadge.php index a58f86cd13e4c..41e14141f71f8 100644 --- a/src/Symfony/Component/Security/Http/Authenticator/Passport/Badge/UserBadge.php +++ b/src/Symfony/Component/Security/Http/Authenticator/Passport/Badge/UserBadge.php @@ -11,7 +11,7 @@ namespace Symfony\Component\Security\Http\Authenticator\Passport\Badge; -use Symfony\Component\Security\Core\Exception\UsernameNotFoundException; +use Symfony\Component\Security\Core\Exception\AuthenticationServiceException; use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Http\EventListener\UserProviderListener; @@ -40,7 +40,7 @@ class UserBadge implements BadgeInterface * based on email *and* company name). This string can be used for e.g. login throttling. * * Optionally, you may pass a user loader. This callable receives the $userIdentifier - * as argument and must return a UserInterface object (otherwise a UsernameNotFoundException + * as argument and must return a UserInterface object (otherwise an AuthenticationServiceException * is thrown). If this is not set, the default user provider will be used with * $userIdentifier as username. */ @@ -64,7 +64,7 @@ public function getUser(): UserInterface $this->user = ($this->userLoader)($this->userIdentifier); if (!$this->user instanceof UserInterface) { - throw new UsernameNotFoundException(); + throw new AuthenticationServiceException(sprintf('The user provider must return a UserInterface object, "%s" given.', \get_debug_type($this->user))); } } diff --git a/src/Symfony/Component/Security/Http/Authenticator/RememberMeAuthenticator.php b/src/Symfony/Component/Security/Http/Authenticator/RememberMeAuthenticator.php index bc7ecc3996407..b663a275062dc 100644 --- a/src/Symfony/Component/Security/Http/Authenticator/RememberMeAuthenticator.php +++ b/src/Symfony/Component/Security/Http/Authenticator/RememberMeAuthenticator.php @@ -82,7 +82,8 @@ public function authenticate(Request $request): PassportInterface throw new \LogicException('No remember me token is set.'); } - return new SelfValidatingPassport(new UserBadge($token->getUsername(), [$token, 'getUser'])); + // @deprecated since 5.3, change to $token->getUserIdentifier() in 6.0 + return new SelfValidatingPassport(new UserBadge(method_exists($token, 'getUserIdentifier') ? $token->getUserIdentifier() : $token->getUsername(), [$token, 'getUser'])); } public function createAuthenticatedToken(PassportInterface $passport, string $firewallName): TokenInterface diff --git a/src/Symfony/Component/Security/Http/EventListener/UserProviderListener.php b/src/Symfony/Component/Security/Http/EventListener/UserProviderListener.php index 715ae675c0bef..21fbf97daafad 100644 --- a/src/Symfony/Component/Security/Http/EventListener/UserProviderListener.php +++ b/src/Symfony/Component/Security/Http/EventListener/UserProviderListener.php @@ -46,6 +46,13 @@ public function checkPassport(CheckPassportEvent $event): void return; } - $badge->setUserLoader([$this->userProvider, 'loadUserByUsername']); + // @deprecated since 5.3, change to $this->userProvider->loadUserByIdentifier() in 6.0 + if (method_exists($this->userProvider, 'loadUserByIdentifier')) { + $badge->setUserLoader([$this->userProvider, 'loadUserByIdentifier']); + } else { + trigger_deprecation('symfony/security-http', '5.3', 'Not implementing method "loadUserByIdentifier()" in user provider "%s" is deprecated. This method will replace "loadUserByUsername()" in Symfony 6.0.', get_debug_type($this->userProvider)); + + $badge->setUserLoader([$this->userProvider, 'loadUserByUsername']); + } } } diff --git a/src/Symfony/Component/Security/Http/Firewall/AbstractAuthenticationListener.php b/src/Symfony/Component/Security/Http/Firewall/AbstractAuthenticationListener.php index 1e4405e629158..616a4efdd7b9b 100644 --- a/src/Symfony/Component/Security/Http/Firewall/AbstractAuthenticationListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/AbstractAuthenticationListener.php @@ -195,7 +195,8 @@ private function onFailure(Request $request, AuthenticationException $failed): R private function onSuccess(Request $request, TokenInterface $token): Response { if (null !== $this->logger) { - $this->logger->info('User has been authenticated successfully.', ['username' => $token->getUsername()]); + // @deprecated since 5.3, change to $token->getUserIdentifier() in 6.0 + $this->logger->info('User has been authenticated successfully.', ['username' => method_exists($token, 'getUserIdentifier') ? $token->getUserIdentifier() : $token->getUsername()]); } $this->tokenStorage->setToken($token); diff --git a/src/Symfony/Component/Security/Http/Firewall/AbstractPreAuthenticatedListener.php b/src/Symfony/Component/Security/Http/Firewall/AbstractPreAuthenticatedListener.php index 2cd905157b70b..7f8fcc1d82136 100644 --- a/src/Symfony/Component/Security/Http/Firewall/AbstractPreAuthenticatedListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/AbstractPreAuthenticatedListener.php @@ -83,7 +83,8 @@ public function authenticate(RequestEvent $event) } if (null !== $token = $this->tokenStorage->getToken()) { - if ($token instanceof PreAuthenticatedToken && $this->providerKey == $token->getFirewallName() && $token->isAuthenticated() && $token->getUsername() === $user) { + // @deprecated since 5.3, change to $token->getUserIdentifier() in 6.0 + if ($token instanceof PreAuthenticatedToken && $this->providerKey == $token->getFirewallName() && $token->isAuthenticated() && (method_exists($token, 'getUserIdentifier') ? $token->getUserIdentifier() : $token->getUsername()) === $user) { return; } } diff --git a/src/Symfony/Component/Security/Http/Firewall/BasicAuthenticationListener.php b/src/Symfony/Component/Security/Http/Firewall/BasicAuthenticationListener.php index a9ef56705fdd0..bf18dc168753b 100644 --- a/src/Symfony/Component/Security/Http/Firewall/BasicAuthenticationListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/BasicAuthenticationListener.php @@ -73,7 +73,8 @@ public function authenticate(RequestEvent $event) } if (null !== $token = $this->tokenStorage->getToken()) { - if ($token instanceof UsernamePasswordToken && $token->isAuthenticated() && $token->getUsername() === $username) { + // @deprecated since 5.3, change to $token->getUserIdentifier() in 6.0 + if ($token instanceof UsernamePasswordToken && $token->isAuthenticated() && (method_exists($token, 'getUserIdentifier') ? $token->getUserIdentifier() : $token->getUsername()) === $username) { return; } } diff --git a/src/Symfony/Component/Security/Http/Firewall/ContextListener.php b/src/Symfony/Component/Security/Http/Firewall/ContextListener.php index a3511067ac9b6..6b3984687b7f4 100644 --- a/src/Symfony/Component/Security/Http/Firewall/ContextListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/ContextListener.php @@ -27,7 +27,7 @@ use Symfony\Component\Security\Core\Authentication\Token\SwitchUserToken; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Exception\UnsupportedUserException; -use Symfony\Component\Security\Core\Exception\UsernameNotFoundException; +use Symfony\Component\Security\Core\Exception\UserNotFoundException; use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Core\User\UserProviderInterface; use Symfony\Component\Security\Http\Event\DeauthenticatedEvent; @@ -222,7 +222,8 @@ protected function refreshUser(TokenInterface $token): ?TokenInterface $userDeauthenticated = true; if (null !== $this->logger) { - $this->logger->debug('Cannot refresh token because user has changed.', ['username' => $refreshedUser->getUsername(), 'provider' => \get_class($provider)]); + // @deprecated since 5.3, change to $refreshedUser->getUserIdentifier() in 6.0 + $this->logger->debug('Cannot refresh token because user has changed.', ['username' => method_exists($refreshedUser, 'getUserIdentifier') ? $refreshedUser->getUserIdentifier() : $refreshedUser->getUsername(), 'provider' => \get_class($provider)]); } continue; @@ -231,10 +232,12 @@ protected function refreshUser(TokenInterface $token): ?TokenInterface $token->setUser($refreshedUser); if (null !== $this->logger) { - $context = ['provider' => \get_class($provider), 'username' => $refreshedUser->getUsername()]; + // @deprecated since 5.3, change to $refreshedUser->getUserIdentifier() in 6.0 + $context = ['provider' => \get_class($provider), 'username' => method_exists($refreshedUser, 'getUserIdentifier') ? $refreshedUser->getUserIdentifier() : $refreshedUser->getUsername()]; if ($token instanceof SwitchUserToken) { - $context['impersonator_username'] = $token->getOriginalToken()->getUsername(); + // @deprecated since 5.3, change to $token->getUserIdentifier() in 6.0 + $context['impersonator_username'] = method_exists($token, 'getUserIdentifier') ? $token->getUserIdentifier() : $token->getOriginalToken()->getUsername(); } $this->logger->debug('User was reloaded from a user provider.', $context); @@ -243,9 +246,9 @@ protected function refreshUser(TokenInterface $token): ?TokenInterface return $token; } catch (UnsupportedUserException $e) { // let's try the next user provider - } catch (UsernameNotFoundException $e) { + } catch (UserNotFoundException $e) { if (null !== $this->logger) { - $this->logger->warning('Username could not be found in the selected user provider.', ['username' => $e->getUsername(), 'provider' => \get_class($provider)]); + $this->logger->warning('Username could not be found in the selected user provider.', ['username' => method_exists($e, 'getUserIdentifier') ? $e->getUserIdentifier() : $e->getUsername(), 'provider' => \get_class($provider)]); } $userNotFoundByProvider = true; diff --git a/src/Symfony/Component/Security/Http/Firewall/SwitchUserListener.php b/src/Symfony/Component/Security/Http/Firewall/SwitchUserListener.php index 15b794c0a7503..b765b89da7d18 100644 --- a/src/Symfony/Component/Security/Http/Firewall/SwitchUserListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/SwitchUserListener.php @@ -140,7 +140,8 @@ private function attemptSwitchUser(Request $request, string $username): ?TokenIn $originalToken = $this->getOriginalToken($token); if (null !== $originalToken) { - if ($token->getUsername() === $username) { + // @deprecated since 5.3, change to $token->getUserIdentifier() in 6.0 + if ((method_exists($token, 'getUserIdentifier') ? $token->getUserIdentifier() : $token->getUsername()) === $username) { return $token; } @@ -148,20 +149,27 @@ private function attemptSwitchUser(Request $request, string $username): ?TokenIn $token = $this->attemptExitUser($request); } - $currentUsername = $token->getUsername(); + // @deprecated since 5.3, change to $token->getUserIdentifier() in 6.0 + $currentUsername = method_exists($token, 'getUserIdentifier') ? $token->getUserIdentifier() : $token->getUsername(); $nonExistentUsername = '_'.md5(random_bytes(8).$username); // To protect against user enumeration via timing measurements // we always load both successfully and unsuccessfully + $methodName = 'loadUserByIdentifier'; + if (!method_exists($this->provider, $methodName)) { + trigger_deprecation('symfony/security-core', '5.3', 'Not implementing method "loadUserByIdentifier()" in user provider "%s" is deprecated. This method will replace "loadUserByUsername()" in Symfony 6.0.', get_debug_type($this->provider)); + + $methodName = 'loadUserByUsername'; + } try { - $user = $this->provider->loadUserByUsername($username); + $user = $this->provider->$methodName($username); try { - $this->provider->loadUserByUsername($nonExistentUsername); + $this->provider->$methodName($nonExistentUsername); } catch (\Exception $e) { } } catch (AuthenticationException $e) { - $this->provider->loadUserByUsername($currentUsername); + $this->provider->$methodName($currentUsername); throw $e; } diff --git a/src/Symfony/Component/Security/Http/Firewall/UsernamePasswordJsonAuthenticationListener.php b/src/Symfony/Component/Security/Http/Firewall/UsernamePasswordJsonAuthenticationListener.php index 9276e7f2ceb9e..5029cb95361c3 100644 --- a/src/Symfony/Component/Security/Http/Firewall/UsernamePasswordJsonAuthenticationListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/UsernamePasswordJsonAuthenticationListener.php @@ -151,7 +151,8 @@ public function authenticate(RequestEvent $event) private function onSuccess(Request $request, TokenInterface $token): ?Response { if (null !== $this->logger) { - $this->logger->info('User has been authenticated successfully.', ['username' => $token->getUsername()]); + // @deprecated since 5.3, change to $token->getUserIdentifier() in 6.0 + $this->logger->info('User has been authenticated successfully.', ['username' => method_exists($token, 'getUserIdentifier') ? $token->getUserIdentifier() : $token->getUsername()]); } $this->migrateSession($request, $token); diff --git a/src/Symfony/Component/Security/Http/LoginLink/LoginLinkHandler.php b/src/Symfony/Component/Security/Http/LoginLink/LoginLinkHandler.php index 1f4ddcc1ddc11..bfa67d6b6eb2a 100644 --- a/src/Symfony/Component/Security/Http/LoginLink/LoginLinkHandler.php +++ b/src/Symfony/Component/Security/Http/LoginLink/LoginLinkHandler.php @@ -15,7 +15,7 @@ use Symfony\Component\PropertyAccess\PropertyAccessorInterface; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Component\Routing\RequestContext; -use Symfony\Component\Security\Core\Exception\UsernameNotFoundException; +use Symfony\Component\Security\Core\Exception\UserNotFoundException; use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Core\User\UserProviderInterface; use Symfony\Component\Security\Http\LoginLink\Exception\ExpiredLoginLinkException; @@ -56,7 +56,8 @@ public function createLoginLink(UserInterface $user, Request $request = null): L $expires = $expiresAt->format('U'); $parameters = [ - 'user' => $user->getUsername(), + // @deprecated since 5.3, change to $user->getUserIdentifier() in 6.0 + 'user' => method_exists($user, 'getUserIdentifier') ? $user->getUserIdentifier() : $user->getUsername(), 'expires' => $expires, 'hash' => $this->computeSignatureHash($user, $expires), ]; @@ -83,11 +84,18 @@ public function createLoginLink(UserInterface $user, Request $request = null): L public function consumeLoginLink(Request $request): UserInterface { - $username = $request->get('user'); + $userIdentifier = $request->get('user'); try { - $user = $this->userProvider->loadUserByUsername($username); - } catch (UsernameNotFoundException $exception) { + // @deprecated since 5.3, change to $this->userProvider->loadUserByIdentifier() in 6.0 + if (method_exists($this->userProvider, 'loadUserByIdentifier')) { + $user = $this->userProvider->loadUserByIdentifier($userIdentifier); + } else { + trigger_deprecation('symfony/security-core', '5.3', 'Not implementing method "loadUserByIdentifier()" in user provider "%s" is deprecated. This method will replace "loadUserByUsername()" in Symfony 6.0.', get_debug_type($this->userProvider)); + + $user = $this->userProvider->loadUserByUsername($userIdentifier); + } + } catch (UserNotFoundException $exception) { throw new InvalidLoginLinkException('User not found.', 0, $exception); } @@ -115,7 +123,8 @@ public function consumeLoginLink(Request $request): UserInterface private function computeSignatureHash(UserInterface $user, int $expires): string { - $signatureFields = [base64_encode($user->getUsername()), $expires]; + // @deprecated since 5.3, change to $user->getUserIdentifier() in 6.0 + $signatureFields = [base64_encode(method_exists($user, 'getUserIdentifier') ? $user->getUserIdentifier() : $user->getUsername()), $expires]; foreach ($this->signatureProperties as $property) { $value = $this->propertyAccessor->getValue($user, $property) ?? ''; diff --git a/src/Symfony/Component/Security/Http/RememberMe/AbstractRememberMeServices.php b/src/Symfony/Component/Security/Http/RememberMe/AbstractRememberMeServices.php index 295e7d80a4951..aa2ab934d13b9 100644 --- a/src/Symfony/Component/Security/Http/RememberMe/AbstractRememberMeServices.php +++ b/src/Symfony/Component/Security/Http/RememberMe/AbstractRememberMeServices.php @@ -20,7 +20,7 @@ use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Core\Exception\CookieTheftException; use Symfony\Component\Security\Core\Exception\UnsupportedUserException; -use Symfony\Component\Security\Core\Exception\UsernameNotFoundException; +use Symfony\Component\Security\Core\Exception\UserNotFoundException; use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Core\User\UserProviderInterface; use Symfony\Component\Security\Http\Logout\LogoutHandlerInterface; @@ -40,6 +40,8 @@ abstract class AbstractRememberMeServices implements RememberMeServicesInterface 'secure' => false, 'httponly' => true, 'samesite' => null, + 'path' => null, + 'domain' => null, ]; private $firewallName; private $secret; @@ -128,7 +130,7 @@ final public function autoLogin(Request $request): ?TokenInterface $this->loginFail($request, $e); throw $e; - } catch (UsernameNotFoundException $e) { + } catch (UserNotFoundException $e) { if (null !== $this->logger) { $this->logger->info('User for remember-me cookie not found.', ['exception' => $e]); } diff --git a/src/Symfony/Component/Security/Http/RememberMe/PersistentTokenBasedRememberMeServices.php b/src/Symfony/Component/Security/Http/RememberMe/PersistentTokenBasedRememberMeServices.php index 5711f0f5acca8..c7376faa910d9 100644 --- a/src/Symfony/Component/Security/Http/RememberMe/PersistentTokenBasedRememberMeServices.php +++ b/src/Symfony/Component/Security/Http/RememberMe/PersistentTokenBasedRememberMeServices.php @@ -93,7 +93,24 @@ protected function processAutoLoginCookie(array $cookieParts, Request $request) ) ); - return $this->getUserProvider($persistentToken->getClass())->loadUserByUsername($persistentToken->getUsername()); + $userProvider = $this->getUserProvider($persistentToken->getClass()); + // @deprecated since 5.3, change to $persistentToken->getUserIdentifier() in 6.0 + if (method_exists($persistentToken, 'getUserIdentifier')) { + $userIdentifier = $persistentToken->getUserIdentifier(); + } else { + trigger_deprecation('symfony/security-core', '5.3', 'Not implementing method "getUserIdentifier()" in persistent token "%s" is deprecated. This method will replace "loadUserByUsername()" in Symfony 6.0.', get_debug_type($persistentToken)); + + $userIdentifier = $persistentToken->getUsername(); + } + + // @deprecated since 5.3, change to $userProvider->loadUserByIdentifier() in 6.0 + if (method_exists($userProvider, 'loadUserByIdentifier')) { + return $userProvider->loadUserByIdentifier($userIdentifier); + } else { + trigger_deprecation('symfony/security-core', '5.3', 'Not implementing method "loadUserByIdentifier()" in user provider "%s" is deprecated. This method will replace "loadUserByUsername()" in Symfony 6.0.', get_debug_type($userProvider)); + + return $userProvider->loadUserByUsername($userIdentifier); + } } /** @@ -107,7 +124,8 @@ protected function onLoginSuccess(Request $request, Response $response, TokenInt $this->tokenProvider->createNewToken( new PersistentToken( \get_class($user = $token->getUser()), - $user->getUsername(), + // @deprecated since 5.3, change to $user->getUserIdentifier() in 6.0 + method_exists($user, 'getUserIdentifier') ? $user->getUserIdentifier() : $user->getUsername(), $series, $this->generateHash($tokenValue), new \DateTime() diff --git a/src/Symfony/Component/Security/Http/RememberMe/TokenBasedRememberMeServices.php b/src/Symfony/Component/Security/Http/RememberMe/TokenBasedRememberMeServices.php index 80e0b058157e2..a6e79f42e56d7 100644 --- a/src/Symfony/Component/Security/Http/RememberMe/TokenBasedRememberMeServices.php +++ b/src/Symfony/Component/Security/Http/RememberMe/TokenBasedRememberMeServices.php @@ -35,12 +35,20 @@ protected function processAutoLoginCookie(array $cookieParts, Request $request) throw new AuthenticationException('The cookie is invalid.'); } - [$class, $username, $expires, $hash] = $cookieParts; - if (false === $username = base64_decode($username, true)) { - throw new AuthenticationException('$username contains a character from outside the base64 alphabet.'); + [$class, $userIdentifier, $expires, $hash] = $cookieParts; + if (false === $userIdentifier = base64_decode($userIdentifier, true)) { + throw new AuthenticationException('$userIdentifier contains a character from outside the base64 alphabet.'); } try { - $user = $this->getUserProvider($class)->loadUserByUsername($username); + $userProvider = $this->getUserProvider($class); + // @deprecated since 5.3, change to $userProvider->loadUserByIdentifier() in 6.0 + if (method_exists($userProvider, 'loadUserByIdentifier')) { + $user = $userProvider->loadUserByIdentifier($userIdentifier); + } else { + trigger_deprecation('symfony/security-core', '5.3', 'Not implementing method "loadUserByIdentifier()" in user provider "%s" is deprecated. This method will replace "loadUserByUsername()" in Symfony 6.0.', get_debug_type($userProvider)); + + $user = $userProvider->loadUserByUsername($userIdentifier); + } } catch (\Exception $e) { if (!$e instanceof AuthenticationException) { $e = new AuthenticationException($e->getMessage(), $e->getCode(), $e); @@ -53,7 +61,7 @@ protected function processAutoLoginCookie(array $cookieParts, Request $request) throw new \RuntimeException(sprintf('The UserProviderInterface implementation must return an instance of UserInterface, but returned "%s".', get_debug_type($user))); } - if (true !== hash_equals($this->generateCookieHash($class, $username, $expires, $user->getPassword()), $hash)) { + if (true !== hash_equals($this->generateCookieHash($class, $userIdentifier, $expires, $user->getPassword()), $hash)) { throw new AuthenticationException('The cookie\'s hash is invalid.'); } @@ -71,7 +79,8 @@ protected function onLoginSuccess(Request $request, Response $response, TokenInt { $user = $token->getUser(); $expires = time() + $this->options['lifetime']; - $value = $this->generateCookieValue(\get_class($user), $user->getUsername(), $expires, $user->getPassword()); + // @deprecated since 5.3, change to $user->getUserIdentifier() in 6.0 + $value = $this->generateCookieValue(\get_class($user), method_exists($user, 'getUserIdentifier') ? $user->getUserIdentifier() : $user->getUsername(), $expires, $user->getPassword()); $response->headers->setCookie( new Cookie( @@ -96,15 +105,15 @@ protected function onLoginSuccess(Request $request, Response $response, TokenInt * * @return string */ - protected function generateCookieValue(string $class, string $username, int $expires, ?string $password) + protected function generateCookieValue(string $class, string $userIdentifier, int $expires, ?string $password) { - // $username is encoded because it might contain COOKIE_DELIMITER, + // $userIdentifier is encoded because it might contain COOKIE_DELIMITER, // we assume other values don't return $this->encodeCookie([ $class, - base64_encode($username), + base64_encode($userIdentifier), $expires, - $this->generateCookieHash($class, $username, $expires, $password), + $this->generateCookieHash($class, $userIdentifier, $expires, $password), ]); } @@ -116,8 +125,8 @@ protected function generateCookieValue(string $class, string $username, int $exp * * @return string */ - protected function generateCookieHash(string $class, string $username, int $expires, ?string $password) + protected function generateCookieHash(string $class, string $userIdentifier, int $expires, ?string $password) { - return hash_hmac('sha256', $class.self::COOKIE_DELIMITER.$username.self::COOKIE_DELIMITER.$expires.self::COOKIE_DELIMITER.$password, $this->getSecret()); + return hash_hmac('sha256', $class.self::COOKIE_DELIMITER.$userIdentifier.self::COOKIE_DELIMITER.$expires.self::COOKIE_DELIMITER.$password, $this->getSecret()); } } diff --git a/src/Symfony/Component/Security/Http/Tests/Authentication/AuthenticatorManagerTest.php b/src/Symfony/Component/Security/Http/Tests/Authentication/AuthenticatorManagerTest.php index 969243f83e862..89f0ddf07a10a 100644 --- a/src/Symfony/Component/Security/Http/Tests/Authentication/AuthenticatorManagerTest.php +++ b/src/Symfony/Component/Security/Http/Tests/Authentication/AuthenticatorManagerTest.php @@ -44,6 +44,7 @@ protected function setUp(): void $this->request = new Request(); $this->user = new InMemoryUser('wouter', null); $this->token = $this->createMock(TokenInterface::class); + $this->token->expects($this->any())->method('getUser')->willReturn($this->user); $this->response = $this->createMock(Response::class); } @@ -166,6 +167,7 @@ public function testAuthenticateRequestCanModifyTokenFromEvent() $authenticator->expects($this->any())->method('createAuthenticatedToken')->willReturn($this->token); $modifiedToken = $this->createMock(TokenInterface::class); + $modifiedToken->expects($this->any())->method('getUser')->willReturn($this->user); $listenerCalled = false; $this->eventDispatcher->addListener(AuthenticationTokenCreatedEvent::class, function (AuthenticationTokenCreatedEvent $event) use (&$listenerCalled, $modifiedToken) { $event->setAuthenticatedToken($modifiedToken); @@ -198,6 +200,7 @@ public function testAuthenticateUserCanModifyTokenFromEvent() $authenticator->expects($this->any())->method('onAuthenticationSuccess')->willReturn($this->response); $modifiedToken = $this->createMock(TokenInterface::class); + $modifiedToken->expects($this->any())->method('getUser')->willReturn($this->user); $listenerCalled = false; $this->eventDispatcher->addListener(AuthenticationTokenCreatedEvent::class, function (AuthenticationTokenCreatedEvent $event) use (&$listenerCalled, $modifiedToken) { $event->setAuthenticatedToken($modifiedToken); diff --git a/src/Symfony/Component/Security/Http/Tests/Authenticator/JsonLoginAuthenticatorTest.php b/src/Symfony/Component/Security/Http/Tests/Authenticator/JsonLoginAuthenticatorTest.php index 1f86aa648abe7..47e02689ead93 100644 --- a/src/Symfony/Component/Security/Http/Tests/Authenticator/JsonLoginAuthenticatorTest.php +++ b/src/Symfony/Component/Security/Http/Tests/Authenticator/JsonLoginAuthenticatorTest.php @@ -17,7 +17,7 @@ use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Core\Exception\BadCredentialsException; use Symfony\Component\Security\Core\Security; -use Symfony\Component\Security\Core\User\UserProviderInterface; +use Symfony\Component\Security\Core\User\InMemoryUserProvider; use Symfony\Component\Security\Http\Authenticator\JsonLoginAuthenticator; use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\PasswordCredentials; use Symfony\Component\Security\Http\HttpUtils; @@ -32,7 +32,7 @@ class JsonLoginAuthenticatorTest extends TestCase protected function setUp(): void { - $this->userProvider = $this->createMock(UserProviderInterface::class); + $this->userProvider = new InMemoryUserProvider(); } /** diff --git a/src/Symfony/Component/Security/Http/Tests/Authenticator/X509AuthenticatorTest.php b/src/Symfony/Component/Security/Http/Tests/Authenticator/X509AuthenticatorTest.php index 0acf418068078..7ee6aaa973894 100644 --- a/src/Symfony/Component/Security/Http/Tests/Authenticator/X509AuthenticatorTest.php +++ b/src/Symfony/Component/Security/Http/Tests/Authenticator/X509AuthenticatorTest.php @@ -48,7 +48,7 @@ public function testAuthentication($username, $credentials) $this->userProvider->createUser(new InMemoryUser($username, null)); $passport = $this->authenticator->authenticate($request); - $this->assertEquals($username, $passport->getUser()->getUsername()); + $this->assertEquals($username, $passport->getUser()->getUserIdentifier()); } public static function provideServerVars() @@ -69,7 +69,7 @@ public function testAuthenticationNoUser($emailAddress, $credentials) $this->userProvider->createUser(new InMemoryUser($emailAddress, null)); $passport = $this->authenticator->authenticate($request); - $this->assertEquals($emailAddress, $passport->getUser()->getUsername()); + $this->assertEquals($emailAddress, $passport->getUser()->getUserIdentifier()); } public static function provideServerVarsNoUser() @@ -102,7 +102,7 @@ public function testAuthenticationCustomUserKey() $this->userProvider->createUser(new InMemoryUser('TheUser', null)); $passport = $this->authenticator->authenticate($request); - $this->assertEquals('TheUser', $passport->getUser()->getUsername()); + $this->assertEquals('TheUser', $passport->getUser()->getUserIdentifier()); } public function testAuthenticationCustomCredentialsKey() @@ -117,7 +117,7 @@ public function testAuthenticationCustomCredentialsKey() $this->userProvider->createUser(new InMemoryUser('cert@example.com', null)); $passport = $authenticator->authenticate($request); - $this->assertEquals('cert@example.com', $passport->getUser()->getUsername()); + $this->assertEquals('cert@example.com', $passport->getUser()->getUserIdentifier()); } private function createRequest(array $server) diff --git a/src/Symfony/Component/Security/Http/Tests/EventListener/LoginThrottlingListenerTest.php b/src/Symfony/Component/Security/Http/Tests/EventListener/LoginThrottlingListenerTest.php index 1374fc39b0a4a..bd53763273b50 100644 --- a/src/Symfony/Component/Security/Http/Tests/EventListener/LoginThrottlingListenerTest.php +++ b/src/Symfony/Component/Security/Http/Tests/EventListener/LoginThrottlingListenerTest.php @@ -90,7 +90,7 @@ private function createPassport($username) private function createLoginSuccessfulEvent($passport, $username = 'wouter') { $token = $this->createMock(TokenInterface::class); - $token->expects($this->any())->method('getUsername')->willReturn($username); + $token->expects($this->any())->method('getUserIdentifier')->willReturn($username); return new LoginSuccessEvent($this->createMock(AuthenticatorInterface::class), $passport, $token, $this->requestStack->getCurrentRequest(), null, 'main'); } diff --git a/src/Symfony/Component/Security/Http/Tests/EventListener/PasswordMigratingListenerTest.php b/src/Symfony/Component/Security/Http/Tests/EventListener/PasswordMigratingListenerTest.php index b347276b0f673..e07b502a80c35 100644 --- a/src/Symfony/Component/Security/Http/Tests/EventListener/PasswordMigratingListenerTest.php +++ b/src/Symfony/Component/Security/Http/Tests/EventListener/PasswordMigratingListenerTest.php @@ -99,14 +99,14 @@ public function testUpgradeWithUpgrader() public function testUpgradeWithoutUpgrader() { $userLoader = $this->getMockForAbstractClass(TestMigratingUserProvider::class); - $userLoader->expects($this->any())->method('loadUserByUsername')->willReturn($this->user); + $userLoader->expects($this->any())->method('loadUserByIdentifier')->willReturn($this->user); $userLoader->expects($this->once()) ->method('upgradePassword') ->with($this->user, 'new-hash') ; - $event = $this->createEvent(new SelfValidatingPassport(new UserBadge('test', [$userLoader, 'loadUserByUsername']), [new PasswordUpgradeBadge('pa$$word')])); + $event = $this->createEvent(new SelfValidatingPassport(new UserBadge('test', [$userLoader, 'loadUserByIdentifier']), [new PasswordUpgradeBadge('pa$$word')])); $this->listener->onLoginSuccess($event); } @@ -124,6 +124,7 @@ private function createEvent(PassportInterface $passport) abstract class TestMigratingUserProvider implements UserProviderInterface, PasswordUpgraderInterface { abstract public function upgradePassword(PasswordAuthenticatedUserInterface $user, string $newHashedPassword): void; + abstract public function loadUserByIdentifier(string $identifier): UserInterface; } abstract class TestPasswordAuthenticatedUser implements UserInterface, PasswordAuthenticatedUserInterface diff --git a/src/Symfony/Component/Security/Http/Tests/EventListener/UserProviderListenerTest.php b/src/Symfony/Component/Security/Http/Tests/EventListener/UserProviderListenerTest.php index 8901b95658896..1f5a65ac0b926 100644 --- a/src/Symfony/Component/Security/Http/Tests/EventListener/UserProviderListenerTest.php +++ b/src/Symfony/Component/Security/Http/Tests/EventListener/UserProviderListenerTest.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Security\Http\Tests\EventListener; use PHPUnit\Framework\TestCase; +use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; use Symfony\Component\Security\Core\User\InMemoryUser; use Symfony\Component\Security\Core\User\InMemoryUserProvider; use Symfony\Component\Security\Http\Authenticator\AuthenticatorInterface; @@ -22,6 +23,8 @@ class UserProviderListenerTest extends TestCase { + use ExpectDeprecationTrait; + private $userProvider; private $listener; @@ -38,7 +41,7 @@ public function testSetUserProvider() $this->listener->checkPassport(new CheckPassportEvent($this->createMock(AuthenticatorInterface::class), $passport)); $badge = $passport->getBadge(UserBadge::class); - $this->assertEquals([$this->userProvider, 'loadUserByUsername'], $badge->getUserLoader()); + $this->assertEquals([$this->userProvider, 'loadUserByIdentifier'], $badge->getUserLoader()); $user = new InMemoryUser('wouter', null); $this->userProvider->createUser($user); diff --git a/src/Symfony/Component/Security/Http/Tests/Firewall/AccessListenerTest.php b/src/Symfony/Component/Security/Http/Tests/Firewall/AccessListenerTest.php index 525456b59ebd4..aac67379b4e7f 100644 --- a/src/Symfony/Component/Security/Http/Tests/Firewall/AccessListenerTest.php +++ b/src/Symfony/Component/Security/Http/Tests/Firewall/AccessListenerTest.php @@ -338,12 +338,7 @@ public function testHandleMWithultipleAttributesShouldBeHandledAsAnd() ->willReturn([['foo' => 'bar', 'bar' => 'baz'], null]) ; - $authenticatedToken = $this->createMock(TokenInterface::class); - $authenticatedToken - ->expects($this->any()) - ->method('isAuthenticated') - ->willReturn(true) - ; + $authenticatedToken = new UsernamePasswordToken('test', 'test', 'test', ['ROLE_USER']); $tokenStorage = new TokenStorage(); $tokenStorage->setToken($authenticatedToken); diff --git a/src/Symfony/Component/Security/Http/Tests/Firewall/ContextListenerTest.php b/src/Symfony/Component/Security/Http/Tests/Firewall/ContextListenerTest.php index a48c8f7cb0515..6a612cdb80928 100644 --- a/src/Symfony/Component/Security/Http/Tests/Firewall/ContextListenerTest.php +++ b/src/Symfony/Component/Security/Http/Tests/Firewall/ContextListenerTest.php @@ -31,7 +31,7 @@ use Symfony\Component\Security\Core\Authentication\Token\Storage\UsageTrackingTokenStorage; use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; use Symfony\Component\Security\Core\Exception\UnsupportedUserException; -use Symfony\Component\Security\Core\Exception\UsernameNotFoundException; +use Symfony\Component\Security\Core\Exception\UserNotFoundException; use Symfony\Component\Security\Core\User\InMemoryUser; use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Core\User\UserProviderInterface; @@ -69,7 +69,7 @@ public function testOnKernelResponseWillAddSession() $token = unserialize($session->get('_security_session')); $this->assertInstanceOf(UsernamePasswordToken::class, $token); - $this->assertEquals('test1', $token->getUsername()); + $this->assertEquals('test1', $token->getUserIdentifier()); } public function testOnKernelResponseWillReplaceSession() @@ -81,7 +81,7 @@ public function testOnKernelResponseWillReplaceSession() $token = unserialize($session->get('_security_session')); $this->assertInstanceOf(UsernamePasswordToken::class, $token); - $this->assertEquals('test1', $token->getUsername()); + $this->assertEquals('test1', $token->getUserIdentifier()); } public function testOnKernelResponseWillRemoveSession() @@ -478,7 +478,12 @@ public function __construct($throwsUnsupportedException) public function loadUserByUsername($username): UserInterface { - throw new UsernameNotFoundException(); + throw new UserNotFoundException(); + } + + public function loadUserByIdentifier(string $identifier): UserInterface + { + throw new UserNotFoundException(); } public function refreshUser(UserInterface $user): UserInterface @@ -509,6 +514,10 @@ public function loadUserByUsername($username): UserInterface { } + public function loadUserByIdentifier(string $identifier): UserInterface + { + } + public function refreshUser(UserInterface $user): UserInterface { if (!$user instanceof InMemoryUser) { @@ -516,7 +525,7 @@ public function refreshUser(UserInterface $user): UserInterface } if (null === $this->refreshedUser) { - throw new UsernameNotFoundException(); + throw new UserNotFoundException(); } return $this->refreshedUser; diff --git a/src/Symfony/Component/Security/Http/Tests/Firewall/SwitchUserListenerTest.php b/src/Symfony/Component/Security/Http/Tests/Firewall/SwitchUserListenerTest.php index 9e5adefab7036..64cc07e15d6b3 100644 --- a/src/Symfony/Component/Security/Http/Tests/Firewall/SwitchUserListenerTest.php +++ b/src/Symfony/Component/Security/Http/Tests/Firewall/SwitchUserListenerTest.php @@ -22,7 +22,6 @@ use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface; use Symfony\Component\Security\Core\Exception\AccessDeniedException; use Symfony\Component\Security\Core\Exception\AuthenticationCredentialsNotFoundException; -use Symfony\Component\Security\Core\Exception\UsernameNotFoundException; use Symfony\Component\Security\Core\User\InMemoryUser; use Symfony\Component\Security\Core\User\InMemoryUserProvider; use Symfony\Component\Security\Core\User\UserCheckerInterface; @@ -200,11 +199,11 @@ public function testSwitchUser() $this->request->query->set('_switch_user', 'kuba'); $this->accessDecisionManager->expects($this->once()) - ->method('decide')->with($token, ['ROLE_ALLOWED_TO_SWITCH'], $this->callback(function ($user) { return 'kuba' === $user->getUsername(); })) + ->method('decide')->with($token, ['ROLE_ALLOWED_TO_SWITCH'], $this->callback(function ($user) { return 'kuba' === $user->getUserIdentifier(); })) ->willReturn(true); $this->userChecker->expects($this->once()) - ->method('checkPostAuth')->with($this->callback(function ($user) { return 'kuba' === $user->getUsername(); })); + ->method('checkPostAuth')->with($this->callback(function ($user) { return 'kuba' === $user->getUserIdentifier(); })); $listener = new SwitchUserListener($this->tokenStorage, $this->userProvider, $this->userChecker, 'provider123', $this->accessDecisionManager); $listener($this->event); @@ -222,11 +221,9 @@ public function testSwitchUserAlreadySwitched() $tokenStorage = new TokenStorage(); $tokenStorage->setToken($alreadySwitchedToken); - $targetUser = new InMemoryUser('kuba', 'password', ['ROLE_FOO', 'ROLE_BAR']); - $this->request->query->set('_switch_user', 'kuba'); - $targetsUser = $this->callback(function ($user) { return 'kuba' === $user->getUsername(); }); + $targetsUser = $this->callback(function ($user) { return 'kuba' === $user->getUserIdentifier(); }); $this->accessDecisionManager->expects($this->once()) ->method('decide')->with($originalToken, ['ROLE_ALLOWED_TO_SWITCH'], $targetsUser) ->willReturn(true); @@ -240,7 +237,7 @@ public function testSwitchUserAlreadySwitched() $this->assertSame([], $this->request->query->all()); $this->assertSame('', $this->request->server->get('QUERY_STRING')); $this->assertInstanceOf(SwitchUserToken::class, $tokenStorage->getToken()); - $this->assertSame('kuba', $tokenStorage->getToken()->getUsername()); + $this->assertSame('kuba', $tokenStorage->getToken()->getUserIdentifier()); $this->assertSame($originalToken, $tokenStorage->getToken()->getOriginalToken()); } @@ -279,7 +276,7 @@ public function testSwitchUserKeepsOtherQueryStringParameters() 'section' => 2, ]); - $targetsUser = $this->callback(function ($user) { return 'kuba' === $user->getUsername(); }); + $targetsUser = $this->callback(function ($user) { return 'kuba' === $user->getUserIdentifier(); }); $this->accessDecisionManager->expects($this->once()) ->method('decide')->with($token, ['ROLE_ALLOWED_TO_SWITCH'], $targetsUser) ->willReturn(true); @@ -306,7 +303,7 @@ public function testSwitchUserWithReplacedToken() $this->request->query->set('_switch_user', 'kuba'); $this->accessDecisionManager->expects($this->any()) - ->method('decide')->with($token, ['ROLE_ALLOWED_TO_SWITCH'], $this->callback(function ($user) { return 'kuba' === $user->getUsername(); })) + ->method('decide')->with($token, ['ROLE_ALLOWED_TO_SWITCH'], $this->callback(function ($user) { return 'kuba' === $user->getUserIdentifier(); })) ->willReturn(true); $dispatcher = $this->createMock(EventDispatcherInterface::class); @@ -314,8 +311,8 @@ public function testSwitchUserWithReplacedToken() ->expects($this->once()) ->method('dispatch') ->with( - $this->callback(function (SwitchUserEvent $event) use ($replacedToken) { - if ('kuba' !== $event->getTargetUser()->getUsername()) { + $this->callback(function (SwitchUserEvent $event) use ($replacedToken, $user) { + if ('kuba' !== $event->getTargetUser()->getUserIdentifier()) { return false; } $event->setToken($replacedToken); @@ -343,12 +340,11 @@ public function testSwitchUserThrowsAuthenticationExceptionIfNoCurrentToken() public function testSwitchUserStateless() { $token = new UsernamePasswordToken('username', '', 'key', ['ROLE_FOO']); - $user = new InMemoryUser('username', 'password', []); $this->tokenStorage->setToken($token); $this->request->query->set('_switch_user', 'kuba'); - $targetsUser = $this->callback(function ($user) { return 'kuba' === $user->getUsername(); }); + $targetsUser = $this->callback(function ($user) { return 'kuba' === $user->getUserIdentifier(); }); $this->accessDecisionManager->expects($this->once()) ->method('decide')->with($token, ['ROLE_ALLOWED_TO_SWITCH'], $targetsUser) ->willReturn(true); diff --git a/src/Symfony/Component/Security/Http/Tests/LoginLink/LoginLinkHandlerTest.php b/src/Symfony/Component/Security/Http/Tests/LoginLink/LoginLinkHandlerTest.php index 9d063f6630290..74050396ac077 100644 --- a/src/Symfony/Component/Security/Http/Tests/LoginLink/LoginLinkHandlerTest.php +++ b/src/Symfony/Component/Security/Http/Tests/LoginLink/LoginLinkHandlerTest.php @@ -18,7 +18,7 @@ use Symfony\Component\PropertyAccess\PropertyAccessorInterface; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Component\Routing\RequestContext; -use Symfony\Component\Security\Core\Exception\UsernameNotFoundException; +use Symfony\Component\Security\Core\Exception\UserNotFoundException; use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Core\User\UserProviderInterface; use Symfony\Component\Security\Http\LoginLink\Exception\ExpiredLoginLinkException; @@ -208,13 +208,13 @@ public function createUser(TestLoginLinkHandlerUser $user): void $this->users[$user->getUsername()] = $user; } - public function loadUserByUsername(string $username): TestLoginLinkHandlerUser + public function loadUserByIdentifier(string $userIdentifier): TestLoginLinkHandlerUser { - if (!isset($this->users[$username])) { - throw new UsernameNotFoundException(); + if (!isset($this->users[$userIdentifier])) { + throw new UserNotFoundException(); } - return clone $this->users[$username]; + return clone $this->users[$userIdentifier]; } public function refreshUser(UserInterface $user) @@ -263,6 +263,11 @@ public function getUsername() return $this->username; } + public function getUserIdentifier() + { + return $this->username; + } + public function eraseCredentials() { } 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