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+
}
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)
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