diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index e3aa86563c..ed42142697 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -3846,18 +3846,6 @@ parameters: count: 2 path: src/bundle/Core/URLChecker/Handler/AbstractURLHandler.php - - - message: '#^Cannot call method error\(\) on Psr\\Log\\LoggerInterface\|null\.$#' - identifier: method.nonObject - count: 1 - path: src/bundle/Core/URLChecker/Handler/AbstractURLHandler.php - - - - message: '#^Cannot call method info\(\) on Psr\\Log\\LoggerInterface\|null\.$#' - identifier: method.nonObject - count: 1 - path: src/bundle/Core/URLChecker/Handler/AbstractURLHandler.php - - message: '#^Method Ibexa\\Bundle\\Core\\URLChecker\\Handler\\AbstractURLHandler\:\:getOptions\(\) return type has no value type specified in iterable type array\.$#' identifier: missingType.iterableValue @@ -3912,12 +3900,6 @@ parameters: count: 1 path: src/bundle/Core/URLChecker/Handler/MailToHandler.php - - - message: '#^Cannot call method error\(\) on Psr\\Log\\LoggerInterface\|null\.$#' - identifier: method.nonObject - count: 1 - path: src/bundle/Core/URLChecker/URLChecker.php - - message: '#^Method Ibexa\\Bundle\\Core\\URLChecker\\URLChecker\:\:check\(\) has no return type specified\.$#' identifier: missingType.return @@ -10332,12 +10314,6 @@ parameters: count: 1 path: src/lib/Persistence/Cache/Handler.php - - - message: '#^Cannot call method debug\(\) on Psr\\Log\\LoggerInterface\|null\.$#' - identifier: method.nonObject - count: 1 - path: src/lib/Persistence/Cache/Identifier/CacheIdentifierGenerator.php - - message: '#^Method Ibexa\\Core\\Persistence\\Cache\\Identifier\\CacheIdentifierGenerator\:\:__construct\(\) has parameter \$keyPatterns with no value type specified in iterable type array\.$#' identifier: missingType.iterableValue @@ -16920,12 +16896,6 @@ parameters: count: 1 path: src/lib/Repository/ContentTypeService.php - - - message: '#^Cannot call method error\(\) on Psr\\Log\\LoggerInterface\|null\.$#' - identifier: method.nonObject - count: 1 - path: src/lib/Repository/Helper/RelationProcessor.php - - message: '#^Method Ibexa\\Core\\Repository\\Helper\\RelationProcessor\:\:appendFieldRelations\(\) has no return type specified\.$#' identifier: missingType.return @@ -17070,12 +17040,6 @@ parameters: count: 1 path: src/lib/Repository/Mapper/ContentDomainMapper.php - - - message: '#^Cannot call method error\(\) on Psr\\Log\\LoggerInterface\|null\.$#' - identifier: method.nonObject - count: 1 - path: src/lib/Repository/Mapper/ContentDomainMapper.php - - message: '#^Method Ibexa\\Core\\Repository\\Mapper\\ContentDomainMapper\:\:buildContentDomainObject\(\) has parameter \$prioritizedLanguages with no value type specified in iterable type array\.$#' identifier: missingType.iterableValue @@ -18264,12 +18228,6 @@ parameters: count: 1 path: src/lib/Repository/UserService.php - - - message: '#^Method Ibexa\\Core\\Repository\\UserService\:\:setLogger\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: src/lib/Repository/UserService.php - - message: '#^PHPDoc tag @param for parameter \$id with type mixed is not subtype of native type int\.$#' identifier: parameter.phpDocType diff --git a/phpstan.neon.dist b/phpstan.neon.dist index c448982be5..8f6322c64e 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -9,7 +9,7 @@ parameters: treatPhpDocTypesAsCertain: false ignoreErrors: - - message: "#^Cannot call method warning\\(\\) on Psr\\\\Log\\\\LoggerInterface\\|null\\.$#" + message: "#^Cannot call method (log|debug|info|notice|warning|error|critical|alert|emergency)\\(\\) on Psr\\\\Log\\\\LoggerInterface\\|null\\.$#" paths: - src - tests diff --git a/src/bundle/Core/DependencyInjection/Configuration/Parser/Repository/PasswordHash.php b/src/bundle/Core/DependencyInjection/Configuration/Parser/Repository/PasswordHash.php new file mode 100644 index 0000000000..a18ea2127a --- /dev/null +++ b/src/bundle/Core/DependencyInjection/Configuration/Parser/Repository/PasswordHash.php @@ -0,0 +1,65 @@ +arrayNode('password_hash') + ->info('Password hash options') + ->children() + ->integerNode('default_type') + ->info('Default password hash type, see the constants in Ibexa\Contracts\Core\Repository\Values\User\User.') + ->example('!php/const:Ibexa\Contracts\Core\Repository\Values\User\User::PASSWORD_HASH_PHP_DEFAULT') + ->defaultValue(User::PASSWORD_HASH_PHP_DEFAULT) + ->validate() + ->ifTrue(static function ($value): bool { + $hashType = (int) $value; + + if ($hashType === User::PASSWORD_HASH_ARGON2I) { + return !defined('PASSWORD_ARGON2I'); + } elseif ($hashType === User::PASSWORD_HASH_ARGON2ID) { + return !defined('PASSWORD_ARGON2ID'); + } + + return !in_array($hashType, User::SUPPORTED_PASSWORD_HASHES, true); + }) + ->thenInvalid('Invalid password hash type "%s". If you tried to use Argon2, make sure it\'s compiled in PHP.') + ->end() + ->end() + ->booleanNode('update_type_on_change') + ->info('Whether the password hash type should be changed when the password is changed if it differs from the default type.') + ->example('false') + ->defaultFalse() + ->end() + ->end() + ->end(); + } +} diff --git a/src/bundle/Core/IbexaCoreBundle.php b/src/bundle/Core/IbexaCoreBundle.php index 3f0e76f71a..78577d695f 100644 --- a/src/bundle/Core/IbexaCoreBundle.php +++ b/src/bundle/Core/IbexaCoreBundle.php @@ -96,6 +96,10 @@ public function build(ContainerBuilder $container): void $container->addCompilerPass(new TranslationCollectorPass()); $container->addCompilerPass(new SlugConverterConfigurationPass()); + /** @var \Ibexa\Bundle\Core\DependencyInjection\IbexaCoreExtension $kernel */ + $kernel = $container->getExtension('ibexa'); + $kernel->addRepositoryConfigParser(new RepositoryConfigParser\PasswordHash()); + $container->registerForAutoconfiguration(VariableProvider::class)->addTag('ezplatform.view.variable_provider'); } @@ -129,6 +133,7 @@ public function getContainerExtension(): ExtensionInterface new RepositoryConfigParser\Search(), new RepositoryConfigParser\FieldGroups(), new RepositoryConfigParser\Options(), + new RepositoryConfigParser\PasswordHash(), ] ); } diff --git a/src/contracts/Repository/Exceptions/PasswordInUnsupportedFormatException.php b/src/contracts/Repository/Exceptions/PasswordInUnsupportedFormatException.php index d11db4e1f0..dcaa9bb415 100644 --- a/src/contracts/Repository/Exceptions/PasswordInUnsupportedFormatException.php +++ b/src/contracts/Repository/Exceptions/PasswordInUnsupportedFormatException.php @@ -15,6 +15,6 @@ class PasswordInUnsupportedFormatException extends AuthenticationException { public function __construct(?Throwable $previous = null) { - parent::__construct("User's password is in a format which is not supported any more.", 0, $previous); + parent::__construct("User's password is in a format which is not supported.", 0, $previous); } } diff --git a/src/contracts/Repository/PasswordHashService.php b/src/contracts/Repository/PasswordHashService.php index a2f74da9eb..d1775fd314 100644 --- a/src/contracts/Repository/PasswordHashService.php +++ b/src/contracts/Repository/PasswordHashService.php @@ -10,6 +10,20 @@ interface PasswordHashService { + /** + * Sets the default password hash type. + * + * @param int $defaultHashType The default password hash type, one of Ibexa\Contracts\Core\Repository\Values\User\User::SUPPORTED_PASSWORD_HASHES. + */ + public function setDefaultHashType(int $defaultHashType): void; + + /** + * Sets whether the password hash type should be updated when the password is changed. + * + * @param bool $updateTypeOnChange Whether to update the password hash type on change. + */ + public function setUpdateTypeOnChange(bool $updateTypeOnChange): void; + /** * Returns default password hash type. * @@ -33,6 +47,9 @@ public function isHashTypeSupported(int $hashType): bool; * Create hash from given plain password. * * If non-provided, the default password hash type will be used. + * + * @throws \Ibexa\Core\Repository\User\Exception\PasswordHashTypeNotCompiled + * @throws \Ibexa\Core\Repository\User\Exception\UnsupportedPasswordHashType */ public function createPasswordHash(string $plainPassword, ?int $hashType = null): string; @@ -42,4 +59,6 @@ public function createPasswordHash(string $plainPassword, ?int $hashType = null) * If non-provided, the default password hash type will be used. */ public function isValidPassword(string $plainPassword, string $passwordHash, ?int $hashType = null): bool; + + public function shouldPasswordHashTypeBeUpdatedOnChange(): bool; } diff --git a/src/contracts/Repository/Values/User/User.php b/src/contracts/Repository/Values/User/User.php index 07457de91c..92c119e5f1 100644 --- a/src/contracts/Repository/Values/User/User.php +++ b/src/contracts/Repository/Values/User/User.php @@ -28,6 +28,8 @@ abstract class User extends Content implements UserReference public const array SUPPORTED_PASSWORD_HASHES = [ self::PASSWORD_HASH_BCRYPT, self::PASSWORD_HASH_PHP_DEFAULT, + self::PASSWORD_HASH_ARGON2I, + self::PASSWORD_HASH_ARGON2ID, self::PASSWORD_HASH_INVALID, ]; @@ -35,6 +37,10 @@ abstract class User extends Content implements UserReference public const int PASSWORD_HASH_PHP_DEFAULT = 7; + public const int PASSWORD_HASH_ARGON2I = 8; + + public const int PASSWORD_HASH_ARGON2ID = 9; + public const int PASSWORD_HASH_INVALID = 256; public const int DEFAULT_PASSWORD_HASH = self::PASSWORD_HASH_PHP_DEFAULT; diff --git a/src/lib/Base/Container/ApiLoader/RepositoryFactory.php b/src/lib/Base/Container/ApiLoader/RepositoryFactory.php index db341c7e9b..6e1385f4b2 100644 --- a/src/lib/Base/Container/ApiLoader/RepositoryFactory.php +++ b/src/lib/Base/Container/ApiLoader/RepositoryFactory.php @@ -18,6 +18,7 @@ use Ibexa\Contracts\Core\Repository\PermissionService; use Ibexa\Contracts\Core\Repository\Repository; use Ibexa\Contracts\Core\Repository\Validator\ContentValidator; +use Ibexa\Contracts\Core\Repository\Values\User\User; use Ibexa\Contracts\Core\Search\Handler as SearchHandler; use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; use Ibexa\Core\Base\Exceptions\InvalidArgumentException; @@ -98,6 +99,11 @@ public function buildRepository( ): Repository { $config = $this->repositoryConfigurationProvider->getRepositoryConfig(); + if (isset($config['password_hash'])) { + $passwordHashService->setDefaultHashType($config['password_hash']['default_type'] ?? User::PASSWORD_HASH_PHP_DEFAULT); + $passwordHashService->setUpdateTypeOnChange($config['password_hash']['update_type_on_change'] ?? false); + } + return new CoreRepository( $persistenceHandler, $searchHandler, diff --git a/src/lib/MVC/Symfony/Security/Authentication/EventSubscriber/RepositoryUserAuthenticationSubscriber.php b/src/lib/MVC/Symfony/Security/Authentication/EventSubscriber/RepositoryUserAuthenticationSubscriber.php index 707578fbe3..b49b0a72e2 100644 --- a/src/lib/MVC/Symfony/Security/Authentication/EventSubscriber/RepositoryUserAuthenticationSubscriber.php +++ b/src/lib/MVC/Symfony/Security/Authentication/EventSubscriber/RepositoryUserAuthenticationSubscriber.php @@ -12,6 +12,7 @@ use Ibexa\Contracts\Core\Repository\Exceptions\PasswordInUnsupportedFormatException; use Ibexa\Contracts\Core\Repository\UserService; use Ibexa\Core\MVC\Symfony\Security\UserInterface as IbexaUserInterface; +use Ibexa\Core\Repository\User\Exception\PasswordHashTypeNotCompiled; use Ibexa\Core\Repository\User\Exception\UnsupportedPasswordHashType; use Psr\Log\LoggerAwareTrait; use Psr\Log\LoggerInterface; @@ -77,7 +78,7 @@ public function validateRepositoryUser(CheckPassportEvent $event): void $user->getAPIUser(), $user->getPassword() ?? '' ); - } catch (UnsupportedPasswordHashType $exception) { + } catch (UnsupportedPasswordHashType|PasswordHashTypeNotCompiled $exception) { $this->sleepUsingConstantTimer($startTime); throw new PasswordInUnsupportedFormatException($exception); diff --git a/src/lib/Repository/User/Exception/PasswordHashTypeNotCompiled.php b/src/lib/Repository/User/Exception/PasswordHashTypeNotCompiled.php new file mode 100644 index 0000000000..bd7a5f092f --- /dev/null +++ b/src/lib/Repository/User/Exception/PasswordHashTypeNotCompiled.php @@ -0,0 +1,22 @@ +defaultHashType = $defaultHashType; + $this->updateTypeOnChange = $updateTypeOnChange; + } + + public function setDefaultHashType(int $defaultHashType): void + { + $this->defaultHashType = $defaultHashType; + } + + public function setUpdateTypeOnChange(bool $updateTypeOnChange): void { - $this->defaultHashType = $hashType; + $this->updateTypeOnChange = $updateTypeOnChange; } public function getSupportedHashTypes(): array @@ -39,26 +55,37 @@ public function getDefaultHashType(): int return $this->defaultHashType; } - /** - * @throws \Ibexa\Core\Repository\User\Exception\UnsupportedPasswordHashType - */ public function createPasswordHash( #[\SensitiveParameter] - string $password, + string $plainPassword, ?int $hashType = null ): string { - $hashType = $hashType ?? $this->defaultHashType; + $hashType = $hashType ?? $this->getDefaultHashType(); switch ($hashType) { case User::PASSWORD_HASH_BCRYPT: - return password_hash($password, PASSWORD_BCRYPT); + return password_hash($plainPassword, PASSWORD_BCRYPT); case User::PASSWORD_HASH_PHP_DEFAULT: - return password_hash($password, PASSWORD_DEFAULT); + return password_hash($plainPassword, PASSWORD_DEFAULT); case User::PASSWORD_HASH_INVALID: return ''; + case User::PASSWORD_HASH_ARGON2I: + if (!defined('PASSWORD_ARGON2I')) { + throw new PasswordHashTypeNotCompiled('PASSWORD_ARGON2I'); + } + + return password_hash($plainPassword, PASSWORD_ARGON2I); + + case User::PASSWORD_HASH_ARGON2ID: + if (!defined('PASSWORD_ARGON2ID')) { + throw new PasswordHashTypeNotCompiled('PASSWORD_ARGON2ID'); + } + + return password_hash($plainPassword, PASSWORD_ARGON2ID); + default: throw new UnsupportedPasswordHashType($hashType); } @@ -74,12 +101,24 @@ public function isValidPassword( if ( $hashType === User::PASSWORD_HASH_BCRYPT || $hashType === User::PASSWORD_HASH_PHP_DEFAULT + || $hashType === User::PASSWORD_HASH_ARGON2I + || $hashType === User::PASSWORD_HASH_ARGON2ID || $hashType === User::PASSWORD_HASH_INVALID ) { - // In case of bcrypt let PHP's password functionality do its magic + // Let PHP's password functionality do its magic return password_verify($plainPassword, $passwordHash); } - return $passwordHash === $this->createPasswordHash($plainPassword, $hashType); + try { + return $passwordHash === $this->createPasswordHash($plainPassword, $hashType); + } catch (PasswordHashTypeNotCompiled|UnsupportedPasswordHashType $e) { + // If the hash type is not compiled or unsupported we can't verify the password so it's not valid + return false; + } + } + + public function shouldPasswordHashTypeBeUpdatedOnChange(): bool + { + return $this->updateTypeOnChange; } } diff --git a/src/lib/Repository/UserService.php b/src/lib/Repository/UserService.php index 85c2d2050c..d49e6a87bf 100644 --- a/src/lib/Repository/UserService.php +++ b/src/lib/Repository/UserService.php @@ -49,19 +49,25 @@ use Ibexa\Core\Base\Exceptions\MissingUserFieldTypeException; use Ibexa\Core\Base\Exceptions\UnauthorizedException; use Ibexa\Core\FieldType\User\Value as UserValue; +use Ibexa\Core\Repository\User\Exception\PasswordHashTypeNotCompiled; use Ibexa\Core\Repository\User\Exception\UnsupportedPasswordHashType; use Ibexa\Core\Repository\User\PasswordValidatorInterface; use Ibexa\Core\Repository\Values\User\User; use Ibexa\Core\Repository\Values\User\UserCreateStruct; use Ibexa\Core\Repository\Values\User\UserGroup; use Ibexa\Core\Repository\Values\User\UserGroupCreateStruct; +use Psr\Log\LoggerAwareTrait; use Psr\Log\LoggerInterface; +use Psr\Log\LogLevel; +use Psr\Log\NullLogger; /** * This service provides methods for managing users and user groups. */ class UserService implements UserServiceInterface { + use LoggerAwareTrait; + private const USER_FIELD_TYPE_NAME = 'ibexa_user'; /** @var \Ibexa\Contracts\Core\Repository\Repository */ @@ -76,9 +82,6 @@ class UserService implements UserServiceInterface /** @var array */ protected $settings; - /** @var \Psr\Log\LoggerInterface|null */ - protected $logger; - /** @var \Ibexa\Contracts\Core\Repository\PermissionResolver */ private $permissionResolver; @@ -90,11 +93,6 @@ class UserService implements UserServiceInterface private ConfigResolverInterface $configResolver; - public function setLogger(?LoggerInterface $logger = null) - { - $this->logger = $logger; - } - /** * Setups service with reference to repository object that created it & corresponding handler. */ @@ -106,7 +104,8 @@ public function __construct( PasswordHashService $passwordHashGenerator, PasswordValidatorInterface $passwordValidator, ConfigResolverInterface $configResolver, - array $settings = [] + array $settings = [], + ?LoggerInterface $logger = null ) { $this->repository = $repository; $this->permissionResolver = $permissionResolver; @@ -123,6 +122,7 @@ public function __construct( $this->passwordHashService = $passwordHashGenerator; $this->passwordValidator = $passwordValidator; $this->configResolver = $configResolver; + $this->logger = $logger ?? new NullLogger(); } /** @@ -778,14 +778,46 @@ public function updateUserPassword( ); } - $passwordHashAlgorithm = (int) $loadedUser->hashAlgorithm; - try { - $passwordHash = $this->passwordHashService->createPasswordHash($newPassword, $passwordHashAlgorithm); - } catch (UnsupportedPasswordHashType $e) { - $passwordHashAlgorithm = $this->passwordHashService->getDefaultHashType(); - $passwordHash = $this->passwordHashService->createPasswordHash($newPassword, $passwordHashAlgorithm); + $defaultPasswordHashAlgorithm = $this->passwordHashService->getDefaultHashType(); + $userCurrentPasswordHashAlgorithm = (int) $loadedUser->hashAlgorithm; + $updatePasswordHashTypeOnChange = $this->passwordHashService->shouldPasswordHashTypeBeUpdatedOnChange(); + $passwordHashAlgorithm = $userCurrentPasswordHashAlgorithm; + if ($updatePasswordHashTypeOnChange) { + $passwordHashAlgorithm = $defaultPasswordHashAlgorithm; } + $passwordHash = null; + do { + try { + $passwordHash = $this->passwordHashService->createPasswordHash($newPassword, $passwordHashAlgorithm); + } catch (UnsupportedPasswordHashType|PasswordHashTypeNotCompiled $e) { + $this->logger->log(LogLevel::WARNING, $e->getMessage(), [ + 'exception' => $e, + ]); + + if ( + $updatePasswordHashTypeOnChange && + $passwordHashAlgorithm !== $userCurrentPasswordHashAlgorithm + ) { + // If we're trying to upgrade the password hash algorithm but the upgrade fails, + // we fall back to the user's current password hash algorithm. + $passwordHashAlgorithm = $userCurrentPasswordHashAlgorithm; + } elseif ( + !$updatePasswordHashTypeOnChange && + $passwordHashAlgorithm !== $defaultPasswordHashAlgorithm + ) { + // If we're not trying to upgrade the password hash algorithm and the user's current + // password hash algorithm fails, we fall back to the default one. + $passwordHashAlgorithm = $defaultPasswordHashAlgorithm; + } else { + throw new InvalidArgumentException( + 'passwordHashAlgorithm', + 'The password hash algorithm is not supported or not compiled.' + ); + } + } + } while ($passwordHash === null); + $this->repository->beginTransaction(); try { $this->userHandler->updatePassword( diff --git a/tests/integration/Core/Repository/UserServiceTest.php b/tests/integration/Core/Repository/UserServiceTest.php index 041bdf967d..fb7d1b8c16 100644 --- a/tests/integration/Core/Repository/UserServiceTest.php +++ b/tests/integration/Core/Repository/UserServiceTest.php @@ -1325,7 +1325,7 @@ public function testCreateUserWithWeakPasswordThrowsUserPasswordValidationExcept try { // This call will fail with a "UserPasswordValidationException" because the - // the password does not follow specified rules. + // password does not follow specified rules. $this->createTestUserWithPassword('pass', $userContentType); } catch (ContentFieldValidationException $e) { // Exception is caught, as there is no other way to check exception properties. @@ -2177,13 +2177,41 @@ public function testUpdateUserPasswordWithUnsupportedHashType(): void $wrongHashType = 1; $this->updateRawPasswordHash($user->getUserId(), $wrongHashType); $newPassword = 'new_secret123'; - // no need to invalidate cache since there was no load between create & raw database update + // no need to invalidate cache since there was no load between creation + // and raw database update $user = $userService->updateUserPassword($user, $newPassword); self::assertTrue($userService->checkUserCredentials($user, $newPassword)); self::assertNotEquals($oldPasswordHash, $user->passwordHash); } + /** + * @throws \Doctrine\DBAL\Exception + * @throws \ErrorException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentFieldValidationException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + public function testUpdateUserPasswordHashToArgon2Id(): void + { + $repository = $this->getRepository(); + $userService = $repository->getUserService(); + + $user = $this->createUser('john.doe', 'John', 'Doe'); + $oldPasswordHash = $user->passwordHash; + + $argon2IdHashType = User::PASSWORD_HASH_ARGON2ID; + $this->updateRawPasswordHash($user->getUserId(), $argon2IdHashType); + $newPassword = 'new_secret123'; + // no need to invalidate cache since there was no load between creation + // and raw database update + $user = $userService->updateUserPassword($user, $newPassword); + $passwordInfo = password_get_info($user->passwordHash); + + self::assertTrue($userService->checkUserCredentials($user, $newPassword)); + self::assertNotEquals($oldPasswordHash, $user->passwordHash); + self::assertEquals(PASSWORD_ARGON2ID, $passwordInfo['algo']); + } + /** * Test for the loadUserGroupsOfUser() method. * diff --git a/tests/integration/Core/Resources/settings/integration_legacy.yml b/tests/integration/Core/Resources/settings/integration_legacy.yml index f6bdd6a364..ee9b9cee33 100644 --- a/tests/integration/Core/Resources/settings/integration_legacy.yml +++ b/tests/integration/Core/Resources/settings/integration_legacy.yml @@ -26,6 +26,9 @@ parameters: options: default_version_archive_limit: 5 remove_archived_versions_on_publish: true + password_hash: + default_type: 7 + update_type_on_change: false ibexa.site_access.config.default.repository: default ibexa.site_access.config.default.languages: '%languages%' diff --git a/tests/lib/Repository/User/PasswordHashServiceTest.php b/tests/lib/Repository/User/PasswordHashServiceTest.php index 80aaedee8e..606a5670d3 100644 --- a/tests/lib/Repository/User/PasswordHashServiceTest.php +++ b/tests/lib/Repository/User/PasswordHashServiceTest.php @@ -11,9 +11,9 @@ use Ibexa\Contracts\Core\Repository\Values\User\User; use Ibexa\Core\Repository\User\Exception\UnsupportedPasswordHashType; use Ibexa\Core\Repository\User\PasswordHashService; -use PHPUnit\Framework\TestCase; +use Ibexa\Tests\Bundle\Core\ApiLoader\BaseRepositoryConfigurationProviderTestCase; -final class PasswordHashServiceTest extends TestCase +final class PasswordHashServiceTest extends BaseRepositoryConfigurationProviderTestCase { private const int NON_EXISTING_PASSWORD_HASH = PHP_INT_MAX; @@ -30,6 +30,8 @@ public function testGetSupportedHashTypes(): void [ User::PASSWORD_HASH_BCRYPT, User::PASSWORD_HASH_PHP_DEFAULT, + User::PASSWORD_HASH_ARGON2I, + User::PASSWORD_HASH_ARGON2ID, User::PASSWORD_HASH_INVALID, ], $this->passwordHashService->getSupportedHashTypes()