Skip to content

Commit 8b0dda3

Browse files
[Security] Improve BC-layer to deprecate eraseCredentials methods
1 parent 71c434b commit 8b0dda3

File tree

6 files changed

+59
-36
lines changed

6 files changed

+59
-36
lines changed

Authentication/AuthenticatorManager.php

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use Psr\Log\LoggerInterface;
1515
use Symfony\Component\HttpFoundation\Request;
1616
use Symfony\Component\HttpFoundation\Response;
17+
use Symfony\Component\Security\Core\Authentication\Token\AbstractToken;
1718
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
1819
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
1920
use Symfony\Component\Security\Core\AuthenticationEvents;
@@ -69,10 +70,6 @@ public function __construct(
6970
}
7071

7172
$this->exposeSecurityErrors = $exposeSecurityErrors;
72-
73-
if ($eraseCredentials) {
74-
trigger_deprecation('symfony/security-http', '7.3', sprintf('Passing true as "$eraseCredentials" argument to "%s::__construct()" is deprecated and won\'t have any effect in 8.0, pass "false" instead and use your own erasing logic if needed.', __CLASS__));
75-
}
7673
}
7774

7875
/**
@@ -212,9 +209,8 @@ private function executeAuthenticator(AuthenticatorInterface $authenticator, Req
212209
// announce the authentication token
213210
$authenticatedToken = $this->eventDispatcher->dispatch(new AuthenticationTokenCreatedEvent($authenticatedToken, $passport))->getAuthenticatedToken();
214211

215-
// @deprecated since Symfony 7.3, remove the if statement in 8.0
216-
if (true === $this->eraseCredentials) {
217-
$authenticatedToken->eraseCredentials();
212+
if ($this->eraseCredentials) {
213+
self::checkEraseCredentials($authenticatedToken)?->eraseCredentials();
218214
}
219215

220216
$this->eventDispatcher->dispatch(new AuthenticationSuccessEvent($authenticatedToken), AuthenticationEvents::AUTHENTICATION_SUCCESS);
@@ -292,4 +288,41 @@ private function isSensitiveException(AuthenticationException $exception): bool
292288

293289
return false;
294290
}
291+
292+
/**
293+
* @deprecated since Symfony 7.3
294+
*/
295+
private static function checkEraseCredentials(TokenInterface|UserInterface|null $token): TokenInterface|UserInterface|null
296+
{
297+
if (!$token || !method_exists($token, 'eraseCredentials')) {
298+
return null;
299+
}
300+
301+
static $genericImplementations = [];
302+
$m = null;
303+
304+
if (!isset($genericImplementations[$token::class])) {
305+
$m = new \ReflectionMethod($token, 'eraseCredentials');
306+
$genericImplementations[$token::class] = AbstractToken::class === $m->class;
307+
}
308+
309+
if ($genericImplementations[$token::class]) {
310+
return self::checkEraseCredentials($token->getUser());
311+
}
312+
313+
static $deprecatedImplementations = [];
314+
315+
if (!isset($deprecatedImplementations[$token::class])) {
316+
$m ??= new \ReflectionMethod($token, 'eraseCredentials');
317+
$deprecatedImplementations[$token::class] = !$m->getAttributes(\Deprecated::class);
318+
}
319+
320+
if ($deprecatedImplementations[$token::class]) {
321+
trigger_deprecation('symfony/security-http', '7.3', 'Implementing "%s::eraseCredentials()" is deprecated since Symfony 7.3; add the #[\Deprecated] attribute on the method to signal its either empty or that you moved the logic elsewhere, typically to the "__serialize()" method.', get_debug_type($token));
322+
323+
return $token;
324+
}
325+
326+
return null;
327+
}
295328
}

CHANGELOG.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@ CHANGELOG
66

77
* Add encryption support to `OidcTokenHandler` (JWE)
88
* Replace `$hideAccountStatusExceptions` argument with `$exposeSecurityErrors` in `AuthenticatorManager` constructor
9-
* Deprecate passing `true` for the `$eraseCredentials` parameter of `AuthenticatorManager::__construct()`, erase credentials
10-
on your own e.g. upon `AuthenticationTokenCreatedEvent` instead. Passing it won't thave any effect in 8.0.
119

1210
7.2
1311
---

Tests/Authentication/AuthenticatorManagerTest.php

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
use Symfony\Component\EventDispatcher\EventDispatcher;
2020
use Symfony\Component\HttpFoundation\Request;
2121
use Symfony\Component\HttpFoundation\Response;
22+
use Symfony\Component\Security\Core\Authentication\Token\AbstractToken;
2223
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
2324
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
2425
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
@@ -166,7 +167,7 @@ public function testRequiredBadgeMissing()
166167

167168
$authenticator->expects($this->once())->method('onAuthenticationFailure')->with($this->anything(), $this->callback(fn ($exception) => 'Authentication failed; Some badges marked as required by the firewall config are not available on the passport: "'.CsrfTokenBadge::class.'".' === $exception->getMessage()));
168169

169-
$manager = $this->createManager([$authenticator], 'main', false, [CsrfTokenBadge::class], exposeSecurityErrors: ExposeSecurityLevel::None);
170+
$manager = $this->createManager([$authenticator], 'main', true, [CsrfTokenBadge::class], exposeSecurityErrors: ExposeSecurityLevel::None);
170171
$manager->authenticateRequest($this->request);
171172
}
172173

@@ -182,12 +183,13 @@ public function testAllRequiredBadgesPresent()
182183

183184
$authenticator->expects($this->once())->method('onAuthenticationSuccess');
184185

185-
$manager = $this->createManager([$authenticator], 'main', false, [CsrfTokenBadge::class], exposeSecurityErrors: ExposeSecurityLevel::None);
186+
$manager = $this->createManager([$authenticator], 'main', true, [CsrfTokenBadge::class], exposeSecurityErrors: ExposeSecurityLevel::None);
186187
$manager->authenticateRequest($this->request);
187188
}
188189

189190
/**
190191
* @group legacy
192+
*
191193
* @dataProvider provideEraseCredentialsData
192194
*/
193195
public function testEraseCredentials($eraseCredentials)
@@ -197,16 +199,25 @@ public function testEraseCredentials($eraseCredentials)
197199

198200
$authenticator->expects($this->any())->method('authenticate')->willReturn(new SelfValidatingPassport(new UserBadge('wouter', fn () => $this->user)));
199201

200-
$authenticator->expects($this->any())->method('createToken')->willReturn($this->token);
202+
$token = new class extends AbstractToken {
203+
public $erased = false;
204+
205+
public function eraseCredentials(): void
206+
{
207+
$this->erased = true;
208+
}
209+
};
201210

202-
$this->token->expects($eraseCredentials ? $this->once() : $this->never())->method('eraseCredentials');
211+
$authenticator->expects($this->any())->method('createToken')->willReturn($token);
203212

204213
if ($eraseCredentials) {
205-
$this->expectDeprecation('Since symfony/security-http 7.3: Passing true as "$eraseCredentials" argument to "Symfony\Component\Security\Http\Authentication\AuthenticatorManager::__construct()" is deprecated and won\'t have any effect in 8.0, pass "false" instead and use your own erasing logic if needed.');
214+
$this->expectDeprecation(\sprintf('Since symfony/security-http 7.3: Implementing "%s@anonymous::eraseCredentials()" is deprecated since Symfony 7.3; add the #[\Deprecated] attribute on the method to signal its either empty or that you moved the logic elsewhere, typically to the "__serialize()" method.', AbstractToken::class));
206215
}
207216

208217
$manager = $this->createManager([$authenticator], 'main', $eraseCredentials, exposeSecurityErrors: ExposeSecurityLevel::None);
209218
$manager->authenticateRequest($this->request);
219+
220+
$this->assertSame($eraseCredentials, $token->erased);
210221
}
211222

212223
public static function provideEraseCredentialsData()

Tests/EventListener/PasswordMigratingListenerTest.php

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -150,8 +150,6 @@ public function loadUserByUsername(string $username): UserInterface
150150
abstract class TestPasswordAuthenticatedUser implements UserInterface, PasswordAuthenticatedUserInterface
151151
{
152152
abstract public function getPassword(): ?string;
153-
154-
abstract public function getSalt(): ?string;
155153
}
156154

157155
class DummyTestPasswordAuthenticatedUser extends TestPasswordAuthenticatedUser
@@ -161,16 +159,12 @@ public function getPassword(): ?string
161159
return null;
162160
}
163161

164-
public function getSalt(): ?string
165-
{
166-
return null;
167-
}
168-
169162
public function getRoles(): array
170163
{
171164
return [];
172165
}
173166

167+
#[\Deprecated]
174168
public function eraseCredentials(): void
175169
{
176170
}

Tests/Firewall/ContextListenerTest.php

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -563,21 +563,11 @@ public function __serialize(): array
563563
return [$this->user, $this->roles];
564564
}
565565

566-
public function serialize(): string
567-
{
568-
return serialize($this->__serialize());
569-
}
570-
571566
public function __unserialize(array $data): void
572567
{
573568
[$this->user, $this->roles] = $data;
574569
}
575570

576-
public function unserialize($serialized)
577-
{
578-
$this->__unserialize(\is_array($serialized) ? $serialized : unserialize($serialized));
579-
}
580-
581571
public function __toString(): string
582572
{
583573
return $this->user->getUserIdentifier();
@@ -603,6 +593,7 @@ public function getUserIdentifier(): string
603593
return $this->getUserIdentifier();
604594
}
605595

596+
#[\Deprecated]
606597
public function eraseCredentials(): void
607598
{
608599
}

Tests/LoginLink/LoginLinkHandlerTest.php

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -284,16 +284,12 @@ public function getPassword(): string
284284
return $this->passwordProperty;
285285
}
286286

287-
public function getSalt(): string
288-
{
289-
return '';
290-
}
291-
292287
public function getUserIdentifier(): string
293288
{
294289
return $this->username;
295290
}
296291

292+
#[\Deprecated]
297293
public function eraseCredentials(): void
298294
{
299295
}

0 commit comments

Comments
 (0)