[Security] Rename UserInterface::getUsername() to getUserIdentifier()

This commit is contained in:
Wouter de Jong 2021-03-10 17:10:58 +01:00 committed by Robin Chalas
parent c469ea6fff
commit 8afd7a3765
116 changed files with 1000 additions and 486 deletions

View File

@ -9,6 +9,7 @@ Asset
DoctrineBridge DoctrineBridge
-------------- --------------
* Deprecate `UserLoaderInterface::loadUserByUsername()` in favor of `UserLoaderInterface::loadUserByIdentifier()
* Remove `UuidV*Generator` classes * Remove `UuidV*Generator` classes
DomCrawler 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 `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 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 * Deprecate all classes in the `Core\Encoder\` sub-namespace, use the `PasswordHasher` component instead

View File

@ -6,6 +6,11 @@ Asset
* Removed `RemoteJsonManifestVersionStrategy`, use `JsonManifestVersionStrategy` instead. * Removed `RemoteJsonManifestVersionStrategy`, use `JsonManifestVersionStrategy` instead.
DoctrineBridge
--------------
* Remove `UserLoaderInterface::loadUserByUsername()` in favor of `UserLoaderInterface::loadUserByIdentifier()
Config 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 * Calling `PasswordUpgraderInterface::upgradePassword()` with a `UserInterface` instance that
does not implement `PasswordAuthenticatedUserInterface` now throws a `\TypeError`. does not implement `PasswordAuthenticatedUserInterface` now throws a `\TypeError`.
* Calling methods `hashPassword()`, `isPasswordValid()` and `needsRehash()` on `UserPasswordHasherInterface` * Calling methods `hashPassword()`, `isPasswordValid()` and `needsRehash()` on `UserPasswordHasherInterface`

View File

@ -4,6 +4,7 @@ CHANGELOG
5.3 5.3
--- ---
* Deprecate `UserLoaderInterface::loadUserByUsername()` in favor of `UserLoaderInterface::loadUserByIdentifier()
* Deprecate `DoctrineTestHelper` and `TestRepositoryFactory` * Deprecate `DoctrineTestHelper` and `TestRepositoryFactory`
* [BC BREAK] Remove `UuidV*Generator` classes * [BC BREAK] Remove `UuidV*Generator` classes
* Add `UuidGenerator` * Add `UuidGenerator`

View File

@ -119,7 +119,8 @@ class DoctrineTokenProvider implements TokenProviderInterface
.' VALUES (:class, :username, :series, :value, :lastUsed)'; .' VALUES (:class, :username, :series, :value, :lastUsed)';
$paramValues = [ $paramValues = [
'class' => $token->getClass(), '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(), 'series' => $token->getSeries(),
'value' => $token->getTokenValue(), 'value' => $token->getTokenValue(),
'lastUsed' => $token->getLastUsed(), 'lastUsed' => $token->getLastUsed(),

View File

@ -16,7 +16,7 @@ use Doctrine\Persistence\Mapping\ClassMetadata;
use Doctrine\Persistence\ObjectManager; use Doctrine\Persistence\ObjectManager;
use Doctrine\Persistence\ObjectRepository; use Doctrine\Persistence\ObjectRepository;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException; 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\PasswordAuthenticatedUserInterface;
use Symfony\Component\Security\Core\User\PasswordUpgraderInterface; use Symfony\Component\Security\Core\User\PasswordUpgraderInterface;
use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Core\User\UserInterface;
@ -50,21 +50,35 @@ class EntityUserProvider implements UserProviderInterface, PasswordUpgraderInter
* {@inheritdoc} * {@inheritdoc}
*/ */
public function loadUserByUsername(string $username) 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(); $repository = $this->getRepository();
if (null !== $this->property) { if (null !== $this->property) {
$user = $repository->findOneBy([$this->property => $username]); $user = $repository->findOneBy([$this->property => $identifier]);
} else { } else {
if (!$repository instanceof UserLoaderInterface) { 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))); 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) { if (null === $user) {
$e = new UsernameNotFoundException(sprintf('User "%s" not found.', $username)); $e = new UserNotFoundException(sprintf('User "%s" not found.', $identifier));
$e->setUsername($username); $e->setUserIdentifier($identifier);
throw $e; throw $e;
} }
@ -96,8 +110,8 @@ class EntityUserProvider implements UserProviderInterface, PasswordUpgraderInter
$refreshedUser = $repository->find($id); $refreshedUser = $repository->find($id);
if (null === $refreshedUser) { if (null === $refreshedUser) {
$e = new UsernameNotFoundException('User with id '.json_encode($id).' not found.'); $e = new UserNotFoundException('User with id '.json_encode($id).' not found.');
$e->setUsername(json_encode($id)); $e->setUserIdentifier(json_encode($id));
throw $e; throw $e;
} }

View File

@ -22,16 +22,11 @@ use Symfony\Component\Security\Core\User\UserInterface;
* *
* @see UserInterface * @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 <michal@kmt-studio.pl> * @author Michal Trojanowski <michal@kmt-studio.pl>
*/ */
interface UserLoaderInterface 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);
} }

View File

@ -37,4 +37,9 @@ class BaseUser
{ {
return $this->username; return $this->username;
} }
public function getUserIdentifier(): string
{
return $this->username;
}
} }

View File

@ -53,6 +53,11 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface
return $this->name; return $this->name;
} }
public function getUserIdentifier(): string
{
return $this->name;
}
public function eraseCredentials() public function eraseCredentials()
{ {
} }

View File

@ -21,7 +21,7 @@ use Symfony\Bridge\Doctrine\Security\User\EntityUserProvider;
use Symfony\Bridge\Doctrine\Security\User\UserLoaderInterface; use Symfony\Bridge\Doctrine\Security\User\UserLoaderInterface;
use Symfony\Bridge\Doctrine\Tests\DoctrineTestHelper; use Symfony\Bridge\Doctrine\Tests\DoctrineTestHelper;
use Symfony\Bridge\Doctrine\Tests\Fixtures\User; 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\PasswordAuthenticatedUserInterface;
use Symfony\Component\Security\Core\User\PasswordUpgraderInterface; use Symfony\Component\Security\Core\User\PasswordUpgraderInterface;
use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Core\User\UserInterface;
@ -60,7 +60,7 @@ class EntityUserProviderTest extends TestCase
$provider = new EntityUserProvider($this->getManager($em), 'Symfony\Bridge\Doctrine\Tests\Fixtures\User', 'name'); $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() public function testLoadUserByUsernameWithUserLoaderRepositoryAndWithoutProperty()
@ -70,7 +70,7 @@ class EntityUserProviderTest extends TestCase
$repository = $this->createMock(UserLoaderRepository::class); $repository = $this->createMock(UserLoaderRepository::class);
$repository $repository
->expects($this->once()) ->expects($this->once())
->method('loadUserByUsername') ->method('loadUserByIdentifier')
->with('user1') ->with('user1')
->willReturn($user); ->willReturn($user);
@ -82,7 +82,7 @@ class EntityUserProviderTest extends TestCase
->willReturn($repository); ->willReturn($repository);
$provider = new EntityUserProvider($this->getManager($em), 'Symfony\Bridge\Doctrine\Tests\Fixtures\User'); $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() public function testLoadUserByUsernameWithNonUserLoaderRepositoryAndWithoutProperty()
@ -98,7 +98,7 @@ class EntityUserProviderTest extends TestCase
$em->flush(); $em->flush();
$provider = new EntityUserProvider($this->getManager($em), 'Symfony\Bridge\Doctrine\Tests\Fixtures\User'); $provider = new EntityUserProvider($this->getManager($em), 'Symfony\Bridge\Doctrine\Tests\Fixtures\User');
$provider->loadUserByUsername('user1'); $provider->loadUserByIdentifier('user1');
} }
public function testRefreshUserRequiresId() public function testRefreshUserRequiresId()
@ -126,7 +126,7 @@ class EntityUserProviderTest extends TestCase
$provider = new EntityUserProvider($this->getManager($em), 'Symfony\Bridge\Doctrine\Tests\Fixtures\User', 'name'); $provider = new EntityUserProvider($this->getManager($em), 'Symfony\Bridge\Doctrine\Tests\Fixtures\User', 'name');
$user2 = new User(1, 2, 'user2'); $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'); $this->expectExceptionMessage('User with id {"id1":1,"id2":2} not found');
$provider->refreshUser($user2); $provider->refreshUser($user2);
@ -153,7 +153,7 @@ class EntityUserProviderTest extends TestCase
{ {
$repository = $this->createMock(UserLoaderRepository::class); $repository = $this->createMock(UserLoaderRepository::class);
$repository->expects($this->once()) $repository->expects($this->once())
->method('loadUserByUsername') ->method('loadUserByIdentifier')
->with('name') ->with('name')
->willReturn( ->willReturn(
$this->createMock(UserInterface::class) $this->createMock(UserInterface::class)
@ -164,7 +164,7 @@ class EntityUserProviderTest extends TestCase
'Symfony\Bridge\Doctrine\Tests\Fixtures\User' 'Symfony\Bridge\Doctrine\Tests\Fixtures\User'
); );
$provider->loadUserByUsername('name'); $provider->loadUserByIdentifier('name');
} }
public function testLoadUserByUserNameShouldDeclineInvalidInterface() public function testLoadUserByUserNameShouldDeclineInvalidInterface()
@ -177,7 +177,7 @@ class EntityUserProviderTest extends TestCase
'Symfony\Bridge\Doctrine\Tests\Fixtures\User' 'Symfony\Bridge\Doctrine\Tests\Fixtures\User'
); );
$provider->loadUserByUsername('name'); $provider->loadUserByIdentifier('name');
} }
public function testPasswordUpgrades() public function testPasswordUpgrades()
@ -231,6 +231,7 @@ class EntityUserProviderTest extends TestCase
abstract class UserLoaderRepository implements ObjectRepository, UserLoaderInterface abstract class UserLoaderRepository implements ObjectRepository, UserLoaderInterface
{ {
abstract public function loadUserByIdentifier(string $identifier): ?UserInterface;
} }
abstract class PasswordUpgraderRepository implements ObjectRepository, PasswordUpgraderInterface abstract class PasswordUpgraderRepository implements ObjectRepository, PasswordUpgraderInterface

View File

@ -42,10 +42,16 @@ abstract class AbstractTokenProcessor
if (null !== $token = $this->getToken()) { if (null !== $token = $this->getToken()) {
$record['extra'][$this->getKey()] = [ $record['extra'][$this->getKey()] = [
'username' => $token->getUsername(),
'authenticated' => $token->isAuthenticated(), 'authenticated' => $token->isAuthenticated(),
'roles' => $token->getRoleNames(), '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; return $record;

View File

@ -37,11 +37,15 @@ class SwitchUserTokenProcessorTest extends TestCase
$expected = [ $expected = [
'impersonator_token' => [ 'impersonator_token' => [
'username' => 'original_user',
'authenticated' => true, 'authenticated' => true,
'roles' => ['ROLE_SUPER_ADMIN'], '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']);
} }
} }

View File

@ -23,8 +23,12 @@ use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
*/ */
class TokenProcessorTest extends TestCase 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']); $token = new UsernamePasswordToken('user', 'password', 'provider', ['ROLE_USER']);
$tokenStorage = $this->createMock(TokenStorageInterface::class); $tokenStorage = $this->createMock(TokenStorageInterface::class);
$tokenStorage->method('getToken')->willReturn($token); $tokenStorage->method('getToken')->willReturn($token);
@ -38,4 +42,24 @@ class TokenProcessorTest extends TestCase
$this->assertEquals($token->isAuthenticated(), $record['extra']['token']['authenticated']); $this->assertEquals($token->isAuthenticated(), $record['extra']['token']['authenticated']);
$this->assertEquals(['ROLE_USER'], $record['extra']['token']['roles']); $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']);
}
} }

View File

@ -21,6 +21,6 @@ class SecurityController implements ContainerAwareInterface
public function profileAction() 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().'!');
} }
} }

View File

@ -97,7 +97,9 @@ class SecurityDataCollector extends DataCollector implements LateDataCollectorIn
$impersonatorUser = null; $impersonatorUser = null;
if ($token instanceof SwitchUserToken) { 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) { if (null !== $this->roleHierarchy) {
@ -126,7 +128,8 @@ class SecurityDataCollector extends DataCollector implements LateDataCollectorIn
'token' => $token, 'token' => $token,
'token_class' => $this->hasVarDumper ? new ClassStub(\get_class($token)) : \get_class($token), 'token_class' => $this->hasVarDumper ? new ClassStub(\get_class($token)) : \get_class($token),
'logout_url' => $logoutUrl, '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, 'roles' => $assignedRoles,
'inherited_roles' => array_unique($inheritedRoles), 'inherited_roles' => array_unique($inheritedRoles),
'supports_role_hierarchy' => null !== $this->roleHierarchy, 'supports_role_hierarchy' => null !== $this->roleHierarchy,

View File

@ -48,8 +48,30 @@ class InMemoryFactory implements UserProviderFactoryInterface
->fixXmlConfig('user') ->fixXmlConfig('user')
->children() ->children()
->arrayNode('users') ->arrayNode('users')
->useAttributeAsKey('name') ->useAttributeAsKey('identifier')
->normalizeKeys(false) ->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') ->prototype('array')
->children() ->children()
->scalarNode('password')->defaultNull()->end() ->scalarNode('password')->defaultNull()->end()

View File

@ -136,7 +136,8 @@
</xsd:complexType> </xsd:complexType>
<xsd:complexType name="user"> <xsd:complexType name="user">
<xsd:attribute name="name" type="xsd:string" use="required" /> <xsd:attribute name="identifier" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
<xsd:attribute name="password" type="xsd:string" /> <xsd:attribute name="password" type="xsd:string" />
<xsd:attribute name="roles" type="xsd:string" /> <xsd:attribute name="roles" type="xsd:string" />
</xsd:complexType> </xsd:complexType>

View File

@ -297,7 +297,7 @@ abstract class CompleteConfigurationTest extends TestCase
} elseif (3 === $i) { } elseif (3 === $i) {
$this->assertEquals('IS_AUTHENTICATED_ANONYMOUSLY', $attributes[0]); $this->assertEquals('IS_AUTHENTICATED_ANONYMOUSLY', $attributes[0]);
$expression = $container->getDefinition((string) $attributes[1])->getArgument(0); $expression = $container->getDefinition((string) $attributes[1])->getArgument(0);
$this->assertEquals("token.getUsername() matches '/^admin/'", $expression); $this->assertEquals("token.getUserIdentifier() matches '/^admin/'", $expression);
} }
} }
} }

View File

@ -97,7 +97,7 @@ $container->loadFromExtension('security', [
'access_control' => [ 'access_control' => [
['path' => '/blog/524', 'role' => 'ROLE_USER', 'requires_channel' => 'https', 'methods' => ['get', 'POST'], 'port' => 8000], ['path' => '/blog/524', 'role' => 'ROLE_USER', 'requires_channel' => 'https', 'methods' => ['get', 'POST'], 'port' => 8000],
['path' => '/blog/.*', 'role' => 'IS_AUTHENTICATED_ANONYMOUSLY'], ['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' => [ 'role_hierarchy' => [

View File

@ -97,7 +97,7 @@ $container->loadFromExtension('security', [
'access_control' => [ 'access_control' => [
['path' => '/blog/524', 'role' => 'ROLE_USER', 'requires_channel' => 'https', 'methods' => ['get', 'POST'], 'port' => 8000], ['path' => '/blog/524', 'role' => 'ROLE_USER', 'requires_channel' => 'https', 'methods' => ['get', 'POST'], 'port' => 8000],
['path' => '/blog/.*', 'role' => 'IS_AUTHENTICATED_ANONYMOUSLY'], ['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' => [ 'role_hierarchy' => [

View File

@ -12,7 +12,7 @@
<provider name="default"> <provider name="default">
<memory> <memory>
<user name="foo" password="foo" roles="ROLE_USER" /> <user identifier="foo" password="foo" roles="ROLE_USER" />
</memory> </memory>
</provider> </provider>

View File

@ -10,7 +10,7 @@
<config> <config>
<provider name="default"> <provider name="default">
<memory> <memory>
<user name="foo" password="foo" roles="ROLE_USER" /> <user identifier="foo" password="foo" roles="ROLE_USER" />
</memory> </memory>
</provider> </provider>

View File

@ -12,7 +12,7 @@
<provider name="default"> <provider name="default">
<memory> <memory>
<user name="foo" password="foo" roles="ROLE_USER" /> <user identifier="foo" password="foo" roles="ROLE_USER" />
</memory> </memory>
</provider> </provider>

View File

@ -12,7 +12,7 @@
<provider name="default"> <provider name="default">
<memory> <memory>
<user name="foo" password="foo" roles="ROLE_USER" /> <user identifier="foo" password="foo" roles="ROLE_USER" />
</memory> </memory>
</provider> </provider>

View File

@ -25,20 +25,20 @@
<provider name="default"> <provider name="default">
<memory> <memory>
<user name="foo" password="foo" roles="ROLE_USER" /> <user identifier="foo" password="foo" roles="ROLE_USER" />
</memory> </memory>
</provider> </provider>
<provider name="digest"> <provider name="digest">
<memory> <memory>
<user name="foo" password="foo" roles="ROLE_USER, ROLE_ADMIN" /> <user identifier="foo" password="foo" roles="ROLE_USER, ROLE_ADMIN" />
</memory> </memory>
</provider> </provider>
<provider name="basic"> <provider name="basic">
<memory> <memory>
<user name="foo" password="0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33" roles="ROLE_SUPER_ADMIN" /> <user identifier="foo" password="0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33" roles="ROLE_SUPER_ADMIN" />
<user name="bar" password="0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33" roles="ROLE_USER, ROLE_ADMIN" /> <user identifier="bar" password="0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33" roles="ROLE_USER, ROLE_ADMIN" />
</memory> </memory>
</provider> </provider>
@ -78,6 +78,6 @@
<rule path="/blog/524" role="ROLE_USER" requires-channel="https" methods="get,POST" port="8000" /> <rule path="/blog/524" role="ROLE_USER" requires-channel="https" methods="get,POST" port="8000" />
<rule role='IS_AUTHENTICATED_ANONYMOUSLY' path="/blog/.*" /> <rule role='IS_AUTHENTICATED_ANONYMOUSLY' path="/blog/.*" />
<rule role='IS_AUTHENTICATED_ANONYMOUSLY' allow-if="token.getUsername() matches '/^admin/'" path="/blog/524" /> <rule role='IS_AUTHENTICATED_ANONYMOUSLY' allow-if="token.getUserIdentifier() matches '/^admin/'" path="/blog/524" />
</config> </config>
</srv:container> </srv:container>

View File

@ -25,20 +25,20 @@
<provider name="default"> <provider name="default">
<memory> <memory>
<user name="foo" password="foo" roles="ROLE_USER" /> <user identifier="foo" password="foo" roles="ROLE_USER" />
</memory> </memory>
</provider> </provider>
<provider name="digest"> <provider name="digest">
<memory> <memory>
<user name="foo" password="foo" roles="ROLE_USER, ROLE_ADMIN" /> <user identifier="foo" password="foo" roles="ROLE_USER, ROLE_ADMIN" />
</memory> </memory>
</provider> </provider>
<provider name="basic"> <provider name="basic">
<memory> <memory>
<user name="foo" password="0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33" roles="ROLE_SUPER_ADMIN" /> <user identifier="foo" password="0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33" roles="ROLE_SUPER_ADMIN" />
<user name="bar" password="0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33" roles="ROLE_USER, ROLE_ADMIN" /> <user identifier="bar" password="0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33" roles="ROLE_USER, ROLE_ADMIN" />
</memory> </memory>
</provider> </provider>
@ -78,6 +78,6 @@
<rule path="/blog/524" role="ROLE_USER" requires-channel="https" methods="get,POST" port="8000" /> <rule path="/blog/524" role="ROLE_USER" requires-channel="https" methods="get,POST" port="8000" />
<rule role='IS_AUTHENTICATED_ANONYMOUSLY' path="/blog/.*" /> <rule role='IS_AUTHENTICATED_ANONYMOUSLY' path="/blog/.*" />
<rule role='IS_AUTHENTICATED_ANONYMOUSLY' allow-if="token.getUsername() matches '/^admin/'" path="/blog/524" /> <rule role='IS_AUTHENTICATED_ANONYMOUSLY' allow-if="token.getUserIdentifier() matches '/^admin/'" path="/blog/524" />
</config> </config>
</srv:container> </srv:container>

View File

@ -10,7 +10,7 @@
<config> <config>
<provider name="default"> <provider name="default">
<memory> <memory>
<user name="foo" password="foo" roles="ROLE_USER" /> <user identifier="foo" password="foo" roles="ROLE_USER" />
</memory> </memory>
</provider> </provider>

View File

@ -84,4 +84,4 @@ security:
- -
path: /blog/.* path: /blog/.*
role: IS_AUTHENTICATED_ANONYMOUSLY 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/'" }

View File

@ -84,4 +84,4 @@ security:
- -
path: /blog/.* path: /blog/.*
role: IS_AUTHENTICATED_ANONYMOUSLY 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/'" }

View File

@ -19,6 +19,6 @@ class ProfileController extends AbstractController
{ {
$this->denyAccessUnlessGranted('ROLE_USER'); $this->denyAccessUnlessGranted('ROLE_USER');
return $this->json(['email' => $this->getUser()->getUsername()]); return $this->json(['email' => $this->getUser()->getUserIdentifier()]);
} }
} }

View File

@ -1,7 +1,7 @@
{% extends "base.html.twig" %} {% extends "base.html.twig" %}
{% block body %} {% block body %}
Hello {{ app.user.username }}!<br /><br /> Hello {{ app.user.userIdentifier }}!<br /><br />
You're browsing to path "{{ app.request.pathInfo }}".<br /><br /> You're browsing to path "{{ app.request.pathInfo }}".<br /><br />
<a href="{{ logout_path('default') }}">Log out</a>. <a href="{{ logout_path('default') }}">Log out</a>.
<a href="{{ logout_url('default') }}">Log out</a>. <a href="{{ logout_url('default') }}">Log out</a>.

View File

@ -1,7 +1,7 @@
{% extends "base.html.twig" %} {% extends "base.html.twig" %}
{% block body %} {% block body %}
Hello {{ user.username }}!<br /><br /> Hello {{ user.userIdentifier }}!<br /><br />
You're browsing to path "{{ app.request.pathInfo }}". You're browsing to path "{{ app.request.pathInfo }}".
<a href="{{ logout_path('default') }}">Log out</a>. <a href="{{ logout_path('default') }}">Log out</a>.

View File

@ -33,6 +33,6 @@ class AuthenticationController
return new Response('Not logged in.'); return new Response('Not logged in.');
} }
return new Response('Username: '.$user->getUsername()); return new Response('Username: '.$user->getUserIdentifier());
} }
} }

View File

@ -21,6 +21,6 @@ class TestController
{ {
public function loginCheckAction(UserInterface $user) public function loginCheckAction(UserInterface $user)
{ {
return new JsonResponse(['message' => sprintf('Welcome @%s!', $user->getUsername())]); return new JsonResponse(['message' => sprintf('Welcome @%s!', $user->getUserIdentifier())]);
} }
} }

View File

@ -21,6 +21,6 @@ class JsonAuthenticationSuccessHandler implements AuthenticationSuccessHandlerIn
{ {
public function onAuthenticationSuccess(Request $request, TokenInterface $token): Response public function onAuthenticationSuccess(Request $request, TokenInterface $token): Response
{ {
return new JsonResponse(['message' => sprintf('Good game @%s!', $token->getUsername())]); return new JsonResponse(['message' => sprintf('Good game @%s!', $token->getUserIdentifier())]);
} }
} }

View File

@ -11,6 +11,6 @@ class TestCustomLoginLinkSuccessHandler implements AuthenticationSuccessHandlerI
{ {
public function onAuthenticationSuccess(Request $request, TokenInterface $token) 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())]);
} }
} }

View File

@ -4,7 +4,7 @@ namespace Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\SecuredPageBundl
use Symfony\Bundle\SecurityBundle\Tests\Functional\UserWithoutEquatable; use Symfony\Bundle\SecurityBundle\Tests\Functional\UserWithoutEquatable;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException; 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\InMemoryUser;
use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface; use Symfony\Component\Security\Core\User\UserProviderInterface;
@ -16,7 +16,7 @@ class ArrayUserProvider implements UserProviderInterface
public function addUser(UserInterface $user) public function addUser(UserInterface $user)
{ {
$this->users[$user->getUsername()] = $user; $this->users[$user->getUserIdentifier()] = $user;
} }
public function setUser($username, UserInterface $user) public function setUser($username, UserInterface $user)
@ -31,11 +31,16 @@ class ArrayUserProvider implements UserProviderInterface
public function loadUserByUsername($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) { if (null === $user) {
$e = new UsernameNotFoundException(sprintf('User "%s" not found.', $username)); $e = new UserNotFoundException(sprintf('User "%s" not found.', $identifier));
$e->setUsername($username); $e->setUsername($identifier);
throw $e; throw $e;
} }
@ -49,10 +54,10 @@ class ArrayUserProvider implements UserProviderInterface
throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', get_debug_type($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); $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) public function supportsClass($class)

View File

@ -51,7 +51,7 @@ class RememberMeFooController
{ {
public function __invoke(UserInterface $user) public function __invoke(UserInterface $user)
{ {
return new Response($user->getUsername()); return new Response($user->getUserIdentifier());
} }
} }
@ -66,7 +66,12 @@ class RememberMeUserProvider implements UserProviderInterface
public function loadUserByUsername($username) 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) public function refreshUser(UserInterface $user)

View File

@ -28,7 +28,7 @@ class LoginLinkAuthenticationTest extends AbstractWebTestCase
$this->markTestSkipped('Login link auth requires symfony/security-http:^5.2'); $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 // we need an active request that is under the firewall to use the linker
$request = Request::create('/get-login-link'); $request = Request::create('/get-login-link');

View File

@ -106,7 +106,7 @@ final class UserWithoutEquatable implements UserInterface, PasswordAuthenticated
public function __toString() public function __toString()
{ {
return $this->getUsername(); return $this->getUserIdentifier();
} }
/** /**
@ -141,6 +141,11 @@ final class UserWithoutEquatable implements UserInterface, PasswordAuthenticated
return $this->username; return $this->username;
} }
public function getUserIdentifier()
{
return $this->username;
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */

View File

@ -83,7 +83,8 @@ class CheckLdapCredentialsListener implements EventSubscriberInterface
} else { } else {
throw new LogicException('Using the "query_string" config without using a "search_dn" and a "search_password" is not supported.'); 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()); $query = str_replace('{username}', $username, $ldapBadge->getQueryString());
$result = $ldap->query($ldapBadge->getDnString(), $query)->execute(); $result = $ldap->query($ldapBadge->getDnString(), $query)->execute();
if (1 !== $result->count()) { if (1 !== $result->count()) {
@ -92,7 +93,8 @@ class CheckLdapCredentialsListener implements EventSubscriberInterface
$dn = $result[0]->getDn(); $dn = $result[0]->getDn();
} else { } 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()); $dn = str_replace('{username}', $username, $ldapBadge->getDnString());
} }

View File

@ -75,6 +75,13 @@ class LdapUser implements UserInterface, PasswordAuthenticatedUserInterface, Equ
* {@inheritdoc} * {@inheritdoc}
*/ */
public function getUsername(): string 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; return $this->username;
} }

View File

@ -17,7 +17,7 @@ use Symfony\Component\Ldap\Exception\ExceptionInterface;
use Symfony\Component\Ldap\LdapInterface; use Symfony\Component\Ldap\LdapInterface;
use Symfony\Component\Security\Core\Exception\InvalidArgumentException; use Symfony\Component\Security\Core\Exception\InvalidArgumentException;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException; 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\PasswordUpgraderInterface;
use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface; use Symfony\Component\Security\Core\User\UserProviderInterface;
@ -48,7 +48,7 @@ class LdapUserProvider implements UserProviderInterface, PasswordUpgraderInterfa
} }
if (null === $filter) { if (null === $filter) {
$filter = '({uid_key}={username})'; $filter = '({uid_key}={user_identifier})';
} }
$this->ldap = $ldap; $this->ldap = $ldap;
@ -66,15 +66,22 @@ class LdapUserProvider implements UserProviderInterface, PasswordUpgraderInterfa
* {@inheritdoc} * {@inheritdoc}
*/ */
public function loadUserByUsername(string $username) 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 { try {
$this->ldap->bind($this->searchDn, $this->searchPassword); $this->ldap->bind($this->searchDn, $this->searchPassword);
$username = $this->ldap->escape($username, '', LdapInterface::ESCAPE_FILTER); $identifier = $this->ldap->escape($identifier, '', LdapInterface::ESCAPE_FILTER);
$query = str_replace('{username}', $username, $this->defaultSearch); $query = str_replace(['{username}', '{user_identifier}'], $identifier, $this->defaultSearch);
$search = $this->ldap->query($this->baseDn, $query); $search = $this->ldap->query($this->baseDn, $query);
} catch (ConnectionException $e) { } catch (ConnectionException $e) {
$e = new UsernameNotFoundException(sprintf('User "%s" not found.', $username), 0, $e); $e = new UserNotFoundException(sprintf('User "%s" not found.', $identifier), 0, $e);
$e->setUsername($username); $e->setUserIdentifier($identifier);
throw $e; throw $e;
} }
@ -83,15 +90,15 @@ class LdapUserProvider implements UserProviderInterface, PasswordUpgraderInterfa
$count = \count($entries); $count = \count($entries);
if (!$count) { if (!$count) {
$e = new UsernameNotFoundException(sprintf('User "%s" not found.', $username)); $e = new UserNotFoundException(sprintf('User "%s" not found.', $identifier));
$e->setUsername($username); $e->setUserIdentifier($identifier);
throw $e; throw $e;
} }
if ($count > 1) { if ($count > 1) {
$e = new UsernameNotFoundException('More than one user found.'); $e = new UserNotFoundException('More than one user found.');
$e->setUsername($username); $e->setUserIdentifier($identifier);
throw $e; throw $e;
} }
@ -100,12 +107,12 @@ class LdapUserProvider implements UserProviderInterface, PasswordUpgraderInterfa
try { try {
if (null !== $this->uidKey) { if (null !== $this->uidKey) {
$username = $this->getAttributeValue($entry, $this->uidKey); $identifier = $this->getAttributeValue($entry, $this->uidKey);
} }
} catch (InvalidArgumentException $e) { } catch (InvalidArgumentException $e) {
} }
return $this->loadUser($username, $entry); return $this->loadUser($identifier, $entry);
} }
/** /**
@ -117,7 +124,7 @@ class LdapUserProvider implements UserProviderInterface, PasswordUpgraderInterfa
throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', get_debug_type($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 @@ class LdapUserProvider implements UserProviderInterface, PasswordUpgraderInterfa
* *
* @return UserInterface * @return UserInterface
*/ */
protected function loadUser(string $username, Entry $entry) protected function loadUser(string $identifier, Entry $entry)
{ {
$password = null; $password = null;
$extraFields = []; $extraFields = [];
@ -170,7 +177,7 @@ class LdapUserProvider implements UserProviderInterface, PasswordUpgraderInterfa
$extraFields[$field] = $this->getAttributeValue($entry, $field); $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) private function getAttributeValue(Entry $entry, string $attribute)

View File

@ -20,7 +20,7 @@ use Symfony\Component\Ldap\LdapInterface;
use Symfony\Component\Ldap\Security\LdapUser; use Symfony\Component\Ldap\Security\LdapUser;
use Symfony\Component\Ldap\Security\LdapUserProvider; use Symfony\Component\Ldap\Security\LdapUserProvider;
use Symfony\Component\Security\Core\Exception\InvalidArgumentException; use Symfony\Component\Security\Core\Exception\InvalidArgumentException;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException; use Symfony\Component\Security\Core\Exception\UserNotFoundException;
/** /**
* @requires extension ldap * @requires extension ldap
@ -29,7 +29,7 @@ class LdapUserProviderTest extends TestCase
{ {
public function testLoadUserByUsernameFailsIfCantConnectToLdap() public function testLoadUserByUsernameFailsIfCantConnectToLdap()
{ {
$this->expectException(UsernameNotFoundException::class); $this->expectException(UserNotFoundException::class);
$ldap = $this->createMock(LdapInterface::class); $ldap = $this->createMock(LdapInterface::class);
$ldap $ldap
@ -39,12 +39,12 @@ class LdapUserProviderTest extends TestCase
; ;
$provider = new LdapUserProvider($ldap, 'ou=MyBusiness,dc=symfony,dc=com'); $provider = new LdapUserProvider($ldap, 'ou=MyBusiness,dc=symfony,dc=com');
$provider->loadUserByUsername('foo'); $provider->loadUserByIdentifier('foo');
} }
public function testLoadUserByUsernameFailsIfNoLdapEntries() public function testLoadUserByUsernameFailsIfNoLdapEntries()
{ {
$this->expectException(UsernameNotFoundException::class); $this->expectException(UserNotFoundException::class);
$result = $this->createMock(CollectionInterface::class); $result = $this->createMock(CollectionInterface::class);
$query = $this->createMock(QueryInterface::class); $query = $this->createMock(QueryInterface::class);
@ -71,12 +71,12 @@ class LdapUserProviderTest extends TestCase
; ;
$provider = new LdapUserProvider($ldap, 'ou=MyBusiness,dc=symfony,dc=com'); $provider = new LdapUserProvider($ldap, 'ou=MyBusiness,dc=symfony,dc=com');
$provider->loadUserByUsername('foo'); $provider->loadUserByIdentifier('foo');
} }
public function testLoadUserByUsernameFailsIfMoreThanOneLdapEntry() public function testLoadUserByUsernameFailsIfMoreThanOneLdapEntry()
{ {
$this->expectException(UsernameNotFoundException::class); $this->expectException(UserNotFoundException::class);
$result = $this->createMock(CollectionInterface::class); $result = $this->createMock(CollectionInterface::class);
$query = $this->createMock(QueryInterface::class); $query = $this->createMock(QueryInterface::class);
@ -103,7 +103,7 @@ class LdapUserProviderTest extends TestCase
; ;
$provider = new LdapUserProvider($ldap, 'ou=MyBusiness,dc=symfony,dc=com'); $provider = new LdapUserProvider($ldap, 'ou=MyBusiness,dc=symfony,dc=com');
$provider->loadUserByUsername('foo'); $provider->loadUserByIdentifier('foo');
} }
public function testLoadUserByUsernameFailsIfMoreThanOneLdapPasswordsInEntry() public function testLoadUserByUsernameFailsIfMoreThanOneLdapPasswordsInEntry()
@ -144,7 +144,7 @@ class LdapUserProviderTest extends TestCase
; ;
$provider = new LdapUserProvider($ldap, 'ou=MyBusiness,dc=symfony,dc=com', null, null, [], 'sAMAccountName', '({uid_key}={username})', 'userpassword'); $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() public function testLoadUserByUsernameShouldNotFailIfEntryHasNoUidKeyAttribute()
@ -180,7 +180,7 @@ class LdapUserProviderTest extends TestCase
; ;
$provider = new LdapUserProvider($ldap, 'ou=MyBusiness,dc=symfony,dc=com', null, null, [], 'sAMAccountName', '({uid_key}={username})'); $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() public function testLoadUserByUsernameFailsIfEntryHasNoPasswordAttribute()
@ -218,7 +218,7 @@ class LdapUserProviderTest extends TestCase
; ;
$provider = new LdapUserProvider($ldap, 'ou=MyBusiness,dc=symfony,dc=com', null, null, [], 'sAMAccountName', '({uid_key}={username})', 'userpassword'); $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() public function testLoadUserByUsernameIsSuccessfulWithoutPasswordAttribute()
@ -254,7 +254,7 @@ class LdapUserProviderTest extends TestCase
; ;
$provider = new LdapUserProvider($ldap, 'ou=MyBusiness,dc=symfony,dc=com'); $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() public function testLoadUserByUsernameIsSuccessfulWithoutPasswordAttributeAndWrongCase()
@ -290,7 +290,7 @@ class LdapUserProviderTest extends TestCase
; ;
$provider = new LdapUserProvider($ldap, 'ou=MyBusiness,dc=symfony,dc=com'); $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() public function testLoadUserByUsernameIsSuccessfulWithPasswordAttribute()
@ -330,7 +330,7 @@ class LdapUserProviderTest extends TestCase
; ;
$provider = new LdapUserProvider($ldap, 'ou=MyBusiness,dc=symfony,dc=com', null, null, [], 'sAMAccountName', '({uid_key}={username})', 'userpassword', ['email']); $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() public function testRefreshUserShouldReturnUserWithSameProperties()

View File

@ -196,6 +196,10 @@ class SomeUser implements UserInterface
{ {
} }
public function getUserIdentifier(): string
{
}
public function eraseCredentials() public function eraseCredentials()
{ {
} }

View File

@ -4,6 +4,11 @@ CHANGELOG
5.3 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` * Add `PassportInterface:getBadges()`, implemented by `PassportTrait`
* [BC BREAK] Remove method `checkIfCompletelyResolved()` from `PassportInterface`, checking that passport badges are * [BC BREAK] Remove method `checkIfCompletelyResolved()` from `PassportInterface`, checking that passport badges are
resolved is up to `AuthenticatorManager` resolved is up to `AuthenticatorManager`

View File

@ -21,6 +21,7 @@ use Symfony\Component\Security\Core\Exception\AccountStatusException;
use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Exception\BadCredentialsException; use Symfony\Component\Security\Core\Exception\BadCredentialsException;
use Symfony\Component\Security\Core\Exception\ProviderNotFoundException; use Symfony\Component\Security\Core\Exception\ProviderNotFoundException;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
// Help opcache.preload discover always-needed symbols // Help opcache.preload discover always-needed symbols
@ -105,6 +106,11 @@ class AuthenticationProviderManager implements AuthenticationManagerInterface
$this->eventDispatcher->dispatch(new AuthenticationSuccessEvent($result), AuthenticationEvents::AUTHENTICATION_SUCCESS); $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; return $result;
} }

View File

@ -16,7 +16,7 @@ use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface; use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationServiceException; use Symfony\Component\Security\Core\Exception\AuthenticationServiceException;
use Symfony\Component\Security\Core\Exception\BadCredentialsException; 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\LegacyPasswordAuthenticatedUserInterface;
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface; use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
use Symfony\Component\Security\Core\User\PasswordUpgraderInterface; use Symfony\Component\Security\Core\User\PasswordUpgraderInterface;
@ -108,7 +108,7 @@ class DaoAuthenticationProvider extends UserAuthenticationProvider
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
protected function retrieveUser(string $username, UsernamePasswordToken $token) protected function retrieveUser(string $userIdentifier, UsernamePasswordToken $token)
{ {
$user = $token->getUser(); $user = $token->getUser();
if ($user instanceof UserInterface) { if ($user instanceof UserInterface) {
@ -116,15 +116,22 @@ class DaoAuthenticationProvider extends UserAuthenticationProvider
} }
try { 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) { if (!$user instanceof UserInterface) {
throw new AuthenticationServiceException('The user provider must return a UserInterface object.'); throw new AuthenticationServiceException('The user provider must return a UserInterface object.');
} }
return $user; return $user;
} catch (UsernameNotFoundException $e) { } catch (UserNotFoundException $e) {
$e->setUsername($username); $e->setUserIdentifier($userIdentifier);
throw $e; throw $e;
} catch (\Exception $e) { } catch (\Exception $e) {
$e = new AuthenticationServiceException($e->getMessage(), 0, $e); $e = new AuthenticationServiceException($e->getMessage(), 0, $e);

View File

@ -16,7 +16,7 @@ use Symfony\Component\Ldap\LdapInterface;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Core\Exception\BadCredentialsException; use Symfony\Component\Security\Core\Exception\BadCredentialsException;
use Symfony\Component\Security\Core\Exception\LogicException; 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\UserCheckerInterface;
use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface; use Symfony\Component\Security\Core\User\UserProviderInterface;
@ -38,7 +38,7 @@ class LdapBindAuthenticationProvider extends UserAuthenticationProvider
private $searchDn; private $searchDn;
private $searchPassword; 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); parent::__construct($userChecker, $providerKey, $hideUserNotFoundExceptions);
@ -50,7 +50,7 @@ class LdapBindAuthenticationProvider extends UserAuthenticationProvider
} }
/** /**
* 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) public function setQueryString(string $queryString)
{ {
@ -60,13 +60,20 @@ class LdapBindAuthenticationProvider extends UserAuthenticationProvider
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
protected function retrieveUser(string $username, UsernamePasswordToken $token) protected function retrieveUser(string $userIdentifier, UsernamePasswordToken $token)
{ {
if (AuthenticationProviderInterface::USERNAME_NONE_PROVIDED === $username) { if (AuthenticationProviderInterface::USERNAME_NONE_PROVIDED === $userIdentifier) {
throw new UsernameNotFoundException('Username can not be null.'); 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 @@ class LdapBindAuthenticationProvider extends UserAuthenticationProvider
*/ */
protected function checkAuthentication(UserInterface $user, 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(); $password = $token->getCredentials();
if ('' === (string) $password) { if ('' === (string) $password) {
@ -88,8 +96,8 @@ class LdapBindAuthenticationProvider extends UserAuthenticationProvider
} else { } else {
throw new LogicException('Using the "query_string" config without using a "search_dn" and a "search_password" is not supported.'); 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); $userIdentifier = $this->ldap->escape($userIdentifier, '', LdapInterface::ESCAPE_FILTER);
$query = str_replace('{username}', $username, $this->queryString); $query = str_replace(['{username}', '{user_identifier}'], $userIdentifier, $this->queryString);
$result = $this->ldap->query($this->dnString, $query)->execute(); $result = $this->ldap->query($this->dnString, $query)->execute();
if (1 !== $result->count()) { if (1 !== $result->count()) {
throw new BadCredentialsException('The presented username is invalid.'); throw new BadCredentialsException('The presented username is invalid.');
@ -97,8 +105,8 @@ class LdapBindAuthenticationProvider extends UserAuthenticationProvider
$dn = $result[0]->getDn(); $dn = $result[0]->getDn();
} else { } else {
$username = $this->ldap->escape($username, '', LdapInterface::ESCAPE_DN); $userIdentifier = $this->ldap->escape($userIdentifier, '', LdapInterface::ESCAPE_DN);
$dn = str_replace('{username}', $username, $this->dnString); $dn = str_replace(['{username}', '{user_identifier}'], $userIdentifier, $this->dnString);
} }
$this->ldap->bind($dn, $password); $this->ldap->bind($dn, $password);

View File

@ -24,7 +24,7 @@ use Symfony\Component\Security\Core\User\UserProviderInterface;
* This authentication provider will not perform any checks on authentication * This authentication provider will not perform any checks on authentication
* requests, as they should already be pre-authenticated. However, the * requests, as they should already be pre-authenticated. However, the
* UserProviderInterface implementation may still throw a * UserProviderInterface implementation may still throw a
* UsernameNotFoundException, for example. * UserNotFoundException, for example.
* *
* @author Fabien Potencier <fabien@symfony.com> * @author Fabien Potencier <fabien@symfony.com>
*/ */
@ -54,7 +54,15 @@ class PreAuthenticatedAuthenticationProvider implements AuthenticationProviderIn
throw new BadCredentialsException('No pre-authenticated principal found in request.'); 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); $this->userChecker->checkPostAuth($user);

View File

@ -51,7 +51,7 @@ class RememberMeAuthenticationProvider implements AuthenticationProviderInterfac
$user = $token->getUser(); $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))); throw new LogicException(sprintf('Method "%s::getUser()" must return a "%s" instance, "%s" returned.', get_debug_type($token), UserInterface::class, get_debug_type($user)));
} }

View File

@ -17,7 +17,7 @@ use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Exception\AuthenticationServiceException; use Symfony\Component\Security\Core\Exception\AuthenticationServiceException;
use Symfony\Component\Security\Core\Exception\BadCredentialsException; 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\UserCheckerInterface;
use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Core\User\UserInterface;
@ -55,18 +55,18 @@ abstract class UserAuthenticationProvider implements AuthenticationProviderInter
throw new AuthenticationException('The token is not supported by this authentication provider.'); 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) { if ('' === $username || null === $username) {
$username = AuthenticationProviderInterface::USERNAME_NONE_PROVIDED; $username = AuthenticationProviderInterface::USERNAME_NONE_PROVIDED;
} }
try { try {
$user = $this->retrieveUser($username, $token); $user = $this->retrieveUser($username, $token);
} catch (UsernameNotFoundException $e) { } catch (UserNotFoundException $e) {
if ($this->hideUserNotFoundExceptions) { if ($this->hideUserNotFoundExceptions) {
throw new BadCredentialsException('Bad credentials.', 0, $e); throw new BadCredentialsException('Bad credentials.', 0, $e);
} }
$e->setUsername($username); $e->setUserIdentifier($username);
throw $e; throw $e;
} }

View File

@ -45,7 +45,7 @@ class InMemoryTokenProvider implements TokenProviderInterface
$token = new PersistentToken( $token = new PersistentToken(
$this->tokens[$series]->getClass(), $this->tokens[$series]->getClass(),
$this->tokens[$series]->getUsername(), method_exists($this->tokens[$series], 'getUserIdentifier') ? $this->tokens[$series]->getUserIdentifier() : $this->tokens[$series]->getUsername(),
$series, $series,
$tokenValue, $tokenValue,
$lastUsed $lastUsed

View File

@ -19,18 +19,18 @@ namespace Symfony\Component\Security\Core\Authentication\RememberMe;
final class PersistentToken implements PersistentTokenInterface final class PersistentToken implements PersistentTokenInterface
{ {
private $class; private $class;
private $username; private $userIdentifier;
private $series; private $series;
private $tokenValue; private $tokenValue;
private $lastUsed; 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)) { if (empty($class)) {
throw new \InvalidArgumentException('$class must not be empty.'); throw new \InvalidArgumentException('$class must not be empty.');
} }
if ('' === $username) { if ('' === $userIdentifier) {
throw new \InvalidArgumentException('$username must not be empty.'); throw new \InvalidArgumentException('$userIdentifier must not be empty.');
} }
if (empty($series)) { if (empty($series)) {
throw new \InvalidArgumentException('$series must not be empty.'); throw new \InvalidArgumentException('$series must not be empty.');
@ -40,7 +40,7 @@ final class PersistentToken implements PersistentTokenInterface
} }
$this->class = $class; $this->class = $class;
$this->username = $username; $this->userIdentifier = $userIdentifier;
$this->series = $series; $this->series = $series;
$this->tokenValue = $tokenValue; $this->tokenValue = $tokenValue;
$this->lastUsed = $lastUsed; $this->lastUsed = $lastUsed;
@ -59,7 +59,14 @@ final class PersistentToken implements PersistentTokenInterface
*/ */
public function getUsername(): 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;
} }
/** /**

View File

@ -15,6 +15,8 @@ namespace Symfony\Component\Security\Core\Authentication\RememberMe;
* Interface to be implemented by persistent token classes (such as * Interface to be implemented by persistent token classes (such as
* Doctrine entities representing a remember-me token). * 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 <schmittjoh@gmail.com> * @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/ */
interface PersistentTokenInterface interface PersistentTokenInterface
@ -26,13 +28,6 @@ interface PersistentTokenInterface
*/ */
public function getClass(); public function getClass();
/**
* Returns the username.
*
* @return string
*/
public function getUsername();
/** /**
* Returns the series. * Returns the series.
* *

View File

@ -51,10 +51,32 @@ abstract class AbstractToken implements TokenInterface
/** /**
* {@inheritdoc} * {@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) { if ($this->user instanceof UserInterface) {
return $this->user->getUsername(); 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) {
// @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; return (string) $this->user;
@ -234,7 +256,7 @@ abstract class AbstractToken implements TokenInterface
$roles[] = $role; $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 @@ abstract class AbstractToken implements TokenInterface
return true; 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; return true;
} }

View File

@ -42,6 +42,13 @@ class NullToken implements TokenInterface
} }
public function getUsername() public function getUsername()
{
trigger_deprecation('symfony/security-core', '5.3', 'Method "%s()" is deprecated, use getUserIdentifier() instead.', __METHOD__);
return '';
}
public function getUserIdentifier(): string
{ {
return ''; return '';
} }

View File

@ -48,6 +48,11 @@ class TokenStorage implements TokenStorageInterface, ResetInterface
if ($token) { if ($token) {
// ensure any initializer is called // ensure any initializer is called
$this->getToken(); $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; $this->initializer = null;

View File

@ -16,6 +16,8 @@ use Symfony\Component\Security\Core\User\UserInterface;
/** /**
* TokenInterface is the interface for the user authentication information. * 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 <fabien@symfony.com> * @author Fabien Potencier <fabien@symfony.com>
* @author Johannes M. Schmitt <schmittjoh@gmail.com> * @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/ */
@ -65,13 +67,6 @@ interface TokenInterface extends \Serializable
*/ */
public function setUser($user); public function setUser($user);
/**
* Returns the username.
*
* @return string
*/
public function getUsername();
/** /**
* Returns whether the user is authenticated or not. * Returns whether the user is authenticated or not.
* *

View File

@ -0,0 +1,96 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Security\Core\Exception;
/**
* UserNotFoundException is thrown if a User cannot be found for the given identifier.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Alexander <iam.asm89@gmail.com>
*/
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);

View File

@ -11,65 +11,15 @@
namespace Symfony\Component\Security\Core\Exception; namespace Symfony\Component\Security\Core\Exception;
/** trigger_deprecation('symfony/security-core', '5.3', 'The "%s" class is deprecated, use "%s" instead.', UsernameNotFoundException::class, UserNotFoundException::class);
* UsernameNotFoundException is thrown if a User cannot be found by its username.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Alexander <iam.asm89@gmail.com>
*/
class UsernameNotFoundException extends AuthenticationException
{
private $username;
/** class_exists(UserNotFoundException::class);
* {@inheritdoc}
*/
public function getMessageKey()
{
return 'Username could not be found.';
}
if (false) {
/** /**
* Get the username. * @deprecated since Symfony 5.3 to be removed in 6.0, use UserNotFoundException instead.
*
* @return string
*/ */
public function getUsername() class UsernameNotFoundException extends AuthenticationException
{ {
return $this->username;
}
/**
* Set the username.
*/
public function setUsername(string $username)
{
$this->username = $username;
}
/**
* {@inheritdoc}
*/
public function getMessageData()
{
return ['{{ username }}' => $this->username];
}
/**
* {@inheritdoc}
*/
public function __serialize(): array
{
return [$this->username, parent::__serialize()];
}
/**
* {@inheritdoc}
*/
public function __unserialize(array $data): void
{
[$this->username, $parentData] = $data;
$parentData = \is_array($parentData) ? $parentData : unserialize($parentData);
parent::__unserialize($parentData);
} }
} }

View File

@ -23,6 +23,7 @@ use Symfony\Component\Security\Core\Event\AuthenticationSuccessEvent;
use Symfony\Component\Security\Core\Exception\AccountStatusException; use Symfony\Component\Security\Core\Exception\AccountStatusException;
use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Exception\ProviderNotFoundException; use Symfony\Component\Security\Core\Exception\ProviderNotFoundException;
use Symfony\Component\Security\Core\User\InMemoryUser;
class AuthenticationProviderManagerTest extends TestCase class AuthenticationProviderManagerTest extends TestCase
{ {
@ -90,9 +91,12 @@ class AuthenticationProviderManagerTest extends TestCase
public function testAuthenticateWhenOneReturnsAuthenticationExceptionButNotAll() public function testAuthenticateWhenOneReturnsAuthenticationExceptionButNotAll()
{ {
$expected = $this->createMock(TokenInterface::class);
$expected->expects($this->any())->method('getUser')->willReturn(new InMemoryUser('wouter', null));
$manager = new AuthenticationProviderManager([ $manager = new AuthenticationProviderManager([
$this->getAuthenticationProvider(true, null, AuthenticationException::class), $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)); $token = $manager->authenticate($this->createMock(TokenInterface::class));
@ -106,8 +110,10 @@ class AuthenticationProviderManagerTest extends TestCase
->expects($this->never()) ->expects($this->never())
->method('supports') ->method('supports')
; ;
$expected = $this->createMock(TokenInterface::class);
$expected->expects($this->any())->method('getUser')->willReturn(new InMemoryUser('wouter', null));
$manager = new AuthenticationProviderManager([ $manager = new AuthenticationProviderManager([
$this->getAuthenticationProvider(true, $expected = $this->createMock(TokenInterface::class)), $this->getAuthenticationProvider(true, $expected),
$second, $second,
]); ]);

View File

@ -155,6 +155,10 @@ class FakeCustomToken implements TokenInterface
{ {
} }
public function getUserIdentifier(): string
{
}
public function isAuthenticated(): bool public function isAuthenticated(): bool
{ {
} }

View File

@ -12,6 +12,7 @@
namespace Symfony\Component\Security\Core\Tests\Authentication\Provider; namespace Symfony\Component\Security\Core\Tests\Authentication\Provider;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait;
use Symfony\Component\PasswordHasher\Hasher\PasswordHasherFactoryInterface; use Symfony\Component\PasswordHasher\Hasher\PasswordHasherFactoryInterface;
use Symfony\Component\PasswordHasher\Hasher\PlaintextPasswordHasher; use Symfony\Component\PasswordHasher\Hasher\PlaintextPasswordHasher;
use Symfony\Component\PasswordHasher\PasswordHasherInterface; use Symfony\Component\PasswordHasher\PasswordHasherInterface;
@ -20,8 +21,9 @@ use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface; use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationServiceException; use Symfony\Component\Security\Core\Exception\AuthenticationServiceException;
use Symfony\Component\Security\Core\Exception\BadCredentialsException; 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\InMemoryUser;
use Symfony\Component\Security\Core\User\InMemoryUserProvider;
use Symfony\Component\Security\Core\User\PasswordUpgraderInterface; use Symfony\Component\Security\Core\User\PasswordUpgraderInterface;
use Symfony\Component\Security\Core\User\UserCheckerInterface; use Symfony\Component\Security\Core\User\UserCheckerInterface;
use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Core\User\UserInterface;
@ -29,13 +31,25 @@ use Symfony\Component\Security\Core\User\UserProviderInterface;
class DaoAuthenticationProviderTest extends TestCase class DaoAuthenticationProviderTest extends TestCase
{ {
use ExpectDeprecationTrait;
/**
* @group legacy
*/
public function testRetrieveUserWhenProviderDoesNotReturnAnUserInterface() public function testRetrieveUserWhenProviderDoesNotReturnAnUserInterface()
{ {
$this->expectException(AuthenticationServiceException::class); $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 = new \ReflectionMethod($provider, 'retrieveUser');
$method->setAccessible(true); $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()); $method->invoke($provider, 'fabien', $this->getSupportedToken());
} }
@ -44,12 +58,8 @@ class DaoAuthenticationProviderTest extends TestCase
*/ */
public function testRetrieveUserWhenUsernameIsNotFoundWithLegacyEncoderFactory() public function testRetrieveUserWhenUsernameIsNotFoundWithLegacyEncoderFactory()
{ {
$this->expectException(UsernameNotFoundException::class); $this->expectException(UserNotFoundException::class);
$userProvider = $this->createMock(UserProviderInterface::class); $userProvider = new InMemoryUserProvider();
$userProvider->expects($this->once())
->method('loadUserByUsername')
->willThrowException(new UsernameNotFoundException())
;
$provider = new DaoAuthenticationProvider($userProvider, $this->createMock(UserCheckerInterface::class), 'key', $this->createMock(EncoderFactoryInterface::class)); $provider = new DaoAuthenticationProvider($userProvider, $this->createMock(UserCheckerInterface::class), 'key', $this->createMock(EncoderFactoryInterface::class));
$method = new \ReflectionMethod($provider, 'retrieveUser'); $method = new \ReflectionMethod($provider, 'retrieveUser');
@ -60,12 +70,8 @@ class DaoAuthenticationProviderTest extends TestCase
public function testRetrieveUserWhenUsernameIsNotFound() public function testRetrieveUserWhenUsernameIsNotFound()
{ {
$this->expectException(UsernameNotFoundException::class); $this->expectException(UserNotFoundException::class);
$userProvider = $this->createMock(UserProviderInterface::class); $userProvider = new InMemoryUserProvider();
$userProvider->expects($this->once())
->method('loadUserByUsername')
->willThrowException(new UsernameNotFoundException())
;
$provider = new DaoAuthenticationProvider($userProvider, $this->createMock(UserCheckerInterface::class), 'key', $this->createMock(PasswordHasherFactoryInterface::class)); $provider = new DaoAuthenticationProvider($userProvider, $this->createMock(UserCheckerInterface::class), 'key', $this->createMock(PasswordHasherFactoryInterface::class));
$method = new \ReflectionMethod($provider, 'retrieveUser'); $method = new \ReflectionMethod($provider, 'retrieveUser');
@ -77,9 +83,9 @@ class DaoAuthenticationProviderTest extends TestCase
public function testRetrieveUserWhenAnExceptionOccurs() public function testRetrieveUserWhenAnExceptionOccurs()
{ {
$this->expectException(AuthenticationServiceException::class); $this->expectException(AuthenticationServiceException::class);
$userProvider = $this->createMock(UserProviderInterface::class); $userProvider = $this->createMock(InMemoryUserProvider::class);
$userProvider->expects($this->once()) $userProvider->expects($this->once())
->method('loadUserByUsername') ->method('loadUserByIdentifier')
->willThrowException(new \RuntimeException()) ->willThrowException(new \RuntimeException())
; ;
@ -92,9 +98,9 @@ class DaoAuthenticationProviderTest extends TestCase
public function testRetrieveUserReturnsUserFromTokenOnReauthentication() public function testRetrieveUserReturnsUserFromTokenOnReauthentication()
{ {
$userProvider = $this->createMock(UserProviderInterface::class); $userProvider = $this->createMock(InMemoryUserProvider::class);
$userProvider->expects($this->never()) $userProvider->expects($this->never())
->method('loadUserByUsername') ->method('loadUserByIdentifier')
; ;
$user = new TestUser(); $user = new TestUser();
@ -114,19 +120,13 @@ class DaoAuthenticationProviderTest extends TestCase
public function testRetrieveUser() public function testRetrieveUser()
{ {
$user = new TestUser(); $userProvider = new InMemoryUserProvider(['fabien' => []]);
$userProvider = $this->createMock(UserProviderInterface::class);
$userProvider->expects($this->once())
->method('loadUserByUsername')
->willReturn($user)
;
$provider = new DaoAuthenticationProvider($userProvider, $this->createMock(UserCheckerInterface::class), 'key', $this->createMock(PasswordHasherFactoryInterface::class)); $provider = new DaoAuthenticationProvider($userProvider, $this->createMock(UserCheckerInterface::class), 'key', $this->createMock(PasswordHasherFactoryInterface::class));
$method = new \ReflectionMethod($provider, 'retrieveUser'); $method = new \ReflectionMethod($provider, 'retrieveUser');
$method->setAccessible(true); $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() public function testCheckAuthenticationWhenCredentialsAreEmpty()
@ -323,15 +323,17 @@ class DaoAuthenticationProviderTest extends TestCase
return $mock; return $mock;
} }
protected function getProvider($user = null, $userChecker = null, $passwordHasher = null) protected function getProvider($user = null, $userChecker = null, $passwordHasher = null, $userProvider = null)
{ {
if (null === $userProvider) {
$userProvider = $this->createMock(PasswordUpgraderProvider::class); $userProvider = $this->createMock(PasswordUpgraderProvider::class);
if (null !== $user) { if (null !== $user) {
$userProvider->expects($this->once()) $userProvider->expects($this->once())
->method('loadUserByUsername') ->method('loadUserByIdentifier')
->willReturn($user) ->willReturn($user)
; ;
} }
}
if (null === $userChecker) { if (null === $userChecker) {
$userChecker = $this->createMock(UserCheckerInterface::class); $userChecker = $this->createMock(UserCheckerInterface::class);
@ -374,6 +376,11 @@ class TestUser implements UserInterface
return 'jane_doe'; return 'jane_doe';
} }
public function getUserIdentifier(): string
{
return 'jane_doe';
}
public function eraseCredentials() public function eraseCredentials()
{ {
} }
@ -381,4 +388,10 @@ class TestUser implements UserInterface
interface PasswordUpgraderProvider extends UserProviderInterface, PasswordUpgraderInterface interface PasswordUpgraderProvider extends UserProviderInterface, PasswordUpgraderInterface
{ {
public function upgradePassword(UserInterface $user, string $newHashedPassword): void; public function upgradePassword(UserInterface $user, string $newHashedPassword): void;
public function loadUserByIdentifier(string $identifier): UserInterface;
}
interface DaoAuthenticationProviderTest_UserProvider extends UserProviderInterface
{
public function loadUserByUsername($username);
} }

View File

@ -21,6 +21,7 @@ use Symfony\Component\Security\Core\Authentication\Provider\LdapBindAuthenticati
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Core\Exception\BadCredentialsException; use Symfony\Component\Security\Core\Exception\BadCredentialsException;
use Symfony\Component\Security\Core\User\InMemoryUser; 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\UserCheckerInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface; use Symfony\Component\Security\Core\User\UserProviderInterface;
@ -81,10 +82,10 @@ class LdapBindAuthenticationProviderTest extends TestCase
public function testRetrieveUser() public function testRetrieveUser()
{ {
$userProvider = $this->createMock(UserProviderInterface::class); $userProvider = $this->createMock(InMemoryUserProvider::class);
$userProvider $userProvider
->expects($this->once()) ->expects($this->once())
->method('loadUserByUsername') ->method('loadUserByIdentifier')
->with('foo') ->with('foo')
; ;
$ldap = $this->createMock(LdapInterface::class); $ldap = $this->createMock(LdapInterface::class);

View File

@ -18,6 +18,7 @@ use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Exception\BadCredentialsException; use Symfony\Component\Security\Core\Exception\BadCredentialsException;
use Symfony\Component\Security\Core\Exception\LockedException; 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\UserCheckerInterface;
use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface; use Symfony\Component\Security\Core\User\UserProviderInterface;
@ -120,10 +121,10 @@ class PreAuthenticatedAuthenticationProviderTest extends TestCase
protected function getProvider($user = null, $userChecker = null) protected function getProvider($user = null, $userChecker = null)
{ {
$userProvider = $this->createMock(UserProviderInterface::class); $userProvider = $this->createMock(InMemoryUserProvider::class);
if (null !== $user) { if (null !== $user) {
$userProvider->expects($this->once()) $userProvider->expects($this->once())
->method('loadUserByUsername') ->method('loadUserByIdentifier')
->willReturn($user) ->willReturn($user)
; ;
} }

View File

@ -21,7 +21,8 @@ use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Exception\AuthenticationServiceException; use Symfony\Component\Security\Core\Exception\AuthenticationServiceException;
use Symfony\Component\Security\Core\Exception\BadCredentialsException; use Symfony\Component\Security\Core\Exception\BadCredentialsException;
use Symfony\Component\Security\Core\Exception\CredentialsExpiredException; 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\UserCheckerInterface;
use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Core\User\UserInterface;
@ -46,11 +47,11 @@ class UserAuthenticationProviderTest extends TestCase
public function testAuthenticateWhenUsernameIsNotFound() public function testAuthenticateWhenUsernameIsNotFound()
{ {
$this->expectException(UsernameNotFoundException::class); $this->expectException(UserNotFoundException::class);
$provider = $this->getProvider(false, false); $provider = $this->getProvider(false, false);
$provider->expects($this->once()) $provider->expects($this->once())
->method('retrieveUser') ->method('retrieveUser')
->willThrowException(new UsernameNotFoundException()) ->willThrowException(new UserNotFoundException())
; ;
$provider->authenticate($this->getSupportedToken()); $provider->authenticate($this->getSupportedToken());
@ -62,7 +63,7 @@ class UserAuthenticationProviderTest extends TestCase
$provider = $this->getProvider(false, true); $provider = $this->getProvider(false, true);
$provider->expects($this->once()) $provider->expects($this->once())
->method('retrieveUser') ->method('retrieveUser')
->willThrowException(new UsernameNotFoundException()) ->willThrowException(new UserNotFoundException())
; ;
$provider->authenticate($this->getSupportedToken()); $provider->authenticate($this->getSupportedToken());
@ -194,7 +195,7 @@ class UserAuthenticationProviderTest extends TestCase
; ;
$originalToken = $this->createMock(TokenInterface::class); $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']); $token->setAttributes(['foo' => 'bar']);
$authToken = $provider->authenticate($token); $authToken = $provider->authenticate($token);

View File

@ -12,19 +12,33 @@
namespace Symfony\Component\Security\Core\Tests\Authentication\RememberMe; namespace Symfony\Component\Security\Core\Tests\Authentication\RememberMe;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait;
use Symfony\Component\Security\Core\Authentication\RememberMe\PersistentToken; use Symfony\Component\Security\Core\Authentication\RememberMe\PersistentToken;
class PersistentTokenTest extends TestCase class PersistentTokenTest extends TestCase
{ {
use ExpectDeprecationTrait;
public function testConstructor() public function testConstructor()
{ {
$lastUsed = new \DateTime(); $lastUsed = new \DateTime();
$token = new PersistentToken('fooclass', 'fooname', 'fooseries', 'footokenvalue', $lastUsed); $token = new PersistentToken('fooclass', 'fooname', 'fooseries', 'footokenvalue', $lastUsed);
$this->assertEquals('fooclass', $token->getClass()); $this->assertEquals('fooclass', $token->getClass());
$this->assertEquals('fooname', $token->getUsername()); $this->assertEquals('fooname', $token->getUserIdentifier());
$this->assertEquals('fooseries', $token->getSeries()); $this->assertEquals('fooseries', $token->getSeries());
$this->assertEquals('footokenvalue', $token->getTokenValue()); $this->assertEquals('footokenvalue', $token->getTokenValue());
$this->assertSame($lastUsed, $token->getLastUsed()); $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());
}
} }

View File

@ -12,12 +12,19 @@
namespace Symfony\Component\Security\Core\Tests\Authentication\Token; namespace Symfony\Component\Security\Core\Tests\Authentication\Token;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait;
use Symfony\Component\Security\Core\Authentication\Token\AbstractToken; use Symfony\Component\Security\Core\Authentication\Token\AbstractToken;
use Symfony\Component\Security\Core\User\InMemoryUser;
use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Core\User\UserInterface;
class AbstractTokenTest extends TestCase class AbstractTokenTest extends TestCase
{ {
public function testGetUsername() use ExpectDeprecationTrait;
/**
* @group legacy
*/
public function testLegacyGetUsername()
{ {
$token = new ConcreteToken(['ROLE_FOO']); $token = new ConcreteToken(['ROLE_FOO']);
$token->setUser('fabien'); $token->setUser('fabien');
@ -26,10 +33,43 @@ class AbstractTokenTest extends TestCase
$token->setUser(new TestUser('fabien')); $token->setUser(new TestUser('fabien'));
$this->assertEquals('fabien', $token->getUsername()); $this->assertEquals('fabien', $token->getUsername());
$user = $this->createMock(UserInterface::class); $legacyUser = new class implements UserInterface {
$user->expects($this->once())->method('getUsername')->willReturn('fabien'); public function getUsername()
$token->setUser($user); {
return 'fabien';
}
public function getRoles()
{}
public function getPassword()
{}
public function getSalt()
{}
public function eraseCredentials()
{}
};
$token->setUser($legacyUser);
$this->assertEquals('fabien', $token->getUsername()); $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() public function testEraseCredentials()
@ -106,10 +146,8 @@ class AbstractTokenTest extends TestCase
public function getUsers() public function getUsers()
{ {
$user = $this->createMock(UserInterface::class);
return [ return [
[$user], [new InMemoryUser('foo', null)],
[new TestUser('foo')], [new TestUser('foo')],
['foo'], ['foo'],
]; ];
@ -210,6 +248,11 @@ class SerializableUser implements UserInterface, \Serializable
return $this->name; return $this->name;
} }
public function getUserIdentifier()
{
return $this->name;
}
public function getPassword() public function getPassword()
{ {
return '***'; return '***';

View File

@ -16,6 +16,7 @@ use Psr\Container\ContainerInterface;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Session\SessionInterface; 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\TokenStorage;
use Symfony\Component\Security\Core\Authentication\Token\Storage\UsageTrackingTokenStorage; use Symfony\Component\Security\Core\Authentication\Token\Storage\UsageTrackingTokenStorage;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
@ -46,7 +47,7 @@ class UsageTrackingTokenStorageTest extends TestCase
$trackingStorage = new UsageTrackingTokenStorage($tokenStorage, $sessionLocator); $trackingStorage = new UsageTrackingTokenStorage($tokenStorage, $sessionLocator);
$this->assertNull($trackingStorage->getToken()); $this->assertNull($trackingStorage->getToken());
$token = $this->createMock(TokenInterface::class); $token = new NullToken();
$trackingStorage->setToken($token); $trackingStorage->setToken($token);
$this->assertSame($token, $trackingStorage->getToken()); $this->assertSame($token, $trackingStorage->getToken());

View File

@ -26,7 +26,7 @@ class SwitchUserTokenTest extends TestCase
$unserializedToken = unserialize(serialize($token)); $unserializedToken = unserialize(serialize($token));
$this->assertInstanceOf(SwitchUserToken::class, $unserializedToken); $this->assertInstanceOf(SwitchUserToken::class, $unserializedToken);
$this->assertSame('admin', $unserializedToken->getUsername()); $this->assertSame('admin', $unserializedToken->getUserIdentifier());
$this->assertSame('bar', $unserializedToken->getCredentials()); $this->assertSame('bar', $unserializedToken->getCredentials());
$this->assertSame('provider-key', $unserializedToken->getFirewallName()); $this->assertSame('provider-key', $unserializedToken->getFirewallName());
$this->assertEquals(['ROLE_USER'], $unserializedToken->getRoleNames()); $this->assertEquals(['ROLE_USER'], $unserializedToken->getRoleNames());
@ -35,7 +35,7 @@ class SwitchUserTokenTest extends TestCase
$unserializedOriginalToken = $unserializedToken->getOriginalToken(); $unserializedOriginalToken = $unserializedToken->getOriginalToken();
$this->assertInstanceOf(UsernamePasswordToken::class, $unserializedOriginalToken); $this->assertInstanceOf(UsernamePasswordToken::class, $unserializedOriginalToken);
$this->assertSame('user', $unserializedOriginalToken->getUsername()); $this->assertSame('user', $unserializedOriginalToken->getUserIdentifier());
$this->assertSame('foo', $unserializedOriginalToken->getCredentials()); $this->assertSame('foo', $unserializedOriginalToken->getCredentials());
$this->assertSame('provider-key', $unserializedOriginalToken->getFirewallName()); $this->assertSame('provider-key', $unserializedOriginalToken->getFirewallName());
$this->assertEquals(['ROLE_ADMIN', 'ROLE_ALLOWED_TO_SWITCH'], $unserializedOriginalToken->getRoleNames()); $this->assertEquals(['ROLE_ADMIN', 'ROLE_ALLOWED_TO_SWITCH'], $unserializedOriginalToken->getRoleNames());
@ -49,6 +49,11 @@ class SwitchUserTokenTest extends TestCase
return 'impersonated'; return 'impersonated';
} }
public function getUserIdentifier()
{
return 'impersonated';
}
public function getPassword() public function getPassword()
{ {
return null; return null;
@ -92,7 +97,7 @@ class SwitchUserTokenTest extends TestCase
self::assertInstanceOf(SwitchUserToken::class, $token); self::assertInstanceOf(SwitchUserToken::class, $token);
self::assertInstanceOf(UsernamePasswordToken::class, $token->getOriginalToken()); self::assertInstanceOf(UsernamePasswordToken::class, $token->getOriginalToken());
self::assertSame('john', $token->getUsername()); self::assertSame('john', $token->getUserIdentifier());
self::assertSame(['foo' => 'bar'], $token->getCredentials()); self::assertSame(['foo' => 'bar'], $token->getCredentials());
self::assertSame('main', $token->getFirewallName()); self::assertSame('main', $token->getFirewallName());
self::assertEquals(['ROLE_USER'], $token->getRoleNames()); self::assertEquals(['ROLE_USER'], $token->getRoleNames());

View File

@ -213,6 +213,10 @@ class SomeUser implements UserInterface
{ {
} }
public function getUserIdentifier(): string
{
}
public function eraseCredentials() public function eraseCredentials()
{ {
} }

View File

@ -0,0 +1,39 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\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());
}
}

View File

@ -1,26 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\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());
}
}

View File

@ -13,9 +13,10 @@ namespace Symfony\Component\Security\Core\Tests\User;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException; 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\ChainUserProvider;
use Symfony\Component\Security\Core\User\InMemoryUser; 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\PasswordAuthenticatedUserInterface;
use Symfony\Component\Security\Core\User\PasswordUpgraderInterface; use Symfony\Component\Security\Core\User\PasswordUpgraderInterface;
use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Core\User\UserInterface;
@ -25,59 +26,59 @@ class ChainUserProviderTest extends TestCase
{ {
public function testLoadUserByUsername() public function testLoadUserByUsername()
{ {
$provider1 = $this->createMock(UserProviderInterface::class); $provider1 = $this->createMock(InMemoryUserProvider::class);
$provider1 $provider1
->expects($this->once()) ->expects($this->once())
->method('loadUserByUsername') ->method('loadUserByIdentifier')
->with($this->equalTo('foo')) ->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 $provider2
->expects($this->once()) ->expects($this->once())
->method('loadUserByUsername') ->method('loadUserByIdentifier')
->with($this->equalTo('foo')) ->with($this->equalTo('foo'))
->willReturn($account = $this->createMock(UserInterface::class)) ->willReturn($account = $this->createMock(UserInterface::class))
; ;
$provider = new ChainUserProvider([$provider1, $provider2]); $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); $this->expectException(UserNotFoundException::class);
$provider1 = $this->createMock(UserProviderInterface::class); $provider1 = $this->createMock(InMemoryUserProvider::class);
$provider1 $provider1
->expects($this->once()) ->expects($this->once())
->method('loadUserByUsername') ->method('loadUserByIdentifier')
->with($this->equalTo('foo')) ->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 $provider2
->expects($this->once()) ->expects($this->once())
->method('loadUserByUsername') ->method('loadUserByIdentifier')
->with($this->equalTo('foo')) ->with($this->equalTo('foo'))
->willThrowException(new UsernameNotFoundException('not found')) ->willThrowException(new UserNotFoundException('not found'))
; ;
$provider = new ChainUserProvider([$provider1, $provider2]); $provider = new ChainUserProvider([$provider1, $provider2]);
$provider->loadUserByUsername('foo'); $provider->loadUserByIdentifier('foo');
} }
public function testRefreshUser() public function testRefreshUser()
{ {
$provider1 = $this->createMock(UserProviderInterface::class); $provider1 = $this->createMock(InMemoryUserProvider::class);
$provider1 $provider1
->expects($this->once()) ->expects($this->once())
->method('supportsClass') ->method('supportsClass')
->willReturn(false) ->willReturn(false)
; ;
$provider2 = $this->createMock(UserProviderInterface::class); $provider2 = $this->createMock(InMemoryUserProvider::class);
$provider2 $provider2
->expects($this->once()) ->expects($this->once())
->method('supportsClass') ->method('supportsClass')
@ -90,7 +91,7 @@ class ChainUserProviderTest extends TestCase
->willThrowException(new UnsupportedUserException('unsupported')) ->willThrowException(new UnsupportedUserException('unsupported'))
; ;
$provider3 = $this->createMock(UserProviderInterface::class); $provider3 = $this->createMock(InMemoryUserProvider::class);
$provider3 $provider3
->expects($this->once()) ->expects($this->once())
->method('supportsClass') ->method('supportsClass')
@ -109,7 +110,7 @@ class ChainUserProviderTest extends TestCase
public function testRefreshUserAgain() public function testRefreshUserAgain()
{ {
$provider1 = $this->createMock(UserProviderInterface::class); $provider1 = $this->createMock(InMemoryUserProvider::class);
$provider1 $provider1
->expects($this->once()) ->expects($this->once())
->method('supportsClass') ->method('supportsClass')
@ -119,10 +120,10 @@ class ChainUserProviderTest extends TestCase
$provider1 $provider1
->expects($this->once()) ->expects($this->once())
->method('refreshUser') ->method('refreshUser')
->willThrowException(new UsernameNotFoundException('not found')) ->willThrowException(new UserNotFoundException('not found'))
; ;
$provider2 = $this->createMock(UserProviderInterface::class); $provider2 = $this->createMock(InMemoryUserProvider::class);
$provider2 $provider2
->expects($this->once()) ->expects($this->once())
->method('supportsClass') ->method('supportsClass')
@ -142,7 +143,7 @@ class ChainUserProviderTest extends TestCase
public function testRefreshUserThrowsUnsupportedUserException() public function testRefreshUserThrowsUnsupportedUserException()
{ {
$this->expectException(UnsupportedUserException::class); $this->expectException(UnsupportedUserException::class);
$provider1 = $this->createMock(UserProviderInterface::class); $provider1 = $this->createMock(InMemoryUserProvider::class);
$provider1 $provider1
->expects($this->once()) ->expects($this->once())
->method('supportsClass') ->method('supportsClass')
@ -155,7 +156,7 @@ class ChainUserProviderTest extends TestCase
->willThrowException(new UnsupportedUserException('unsupported')) ->willThrowException(new UnsupportedUserException('unsupported'))
; ;
$provider2 = $this->createMock(UserProviderInterface::class); $provider2 = $this->createMock(InMemoryUserProvider::class);
$provider2 $provider2
->expects($this->once()) ->expects($this->once())
->method('supportsClass') ->method('supportsClass')
@ -174,7 +175,7 @@ class ChainUserProviderTest extends TestCase
public function testSupportsClass() public function testSupportsClass()
{ {
$provider1 = $this->createMock(UserProviderInterface::class); $provider1 = $this->createMock(InMemoryUserProvider::class);
$provider1 $provider1
->expects($this->once()) ->expects($this->once())
->method('supportsClass') ->method('supportsClass')
@ -182,7 +183,7 @@ class ChainUserProviderTest extends TestCase
->willReturn(false) ->willReturn(false)
; ;
$provider2 = $this->createMock(UserProviderInterface::class); $provider2 = $this->createMock(InMemoryUserProvider::class);
$provider2 $provider2
->expects($this->once()) ->expects($this->once())
->method('supportsClass') ->method('supportsClass')
@ -196,7 +197,7 @@ class ChainUserProviderTest extends TestCase
public function testSupportsClassWhenNotSupported() public function testSupportsClassWhenNotSupported()
{ {
$provider1 = $this->createMock(UserProviderInterface::class); $provider1 = $this->createMock(InMemoryUserProvider::class);
$provider1 $provider1
->expects($this->once()) ->expects($this->once())
->method('supportsClass') ->method('supportsClass')
@ -204,7 +205,7 @@ class ChainUserProviderTest extends TestCase
->willReturn(false) ->willReturn(false)
; ;
$provider2 = $this->createMock(UserProviderInterface::class); $provider2 = $this->createMock(InMemoryUserProvider::class);
$provider2 $provider2
->expects($this->once()) ->expects($this->once())
->method('supportsClass') ->method('supportsClass')
@ -218,7 +219,7 @@ class ChainUserProviderTest extends TestCase
public function testAcceptsTraversable() public function testAcceptsTraversable()
{ {
$provider1 = $this->createMock(UserProviderInterface::class); $provider1 = $this->createMock(InMemoryUserProvider::class);
$provider1 $provider1
->expects($this->once()) ->expects($this->once())
->method('supportsClass') ->method('supportsClass')
@ -231,7 +232,7 @@ class ChainUserProviderTest extends TestCase
->willThrowException(new UnsupportedUserException('unsupported')) ->willThrowException(new UnsupportedUserException('unsupported'))
; ;
$provider2 = $this->createMock(UserProviderInterface::class); $provider2 = $this->createMock(InMemoryUserProvider::class);
$provider2 $provider2
->expects($this->once()) ->expects($this->once())
->method('supportsClass') ->method('supportsClass')

View File

@ -13,7 +13,7 @@ namespace Symfony\Component\Security\Core\Tests\User;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; 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\InMemoryUser;
use Symfony\Component\Security\Core\User\InMemoryUserProvider; use Symfony\Component\Security\Core\User\InMemoryUserProvider;
use Symfony\Component\Security\Core\User\User; use Symfony\Component\Security\Core\User\User;
@ -26,7 +26,7 @@ class InMemoryUserProviderTest extends TestCase
{ {
$provider = $this->createProvider(); $provider = $this->createProvider();
$user = $provider->loadUserByUsername('fabien'); $user = $provider->loadUserByIdentifier('fabien');
$this->assertEquals('foo', $user->getPassword()); $this->assertEquals('foo', $user->getPassword());
$this->assertEquals(['ROLE_USER'], $user->getRoles()); $this->assertEquals(['ROLE_USER'], $user->getRoles());
$this->assertFalse($user->isEnabled()); $this->assertFalse($user->isEnabled());
@ -76,7 +76,7 @@ class InMemoryUserProviderTest extends TestCase
$provider = new InMemoryUserProvider(); $provider = new InMemoryUserProvider();
$provider->createUser(new InMemoryUser('fabien', 'foo')); $provider->createUser(new InMemoryUser('fabien', 'foo'));
$user = $provider->loadUserByUsername('fabien'); $user = $provider->loadUserByIdentifier('fabien');
$this->assertEquals('foo', $user->getPassword()); $this->assertEquals('foo', $user->getPassword());
} }
@ -90,8 +90,8 @@ class InMemoryUserProviderTest extends TestCase
public function testLoadUserByUsernameDoesNotExist() public function testLoadUserByUsernameDoesNotExist()
{ {
$this->expectException(UsernameNotFoundException::class); $this->expectException(UserNotFoundException::class);
$provider = new InMemoryUserProvider(); $provider = new InMemoryUserProvider();
$provider->loadUserByUsername('fabien'); $provider->loadUserByIdentifier('fabien');
} }
} }

View File

@ -12,12 +12,15 @@
namespace Symfony\Component\Security\Core\Tests\User; namespace Symfony\Component\Security\Core\Tests\User;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait;
use Symfony\Component\Security\Core\User\EquatableInterface; use Symfony\Component\Security\Core\User\EquatableInterface;
use Symfony\Component\Security\Core\User\InMemoryUser; use Symfony\Component\Security\Core\User\InMemoryUser;
use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Core\User\UserInterface;
class InMemoryUserTest extends TestCase class InMemoryUserTest extends TestCase
{ {
use ExpectDeprecationTrait;
public function testConstructorException() public function testConstructorException()
{ {
$this->expectException(\InvalidArgumentException::class); $this->expectException(\InvalidArgumentException::class);
@ -39,12 +42,23 @@ class InMemoryUserTest extends TestCase
$this->assertEquals('superpass', $user->getPassword()); $this->assertEquals('superpass', $user->getPassword());
} }
/**
* @group legacy
*/
public function testGetUsername() public function testGetUsername()
{ {
$user = new InMemoryUser('fabien', 'superpass'); $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()); $this->assertEquals('fabien', $user->getUsername());
} }
public function testGetUserIdentifier()
{
$user = new InMemoryUser('fabien', 'superpass');
$this->assertEquals('fabien', $user->getUserIdentifier());
}
public function testGetSalt() public function testGetSalt()
{ {
$user = new InMemoryUser('fabien', 'superpass'); $user = new InMemoryUser('fabien', 'superpass');

View File

@ -12,6 +12,7 @@
namespace Symfony\Component\Security\Core\Tests\User; namespace Symfony\Component\Security\Core\Tests\User;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait;
use Symfony\Component\Security\Core\User\EquatableInterface; use Symfony\Component\Security\Core\User\EquatableInterface;
use Symfony\Component\Security\Core\User\User; use Symfony\Component\Security\Core\User\User;
use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Core\User\UserInterface;
@ -21,6 +22,8 @@ use Symfony\Component\Security\Core\User\UserInterface;
*/ */
class UserTest extends TestCase class UserTest extends TestCase
{ {
use ExpectDeprecationTrait;
public function testConstructorException() public function testConstructorException()
{ {
$this->expectException(\InvalidArgumentException::class); $this->expectException(\InvalidArgumentException::class);
@ -42,12 +45,23 @@ class UserTest extends TestCase
$this->assertEquals('superpass', $user->getPassword()); $this->assertEquals('superpass', $user->getPassword());
} }
/**
* @group legacy
*/
public function testGetUsername() public function testGetUsername()
{ {
$user = new User('fabien', 'superpass'); $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()); $this->assertEquals('fabien', $user->getUsername());
} }
public function testGetUserIdentifier()
{
$user = new User('fabien', 'superpass');
$this->assertEquals('fabien', $user->getUserIdentifier());
}
public function testGetSalt() public function testGetSalt()
{ {
$user = new User('fabien', 'superpass'); $user = new User('fabien', 'superpass');

View File

@ -12,7 +12,7 @@
namespace Symfony\Component\Security\Core\User; namespace Symfony\Component\Security\Core\User;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException; use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException; use Symfony\Component\Security\Core\Exception\UserNotFoundException;
/** /**
* Chain User Provider. * Chain User Provider.
@ -50,17 +50,31 @@ class ChainUserProvider implements UserProviderInterface, PasswordUpgraderInterf
* {@inheritdoc} * {@inheritdoc}
*/ */
public function loadUserByUsername(string $username) 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) { foreach ($this->providers as $provider) {
try { try {
return $provider->loadUserByUsername($username); // @deprecated since 5.3, change to $provider->loadUserByIdentifier() in 6.0
} catch (UsernameNotFoundException $e) { 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 // try next one
} }
} }
$ex = new UsernameNotFoundException(sprintf('There is no user with name "%s".', $username)); $ex = new UserNotFoundException(sprintf('There is no user with identifier "%s".', $userIdentifier));
$ex->setUsername($username); $ex->setUserIdentifier($userIdentifier);
throw $ex; throw $ex;
} }
@ -80,15 +94,17 @@ class ChainUserProvider implements UserProviderInterface, PasswordUpgraderInterf
return $provider->refreshUser($user); return $provider->refreshUser($user);
} catch (UnsupportedUserException $e) { } catch (UnsupportedUserException $e) {
// try next one // try next one
} catch (UsernameNotFoundException $e) { } catch (UserNotFoundException $e) {
$supportedUserFound = true; $supportedUserFound = true;
// try next one // try next one
} }
} }
if ($supportedUserFound) { if ($supportedUserFound) {
$e = new UsernameNotFoundException(sprintf('There is no user with name "%s".', $user->getUsername())); // @deprecated since 5.3, change to $user->getUserIdentifier() in 6.0
$e->setUsername($user->getUsername()); $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; throw $e;
} else { } 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))); 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)));

View File

@ -12,7 +12,7 @@
namespace Symfony\Component\Security\Core\User; namespace Symfony\Component\Security\Core\User;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException; 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. * InMemoryUserProvider is a simple non persistent user provider.
@ -51,11 +51,13 @@ class InMemoryUserProvider implements UserProviderInterface
*/ */
public function createUser(UserInterface $user) 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.'); 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 @@ class InMemoryUserProvider implements UserProviderInterface
*/ */
public function loadUserByUsername(string $username) 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 @@ class InMemoryUserProvider implements UserProviderInterface
throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', get_debug_type($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 // @deprecated since Symfony 5.3
if (User::class === \get_class($user)) { if (User::class === \get_class($user)) {
@ -91,10 +103,10 @@ class InMemoryUserProvider implements UserProviderInterface
$accountNonLocked = $storedUser->isAccountNonLocked(); $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 @@ class InMemoryUserProvider implements UserProviderInterface
/** /**
* Returns the user by given username. * 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 */ private function getUser(string $username)/*: InMemoryUser */
{ {
if (!isset($this->users[strtolower($username)])) { if (!isset($this->users[strtolower($username)])) {
$ex = new UsernameNotFoundException(sprintf('Username "%s" does not exist.', $username)); $ex = new UserNotFoundException(sprintf('Username "%s" does not exist.', $username));
$ex->setUsername($username); $ex->setUserIdentifier($username);
throw $ex; throw $ex;
} }

View File

@ -37,6 +37,11 @@ class MissingUserProvider implements UserProviderInterface
throw new \BadMethodCallException(); throw new \BadMethodCallException();
} }
public function loadUserByIdentifier(string $identifier): UserInterface
{
throw new \BadMethodCallException();
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */

View File

@ -53,7 +53,7 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface, Equatab
public function __toString(): string public function __toString(): string
{ {
return $this->getUsername(); return $this->getUserIdentifier();
} }
/** /**
@ -84,6 +84,16 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface, Equatab
* {@inheritdoc} * {@inheritdoc}
*/ */
public function getUsername(): string 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; return $this->username;
} }
@ -184,7 +194,7 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface, Equatab
return false; return false;
} }
if ($this->getUsername() !== $user->getUsername()) { if ($this->getUserIdentifier() !== $user->getUserIdentifier()) {
return false; return false;
} }

View File

@ -26,6 +26,8 @@ namespace Symfony\Component\Security\Core\User;
* *
* @see UserProviderInterface * @see UserProviderInterface
* *
* @method string getUserIdentifier() returns the identifier for this user (e.g. its username or e-mailaddress)
*
* @author Fabien Potencier <fabien@symfony.com> * @author Fabien Potencier <fabien@symfony.com>
*/ */
interface UserInterface interface UserInterface
@ -69,13 +71,6 @@ interface UserInterface
*/ */
public function getSalt(); public function getSalt();
/**
* Returns the username used to authenticate the user.
*
* @return string The username
*/
public function getUsername();
/** /**
* Removes sensitive data from the user. * Removes sensitive data from the user.
* *

View File

@ -12,16 +12,16 @@
namespace Symfony\Component\Security\Core\User; namespace Symfony\Component\Security\Core\User;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException; 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. * 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 * In a typical authentication configuration, a user identifier (e.g. a
* user identifier) credential enters the system (via form login, or any * username or e-mailaddress) credential enters the system (via form login, or
* method). The user provider that is configured with that authentication * any method). The user provider that is configured with that authentication
* method is asked to load the UserInterface object for the given username * method is asked to load the UserInterface object for the given identifier (via
* (via loadUserByUsername) so that the rest of the process can continue. * loadUserByIdentifier) so that the rest of the process can continue.
* *
* Internally, a user provider can load users from any source (databases, * Internally, a user provider can load users from any source (databases,
* configuration, web service). This is totally independent of how the authentication * configuration, web service). This is totally independent of how the authentication
@ -29,22 +29,13 @@ use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
* *
* @see UserInterface * @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 <fabien@symfony.com> * @author Fabien Potencier <fabien@symfony.com>
*/ */
interface UserProviderInterface 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. * Refreshes the user.
* *
@ -56,7 +47,7 @@ interface UserProviderInterface
* @return UserInterface * @return UserInterface
* *
* @throws UnsupportedUserException if the user is not supported * @throws UnsupportedUserException if the user is not supported
* @throws UsernameNotFoundException if the user is not found * @throws UserNotFoundException if the user is not found
*/ */
public function refreshUser(UserInterface $user); public function refreshUser(UserInterface $user);

View File

@ -15,7 +15,7 @@ use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException; 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\PasswordAuthenticatedUserInterface;
use Symfony\Component\Security\Core\User\PasswordUpgraderInterface; use Symfony\Component\Security\Core\User\PasswordUpgraderInterface;
use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Core\User\UserInterface;
@ -98,7 +98,7 @@ class GuardBridgeAuthenticator implements InteractiveAuthenticatorInterface, Aut
$user = $this->guard->getUser($credentials, $this->userProvider); $user = $this->guard->getUser($credentials, $this->userProvider);
if (null === $user) { 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) { if (!$user instanceof UserInterface) {

View File

@ -70,7 +70,7 @@ interface AuthenticatorInterface extends AuthenticationEntryPointInterface
* The *credentials* are the return value from getCredentials() * The *credentials* are the return value from getCredentials()
* *
* You may throw an AuthenticationException if you wish. If you return * 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 * @param mixed $credentials
* *

View File

@ -18,7 +18,7 @@ use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Exception\AuthenticationExpiredException; use Symfony\Component\Security\Core\Exception\AuthenticationExpiredException;
use Symfony\Component\Security\Core\Exception\BadCredentialsException; 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\PasswordAuthenticatedUserInterface;
use Symfony\Component\Security\Core\User\PasswordUpgraderInterface; use Symfony\Component\Security\Core\User\PasswordUpgraderInterface;
use Symfony\Component\Security\Core\User\UserCheckerInterface; use Symfony\Component\Security\Core\User\UserCheckerInterface;
@ -112,8 +112,9 @@ class GuardAuthenticationProvider implements AuthenticationProviderInterface
$user = $guardAuthenticator->getUser($token->getCredentials(), $this->userProvider); $user = $guardAuthenticator->getUser($token->getCredentials(), $this->userProvider);
if (null === $user) { if (null === $user) {
$e = new UsernameNotFoundException(sprintf('Null returned from "%s::getUser()".', get_debug_type($guardAuthenticator))); $e = new UserNotFoundException(sprintf('Null returned from "%s::getUser()".', get_debug_type($guardAuthenticator)));
$e->setUsername($token->getUsername()); // @deprecated since 5.3, change to $token->getUserIdentifier() in 6.0
$e->setUserIdentifier(method_exists($token, 'getUserIdentifier') ? $token->getUserIdentifier() : $token->getUsername());
throw $e; throw $e;
} }

View File

@ -15,7 +15,7 @@ use PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Exception\AuthenticationException; 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\InMemoryUser;
use Symfony\Component\Security\Core\User\UserProviderInterface; use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Guard\Authenticator\GuardBridgeAuthenticator; use Symfony\Component\Security\Guard\Authenticator\GuardBridgeAuthenticator;
@ -97,7 +97,7 @@ class GuardBridgeAuthenticatorTest extends TestCase
public function testAuthenticateNoUser() public function testAuthenticateNoUser()
{ {
$this->expectException(UsernameNotFoundException::class); $this->expectException(UserNotFoundException::class);
$request = new Request(); $request = new Request();

View File

@ -70,7 +70,8 @@ class AuthenticatorManager implements AuthenticatorManagerInterface, UserAuthent
public function authenticateUser(UserInterface $user, AuthenticatorInterface $authenticator, Request $request, array $badges = []): ?Response public function authenticateUser(UserInterface $user, AuthenticatorInterface $authenticator, Request $request, array $badges = []): ?Response
{ {
// create an authenticated token for the User // 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 // announce the authenticated token
$token = $this->eventDispatcher->dispatch(new AuthenticationTokenCreatedEvent($token))->getAuthenticatedToken(); $token = $this->eventDispatcher->dispatch(new AuthenticationTokenCreatedEvent($token))->getAuthenticatedToken();
@ -215,6 +216,11 @@ class AuthenticatorManager implements AuthenticatorManagerInterface, UserAuthent
private function handleAuthenticationSuccess(TokenInterface $authenticatedToken, PassportInterface $passport, Request $request, AuthenticatorInterface $authenticator): ?Response 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); $this->tokenStorage->setToken($authenticatedToken);
$response = $authenticator->onAuthenticationSuccess($request, $authenticatedToken, $this->firewallName); $response = $authenticator->onAuthenticationSuccess($request, $authenticatedToken, $this->firewallName);

View File

@ -87,9 +87,18 @@ abstract class AbstractPreAuthenticatedAuthenticator implements InteractiveAuthe
public function authenticate(Request $request): PassportInterface public function authenticate(Request $request): PassportInterface
{ {
return new SelfValidatingPassport(new UserBadge($request->attributes->get('_pre_authenticated_username'), function ($username) { // @deprecated since 5.3, change to $this->userProvider->loadUserByIdentifier() in 6.0
return $this->userProvider->loadUserByUsername($username); $method = 'loadUserByIdentifier';
}), [new PreAuthenticatedUserBadge()]); 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 public function createAuthenticatedToken(PassportInterface $passport, string $firewallName): TokenInterface

View File

@ -46,7 +46,7 @@ interface AuthenticatorInterface
* presented password and the CSRF token value. * presented password and the CSRF token value.
* *
* You may throw any AuthenticationException in this method in case of error (e.g. * 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 * @throws AuthenticationException
*/ */

View File

@ -84,14 +84,19 @@ class FormLoginAuthenticator extends AbstractLoginFormAuthenticator
{ {
$credentials = $this->getCredentials($request); $credentials = $this->getCredentials($request);
$passport = new Passport(new UserBadge($credentials['username'], function ($username) { // @deprecated since 5.3, change to $this->userProvider->loadUserByIdentifier() in 6.0
$user = $this->userProvider->loadUserByUsername($username); $method = 'loadUserByIdentifier';
if (!$user instanceof UserInterface) { if (!method_exists($this->userProvider, 'loadUserByIdentifier')) {
throw new AuthenticationServiceException('The user provider must return a UserInterface object.'); 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; $passport = new Passport(
}), new PasswordCredentials($credentials['password']), [new RememberMeBadge()]); new UserBadge($credentials['username'], [$this->userProvider, $method]),
new PasswordCredentials($credentials['password']),
[new RememberMeBadge()]
);
if ($this->options['enable_csrf']) { if ($this->options['enable_csrf']) {
$passport->addBadge(new CsrfTokenBadge($this->options['csrf_token_id'], $credentials['csrf_token'])); $passport->addBadge(new CsrfTokenBadge($this->options['csrf_token_id'], $credentials['csrf_token']));
} }

View File

@ -67,14 +67,18 @@ class HttpBasicAuthenticator implements AuthenticatorInterface, AuthenticationEn
$username = $request->headers->get('PHP_AUTH_USER'); $username = $request->headers->get('PHP_AUTH_USER');
$password = $request->headers->get('PHP_AUTH_PW', ''); $password = $request->headers->get('PHP_AUTH_PW', '');
$passport = new Passport(new UserBadge($username, function ($username) { // @deprecated since 5.3, change to $this->userProvider->loadUserByIdentifier() in 6.0
$user = $this->userProvider->loadUserByUsername($username); $method = 'loadUserByIdentifier';
if (!$user instanceof UserInterface) { if (!method_exists($this->userProvider, 'loadUserByIdentifier')) {
throw new AuthenticationServiceException('The user provider must return a UserInterface object.'); 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; $passport = new Passport(
}), new PasswordCredentials($password)); new UserBadge($username, [$this->userProvider, $method]),
new PasswordCredentials($password)
);
if ($this->userProvider instanceof PasswordUpgraderInterface) { if ($this->userProvider instanceof PasswordUpgraderInterface) {
$passport->addBadge(new PasswordUpgradeBadge($password, $this->userProvider)); $passport->addBadge(new PasswordUpgradeBadge($password, $this->userProvider));
} }

View File

@ -94,14 +94,18 @@ class JsonLoginAuthenticator implements InteractiveAuthenticatorInterface
throw $e; throw $e;
} }
$passport = new Passport(new UserBadge($credentials['username'], function ($username) { // @deprecated since 5.3, change to $this->userProvider->loadUserByIdentifier() in 6.0
$user = $this->userProvider->loadUserByUsername($username); $method = 'loadUserByIdentifier';
if (!$user instanceof UserInterface) { if (!method_exists($this->userProvider, 'loadUserByIdentifier')) {
throw new AuthenticationServiceException('The user provider must return a UserInterface object.'); 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; $passport = new Passport(
}), new PasswordCredentials($credentials['password'])); new UserBadge($credentials['username'], [$this->userProvider, $method]),
new PasswordCredentials($credentials['password'])
);
if ($this->userProvider instanceof PasswordUpgraderInterface) { if ($this->userProvider instanceof PasswordUpgraderInterface) {
$passport->addBadge(new PasswordUpgradeBadge($credentials['password'], $this->userProvider)); $passport->addBadge(new PasswordUpgradeBadge($credentials['password'], $this->userProvider));
} }

View File

@ -11,7 +11,7 @@
namespace Symfony\Component\Security\Http\Authenticator\Passport\Badge; 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\Core\User\UserInterface;
use Symfony\Component\Security\Http\EventListener\UserProviderListener; 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. * 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 * 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 * is thrown). If this is not set, the default user provider will be used with
* $userIdentifier as username. * $userIdentifier as username.
*/ */
@ -64,7 +64,7 @@ class UserBadge implements BadgeInterface
$this->user = ($this->userLoader)($this->userIdentifier); $this->user = ($this->userLoader)($this->userIdentifier);
if (!$this->user instanceof UserInterface) { 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)));
} }
} }

View File

@ -82,7 +82,8 @@ class RememberMeAuthenticator implements InteractiveAuthenticatorInterface
throw new \LogicException('No remember me token is set.'); 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 public function createAuthenticatedToken(PassportInterface $passport, string $firewallName): TokenInterface

View File

@ -46,6 +46,13 @@ class UserProviderListener
return; return;
} }
// @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']); $badge->setUserLoader([$this->userProvider, 'loadUserByUsername']);
} }
}
} }

View File

@ -195,7 +195,8 @@ abstract class AbstractAuthenticationListener extends AbstractListener
private function onSuccess(Request $request, TokenInterface $token): Response private function onSuccess(Request $request, TokenInterface $token): Response
{ {
if (null !== $this->logger) { 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); $this->tokenStorage->setToken($token);

View File

@ -83,7 +83,8 @@ abstract class AbstractPreAuthenticatedListener extends AbstractListener
} }
if (null !== $token = $this->tokenStorage->getToken()) { 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; return;
} }
} }

View File

@ -73,7 +73,8 @@ class BasicAuthenticationListener extends AbstractListener
} }
if (null !== $token = $this->tokenStorage->getToken()) { 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; return;
} }
} }

View File

@ -27,7 +27,7 @@ use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInt
use Symfony\Component\Security\Core\Authentication\Token\SwitchUserToken; use Symfony\Component\Security\Core\Authentication\Token\SwitchUserToken;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException; 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\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface; use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Http\Event\DeauthenticatedEvent; use Symfony\Component\Security\Http\Event\DeauthenticatedEvent;
@ -222,7 +222,8 @@ class ContextListener extends AbstractListener
$userDeauthenticated = true; $userDeauthenticated = true;
if (null !== $this->logger) { 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; continue;
@ -231,10 +232,12 @@ class ContextListener extends AbstractListener
$token->setUser($refreshedUser); $token->setUser($refreshedUser);
if (null !== $this->logger) { 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) { 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); $this->logger->debug('User was reloaded from a user provider.', $context);
@ -243,9 +246,9 @@ class ContextListener extends AbstractListener
return $token; return $token;
} catch (UnsupportedUserException $e) { } catch (UnsupportedUserException $e) {
// let's try the next user provider // let's try the next user provider
} catch (UsernameNotFoundException $e) { } catch (UserNotFoundException $e) {
if (null !== $this->logger) { 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; $userNotFoundByProvider = true;

Some files were not shown because too many files have changed in this diff Show More