mirror of
https://github.com/Icinga/icingaweb2.git
synced 2025-09-25 18:59:04 +02:00
Implement 'None Password Policy'-class for consistency
This commit is contained in:
parent
caf29a127d
commit
b618b10ead
@ -5,6 +5,7 @@ namespace Icinga\Forms\Account;
|
||||
|
||||
use Icinga\Application\Config;
|
||||
use Icinga\Application\Hook\PasswordPolicyHook;
|
||||
use Icinga\Application\ProvidedHook\DefaultPasswordPolicy;
|
||||
use Icinga\Authentication\PasswordValidator;
|
||||
use Icinga\Authentication\User\DbUserBackend;
|
||||
use Icinga\Data\Filter\Filter;
|
||||
@ -27,20 +28,10 @@ class ChangePasswordForm extends Form
|
||||
/**
|
||||
* The password policy object
|
||||
*
|
||||
* @var PasswordPolicyHook|null
|
||||
* @var PasswordPolicyHook
|
||||
*/
|
||||
protected ?PasswordPolicyHook $passwordPolicyObject;
|
||||
protected ?PasswordPolicyHook $passwordPolicyObject = null;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param PasswordPolicyHook|null $passwordPolicyObject
|
||||
*/
|
||||
public function __construct(?PasswordPolicyHook $passwordPolicyObject = null)
|
||||
{
|
||||
$this->passwordPolicyObject = $passwordPolicyObject;
|
||||
parent::__construct();
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
@ -54,20 +45,15 @@ class ChangePasswordForm extends Form
|
||||
*/
|
||||
public function createElements(array $formData)
|
||||
{
|
||||
if ($this->passwordPolicyObject === null) {
|
||||
$passwordPolicy = Config::app()->get(
|
||||
'global',
|
||||
'password_policy'
|
||||
);
|
||||
if (isset($passwordPolicy) && class_exists($passwordPolicy)) {
|
||||
$this->passwordPolicyObject = new $passwordPolicy();
|
||||
}
|
||||
}
|
||||
$passwordPolicyDescription = $this->passwordPolicyObject->displayPasswordPolicy();
|
||||
|
||||
if ($this->passwordPolicyObject) {
|
||||
$this->addDescription(
|
||||
$this->passwordPolicyObject->displayPasswordPolicy()
|
||||
);
|
||||
if ($passwordPolicyDescription != '') {
|
||||
$this->addDescription($passwordPolicyDescription);
|
||||
}
|
||||
|
||||
$this->addElement(
|
||||
@ -84,7 +70,9 @@ class ChangePasswordForm extends Form
|
||||
array(
|
||||
'label' => $this->translate('New Password'),
|
||||
'required' => true,
|
||||
'validators' => [new PasswordValidator($this->passwordPolicyObject)]
|
||||
'validators' =>
|
||||
$this->passwordPolicyObject !== null ?
|
||||
[new PasswordValidator($this->passwordPolicyObject)] : [],
|
||||
)
|
||||
);
|
||||
$this->addElement(
|
||||
|
@ -4,6 +4,7 @@
|
||||
namespace Icinga\Forms\Config\General;
|
||||
|
||||
use Icinga\Application\Hook;
|
||||
use Icinga\Application\ProvidedHook\DefaultPasswordPolicy;
|
||||
use Icinga\Web\Form;
|
||||
|
||||
/**
|
||||
@ -35,13 +36,12 @@ class PasswordPolicyConfigForm extends Form
|
||||
'Enforce strong password requirements for new passwords'
|
||||
),
|
||||
'label' => $this->translate('Password Policy'),
|
||||
'multiOptions' => array_merge(
|
||||
['' => $this->translate('No Password Policy')],
|
||||
$passwordPolicies
|
||||
),
|
||||
'value' => DefaultPasswordPolicy::class,
|
||||
'multiOptions' =>$passwordPolicies
|
||||
]
|
||||
);
|
||||
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,6 @@ class UserForm extends RepositoryForm
|
||||
*
|
||||
* @param array $formData The data sent by the user
|
||||
*/
|
||||
|
||||
protected function createInsertElements(array $formData)
|
||||
{
|
||||
$passwordPolicy = Config::app()->get('global', 'password_policy');
|
||||
|
@ -8,6 +8,7 @@ use ErrorException;
|
||||
use Exception;
|
||||
use Icinga\Application\ProvidedHook\DbMigration;
|
||||
use Icinga\Application\ProvidedHook\DefaultPasswordPolicy;
|
||||
use Icinga\Application\ProvidedHook\NonePasswordPolicy;
|
||||
use ipl\I18n\GettextTranslator;
|
||||
use ipl\I18n\StaticTranslator;
|
||||
use LogicException;
|
||||
@ -742,6 +743,7 @@ abstract class ApplicationBootstrap
|
||||
{
|
||||
Hook::register('DbMigration', DbMigration::class, DbMigration::class);
|
||||
Hook::register('passwordpolicy', DefaultPasswordPolicy::class, DefaultPasswordPolicy::class);
|
||||
Hook::register('passwordpolicy', NonePasswordPolicy::class, NonePasswordPolicy::class);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
@ -26,5 +26,5 @@ interface PasswordPolicyHook
|
||||
* @return string|null Returns null if the password is valid,
|
||||
* otherwise returns an error message describing the violations
|
||||
*/
|
||||
public function validatePassword(string $password): ?string;
|
||||
public function validatePassword(string $password): ?array;
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
namespace Icinga\Application\ProvidedHook;
|
||||
|
||||
use Icinga\Application\Hook\PasswordPolicyHook;
|
||||
use ipl\I18n\Translation;
|
||||
|
||||
/**
|
||||
* Default implementation of a password policy
|
||||
@ -17,56 +18,64 @@ use Icinga\Application\Hook\PasswordPolicyHook;
|
||||
*/
|
||||
class DefaultPasswordPolicy implements PasswordPolicyHook
|
||||
{
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
use Translation;
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return 'Default Password Policy';
|
||||
return 'Default';
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function displayPasswordPolicy(): string
|
||||
{
|
||||
$message = (
|
||||
'Password requirements: ' . 'minimum 12 characters, ' .
|
||||
'at least 1 number, ' .
|
||||
'1 special character, ' . 'uppercase and lowercase letters'
|
||||
$message =
|
||||
$this->translate(
|
||||
'Password requirements: minimum 12 characters, at least 1 number, ' .
|
||||
'1 special character, uppercase and lowercase letters.'
|
||||
);
|
||||
return $message;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function validatePassword(string $password): ?string
|
||||
public function validatePassword(string $password): ?array
|
||||
{
|
||||
$violations = [];
|
||||
|
||||
if (strlen($password) < 12) {
|
||||
$violations[] = ('Password must be at least 12 characters long');
|
||||
$violations[] =
|
||||
$this->translate(
|
||||
'Password must be at least 12 characters long'
|
||||
);
|
||||
}
|
||||
|
||||
if (! preg_match('/[0-9]/', $password)) {
|
||||
$violations[] = ('Password must contain at least one number');
|
||||
$violations[] =
|
||||
$this->translate(
|
||||
'Password must contain at least one number'
|
||||
);
|
||||
}
|
||||
|
||||
if (! preg_match('/[^a-zA-Z0-9]/', $password)) {
|
||||
$violations[] = ('Password must contain at least one special character');
|
||||
$violations[] =
|
||||
$this->translate(
|
||||
'Password must contain at least one special character'
|
||||
);
|
||||
}
|
||||
|
||||
if (! preg_match('/[A-Z]/', $password)) {
|
||||
$violations[] = ('Password must contain at least one uppercase letter');
|
||||
$violations[] =
|
||||
$this->translate(
|
||||
'Password must contain at least one uppercase letter'
|
||||
);
|
||||
}
|
||||
|
||||
if (! preg_match('/[a-z]/', $password)) {
|
||||
$violations[] = ('Password must contain at least one lowercase letter');
|
||||
$violations[] =
|
||||
$this->translate(
|
||||
'Password must contain at least one lowercase letter'
|
||||
);
|
||||
}
|
||||
|
||||
if (! empty($violations)) {
|
||||
return implode(", ", $violations);
|
||||
return $violations;
|
||||
}
|
||||
|
||||
return null;
|
||||
|
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
/* Icinga Web 2 | (c) 2025 Icinga GmbH | GPLv2+ */
|
||||
|
||||
namespace Icinga\Application\ProvidedHook;
|
||||
|
||||
use Icinga\Application\Hook\PasswordPolicyHook;
|
||||
|
||||
/**
|
||||
* None Password Policy to validate all passwords
|
||||
*/
|
||||
class NonePasswordPolicy implements PasswordPolicyHook
|
||||
{
|
||||
public function getName(): string
|
||||
{
|
||||
return 'None';
|
||||
}
|
||||
|
||||
public function displayPasswordPolicy(): string
|
||||
{
|
||||
return '';
|
||||
|
||||
}
|
||||
|
||||
public function validatePassword(string $password): ?array
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
@ -9,16 +9,18 @@ use Zend_Validate_Abstract;
|
||||
class PasswordValidator extends Zend_Validate_Abstract
|
||||
{
|
||||
/**
|
||||
* @var PasswordPolicyHook|null
|
||||
* The password policy object
|
||||
*
|
||||
* @var PasswordPolicyHook
|
||||
*/
|
||||
private ?PasswordPolicyHook $passwordPolicyObject;
|
||||
private PasswordPolicyHook $passwordPolicyObject;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param PasswordPolicyHook|null $passwordPolicyObject
|
||||
* @param PasswordPolicyHook $passwordPolicyObject
|
||||
*/
|
||||
public function __construct(?PasswordPolicyHook $passwordPolicyObject = null)
|
||||
public function __construct(PasswordPolicyHook $passwordPolicyObject)
|
||||
{
|
||||
$this->passwordPolicyObject = $passwordPolicyObject;
|
||||
}
|
||||
@ -27,28 +29,17 @@ class PasswordValidator extends Zend_Validate_Abstract
|
||||
* Checks if password matches with password policy
|
||||
* throws a message if not
|
||||
*
|
||||
* If no password policy is set, all passwords are considered valid
|
||||
*
|
||||
* @param mixed $value The password to validate
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
*/
|
||||
public function isValid($value): bool
|
||||
{
|
||||
$this->_messages = [];
|
||||
|
||||
if ($this->passwordPolicyObject === null) {
|
||||
if ($this->passwordPolicyObject->validatePassword($value) === null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$errorMessage = $this->passwordPolicyObject->validatePassword($value);
|
||||
|
||||
if ($errorMessage !== null) {
|
||||
$this->_messages[] = $errorMessage;
|
||||
$this->_messages = $this->passwordPolicyObject->validatePassword($value);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user