From 6ee3b2a94dba8f21448a26055482e1f9ac6308a9 Mon Sep 17 00:00:00 2001 From: Andreas Schempp Date: Mon, 4 Mar 2024 09:29:49 +0100 Subject: [PATCH] Updated PHP and Contao requirements --- .editorconfig | 24 +++++++ .github/FUNDING.yml | 2 + .github/workflows/ci.yml | 29 ++++++++ .gitignore | 2 +- README.md | 2 +- composer.json | 34 +++++----- config/listeners.yml | 50 +++++++------- config/services.yml | 11 +-- config/validators.yml | 67 ++++++++++--------- contao/dca/tl_page.php | 2 +- src/ContaoManager/Plugin.php | 14 +--- src/DependencyInjection/Configuration.php | 13 +--- .../Terminal42PasswordValidationExtension.php | 13 +--- src/EventListener/NoPwChangePageWarning.php | 39 +++-------- src/EventListener/PasswordChangeListener.php | 22 +++--- src/EventListener/PasswordHistoryListener.php | 49 ++++---------- src/EventListener/PasswordRegexpListener.php | 49 ++++++-------- .../RedirectToPasswordChangePageListener.php | 54 +++------------ src/EventListener/SetNewPasswordListener.php | 23 +++---- src/Exception/PasswordValidatorException.php | 8 --- src/Model/PasswordHistory.php | 14 ++-- src/Terminal42PasswordValidationBundle.php | 8 --- src/Validation/PasswordValidatorInterface.php | 8 --- src/Validation/ValidationConfiguration.php | 2 +- src/Validation/ValidationContext.php | 16 ++--- src/Validation/Validator/HaveIBeenPwned.php | 35 +++------- src/Validation/Validator/MaximumLength.php | 28 ++------ src/Validation/Validator/MinimumLength.php | 28 ++------ src/Validation/Validator/PasswordHistory.php | 45 +++---------- .../Validator/RequiredCharacters.php | 47 +++++++------ src/Validation/ValidatorManager.php | 17 ++--- 31 files changed, 297 insertions(+), 458 deletions(-) create mode 100644 .editorconfig create mode 100644 .github/FUNDING.yml create mode 100644 .github/workflows/ci.yml diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..521d748 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,24 @@ +# EditorConfig is awesome: http://EditorConfig.org + +# top-most EditorConfig file +root = true + +# Unix-style newlines with a newline ending every file +[*] +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +[*.{php,twig,yml}] +indent_style = space +indent_size = 4 + +[*.{svg,min.css,min.js}] +insert_final_newline = false + +[*.html5] +indent_style = space +indent_size = 2 + +[*.md] +trim_trailing_whitespace = false diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..0390455 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,2 @@ +github: terminal42 +ko_fi: terminal42 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..fc89221 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,29 @@ +name: CI + +on: + push: ~ + pull_request: ~ + +permissions: read-all + +jobs: + ecs: + name: build-tools + runs-on: ubuntu-latest + steps: + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: 8.1 + extensions: dom, fileinfo, filter, gd, hash, intl, json, mbstring, mysqli, pcre, pdo_mysql, zlib + coverage: none + + - name: Checkout + uses: actions/checkout@v3 + + - name: Install the dependencies + run: | + composer install --no-interaction --no-progress + + - name: Run all build-tools to validate code + run: composer run build-tools diff --git a/.gitignore b/.gitignore index 3a9875b..4fbb073 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ /vendor/ -composer.lock +/composer.lock diff --git a/README.md b/README.md index b16ecb1..e16633a 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ terminal42/contao-password-validation ===================================== -A Contao 4 bundle that validates user passwords against your password policy. +A Contao bundle that validates user passwords against your password policy. ## Features diff --git a/composer.json b/composer.json index 44d33ee..bea8930 100644 --- a/composer.json +++ b/composer.json @@ -11,19 +11,29 @@ "homepage": "https://www.terminal42.ch" } ], + "funding": [ + { + "type": "github", + "url": "https://github.com/terminal42" + }, + { + "type": "other", + "url": "https://ko-fi.com/terminal42" + } + ], "support": { "issues": "https://github.com/terminal42/contao-password-validation/issues", - "source": "https://github.com/terminal42/contao-password-validation" + "source": "https://github.com/terminal42/contao-password-validation", + "forum": "https://community.contao.org" }, "require": { - "php": "^7.1 || ^8.0", - "contao/core-bundle": "^4.9", - "doctrine/dbal": "^2.11 || ^3", + "php": "^8.1", + "contao/core-bundle": "^4.13 || ^5.0", "paragonie/hidden-string": "^1.0", - "symfony/config": "^4.2 || ^5.0", - "symfony/dependency-injection": "^4.0 || ^5.0", - "symfony/http-client": "^4.0 || ^5.0", - "symfony/http-kernel": "^4.0 || ^5.0" + "symfony/config": "^5.0 || ^6.0", + "symfony/dependency-injection": "^5.0 || ^6.0", + "symfony/http-client": "^5.0 || ^6.0", + "symfony/http-kernel": "^5.0 || ^6.0" }, "require-dev": { "contao/manager-plugin": "^2.7", @@ -36,13 +46,5 @@ }, "extra": { "contao-manager-plugin": "Terminal42\\PasswordValidationBundle\\ContaoManager\\Plugin" - }, - "config": { - "allow-plugins": { - "contao-components/installer": true, - "contao/manager-plugin": true, - "dealerdirect/phpcodesniffer-composer-installer": true, - "terminal42/contao-build-tools": true - } } } diff --git a/config/listeners.yml b/config/listeners.yml index 9412b68..1cc413c 100644 --- a/config/listeners.yml +++ b/config/listeners.yml @@ -1,29 +1,33 @@ services: - Terminal42\PasswordValidationBundle\EventListener\PasswordRegexpListener: - arguments: - - '@terminal42_password_validation.validator_manager' - - '@terminal42_password_validation.validator_configuration' - - '@request_stack' + Terminal42\PasswordValidationBundle\EventListener\PasswordRegexpListener: + arguments: + - '@terminal42_password_validation.validator_manager' + - '@terminal42_password_validation.validator_configuration' + - '@request_stack' + - '@contao.routing.scope_matcher' + - '@security.helper' - Terminal42\PasswordValidationBundle\EventListener\PasswordHistoryListener: - arguments: - ['@terminal42_password_validation.validator_configuration', '@security.encoder_factory', '@database_connection'] + Terminal42\PasswordValidationBundle\EventListener\PasswordHistoryListener: + arguments: + - '@terminal42_password_validation.validator_configuration' + - '@security.password_hasher_factory' + - '@database_connection' - Terminal42\PasswordValidationBundle\EventListener\PasswordChangeListener: - arguments: - ['@terminal42_password_validation.validator_configuration'] + Terminal42\PasswordValidationBundle\EventListener\PasswordChangeListener: + arguments: + - '@terminal42_password_validation.validator_configuration' - Terminal42\PasswordValidationBundle\EventListener\NoPwChangePageWarning: - arguments: - ['@terminal42_password_validation.validator_configuration'] + Terminal42\PasswordValidationBundle\EventListener\NoPwChangePageWarning: + arguments: + - '@terminal42_password_validation.validator_configuration' + - '@translator' - Terminal42\PasswordValidationBundle\EventListener\RedirectToPasswordChangePageListener: - arguments: - - '@contao.framework' - - '@security.token_storage' - - '@security.authentication.trust_resolver' - - '@contao.routing.scope_matcher' - tags: - - kernel.event_listener + Terminal42\PasswordValidationBundle\EventListener\RedirectToPasswordChangePageListener: + arguments: + - '@contao.framework' + - '@security.token_storage' + - '@contao.routing.scope_matcher' + tags: + - kernel.event_listener - Terminal42\PasswordValidationBundle\EventListener\SetNewPasswordListener: ~ + Terminal42\PasswordValidationBundle\EventListener\SetNewPasswordListener: ~ diff --git a/config/services.yml b/config/services.yml index 43ba850..01f8bfb 100644 --- a/config/services.yml +++ b/config/services.yml @@ -1,7 +1,8 @@ services: - terminal42_password_validation.validator_configuration: - class: Terminal42\PasswordValidationBundle\Validation\ValidationConfiguration + terminal42_password_validation.validator_configuration: + class: Terminal42\PasswordValidationBundle\Validation\ValidationConfiguration - terminal42_password_validation.validator_manager: - class: Terminal42\PasswordValidationBundle\Validation\ValidatorManager - arguments: [ !tagged_iterator { tag: 'terminal42_password_validation.validator', index_by: 'alias' } ] + terminal42_password_validation.validator_manager: + class: Terminal42\PasswordValidationBundle\Validation\ValidatorManager + arguments: + - !tagged_iterator { tag: 'terminal42_password_validation.validator', index_by: 'alias' } diff --git a/config/validators.yml b/config/validators.yml index 3dcf991..9d80da4 100644 --- a/config/validators.yml +++ b/config/validators.yml @@ -1,35 +1,42 @@ services: - terminal42_password_validation.validator.password_history: - class: Terminal42\PasswordValidationBundle\Validation\Validator\PasswordHistory - tags: - - { name: terminal42_password_validation.validator, alias: password_history } - arguments: - ['@terminal42_password_validation.validator_configuration', '@security.encoder_factory'] + terminal42_password_validation.validator.password_history: + class: Terminal42\PasswordValidationBundle\Validation\Validator\PasswordHistory + tags: + - { name: terminal42_password_validation.validator, alias: password_history } + arguments: + - '@terminal42_password_validation.validator_configuration' + - '@security.password_hasher_factory' + - '@translator' - terminal42_password_validation.validator.minimum_length: - class: Terminal42\PasswordValidationBundle\Validation\Validator\MinimumLength - tags: - - { name: terminal42_password_validation.validator, alias: minimum_length } - arguments: - ['@terminal42_password_validation.validator_configuration'] + terminal42_password_validation.validator.minimum_length: + class: Terminal42\PasswordValidationBundle\Validation\Validator\MinimumLength + tags: + - { name: terminal42_password_validation.validator, alias: minimum_length } + arguments: + - '@terminal42_password_validation.validator_configuration' + - '@translator' - terminal42_password_validation.validator.maximum_length: - class: Terminal42\PasswordValidationBundle\Validation\Validator\MaximumLength - tags: - - { name: terminal42_password_validation.validator, alias: maximum_length } - arguments: - ['@terminal42_password_validation.validator_configuration'] + terminal42_password_validation.validator.maximum_length: + class: Terminal42\PasswordValidationBundle\Validation\Validator\MaximumLength + tags: + - { name: terminal42_password_validation.validator, alias: maximum_length } + arguments: + - '@terminal42_password_validation.validator_configuration' + - '@translator' - terminal42_password_validation.validator.required_characters: - class: Terminal42\PasswordValidationBundle\Validation\Validator\RequiredCharacters - tags: - - { name: terminal42_password_validation.validator, alias: required_characters } - arguments: - ['@terminal42_password_validation.validator_configuration'] + terminal42_password_validation.validator.required_characters: + class: Terminal42\PasswordValidationBundle\Validation\Validator\RequiredCharacters + tags: + - { name: terminal42_password_validation.validator, alias: required_characters } + arguments: + - '@terminal42_password_validation.validator_configuration' + - '@translator' - terminal42_password_validation.validator.haveibeenpwned: - class: Terminal42\PasswordValidationBundle\Validation\Validator\HaveIBeenPwned - tags: - - { name: terminal42_password_validation.validator, alias: haveibeenpwned } - arguments: - ['@terminal42_password_validation.validator_configuration', '@http_client'] + terminal42_password_validation.validator.haveibeenpwned: + class: Terminal42\PasswordValidationBundle\Validation\Validator\HaveIBeenPwned + tags: + - { name: terminal42_password_validation.validator, alias: haveibeenpwned } + arguments: + - '@terminal42_password_validation.validator_configuration' + - '@http_client' + - '@translator' diff --git a/contao/dca/tl_page.php b/contao/dca/tl_page.php index 2cd4958..36483b1 100644 --- a/contao/dca/tl_page.php +++ b/contao/dca/tl_page.php @@ -11,7 +11,7 @@ $paletteManipulator = PaletteManipulator::create() try { $paletteManipulator->applyToPalette('root', 'tl_page'); $paletteManipulator->applyToPalette('rootfallback', 'tl_page'); -} catch (PaletteNotFoundException $e) { +} catch (PaletteNotFoundException) { } $GLOBALS['TL_DCA']['tl_page']['fields']['pwChangePage'] = [ diff --git a/src/ContaoManager/Plugin.php b/src/ContaoManager/Plugin.php index 366a3e3..60f3866 100644 --- a/src/ContaoManager/Plugin.php +++ b/src/ContaoManager/Plugin.php @@ -2,14 +2,6 @@ declare(strict_types=1); -/* - * This file is part of terminal42/contao-password-validation. - * - * (c) terminal42 gmbh - * - * @license MIT - */ - namespace Terminal42\PasswordValidationBundle\ContaoManager; use Contao\CoreBundle\ContaoCoreBundle; @@ -24,11 +16,7 @@ final class Plugin implements BundlePluginInterface { return [ BundleConfig::create(Terminal42PasswordValidationBundle::class) - ->setLoadAfter( - [ - ContaoCoreBundle::class, - ] - ), + ->setLoadAfter([ContaoCoreBundle::class]), ]; } } diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php index eb47ba6..91bcf58 100644 --- a/src/DependencyInjection/Configuration.php +++ b/src/DependencyInjection/Configuration.php @@ -2,14 +2,6 @@ declare(strict_types=1); -/* - * This file is part of terminal42/contao-password-validation. - * - * (c) terminal42 gmbh - * - * @license MIT - */ - namespace Terminal42\PasswordValidationBundle\DependencyInjection; use Symfony\Component\Config\Definition\Builder\TreeBuilder; @@ -20,7 +12,6 @@ final class Configuration implements ConfigurationInterface public function getConfigTreeBuilder(): TreeBuilder { $treeBuilder = new TreeBuilder('terminal42_password_validation'); - $treeBuilder ->getRootNode() ->useAttributeAsKey('entity') @@ -29,10 +20,10 @@ final class Configuration implements ConfigurationInterface ->integerNode('min_length')->end() ->integerNode('max_length')->end() ->integerNode('invalid_attempts') - ->setDeprecated('The "%node%" option is not available since Contao 4.9 comes with improved brute-force-protection.') + ->setDeprecated('terminal42/contao-password-validation', '1.0.8', 'The "%node%" option is not available since Contao 4.9 comes with improved brute-force-protection.') ->end() ->integerNode('nc_account_disabled') - ->setDeprecated('The "%node%" option is not available since Contao 4.9 comes with improved brute-force-protection.') + ->setDeprecated('terminal42/contao-password-validation', '1.0.8', 'The "%node%" option is not available since Contao 4.9 comes with improved brute-force-protection.') ->end() ->integerNode('password_history')->end() ->integerNode('change_days')->end() diff --git a/src/DependencyInjection/Terminal42PasswordValidationExtension.php b/src/DependencyInjection/Terminal42PasswordValidationExtension.php index 1082623..9c418d4 100644 --- a/src/DependencyInjection/Terminal42PasswordValidationExtension.php +++ b/src/DependencyInjection/Terminal42PasswordValidationExtension.php @@ -2,14 +2,6 @@ declare(strict_types=1); -/* - * This file is part of terminal42/contao-password-validation. - * - * (c) terminal42 gmbh - * - * @license MIT - */ - namespace Terminal42\PasswordValidationBundle\DependencyInjection; use Symfony\Component\Config\FileLocator; @@ -19,15 +11,12 @@ use Symfony\Component\HttpKernel\DependencyInjection\Extension; final class Terminal42PasswordValidationExtension extends Extension { - private static $files = [ + private static array $files = [ 'listeners.yml', 'services.yml', 'validators.yml', ]; - /** - * {@inheritdoc} - */ public function load(array $configs, ContainerBuilder $container): void { $config = $this->processConfiguration(new Configuration(), $configs); diff --git a/src/EventListener/NoPwChangePageWarning.php b/src/EventListener/NoPwChangePageWarning.php index 1cf98ca..0251a25 100644 --- a/src/EventListener/NoPwChangePageWarning.php +++ b/src/EventListener/NoPwChangePageWarning.php @@ -2,23 +2,16 @@ declare(strict_types=1); -/* - * This file is part of terminal42/contao-password-validation. - * - * (c) terminal42 gmbh - * - * @license MIT - */ - namespace Terminal42\PasswordValidationBundle\EventListener; -use Contao\CoreBundle\ServiceAnnotation\Callback; -use Contao\CoreBundle\ServiceAnnotation\Hook; +use Contao\CoreBundle\DependencyInjection\Attribute\AsCallback; +use Contao\CoreBundle\DependencyInjection\Attribute\AsHook; use Contao\FrontendUser; use Contao\Input; use Contao\MemberModel; use Contao\Message; use Contao\PageModel; +use Symfony\Contracts\Translation\TranslatorInterface; use Terminal42\PasswordValidationBundle\Validation\ValidationConfiguration; /** @@ -26,16 +19,13 @@ use Terminal42\PasswordValidationBundle\Validation\ValidationConfiguration; */ final class NoPwChangePageWarning { - private $configuration; - - public function __construct(ValidationConfiguration $configuration) - { - $this->configuration = $configuration; + public function __construct( + private readonly ValidationConfiguration $configuration, + private readonly TranslatorInterface $translator, + ) { } - /** - * @Hook("getSystemMessages") - */ + #[AsHook('getSystemMessages')] public function onGetSystemMessages(): string { $needsPwChangePage = false; @@ -62,16 +52,14 @@ final class NoPwChangePageWarning $rootPages = PageModel::findBy(["tl_page.type='root' AND tl_page.pwChangePage=''"], []); - if ($needsPwChangePage && null !== $rootPages) { - return '

'.$this->translate('noPwChangePage').'

'; + if (null !== $rootPages) { + return '

'.$this->translator->trans('ERR.passwordValidation.noPwChangePage', [], 'contao_default').'

'; } return ''; } - /** - * @Callback(table="tl_page", target="config.onload") - */ + #[AsCallback(table: 'tl_page', target: 'config.onload')] public function tlPageShowWarning(): void { if (Input::get('act')) { @@ -80,9 +68,4 @@ final class NoPwChangePageWarning Message::addRaw($this->onGetSystemMessages()); } - - private function translate(string $key) - { - return $GLOBALS['TL_LANG']['ERR']['passwordValidation'][$key]; - } } diff --git a/src/EventListener/PasswordChangeListener.php b/src/EventListener/PasswordChangeListener.php index 0fcd00e..63e616f 100644 --- a/src/EventListener/PasswordChangeListener.php +++ b/src/EventListener/PasswordChangeListener.php @@ -12,23 +12,19 @@ declare(strict_types=1); namespace Terminal42\PasswordValidationBundle\EventListener; -use Contao\CoreBundle\ServiceAnnotation\Hook; +use Contao\CoreBundle\DependencyInjection\Attribute\AsHook; use Contao\User; use Terminal42\PasswordValidationBundle\Model\PasswordHistory; use Terminal42\PasswordValidationBundle\Validation\ValidationConfiguration; /** * This listener forces a password change for passwords being too old. - * - * @Hook("postLogin") */ +#[AsHook('postLogin')] final class PasswordChangeListener { - private $configuration; - - public function __construct(ValidationConfiguration $configuration) + public function __construct(private readonly ValidationConfiguration $configuration) { - $this->configuration = $configuration; } public function __invoke(User $user): void @@ -38,10 +34,12 @@ final class PasswordChangeListener } $userId = (int) $user->id; - $passwordLog = PasswordHistory::findCurrentLog(\get_class($user), $userId); + $passwordLog = PasswordHistory::findCurrentLog($user::class, $userId); - if (null === $passwordLog) { + if (!$passwordLog) { $this->forcePasswordChange($user); + + return; } $maxAge = strtotime("-$maxDays days"); @@ -51,9 +49,9 @@ final class PasswordChangeListener } } - private function getMaxDays(User $user): ?int + private function getMaxDays(User $user): int|null { - $userEntity = \get_class($user); + $userEntity = $user::class; if (false === $this->configuration->hasConfiguration($userEntity)) { return null; @@ -72,7 +70,7 @@ final class PasswordChangeListener private function forcePasswordChange(User $user): void { - $user->pwChange = 1; + $user->pwChange = true; $user->save(); } } diff --git a/src/EventListener/PasswordHistoryListener.php b/src/EventListener/PasswordHistoryListener.php index 5c195d3..1d16418 100644 --- a/src/EventListener/PasswordHistoryListener.php +++ b/src/EventListener/PasswordHistoryListener.php @@ -2,19 +2,11 @@ declare(strict_types=1); -/* - * This file is part of terminal42/contao-password-validation. - * - * (c) terminal42 gmbh - * - * @license MIT - */ - namespace Terminal42\PasswordValidationBundle\EventListener; use Contao\BackendUser; -use Contao\CoreBundle\ServiceAnnotation\Callback; -use Contao\CoreBundle\ServiceAnnotation\Hook; +use Contao\CoreBundle\DependencyInjection\Attribute\AsCallback; +use Contao\CoreBundle\DependencyInjection\Attribute\AsHook; use Contao\Database\Result as DatabaseResult; use Contao\DataContainer; use Contao\FrontendUser; @@ -22,7 +14,7 @@ use Contao\MemberModel; use Contao\StringUtil; use Contao\User; use Doctrine\DBAL\Connection; -use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface; +use Symfony\Component\PasswordHasher\Hasher\PasswordHasherFactoryInterface; use Terminal42\PasswordValidationBundle\Model\PasswordHistory; use Terminal42\PasswordValidationBundle\Validation\ValidationConfiguration; @@ -31,27 +23,20 @@ use Terminal42\PasswordValidationBundle\Validation\ValidationConfiguration; */ final class PasswordHistoryListener { - private $configuration; - - private $encoderFactory; - - private $connection; - - public function __construct(ValidationConfiguration $configuration, EncoderFactoryInterface $encoderFactory, Connection $connection) - { - $this->configuration = $configuration; - $this->encoderFactory = $encoderFactory; - $this->connection = $connection; + public function __construct( + private readonly ValidationConfiguration $configuration, + private readonly PasswordHasherFactoryInterface $encoderFactory, + private readonly Connection $connection, + ) { } /** * This hook is triggered for frontend users exclusively (ModuleChangePassword and save_callback). * - * @Hook("setNewPassword") - * * @param MemberModel|DatabaseResult $member */ - public function onSetNewPassword($member, string $password): void + #[AsHook('setNewPassword')] + public function onSetNewPassword($member, #[\SensitiveParameter] string $password): void { if (false === $this->configuration->hasConfiguration(FrontendUser::class)) { return; @@ -96,10 +81,9 @@ final class PasswordHistoryListener /** * This listener keeps track of the backend user's passwords. - * - * @Callback(table="tl_user", target="fields.password.save") */ - public function onBackendSaveCallback(string $password, DataContainer $dc): string + #[AsCallback(table: 'tl_user', target: 'fields.password.save')] + public function onBackendSaveCallback(#[\SensitiveParameter] string $password, DataContainer $dc): string { if (false === $this->configuration->hasConfiguration(BackendUser::class)) { return $password; @@ -115,7 +99,7 @@ final class PasswordHistoryListener $hash = $password; if (!$this->isHashedPassword($hash)) { - $hash = $this->hashPassword($hash); + $hash = $this->encoderFactory->getPasswordHasher(User::class)->hash($hash); } $userId = (int) $dc->id; @@ -125,13 +109,8 @@ final class PasswordHistoryListener return $password; } - private function isHashedPassword(string $password): bool + private function isHashedPassword(#[\SensitiveParameter] string $password): bool { return (bool) password_get_info($password)['algo']; } - - private function hashPassword(string $password): string - { - return $this->encoderFactory->getEncoder(User::class)->encodePassword($password, null); - } } diff --git a/src/EventListener/PasswordRegexpListener.php b/src/EventListener/PasswordRegexpListener.php index 45d94e0..79e2e17 100644 --- a/src/EventListener/PasswordRegexpListener.php +++ b/src/EventListener/PasswordRegexpListener.php @@ -2,18 +2,11 @@ declare(strict_types=1); -/* - * This file is part of terminal42/contao-password-validation. - * - * (c) terminal42 gmbh - * - * @license MIT - */ - namespace Terminal42\PasswordValidationBundle\EventListener; use Contao\BackendUser; -use Contao\CoreBundle\ServiceAnnotation\Hook; +use Contao\CoreBundle\DependencyInjection\Attribute\AsHook; +use Contao\CoreBundle\Routing\ScopeMatcher; use Contao\FrontendUser; use Contao\Input; use Contao\ModulePersonalData; @@ -21,6 +14,7 @@ use Contao\ModuleRegistration; use Contao\Widget; use ParagonIE\HiddenString\HiddenString; use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\Security\Core\Security; use Terminal42\PasswordValidationBundle\Exception\PasswordValidatorException; use Terminal42\PasswordValidationBundle\Validation\ValidationConfiguration; use Terminal42\PasswordValidationBundle\Validation\ValidationContext; @@ -28,33 +22,27 @@ use Terminal42\PasswordValidationBundle\Validation\ValidatorManager; /** * This listener validates the password input by providing a regexp. - * - * @Hook("addCustomRegexp") */ +#[AsHook('addCustomRegexp')] final class PasswordRegexpListener { - private $validatorManager; - - private $configuration; - /** - * @var RequestStack - */ - private $requestStack; - - public function __construct(ValidatorManager $validatorManager, ValidationConfiguration $configuration, RequestStack $requestStack) - { - $this->validatorManager = $validatorManager; - $this->configuration = $configuration; - $this->requestStack = $requestStack; + public function __construct( + private readonly ValidatorManager $validatorManager, + private readonly ValidationConfiguration $configuration, + private readonly RequestStack $requestStack, + private readonly ScopeMatcher $scopeMatcher, + private readonly Security $security, + ) { } - public function __invoke(string $rgxp, $input, Widget $widget): bool + public function __invoke(string $rgxp, #[\SensitiveParameter] mixed $input, Widget $widget): bool { if ('terminal42_password_validation' !== $rgxp) { return false; } $dc = $widget->dataContainer; + $request = $this->requestStack->getCurrentRequest(); if ($dc instanceof ModulePersonalData) { $userId = (int) FrontendUser::getInstance()->id; @@ -62,11 +50,12 @@ final class PasswordRegexpListener } elseif ($dc instanceof ModuleRegistration) { $userId = null; $userEntity = FrontendUser::class; - } elseif ('FE' === TL_MODE && FE_USER_LOGGED_IN) { + } elseif ($request && $this->scopeMatcher->isFrontendRequest($request) && $this->security->isGranted('ROLE_MEMBER')) { $userId = (int) FrontendUser::getInstance()->id; $userEntity = FrontendUser::class; } elseif ( - 'FE' === TL_MODE + $request + && $this->scopeMatcher->isFrontendRequest($request) && Input::post('FORM_SUBMIT') === $this->requestStack->getSession()->get('setPasswordToken') && $widget->currentRecord ) { @@ -106,9 +95,9 @@ final class PasswordRegexpListener $widget->addError($e->getMessage()); return true; - } catch (\Throwable $e) { - // Unhandled exceptions can be dangerous since the plaintext password is passed to this method. - // The password would have been available in the stack trace. + } catch (\Throwable) { + // Unhandled exceptions can be dangerous since the plaintext password is passed + // to this method. The password would have been available in the stack trace. $widget->addError('An unexpected error occurred.'); return true; diff --git a/src/EventListener/RedirectToPasswordChangePageListener.php b/src/EventListener/RedirectToPasswordChangePageListener.php index 830ed42..1e1fc52 100644 --- a/src/EventListener/RedirectToPasswordChangePageListener.php +++ b/src/EventListener/RedirectToPasswordChangePageListener.php @@ -2,14 +2,6 @@ declare(strict_types=1); -/* - * This file is part of terminal42/contao-password-validation. - * - * (c) terminal42 gmbh - * - * @license MIT - */ - namespace Terminal42\PasswordValidationBundle\EventListener; use Contao\CoreBundle\Exception\PageNotFoundException; @@ -19,50 +11,28 @@ use Contao\CoreBundle\Routing\ScopeMatcher; use Contao\FrontendUser; use Contao\PageModel; use Symfony\Component\HttpKernel\Event\RequestEvent; -use Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolverInterface; -use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; +use Symfony\Component\Security\Core\Security; final class RedirectToPasswordChangePageListener { - private $framework; - - private $tokenStorage; - - private $authenticationTrustResolver; - - private $scopeMatcher; - - public function __construct(ContaoFramework $framework, TokenStorageInterface $tokenStorage, AuthenticationTrustResolverInterface $authenticationTrustResolver, ScopeMatcher $scopeMatcher) - { - $this->framework = $framework; - $this->tokenStorage = $tokenStorage; - $this->authenticationTrustResolver = $authenticationTrustResolver; - $this->scopeMatcher = $scopeMatcher; + public function __construct( + private readonly ContaoFramework $framework, + private readonly Security $security, + private readonly ScopeMatcher $scopeMatcher, + ) { } public function __invoke(RequestEvent $event): void { - if (!$this->scopeMatcher->isFrontendMasterRequest($event)) { - return; - } - - $token = $this->tokenStorage->getToken(); - - if (null === $token || $this->authenticationTrustResolver->isAnonymous($token)) { + if (!$this->scopeMatcher->isFrontendMainRequest($event)) { return; } $request = $event->getRequest(); + $user = $this->security->getUser(); $page = $request->attributes->get('pageModel'); - // Check if actual page is available - if (!$page instanceof PageModel) { - return; - } - - $user = $token->getUser(); - - if (!$user instanceof FrontendUser) { + if (!$page instanceof PageModel || !$user instanceof FrontendUser || !$user->pwChange) { return; } @@ -70,12 +40,8 @@ final class RedirectToPasswordChangePageListener $adapter = $this->framework->getAdapter(PageModel::class); $rootPage = $adapter->findByPk($page->rootId); - if (!$user->pwChange) { - return; - } - // Search for password-change page - $pwChangePage = $adapter->findPublishedById($rootPage->pwChangePage); + $pwChangePage = $adapter->findPublishedById($rootPage->pwChangePage ?? 0); if (!$pwChangePage instanceof PageModel) { throw new PageNotFoundException('No password-change page found.'); diff --git a/src/EventListener/SetNewPasswordListener.php b/src/EventListener/SetNewPasswordListener.php index 662cf7a..75b2bf1 100644 --- a/src/EventListener/SetNewPasswordListener.php +++ b/src/EventListener/SetNewPasswordListener.php @@ -2,29 +2,24 @@ declare(strict_types=1); -/* - * This file is part of terminal42/contao-password-validation. - * - * (c) terminal42 gmbh - * - * @license MIT - */ - namespace Terminal42\PasswordValidationBundle\EventListener; -use Contao\CoreBundle\ServiceAnnotation\Hook; +use Contao\CoreBundle\DependencyInjection\Attribute\AsHook; +use Contao\Database\Result; use Contao\MemberModel; -/** - * @Hook("setNewPassword") - */ +#[AsHook('setNewPassword')] final class SetNewPasswordListener { + /** + * @param MemberModel|Result|object $member + */ public function __invoke($member): void { if ($member instanceof MemberModel) { - // We only want to reset "pwChange" if the user changed their password with the change-password module. - // If this hook is called within the save_callback (backend mask), $member is an instance of Database\Result. + // We only want to reset "pwChange" if the user changed their password with the + // change-password module. If this hook is called within the save_callback + // (backend mask), $member is an instance of Database\Result. $member->pwChange = ''; $member->save(); } diff --git a/src/Exception/PasswordValidatorException.php b/src/Exception/PasswordValidatorException.php index 3d9d3c2..31d411e 100644 --- a/src/Exception/PasswordValidatorException.php +++ b/src/Exception/PasswordValidatorException.php @@ -2,14 +2,6 @@ declare(strict_types=1); -/* - * This file is part of terminal42/contao-password-validation. - * - * (c) terminal42 gmbh - * - * @license MIT - */ - namespace Terminal42\PasswordValidationBundle\Exception; class PasswordValidatorException extends \Exception diff --git a/src/Model/PasswordHistory.php b/src/Model/PasswordHistory.php index 46dda71..b4e9f52 100644 --- a/src/Model/PasswordHistory.php +++ b/src/Model/PasswordHistory.php @@ -19,7 +19,7 @@ final class PasswordHistory extends Model { protected static $strTable = 'tl_password_history'; - public static function addLog(string $entity, int $userId, string $hashedPassword): void + public static function addLog(string $entity, int $userId, #[\SensitiveParameter] string $hashedPassword): void { $log = new self(); @@ -31,7 +31,10 @@ final class PasswordHistory extends Model $log->save(); } - public static function findHistory(string $entity, int $userId, int $length = 10, int $offset = 0): ?Collection + /** + * @return Collection|null + */ + public static function findHistory(string $entity, int $userId, int $length = 10, int $offset = 0): Collection|null { return self::findBy( [ @@ -47,11 +50,11 @@ final class PasswordHistory extends Model 'limit' => $length, 'offset' => $offset, 'return' => 'Collection', - ] + ], ); } - public static function findCurrentLog(string $entity, int $userId): ?Model + public static function findCurrentLog(string $entity, int $userId): self|null { return self::findOneBy( [ @@ -64,7 +67,7 @@ final class PasswordHistory extends Model ], [ 'order' => 'tstamp DESC', - ] + ], ); } @@ -73,7 +76,6 @@ final class PasswordHistory extends Model $history = self::findHistory($entity, $userId, 0, $lengthToKeep); if (null !== $history) { - /** @var PasswordHistory $log */ foreach ($history as $log) { $log->delete(); } diff --git a/src/Terminal42PasswordValidationBundle.php b/src/Terminal42PasswordValidationBundle.php index d9e1115..f1dbf5c 100644 --- a/src/Terminal42PasswordValidationBundle.php +++ b/src/Terminal42PasswordValidationBundle.php @@ -2,14 +2,6 @@ declare(strict_types=1); -/* - * This file is part of terminal42/contao-password-validation. - * - * (c) terminal42 gmbh - * - * @license MIT - */ - namespace Terminal42\PasswordValidationBundle; use Symfony\Component\HttpKernel\Bundle\Bundle; diff --git a/src/Validation/PasswordValidatorInterface.php b/src/Validation/PasswordValidatorInterface.php index 5df34e7..8ce15b0 100644 --- a/src/Validation/PasswordValidatorInterface.php +++ b/src/Validation/PasswordValidatorInterface.php @@ -2,14 +2,6 @@ declare(strict_types=1); -/* - * This file is part of terminal42/contao-password-validation. - * - * (c) terminal42 gmbh - * - * @license MIT - */ - namespace Terminal42\PasswordValidationBundle\Validation; use Terminal42\PasswordValidationBundle\Exception\PasswordValidatorException; diff --git a/src/Validation/ValidationConfiguration.php b/src/Validation/ValidationConfiguration.php index 0de52ff..61fb774 100644 --- a/src/Validation/ValidationConfiguration.php +++ b/src/Validation/ValidationConfiguration.php @@ -32,7 +32,7 @@ final class ValidationConfiguration return \array_key_exists($entityName, $this->configurations); } - public function getConfiguration(string $entityName): ?array + public function getConfiguration(string $entityName): array|null { return $this->configurations[$entityName]; } diff --git a/src/Validation/ValidationContext.php b/src/Validation/ValidationContext.php index fed920d..b00c873 100644 --- a/src/Validation/ValidationContext.php +++ b/src/Validation/ValidationContext.php @@ -21,15 +21,11 @@ use ParagonIE\HiddenString\HiddenString; */ final class ValidationContext { - private $userEntity; - private $userId; - private $password; - - public function __construct(string $userEntity, ?int $userId, HiddenString $password) - { - $this->userId = $userId; - $this->password = $password; - $this->userEntity = $userEntity; + public function __construct( + private readonly string $userEntity, + private readonly int|null $userId, + private readonly HiddenString $password, + ) { } public function isFrontendUser(): bool @@ -42,7 +38,7 @@ final class ValidationContext return BackendUser::class === $this->userEntity; } - public function getUserId(): ?int + public function getUserId(): int|null { return $this->userId; } diff --git a/src/Validation/Validator/HaveIBeenPwned.php b/src/Validation/Validator/HaveIBeenPwned.php index a590863..5f11276 100644 --- a/src/Validation/Validator/HaveIBeenPwned.php +++ b/src/Validation/Validator/HaveIBeenPwned.php @@ -2,19 +2,11 @@ declare(strict_types=1); -/* - * This file is part of terminal42/contao-password-validation. - * - * (c) terminal42 gmbh - * - * @license MIT - */ - namespace Terminal42\PasswordValidationBundle\Validation\Validator; -use Contao\System; use Symfony\Contracts\HttpClient\Exception\HttpExceptionInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; +use Symfony\Contracts\Translation\TranslatorInterface; use Terminal42\PasswordValidationBundle\Exception\PasswordValidatorException; use Terminal42\PasswordValidationBundle\Validation\PasswordValidatorInterface; use Terminal42\PasswordValidationBundle\Validation\ValidationConfiguration; @@ -22,13 +14,11 @@ use Terminal42\PasswordValidationBundle\Validation\ValidationContext; final class HaveIBeenPwned implements PasswordValidatorInterface { - private $configuration; - private $client; - - public function __construct(ValidationConfiguration $configuration, HttpClientInterface $client) - { - $this->configuration = $configuration; - $this->client = $client; + public function __construct( + private readonly ValidationConfiguration $configuration, + private readonly HttpClientInterface $client, + private readonly TranslatorInterface $translator, + ) { } public function validate(ValidationContext $context): bool @@ -50,7 +40,7 @@ final class HaveIBeenPwned implements PasswordValidatorInterface try { $response = $this->client->request('GET', 'https://api.pwnedpasswords.com/range/'.$hash05)->getContent(); - } catch (HttpExceptionInterface $e) { + } catch (HttpExceptionInterface) { return true; } @@ -62,20 +52,13 @@ final class HaveIBeenPwned implements PasswordValidatorInterface return $carry; }, - [] + [], ); if (\array_key_exists($hash, $breaches) && ($quantity = $breaches[$hash]) && $quantity > $minDataBreaches) { - throw new PasswordValidatorException(sprintf($this->translate('haveibeenpwned'), $quantity)); + throw new PasswordValidatorException($this->translator->trans('XPT.passwordValidation.haveibeenpwned', [$quantity], 'contao_exception')); } return true; } - - private function translate(string $key) - { - System::loadLanguageFile('exception'); - - return $GLOBALS['TL_LANG']['XPT']['passwordValidation'][$key]; - } } diff --git a/src/Validation/Validator/MaximumLength.php b/src/Validation/Validator/MaximumLength.php index 3d0f01f..faab14b 100644 --- a/src/Validation/Validator/MaximumLength.php +++ b/src/Validation/Validator/MaximumLength.php @@ -2,17 +2,9 @@ declare(strict_types=1); -/* - * This file is part of terminal42/contao-password-validation. - * - * (c) terminal42 gmbh - * - * @license MIT - */ - namespace Terminal42\PasswordValidationBundle\Validation\Validator; -use Contao\System; +use Symfony\Contracts\Translation\TranslatorInterface; use Terminal42\PasswordValidationBundle\Exception\PasswordValidatorException; use Terminal42\PasswordValidationBundle\Validation\PasswordValidatorInterface; use Terminal42\PasswordValidationBundle\Validation\ValidationConfiguration; @@ -20,11 +12,10 @@ use Terminal42\PasswordValidationBundle\Validation\ValidationContext; final class MaximumLength implements PasswordValidatorInterface { - private $configuration; - - public function __construct(ValidationConfiguration $configuration) - { - $this->configuration = $configuration; + public function __construct( + private readonly ValidationConfiguration $configuration, + private readonly TranslatorInterface $translator, + ) { } public function validate(ValidationContext $context): bool @@ -43,16 +34,9 @@ final class MaximumLength implements PasswordValidatorInterface $password = $context->getPassword()->getString(); if (\strlen($password) > $maximumLength) { - throw new PasswordValidatorException(sprintf($this->translate('maxLength'), $maximumLength)); + throw new PasswordValidatorException($this->translator->trans('XPT.passwordValidation.maxLength', [$maximumLength], 'contao_exception')); } return true; } - - private function translate(string $key) - { - System::loadLanguageFile('exception'); - - return $GLOBALS['TL_LANG']['XPT']['passwordValidation'][$key]; - } } diff --git a/src/Validation/Validator/MinimumLength.php b/src/Validation/Validator/MinimumLength.php index c8c3393..1e13e54 100644 --- a/src/Validation/Validator/MinimumLength.php +++ b/src/Validation/Validator/MinimumLength.php @@ -2,17 +2,9 @@ declare(strict_types=1); -/* - * This file is part of terminal42/contao-password-validation. - * - * (c) terminal42 gmbh - * - * @license MIT - */ - namespace Terminal42\PasswordValidationBundle\Validation\Validator; -use Contao\System; +use Symfony\Contracts\Translation\TranslatorInterface; use Terminal42\PasswordValidationBundle\Exception\PasswordValidatorException; use Terminal42\PasswordValidationBundle\Validation\PasswordValidatorInterface; use Terminal42\PasswordValidationBundle\Validation\ValidationConfiguration; @@ -20,11 +12,10 @@ use Terminal42\PasswordValidationBundle\Validation\ValidationContext; final class MinimumLength implements PasswordValidatorInterface { - private $configuration; - - public function __construct(ValidationConfiguration $configuration) - { - $this->configuration = $configuration; + public function __construct( + private readonly ValidationConfiguration $configuration, + private readonly TranslatorInterface $translator, + ) { } public function validate(ValidationContext $context): bool @@ -43,16 +34,9 @@ final class MinimumLength implements PasswordValidatorInterface $password = $context->getPassword()->getString(); if (\strlen($password) < $minimumLength) { - throw new PasswordValidatorException(sprintf($this->translate('minLength'), $minimumLength)); + throw new PasswordValidatorException($this->translator->trans('XPT.passwordValidation.minLength', [$minimumLength], 'contao_exception')); } return true; } - - private function translate(string $key) - { - System::loadLanguageFile('exception'); - - return $GLOBALS['TL_LANG']['XPT']['passwordValidation'][$key]; - } } diff --git a/src/Validation/Validator/PasswordHistory.php b/src/Validation/Validator/PasswordHistory.php index bfc8ffb..b8e3a9c 100644 --- a/src/Validation/Validator/PasswordHistory.php +++ b/src/Validation/Validator/PasswordHistory.php @@ -2,19 +2,11 @@ declare(strict_types=1); -/* - * This file is part of terminal42/contao-password-validation. - * - * (c) terminal42 gmbh - * - * @license MIT - */ - namespace Terminal42\PasswordValidationBundle\Validation\Validator; -use Contao\System; use Contao\User; -use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface; +use Symfony\Component\PasswordHasher\Hasher\PasswordHasherFactoryInterface; +use Symfony\Contracts\Translation\TranslatorInterface; use Terminal42\PasswordValidationBundle\Exception\PasswordValidatorException; use Terminal42\PasswordValidationBundle\Model\PasswordHistory as PasswordHistoryModel; use Terminal42\PasswordValidationBundle\Validation\PasswordValidatorInterface; @@ -23,14 +15,11 @@ use Terminal42\PasswordValidationBundle\Validation\ValidationContext; final class PasswordHistory implements PasswordValidatorInterface { - private $configuration; - - private $encoderFactory; - - public function __construct(ValidationConfiguration $configuration, EncoderFactoryInterface $encoderFactory) - { - $this->configuration = $configuration; - $this->encoderFactory = $encoderFactory; + public function __construct( + private readonly ValidationConfiguration $configuration, + private readonly PasswordHasherFactoryInterface $encoderFactory, + private readonly TranslatorInterface $translator, + ) { } public function validate(ValidationContext $context): bool @@ -62,27 +51,11 @@ final class PasswordHistory implements PasswordValidatorInterface /** @var PasswordHistoryModel $log */ foreach ($history as $log) { - if ($this->verifyPassword((string) $log->password, $password)) { - throw new PasswordValidatorException($this->translate('passwordHistory')); + if ($this->encoderFactory->getPasswordHasher(User::class)->verify((string) $log->password, $password)) { + throw new PasswordValidatorException($this->translator->trans('XPT.passwordValidation.passwordHistory', [], 'contao_exception')); } } return true; } - - private function verifyPassword(string $hashedPassword, string $password) - { - if (version_compare(VERSION, '4.8', '>=')) { - return $this->encoderFactory->getEncoder(User::class)->isPasswordValid($hashedPassword, $password, null); - } - - return password_verify($password, $hashedPassword); - } - - private function translate(string $key) - { - System::loadLanguageFile('exception'); - - return $GLOBALS['TL_LANG']['XPT']['passwordValidation'][$key]; - } } diff --git a/src/Validation/Validator/RequiredCharacters.php b/src/Validation/Validator/RequiredCharacters.php index 4236080..050677d 100644 --- a/src/Validation/Validator/RequiredCharacters.php +++ b/src/Validation/Validator/RequiredCharacters.php @@ -12,8 +12,7 @@ declare(strict_types=1); namespace Terminal42\PasswordValidationBundle\Validation\Validator; -use Contao\StringUtil; -use Contao\System; +use Symfony\Contracts\Translation\TranslatorInterface; use Terminal42\PasswordValidationBundle\Exception\PasswordValidatorException; use Terminal42\PasswordValidationBundle\Validation\PasswordValidatorInterface; use Terminal42\PasswordValidationBundle\Validation\ValidationConfiguration; @@ -21,11 +20,10 @@ use Terminal42\PasswordValidationBundle\Validation\ValidationContext; final class RequiredCharacters implements PasswordValidatorInterface { - private $configuration; - - public function __construct(ValidationConfiguration $configuration) - { - $this->configuration = $configuration; + public function __construct( + private readonly ValidationConfiguration $configuration, + private readonly TranslatorInterface $translator, + ) { } public function validate(ValidationContext $context): bool @@ -59,17 +57,27 @@ final class RequiredCharacters implements PasswordValidatorInterface if ($actual < $minimum) { if ('other' === $category) { $errors[] = new PasswordValidatorException( - sprintf($this->translate('required.other'), $minimum, $configuration['other_chars'] ?? null) + $this->translator->trans( + 'XPT.passwordValidation.required.other', + [$minimum, $configuration['other_chars'] ?? ''], + 'contao_exception', + ), ); continue; } - $errors[] = new PasswordValidatorException(sprintf($this->translate('required.'.$category), $minimum)); + $errors[] = new PasswordValidatorException( + $this->translator->trans( + 'XPT.passwordValidation.required.'.$category, + [$minimum], + 'contao_exception', + ), + ); } } if (\count($errors) > 1) { - throw new PasswordValidatorException(sprintf($this->translate('required.summary'), $require['uppercase'] ?? null, $require['lowercase'] ?? null, $require['numbers'] ?? null, $require['other'] ?? null, $configuration['other_chars'] ?? null)); + throw new PasswordValidatorException($this->translator->trans('XPT.passwordValidation.required.summary', [$require['uppercase'] ?? 0, $require['lowercase'] ?? 0, $require['numbers'] ?? 0, $require['other'] ?? 0, $configuration['other_chars'] ?? ''], 'contao_exception')); } if (\count($errors) > 0) { @@ -79,7 +87,7 @@ final class RequiredCharacters implements PasswordValidatorInterface return true; } - private function countRequirement(string $category, string $string, ValidationContext $context): ?int + private function countRequirement(string $category, string $string, ValidationContext $context): int|null { switch ($category) { case 'lowercase': @@ -93,7 +101,7 @@ final class RequiredCharacters implements PasswordValidatorInterface return \strlen($lowercase) - similar_text($string, $lowercase); case 'numbers': - return \strlen(preg_replace('/\D+/', '', $string)); + return \strlen((string) preg_replace('/\D+/', '', $string)); case 'other': $chars = $this->getRequiredOtherCharactersForRegexp($context); @@ -102,14 +110,14 @@ final class RequiredCharacters implements PasswordValidatorInterface return null; } - return \strlen(preg_replace('/[^'.$chars.']+/', '', $string)); + return \strlen((string) preg_replace('/[^'.$chars.']+/', '', $string)); default: return null; } } - private function getRequiredOtherCharactersForRegexp(ValidationContext $context): ?string + private function getRequiredOtherCharactersForRegexp(ValidationContext $context): string|null { $config = $this->configuration->getConfiguration($context->getUserEntity()); $chars = $config['other_chars'] ?? null; @@ -120,19 +128,10 @@ final class RequiredCharacters implements PasswordValidatorInterface $return = ''; - foreach (array_unique(preg_split('//u', $chars, -1, PREG_SPLIT_NO_EMPTY)) as $char) { + foreach (array_unique(preg_split('//u', (string) $chars, -1, PREG_SPLIT_NO_EMPTY)) as $char) { $return .= '\\'.$char; } return $return; } - - private function translate(string $key) - { - System::loadLanguageFile('exception'); - - [$key1, $key2] = StringUtil::trimsplit('.', $key); - - return $GLOBALS['TL_LANG']['XPT']['passwordValidation'][$key1][$key2]; - } } diff --git a/src/Validation/ValidatorManager.php b/src/Validation/ValidatorManager.php index 5ca9689..4556154 100644 --- a/src/Validation/ValidatorManager.php +++ b/src/Validation/ValidatorManager.php @@ -2,14 +2,6 @@ declare(strict_types=1); -/* - * This file is part of terminal42/contao-password-validation. - * - * (c) terminal42 gmbh - * - * @license MIT - */ - namespace Terminal42\PasswordValidationBundle\Validation; /** @@ -18,9 +10,9 @@ namespace Terminal42\PasswordValidationBundle\Validation; final class ValidatorManager { /** - * @var array + * @var array */ - private $validators = []; + private array $validators; public function __construct(iterable $validators) { @@ -32,12 +24,15 @@ final class ValidatorManager $this->validators[$alias] = $validator; } + /** + * @return array + */ public function getValidatorNames(): array { return array_keys($this->validators); } - public function getValidator($name): ?PasswordValidatorInterface + public function getValidator(string $name): PasswordValidatorInterface|null { return $this->validators[$name] ?? null; }