blob: 98224566435d6b93137315200ae9121fb449f908 [file] [log] [blame]
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +02001<?php
2
3namespace LdapRecord\Models\Concerns;
4
5use LdapRecord\ConnectionException;
6use LdapRecord\LdapRecordException;
7use LdapRecord\Models\Attributes\Password;
8
9trait HasPassword
10{
11 /**
12 * Set the password on the user.
13 *
14 * @param string|array $password
15 *
16 * @throws ConnectionException
17 */
18 public function setPasswordAttribute($password)
19 {
20 $this->validateSecureConnection();
21
22 // Here we will attempt to determine the password hash method in use
23 // by parsing the users hashed password (if it as available). If a
24 // method is determined, we will override the default here.
25 if (! ($method = $this->determinePasswordHashMethod())) {
26 $method = $this->getPasswordHashMethod();
27 }
28
29 // If the password given is an array, we can assume we
30 // are changing the password for the current user.
31 if (is_array($password)) {
32 $this->setChangedPassword(
33 $this->getHashedPassword($method, $password[0], $this->getPasswordSalt($method)),
34 $this->getHashedPassword($method, $password[1]),
35 $this->getPasswordAttributeName()
36 );
37 }
38 // Otherwise, we will assume the password is being
39 // reset, overwriting the one currently in place.
40 else {
41 $this->setPassword(
42 $this->getHashedPassword($method, $password),
43 $this->getPasswordAttributeName()
44 );
45 }
46 }
47
48 /**
49 * Alias for setting the password on the user.
50 *
51 * @param string|array $password
52 *
53 * @throws ConnectionException
54 */
55 public function setUnicodepwdAttribute($password)
56 {
57 $this->setPasswordAttribute($password);
58 }
59
60 /**
61 * An accessor for retrieving the user's hashed password value.
62 *
63 * @return string|null
64 */
65 public function getPasswordAttribute()
66 {
67 return $this->getAttribute($this->getPasswordAttributeName())[0] ?? null;
68 }
69
70 /**
71 * Get the name of the attribute that contains the user's password.
72 *
73 * @return string
74 */
75 public function getPasswordAttributeName()
76 {
77 if (property_exists($this, 'passwordAttribute')) {
78 return $this->passwordAttribute;
79 }
80
81 if (method_exists($this, 'passwordAttribute')) {
82 return $this->passwordAttribute();
83 }
84
85 return 'unicodepwd';
86 }
87
88 /**
89 * Get the name of the method to use for hashing the user's password.
90 *
91 * @return string
92 */
93 public function getPasswordHashMethod()
94 {
95 if (property_exists($this, 'passwordHashMethod')) {
96 return $this->passwordHashMethod;
97 }
98
99 if (method_exists($this, 'passwordHashMethod')) {
100 return $this->passwordHashMethod();
101 }
102
103 return 'encode';
104 }
105
106 /**
107 * Set the changed password.
108 *
109 * @param string $oldPassword
110 * @param string $newPassword
111 * @param string $attribute
112 *
113 * @return void
114 */
115 protected function setChangedPassword($oldPassword, $newPassword, $attribute)
116 {
117 // Create batch modification for removing the old password.
118 $this->addModification(
119 $this->newBatchModification(
120 $attribute,
121 LDAP_MODIFY_BATCH_REMOVE,
122 [$oldPassword]
123 )
124 );
125
126 // Create batch modification for adding the new password.
127 $this->addModification(
128 $this->newBatchModification(
129 $attribute,
130 LDAP_MODIFY_BATCH_ADD,
131 [$newPassword]
132 )
133 );
134 }
135
136 /**
137 * Set the password on the model.
138 *
139 * @param string $password
140 * @param string $attribute
141 *
142 * @return void
143 */
144 protected function setPassword($password, $attribute)
145 {
146 $this->addModification(
147 $this->newBatchModification(
148 $attribute,
149 LDAP_MODIFY_BATCH_REPLACE,
150 [$password]
151 )
152 );
153 }
154
155 /**
156 * Encode / hash the given password.
157 *
158 * @param string $method
159 * @param string $password
160 * @param string $salt
161 *
162 * @throws LdapRecordException
163 *
164 * @return string
165 */
166 protected function getHashedPassword($method, $password, $salt = null)
167 {
168 if (! method_exists(Password::class, $method)) {
169 throw new LdapRecordException("Password hashing method [{$method}] does not exist.");
170 }
171
172 if (Password::hashMethodRequiresSalt($method)) {
173 return Password::{$method}($password, $salt);
174 }
175
176 return Password::{$method}($password);
177 }
178
179 /**
180 * Validates that the current LDAP connection is secure.
181 *
182 * @throws ConnectionException
183 *
184 * @return void
185 */
186 protected function validateSecureConnection()
187 {
188 $connection = $this->getConnection();
189
190 if ($connection->isConnected()) {
191 $secure = $connection->getLdapConnection()->canChangePasswords();
192 } else {
193 $secure = $connection->getConfiguration()->get('use_ssl') || $connection->getConfiguration()->get('use_tls');
194 }
195
196 if (! $secure) {
197 throw new ConnectionException(
198 'You must be connected to your LDAP server with TLS or SSL to perform this operation.'
199 );
200 }
201 }
202
203 /**
204 * Attempt to retrieve the password's salt.
205 *
206 * @param string $method
207 *
208 * @return string|null
209 */
210 public function getPasswordSalt($method)
211 {
212 if (! Password::hashMethodRequiresSalt($method)) {
213 return;
214 }
215
216 return Password::getSalt($this->password);
217 }
218
219 /**
220 * Determine the password hash method to use from the users current password.
221 *
222 * @return string|void
223 */
224 public function determinePasswordHashMethod()
225 {
226 if (! $password = $this->password) {
227 return;
228 }
229
230 if (! $method = Password::getHashMethod($password)) {
231 return;
232 }
233
234 [,$algo] = array_pad(
235 Password::getHashMethodAndAlgo($password) ?? [],
236 $length = 2,
237 $value = null
238 );
239
240 switch ($algo) {
241 case Password::CRYPT_SALT_TYPE_MD5:
242 return 'md5'.$method;
243 case Password::CRYPT_SALT_TYPE_SHA256:
244 return 'sha256'.$method;
245 case Password::CRYPT_SALT_TYPE_SHA512:
246 return 'sha512'.$method;
247 default:
248 return $method;
249 }
250 }
251}