8000 feature #42423 [Security] Deprecate AnonymousToken, non-UserInterface… · symfony/symfony@76a7fe7 · GitHub
[go: up one dir, main page]

Skip to content

Commit 76a7fe7

Browse files
committed
feature #42423 [Security] Deprecate AnonymousToken, non-UserInterface users, and token credentials (wouterj)
This PR was squashed before being merged into the 5.4 branch. Discussion ---------- [Security] Deprecate AnonymousToken, non-UserInterface users, and token credentials | Q | A | ------------- | --- | Branch? | 5.4 | Bug fix? | no | New feature? | no | Deprecations? | yes | Tickets | Ref #41613, #34909 | License | MIT | Doc PR | - This is a continuation of `@xabbuh`'s experiment in #34909 and `@chalasr`'s work in #42050. This hopefully is the last cleanup of `TokenInterface`: * As tokens now always represent an authenticated user (and no longer e.g. the "username" input of the form), we can finally remove the weird `string|\Stringable` union from `Token::getUser()` and other helper methods and require a user to be an instance of `UserInterface`. * For the same reason, we can also deprecate token credentials. I didn't deprecate `Token::eraseCredentials()` as this is still used to remove credentials from `UserInterface`. * Meanwhile, this also deprecated the `AnonymousToken`, which we forgot in 5.3. This token is not used anymore in the new system (anonymous does no longer exists). This was also the only token in core that didn't fulfill the `UserInterface` requirement for authenticated tokens. Commits ------- 44b843a [Security] Deprecate AnonymousToken, non-UserInterface users, and token credentials
2 parents c939ea1 + 44b843a commit 76a7fe7

File tree

47 files changed

+347
-130
lines changed

Some content is hidden

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

47 files changed

+347
-130
lines changed

UPGRADE-5.4.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,11 @@ SecurityBundle
5959
Security
6060
--------
6161

62-
* Deprecate the `$authManager` argument of `AccessListener`
63-
* Deprecate the `$authenticationManager` argument of the `AuthorizationChecker` constructor
62+
* Deprecate `AnonymousToken`, as the related authenticator was deprecated in 5.3
63+
* Deprecate `Token::getCredentials()`, tokens should no longer contain credentials (as they represent authenticated sessions)
64+
* Deprecate not returning an `UserInterface` from `Token::getUser()`
65+
* Deprecate the `$authManager` argument of `AccessListener`, the argument will be removed
66+
* Deprecate the `$authenticationManager` argument of the `AuthorizationChecker` constructor, the argument will be removed
6467
* Deprecate setting the `$alwaysAuthenticate` argument to `true` and not setting the
6568
`$exceptionOnNoToken argument to `false` of `AuthorizationChecker` (this is the default
6669
behavior when using `enable_authenticator_manager: true`)

UPGRADE-6.0.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,9 @@ Routing
207207
Security
208208
--------
209209

210+
* Remove `AnonymousToken`
211+
* Remove `Token::getCredentials()`, tokens should no longer contain credentials (as they represent authenticated sessions)
212+
* Restrict the return type of `Token::getUser()` to `UserInterface` (removing `string|\Stringable`)
210213
* Remove the 4th and 5th argument of `AuthorizationChecker`
211214
* Remove the 5th argument of `AccessListener`
212215
* Remove class `User`, use `InMemoryUser` or your own implementation instead.

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

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
1717
use Symfony\Component\Security\Core\Authentication\Token\SwitchUserToken;
1818
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
19+
use Symfony\Component\Security\Core\User\InMemoryUser;
20+
use Symfony\Component\Security\Core\User\User;
1921

2022
/**
2123
* Tests the SwitchUserTokenProcessor.
@@ -26,8 +28,13 @@ class SwitchUserTokenProcessorTest extends TestCase
2628
{
2729
public function testProcessor()
2830
{
29-
$originalToken = new UsernamePasswordToken('original_user', 'password', 'provider', ['ROLE_SUPER_ADMIN']);
30-
$switchUserToken = new SwitchUserToken('user', 'passsword', 'provider', ['ROLE_USER'], $originalToken);
31+
if (class_exists(InMemoryUser::class)) {
32+
$originalToken = new UsernamePasswordToken(new InMemoryUser('original_user', 'password', ['ROLE_SUPER_ADMIN']), 'provider', ['ROLE_SUPER_ADMIN']);
33+
$switchUserToken = new SwitchUserToken(new InMemoryUser('user', 'passsword', ['ROLE_USER']), 'provider', ['ROLE_USER'], $originalToken);
34+
} else {
35+
$originalToken = new UsernamePasswordToken(new User('original_user', 'password', ['ROLE_SUPER_ADMIN']), null, 'provider', ['ROLE_SUPER_ADMIN']);
36+
$switchUserToken = new SwitchUserToken(new User('user', 'passsword', ['ROLE_USER']), null, 'provider', ['ROLE_USER'], $originalToken);
37+
}
3138
$tokenStorage = $this->createMock(TokenStorageInterface::class);
3239
$tokenStorage->method('getToken')->willReturn($switchUserToken);
3340

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use Symfony\Bridge\Monolog\Processor\TokenProcessor;
1616
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
1717
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
18+
use Symfony\Component\Security\Core\User\InMemoryUser;
1819

1920
/**
2021
* Tests the TokenProcessor.
@@ -23,6 +24,9 @@
2324
*/
2425
class TokenProcessorTest extends TestCase
2526
{
27+
/**
28+
* @group legacy
29+
*/
2630
public function testLegacyProcessor()
2731
{
2832
if (method_exists(UsernamePasswordToken::class, 'getUserIdentifier')) {
@@ -48,7 +52,7 @@ public function testProcessor()
4852
$this->markTestSkipped('This test requires symfony/security-core 5.3+');
4953
}
5054

51-
$token = new UsernamePasswordToken('user', 'password', 'provider', ['ROLE_USER']);
55+
$token = new UsernamePasswordToken(new InMemoryUser('user', 'password', ['ROLE_USER']), 'provider', ['ROLE_USER']);
5256
$tokenStorage = $this->createMock(TokenStorageInterface::class);
5357
$tokenStorage->method('getToken')->willReturn($token);
5458

src/Symfony/Bridge/Twig/AppVariable.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ public function getUser()
8484

8585
$user = $token->getUser();
8686

87+
// @deprecated since 5.4, $user will always be a UserInterface instance
8788
return \is_object($user) ? $user : null;
8889
}
8990

src/Symfony/Bundle/FrameworkBundle/Controller/AbstractController.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -395,7 +395,7 @@ protected function getDoctrine(): ManagerRegistry
395395
/**
396396
* Get a user from the Security Token Storage.
397397
*
398-
* @return UserInterface|object|null
398+
* @return UserInterface|null
399399
*
400400
* @throws \LogicException If SecurityBundle is not available
401401
*
@@ -411,6 +411,7 @@ protected function getUser()
411411
return null;
412412
}
413413

414+
// @deprecated since 5.4, $user will always be a UserInterface instance
414415
if (!\is_object($user = $token->getUser())) {
415416
// e.g. anonymous authentication
416417
return null;

src/Symfony/Bundle/FrameworkBundle/Tests/Controller/AbstractControllerTest.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,14 +138,17 @@ public function testForward()
138138
public function testGetUser()
139139
{
140140
$user = new InMemoryUser('user', 'pass');
141-
$token = new UsernamePasswordToken($user, 'pass', 'default', ['ROLE_USER']);
141+
$token = new UsernamePasswordToken($user, 'default', ['ROLE_USER']);
142142

143143
$controller = $this->createController();
144144
$controller->setContainer($this->getContainerWithTokenStorage($token));
145145

146146
$this->assertSame($controller->getUser(), $user);
147147
}
148148

149+
/**
150+
* @group legacy
151+
*/
149152
public function testGetUserAnonymousUserConvertedToNull()
150153
{
151154
$token = new AnonymousToken('default', 'anon.');

src/Symfony/Bundle/FrameworkBundle/composer.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@
5353
"symfony/notifier": "^5.3|^6.0",
5454
"symfony/process": "^4.4|^5.0|^6.0",
5555
"symfony/rate-limiter": "^5.2|^6.0",
56-
"symfony/security-bundle": "^5.3|^6.0",
56+
"symfony/security-bundle": "^5.4|^6.0",
5757
"symfony/serializer": "^5.4|^6.0",
5858
"symfony/stopwatch": "^4.4|^5.0|^6.0",
5959
"symfony/string": "^5.0|^6.0",
@@ -89,7 +89,6 @@
8989
"symfony/property-access": "<5.3",
9090
"symfony/serializer": "<5.2",
9191
"symfony/security-csrf": "<5.3",
92-
"symfony/security-core": "<5.3",
9392
"symfony/stopwatch": "<4.4",
9493
"symfony/translation": "<5.3",
9594
"symfony/twig-bridge": "<4.4",

src/Symfony/Bundle/SecurityBundle/Tests/DataCollector/SecurityDataCollectorTest.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
use Symfony\Component\Security\Core\Authorization\Voter\TraceableVoter;
3030
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
3131
use Symfony\Component\Security\Core\Role\RoleHierarchy;
32+
use Symfony\Component\Security\Core\User\InMemoryUser;
3233
use Symfony\Component\Security\Http\FirewallMapInterface;
3334
use Symfony\Component\Security\Http\Logout\LogoutUrlGenerator;
3435
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
@@ -78,7 +79,7 @@ public function testCollectWhenAuthenticationTokenIsNull()
7879
public function testCollectAuthenticationTokenAndRoles(array $roles, array $normalizedRoles, array $inheritedRoles)
7980
{
8081
$tokenStorage = new TokenStorage();
81-
$tokenStorage->setToken(new UsernamePasswordToken('hhamon', 'P4$$w0rD', 'provider', $roles));
82+
$tokenStorage->setToken(new UsernamePasswordToken(new InMemoryUser('hhamon', 'P4$$w0rD', $roles), 'provider', $roles));
8283

8384
$collector = new SecurityDataCollector($tokenStorage, $this->getRoleHierarchy(), null, null, null, null, true);
8485
$collector->collect(new Request(), new Response());
@@ -99,10 +100,10 @@ public function testCollectAuthenticationTokenAndRoles(array $roles, array $norm
99100

100101
public function testCollectSwitchUserToken()
101102
{
102-
$adminToken = new UsernamePasswordToken('yceruto', 'P4$$w0rD', 'provider', ['ROLE_ADMIN']);
103+
$adminToken = new UsernamePassw BD94 ordToken(new InMemoryUser('yceruto', 'P4$$w0rD', ['ROLE_ADMIN']), 'provider', ['ROLE_ADMIN']);
103104

104105
$tokenStorage = new TokenStorage();
105-
$tokenStorage->setToken(new SwitchUserToken('hhamon', 'P4$$w0rD', 'provider', ['ROLE_USER', 'ROLE_PREVIOUS_ADMIN'], $adminToken));
106+
$tokenStorage->setToken(new SwitchUserToken(new InMemoryUser('hhamon', 'P4$$w0rD', ['ROLE_USER', 'ROLE_PREVIOUS_ADMIN']), 'provider', ['ROLE_USER', 'ROLE_PREVIOUS_ADMIN'], $adminToken));
106107

107108
$collector = new SecurityDataCollector($tokenStorage, $this->getRoleHierarchy(), null, null, null, null, true);
108109
$collector->collect(new Request(), new Response());

src/Symfony/Bundle/SecurityBundle/Tests/Functional/MissingUserProviderTest.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ public function testUserProviderIsNeeded()
2828
]);
2929
}
3030

31+
/**
32+
* @group legacy
33+
*/
3134
public function testLegacyUserProviderIsNeeded()
3235
{
3336
$client = $this->createClient(['test_case' => 'MissingUserProvider', 'root_config' => 'config.yml', 'debug' => true]);

src/Symfony/Bundle/SecurityBundle/Tests/Functional/SecurityTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ public function testServiceIsFunctional()
2727

2828
// put a token into the storage so the final calls can function
2929
$user = new InMemoryUser('foo', 'pass');
30-
$token = new UsernamePasswordToken($user, '', 'provider', ['ROLE_USER']);
30+
$token = new UsernamePasswordToken($user, 'provider', ['ROLE_USER']);
3131
$container->get('functional.test.security.token_storage')->setToken($token);
3232

3333
$security = $container->get('functional_test.security.helper');
@@ -105,7 +105,7 @@ public function testLegacyServiceIsFunctional()
105105

106106
// put a token into the storage so the final calls can function
107107
$user = new InMemoryUser('foo', 'pass');
108-
$token = new UsernamePasswordToken($user, '', 'provider', ['ROLE_USER']);
108+
$token = new UsernamePasswordToken($user, 'provider', ['ROLE_USER']);
109109
$container->get('functional.test.security.token_storage')->setToken($token);
110110

111111
$security = $container->get('functional_test.security.helper');

src/Symfony/Component/Security/Core/Authentication/Token/AbstractToken.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,10 @@ public function setUser($user)
9999
throw new \InvalidArgumentException('$user must be an instanceof UserInterface, an object implementing a __toString method, or a primitive string.');
100100
}
101101

102+
if (!$user instanceof UserInterface) {
103+
trigger_deprecation('symfony/security-core', '5.4', 'Using an object that is not an instance of "%s" as $user in "%s" is deprecated.', UserInterface::class, static::class);
104+
}
105+
102106
// @deprecated since Symfony 5.4, remove the whole block if/elseif/else block in 6.0
103107
if (1 < \func_num_args() && !func_get_arg(1)) {
104108
// ContextListener checks if the user has changed on its own and calls `setAuthenticated()` subsequently,

src/Symfony/Component/Security/Core/Authentication/Token/AnonymousToken.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
* AnonymousToken represents an anonymous token.
1818
*
1919
* @author Fabien Potencier <fabien@symfony.com>
20+
*
21+
* @deprecated since 5.4, anonymous is now represented by the absence of a token
2022
*/
2123
class AnonymousToken extends AbstractToken
2224
{
@@ -29,6 +31,8 @@ class AnonymousToken extends AbstractToken
2931
*/
3032
public function __construct(string $secret, $user, array $roles = [])
3133
{
34+
trigger_deprecation('symfony/security-core', '5.4', 'The "%s" class is deprecated.', __CLASS__);
35+
3236
parent::__construct($roles);
3337

3438
$this->secret = $secret;

src/Symfony/Component/Security/Core/Authentication/Token/PreAuthenticatedToken.php

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,20 +24,28 @@ class PreAuthenticatedToken extends AbstractToken
2424
private $firewallName;
2525

2626
/**
27-
* @param string|\Stringable|UserInterface $user
28-
* @param mixed $credentials
29-
* @param string[] $roles
27+
* @param UserInterface $user
28+
* @param string $firewallName
29+
* @param string[] $roles
3030
*/
31-
public function __construct($user, $credentials, string $firewallName, array $roles = [])
31+
public function __construct($user, /*string*/ $firewallName, /*array*/ $roles = [])
3232
{
33+
if (\is_string($roles)) {
34+
trigger_deprecation('symfony/security-core', '5.4', 'Argument $credentials of "%s()" is deprecated.', __METHOD__);
35+
36+
$credentials = $firewallName;
37+
$firewallName = $roles;
38+
$roles = \func_num_args() > 3 ? func_get_arg(3) : [];
39+
}
40+
3341
parent::__construct($roles);
3442

3543
if ('' === $firewallName) {
3644
throw new \InvalidArgumentException('$firewallName must not be empty.');
3745
}
3846

3947
$this->setUser($user);
40-
$this->credentials = $credentials;
48+
$this->credentials = $credentials ?? null;
4149
$this->firewallName = $firewallName;
4250

4351
if ($roles) {
@@ -55,7 +63,7 @@ public function __construct($user, $credentials, string $firewallName, array $ro
5563
public function getProviderKey()
5664
{
5765
if (1 !== \func_num_args() || true !== func_get_arg(0)) {
58-
trigger_deprecation('symfony/security-core', '5.2', 'Method "%s" is deprecated, use "getFirewallName()" instead.', __METHOD__);
66+
trigger_deprecation('symfony/security-core', '5.2', 'Method "%s()" is deprecated, use "getFirewallName()" instead.', __METHOD__);
5967
}
6068

6169
return $this->firewallName;
@@ -71,6 +79,8 @@ public function getFirewallName(): string
7179
*/
7280
public function getCredentials()
7381
{
82+
trigger_deprecation('symfony/security-core', '5.4', 'Method "%s()" is deprecated.', __METHOD__);
83+
7484
return $this->credentials;
7585
}
7686

src/Symfony/Component/Security/Core/Authentication/Token/RememberMeToken.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ public function setAuthenticated(bool $authenticated)
6969
public function getProviderKey()
7070
{
7171
if (1 !== \func_num_args() || true !== func_get_arg(0)) {
72-
trigger_deprecation('symfony/security-core', '5.2', 'Method "%s" is deprecated, use "getFirewallName()" instead.', __METHOD__);
72+
trigger_deprecation('symfony/security-core', '5.2', 'Method "%s()" is deprecated, use "getFirewallName()" instead.', __METHOD__);
7373
}
7474

7575
return $this->firewallName;
@@ -95,6 +95,8 @@ public function getSecret()
9595
*/
9696
public function getCredentials()
9797
{
98+
trigger_deprecation('symfony/security-core', '5.4', 'Method "%s()" is deprecated.', __METHOD__);
99+
98100
return '';
99101
}
100102

src/Symfony/Component/Security/Core/Authentication/Token/SwitchUserToken.php

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111

1212
namespace Symfony\Component\Security\Core\Authentication\Token;
1313

14+
use Symfony\Component\Security\Core\User\UserInterface;
15+
1416
/**
1517
* Token representing a user who temporarily impersonates another one.
1618
*
@@ -22,15 +24,29 @@ class SwitchUserToken extends UsernamePasswordToken
2224
private $originatedFromUri;
2325

2426
/**
25-
* @param string|object $user The username (like a nickname, email address, etc.), or a UserInterface instance or an object implementing a __toString method
26-
* @param mixed $credentials This usually is the password of the user
27+
* @param UserInterface $user
2728
* @param string|null $originatedFromUri The URI where was the user at the switch
2829
*
2930
* @throws \InvalidArgumentException
3031
*/
31-
public function __construct($user, $credentials, string $firewallName, array $roles, TokenInterface $originalToken, string $originatedFromUri = null)
32+
public function __construct($user, /*string*/ $firewallName, /*array*/ $roles, /*TokenInterface*/ $originalToken, /*string*/ $originatedFromUri = null)
3233
{
33-
parent::__construct($user, $credentials, $firewallName, $roles);
34+
if (\is_string($roles)) {
35+
// @deprecated since 5.4, deprecation is triggered by UsernamePasswordToken::__construct()
36+
$credentials = $firewallName;
37+
$firewallName = $roles;
38+
$roles = $originalToken;
39+
$originalToken = $originatedFromUri;
40+
$originatedFromUri = \func_num_args() > 5 ? func_get_arg(5) : null;
41+
42+
parent::__construct($user, $credentials, $firewallName, $roles);
43+
} else {
44+
parent::__construct($user, $firewallName, $roles);
45+
}
46+
47+
if (!$originalToken instanceof TokenInterface) {
48+
throw new \TypeError(sprintf('Argument $originalToken of "%s" must be an instance of "%s", "%s" given.', __METHOD__, TokenInterface::class, get_debug_type($originalToken)));
49+
}
3450

3551
$this->originalToken = $originalToken;
3652
$this->originatedFromUri = $originatedFromUri;

src/Symfony/Component/Security/Core/Authentication/Token/TokenInterface.php

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,25 +43,24 @@ public function getRoleNames(): array;
4343
* Returns the user credentials.
4444
*
4545
* @return mixed The user credentials
46+
*
47+
* @deprecated since 5.4
4648
*/
4749
public function getCredentials();
4850

4951
/**
5052
* Returns a user representation.
5153
*
52-
* @return string|\Stringable|UserInterface
54+
* @return UserInterface
5355
*
5456
* @see AbstractToken::setUser()
5557
*/
5658
public function getUser();
5759

5860
/**
59-
* Sets the user in the token.
60-
*
61-
* The user can be a UserInterface instance, or an object implementing
62-
* a __toString method or the username as a regular string.
61+
* Sets the authenticated user in the token.
6362
*
64-
* @param string|\Stringable|UserInterface $user
63+
* @param UserInterface $user
6564
*
6665
* @throws \InvalidArgumentException
6766
*/

0 commit comments

Comments
 (0)
0