Completed
Push — master ( 42d5cc...b4b3bc )
by Bobby
01:20
created

Validator::validateNotInArray()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8

Duplication

Lines 8
Ratio 100 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 0
Metric Value
dl 8
loc 8
c 0
b 0
f 0
ccs 5
cts 5
cp 1
rs 10
cc 2
nc 2
nop 0
crap 2
1
<?php
2
3
namespace Ballen\Plexity\Support;
4
5
use Ballen\Plexity\Interfaces\PasswordHistoryInterface;
6
use Ballen\Plexity\Plexity;
7
use Ballen\Plexity\Exceptions\ValidationException;
8
9
/**
10
 * Plexity
11
 *
12
 * Plexity (Password Complexity) is a password complexity library that
13
 * enables you to set "rules" for a password (or any other kind of string) that
14
 * you can then check against in your application.
15
 *
16
 * @author Bobby Allen <[email protected]>
17
 * @license http://opensource.org/licenses/MIT
18
 * @link https://github.com/allebb/passplexity
19
 * @link https://bobbyallen.me
20
 *
21
 */
22
class Validator
23
{
24
25
    /**
26
     * RegEx for uppercase character detection.
27
     */
28
    const REGEX_UPPER_CASE = "/[A-Z]/";
29
30
    /**
31
     * RegEx for lowercase character detection.
32
     */
33
    const REGEX_LOWER_CASE = "/[a-z]/";
34
35
    /**
36
     * The Plexity object (contains the validation configuration)
37
     * @var Plexity
38
     */
39
    private $configuration;
40
41
    /**
42
     * Numeric values list
43
     * @var array
44
     */
45
    protected $numbers = [
46
        1,
47
        2,
48
        3,
49
        4,
50
        5,
51
        6,
52
        7,
53
        8,
54
        9,
55
        0
56
    ];
57
58
    /**
59
     * Special Character list
60
     * @see https://www.owasp.org/index.php/Password_special_characters
61
     * @var array
62
     */
63
    protected $specialCharacters = [
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
     * Validates all the configured rules and responds as requested.
100
     * @return boolean
101
     * @throws ValidationException
102
     */
103 28
    public function validate(Plexity $configuration)
104
    {
105 28
        $this->configuration = $configuration;
106 28
        $this->checkMinimumLength();
107 26
        $this->checkMaximumLength();
108 24
        $this->checkLowerCase();
109 22
        $this->checkUpperCase();
110 20
        $this->checkNumericCharacters();
111 18
        $this->checkSpecialCharacters();
112 16
        $this->checkNotIn();
113 14
        return true;
114
    }
115
116
    /**
117
     * Checks the minimum length requirement.
118
     * @throws ValidationException
119
     */
120 28 View Code Duplication
    public function checkMinimumLength()
121
    {
122 28
        if ($this->configuration->rules()->get(Plexity::RULE_LENGTH_MIN) > 0) {
123 6
            if (!$this->validateLengthMin()) {
124 2
                throw new ValidationException('The length does not meet the minimum length requirements.');
125
            }
126
        }
127 26
    }
128
129
    /**
130
     * Checks the minimum maximum length requirement.
131
     * @throws ValidationException
132
     */
133 26 View Code Duplication
    public function checkMaximumLength()
134
    {
135 26
        if ($this->configuration->rules()->get(Plexity::RULE_LENGTH_MAX) > 0) {
136 5
            if (!$this->validateLengthMax()) {
137 2
                throw new ValidationException('The length exceeds the maximum length requirements.');
138
            }
139
        }
140 24
    }
141
142
    /**
143
     * Checks the lowercase character(s) requirement.
144
     * @throws ValidationException
145
     */
146 24 View Code Duplication
    public function checkLowerCase()
147
    {
148 24
        if ($this->configuration->rules()->get(Plexity::RULE_LOWER) > 0) {
149 4
            if (!$this->validateLowerCase()) {
150 2
                throw new ValidationException('The string failed to meet the lower case requirements.');
151
            }
152
        }
153 22
    }
154
155
    /**
156
     * Checks the upper case character(s) requirement.
157
     * @throws ValidationException
158
     */
159 22 View Code Duplication
    public function checkUpperCase()
160
    {
161 22
        if ($this->configuration->rules()->get(Plexity::RULE_UPPER) > 0) {
162 4
            if (!$this->validateUpperCase()) {
163 2
                throw new ValidationException('The string failed to meet the upper case requirements.');
164
            }
165
        }
166 20
    }
167
168
    /**
169
     * Checks the numeric character(s) requirement.
170
     * @throws ValidationException
171
     */
172 20 View Code Duplication
    public function checkNumericCharacters()
173
    {
174 20
        if ($this->configuration->rules()->get(Plexity::RULE_NUMERIC) > 0) {
175 4
            if (!$this->validateNumericCharacters()) {
176 2
                throw new ValidationException('The string failed to meet the numeric character requirements.');
177
            }
178
        }
179 18
    }
180
181
    /**
182
     * Checks the special character(s) requirement.
183
     * @throws ValidationException
184
     */
185 18 View Code Duplication
    public function checkSpecialCharacters()
186
    {
187 18
        if ($this->configuration->rules()->get(Plexity::RULE_SPECIAL) > 0) {
188 4
            if (!$this->validateSpecialCharacters()) {
189 2
                throw new ValidationException('The string failed to meet the special character requirements.');
190
            }
191
        }
192 16
    }
193
194
    /**
195
     * Validates if a string is not in a array (password history database).
196
     * @throws ValidationException
197
     */
198 16
    public function checkNotIn()
199
    {
200
201 16
        if ($this->configuration->rules()->get(Plexity::RULE_NOT_IN) === null) {
202 12
            return true;
203
        }
204
205 4
        if ($this->configuration->rules()->get(Plexity::RULE_NOT_IN) instanceof PasswordHistoryInterface) {
206 2
            if ($this->validateNotInPasswordHistoryImplementation()) {
207 1
                throw new ValidationException('The string exists in the list of disallowed values requirements.');
208
            }
209
        }
210
211 3
        if (is_array($this->configuration->rules()->get(Plexity::RULE_NOT_IN)) && count($this->configuration->rules()->get(Plexity::RULE_NOT_IN)) > 0) {
212 2
            if (!$this->validateNotInArray()) {
213 1
                throw new ValidationException('The string exists in the list of disallowed values requirements.');
214
            }
215
        }
216
217 2
    }
218
219
    /**
220
     * Validates the upper case requirements.
221
     * @return boolean
222
     */
223 4 View Code Duplication
    private function validateUpperCase()
224
    {
225 4
        $occurences = preg_match_all(self::REGEX_UPPER_CASE, $this->configuration->checkString());
226
227 4
        if ($occurences >= $this->configuration->rules()->get(Plexity::RULE_UPPER)) {
228 2
            return true;
229
        }
230
231 2
        return false;
232
    }
233
234
    /**
235
     * Validates the lower case requirements.
236
     * @return boolean
237
     */
238 4 View Code Duplication
    private function validateLowerCase()
239
    {
240 4
        $occurrences = preg_match_all(self::REGEX_LOWER_CASE, $this->configuration->checkString());
241
242 4
        if ($occurrences >= $this->configuration->rules()->get(Plexity::RULE_LOWER)) {
243 2
            return true;
244
        }
245
246 2
        return false;
247
    }
248
249
    /**
250
     * Validates the special character requirements.
251
     * @return boolean
252
     */
253 4 View Code Duplication
    private function validateSpecialCharacters()
254
    {
255 4
        if ($this->countOccurrences($this->specialCharacters,
256 4
                $this->configuration->checkString()) >= $this->configuration->rules()->get(Plexity::RULE_SPECIAL)) {
257 2
            return true;
258
        }
259 2
        return false;
260
    }
261
262
    /**
263
     * Validates the numeric case requirements.
264
     * @return boolean
265
     */
266 4 View Code Duplication
    private function validateNumericCharacters()
267
    {
268 4
        if ($this->countOccurrences($this->numbers,
269 4
                $this->configuration->checkString()) >= $this->configuration->rules()->get(Plexity::RULE_NUMERIC)) {
270 2
            return true;
271
        }
272 2
        return false;
273
    }
274
275
    /**
276
     * Validates the minimum string length requirements.
277
     * @return boolean
278
     */
279 6 View Code Duplication
    private function validateLengthMin()
280
    {
281 6
        if (strlen($this->configuration->checkString()) >= $this->configuration->rules()->get(Plexity::RULE_LENGTH_MIN)) {
282 4
            return true;
283
        }
284 2
        return false;
285
    }
286
287
    /**
288
     * Validates the maximum string length requirements.
289
     * @return boolean
290
     */
291 5 View Code Duplication
    private function validateLengthMax()
292
    {
293 5
        if (strlen($this->configuration->checkString()) <= $this->configuration->rules()->get(Plexity::RULE_LENGTH_MAX)) {
294 3
            return true;
295
        }
296 2
        return false;
297
    }
298
299
    /**
300
     * Validates the not_in requirements against a simple array.
301
     * @return boolean
302
     */
303 2 View Code Duplication
    private function validateNotInArray()
304
    {
305 2
        if (in_array($this->configuration->checkString(),
306 2
            (array)$this->configuration->rules()->get(Plexity::RULE_NOT_IN))) {
307 1
            return false;
308
        }
309 1
        return true;
310
    }
311
312
    /**
313
     * Validates the not_in requirements against an implementation of PasswordHistoryInterface.
314
     * @return boolean
315
     */
316 2
    private function validateNotInPasswordHistoryImplementation()
317
    {
318 2
        return ($this->configuration->rules()->get(Plexity::RULE_NOT_IN))->checkHistory($this->configuration->checkString());
319
    }
320
321
    /**
322
     * Count the number of occurrences of a character or string in a string.
323
     * @param array $needles The character/string to count occurrences of.
324
     * @param string $haystack The string to check against.
325
     * @return int The number of occurrences.
326
     */
327 8
    private function countOccurrences(array $needles, $haystack)
328
    {
329 8
        $count = 0;
330 8
        foreach ($needles as $char) {
331 8
            $count += substr_count($haystack, $char);
332
        }
333 8
        return $count;
334
    }
335
}
336