feat: complete jobcard operations and access controls
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Domain\User;
|
||||
|
||||
/**
|
||||
* Applies the same password rules to initial credentials and password resets.
|
||||
* It validates only; callers remain responsible for hashing accepted passwords.
|
||||
*/
|
||||
final class PasswordPolicy
|
||||
{
|
||||
public const MINIMUM_LENGTH = 12;
|
||||
|
||||
/** @return array{valid: bool, errors: list<string>} */
|
||||
public function validate(mixed $password): array
|
||||
{
|
||||
$errors = [];
|
||||
if (!is_string($password)) {
|
||||
return ['valid' => false, 'errors' => ['password must be a string']];
|
||||
}
|
||||
|
||||
if (mb_strlen($password) < self::MINIMUM_LENGTH) {
|
||||
$errors[] = 'minimum length';
|
||||
}
|
||||
if (preg_match('/\p{Lu}/u', $password) !== 1) {
|
||||
$errors[] = 'uppercase letter';
|
||||
}
|
||||
if (preg_match('/\p{Ll}/u', $password) !== 1) {
|
||||
$errors[] = 'lowercase letter';
|
||||
}
|
||||
if (preg_match('/\p{N}/u', $password) !== 1) {
|
||||
$errors[] = 'number';
|
||||
}
|
||||
if (preg_match('/[^\p{L}\p{N}\s]/u', $password) !== 1) {
|
||||
$errors[] = 'symbol';
|
||||
}
|
||||
if ($this->isCommonPlaceholder($password)) {
|
||||
$errors[] = 'common placeholder';
|
||||
}
|
||||
|
||||
return ['valid' => $errors === [], 'errors' => $errors];
|
||||
}
|
||||
|
||||
/** @return array{valid: bool, errors: list<string>} */
|
||||
public function validateInitial(mixed $password): array
|
||||
{
|
||||
return $this->validate($password);
|
||||
}
|
||||
|
||||
/** @return array{valid: bool, errors: list<string>} */
|
||||
public function validateReset(mixed $password): array
|
||||
{
|
||||
return $this->validate($password);
|
||||
}
|
||||
|
||||
public function isValid(mixed $password): bool
|
||||
{
|
||||
return $this->validate($password)['valid'];
|
||||
}
|
||||
|
||||
private function isCommonPlaceholder(string $password): bool
|
||||
{
|
||||
$canonical = strtolower($password);
|
||||
$canonical = strtr($canonical, ['@' => 'a', '$' => 's', '0' => 'o']);
|
||||
$canonical = preg_replace('/[^a-z0-9]/', '', $canonical) ?? '';
|
||||
|
||||
return preg_match(
|
||||
'/^(?:password|changeme|welcome|admin|administrator|temporary|temppassword|qwerty|letmein)[0-9]*$/',
|
||||
$canonical,
|
||||
) === 1;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user