|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
class FroxlorChangePasswordDriver implements \RainLoop\Providers\ChangePassword\ChangePasswordInterface |
|
4
|
|
|
{ |
|
5
|
|
|
/** |
|
6
|
|
|
* @var string |
|
7
|
|
|
*/ |
|
8
|
|
|
private $sDsn = ''; |
|
9
|
|
|
|
|
10
|
|
|
/** |
|
11
|
|
|
* @var string |
|
12
|
|
|
*/ |
|
13
|
|
|
private $sUser = ''; |
|
14
|
|
|
|
|
15
|
|
|
/** |
|
16
|
|
|
* @var string |
|
17
|
|
|
*/ |
|
18
|
|
|
private $sPassword = ''; |
|
19
|
|
|
|
|
20
|
|
|
/** |
|
21
|
|
|
* @var string |
|
22
|
|
|
*/ |
|
23
|
|
|
private $sAllowedEmails = ''; |
|
24
|
|
|
|
|
25
|
|
|
/** |
|
26
|
|
|
* @var \MailSo\Log\Logger |
|
27
|
|
|
*/ |
|
28
|
|
|
private $oLogger = null; |
|
29
|
|
|
|
|
30
|
|
|
/** |
|
31
|
|
|
* @param string $sDsn |
|
32
|
|
|
* @param string $sUser |
|
33
|
|
|
* @param string $sPassword |
|
34
|
|
|
* |
|
35
|
|
|
* @return \FroxlorChangePasswordDriver |
|
36
|
|
|
*/ |
|
37
|
|
|
public function SetConfig($sDsn, $sUser, $sPassword) |
|
38
|
|
|
{ |
|
39
|
|
|
$this->sDsn = $sDsn; |
|
40
|
|
|
$this->sUser = $sUser; |
|
41
|
|
|
$this->sPassword = $sPassword; |
|
42
|
|
|
|
|
43
|
|
|
return $this; |
|
44
|
|
|
} |
|
45
|
|
|
|
|
46
|
|
|
/** |
|
47
|
|
|
* @param string $sAllowedEmails |
|
48
|
|
|
* |
|
49
|
|
|
* @return \FroxlorChangePasswordDriver |
|
50
|
|
|
*/ |
|
51
|
|
|
public function SetAllowedEmails($sAllowedEmails) |
|
52
|
|
|
{ |
|
53
|
|
|
$this->sAllowedEmails = $sAllowedEmails; |
|
54
|
|
|
return $this; |
|
55
|
|
|
} |
|
56
|
|
|
|
|
57
|
|
|
/** |
|
58
|
|
|
* @param \MailSo\Log\Logger $oLogger |
|
59
|
|
|
* |
|
60
|
|
|
* @return \FroxlorChangePasswordDriver |
|
61
|
|
|
*/ |
|
62
|
|
|
public function SetLogger($oLogger) |
|
63
|
|
|
{ |
|
64
|
|
|
if ($oLogger instanceof \MailSo\Log\Logger) |
|
|
|
|
|
|
65
|
|
|
{ |
|
66
|
|
|
$this->oLogger = $oLogger; |
|
67
|
|
|
} |
|
68
|
|
|
|
|
69
|
|
|
return $this; |
|
70
|
|
|
} |
|
71
|
|
|
|
|
72
|
|
|
/** |
|
73
|
|
|
* @param \RainLoop\Account $oAccount |
|
74
|
|
|
* |
|
75
|
|
|
* @return bool |
|
76
|
|
|
*/ |
|
77
|
|
|
public function PasswordChangePossibility($oAccount) |
|
78
|
|
|
{ |
|
79
|
|
|
return $oAccount && $oAccount->Email() && |
|
80
|
|
|
\RainLoop\Plugins\Helper::ValidateWildcardValues($oAccount->Email(), $this->sAllowedEmails); |
|
81
|
|
|
} |
|
82
|
|
|
|
|
83
|
|
|
/** |
|
84
|
|
|
* @param \RainLoop\Account $oAccount |
|
85
|
|
|
* @param string $sPrevPassword |
|
86
|
|
|
* @param string $sNewPassword |
|
87
|
|
|
* |
|
88
|
|
|
* @return bool |
|
89
|
|
|
*/ |
|
90
|
|
|
public function ChangePassword(\RainLoop\Account $oAccount, $sPrevPassword, $sNewPassword) |
|
91
|
|
|
{ |
|
92
|
|
|
if ($this->oLogger) { |
|
93
|
|
|
$this->oLogger->Write('Froxlor: Try to change password for '.$oAccount->Email()); |
|
94
|
|
|
} |
|
95
|
|
|
|
|
96
|
|
|
$bResult = false; |
|
97
|
|
|
if (!empty($this->sDsn) && 0 < \strlen($this->sUser) && 0 < \strlen($this->sPassword) && $oAccount) |
|
98
|
|
|
{ |
|
99
|
|
|
try |
|
100
|
|
|
{ |
|
101
|
|
|
$oPdo = new \PDO($this->sDsn, $this->sUser, $this->sPassword); |
|
102
|
|
|
$oPdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); |
|
103
|
|
|
|
|
104
|
|
|
$oStmt = $oPdo->prepare('SELECT password_enc, id FROM mail_users WHERE username = ? LIMIT 1'); |
|
105
|
|
|
if ($oStmt->execute(array($oAccount->IncLogin()))) |
|
106
|
|
|
{ |
|
107
|
|
|
$aFetchResult = $oStmt->fetchAll(\PDO::FETCH_ASSOC); |
|
108
|
|
|
if (\is_array($aFetchResult) && isset($aFetchResult[0]['password_enc'], $aFetchResult[0]['id'])) |
|
109
|
|
|
{ |
|
110
|
|
|
$sDbPassword = \stripslashes($aFetchResult[0]['password_enc']); |
|
111
|
|
|
|
|
112
|
|
|
if ( $this->validatePasswordLogin( $sDbPassword, $sPrevPassword ) ) { |
|
113
|
|
|
$sEncNewPassword = $this->cryptPassword($sNewPassword, 3); |
|
114
|
|
|
$oStmt = $oPdo->prepare('UPDATE mail_users SET password_enc = ?,password = ? WHERE id = ?'); |
|
115
|
|
|
$bResult = (bool) $oStmt->execute( |
|
116
|
|
|
array($sEncNewPassword, $sNewPassword, $aFetchResult[0]['id'])); |
|
117
|
|
|
} |
|
118
|
|
|
} |
|
119
|
|
|
} |
|
120
|
|
|
} |
|
121
|
|
|
catch (\Exception $oException) |
|
122
|
|
|
{ |
|
123
|
|
|
if ($this->oLogger) |
|
124
|
|
|
{ |
|
125
|
|
|
$this->oLogger->WriteException($oException); |
|
126
|
|
|
} |
|
127
|
|
|
} |
|
128
|
|
|
} |
|
129
|
|
|
|
|
130
|
|
|
return $bResult; |
|
131
|
|
|
} |
|
132
|
|
|
|
|
133
|
|
|
/** |
|
134
|
|
|
* @param string $sPassword |
|
135
|
|
|
* @return string |
|
136
|
|
|
*/ |
|
137
|
|
|
private function cryptPassword($sPassword, $type = 3) |
|
138
|
|
|
{ |
|
139
|
|
|
return $this->makeCryptPassword($sPassword,$type); |
|
140
|
|
|
} |
|
141
|
|
|
|
|
142
|
|
|
/** |
|
143
|
|
|
* This file is part of the Froxlor project. |
|
144
|
|
|
* Copyright (c) 2010 the Froxlor Team (see authors). |
|
145
|
|
|
* |
|
146
|
|
|
* For the full copyright and license information, please view the COPYING |
|
147
|
|
|
* file that was distributed with this source code. You can also view the |
|
148
|
|
|
* COPYING file online at http://files.froxlor.org/misc/COPYING.txt |
|
149
|
|
|
* |
|
150
|
|
|
* @copyright (c) the authors |
|
151
|
|
|
* @author Michal Wojcik <[email protected]> |
|
152
|
|
|
* @author Michael Kaufmann <[email protected]> |
|
153
|
|
|
* @author Froxlor team <[email protected]> (2010-) |
|
154
|
|
|
* @license GPLv2 http://files.froxlor.org/misc/COPYING.txt |
|
155
|
|
|
* @package Functions |
|
156
|
|
|
* |
|
157
|
|
|
*/ |
|
158
|
|
|
|
|
159
|
|
|
/** |
|
160
|
|
|
* Make crypted password from clear text password |
|
161
|
|
|
* |
|
162
|
|
|
* @author Michal Wojcik <[email protected]> |
|
163
|
|
|
* @author Michael Kaufmann <[email protected]> |
|
164
|
|
|
* @author Froxlor team <[email protected]> (2010-) |
|
165
|
|
|
* |
|
166
|
|
|
* 0 - default crypt (depenend on system configuration) |
|
167
|
|
|
* 1 - MD5 $1$ |
|
168
|
|
|
* 2 - BLOWFISH $2a$ | $2y$07$ (on php 5.3.7+) |
|
169
|
|
|
* 3 - SHA-256 $5$ (default) |
|
170
|
|
|
* 4 - SHA-512 $6$ |
|
171
|
|
|
* |
|
172
|
|
|
* @param string $password Password to be crypted |
|
173
|
|
|
* |
|
174
|
|
|
* @return string encrypted password |
|
175
|
|
|
*/ |
|
176
|
|
|
private function makeCryptPassword ($password,$type = 3) { |
|
177
|
|
|
switch ($type) { |
|
178
|
|
|
case 0: |
|
179
|
|
|
$cryptPassword = \crypt($password); |
|
180
|
|
|
break; |
|
181
|
|
View Code Duplication |
case 1: |
|
|
|
|
|
|
182
|
|
|
$cryptPassword = \crypt($password, '$1$' . $this->generatePassword(true). $this->generatePassword(true)); |
|
183
|
|
|
break; |
|
184
|
|
|
case 2: |
|
185
|
|
|
if (\version_compare(\phpversion(), '5.3.7', '<')) { |
|
186
|
|
|
$cryptPassword = \crypt($password, '$2a$' . $this->generatePassword(true). $this->generatePassword(true)); |
|
187
|
|
|
} else { |
|
188
|
|
|
// Blowfish hashing with a salt as follows: "$2a$", "$2x$" or "$2y$", |
|
189
|
|
|
// a two digit cost parameter, "$", and 22 characters from the alphabet "./0-9A-Za-z" |
|
190
|
|
|
$cryptPassword = \crypt( |
|
191
|
|
|
$password, |
|
192
|
|
|
'$2y$07$' . \substr($this->generatePassword(true).$this->generatePassword(true).$this->generatePassword(true), 0, 22) |
|
193
|
|
|
); |
|
194
|
|
|
} |
|
195
|
|
|
break; |
|
196
|
|
View Code Duplication |
case 3: |
|
|
|
|
|
|
197
|
|
|
$cryptPassword = \crypt($password, '$5$' . $this->generatePassword(true). $this->generatePassword(true)); |
|
198
|
|
|
break; |
|
199
|
|
View Code Duplication |
case 4: |
|
|
|
|
|
|
200
|
|
|
$cryptPassword = \crypt($password, '$6$' . $this->generatePassword(true). $this->generatePassword(true)); |
|
201
|
|
|
break; |
|
202
|
|
|
default: |
|
203
|
|
|
$cryptPassword = \crypt($password); |
|
204
|
|
|
break; |
|
205
|
|
|
} |
|
206
|
|
|
|
|
207
|
|
|
return $cryptPassword; |
|
208
|
|
|
} |
|
209
|
|
|
|
|
210
|
|
|
/** |
|
211
|
|
|
* Generates a random password |
|
212
|
|
|
* |
|
213
|
|
|
* @param boolean $isSalt |
|
214
|
|
|
* optional, create a hash for a salt used in makeCryptPassword because crypt() does not like some special characters in its salts, default is false |
|
215
|
|
|
*/ |
|
216
|
|
|
private function generatePassword($isSalt = false) |
|
217
|
|
|
{ |
|
218
|
|
|
$alpha_lower = 'abcdefghijklmnopqrstuvwxyz'; |
|
219
|
|
|
$alpha_upper = \strtoupper($alpha_lower); |
|
220
|
|
|
$numeric = '0123456789'; |
|
221
|
|
|
$special = '!?<>§$%&+#='; |
|
|
|
|
|
|
222
|
|
|
$length = 10; |
|
223
|
|
|
|
|
224
|
|
|
$pw = $this->special_shuffle($alpha_lower); |
|
225
|
|
|
$n = \floor(($length) / 4); |
|
226
|
|
|
$pw .= \mb_substr($this->special_shuffle($alpha_upper), 0, $n); |
|
227
|
|
|
$pw .= \mb_substr($this->special_shuffle($numeric), 0, $n); |
|
228
|
|
|
$pw = \mb_substr($pw, - $length); |
|
229
|
|
|
return $this->special_shuffle($pw); |
|
230
|
|
|
} |
|
231
|
|
|
|
|
232
|
|
|
/** |
|
233
|
|
|
* multibyte-character safe shuffle function |
|
234
|
|
|
* |
|
235
|
|
|
* @param string $str |
|
236
|
|
|
* |
|
237
|
|
|
* @return string |
|
238
|
|
|
*/ |
|
239
|
|
|
private function special_shuffle($str = null) |
|
240
|
|
|
{ |
|
241
|
|
|
$len = \mb_strlen($str); |
|
242
|
|
|
$sploded = array(); |
|
243
|
|
|
while ($len -- > 0) { |
|
244
|
|
|
$sploded[] = \mb_substr($str, $len, 1); |
|
245
|
|
|
} |
|
246
|
|
|
\shuffle($sploded); |
|
247
|
|
|
return \join('', $sploded); |
|
248
|
|
|
} |
|
249
|
|
|
|
|
250
|
|
|
/** |
|
251
|
|
|
* Function validatePasswordLogin |
|
252
|
|
|
* |
|
253
|
|
|
* compare user password-hash with given user-password |
|
254
|
|
|
* and check if they are the same |
|
255
|
|
|
* additionally it updates the hash if the system settings changed |
|
256
|
|
|
* or if the very old md5() sum is used |
|
257
|
|
|
* |
|
258
|
|
|
* @param array $userinfo user-data from table |
|
|
|
|
|
|
259
|
|
|
* @param string $password the password to validate |
|
260
|
|
|
* @param string $table either panel_customers or panel_admins |
|
|
|
|
|
|
261
|
|
|
* @param string $uid user-id-field in $table |
|
|
|
|
|
|
262
|
|
|
* |
|
263
|
|
|
* @return boolean |
|
264
|
|
|
*/ |
|
265
|
|
|
private function validatePasswordLogin($pwd_hash, $password = null) { |
|
266
|
|
|
|
|
267
|
|
|
$systype = 3; // SHA256 |
|
268
|
|
|
$update_hash = false; |
|
|
|
|
|
|
269
|
|
|
// check for good'ole md5 |
|
270
|
|
|
if (\strlen($pwd_hash) == 32 && \ctype_xdigit($pwd_hash)) { |
|
271
|
|
|
$pwd_check = \md5($password); |
|
272
|
|
|
$update_hash = true; |
|
|
|
|
|
|
273
|
|
|
} else { |
|
274
|
|
|
// cut out the salt from the hash |
|
275
|
|
|
$pwd_salt = \str_replace(\substr(\strrchr($pwd_hash, "$"), 1), "", $pwd_hash); |
|
276
|
|
|
// create same hash to compare |
|
277
|
|
|
$pwd_check = \crypt($password, $pwd_salt); |
|
278
|
|
|
// check whether the hash needs to be updated |
|
279
|
|
|
$hash_type_chk = \substr($pwd_hash, 0, 3); |
|
280
|
|
|
if (($systype == 1 && $hash_type_chk != '$1$') || // MD5 |
|
281
|
|
|
($systype == 2 && $hash_type_chk != '$2$') || // BLOWFISH |
|
282
|
|
|
($systype == 3 && $hash_type_chk != '$5$') || // SHA256 |
|
283
|
|
|
($systype == 4 && $hash_type_chk != '$6$') // SHA512 |
|
284
|
|
|
) { |
|
285
|
|
|
$update_hash = true; |
|
|
|
|
|
|
286
|
|
|
} |
|
287
|
|
|
} |
|
288
|
|
|
|
|
289
|
|
|
return $pwd_check; |
|
290
|
|
|
} |
|
291
|
|
|
|
|
292
|
|
|
|
|
293
|
|
|
} |
|
294
|
|
|
|
This error could be the result of:
1. Missing dependencies
PHP Analyzer uses your
composer.jsonfile (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects thecomposer.jsonto be in the root folder of your repository.Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the
requireorrequire-devsection?2. Missing use statement
PHP does not complain about undefined classes in
ìnstanceofchecks. For example, the following PHP code will work perfectly fine:If you have not tested against this specific condition, such errors might go unnoticed.