Password   A
last analyzed

Complexity

Total Complexity 8

Size/Duplication

Total Lines 155
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 5
Bugs 3 Features 0
Metric Value
wmc 8
eloc 28
c 5
b 3
f 0
dl 0
loc 155
ccs 26
cts 26
cp 1
rs 10

5 Methods

Rating   Name   Duplication   Size   Complexity  
A needsRehash() 0 3 1
A getInfo() 0 3 1
A hash() 0 3 1
A __construct() 0 28 4
A verify() 0 3 1
1
<?php
2
3
/**
4
 * Linna Framework.
5
 *
6
 * @author Sebastian Rapetti <[email protected]>
7
 * @copyright (c) 2018, Sebastian Rapetti
8
 * @license http://opensource.org/licenses/MIT MIT License
9
 */
10
declare(strict_types=1);
11
12
namespace Linna\Authentication;
13
14
/**
15
 * Provide methods for manage password, this class use PHP password hashing function,
16
 * see php documentation for more information.
17
 * <a href="http://php.net/manual/en/book.password.php">http://php.net/manual/en/book.password.php</a>
18
 */
19
class Password
20
{
21
    /**
22
     * @var array<mixed> An associative array containing options
23
     *
24
     * http://php.net/manual/en/function.password-hash.php
25
     */
26
    protected array $options = [
27
        PASSWORD_BCRYPT => ['cost' => 11],
28
        PASSWORD_DEFAULT => ['cost' => 11]
29
    ];
30
31
    /**
32
     * @var array<mixed> An associate array containing algorithm constants
33
     */
34
    protected array $algoLists = [
35
        PASSWORD_BCRYPT,
36
        PASSWORD_DEFAULT
37
    ];
38
39
    /**
40
     * @var string|null Password default algorithm
41
     */
42
    protected ?string $algo = PASSWORD_BCRYPT;
43
44
    /**
45
     * Class constructor.
46
     *
47
     * <p>For password algorithm constants see <a href="http://php.net/manual/en/password.constants.php">Password Constants</a>.</p>
48
     * <pre><code class="php">use Linna\Authentication\Password;
49
     *
50
     * $password = new Password(PASSWORD_DEFAULT, [
51
     *     'cost' => 11
52
     * ]);
53
     * </code></pre>
54
     *
55
     * Strict typing removed for $algo because on php 7.4 password hashing
56
     * algorithm identifiers are nullable strings rather than integers.
57
     *
58
     * @param string|null   $algo        Algorithm used for hash passwords.
59
     * @param array<mixed>  $options     Options for algoas ['key' => 'value'] array.
60
     *
61
     * @throws \InvalidArgumentException
62
     */
63 19
    public function __construct($algo = PASSWORD_BCRYPT, array $options = [])
64
    {
65
        //necessary for avoid errors if Argon2 library not enabled
66
        //PASSWORD_ARGON2ID const only present since 7.3 PHP version
67 19
        if (\defined('PASSWORD_ARGON2I')) {
68 19
            $this->algoLists[] = PASSWORD_ARGON2I;
69 19
            $this->options[PASSWORD_ARGON2I] = [
70 19
                'memory_cost' => PASSWORD_ARGON2_DEFAULT_MEMORY_COST,
71 19
                'time_cost' => PASSWORD_ARGON2_DEFAULT_TIME_COST,
72 19
                'threads' => PASSWORD_ARGON2_DEFAULT_THREADS
73
            ];
74
        }
75
76 19
        if (\defined('PASSWORD_ARGON2ID')) {
77 19
            $this->algoLists[] = PASSWORD_ARGON2ID;
78 19
            $this->options[PASSWORD_ARGON2ID] = [
79 19
                'memory_cost' => PASSWORD_ARGON2_DEFAULT_MEMORY_COST,
80 19
                'time_cost' => PASSWORD_ARGON2_DEFAULT_TIME_COST,
81 19
                'threads' => PASSWORD_ARGON2_DEFAULT_THREADS
82
            ];
83
        }
84
85 19
        if (!\in_array($algo, $this->algoLists, true)) {
86 1
            throw new \InvalidArgumentException("The password algorithm {$algo} is invalid");
87
        }
88
89 18
        $this->algo = $algo;
90 18
        $this->options[$algo] = \array_replace_recursive($this->options[$algo], $options);
91 18
    }
92
93
    /**
94
     * Verifies if a password matches an hash and return the result as boolean.
95
     *
96
     * <pre><code class="php">$storedHash = '$2y$11$cq3ZWO18l68X7pGs9Y1fveTGcNJ/iyehrDZ10BAvbY8LaBXNvnyk6';
97
     * $password = 'FooPassword';
98
     *
99
     * $verified = $password->verify($password, $storedHash);
100
     * </code></pre>
101
     *
102
     * @param string $password  Plaintext password to be compared.
103
     * @param string $hash      Hashed password.
104
     *
105
     * @return bool True if password match, false if not.
106
     */
107 44
    public function verify(string $password, string $hash): bool
108
    {
109 44
        return \password_verify($password, $hash);
110
    }
111
112
    /**
113
     * Create password hash from the given string and return it.
114
     *
115
     * <pre><code class="php">$hash = $password->hash('FooPassword');
116
     *
117
     * //var_dump result
118
     * //$2y$11$cq3ZWO18l68X7pGs9Y1fveTGcNJ/iyehrDZ10BAvbY8LaBXNvnyk6
119
     * var_dump($hash)
120
     * </code></pre>
121
     *
122
     * @param string $password Plaintext password to be hashed.
123
     *
124
     * @return string Hashed password.
125
     */
126 18
    public function hash(string $password): string
127
    {
128 18
        return \password_hash($password, $this->algo, $this->options[$this->algo]);
0 ignored issues
show
Bug Best Practice introduced by
The expression return password_hash($pa...->options[$this->algo]) could return the type null which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
129
    }
130
131
    /**
132
     * Checks if the given hash matches the algorithm and the options provided.
133
     *
134
     * <pre><code class="php">$hash = '$2y$11$cq3ZWO18l68X7pGs9Y1fveTGcNJ/iyehrDZ10BAvbY8LaBXNvnyk6';
135
     *
136
     * //true if rehash is needed, false if no
137
     * $rehashCheck = $password->needsRehash($hash);
138
     * </code></pre>
139
     *
140
     * @param string $hash Hash to be checked
141
     *
142
     * @return bool
143
     */
144 2
    public function needsRehash(string $hash): bool
145
    {
146 2
        return \password_needs_rehash($hash, $this->algo, $this->options[$this->algo]);
147
    }
148
149
    /**
150
     * Returns information about the given hash.
151
     *
152
     * <pre><code class="php">$hash = '$2y$11$cq3ZWO18l68X7pGs9Y1fveTGcNJ/iyehrDZ10BAvbY8LaBXNvnyk6';
153
     *
154
     * $info = $password->getInfo($hash);
155
     *
156
     * //var_dump result
157
     * //[
158
     * //    'algo' => 1,
159
     * //    'algoName' => 'bcrypt',
160
     * //    'options' => [
161
     * //        'cost' => int 11
162
     * //    ]
163
     * //]
164
     * var_dump($info);
165
     * </code></pre>
166
     *
167
     * @param string $hash Hash for wich get info.
168
     *
169
     * @return array<mixed>
170
     */
171 2
    public function getInfo(string $hash): array
172
    {
173 2
        return \password_get_info($hash);
0 ignored issues
show
Bug Best Practice introduced by
The expression return password_get_info($hash) could return the type null which is incompatible with the type-hinted return array. Consider adding an additional type-check to rule them out.
Loading history...
174
    }
175
}
176