Passed
Push — master ( b61519...33eb60 )
by Magnar Ovedal
03:09
created

GuessableDataRule::containsString()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 2
dl 0
loc 3
ccs 2
cts 2
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 Stadly\PasswordPolice\DateFormatter;
9
use Stadly\PasswordPolice\DateFormatter\DefaultFormatter;
10
use Stadly\PasswordPolice\Password;
11
use Stadly\PasswordPolice\Policy;
12
use Stadly\PasswordPolice\Rule;
13
use Stadly\PasswordPolice\ValidationError;
14
use Stadly\PasswordPolice\WordFormatter;
15
use Stadly\PasswordPolice\WordFormatter\FormatterCombiner;
16
use Traversable;
17
18
final class GuessableDataRule implements Rule
19
{
20
    /**
21
     * @var (string|DateTimeInterface)[] Guessable data.
22
     */
23
    private $guessableData;
24
25
    /**
26
     * @var WordFormatter Word formatter.
27
     */
28
    private $wordFormatter;
29
30
    /**
31
     * @var DateFormatter Date formatter.
32
     */
33
    private $dateFormatter;
34
35
    /**
36
     * @var int Constraint weight.
37
     */
38
    private $weight;
39
40
    /**
41
     * @param (string|DateTimeInterface)[] $guessableData Guessable data.
42
     * @param WordFormatter[] $wordFormatters Word formatters.
43
     * @param DateFormatter|null $dateFormatter Date formatter.
44
     * @param int $weight Constraint weight.
45
     */
46 1
    public function __construct(
47
        array $guessableData = [],
48
        array $wordFormatters = [],
49
        ?DateFormatter $dateFormatter = null,
50
        int $weight = 1
51
    ) {
52 1
        $this->guessableData = $guessableData;
53 1
        $this->wordFormatter = new FormatterCombiner($wordFormatters);
54 1
        $this->dateFormatter = $dateFormatter ?? new DefaultFormatter();
55 1
        $this->weight = $weight;
56 1
    }
57
58
    /**
59
     * Check whether a password is in compliance with the rule.
60
     *
61
     * @param Password|string $password Password to check.
62
     * @param int|null $weight Don't consider constraints with lower weights.
63
     * @return bool Whether the password is in compliance with the rule.
64
     */
65 14
    public function test($password, ?int $weight = 1): bool
66
    {
67 14
        if ($weight !== null && $this->weight < $weight) {
68 1
            return true;
69
        }
70
71 13
        $data = $this->getGuessableData($password);
72
73 13
        return $data === null;
74
    }
75
76
    /**
77
     * Validate that a password is in compliance with the rule.
78
     *
79
     * @param Password|string $password Password to validate.
80
     * @return ValidationError|null Validation error describing why the password is not in compliance with the rule.
81
     */
82 2
    public function validate($password): ?ValidationError
83
    {
84 2
        $data = $this->getGuessableData($password);
85
86 2
        if ($data !== null) {
87 1
            return new ValidationError(
88 1
                $this->getMessage($data),
89 1
                $password,
90 1
                $this,
91 1
                $this->weight
92
            );
93
        }
94
95 1
        return null;
96
    }
97
98
    /**
99
     * @param Password|string $password Password to find guessable data in.
100
     * @return string|DateTimeInterface|null Guessable data in the password.
101
     */
102 15
    private function getGuessableData($password)
103
    {
104 15
        $guessableData = $this->guessableData;
105 15
        if ($password instanceof Password) {
106 14
            $guessableData = array_merge($guessableData, $password->getGuessableData());
107
        }
108
109 15
        foreach ($this->wordFormatter->apply([(string)$password]) as $word) {
110 15
            foreach ($guessableData as $data) {
111 14
                if ($this->contains($word, $data)) {
112 15
                    return $data;
113
                }
114
            }
115
        }
116
117 9
        return null;
118
    }
119
120
    /**
121
     * @param string $password Password to check.
122
     * @param string|DateTimeInterface $data Data to check.
123
     * @return bool Whether the password contains the data.
124
     */
125 14
    private function contains(string $password, $data): bool
126
    {
127 14
        if ($data instanceof DateTimeInterface) {
128 7
            $strings = $this->dateFormatter->apply([$data]);
129
        } else {
130 8
            $strings = [$data];
131
        }
132
133 14
        foreach ($strings as $string) {
134 14
            if (mb_stripos($password, $string) !== false) {
135 14
                return true;
136
            }
137
        }
138
139 9
        return false;
140
    }
141
142
    /**
143
     * @param string|DateTimeInterface $data Data that violates the constraint.
144
     * @return string Message explaining the violation.
145
     */
146 1
    private function getMessage($data): string
0 ignored issues
show
Unused Code introduced by
The parameter $data 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

146
    private function getMessage(/** @scrutinizer ignore-unused */ $data): 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...
147
    {
148 1
        $translator = Policy::getTranslator();
149
150 1
        return $translator->trans(
151 1
            'Must not contain guessable data.'
152
        );
153
    }
154
}
155