Completed
Push — master ( 48fddc...1eba06 )
by Magnar Ovedal
06:59 queued 03:58
created

NoReuseRule::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 4
dl 0
loc 4
ccs 3
cts 3
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 StableSort\StableSort;
8
use Stadly\PasswordPolice\Constraint\PositionConstraint;
9
use Stadly\PasswordPolice\HashFunction;
10
use Stadly\PasswordPolice\Password;
11
use Stadly\PasswordPolice\Policy;
12
use Stadly\PasswordPolice\Rule;
13
use Stadly\PasswordPolice\ValidationError;
14
15
final class NoReuseRule implements Rule
16
{
17
    /**
18
     * @var HashFunction Hash function.
19
     */
20
    private $hashFunction;
21
22
    /**
23
     * @var PositionConstraint[] Rule constraints.
24
     */
25
    private $constraints;
26
27
    /**
28
     * @param HashFunction $hashFunction Hash function to use when comparing passwords.
29
     * @param int|null $count Number of former passwords to consider.
30
     * @param int $first First former password to consider.
31
     * @param int $weight Constraint weight.
32
     */
33 7
    public function __construct(HashFunction $hashFunction, ?int $count = null, int $first = 0, int $weight = 1)
34
    {
35 7
        $this->hashFunction = $hashFunction;
36 7
        $this->addConstraint($count, $first, $weight);
37 4
    }
38
39
    /**
40
     * @param int|null $count Number of former passwords to consider.
41
     * @param int $first First former password to consider.
42
     * @param int $weight Constraint weight.
43
     * @return $this
44
     */
45 2
    public function addConstraint(?int $count = null, int $first = 0, int $weight = 1): self
46
    {
47 2
        $this->constraints[] = new PositionConstraint($first, $count, $weight);
48
49
        StableSort::usort($this->constraints, static function (PositionConstraint $a, PositionConstraint $b): int {
50 2
            return $b->getWeight() <=> $a->getWeight();
51 2
        });
52
53 2
        return $this;
54
    }
55
56
    /**
57
     * @return HashFunction Hash function.
58
     */
59 1
    public function getHashFunction(): HashFunction
60
    {
61 1
        return $this->hashFunction;
62
    }
63
64
    /**
65
     * Check whether a password is in compliance with the rule.
66
     *
67
     * @param Password|string $password Password to check.
68
     * @param int|null $weight Don't consider constraints with lower weights.
69
     * @return bool Whether the password is in compliance with the rule.
70
     */
71 6
    public function test($password, ?int $weight = 1): bool
72
    {
73 6
        $positions = $this->getPositions($password);
74 6
        $constraint = $this->getViolation($positions, $weight);
75
76 6
        return $constraint === null;
77
    }
78
79
    /**
80
     * Validate that a password is in compliance with the rule.
81
     *
82
     * @param Password|string $password Password to validate.
83
     * @return ValidationError|null Validation error describing why the password is not in compliance with the rule.
84
     */
85 2
    public function validate($password): ?ValidationError
86
    {
87 2
        $positions = $this->getPositions($password);
88 2
        $constraint = $this->getViolation($positions);
89
90 2
        if ($constraint !== null) {
91 1
            return new ValidationError(
92 1
                $this->getMessage($constraint),
93 1
                $password,
94 1
                $this,
95 1
                $constraint->getWeight()
96
            );
97
        }
98
99 1
        return null;
100
    }
101
102
    /**
103
     * @param int[] $positions Positions of former passwords matching the password.
104
     * @param int|null $weight Don't consider constraints with lower weights.
105
     * @return PositionConstraint|null Constraint violated by the position.
106
     */
107 8
    private function getViolation(array $positions, ?int $weight = null): ?PositionConstraint
108
    {
109 8
        foreach ($this->constraints as $constraint) {
110 8
            if ($weight !== null && $constraint->getWeight() < $weight) {
111 1
                continue;
112
            }
113 7
            foreach ($positions as $position) {
114 6
                if ($constraint->test($position)) {
115 7
                    return $constraint;
116
                }
117
            }
118
        }
119
120 5
        return null;
121
    }
122
123
    /**
124
     * @param Password|string $password Password to compare with former passwords.
125
     * @return int[] Positions of former passwords matching the password.
126
     */
127 8
    private function getPositions($password): array
128
    {
129 8
        $positions = [];
130
131 8
        if ($password instanceof Password) {
132 7
            $position = 0;
133 7
            foreach ($password->getFormerPasswords() as $formerPassword) {
134 7
                if ($this->hashFunction->compare((string)$password, (string)$formerPassword)) {
135 7
                    $positions[] = $position;
136
                }
137 7
                ++$position;
138
            }
139
        }
140 8
        return $positions;
141
    }
142
143
    /**
144
     * @param PositionConstraint $constraint Constraint that is violated.
145
     * @return string Message explaining the violation.
146
     */
147 1
    private function getMessage(PositionConstraint $constraint): string
0 ignored issues
show
Unused Code introduced by
The parameter $constraint is not used and could be removed. ( Ignorable by Annotation )

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

147
    private function getMessage(/** @scrutinizer ignore-unused */ PositionConstraint $constraint): string

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
148
    {
149 1
        $translator = Policy::getTranslator();
150
151 1
        return $translator->trans(
152 1
            'Cannot reuse former passwords.'
153
        );
154
    }
155
}
156