From 8628479767ae5cd547e8e86ec517695ab60e4833 Mon Sep 17 00:00:00 2001 From: Valentine Boineau Date: Wed, 9 Jun 2021 15:38:57 +0200 Subject: [PATCH] [PasswordHasher] Add union types --- .../Hasher/PasswordHasherFactory.php | 3 +- .../Hasher/PasswordHasherFactoryInterface.php | 4 +- .../Hasher/UserPasswordHasher.php | 49 ++++--------------- .../Hasher/UserPasswordHasherInterface.php | 18 +++++-- .../Tests/Hasher/UserPasswordHasherTest.php | 30 ------------ 5 files changed, 26 insertions(+), 78 deletions(-) diff --git a/src/Symfony/Component/PasswordHasher/Hasher/PasswordHasherFactory.php b/src/Symfony/Component/PasswordHasher/Hasher/PasswordHasherFactory.php index a2f24224744aa..a205617c63756 100644 --- a/src/Symfony/Component/PasswordHasher/Hasher/PasswordHasherFactory.php +++ b/src/Symfony/Component/PasswordHasher/Hasher/PasswordHasherFactory.php @@ -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. @@ -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; diff --git a/src/Symfony/Component/PasswordHasher/Hasher/PasswordHasherFactoryInterface.php b/src/Symfony/Component/PasswordHasher/Hasher/PasswordHasherFactoryInterface.php index fed2a77cf0ce1..6dc158f8e1522 100644 --- a/src/Symfony/Component/PasswordHasher/Hasher/PasswordHasherFactoryInterface.php +++ b/src/Symfony/Component/PasswordHasher/Hasher/PasswordHasherFactoryInterface.php @@ -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; } diff --git a/src/Symfony/Component/PasswordHasher/Hasher/UserPasswordHasher.php b/src/Symfony/Component/PasswordHasher/Hasher/UserPasswordHasher.php index f26164d7e51af..4883d91db57f5 100644 --- a/src/Symfony/Component/PasswordHasher/Hasher/UserPasswordHasher.php +++ b/src/Symfony/Component/PasswordHasher/Hasher/UserPasswordHasher.php @@ -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. @@ -31,21 +30,11 @@ 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); @@ -53,21 +42,11 @@ public function hashPassword($user, string $plainPassword): string 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()) { @@ -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()); diff --git a/src/Symfony/Component/PasswordHasher/Hasher/UserPasswordHasherInterface.php b/src/Symfony/Component/PasswordHasher/Hasher/UserPasswordHasherInterface.php index cf29220740542..9a40054609b49 100644 --- a/src/Symfony/Component/PasswordHasher/Hasher/UserPasswordHasherInterface.php +++ b/src/Symfony/Component/PasswordHasher/Hasher/UserPasswordHasherInterface.php @@ -17,11 +17,21 @@ * Interface for the user password hasher service. * * @author Ariel Ferrandini - * - * @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; } diff --git a/src/Symfony/Component/PasswordHasher/Tests/Hasher/UserPasswordHasherTest.php b/src/Symfony/Component/PasswordHasher/Tests/Hasher/UserPasswordHasherTest.php index fb9188083eab6..289ffaf37e0cc 100644 --- a/src/Symfony/Component/PasswordHasher/Tests/Hasher/UserPasswordHasherTest.php +++ b/src/Symfony/Component/PasswordHasher/Tests/Hasher/UserPasswordHasherTest.php @@ -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);