1 <?php
2
3 4 5 6 7
8 class rex_password_policy
9 {
10 private $options;
11
12 public function __construct(array $options)
13 {
14 $this->options = $options;
15 }
16
17 18 19 20 21 22 23 24
25 public function check($password, $id = null)
26 {
27 if ($this->isValid($password)) {
28 return true;
29 }
30
31 return rex_i18n::msg('password_invalid', $this->getRule());
32 }
33
34 protected function getRule()
35 {
36 $parts = [];
37
38 foreach ($this->options as $key => $options) {
39 if (isset($options['min'], $options['max'])) {
40 $constraint = rex_i18n::msg('password_rule_between', $options['min'], $options['max']);
41 } elseif (isset($options['max'])) {
42 $constraint = rex_i18n::msg('password_rule_max', $options['max']);
43 } else {
44 $constraint = rex_i18n::msg('password_rule_min', $options['min']);
45 }
46
47 $parts[] = rex_i18n::msg('password_rule_'.$key, $constraint);
48 }
49
50 return implode('; ', $parts);
51 }
52
53 protected function isValid($password)
54 {
55 foreach ($this->options as $key => $options) {
56 switch ($key) {
57 case 'length':
58 $count = mb_strlen($password);
59 break;
60 case 'letter':
61 $count = preg_match_all('/[a-zA-Z]/', $password);
62 break;
63 case 'uppercase':
64 $count = preg_match_all('/[A-Z]/', $password);
65 break;
66 case 'lowercase':
67 $count = preg_match_all('/[a-z]/', $password);
68 break;
69 case 'digit':
70 $count = preg_match_all('/[0-9]/', $password);
71 break;
72 case 'symbol':
73 $count = preg_match_all('/[^a-zA-Z0-9]/', $password);
74 break;
75
76 default:
77 throw new rex_exception(sprintf('Unknown password_policy key "%s".', $key));
78 }
79
80 if (!$this->matchesCount($count, $options)) {
81 return false;
82 }
83 }
84
85 return true;
86 }
87
88 protected function matchesCount($count, array $options)
89 {
90 if (isset($options['min']) && $count < $options['min']) {
91 return false;
92 }
93
94 if (isset($options['max']) && $count > $options['max']) {
95 return false;
96 }
97
98 return true;
99 }
100 }
101