Skip to content

[PasswordHasher] Add union types #41640

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 11, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
use Symfony\Component\Security\Core\Encoder\EncoderAwareInterface;
use Symfony\Component\Security\Core\Encoder\PasswordEncoderInterface;
use Symfony\Component\Security\Core\Encoder\PasswordHasherAdapter;
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;

/**
* A generic hasher factory implementation.
Expand All @@ -38,7 +39,7 @@ public function __construct(array $passwordHashers)
/**
* {@inheritdoc}
*/
public function getPasswordHasher($user): PasswordHasherInterface
public function getPasswordHasher(string|PasswordAuthenticatedUserInterface|PasswordHasherAwareInterface $user): PasswordHasherInterface
{
$hasherKey = null;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,7 @@ interface PasswordHasherFactoryInterface
/**
* Returns the password hasher to use for the given user.
*
* @param PasswordHasherAwareInterface|PasswordAuthenticatedUserInterface|string $user
*
* @throws \RuntimeException When no password hasher could be found for the user
*/
public function getPasswordHasher($user): PasswordHasherInterface;
public function getPasswordHasher(string|PasswordAuthenticatedUserInterface|PasswordHasherAwareInterface $user): PasswordHasherInterface;
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@

use Symfony\Component\Security\Core\User\LegacyPasswordAuthenticatedUserInterface;
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
use Symfony\Component\Security\Core\User\UserInterface;

/**
* Hashes passwords based on the user and the PasswordHasherFactory.
Expand All @@ -31,43 +30,23 @@ public function __construct(PasswordHasherFactoryInterface $hasherFactory)
$this->hasherFactory = $hasherFactory;
}

/**
* @param PasswordAuthenticatedUserInterface $user
*/
public function hashPassword($user, string $plainPassword): string
public function hashPassword(PasswordAuthenticatedUserInterface $user, string $plainPassword): string
{
if (!$user instanceof PasswordAuthenticatedUserInterface) {
if (!$user instanceof UserInterface) {
throw new \TypeError(sprintf('Expected an instance of "%s" as first argument, but got "%s".', UserInterface::class, get_debug_type($user)));
}
trigger_deprecation('symfony/password-hasher', '5.3', 'The "%s()" method expects a "%s" instance as first argument. Not implementing it in class "%s" is deprecated.', __METHOD__, PasswordAuthenticatedUserInterface::class, get_debug_type($user));
}

$salt = $user->getSalt();
if ($salt && !$user instanceof LegacyPasswordAuthenticatedUserInterface) {
trigger_deprecation('symfony/password-hasher', '5.3', 'Returning a string from "getSalt()" without implementing the "%s" interface is deprecated, the "%s" class should implement it.', LegacyPasswordAuthenticatedUserInterface::class, get_debug_type($user));
$salt = null;
if ($user instanceof LegacyPasswordAuthenticatedUserInterface) {
$salt = $user->getSalt();
}

$hasher = $this->hasherFactory->getPasswordHasher($user);

return $hasher->hash($plainPassword, $salt);
}

/**
* @param PasswordAuthenticatedUserInterface $user
*/
public function isPasswordValid($user, string $plainPassword): bool
public function isPasswordValid(PasswordAuthenticatedUserInterface $user, string $plainPassword): bool
{
if (!$user instanceof PasswordAuthenticatedUserInterface) {
if (!$user instanceof UserInterface) {
throw new \TypeError(sprintf('Expected an instance of "%s" as first argument, but got "%s".', UserInterface::class, get_debug_type($user)));
}
trigger_deprecation('symfony/password-hasher', '5.3', 'The "%s()" method expects a "%s" instance as first argument. Not implementing it in class "%s" is deprecated.', __METHOD__, PasswordAuthenticatedUserInterface::class, get_debug_type($user));
}

$salt = $user->getSalt();
if ($salt && !$user instanceof LegacyPasswordAuthenticatedUserInterface) {
trigger_deprecation('symfony/password-hasher', '5.3', 'Returning a string from "getSalt()" without implementing the "%s" interface is deprecated, the "%s" class should implement it.', LegacyPasswordAuthenticatedUserInterface::class, get_debug_type($user));
$salt = null;
if ($user instanceof LegacyPasswordAuthenticatedUserInterface) {
$salt = $user->getSalt();
}

if (null === $user->getPassword()) {
Expand All @@ -79,22 +58,12 @@ public function isPasswordValid($user, string $plainPassword): bool
return $hasher->verify($user->getPassword(), $plainPassword, $salt);
}

/**
* @param PasswordAuthenticatedUserInterface $user
*/
public function needsRehash($user): bool
public function needsRehash(PasswordAuthenticatedUserInterface $user): bool
{
if (null === $user->getPassword()) {
return false;
}

if (!$user instanceof PasswordAuthenticatedUserInterface) {
if (!$user instanceof UserInterface) {
throw new \TypeError(sprintf('Expected an instance of "%s" as first argument, but got "%s".', UserInterface::class, get_debug_type($user)));
}
trigger_deprecation('symfony/password-hasher', '5.3', 'The "%s()" method expects a "%s" instance as first argument. Not implementing it in class "%s" is deprecated.', __METHOD__, PasswordAuthenticatedUserInterface::class, get_debug_type($user));
}

$hasher = $this->hasherFactory->getPasswordHasher($user);

return $hasher->needsRehash($user->getPassword());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,21 @@
* Interface for the user password hasher service.
*
* @author Ariel Ferrandini <arielferrandini@gmail.com>
*
* @method string hashPassword(PasswordAuthenticatedUserInterface $user, string $plainPassword) Hashes the plain password for the given user.
* @method bool isPasswordValid(PasswordAuthenticatedUserInterface $user, string $plainPassword) Checks if the plaintext password matches the user's password.
* @method bool needsRehash(PasswordAuthenticatedUserInterface $user) Checks if an encoded password would benefit from rehashing.
*/
interface UserPasswordHasherInterface
{
/**
* Hashes the plain password for the given user.
*/
public function hashPassword(PasswordAuthenticatedUserInterface $user, string $plainPassword): string;

/**
* Checks if the plaintext password matches the user's password.
*/
public function isPasswordValid(PasswordAuthenticatedUserInterface $user, string $plainPassword): bool;

/**
* Checks if an encoded password would benefit from rehashing.
*/
public function needsRehash(PasswordAuthenticatedUserInterface $user): bool;
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,36 +26,6 @@ class UserPasswordHasherTest extends TestCase
{
use ExpectDeprecationTrait;

/**
* @group legacy
*/
public function testHashWithNonPasswordAuthenticatedUser()
{
$this->expectDeprecation('Since symfony/password-hasher 5.3: Returning a string from "getSalt()" without implementing the "Symfony\Component\Security\Core\User\LegacyPasswordAuthenticatedUserInterface" interface is deprecated, the "%s" class should implement it.');

$userMock = $this->createMock('Symfony\Component\Security\Core\User\UserInterface');
$userMock->expects($this->any())
->method('getSalt')
->willReturn('userSalt');

$mockHasher = $this->createMock(PasswordHasherInterface::class);
$mockHasher->expects($this->any())
->method('hash')
->with($this->equalTo('plainPassword'), $this->equalTo('userSalt'))
->willReturn('hash');

$mockPasswordHasherFactory = $this->createMock(PasswordHasherFactoryInterface::class);
$mockPasswordHasherFactory->expects($this->any())
->method('getPasswordHasher')
->with($this->equalTo($userMock))
->willReturn($mockHasher);

$passwordHasher = new UserPasswordHasher($mockPasswordHasherFactory);

$encoded = $passwordHasher->hashPassword($userMock, 'plainPassword');
$this->assertEquals('hash', $encoded);
}

public function testHash()
{
$userMock = $this->createMock(TestPasswordAuthenticatedUser::class);
Expand Down