Passed
Push — b0.27.0 ( 8acaa0...a84b24 )
by Sebastian
04:30
created

Password::getInfo()   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
eloc 1
dl 0
loc 3
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
crap 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 An associative array containing options
23
     *
24
     * http://php.net/manual/en/function.password-hash.php
25
     */
26
    protected $options = [
27
        PASSWORD_BCRYPT => ['cost' => 11],
28
    ];
29
30
    /**
31
     * @var array An associate array containing algorithm constants
32
     */
33
    protected $algoLists = [
34
        PASSWORD_BCRYPT,
35
        //PASSWORD_DEFAULT
36
    ];
37
38
    /**
39
     * @var int|string Password default algorithm
40
     */
41
    protected $algo = PASSWORD_BCRYPT;
42
43
    /**
44
     * Class constructor.
45
     * <p>For password algorithm constants see <a href="http://php.net/manual/en/password.constants.php">Password Constants</a>.</p>
46
     * <pre><code class="php">//Options passed to class constructor as ['key' => 'value'] array.
47
     * $password = new Password(PASSWORD_DEFAULT, [
48
     *     'cost' => 11
49
     * ]);
50
     * </code></pre>
51
     *
52
     * Strict typing removed for $algo because on php 7.4 password hashing
53
     * algorithm identifiers are nullable strings rather than integers.
54
     *
55
     * @param int|string   $algo
56
     * @param array        $options
57
     *
58
     * @throws \InvalidArgumentException
59
     */
60 19
    public function __construct($algo = PASSWORD_BCRYPT, array $options = [])
61
    {
62
        //necessary for avoid errors if Argon2 library not enabled
63
        //PASSWORD_ARGON2ID const only present since 7.3 PHP version
64 19
        if (\defined('PASSWORD_ARGON2I')) {
65
            $this->algoLists[] = PASSWORD_ARGON2I;
66
            $this->options[PASSWORD_ARGON2I] = [
67
                'memory_cost' => PASSWORD_ARGON2_DEFAULT_MEMORY_COST,
68
                'time_cost' => PASSWORD_ARGON2_DEFAULT_TIME_COST,
69
                'threads' => PASSWORD_ARGON2_DEFAULT_THREADS
70
            ];
71
        }
72
73 19
        if (\defined('PASSWORD_ARGON2ID')) {
74
            $this->algoLists[] = PASSWORD_ARGON2ID;
75
            $this->options[PASSWORD_ARGON2ID] = [
76
                'memory_cost' => PASSWORD_ARGON2_DEFAULT_MEMORY_COST,
77
                'time_cost' => PASSWORD_ARGON2_DEFAULT_TIME_COST,
78
                'threads' => PASSWORD_ARGON2_DEFAULT_THREADS
79
            ];
80
        }
81
82 19
        if (!\in_array($algo, $this->algoLists, true)) {
83 1
            throw new \InvalidArgumentException("The password algorithm {$algo} is invalid");
84
        }
85
86 18
        $this->algo = $algo;
87 18
        $this->options[$algo] = \array_replace_recursive($this->options[$algo], $options);
88 18
    }
89
90
    /**
91
     * Verifies if a password matches an hash and return the result as boolean.
92
     * <pre><code class="php">$password = new Password();
93
     *
94
     * $storedHash = '$2y$11$cq3ZWO18l68X7pGs9Y1fveTGcNJ/iyehrDZ10BAvbY8LaBXNvnyk6';
95
     * $password = 'FooPassword';
96
     *
97
     * $verified = $password->verify($password, $storedHash);
98
     * </code></pre>
99
     *
100
     * @param string $password
101
     * @param string $hash
102
     *
103
     * @return bool True if password match, false if not.
104
     */
105 45
    public function verify(string $password, string $hash): bool
106
    {
107 45
        return \password_verify($password, $hash);
108
    }
109
110
    /**
111
     * Create password hash from the given string and return it.
112
     * <pre><code class="php">$password = new Password();
113
     *
114
     * $hash = $password->hash('FooPassword');
115
     *
116
     * //var_dump result
117
     * //$2y$11$cq3ZWO18l68X7pGs9Y1fveTGcNJ/iyehrDZ10BAvbY8LaBXNvnyk6
118
     * var_dump($hash)
119
     * </code></pre>
120
     *
121
     * @param string $password
122
     *
123
     * @return string Hashed password.
124
     */
125 21
    public function hash(string $password): string
126
    {
127 21
        return \password_hash($password, $this->algo, $this->options[$this->algo]);
0 ignored issues
show
Bug introduced by
It seems like $this->algo can also be of type string; however, parameter $algo of password_hash() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

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

127
        return \password_hash($password, /** @scrutinizer ignore-type */ $this->algo, $this->options[$this->algo]);
Loading history...
128
    }
129
130
    /**
131
     * Checks if the given hash matches the algorithm and the options provided.
132
     * <pre><code class="php">$password = new Password();
133
     *
134
     * $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
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]);
0 ignored issues
show
Bug introduced by
It seems like $this->algo can also be of type string; however, parameter $algo of password_needs_rehash() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

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

146
        return \password_needs_rehash($hash, /** @scrutinizer ignore-type */ $this->algo, $this->options[$this->algo]);
Loading history...
147
    }
148
149
    /**
150
     * Returns information about the given hash.
151
     * <pre><code class="php">$password = new Password();
152
     *
153
     * $hash = '$2y$11$cq3ZWO18l68X7pGs9Y1fveTGcNJ/iyehrDZ10BAvbY8LaBXNvnyk6';
154
     *
155
     * $info = $password->getInfo($hash);
156
     *
157
     * //var_dump result
158
     * //[
159
     * //    'algo' => 1,
160
     * //    'algoName' => 'bcrypt',
161
     * //    'options' => [
162
     * //        'cost' => int 11
163
     * //    ]
164
     * //]
165
     * var_dump($info);
166
     * </code></pre>
167
     *
168
     * @param string $hash
169
     *
170
     * @return array
171
     */
172 2
    public function getInfo(string $hash): array
173
    {
174 2
        return \password_get_info($hash);
175
    }
176
}
177