Skip to content

Commit

Permalink
[SecurityBundle][PasswordHasher] Fix password migration with custom h…
Browse files Browse the repository at this point in the history
…asher service with security bundle config
  • Loading branch information
ogizanagi committed Sep 19, 2023
1 parent d217c4d commit 57cd036
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 18 deletions.
50 changes: 32 additions & 18 deletions Hasher/PasswordHasherFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,14 @@ public function getPasswordHasher($user): PasswordHasherInterface
*/
private function createHasher(array $config, bool $isExtra = false): PasswordHasherInterface
{
if (isset($config['instance'])) {
if (!isset($config['migrate_from'])) {
return $config['instance'];
}

$config = $this->getMigratingPasswordConfig($config);
}

if (isset($config['algorithm'])) {
$rawConfig = $config;
$config = $this->getHasherConfigFromAlgorithm($config);
Expand Down Expand Up @@ -142,24 +150,8 @@ private function getHasherConfigFromAlgorithm(array $config): array
];
}

if ($frompasswordHashers = ($config['migrate_from'] ?? false)) {
unset($config['migrate_from']);
$hasherChain = [$this->createHasher($config, true)];

foreach ($frompasswordHashers as $name) {
if (isset($this->passwordHashers[$name])) {
$hasher = $this->createHasherUsingAdapter($name);
} else {
$hasher = $this->createHasher(['algorithm' => $name], true);
}

$hasherChain[] = $hasher;
}

return [
'class' => MigratingPasswordHasher::class,
'arguments' => $hasherChain,
];
if ($config['migrate_from'] ?? false) {
return $this->getMigratingPasswordConfig($config);
}

switch ($config['algorithm']) {
Expand Down Expand Up @@ -239,4 +231,26 @@ private function getHasherConfigFromAlgorithm(array $config): array
],
];
}

private function getMigratingPasswordConfig(array $config): array
{
$frompasswordHashers = $config['migrate_from'];
unset($config['migrate_from']);
$hasherChain = [$this->createHasher($config, true)];

foreach ($frompasswordHashers as $name) {
if ($this->passwordHashers[$name] ?? false) {
$hasher = $this->createHasherUsingAdapter($name);
} else {
$hasher = $this->createHasher(['algorithm' => $name], true);
}

$hasherChain[] = $hasher;
}

return [
'class' => MigratingPasswordHasher::class,
'arguments' => $hasherChain,
];
}
}
33 changes: 33 additions & 0 deletions Tests/Hasher/PasswordHasherFactoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,17 @@ public function testGetHasherWithService()
$this->assertEquals($expectedHasher->hash('foo', ''), $hasher->hash('foo', ''));
}

public function testGetHasherWithInstance()
{
$factory = new PasswordHasherFactory([
PasswordAuthenticatedUserInterface::class => ['instance' => new MessageDigestPasswordHasher('sha1')],
]);

$hasher = $factory->getPasswordHasher($this->createMock(PasswordAuthenticatedUserInterface::class));
$expectedHasher = new MessageDigestPasswordHasher('sha1');
$this->assertEquals($expectedHasher->hash('foo', ''), $hasher->hash('foo', ''));
}

public function testGetHasherWithClassName()
{
$factory = new PasswordHasherFactory([
Expand Down Expand Up @@ -163,6 +174,28 @@ public function testMigrateFrom()
$this->assertStringStartsWith(\SODIUM_CRYPTO_PWHASH_STRPREFIX, $hasher->hash('foo', null));
}

public function testMigrateFromWithCustomInstance()
{
if (!SodiumPasswordHasher::isSupported()) {
$this->markTestSkipped('Sodium is not available');
}

$sodium = new SodiumPasswordHasher();

$factory = new PasswordHasherFactory([
'digest_hasher' => $digest = new MessageDigestPasswordHasher('sha256'),
SomeUser::class => ['instance' => $sodium, 'migrate_from' => ['bcrypt', 'digest_hasher']],
]);

$hasher = $factory->getPasswordHasher(SomeUser::class);
$this->assertInstanceOf(MigratingPasswordHasher::class, $hasher);

$this->assertTrue($hasher->verify((new SodiumPasswordHasher())->hash('foo', null), 'foo', null));
$this->assertTrue($hasher->verify((new NativePasswordHasher(null, null, null, \PASSWORD_BCRYPT))->hash('foo', null), 'foo', null));
$this->assertTrue($hasher->verify($digest->hash('foo', null), 'foo', null));
$this->assertStringStartsWith(\SODIUM_CRYPTO_PWHASH_STRPREFIX, $hasher->hash('foo', null));
}

/**
* @group legacy
*/
Expand Down

0 comments on commit 57cd036

Please sign in to comment.