Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 40a2c19

Browse files
committed
feature #40403 [Security] Rename UserInterface::getUsername() to getUserIdentifier() (wouterj)
This PR was squashed before being merged into the 5.3-dev branch. Discussion ---------- [Security] Rename UserInterface::getUsername() to getUserIdentifier() | Q | A | ------------- | --- | Branch? | 5.x | Bug fix? | no | New feature? | yes | Deprecations? | yes | Tickets | Fix part of #39308 | License | MIT | Doc PR | tbd This continues the great work by @chalasr in #40267 and rounds up the `UserInterface` cleanup. The `getUsername()` has been a point of confusion for many years. In today's applications, many domains no longer have a username, but instead rely on a user's email address or the like. Even more, this username has to be unique for all Security functionality to work correctly - so it's more confusing in complex applications relying on e.g. "username+company" to be unique. **This PR proposes to rename the method to `getUserIdentifier()`**, to more clearly indicate the goal of the method (note: I'm open for any other suggestion). For BC, the `getUsername()` method is deprecated in 5.3 and `getUserIdentifier()` will be added to the `UserInterface` in 6.0. PHPdocs are used to improve type-hinting for 5.3 & 5.4. Both authentication managers (the legacy and experimental one) check the authenticated user object and trigger a deprecation if `getUserIdentifier()` is not implemented. * [x] **judge whether we need to have better deprecations for calling `getUsername()` in the application code.** Consistent with these changes, I've also done the same BC change for `TokenInterface::getUsername()`. * [x] **do the same for remember me's `PersistentTokenInterface::getUsername()`** * [x] **also rename `UserProviderInterface::loadUserByUsername()` & `UsernameNotFoundException`** * [x] **also rename `UserLoaderInterface::loadUserByUsername()`** I'm very much looking forward for feedback and help for this important, but BC-heavy, rename. 😃 Commits ------- 8afd7a3 [Security] Rename UserInterface::getUsername() to getUserIdentifier()
2 parents c469ea6 + 8afd7a3 commit 40a2c19

File tree

116 files changed

+1002
-488
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

116 files changed

+1002
-488
lines changed

UPGRADE-5.3.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ Asset
99
DoctrineBridge
1010
--------------
1111

12+
* Deprecate `UserLoaderInterface::loadUserByUsername()` in favor of `UserLoaderInterface::loadUserByIdentifier()
1213
* Remove `UuidV*Generator` classes
1314

1415
DomCrawler
@@ -178,6 +179,11 @@ Security
178179
}
179180
```
180181

182+
* Deprecate `UserInterface::getUsername()` in favor of `UserInterface::getUserIdentifier()`
183+
* Deprecate `TokenInterface::getUsername()` in favor of `TokenInterface::getUserIdentifier()`
184+
* Deprecate `UserProviderInterface::loadUserByUsername()` in favor of `UserProviderInterface::loadUserByIdentifier()`
185+
* Deprecate `UsernameNotFoundException` in favor of `UserNotFoundException` and `getUsername()`/`setUsername()` in favor of `getUserIdentifier()`/`setUserIdentifier()`
186+
* Deprecate `PersistentTokenInterface::getUsername()` in favor of `PersistentTokenInterface::getUserIdentifier()`
181187
* Deprecate calling `PasswordUpgraderInterface::upgradePassword()` with a `UserInterface` instance that does not implement `PasswordAuthenticatedUserInterface`
182188
* Deprecate calling methods `hashPassword()`, `isPasswordValid()` and `needsRehash()` on `UserPasswordHasherInterface` with a `UserInterface` instance that does not implement `PasswordAuthenticatedUserInterface`
183189
* Deprecate all classes in the `Core\Encoder\` sub-namespace, use the `PasswordHasher` component instead

UPGRADE-6.0.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@ Asset
66

77
* Removed `RemoteJsonManifestVersionStrategy`, use `JsonManifestVersionStrategy` instead.
88

9+
DoctrineBridge
10+
--------------
11+
12+
* Remove `UserLoaderInterface::loadUserByUsername()` in favor of `UserLoaderInterface::loadUserByIdentifier()
13+
914
Config
1015
------
1116

@@ -262,6 +267,11 @@ Security
262267
}
263268
```
264269

270+
* Remove `UserInterface::getUsername()` in favor of `UserInterface::getUserIdentifier()`
271+
* Remove `TokenInterface::getUsername()` in favor of `TokenInterface::getUserIdentifier()`
272+
* Remove `UserProviderInterface::loadUserByUsername()` in favor of `UserProviderInterface::loadUserByIdentifier()`
273+
* Remove `UsernameNotFoundException` in favor of `UserNotFoundException` and `getUsername()`/`setUsername()` in favor of `getUserIdentifier()`/`setUserIdentifier()`
274+
* Remove `PersistentTokenInterface::getUsername()` in favor of `PersistentTokenInterface::getUserIdentifier()`
265275
* Calling `PasswordUpgraderInterface::upgradePassword()` with a `UserInterface` instance that
266276
does not implement `PasswordAuthenticatedUserInterface` now throws a `\TypeError`.
267277
* Calling methods `hashPassword()`, `isPasswordValid()` and `needsRehash()` on `UserPasswordHasherInterface`

src/Symfony/Bridge/Doctrine/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ CHANGELOG
44
5.3
55
---
66

7+
* Deprecate `UserLoaderInterface::loadUserByUsername()` in favor of `UserLoaderInterface::loadUserByIdentifier()
78
* Deprecate `DoctrineTestHelper` and `TestRepositoryFactory`
89
* [BC BREAK] Remove `UuidV*Generator` classes
910
* Add `UuidGenerator`

src/Symfony/Bridge/Doctrine/Security/RememberMe/DoctrineTokenProvider.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,8 @@ public function createNewToken(PersistentTokenInterface $token)
119119
.' VALUES (:class, :username, :series, :value, :lastUsed)';
120120
$paramValues = [
121121
'class' => $token->getClass(),
122-
'username' => $token->getUsername(),
122+
// @deprecated since 5.3, change to $token->getUserIdentifier() in 6.0
123+
'username' => method_exists($token, 'getUserIdentifier') ? $token->getUserIdentifier() : $token->getUsername(),
123124
'series' => $token->getSeries(),
124125
'value' => $token->getTokenValue(),
125126
'lastUsed' => $token->getLastUsed(),

src/Symfony/Bridge/Doctrine/Security/User/EntityUserProvider.php

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
use Doctrine\Persistence\ObjectManager;
1717
use Doctrine\Persistence\ObjectRepository;
1818
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
19-
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
19+
use Symfony\Component\Security\Core\Exception\UserNotFoundException;
2020
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
2121
use Symfony\Component\Security\Core\User\PasswordUpgraderInterface;
2222
use Symfony\Component\Security\Core\User\UserInterface;
@@ -50,21 +50,35 @@ public function __construct(ManagerRegistry $registry, string $classOrAlias, str
5050
* {@inheritdoc}
5151
*/
5252
public function loadUserByUsername(string $username)
53+
{
54+
trigger_deprecation('symfony/doctrine-bridge', '5.3', 'Method "%s()" is deprecated, use loadUserByIdentifier() instead.', __METHOD__);
55+
56+
return $this->loadUserByIdentifier($username);
57+
}
58+
59+
public function loadUserByIdentifier(string $identifier): UserInterface
5360
{
5461
$repository = $this->getRepository();
5562
if (null !== $this->property) {
56-
$user = $repository->findOneBy([$this->property => $username]);
63+
$user = $repository->findOneBy([$this->property => $identifier]);
5764
} else {
5865
if (!$repository instanceof UserLoaderInterface) {
5966
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)));
6067
}
6168

62-
$user = $repository->loadUserByUsername($username);
69+
// @deprecated since 5.3, change to $repository->loadUserByIdentifier() in 6.0
70+
if (method_exists($repository, 'loadUserByIdentifier')) {
71+
$user = $repository->loadUserByIdentifier($identifier);
72+
} else {
73+
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));
74+
75+
$user = $repository->loadUserByUsername($identifier);
76+
}
6377
}
6478

6579
if (null === $user) {
66-
$e = new UsernameNotFoundException(sprintf('User "%s" not found.', $username));
67-
$e->setUsername($username);
80+
$e = new UserNotFoundException(sprintf('User "%s" not found.', $identifier));
81+
$e->setUserIdentifier($identifier);
6882

6983
throw $e;
7084
}
@@ -96,8 +110,8 @@ public function refreshUser(UserInterface $user)
96110

97111
$refreshedUser = $repository->find($id);
98112
if (null === $refreshedUser) {
99-
$e = new UsernameNotFoundException('User with id '.json_encode($id).' not found.');
100-
$e->setUsername(json_encode($id));
113+
$e = new UserNotFoundException('User with id '.json_encode($id).' not found.');
114+
$e->setUserIdentifier(json_encode($id));
101115

102116
throw $e;
103117
}

src/Symfony/Bridge/Doctrine/Security/User/UserLoaderInterface.php

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,11 @@
2222
*
2323
* @see UserInterface
2424
*
25+
* @method UserInterface|null loadUserByIdentifier(string $identifier) loads the user for the given user identifier (e.g. username or email).
26+
* This method must return null if the user is not found.
27+
*
2528
* @author Michal Trojanowski <[email protected]>
2629
*/
2730
interface UserLoaderInterface
2831
{
29-
/**
30-
* Loads the user for the given username.
31-
*
32-
* This method must return null if the user is not found.
33-
*
34-
* @return UserInterface|null
35-
*/
36-
public function loadUserByUsername(string $username);
3732
}

src/Symfony/Bridge/Doctrine/Tests/Fixtures/BaseUser.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,9 @@ public function getUsername(): string
3737
{
3838
return $this->username;
3939
}
40+
41+
public function getUserIdentifier(): string
42+
{
43+
return $this->username;
44+
}
4045
}

src/Symfony/Bridge/Doctrine/Tests/Fixtures/User.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,11 @@ public function getUsername(): string
5353
return $this->name;
5454
}
5555

56+
public function getUserIdentifier(): string
57+
{
58+
return $this->name;
59+
}
60+
5661
public function eraseCredentials()
5762
{
5863
}

src/Symfony/Bridge/Doctrine/Tests/Security/User/EntityUserProviderTest.php

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
use Symfony\Bridge\Doctrine\Security\User\UserLoaderInterface;
2222
use Symfony\Bridge\Doctrine\Tests\DoctrineTestHelper;
2323
use Symfony\Bridge\Doctrine\Tests\Fixtures\User;
24-
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
24+
use Symfony\Component\Security\Core\Exception\UserNotFoundException;
2525
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
2626
use Symfony\Component\Security\Core\User\PasswordUpgraderInterface;
2727
use Symfony\Component\Security\Core\User\UserInterface;
@@ -60,7 +60,7 @@ public function testLoadUserByUsername()
6060

6161
$provider = new EntityUserProvider($this->getManager($em), 'Symfony\Bridge\Doctrine\Tests\Fixtures\User', 'name');
6262

63-
$this->assertSame($user, $provider->loadUserByUsername('user1'));
63+
$this->assertSame($user, $provider->loadUserByIdentifier('user1'));
6464
}
6565

6666
public function testLoadUserByUsernameWithUserLoaderRepositoryAndWithoutProperty()
@@ -70,7 +70,7 @@ public function testLoadUserByUsernameWithUserLoaderRepositoryAndWithoutProperty
7070
$repository = $this->createMock(UserLoaderRepository::class);
7171
$repository
7272
->expects($this->once())
73-
->method('loadUserByUsername')
73+
->method('loadUserByIdentifier')
7474
->with('user1')
7575
->willReturn($user);
7676

@@ -82,7 +82,7 @@ public function testLoadUserByUsernameWithUserLoaderRepositoryAndWithoutProperty
8282
->willReturn($repository);
8383

8484
$provider = new EntityUserProvider($this->getManager($em), 'Symfony\Bridge\Doctrine\Tests\Fixtures\User');
85-
$this->assertSame($user, $provider->loadUserByUsername('user1'));
85+
$this->assertSame($user, $provider->loadUserByIdentifier('user1'));
8686
}
8787

8888
public function testLoadUserByUsernameWithNonUserLoaderRepositoryAndWithoutProperty()
@@ -98,7 +98,7 @@ public function testLoadUserByUsernameWithNonUserLoaderRepositoryAndWithoutPrope
9898
$em->flush();
9999

100100
$provider = new EntityUserProvider($this->getManager($em), 'Symfony\Bridge\Doctrine\Tests\Fixtures\User');
101-
$provider->loadUserByUsername('user1');
101+
$provider->loadUserByIdentifier('user1');
102102
}
103103

104104
public function testRefreshUserRequiresId()
@@ -126,7 +126,7 @@ public function testRefreshInvalidUser()
126126
$provider = new EntityUserProvider($this->getManager($em), 'Symfony\Bridge\Doctrine\Tests\Fixtures\User', 'name');
127127

128128
$user2 = new User(1, 2, 'user2');
129-
$this->expectException(UsernameNotFoundException::class);
129+
$this->expectException(UserNotFoundException::class);
130130
$this->expectExceptionMessage('User with id {"id1":1,"id2":2} not found');
131131

132132
$provider->refreshUser($user2);
@@ -153,7 +153,7 @@ public function testLoadUserByUserNameShouldLoadUserWhenProperInterfaceProvided(
153153
{
154154
$repository = $this->createMock(UserLoaderRepository::class);
155155
$repository->expects($this->once())
156-
->method('loadUserByUsername')
156+
->method('loadUserByIdentifier')
157157
->with('name')
158158
->willReturn(
159159
$this->createMock(UserInterface::class)
@@ -164,7 +164,7 @@ public function testLoadUserByUserNameShouldLoadUserWhenProperInterfaceProvided(
164164
'Symfony\Bridge\Doctrine\Tests\Fixtures\User'
165165
);
166166

167-
$provider->loadUserByUsername('name');
167+
$provider->loadUserByIdentifier('name');
168168
}
169169

170170
public function testLoadUserByUserNameShouldDeclineInvalidInterface()
@@ -177,7 +177,7 @@ public function testLoadUserByUserNameShouldDeclineInvalidInterface()
177177
'Symfony\Bridge\Doctrine\Tests\Fixtures\User'
178178
);
179179

180-
$provider->loadUserByUsername('name');
180+
$provider->loadUserByIdentifier('name');
181181
}
182182

183183
public function testPasswordUpgrades()
@@ -231,6 +231,7 @@ private function createSchema($em)
231231

232232
abstract class UserLoaderRepository implements ObjectRepository, UserLoaderInterface
233233
{
234+
abstract public function loadUserByIdentifier(string $identifier): ?UserInterface;
234235
}
235236

236237
abstract class PasswordUpgraderRepository implements ObjectRepository, PasswordUpgraderInterface

src/Symfony/Bridge/Monolog/Processor/AbstractTokenProcessor.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,16 @@ public function __invoke(array $record): array
4242

4343
if (null !== $token = $this->getToken()) {
4444
$record['extra'][$this->getKey()] = [
45-
'username' => $token->getUsername(),
4645
'authenticated' => $token->isAuthenticated(),
4746
'roles' => $token->getRoleNames(),
4847
];
48+
49+
// @deprecated since 5.3, change to $token->getUserIdentifier() in 6.0
50+
if (method_exists($token, 'getUserIdentifier')) {
51+
$record['extra'][$this->getKey()]['username'] = $record['extra'][$this->getKey()]['user_identifier'] = $token->getUserIdentifier();
52+
} else {
53+
$record['extra'][$this->getKey()]['username'] = $token->getUsername();
54+
}
4955
}
5056

5157
return $record;

src/Symfony/Bridge/Monolog/Tests/Processor/SwitchUserTokenProcessorTest.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,15 @@ public function testProcessor()
3737

3838
$expected = [
3939
'impersonator_token' => [
40-
'username' => 'original_user',
4140
'authenticated' => true,
4241
'roles' => ['ROLE_SUPER_ADMIN'],
42+
'username' => 'original_user',
4343
],
4444
];
45-
$this->assertSame($expected, $record['extra']);
45+
if (method_exists($originalToken, 'getUserIdentifier')) {
46+
$expected['impersonator_token']['user_identifier'] = 'original_user';
47+
}
48+
49+
$this->assertEquals($expected, $record['extra']);
4650
}
4751
}

src/Symfony/Bridge/Monolog/Tests/Processor/TokenProcessorTest.php

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,12 @@
2323
*/
2424
class TokenProcessorTest extends TestCase
2525
{
26-
public function testProcessor()
26+
public function testLegacyProcessor()
2727
{
28+
if (method_exists(UsernamePasswordToken::class, 'getUserIdentifier')) {
29+
$this->markTestSkipped('This test requires symfony/security-core <5.3');
30+
}
31+
2832
$token = new UsernamePasswordToken('user', 'password', 'provider', ['ROLE_USER']);
2933
$tokenStorage = $this->createMock(TokenStorageInterface::class);
3034
$tokenStorage->method('getToken')->willReturn($token);
@@ -38,4 +42,24 @@ public function testProcessor()
3842
$this->assertEquals($token->isAuthenticated(), $record['extra']['token']['authenticated']);
3943
$this->assertEquals(['ROLE_USER'], $record['extra']['token']['roles']);
4044
}
45+
46+
public function testProcessor()
47+
{
48+
if (!method_exists(UsernamePasswordToken::class, 'getUserIdentifier')) {
49+
$this->markTestSkipped('This test requires symfony/security-core 5.3+');
50+
}
51+
52+
$token = new UsernamePasswordToken('user', 'password', 'provider', ['ROLE_USER']);
53+
$tokenStorage = $this->createMock(TokenStorageInterface::class);
54+
$tokenStorage->method('getToken')->willReturn($token);
55+
56+
$processor = new TokenProcessor($tokenStorage);
57+
$record = ['extra' => []];
58+
$record = $processor($record);
59+
60+
$this->assertArrayHasKey('token', $record['extra']);
61+
$this->assertEquals($token->getUserIdentifier(), $record['extra']['token']['user_identifier']);
62+
$this->assertEquals($token->isAuthenticated(), $record['extra']['token']['authenticated']);
63+
$this->assertEquals(['ROLE_USER'], $record['extra']['token']['roles']);
64+
}
4165
}

src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Controller/SecurityController.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,6 @@ class SecurityController implements ContainerAwareInterface
2121

2222
public function profileAction()
2323
{
24-
return new Response('Welcome '.$this->container->get('security.token_storage')->getToken()->getUsername().'!');
24+
return new Response('Welcome '.$this->container->get('security.token_storage')->getToken()->getUserIdentifier().'!');
2525
}
2626
}

src/Symfony/Bundle/SecurityBundle/DataCollector/SecurityDataCollector.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,9 @@ public function collect(Request $request, Response $response, \Throwable $except
9797

9898
$impersonatorUser = null;
9999
if ($token instanceof SwitchUserToken) {
100-
$impersonatorUser = $token->getOriginalToken()->getUsername();
100+
$originalToken = $token->getOriginalToken();
101+
// @deprecated since 5.3, change to $originalToken->getUserIdentifier() in 6.0
102+
$impersonatorUser = method_exists($originalToken, 'getUserIdentifier') ? $originalToken->getUserIdentifier() : $originalToken->getUsername();
101103
}
102104

103105
if (null !== $this->roleHierarchy) {
@@ -126,7 +128,8 @@ public function collect(Request $request, Response $response, \Throwable $except
126128
'token' => $token,
127129
'token_class' => $this->hasVarDumper ? new ClassStub(\get_class($token)) : \get_class($token),
128130
'logout_url' => $logoutUrl,
129-
'user' => $token->getUsername(),
131+
// @deprecated since 5.3, change to $token->getUserIdentifier() in 6.0
132+
'user' => method_exists($token, 'getUserIdentifier') ? $token->getUserIdentifier() : $token->getUsername(),
130133
'roles' => $assignedRoles,
131134
'inherited_roles' => array_unique($inheritedRoles),
132135
'supports_role_hierarchy' => null !== $this->roleHierarchy,

src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/UserProvider/InMemoryFactory.php

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,30 @@ public function addConfiguration(NodeDefinition $node)
4848
->fixXmlConfig('user')
4949
->children()
5050
->arrayNode('users')
51-
->useAttributeAsKey('name')
51+
->useAttributeAsKey('identifier')
5252
->normalizeKeys(false)
53+
->beforeNormalization()
54+
->always()
55+
->then(function ($v) {
56+
$deprecation = false;
57+
foreach ($v as $i => $child) {
58+
if (!isset($child['name'])) {
59+
continue;
60+
}
61+
62+
$deprecation = true;
63+
64+
$v[$i]['identifier'] = $child['name'];
65+
unset($v[$i]['name']);
66+
}
67+
68+
if ($deprecation) {
69+
trigger_deprecation('symfony/security-bundle', '5.3', 'The "in_memory.user.name" option is deprecated, use "identifier" instead.');
70+
}
71+
72+
return $v;
73+
})
74+
->end()
5375
->prototype('array')
5476
->children()
5577
->scalarNode('password')->defaultNull()->end()

src/Symfony/Bundle/SecurityBundle/Resources/config/schema/security-1.0.xsd

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,8 @@
136136
</xsd:complexType>
137137

138138
<xsd:complexType name="user">
139-
<xsd:attribute name="name" type="xsd:string" use="required" />
139+
<xsd:attribute name="identifier" type="xsd:string" />
140+
<xsd:attribute name="name" type="xsd:string" />
140141
<xsd:attribute name="password" type="xsd:string" />
141142
<xsd:attribute name="roles" type="xsd:string" />
142143
</xsd:complexType>

0 commit comments

Comments
 (0)