Skip to content

Commit 1de61c7

Browse files
bug #51686 [SecurityBundle][PasswordHasher] Fix password migration with custom hasher service with security bundle config (ogizanagi)
This PR was merged into the 5.4 branch. Discussion ---------- [SecurityBundle][PasswordHasher] Fix password migration with custom hasher service with security bundle config | Q | A | ------------- | --- | Branch? | 5.4 | Bug fix? | yes | New feature? | no | Deprecations? | no | Tickets | Fix #51670 (alternative) | License | MIT | Doc PR | N/A Supersedes #51670 as a bug fix, so the security config properly uses a for the `migrate_from` when a `MigratingPasswordHasher` when a custom hasher service id is used: ```yaml security: password_hashers: legacy: md5 App\User\Identity: id: App\Security\CustomHasher migrate_from: - legacy ``` --- > **Note** > 6.3 patch : 6.3...ogizanagi:63-security-custom-hasher-migrate Commits ------- 4d65f30 [SecurityBundle][PasswordHasher] Fix password migration with custom hasher service with security bundle config
2 parents ac2e41f + 4d65f30 commit 1de61c7

File tree

4 files changed

+96
-19
lines changed

4 files changed

+96
-19
lines changed

src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -848,7 +848,10 @@ private function createHasher(array $config)
848848
{
849849
// a custom hasher service
850850
if (isset($config['id'])) {
851-
return new Reference($config['id']);
851+
return $config['migrate_from'] ?? false ? [
852+
'instance' => new Reference($config['id']),
853+
'migrate_from' => $config['migrate_from'],
854+
] : new Reference($config['id']);
852855
}
853856

854857
if ($config['migrate_from'] ?? false) {

src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/SecurityExtensionTest.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -881,6 +881,33 @@ public function testLegacyAuthorizationManagerSignature()
881881
$this->assertEquals('%security.access.always_authenticate_before_granting%', (string) $args[3]);
882882
}
883883

884+
public function testCustomHasherWithMigrateFrom()
885+
{
886+
$container = $this->getRawContainer();
887+
888+
$container->loadFromExtension('security', [
889+
'enable_authenticator_manager' => true,
890+
'password_hashers' => [
891+
'legacy' => 'md5',
892+
'App\User' => [
893+
'id' => 'App\Security\CustomHasher',
894+
'migrate_from' => 'legacy',
895+
],
896+
],
897+
'firewalls' => ['main' => ['http_basic' => true]],
898+
]);
899+
900+
$container->compile();
901+
902+
$hashersMap = $container->getDefinition('security.password_hasher_factory')->getArgument(0);
903+
904+
$this->assertArrayHasKey('App\User', $hashersMap);
905+
$this->assertEquals($hashersMap['App\User'], [
906+
'instance' => new Reference('App\Security\CustomHasher'),
907+
'migrate_from' => ['legacy'],
908+
]);
909+
}
910+
884911
protected function getRawContainer()
885912
{
886913
$container = new ContainerBuilder();

src/Symfony/Component/PasswordHasher/Hasher/PasswordHasherFactory.php

Lines changed: 32 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,14 @@ public function getPasswordHasher($user): PasswordHasherInterface
7171
*/
7272
private function createHasher(array $config, bool $isExtra = false): PasswordHasherInterface
7373
{
74+
if (isset($config['instance'])) {
75+
if (!isset($config['migrate_from'])) {
76+
return $config['instance'];
77+
}
78+
79+
$config = $this->getMigratingPasswordConfig($config);
80+
}
81+
7482
if (isset($config['algorithm'])) {
7583
$rawConfig = $config;
7684
$config = $this->getHasherConfigFromAlgorithm($config);
@@ -142,24 +150,8 @@ private function getHasherConfigFromAlgorithm(array $config): array
142150
];
143151
}
144152

145-
if ($frompasswordHashers = ($config['migrate_from'] ?? false)) {
146-
unset($config['migrate_from']);
147-
$hasherChain = [$this->createHasher($config, true)];
148-
149-
foreach ($frompasswordHashers as $name) {
150-
if (isset($this->passwordHashers[$name])) {
151-
$hasher = $this->createHasherUsingAdapter($name);
152-
} else {
153-
$hasher = $this->createHasher(['algorithm' => $name], true);
154-
}
155-
156-
$hasherChain[] = $hasher;
157-
}
158-
159-
return [
160-
'class' => MigratingPasswordHasher::class,
161-
'arguments' => $hasherChain,
162-
];
153+
if ($config['migrate_from'] ?? false) {
154+
return $this->getMigratingPasswordConfig($config);
163155
}
164156

165157
switch ($config['algorithm']) {
@@ -239,4 +231,26 @@ private function getHasherConfigFromAlgorithm(array $config): array
239231
],
240232
];
241233
}
234+
235+
private function getMigratingPasswordConfig(array $config): array
236+
{
237+
$frompasswordHashers = $config['migrate_from'];
238+
unset($config['migrate_from']);
239+
$hasherChain = [$this->createHasher($config, true)];
240+
241+
foreach ($frompasswordHashers as $name) {
242+
if ($this->passwordHashers[$name] ?? false) {
243+
$hasher = $this->createHasherUsingAdapter($name);
244+
} else {
245+
$hasher = $this->createHasher(['algorithm' => $name], true);
246+
}
247+
248+
$hasherChain[] = $hasher;
249+
}
250+
251+
return [
252+
'class' => MigratingPasswordHasher::class,
253+
'arguments' => $hasherChain,
254+
];
255+
}
242256
}

src/Symfony/Component/PasswordHasher/Tests/Hasher/PasswordHasherFactoryTest.php

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

52+
public function testGetHasherWithInstance()
53+
{
54+
$factory = new PasswordHasherFactory([
55+
PasswordAuthenticatedUserInterface::class => ['instance' => new MessageDigestPasswordHasher('sha1')],
56+
]);
57+
58+
$hasher = $factory->getPasswordHasher($this->createMock(PasswordAuthenticatedUserInterface::class));
59+
$expectedHasher = new MessageDigestPasswordHasher('sha1');
60+
$this->assertEquals($expectedHasher->hash('foo', ''), $hasher->hash('foo', ''));
61+
}
62+
5263
public function testGetHasherWithClassName()
5364
{
5465
$factory = new PasswordHasherFactory([
@@ -163,6 +174,28 @@ public function testMigrateFrom()
163174
$this->assertStringStartsWith(\SODIUM_CRYPTO_PWHASH_STRPREFIX, $hasher->hash('foo', null));
164175
}
165176

177+
public function testMigrateFromWithCustomInstance()
178+
{
179+
if (!SodiumPasswordHasher::isSupported()) {
180+
$this->markTestSkipped('Sodium is not available');
181+
}
182+
183+
$sodium = new SodiumPasswordHasher();
184+
185+
$factory = new PasswordHasherFactory([
186+
'digest_hasher' => $digest = new MessageDigestPasswordHasher('sha256'),
187+
SomeUser::class => ['instance' => $sodium, 'migrate_from' => ['bcrypt', 'digest_hasher']],
188+
]);
189+
190+
$hasher = $factory->getPasswordHasher(SomeUser::class);
191+
$this->assertInstanceOf(MigratingPasswordHasher::class, $hasher);
192+
193+
$this->assertTrue($hasher->verify((new SodiumPasswordHasher())->hash('foo', null), 'foo', null));
194+
$this->assertTrue($hasher->verify((new NativePasswordHasher(null, null, null, \PASSWORD_BCRYPT))->hash('foo', null), 'foo', null));
195+
$this->assertTrue($hasher->verify($digest->hash('foo', null), 'foo', null));
196+
$this->assertStringStartsWith(\SODIUM_CRYPTO_PWHASH_STRPREFIX, $hasher->hash('foo', null));
197+
}
198+
166199
/**
167200
* @group legacy
168201
*/

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy