Skip to content

Commit 4a67637

Browse files
Merge branch '6.3' into 6.4
* 6.3: [Cache] Fix Redis6Proxy [Finder] Disable failing test about open_basedir Fix merge Fix merge [Notifier] Telegram Bridge add escaping for \ [Routing] Fix routing collection defaults when adding a new route to a collection [Messenger] Fix cloned TraceableStack not unstacking the stack independently [DependencyInjection] Fix autocasting null env values to empty string with container.env_var_processors_locator [Cache] Fix support for Redis Sentinel using php-redis 6.0.0 [Notifier] Fix Smsmode HttpClient mandatory headers minor #51693 Disable the dead code analysis in Psalm (stof) Update the PR template [SecurityBundle][PasswordHasher] Fix password migration with custom hasher service with security bundle config [Translator] Fix support for `default_path` in XML [FrameworkBundle] Handle tags array attributes in descriptors [HttpClient] Fix TraceableResponse if response has no destruct method [FrameworkBundle] Always use buildDir as `ConfigBuilderGenerator` outputDir
2 parents 391acd6 + 278d3a4 commit 4a67637

File tree

2 files changed

+65
-18
lines changed

2 files changed

+65
-18
lines changed

Hasher/PasswordHasherFactory.php

Lines changed: 32 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,14 @@ public function getPasswordHasher(string|PasswordAuthenticatedUserInterface|Pass
7070
*/
7171
private function createHasher(array $config, bool $isExtra = false): PasswordHasherInterface
7272
{
73+
if (isset($config['instance'])) {
74+
if (!isset($config['migrate_from'])) {
75+
return $config['instance'];
76+
}
77+
78+
$config = $this->getMigratingPasswordConfig($config);
79+
}
80+
7381
if (isset($config['algorithm'])) {
7482
$rawConfig = $config;
7583
$config = $this->getHasherConfigFromAlgorithm($config);
@@ -126,24 +134,8 @@ private function getHasherConfigFromAlgorithm(array $config): array
126134
];
127135
}
128136

129-
if ($frompasswordHashers = ($config['migrate_from'] ?? false)) {
130-
unset($config['migrate_from']);
131-
$hasherChain = [$this->createHasher($config, true)];
132-
133-
foreach ($frompasswordHashers as $name) {
134-
if ($hasher = $this->passwordHashers[$name] ?? false) {
135-
$hasher = $hasher instanceof PasswordHasherInterface ? $hasher : $this->createHasher($hasher, true);
136-
} else {
137-
$hasher = $this->createHasher(['algorithm' => $name], true);
138-
}
139-
140-
$hasherChain[] = $hasher;
141-
}
142-
143-
return [
144-
'class' => MigratingPasswordHasher::class,
145-
'arguments' => $hasherChain,
146-
];
137+
if ($config['migrate_from'] ?? false) {
138+
return $this->getMigratingPasswordConfig($config);
147139
}
148140

149141
switch ($config['algorithm']) {
@@ -223,4 +215,26 @@ private function getHasherConfigFromAlgorithm(array $config): array
223215
],
224216
];
225217
}
218+
219+
private function getMigratingPasswordConfig(array $config): array
220+
{
221+
$frompasswordHashers = $config['migrate_from'];
222+
unset($config['migrate_from']);
223+
$hasherChain = [$this->createHasher($config, true)];
224+
225+
foreach ($frompasswordHashers as $name) {
226+
if ($hasher = $this->passwordHashers[$name] ?? false) {
227+
$hasher = $hasher instanceof PasswordHasherInterface ? $hasher : $this->createHasher($hasher, true);
228+
} else {
229+
$hasher = $this->createHasher(['algorithm' => $name], true);
230+
}
231+
232+
$hasherChain[] = $hasher;
233+
}
234+
235+
return [
236+
'class' => MigratingPasswordHasher::class,
237+
'arguments' => $hasherChain,
238+
];
239+
}
226240
}

Tests/Hasher/PasswordHasherFactoryTest.php

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,17 @@ public function testGetHasherWithService()
4848
$this->assertEquals($expectedHasher->hash('foo', ''), $hasher->hash('foo', ''));
4949
}
5050

51+
public function testGetHasherWithInstance()
52+
{
53+
$factory = new PasswordHasherFactory([
54+
PasswordAuthenticatedUserInterface::class => ['instance' => new MessageDigestPasswordHasher('sha1')],
55+
]);
56+
57+
$hasher = $factory->getPasswordHasher($this->createMock(PasswordAuthenticatedUserInterface::class));
58+
$expectedHasher = new MessageDigestPasswordHasher('sha1');
59+
$this->assertEquals($expectedHasher->hash('foo', ''), $hasher->hash('foo', ''));
60+
}
61+
5162
public function testGetHasherWithClassName()
5263
{
5364
$factory = new PasswordHasherFactory([
@@ -183,6 +194,28 @@ public function testDefaultMigratingHashers()
183194
(new PasswordHasherFactory([SomeUser::class => ['class' => SodiumPasswordHasher::class, 'arguments' => []]]))->getPasswordHasher(SomeUser::class)
184195
);
185196
}
197+
198+
public function testMigrateFromWithCustomInstance()
199+
{
200+
if (!SodiumPasswordHasher::isSupported()) {
201+
$this->markTestSkipped('Sodium is not available');
202+
}
203+
204+
$sodium = new SodiumPasswordHasher();
205+
206+
$factory = new PasswordHasherFactory([
207+
'digest_hasher' => $digest = new MessageDigestPasswordHasher('sha256'),
208+
SomeUser::class => ['instance' => $sodium, 'migrate_from' => ['bcrypt', 'digest_hasher']],
209+
]);
210+
211+
$hasher = $factory->getPasswordHasher(SomeUser::class);
212+
$this->assertInstanceOf(MigratingPasswordHasher::class, $hasher);
213+
214+
$this->assertTrue($hasher->verify((new SodiumPasswordHasher())->hash('foo', null), 'foo', null));
215+
$this->assertTrue($hasher->verify((new NativePasswordHasher(null, null, null, \PASSWORD_BCRYPT))->hash('foo', null), 'foo', null));
216+
$this->assertTrue($hasher->verify($digest->hash('foo', null), 'foo', null));
217+
$this->assertStringStartsWith(\SODIUM_CRYPTO_PWHASH_STRPREFIX, $hasher->hash('foo', null));
218+
}
186219
}
187220

188221
class SomeUser implements PasswordAuthenticatedUserInterface

0 commit comments

Comments
 (0)