8000 Add a normalization step for the user-identifier in firewalls · symfony/symfony@f81db2a · GitHub
[go: up one dir, main page]

Skip to content
8000

Commit f81db2a

Browse files
committed
Add a normalization step for the user-identifier in firewalls
1 parent ae5843f commit f81db2a

File tree

3 files changed

+51
-3
lines changed

3 files changed

+51
-3
lines changed

src/Symfony/Component/Security/Http/Authenticator/Passport/Badge/UserBadge.php

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ class UserBadge implements BadgeInterface
3333
/** @var callable|null */
3434
private $userLoader;
3535
private UserInterface $user;
36+
private ?\Closure $identifierNormalizer = null;
3637

3738
/**
3839
* Initializes the user badge.
@@ -51,6 +52,7 @@ public function __construct(
5152
private string $userIdentifier,
5253
?callable $userLoader = null,
5354
private ?array $attributes = null,
55+
?\Closure $identifierNormalizer = null,
5456
) {
5557
if ('' === $userIdentifier) {
5658
trigger_deprecation('symfony/security-http', '7.2', 'Using an empty string as user identifier is deprecated and will throw an exception in Symfony 8.0.');
@@ -60,12 +62,20 @@ public function __construct(
6062
if (\strlen($userIdentifier) > self::MAX_USERNAME_LENGTH) {
6163
throw new BadCredentialsException('Username too long.');
6264
}
65+
if ($identifierNormalizer) {
66+
$this->identifierNormalizer = static fn () => $identifierNormalizer($userIdentifier);
67+
}
6368

6469
$this->userLoader = $userLoader;
6570
}
6671

6772
public function getUserIdentifier(): string
6873
{
74+
if (isset($this->identifierNormalizer)) {
75+
$this->userIdentifier = ($this->identifierNormalizer)();
76+
$this->identifierNormalizer = null;
77+
}
78+
6979
return $this->userIdentifier;
7080
}
7181

@@ -88,15 +98,15 @@ public function getUser(): UserInterface
8898
}
8999

90100
if (null === $this->getAttributes()) {
91-
$user = ($this->userLoader)($this->userIdentifier);
101+
$user = ($this->userLoader)($this->getUserIdentifier());
92102
} else {
93-
$user = ($this->userLoader)($this->userIdentifier, $this->getAttributes());
103+
$user = ($this->userLoader)($this->getUserIdentifier(), $this->getAttributes());
94104
}
95105

96106
// No user has been found via the $this->userLoader callback
97107
if (null === $user) {
98108
$exception = new UserNotFoundException();
99-
$exception->setUserIdentifier($this->userIdentifier);
109+
$exception->setUserIdentifier($this->getUserIdentifier());
100110

101111
throw $exception;
102112
}

src/Symfony/Component/Security/Http/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
CHANGELOG
22
=========
33

4+
7.3
5+
---
6+
7+
* Add argument `$identifierNormalizer` to `UserBadge::__construct()` to allow normalizing the identifier
8+
49
7.2
510
---
611

src/Symfony/Component/Security/Http/Tests/Authenticator/Passport/Badge/UserBadgeTest.php

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@
1515
use Symfony\Bridge\PhpUnit\ExpectUserDeprecationMessageTrait;
1616
use Symfony\Component\Security\Core\Exception\UserNotFoundException;
1717
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
18+
use Symfony\Component\String\Slugger\AsciiSlugger;
19+
use Symfony\Component\String\UnicodeString;
20+
21+
use function Symfony\Component\String\u;
1822

1923
class UserBadgeTest extends TestCase
2024
{
@@ -36,4 +40,33 @@ public function testEmptyUserIdentifier()
3640
// $this->expectException(BadCredentialsException::class)
3741
new UserBadge('', fn () => null);
3842
}
43+
44+
/**
45+
* @dataProvider provideUserIdentifierNormalizationData
46+
*/
47+
public function testUserIdentifierNormalization(string $identifier, string $expectedNormalizedIdentifier, callable $normalizer)
48+
{
49+
$badge = new UserBadge($identifier, fn () => null, identifierNormalizer: $normalizer);
50+
51+
static::assertSame($expectedNormalizedIdentifier, $badge->getUserIdentifier());
52+
}
53+
54+
public static function provideUserIdentifierNormalizationData(): iterable
55+
{
56+
$lowerAndNFKC = static fn (string $identifier) => u($identifier)->normalize(UnicodeString::NFKC)->lower()->toString();
57+
yield 'Simple lower conversion' => ['SmiTh', 'smith', $lowerAndNFKC];
58+
yield 'Normalize fi to fi. Other unicode characters are preserved (р, с, ѕ and а)' => ['рrinсeѕѕ.fionа', 'рrinсeѕѕ.fionа', $lowerAndNFKC];
59+
yield 'Greek characters' => ['ΝιΚόΛΑος', 'νικόλαος', $lowerAndNFKC];
60+
61+
$slugger = new AsciiSlugger('en');
62+
$asciiWithPrefix = static fn (string $identifier) => u($slugger->slug($identifier))->ascii()->lower()->prepend('USERID--')->toString();
63+
yield 'Username with prefix' => ['John Doe 1', 'USERID--john-doe-1', $asciiWithPrefix];
64+
65+
if (!\extension_loaded('intl')) {
66+
return;
67+
}
68+
$upperAndAscii = fn (string $identifier) => u($identifier)->ascii()->upper()->toString();
69+
yield 'Greek to ASCII' => ['ΝιΚόΛΑος', 'NIKOLAOS', $upperAndAscii];
70+
yield 'Katakana to ASCII' => ['たなかそういち', 'TANAKASOUICHI', $upperAndAscii];
71+
}
3972
}

0 commit comments

Comments
 (0)
0