Passed
Pull Request — master (#5)
by
unknown
11:04
created

Validator::setLocale()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 1
Metric Value
eloc 1
c 1
b 0
f 1
dl 0
loc 2
ccs 0
cts 0
cp 0
rs 10
cc 1
nc 1
nop 1
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/plexity
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
     * Message list of defined iso code
43
     */
44
    private $locale = 'en';
45
46
    /**
47
     * messages list
48
     */
49
    private $messages;
50
51
    /**
52
     * Numeric values list
53
     * @var array<int>
54
     */
55
    protected $numbers = [
56
        1,
57
        2,
58
        3,
59
        4,
60
        5,
61
        6,
62
        7,
63
        8,
64
        9,
65
        0
66
    ];
67
68
    /**
69
     * Special Character list
70
     * @see https://www.owasp.org/index.php/Password_special_characters
71
     * @var array<string>
72
     */
73
    protected $specialCharacters = [
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 31
        '|',
105
        '}',
106 31
        '~',
107 31
    ];
108 29
109 27
    /**
110 25
     * Validates all the configured rules and responds as requested.
111 23
     * @return boolean
112 20
     * @throws ValidationException
113 18
     */
114 16
    public function validate(Plexity $configuration)
115
    {
116
117
        $tmpMessageObj = 'Ballen\\Plexity\\Data\\Locale\\Validator\\' . $this->locale;
118
        if(!class_exists($tmpMessageObj))
119
            throw new ValidationException('Locale class not found.');
120
121
        $this->messages = $tmpMessageObj::$messages;
0 ignored issues
show
Bug introduced by
The property messages does not exist on string.
Loading history...
122 31
123
        $this->configuration = $configuration;
124 31
        $this->checkMinimumLength();
125 6
        $this->checkMaximumLength();
126 2
        $this->checkLowerCase();
127
        $this->checkUpperCase();
128
        $this->checkNumericCharacters();
129
        $this->checkSpecialCharacters();
130
        $this->checkNotIn();
131
        return true;
132
    }
133
134
    /**
135
     * Checks the minimum length requirement.
136 29
     * @return void
137
     * @throws ValidationException
138 29
     */
139 5
    public function checkMinimumLength()
140 2
    {
141
        if ($this->configuration->rules()->get(Plexity::RULE_LENGTH_MIN) > 0) {
142
            if (!$this->validateLengthMin()) {
143
                throw new ValidationException('The length does not meet the minimum length requirements.');
144
            }
145
        }
146
    }
147
148
    /**
149
     * Checks the minimum maximum length requirement.
150 27
     * @return void
151
     * @throws ValidationException
152 27
     */
153 4
    public function checkMaximumLength()
154 2
    {
155
        if ($this->configuration->rules()->get(Plexity::RULE_LENGTH_MAX) > 0) {
156
            if (!$this->validateLengthMax()) {
157
                throw new ValidationException($this->messages[__FUNCTION__]);
158
            }
159
        }
160
    }
161
162
    /**
163
     * Checks the lowercase character(s) requirement.
164 25
     * @return void
165
     * @throws ValidationException
166 25
     */
167 4
    public function checkLowerCase()
168 2
    {
169
        if ($this->configuration->rules()->get(Plexity::RULE_LOWER) > 0) {
170
            if (!$this->validateLowerCase()) {
171
                throw new ValidationException($this->messages[__FUNCTION__]);
172
            }
173
        }
174
    }
175
176
    /**
177
     * Checks the upper case character(s) requirement.
178 23
     * @return void
179
     * @throws ValidationException
180 23
     */
181 6
    public function checkUpperCase()
182 3
    {
183
        if ($this->configuration->rules()->get(Plexity::RULE_UPPER) > 0) {
184
            if (!$this->validateUpperCase()) {
185
                throw new ValidationException($this->messages[__FUNCTION__]);
186
            }
187
        }
188
    }
189
190
    /**
191
     * Checks the numeric character(s) requirement.
192 20
     * @return void
193
     * @throws ValidationException
194 20
     */
195 5
    public function checkNumericCharacters()
196 2
    {
197
        if ($this->configuration->rules()->get(Plexity::RULE_NUMERIC) > 0) {
198
            if (!$this->validateNumericCharacters()) {
199
                throw new ValidationException($this->messages[__FUNCTION__]);
200
            }
201
        }
202
    }
203
204
    /**
205
     * Checks the special character(s) requirement.
206 18
     * @return void
207
     * @throws ValidationException
208
     */
209 18
    public function checkSpecialCharacters()
210 14
    {
211
        if ($this->configuration->rules()->get(Plexity::RULE_SPECIAL) > 0) {
212
            if (!$this->validateSpecialCharacters()) {
213 4
                throw new ValidationException($this->messages[__FUNCTION__]);
214 2
            }
215
        }
216
    }
217 3
218 2
    /**
219
     * Validates if a string is not in a array (password history database).
220
     * @return void
221
     * @throws ValidationException
222
     */
223
    public function checkNotIn()
224
    {
225
226
        if ($this->configuration->rules()->get(Plexity::RULE_NOT_IN) === null) {
227 4
            return;
228
        }
229 4
230
        if ($this->configuration->rules()->get(Plexity::RULE_NOT_IN) instanceof PasswordHistoryInterface) {
231 4
            $this->validateNotInPasswordHistoryImplementation();
232 2
        }
233
234
        if (is_array($this->configuration->rules()->get(Plexity::RULE_NOT_IN)) && count($this->configuration->rules()->get(Plexity::RULE_NOT_IN)) > 0) {
0 ignored issues
show
Bug introduced by
It seems like $this->configuration->ru...y\Plexity::RULE_NOT_IN) can also be of type null; however, parameter $value of count() does only seem to accept Countable|array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

234
        if (is_array($this->configuration->rules()->get(Plexity::RULE_NOT_IN)) && count(/** @scrutinizer ignore-type */ $this->configuration->rules()->get(Plexity::RULE_NOT_IN)) > 0) {
Loading history...
235 2
            $this->validateNotInArray();
236
        }
237
238
    }
239
240
    /**
241
     * Validates the upper case requirements.
242 4
     * @return boolean
243
     */
244 4
    private function validateUpperCase()
245
    {
246 4
        $occurences = preg_match_all(self::REGEX_UPPER_CASE, $this->configuration->checkString());
247 2
248
        if ($occurences >= $this->configuration->rules()->get(Plexity::RULE_UPPER)) {
249
            return true;
250 2
        }
251
252
        return false;
253
    }
254
255
    /**
256
     * Validates the lower case requirements.
257 5
     * @return boolean
258
     */
259 5
    private function validateLowerCase()
260 5
    {
261 3
        $occurrences = preg_match_all(self::REGEX_LOWER_CASE, $this->configuration->checkString());
262
263 2
        if ($occurrences >= $this->configuration->rules()->get(Plexity::RULE_LOWER)) {
264
            return true;
265
        }
266
267
        return false;
268
    }
269
270 6
    /**
271
     * Validates the special character requirements.
272 6
     * @return boolean
273 6
     */
274 3
    private function validateSpecialCharacters()
275
    {
276 3
        if ($this->countOccurrences($this->specialCharacters,
277
                $this->configuration->checkString()) >= $this->configuration->rules()->get(Plexity::RULE_SPECIAL)) {
278
            return true;
279
        }
280
        return false;
281
    }
282
283 6
    /**
284
     * Validates the numeric case requirements.
285 6
     * @return boolean
286 4
     */
287
    private function validateNumericCharacters()
288 2
    {
289
        if ($this->countOccurrences($this->numbers,
290
                $this->configuration->checkString()) >= $this->configuration->rules()->get(Plexity::RULE_NUMERIC)) {
291
            return true;
292
        }
293
        return false;
294
    }
295 5
296
    /**
297 5
     * Validates the minimum string length requirements.
298 3
     * @return boolean
299
     */
300 2
    private function validateLengthMin()
301
    {
302
        if (strlen($this->configuration->checkString()) >= $this->configuration->rules()->get(Plexity::RULE_LENGTH_MIN)) {
303
            return true;
304
        }
305
        return false;
306
    }
307
308 2
    /**
309
     * Validates the maximum string length requirements.
310 2
     * @return boolean
311 2
     */
312 1
    private function validateLengthMax()
313
    {
314
        if (strlen($this->configuration->checkString()) <= $this->configuration->rules()->get(Plexity::RULE_LENGTH_MAX)) {
315
            return true;
316
        }
317
        return false;
318
    }
319
320
    /**
321 2
     * Validates the not_in requirements against a simple array.
322
     * @return void
323 2
     * @throws ValidationException
324 1
     */
325
    private function validateNotInArray()
326
    {
327
        if (in_array($this->configuration->checkString(),
328
            (array)$this->configuration->rules()->get(Plexity::RULE_NOT_IN))) {
329
            throw new ValidationException($this->messages[__FUNCTION__]);
330
        }
331
    }
332
333
    /**
334 11
     * Validates the not_in requirements against an implementation of PasswordHistoryInterface.
335
     * @return void
336 11
     * @throws ValidationException
337 11
     */
338 11
    private function validateNotInPasswordHistoryImplementation()
339
    {
340 11
        if (($this->configuration->rules()->get(Plexity::RULE_NOT_IN))->checkHistory($this->configuration->checkString())) {
341
            throw new ValidationException($this->messages[__FUNCTION__]);
342
        }
343
    }
344
345
    /**
346
     * Count the number of occurrences of a character or string in a string.
347
     * @param array<mixed> $needles The character/string to count occurrences of.
348
     * @param string $haystack The string to check against.
349
     * @return int The number of occurrences.
350
     */
351
    private function countOccurrences(array $needles, $haystack)
352
    {
353
        $count = 0;
354
        foreach ($needles as $char) {
355
            $count += substr_count($haystack, $char);
356
        }
357
        return $count;
358
    }
359
360
    /**
361
     * Defines the locale property.
362
     * @param string $isoCode This data refers to the language ISO 639-1 Code.
363
     * @return void
364
     */
365
    public function setLocale(string $isoCode) {
366
        $this->locale = $isoCode;
367
    }
368
}
369