Passed
Push — v2 ( 960327...58683d )
by Berend
03:11
created

Password::validatePassword()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 0
Metric Value
eloc 4
dl 0
loc 6
ccs 5
cts 5
cp 1
rs 10
c 0
b 0
f 0
cc 2
nc 2
nop 1
crap 2
1
<?php
2
3
namespace miBadger\ActiveRecord\Traits;
4
5
use miBadger\ActiveRecord\ColumnProperty;
6
use miBadger\ActiveRecord\ActiveRecordTraitException;
7
8
const TRAIT_PASSWORD_FIELD_PASSWORD = "password";
9
const TRAIT_PASSWORD_ENCRYPTION = \PASSWORD_BCRYPT;
10
const TRAIT_PASSWORD_STRENTH = 10;
11
const TRAIT_PASSWORD_FIELD_PASSWORD_RESET_TOKEN = "password_reset_token";
12
const TRAIT_PASSWORD_MIN_LENGTH = 8;
13
14
trait Password
15
{
16
	/** @var string The password hash. */
17
	protected $password;
18
19
	/** @var string|null The password reset token. */
20
	protected $passwordResetToken;
21
22
	/**
23
	 * this method is required to be called in the constructor for each class that uses this trait. 
24
	 * It adds the fields necessary for the passwords struct to the table definition
25
	 */
26 7
	protected function initPassword()
27
	{
28 7
		$this->extendTableDefinition(TRAIT_PASSWORD_FIELD_PASSWORD, [
29 7
			'value' => &$this->password,
30 7
			'validate' => [$this, 'validatePassword'],
31 7
			'type' => 'VARCHAR',
32 7
			'length' => 1024,
33
			'properties' => null
34
		]);
35
36 7
		$this->extendTableDefinition(TRAIT_PASSWORD_FIELD_PASSWORD_RESET_TOKEN, [
37 7
			'value' => &$this->passwordResetToken,
38
			'validate' => null,
39 7
			'default' => 0,
40 7
			'type' => 'VARCHAR',
41 7
			'length' => 1024
42
		]);
43 7
	}
44
45
46
	/**
47
	 * Returns whether the users password has been set
48
	 * @return boolean true if the user has a password
49
	 */
50 2
	public function hasPasswordBeenSet()
51
	{
52 2
		return $this->password !== null;
53
	}
54
55
	/**
56
	 * Returns true if the credentials are correct.
57
	 *
58
	 * @param string $password
59
	 * @return boolean true if the credentials are correct
60
	 */
61 2
	public function isPassword($password)
62
	{ 
63 2
		if (!$this->hasPasswordBeenSet())
64
		{
65 1
			throw new ActiveRecordTraitException("Password field has not been set");
66
		}
67
68 1
		if (!password_verify($password, $this->password)) {
69 1
			return false;
70
		}
71
72 1
		if (password_needs_rehash($this->password, TRAIT_PASSWORD_ENCRYPTION, ['cost' => TRAIT_PASSWORD_STRENTH])) {
73
			$this->setPassword($password)->sync();
0 ignored issues
show
Bug introduced by
It seems like sync() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

73
			$this->setPassword($password)->/** @scrutinizer ignore-call */ sync();
Loading history...
74
		}
75
76 1
		return true;
77
	}
78
79 3
	public function validatePassword($password) {
80 3
		if (strlen($password) < TRAIT_PASSWORD_MIN_LENGTH) {
81 2
			$message = sprintf('\'Password\' must be atleast %s characters long. %s characters provied.', TRAIT_PASSWORD_MIN_LENGTH, strlen($password));
82 2
			return [false, $message];
83
		}
84 2
		return [true, ''];
85
	}
86
87
	/**
88
	 * Set the password.
89
	 *
90
	 * @param string $password
91
	 * @return $this
92
	 * @throws \Exception
93
	 */
94 2
	public function setPassword($password)
95
	{
96 2
		[$status, $error] = $this->validatePassword($password);
97 2
		if (!$status) {
98 1
			throw new ActiveRecordTraitException($error);
99
		}
100
101 1
		$passwordHash = \password_hash($password, TRAIT_PASSWORD_ENCRYPTION, ['cost' => TRAIT_PASSWORD_STRENTH]);
102
103 1
		if ($passwordHash === false) {
104
			throw new ActiveRecordTraitException('\'Password\' hash failed.');
105
		}
106
107 1
		$this->password = $passwordHash;
108
109 1
		return $this;
110
	}
111
112
	/**
113
	 * @return string The Hash of the password
114
	 */
115
	public function getPasswordHash()
116
	{
117
		return $this->password;
118
	}
119
120
	/**
121
	 * Returns the currently set password token for the entity, or null if not set
122
	 * @return string|null The password reset token
123
	 */
124 2
	public function getPasswordResetToken()
125
	{
126 2
		return $this->passwordResetToken;
127
	}
128
129
	/**
130
	 * Generates a new password reset token for the user
131
	 */
132 2
	public function generatePasswordResetToken()
133
	{
134 2
		$this->passwordResetToken = md5(uniqid(mt_rand(), true));
135 2
	}
136
137
	/**
138
	 * Clears the current password reset token
139
	 */
140 1
	public function clearPasswordResetToken()
141
	{
142 1
		$this->passwordResetToken = null;
143 1
	}
144
	
145
	/**
146
	 * @return void
147
	 */
148
	abstract protected function extendTableDefinition($columnName, $definition);
149
	
150
	/**
151
	 * @return void
152
	 */
153
	abstract protected function registerSearchHook($columnName, $fn);
154
155
	/**
156
	 * @return void
157
	 */
158
	abstract protected function registerDeleteHook($columnName, $fn);
159
160
	/**
161
	 * @return void
162
	 */
163
	abstract protected function registerUpdateHook($columnName, $fn);
164
165
	/**
166
	 * @return void
167
	 */
168
	abstract protected function registerReadHook($columnName, $fn);
169
170
	/**
171
	 * @return void
172
	 */
173
	abstract protected function registerCreateHook($columnName, $fn);
174
175
}