Passed
Push — master ( 62f1fa...d989df )
by Magnar Ovedal
02:51
created

GuessableData::getMessage()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 3
nc 1
nop 0
dl 0
loc 6
ccs 4
cts 4
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Stadly\PasswordPolice\Rule;
6
7
use DateTimeInterface;
8
use InvalidArgumentException;
9
use RuntimeException;
10
use Stadly\PasswordPolice\Password;
11
use Stadly\PasswordPolice\Policy;
12
use Stadly\PasswordPolice\WordList\WordListInterface;
13
14
final class GuessableData implements RuleInterface
15
{
16
    private const DATE_FORMATS = [
17
        // Year
18
        ['y'], // 18
19
        ['Y'], // 2018
20
21
        // Day month
22
        ['j', 'n'], // 4 8
23
        ['j', 'm'], // 4 08
24
        ['j', 'M'], // 4 Aug
25
        ['j', 'F'], // 4 August
26
27
        // Month day
28
        ['n', 'j'], // 8 4
29
        ['n', 'd'], // 8 04
30
        ['M', 'j'], // Aug 4
31
        ['M', 'd'], // Aug 04
32
        ['F', 'j'], // August 4
33
        ['F', 'd'], // August 04
34
    ];
35
36
    private const DATE_SEPARATORS = [
37
        '',
38
        '-',
39
        ' ',
40
        '/',
41
        '.',
42
        ',',
43
        '. ',
44
        ', ',
45
    ];
46
47
    /**
48
     * Check whether a password adheres to the rule.
49
     *
50
     * @param Password|string $password Password to check.
51
     * @return bool Whether the password adheres to the rule.
52
     */
53 5
    public function test($password): bool
54
    {
55 5
        if ($password instanceof Password) {
56 4
            foreach ($password->getGuessableData() as $data) {
57 4
                if ($this->contains((string)$password, $data)) {
58 4
                    return false;
59
                }
60
            }
61
        }
62 3
        return true;
63
    }
64
65
    /**
66
     * @param string $password Password to check.
67
     * @param string|DateTimeInterface $data Data to check.
68
     * @return bool Whether the password contains the data.
69
     */
70 6
    private function contains(string $password, $data): bool
71
    {
72 6
        if ($data instanceof DateTimeInterface) {
73 3
            return $this->containsDate($password, $data);
74
        }
75
76 4
        return $this->containsString($password, $data);
77
    }
78
79
    /**
80
     * @param string $password Password to check.
81
     * @param string $string String to check.
82
     * @return bool Whether the password contains the string.
83
     */
84 6
    private function containsString(string $password, string $string): bool
85
    {
86 6
        return false !== mb_stripos($password, $string);
87
    }
88
89
    /**
90
     * @param string $password Password to check.
91
     * @param DateTimeInterface $date Date to check.
92
     * @return bool Whether the password contains the date.
93
     */
94 3
    private function containsDate(string $password, DateTimeInterface $date): bool
95
    {
96 3
        foreach ($this->getDateFormats() as $format) {
97 3
            if ($this->containsString($password, $date->format($format))) {
98 3
                return true;
99
            }
100
        }
101
102 2
        return false;
103
    }
104
105
    /**
106
     * @return iterable<string> Date formats.
107
     */
108 3
    private function getDateFormats(): iterable
109
    {
110 3
        foreach (self::DATE_FORMATS as $format) {
111 3
            foreach (self::DATE_SEPARATORS as $separator) {
112 3
                yield implode($separator, $format);
113
            }
114
        }
115 2
    }
116
117
    /**
118
     * Enforce that a password adheres to the rule.
119
     *
120
     * @param Password|string $password Password that must adhere to the rule.
121
     * @throws RuleException If the password does not adhrere to the rule.
122
     */
123 2
    public function enforce($password): void
124
    {
125 2
        if (!$this->test($password)) {
126 1
            throw new RuleException($this, $this->getMessage());
127
        }
128 1
    }
129
130
    /**
131
     * {@inheritDoc}
132
     */
133 1
    public function getMessage(): string
134
    {
135 1
        $translator = Policy::getTranslator();
136
137 1
        return $translator->trans(
138 1
            'Must not contain guessable data.'
139
        );
140
    }
141
}
142