Passed
Push — master ( ae0a03...686af4 )
by Magnar Ovedal
03:17
created

NoReuse::getFormerPassword()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 18
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 5

Importance

Changes 0
Metric Value
cc 5
eloc 10
nc 5
nop 1
dl 0
loc 18
ccs 11
cts 11
cp 1
crap 5
rs 9.6111
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Stadly\PasswordPolice\Rule;
6
7
use InvalidArgumentException;
8
use Stadly\PasswordPolice\FormerPassword;
9
use Stadly\PasswordPolice\Password;
10
use Stadly\PasswordPolice\Policy;
11
use Stadly\PasswordPolice\HashFunction\HashFunctionInterface;
12
13
final class NoReuse implements RuleInterface
14
{
15
    /**
16
     * @var HashFunctionInterface Hash function.
17
     */
18
    private $hashFunction;
19
20
    /**
21
     * @var int First former password to consider.
22
     */
23
    private $first;
24
25
    /**
26
     * @var int|null Number of former passwords to consider.
27
     */
28
    private $count;
29
30
    /**
31
     * @param int|null $count Number of former passwords to consider.
32
     * @param int $first First former password to consider.
33
     */
34 7
    public function __construct(HashFunctionInterface $hashFunction, ?int $count = null, int $first = 1)
35
    {
36 7
        if ($first < 1) {
37 2
            throw new InvalidArgumentException('First must be positive.');
38
        }
39 5
        if ($count !== null && $count < 1) {
40 2
            throw new InvalidArgumentException('Count must be positive.');
41
        }
42
43 3
        $this->hashFunction = $hashFunction;
44 3
        $this->first = $first;
45 3
        $this->count = $count;
46 3
    }
47
48
    /**
49
     * @return HashFunctionInterface Hash function.
50
     */
51 1
    public function getHashFunction(): HashFunctionInterface
52
    {
53 1
        return $this->hashFunction;
54
    }
55
56
    /**
57
     * @return int First former password to consider.
58
     */
59 1
    public function getFirst(): int
60
    {
61 1
        return $this->first;
62
    }
63
64
    /**
65
     * @return int|null Number of former passwords to consider.
66
     */
67 1
    public function getCount(): ?int
68
    {
69 1
        return $this->count;
70
    }
71
72
    /**
73
     * Check whether a password is in compliance with the rule.
74
     *
75
     * @param Password|string $password Password to check.
76
     * @return bool Whether the password is in compliance with the rule.
77
     */
78 5
    public function test($password): bool
79
    {
80 5
        $formerPassword = $this->getFormerPassword($password);
81
82 5
        return $formerPassword === null;
83
    }
84
85
    /**
86
     * Enforce that a password is in compliance with the rule.
87
     *
88
     * @param Password|string $password Password that must adhere to the rule.
89
     * @throws RuleException If the password does not adhrere to the rule.
90
     */
91 2
    public function enforce($password): void
92
    {
93 2
        $formerPassword = $this->getFormerPassword($password);
94
95 2
        if ($formerPassword !== null) {
96 1
            throw new RuleException($this, $this->getMessage());
97
        }
98 1
    }
99
100
    /**
101
     * @param Password|string $password Password to compare with former passwords.
102
     * @return FormerPassword|null Former password matching the password.
103
     */
104 7
    private function getFormerPassword($password): ?FormerPassword
105
    {
106 7
        if ($password instanceof Password) {
107 6
            $formerPasswords = $password->getFormerPasswords();
108
109 6
            $start = $this->first-1;
110 6
            $end = count($formerPasswords);
111 6
            if ($this->count !== null) {
112 3
                $end = min($end, $start+$this->count);
113
            }
114
115 6
            for ($i = $start; $i < $end; ++$i) {
116 6
                if ($this->hashFunction->compare((string)$password, (string)$formerPasswords[$i])) {
117 3
                    return $formerPasswords[$i];
118
                }
119
            }
120
        }
121 4
        return null;
122
    }
123
124
    /**
125
     * {@inheritDoc}
126
     */
127 1
    public function getMessage(): string
128
    {
129 1
        $translator = Policy::getTranslator();
130
131 1
        return $translator->trans(
132 1
            'Cannot reuse former passwords.'
133
        );
134
    }
135
}
136