-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathPasswordValidator.php
More file actions
114 lines (103 loc) · 3.33 KB
/
PasswordValidator.php
File metadata and controls
114 lines (103 loc) · 3.33 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
<?php
/*
Code modified from https://gitlab.com/garybell/password-validation/ (MIT licensed)
*/
namespace Pdsinterop\PhpSolid;
class PasswordValidator
{
private static string $specialCharacters = ' !"#$%&\'()*+,-./:;<=>?@[\]^_{|}~';
private static string $lowercaseCharacters = 'abcdefghijklmnopqrstuvwxyz';
private static string $uppercaseCharacters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
private static string $numbers = '0123456789';
/**
* The maximum number of times the same character can appear in the password
* @var int
*/
private static int $maxOccurrences = 2;
/**
* Get the base amount of characters from the characters used in the password.
* This is the number of possible characters to pick from in the used character sets
* i.e. 26 for only lower case passwords
* @param $password
* @return int
*/
public static function getBase(string $password): int
{
$characters = str_split($password);
$base = 0;
$hasSpecial = false;
$hasLower = false;
$hasUpper = false;
$hasDigits = false;
foreach ($characters as $character) {
if (!$hasLower && strpos(self::$lowercaseCharacters, $character) !== false) {
$hasLower = true;
$base += strlen(self::$lowercaseCharacters);
}
if (!$hasUpper && strpos(self::$uppercaseCharacters, $character) !== false) {
$hasUpper = true;
$base += strlen(self::$uppercaseCharacters);
}
if (!$hasSpecial && strpos(self::$specialCharacters, $character) !== false) {
$hasSpecial = true;
$base += strlen(self::$specialCharacters);
}
if (!$hasDigits && strpos(self::$numbers, $character) !== false) {
$hasDigits = true;
$base += strlen(self::$numbers);
}
if (
strpos(self::$lowercaseCharacters, $character) === false
&& strpos(self::$uppercaseCharacters, $character) === false
&& strpos(self::$specialCharacters, $character) === false
&& strpos(self::$numbers, $character) === false
) {
$base++;
}
}
return $base;
}
/**
* get the calculated entropy of the password based on the rules for excluding duplicate characters
* If a password is in the banned list, entropy will be 0.
* @see bannedPassords()
* @param string $password
* @param array $bannedPasswords a custom list of passwords to disallow
* @return float
*/
public static function getEntropy(string $password, array $bannedPasswords = []): float
{
if (in_array(strtolower($password), $bannedPasswords)) {
// these are so weak, we just want to outright ban them. Entropy will be 0 for anything in this list.
return 0;
}
$base = self::getBase($password);
$length = self::getLength($password);
$decimalPlaces = 2;
return number_format(log($base ** $length), $decimalPlaces);
}
/**
* Check the length of the password based on known rules
* Characters will only be counted a maximum of 2 times e.g. aaa has length 2
* @param $password
* @return int
*/
public static function getLength(string $password): int
{
$usedCharacters = [];
$characters = str_split($password);
$length = 0;
foreach ($characters as $character)
{
if (array_key_exists($character, $usedCharacters) && $usedCharacters[$character] < self::$maxOccurrences) {
$length++;
$usedCharacters[$character]++;
}
if (!array_key_exists($character, $usedCharacters)) {
$usedCharacters[$character] = 1;
$length++;
}
}
return $length;
}
}