8000 [SecurityBundle][PasswordHasher] Fix password migration with custom h… · symfony/symfony@eb342c4 · GitHub
[go: up one dir, main page]

Skip to content

Commit eb342c4

Browse files
committed
[SecurityBundle][PasswordHasher] Fix password migration with custom hasher service with security bundle config
1 parent 7c4f174 commit eb342c4

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+ 67F4
}
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 ($hasher = $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)
0