TYPO3 v15 dev-main snapshot ()
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Core\PasswordPolicy\Event;
|
||||
|
||||
use TYPO3\CMS\Core\PasswordPolicy\Validator\Dto\ContextData;
|
||||
|
||||
/**
|
||||
* Event is dispatched before the `ContextData` DTO is passed to the password policy validator.
|
||||
*
|
||||
* Note, that the `$userData` array will include user data available from the initiating class only.
|
||||
* Event listeners should therefore always consider the initiating class name when accessing data
|
||||
* from `getUserData()`.
|
||||
*/
|
||||
final readonly class EnrichPasswordValidationContextDataEvent
|
||||
{
|
||||
public function __construct(
|
||||
private ContextData $contextData,
|
||||
private array $userData,
|
||||
private string $initiatingClass
|
||||
) {}
|
||||
|
||||
public function getContextData(): ContextData
|
||||
{
|
||||
return $this->contextData;
|
||||
}
|
||||
|
||||
public function getUserData(): array
|
||||
{
|
||||
return $this->userData;
|
||||
}
|
||||
|
||||
public function getInitiatingClass(): string
|
||||
{
|
||||
return $this->initiatingClass;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Core\PasswordPolicy\Generator;
|
||||
|
||||
use Symfony\Component\DependencyInjection\Attribute\Autoconfigure;
|
||||
use TYPO3\CMS\Core\Crypto\Random;
|
||||
use TYPO3\CMS\Core\Exception\InvalidPasswordRulesException;
|
||||
|
||||
/**
|
||||
* @internal only to be used within ext:core, not part of TYPO3 Core API.
|
||||
*/
|
||||
#[Autoconfigure(public: true)]
|
||||
final readonly class PasswordGenerator implements PasswordGeneratorInterface
|
||||
{
|
||||
public function __construct(private Random $random) {}
|
||||
|
||||
/**
|
||||
* @throws InvalidPasswordRulesException
|
||||
*/
|
||||
public function generate(array $options): string
|
||||
{
|
||||
return $this->random->generateRandomPassword($options);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Core\PasswordPolicy\Generator;
|
||||
|
||||
use TYPO3\CMS\Core\Exception\InvalidPasswordRulesException;
|
||||
|
||||
/**
|
||||
* This is an interface that has to be used by all password generators.
|
||||
*
|
||||
* Each password generator needs to implement the generate method that returns the generated password.
|
||||
* In case an invalid option/configuration is passed to the generator, an InvalidPasswordRulesException
|
||||
* or LogicException needs to be thrown.
|
||||
*/
|
||||
interface PasswordGeneratorInterface
|
||||
{
|
||||
/**
|
||||
* @throws InvalidPasswordRulesException
|
||||
*/
|
||||
public function generate(array $options): string;
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Core\PasswordPolicy;
|
||||
|
||||
use TYPO3\CMS\Core\PasswordPolicy\Validator\AbstractPasswordValidator;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* Password policy class which holds information about configured password validators and password requirements
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class PasswordPolicy
|
||||
{
|
||||
/**
|
||||
* @var AbstractPasswordValidator[]
|
||||
*/
|
||||
protected array $validators = [];
|
||||
|
||||
/**
|
||||
* @param array<class-string<AbstractPasswordValidator>, array<string, mixed>> $validators
|
||||
*/
|
||||
public function __construct(array $validators, protected PasswordPolicyAction $action)
|
||||
{
|
||||
foreach ($validators as $validatorClassName => $validatorSettings) {
|
||||
// Exclude validator if current action is defined as excludeAction
|
||||
if (in_array($action, $validatorSettings['excludeActions'] ?? [], true)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->validators[] = GeneralUtility::makeInstance(
|
||||
$validatorClassName,
|
||||
$validatorSettings['options'] ?? []
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function getAction(): PasswordPolicyAction
|
||||
{
|
||||
return $this->action;
|
||||
}
|
||||
|
||||
public function hasValidators(): bool
|
||||
{
|
||||
return !empty($this->validators);
|
||||
}
|
||||
|
||||
public function getValidators(): array
|
||||
{
|
||||
return $this->validators;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array with requirements (e.g. ["Password must at least contain one char"]) for all
|
||||
* configured password validators. The structure of the array is as following:
|
||||
*
|
||||
* ['classId.validatorId' => 'Requirement text']
|
||||
*/
|
||||
public function getRequirements(): array
|
||||
{
|
||||
$requirements = [];
|
||||
foreach ($this->validators as $validator) {
|
||||
$requirements = array_merge($requirements, $validator->getRequirements());
|
||||
}
|
||||
|
||||
return $requirements;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Core\PasswordPolicy;
|
||||
|
||||
/**
|
||||
* This class contains actions which are used in password policy validators
|
||||
*/
|
||||
enum PasswordPolicyAction: string
|
||||
{
|
||||
case UPDATE_USER_PASSWORD = 'updateUserPassword';
|
||||
case UPDATE_USER_PASSWORD_SWITCH_USER_MODE = 'updateUserPasswordSwitchUserMode';
|
||||
case NEW_USER_PASSWORD = 'newUserPassword';
|
||||
case UPDATE_INSTALL_TOOL_PASSWORD = 'updateInstallToolPassword';
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Core\PasswordPolicy;
|
||||
|
||||
use TYPO3\CMS\Core\PasswordPolicy\Validator\Dto\ContextData;
|
||||
|
||||
/**
|
||||
* Validates a password using validators configured in $GLOBALS['TYPO3_CONF_VARS']['SYS']['passwordPolicies'].
|
||||
* The class must be instantiated with an action (see PasswordPolicyAction) and a password policy name.
|
||||
*/
|
||||
class PasswordPolicyValidator
|
||||
{
|
||||
protected ?PasswordPolicy $passwordPolicy = null;
|
||||
protected array $validationErrors = [];
|
||||
|
||||
public function __construct(PasswordPolicyAction $action, string $passwordPolicy = 'default')
|
||||
{
|
||||
$passwordPolicies = $GLOBALS['TYPO3_CONF_VARS']['SYS']['passwordPolicies'] ?? [];
|
||||
if (isset($passwordPolicies[$passwordPolicy])) {
|
||||
$this->passwordPolicy = new PasswordPolicy(
|
||||
$passwordPolicies[$passwordPolicy]['validators'] ?? [],
|
||||
$action,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns, if the given password meets all requirements defined by configured password policy validators.
|
||||
* If no password policy is set or the password policy has no validators, the given password is considered
|
||||
* as valid.
|
||||
*
|
||||
* @param string $password The password to validate
|
||||
* @param ContextData|null $contextData ContextData for usage in additional checks (e.g. password must not contain users firstname).
|
||||
*/
|
||||
public function isValidPassword(string $password, ?ContextData $contextData = null): bool
|
||||
{
|
||||
if (!$this->isEnabled()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$isValid = true;
|
||||
foreach ($this->passwordPolicy->getValidators() as $validator) {
|
||||
if (!$validator->validate($password, $contextData)) {
|
||||
$this->validationErrors = array_merge($this->validationErrors, $validator->getErrorMessages());
|
||||
$isValid = false;
|
||||
}
|
||||
}
|
||||
|
||||
return $isValid;
|
||||
}
|
||||
|
||||
public function isEnabled(): bool
|
||||
{
|
||||
return $this->passwordPolicy !== null && $this->passwordPolicy->hasValidators();
|
||||
}
|
||||
|
||||
public function hasRequirements(): bool
|
||||
{
|
||||
return !empty($this->getRequirements());
|
||||
}
|
||||
|
||||
public function getRequirements(): array
|
||||
{
|
||||
return $this->passwordPolicy ? $this->passwordPolicy->getRequirements() : [];
|
||||
}
|
||||
|
||||
public function getValidationErrors(): array
|
||||
{
|
||||
return $this->validationErrors;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Core\PasswordPolicy;
|
||||
|
||||
use TYPO3\CMS\Core\PasswordPolicy\Validator\Dto\ContextData;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
final readonly class PasswordService
|
||||
{
|
||||
/**
|
||||
* @return array<string, string>
|
||||
*/
|
||||
public function getValidationErrorsForInstallToolUpdate(#[\SensitiveParameter] string $password): array
|
||||
{
|
||||
return $this->getValidationErrorsForPolicyAction(
|
||||
$password,
|
||||
'installTool',
|
||||
PasswordPolicyAction::UPDATE_INSTALL_TOOL_PASSWORD,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $password The password to validate
|
||||
* @param string $passwordPolicyUsageContext Refers to a section in $GLOBALS['TYPO3_CONF_VARS']['SYS']['passwordPolicies'][$passwordPolicyUsage]['validators'][...]
|
||||
* @param PasswordPolicyAction $passwordPolicyAction Policy action to perform (to indicate the action like "update Install Tool password")
|
||||
* @param ContextData|null $contextData Optional context data (for example, previous/current password(s)) used within validators
|
||||
*/
|
||||
public function getValidationErrorsForPolicyAction(
|
||||
#[\SensitiveParameter]
|
||||
string $password,
|
||||
string $passwordPolicyUsageContext,
|
||||
PasswordPolicyAction $passwordPolicyAction,
|
||||
?ContextData $contextData = null,
|
||||
): array {
|
||||
$passwordPolicyValidator = GeneralUtility::makeInstance(
|
||||
PasswordPolicyValidator::class,
|
||||
$passwordPolicyAction,
|
||||
$passwordPolicyUsageContext,
|
||||
);
|
||||
$passwordPolicyValidator->isValidPassword($password, $contextData);
|
||||
return $passwordPolicyValidator->getValidationErrors();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Core\PasswordPolicy\Validator;
|
||||
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use TYPO3\CMS\Core\Http\ApplicationType;
|
||||
use TYPO3\CMS\Core\Localization\LanguageService;
|
||||
use TYPO3\CMS\Core\Localization\LanguageServiceFactory;
|
||||
use TYPO3\CMS\Core\PasswordPolicy\Validator\Dto\ContextData;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* Abstract password validator class, which all TYPO3 password validators must extend.
|
||||
*/
|
||||
abstract class AbstractPasswordValidator
|
||||
{
|
||||
private array $requirements = [];
|
||||
private array $errorMessages = [];
|
||||
|
||||
public function __construct(protected array $options = [])
|
||||
{
|
||||
$this->initializeRequirements();
|
||||
}
|
||||
|
||||
/**
|
||||
* Function must be overwritten by extending classes in order to add requirements.
|
||||
* Use `$this->addRequirement(string $identifier, string $message);` to add a requirement.
|
||||
*/
|
||||
public function initializeRequirements(): void {}
|
||||
|
||||
/**
|
||||
* Validates the given password. Function must be overwritten by extending classes.
|
||||
* If validation is considered as failed, use `addErrorMessage(string $identifier, string $errorMessage)`
|
||||
* to add an error message and return `false`.
|
||||
*
|
||||
* @param string $password The password to validate
|
||||
* @param ContextData|null $contextData ContextData for usage in additional checks (e.g. password must not contain users firstname).
|
||||
*/
|
||||
public function validate(string $password, ?ContextData $contextData = null): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all requirements
|
||||
*/
|
||||
final public function getRequirements(): array
|
||||
{
|
||||
return array_map(htmlspecialchars(...), $this->requirements);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a requirement with the given identifier and message.
|
||||
*
|
||||
* @param string $identifier Unique identifier for requirement
|
||||
* @param string $message Message describing the requirement (e.g. "At least one digit")
|
||||
*/
|
||||
final protected function addRequirement(string $identifier, string $message): void
|
||||
{
|
||||
$classIdentifier = $this->getClassId();
|
||||
$this->requirements[$classIdentifier . $identifier] = $message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all error messages
|
||||
*/
|
||||
final public function getErrorMessages(): array
|
||||
{
|
||||
return $this->errorMessages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an validation error message with the given identifier and message.
|
||||
*
|
||||
* @param string $identifier Unique identifier for error message
|
||||
* @param string $errorMessage Message describing the error (e.g. "The password must at least contain one digit")
|
||||
*/
|
||||
final protected function addErrorMessage(string $identifier, string $errorMessage): void
|
||||
{
|
||||
$classIdentifier = $this->getClassId();
|
||||
$this->errorMessages[$classIdentifier . $identifier] = $errorMessage;
|
||||
}
|
||||
|
||||
private function getClassId(): string
|
||||
{
|
||||
$classParts = explode('\\', static::class);
|
||||
return lcfirst(end($classParts)) . '.';
|
||||
}
|
||||
|
||||
protected function getLanguageService(): LanguageService
|
||||
{
|
||||
$request = $GLOBALS['TYPO3_REQUEST'] ?? null;
|
||||
if ($request instanceof ServerRequestInterface && ApplicationType::fromRequest($request)->isFrontend()) {
|
||||
$languageServiceFactory = GeneralUtility::makeInstance(LanguageServiceFactory::class);
|
||||
return $languageServiceFactory->createFromSiteLanguage($request->getAttribute('language')
|
||||
?? $request->getAttribute('site')->getDefaultLanguage());
|
||||
}
|
||||
|
||||
if (($GLOBALS['LANG'] ?? null) instanceof LanguageService) {
|
||||
return $GLOBALS['LANG'];
|
||||
}
|
||||
|
||||
$languageServiceFactory = GeneralUtility::makeInstance(LanguageServiceFactory::class);
|
||||
return $languageServiceFactory->createFromUserPreferences($GLOBALS['BE_USER'] ?? null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Core\PasswordPolicy\Validator;
|
||||
|
||||
use TYPO3\CMS\Core\PasswordPolicy\Validator\Dto\ContextData;
|
||||
|
||||
/**
|
||||
* Configurable TYPO3 core password validator which can validate, that a password has:
|
||||
*
|
||||
* - A minimum length
|
||||
* - At least one upper case char
|
||||
* - At least one lower case char
|
||||
* - At least one digit
|
||||
* - At least one special char
|
||||
*
|
||||
* @internal only to be used within ext:core, not part of TYPO3 Core API.
|
||||
*/
|
||||
class CorePasswordValidator extends AbstractPasswordValidator
|
||||
{
|
||||
public function validate(string $password, ?ContextData $contextData = null): bool
|
||||
{
|
||||
$isValid = true;
|
||||
$lang = $this->getLanguageService();
|
||||
|
||||
if (strlen($password) < $this->getMinLength()) {
|
||||
$this->addErrorMessage(
|
||||
'minimumLength',
|
||||
sprintf(
|
||||
$lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_password_policy.xlf:error.minimumLength'),
|
||||
$this->getMinLength()
|
||||
)
|
||||
);
|
||||
$isValid = false;
|
||||
}
|
||||
|
||||
if ($this->isCheckEnabled('upperCaseCharacterRequired')
|
||||
&& !$this->evaluatePasswordRequirement($password, 'upperCaseCharacterRequired')
|
||||
) {
|
||||
$this->addErrorMessage(
|
||||
'upperCaseCharacterRequired',
|
||||
$lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_password_policy.xlf:error.upperCaseCharacterRequired')
|
||||
);
|
||||
$isValid = false;
|
||||
}
|
||||
|
||||
if ($this->isCheckEnabled('lowerCaseCharacterRequired')
|
||||
&& !$this->evaluatePasswordRequirement($password, 'lowerCaseCharacterRequired')
|
||||
) {
|
||||
$this->addErrorMessage(
|
||||
'lowerCaseCharacterRequired',
|
||||
$lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_password_policy.xlf:error.lowerCaseCharacterRequired')
|
||||
);
|
||||
$isValid = false;
|
||||
}
|
||||
|
||||
if ($this->isCheckEnabled('digitCharacterRequired')
|
||||
&& !$this->evaluatePasswordRequirement($password, 'digitCharacterRequired')
|
||||
) {
|
||||
$this->addErrorMessage(
|
||||
'digitCharacterRequired',
|
||||
$lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_password_policy.xlf:error.digitCharacterRequired')
|
||||
);
|
||||
$isValid = false;
|
||||
}
|
||||
|
||||
if ($this->isCheckEnabled('specialCharacterRequired')
|
||||
&& !$this->evaluatePasswordRequirement($password, 'specialCharacterRequired')
|
||||
) {
|
||||
$this->addErrorMessage(
|
||||
'specialCharacterRequired',
|
||||
$lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_password_policy.xlf:error.specialCharacterRequired')
|
||||
);
|
||||
$isValid = false;
|
||||
}
|
||||
|
||||
return $isValid;
|
||||
}
|
||||
|
||||
public function initializeRequirements(): void
|
||||
{
|
||||
$lang = $this->getLanguageService();
|
||||
$this->addRequirement(
|
||||
'minimumLength',
|
||||
sprintf(
|
||||
$lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_password_policy.xlf:requirement.minimumLength'),
|
||||
$this->getMinLength()
|
||||
),
|
||||
);
|
||||
|
||||
if ($this->isCheckEnabled('upperCaseCharacterRequired')) {
|
||||
$this->addRequirement(
|
||||
'upperCaseCharacterRequired',
|
||||
$lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_password_policy.xlf:requirement.upperCaseCharacterRequired')
|
||||
);
|
||||
}
|
||||
|
||||
if ($this->isCheckEnabled('lowerCaseCharacterRequired')) {
|
||||
$this->addRequirement(
|
||||
'lowerCaseCharacterRequired',
|
||||
$lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_password_policy.xlf:requirement.lowerCaseCharacterRequired')
|
||||
);
|
||||
}
|
||||
|
||||
if ($this->isCheckEnabled('digitCharacterRequired')) {
|
||||
$this->addRequirement(
|
||||
'digitCharacterRequired',
|
||||
$lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_password_policy.xlf:requirement.digitCharacterRequired')
|
||||
);
|
||||
}
|
||||
|
||||
if ($this->isCheckEnabled('specialCharacterRequired')) {
|
||||
$this->addRequirement(
|
||||
'specialCharacterRequired',
|
||||
$lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_password_policy.xlf:requirement.specialCharacterRequired')
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private function getMinLength(): int
|
||||
{
|
||||
return (int)($this->options['minimumLength'] ?? 8);
|
||||
}
|
||||
|
||||
private function isCheckEnabled(string $checkIdentifier): bool
|
||||
{
|
||||
return $this->options[$checkIdentifier] ?? false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluates the password complexity for the given check
|
||||
*/
|
||||
private function evaluatePasswordRequirement(string $password, string $requirement): bool
|
||||
{
|
||||
$result = true;
|
||||
|
||||
$patterns = [
|
||||
'upperCaseCharacterRequired' => '/[A-Z]/',
|
||||
'lowerCaseCharacterRequired' => '/[a-z]/',
|
||||
'digitCharacterRequired' => '/[0-9]/',
|
||||
'specialCharacterRequired' => '/[^0-9a-z]/i',
|
||||
];
|
||||
|
||||
if (isset($patterns[$requirement]) && !preg_match($patterns[$requirement], $password) > 0) {
|
||||
$result = false;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Core\PasswordPolicy\Validator\Dto;
|
||||
|
||||
/**
|
||||
* Class with context data used in password validators. Uses internally an array with key/value pairs to store data.
|
||||
* Extensions authors using this class should use `setData()` and `getData()` to write or read custom data used in
|
||||
* custom password validators.
|
||||
*
|
||||
* @internal only to be used within ext:core, not part of TYPO3 Core API.
|
||||
*/
|
||||
class ContextData
|
||||
{
|
||||
protected array $data = [];
|
||||
|
||||
public function __construct(
|
||||
string $loginMode = 'BE',
|
||||
string $currentPasswordHash = '',
|
||||
string $newUsername = '',
|
||||
string $newUserFirstName = '',
|
||||
string $newUserLastName = '',
|
||||
string $newUserFullName = '',
|
||||
) {
|
||||
$this->data['loginMode'] = $loginMode;
|
||||
$this->data['currentPasswordHash'] = $currentPasswordHash;
|
||||
$this->data['newUsername'] = $newUsername;
|
||||
$this->data['newUserFirstName'] = $newUserFirstName;
|
||||
$this->data['newUserLastName'] = $newUserLastName;
|
||||
$this->data['newUserFullName'] = $newUserFullName;
|
||||
}
|
||||
|
||||
public function getLoginMode(): string
|
||||
{
|
||||
return $this->getData('loginMode');
|
||||
}
|
||||
|
||||
public function getCurrentPasswordHash(): string
|
||||
{
|
||||
return $this->getData('currentPasswordHash');
|
||||
}
|
||||
|
||||
public function getNewUsername(): string
|
||||
{
|
||||
return $this->getData('newUsername');
|
||||
}
|
||||
|
||||
public function getNewUserFirstName(): string
|
||||
{
|
||||
return $this->getData('newUserFirstName');
|
||||
}
|
||||
|
||||
public function getNewUserLastName(): string
|
||||
{
|
||||
return $this->getData('newUserLastName');
|
||||
}
|
||||
|
||||
public function getNewUserFullName(): string
|
||||
{
|
||||
return $this->getData('newUserFullName');
|
||||
}
|
||||
|
||||
public function getData(string $key): string
|
||||
{
|
||||
return $this->data[$key] ?? '';
|
||||
}
|
||||
|
||||
public function setData(string $key, string $value): void
|
||||
{
|
||||
$this->data[$key] = $value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Core\PasswordPolicy\Validator;
|
||||
|
||||
use TYPO3\CMS\Core\Crypto\PasswordHashing\InvalidPasswordHashException;
|
||||
use TYPO3\CMS\Core\Crypto\PasswordHashing\PasswordHashFactory;
|
||||
use TYPO3\CMS\Core\PasswordPolicy\Validator\Dto\ContextData;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* This validator checks, if the given password matches the current user password
|
||||
*
|
||||
* @internal only to be used within ext:core, not part of TYPO3 Core API.
|
||||
*/
|
||||
class NotCurrentPasswordValidator extends AbstractPasswordValidator
|
||||
{
|
||||
public function validate(string $password, ?ContextData $contextData = null): bool
|
||||
{
|
||||
if (!$contextData) {
|
||||
throw new \RuntimeException('ContextData must be supplied to validator.', 1662808782);
|
||||
}
|
||||
|
||||
if (in_array($contextData->getLoginMode(), ['FE', 'BE'], true)) {
|
||||
$isValid = !$this->isCurrentPassword($password, $contextData);
|
||||
} else {
|
||||
throw new \RuntimeException('Unsupported loginMode provided. Ensure, that loginMode is either "FE" or "BE".', 1649846004);
|
||||
}
|
||||
|
||||
return $isValid;
|
||||
}
|
||||
|
||||
public function initializeRequirements(): void
|
||||
{
|
||||
$this->addRequirement(
|
||||
'notCurrentPassword',
|
||||
$this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_password_policy.xlf:requirement.notCurrentPassword')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if the hash of the given password equals the hash of the current password
|
||||
*/
|
||||
protected function isCurrentPassword(string $password, ContextData $contextData): bool
|
||||
{
|
||||
$result = false;
|
||||
$saltFactory = GeneralUtility::makeInstance(PasswordHashFactory::class);
|
||||
try {
|
||||
$hashInstance = $saltFactory->get($contextData->getCurrentPasswordHash(), $contextData->getLoginMode());
|
||||
$result = $hashInstance->checkPassword(
|
||||
$password,
|
||||
$contextData->getCurrentPasswordHash()
|
||||
);
|
||||
} catch (InvalidPasswordHashException $e) {
|
||||
// Since the password will be updated, we silently ignore, if current password hash can not be checked
|
||||
}
|
||||
|
||||
if ($result) {
|
||||
$this->addErrorMessage(
|
||||
'notCurrentPassword',
|
||||
$this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_password_policy.xlf:error.notCurrentPassword')
|
||||
);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user