From 5133e3114b6e354c1e53bf59b0bf061f1bf6a4ca Mon Sep 17 00:00:00 2001 From: Olha Livitchuk <77281282+olhalivitchuk@users.noreply.github.com> Date: Thu, 21 Dec 2023 17:51:01 +0100 Subject: [PATCH] FRW-5940 Symfony Security 6 Support (#10579) FRW-5940 Symfony Security 6 Support --- phpstan.neon | 5 +- .../Customer/Business/Customer/Customer.php | 33 ++++++++++- .../_support/CustomerBusinessTester.php | 55 ++++++++++++++++--- 3 files changed, 82 insertions(+), 11 deletions(-) diff --git a/phpstan.neon b/phpstan.neon index 81461f19..fcf90c5e 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -4,7 +4,8 @@ parameters: reportUnmatchedIgnoredErrors: false ignoreErrors: - '#Instantiated class .+NativePasswordEncoder not found.#' - - '#Instantiated class .+BCryptPasswordEncoder not found.#' - '#Method .+Customer::getPasswordEncoder\(\) should return .+PasswordEncoderInterface but returns .+NativePasswordEncoder.#' - - '#Method .+Customer::getPasswordEncoder\(\) should return .+PasswordEncoderInterface but returns .+BCryptPasswordEncoder.#' - '#Cannot call method format\(\) on DateTime\|string.#' + - { message: '#Call to method encodePassword\(\) on an unknown class Symfony\\Component\\Security\\Core\\Encoder\\PasswordEncoderInterface.#', path: '%rootDir%/../../../vendor/spryker/spryker/Bundles/Customer/src/Spryker/Zed/Customer/Business/Customer/Customer.php' } + - { message: '#Call to method isPasswordValid\(\) on an unknown class Symfony\\Component\\Security\\Core\\Encoder\\PasswordEncoderInterface.#', path: '%rootDir%/../../../vendor/spryker/spryker/Bundles/Customer/src/Spryker/Zed/Customer/Business/Customer/Customer.php' } + - { message: '#Method .+Customer::getPasswordEncoder\(\) has invalid return type Symfony\\Component\\Security\\Core\\Encoder\\PasswordEncoderInterface.#', path: '%rootDir%/../../../vendor/spryker/spryker/Bundles/Customer/src/Spryker/Zed/Customer/Business/Customer/Customer.php' } diff --git a/src/Spryker/Zed/Customer/Business/Customer/Customer.php b/src/Spryker/Zed/Customer/Business/Customer/Customer.php index 530c201c..b21c723e 100644 --- a/src/Spryker/Zed/Customer/Business/Customer/Customer.php +++ b/src/Spryker/Zed/Customer/Business/Customer/Customer.php @@ -36,6 +36,9 @@ use Spryker\Zed\Customer\Dependency\Facade\CustomerToMailInterface; use Spryker\Zed\Customer\Persistence\CustomerQueryContainerInterface; use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\PasswordHasher\Hasher\NativePasswordHasher; +use Symfony\Component\PasswordHasher\PasswordHasherInterface; +use Symfony\Component\Security\Core\Authentication\AuthenticationProviderManager; use Symfony\Component\Security\Core\Encoder\NativePasswordEncoder; use Symfony\Component\Security\Core\Encoder\PasswordEncoderInterface; @@ -893,7 +896,11 @@ protected function getEncodedPassword($currentPassword) return $currentPassword; } - return $this->getPasswordEncoder()->encodePassword($currentPassword, static::BCRYPT_SALT); + if ($this->isSymfonyVersion5() === true) { + return $this->getPasswordEncoder()->encodePassword($currentPassword, static::BCRYPT_SALT); + } + + return $this->createPasswordHasher()->hash($currentPassword); } /** @@ -904,6 +911,14 @@ protected function getPasswordEncoder(): PasswordEncoderInterface return new NativePasswordEncoder(null, null, static::BCRYPT_FACTOR); } + /** + * @return \Symfony\Component\PasswordHasher\PasswordHasherInterface + */ + public function createPasswordHasher(): PasswordHasherInterface + { + return new NativePasswordHasher(null, null, static::BCRYPT_FACTOR); + } + /** * @param string $hash * @param string $rawPassword @@ -912,7 +927,11 @@ protected function getPasswordEncoder(): PasswordEncoderInterface */ protected function isValidPassword($hash, $rawPassword) { - return $this->getPasswordEncoder()->isPasswordValid($hash, $rawPassword, static::BCRYPT_SALT); + if ($this->isSymfonyVersion5() === true) { + return $this->getPasswordEncoder()->isPasswordValid($hash, $rawPassword, static::BCRYPT_SALT); + } + + return $this->createPasswordHasher()->verify($hash, $rawPassword); } /** @@ -1035,4 +1054,14 @@ public function sendPasswordRestoreMailForCustomerCollection( )); } } + + /** + * @deprecated Shim for Symfony Security Core 5.x, to be removed when Symfony Security Core dependency becomes 6.x+. + * + * @return bool + */ + protected function isSymfonyVersion5(): bool + { + return class_exists(AuthenticationProviderManager::class); + } } diff --git a/tests/SprykerTest/Zed/Customer/_support/CustomerBusinessTester.php b/tests/SprykerTest/Zed/Customer/_support/CustomerBusinessTester.php index fe1d3e1c..03dd8456 100644 --- a/tests/SprykerTest/Zed/Customer/_support/CustomerBusinessTester.php +++ b/tests/SprykerTest/Zed/Customer/_support/CustomerBusinessTester.php @@ -9,7 +9,9 @@ use Codeception\Actor; use Generated\Shared\Transfer\CustomerTransfer; -use Symfony\Component\Security\Core\Encoder\BCryptPasswordEncoder; +use Symfony\Component\PasswordHasher\Hasher\NativePasswordHasher; +use Symfony\Component\PasswordHasher\PasswordHasherInterface; +use Symfony\Component\Security\Core\Authentication\AuthenticationProviderManager; use Symfony\Component\Security\Core\Encoder\NativePasswordEncoder; use Symfony\Component\Security\Core\Encoder\PasswordEncoderInterface; @@ -43,6 +45,11 @@ class CustomerBusinessTester extends Actor */ public const TESTER_PASSWORD = '$2tester'; + /** + * @var int + */ + protected const BCRYPT_FACTOR = 12; + /** * @param string $hash * @param string $rawPassword @@ -52,9 +59,15 @@ class CustomerBusinessTester extends Actor */ public function assertPasswordsEqual(string $hash, string $rawPassword, string $salt = ''): void { - $passwordEncoder = $this->getPasswordEncoder(); + if ($this->isSymfonyVersion5() === true) { + $this->assertPasswordIsEncoded($hash, $rawPassword, $salt); - $this->assertTrue($passwordEncoder->isPasswordValid($hash, $rawPassword, $salt), 'Passwords are not equal.'); + return; + } + + $passwordHasher = $this->createPasswordHasher(); + + $this->assertTrue($passwordHasher->verify($hash, $rawPassword), 'Passwords are not equal.'); } /** @@ -81,15 +94,43 @@ public function createTestCustomer(): CustomerTransfer return $customerTransfer; } + /** + * @param string $hash + * @param string $rawPassword + * @param string $salt + * + * @return void + */ + protected function assertPasswordIsEncoded(string $hash, string $rawPassword, string $salt = ''): void + { + $passwordEncoder = $this->getPasswordEncoder(); + + $this->assertTrue($passwordEncoder->isPasswordValid($hash, $rawPassword, $salt), 'Passwords are not equal.'); + } + /** * @return \Symfony\Component\Security\Core\Encoder\PasswordEncoderInterface */ protected function getPasswordEncoder(): PasswordEncoderInterface { - if (class_exists(BCryptPasswordEncoder::class)) { - return new BCryptPasswordEncoder(12); - } + return new NativePasswordEncoder(null, null, static::BCRYPT_FACTOR); + } + + /** + * @return \Symfony\Component\PasswordHasher\PasswordHasherInterface + */ + protected function createPasswordHasher(): PasswordHasherInterface + { + return new NativePasswordHasher(null, null, static::BCRYPT_FACTOR); + } - return new NativePasswordEncoder(null, null, 12); + /** + * @deprecated Shim for Symfony Security Core 5.x, to be removed when Symfony Security Core dependency becomes 6.x+. + * + * @return bool + */ + protected function isSymfonyVersion5(): bool + { + return class_exists(AuthenticationProviderManager::class); } }