These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | $defflip = (!cfip()) ? exit(header('HTTP/1.1 401 Unauthorized')) : 1; |
||
3 | |||
4 | class User extends Base { |
||
5 | protected $table = 'accounts'; |
||
6 | private $userID = false; |
||
7 | private $user = array(); |
||
8 | |||
9 | /** |
||
10 | * We allow changing the database for shared accounts across pools |
||
11 | * Load the config on construct so we can assign the DB name |
||
12 | * @param config array MPOS configuration |
||
13 | * @return none |
||
14 | **/ |
||
15 | public function __construct($config) { |
||
16 | $this->setConfig($config); |
||
17 | $this->table = $this->config['db']['shared']['accounts'] . '.' . $this->table; |
||
18 | } |
||
19 | |||
20 | // get and set methods |
||
21 | private function getHash($string, $version=0, $pepper='') { |
||
22 | switch($version) { |
||
23 | case 0: |
||
24 | return hash('sha256', $string.$this->salt); |
||
25 | break; |
||
26 | case 1: |
||
27 | return '$' . $version . '$' . $pepper . '$' . hash('sha256', $string.$this->salt.$pepper); |
||
28 | break; |
||
29 | } |
||
30 | } |
||
31 | public function getUserName($id) { |
||
32 | return $this->getSingle($id, 'username', 'id'); |
||
33 | } |
||
34 | public function getUserNameAnon($id) { |
||
35 | return $this->getSingle($id, 'is_anonymous', 'id'); |
||
36 | } |
||
37 | public function getUserNameByEmail($email) { |
||
38 | return $this->getSingle($email, 'username', 'email', 's'); |
||
39 | } |
||
40 | public function getUserId($username, $lower=false) { |
||
41 | return $this->getSingle($username, 'id', 'username', 's', $lower); |
||
42 | } |
||
43 | public function getUserIdByEmail($email, $lower=false) { |
||
44 | return $this->getSingle($email, 'id', 'email', 's', $lower); |
||
45 | } |
||
46 | public function getUserEmail($username, $lower=false) { |
||
47 | return $this->getSingle($username, 'email', 'username', 's', $lower); |
||
48 | } |
||
49 | public function getUserEmailById($id) { |
||
50 | return $this->getSingle($id, 'email', 'id', 'i'); |
||
51 | } |
||
52 | public function getUserPasswordHashById($id) { |
||
53 | return $this->getSingle($id, 'pass', 'id', 'i'); |
||
54 | } |
||
55 | public function getUserPinHashById($id) { |
||
56 | return $this->getSingle($id, 'pin', 'id', 'i'); |
||
57 | } |
||
58 | public function getUserNoFee($id) { |
||
59 | return $this->getSingle($id, 'no_fees', 'id'); |
||
60 | } |
||
61 | public function getUserDonatePercent($id) { |
||
62 | return $this->getDonatePercent($id); |
||
63 | } |
||
64 | public function getUserAdmin($id) { |
||
65 | return $this->getSingle($id, 'is_admin', 'id'); |
||
66 | } |
||
67 | public function getUserLocked($id) { |
||
68 | return $this->getSingle($id, 'is_locked', 'id'); |
||
69 | } |
||
70 | public function getUserIp($id) { |
||
71 | return $this->getSingle($id, 'loggedIp', 'id'); |
||
72 | } |
||
73 | public function getLastLogin($id) { |
||
74 | return $this->getSingle($id, 'last_login', 'id'); |
||
75 | } |
||
76 | public function getEmail($email) { |
||
77 | return $this->getSingle($email, 'email', 'email', 's'); |
||
78 | } |
||
79 | public function getUserFailed($id) { |
||
80 | return $this->getSingle($id, 'failed_logins', 'id'); |
||
81 | } |
||
82 | public function getUserPinFailed($id) { |
||
83 | return $this->getSingle($id, 'failed_pins', 'id'); |
||
84 | } |
||
85 | public function isNoFee($id) { |
||
86 | return $this->getUserNoFee($id); |
||
87 | } |
||
88 | public function isLocked($id) { |
||
89 | return $this->getUserLocked($id); |
||
90 | } |
||
91 | public function isAdmin($id) { |
||
92 | return $this->getUserAdmin($id); |
||
93 | } |
||
94 | public function getSignupTime($id) { |
||
95 | return $this->getSingle($id, 'signup_timestamp', 'id'); |
||
96 | } |
||
97 | public function changeNoFee($id) { |
||
98 | $field = array('name' => 'no_fees', 'type' => 'i', 'value' => !$this->isNoFee($id)); |
||
99 | $this->log->log("warn", $this->getUserName($id)." changed no_fees to ".$this->isNoFee($id)); |
||
100 | return $this->updateSingle($id, $field); |
||
101 | } |
||
102 | public function setLocked($id, $value) { |
||
103 | $field = array('name' => 'is_locked', 'type' => 'i', 'value' => $value); |
||
104 | $this->log->log("warn", $this->getUserName($id)." changed is_locked to $value"); |
||
105 | return $this->updateSingle($id, $field); |
||
106 | } |
||
107 | public function changeAdmin($id) { |
||
108 | $field = array('name' => 'is_admin', 'type' => 'i', 'value' => !$this->isAdmin($id)); |
||
109 | $this->log->log("warn", $this->getUserName($id)." changed is_admin to ".$this->isAdmin($id)); |
||
110 | return $this->updateSingle($id, $field); |
||
111 | } |
||
112 | public function setUserFailed($id, $value) { |
||
113 | $field = array( 'name' => 'failed_logins', 'type' => 'i', 'value' => $value); |
||
114 | return $this->updateSingle($id, $field); |
||
115 | } |
||
116 | public function setUserPinFailed($id, $value) { |
||
117 | $field = array( 'name' => 'failed_pins', 'type' => 'i', 'value' => $value); |
||
118 | return $this->updateSingle($id, $field); |
||
119 | } |
||
120 | private function incUserFailed($id) { |
||
121 | $field = array( 'name' => 'failed_logins', 'type' => 'i', 'value' => $this->getUserFailed($id) + 1); |
||
122 | return $this->updateSingle($id, $field); |
||
123 | } |
||
124 | private function incUserPinFailed($id) { |
||
125 | $field = array( 'name' => 'failed_pins', 'type' => 'i', 'value' => $this->getUserPinFailed($id) + 1); |
||
126 | return $this->updateSingle($id, $field); |
||
127 | } |
||
128 | private function setUserIp($id, $ip) { |
||
129 | $field = array( 'name' => 'loggedIp', 'type' => 's', 'value' => $ip ); |
||
130 | return $this->updateSingle($id, $field); |
||
131 | } |
||
132 | |||
133 | /** |
||
134 | * Fetch all users for administrative tasks |
||
135 | * @param none |
||
136 | * @return data array All users with db columns as array fields |
||
137 | **/ |
||
138 | public function getUsers($filter='%') { |
||
139 | $stmt = $this->mysqli->prepare("SELECT * FROM " . $this->getTableName() . " WHERE username LIKE ?"); |
||
140 | if ($this->checkStmt($stmt) && $stmt->bind_param('s', $filter) && $stmt->execute() && $result = $stmt->get_result()) { |
||
141 | return $result->fetch_all(MYSQLI_ASSOC); |
||
142 | } |
||
143 | } |
||
144 | |||
145 | /** |
||
146 | * Fetch last registered users for administrative tasks |
||
147 | * @param none |
||
148 | * @return data array All users with db columns as array fields |
||
149 | **/ |
||
150 | View Code Duplication | public function getLastRegisteredUsers($limit=10,$start=0) { |
|
151 | $this->debug->append("STA " . __METHOD__, 4); |
||
152 | $invitation = new Invitation(); |
||
153 | $invitation->setMysql($this->mysqli); |
||
154 | $invitation->setDebug($this->debug); |
||
155 | $invitation->setLog($this->log); |
||
156 | $stmt = $this->mysqli->prepare(" |
||
157 | SELECT a.id,a.username as mposuser,a.email,a.signup_timestamp,u.username AS inviter FROM " . $this->getTableName() . " AS a |
||
158 | LEFT JOIN " . $invitation->getTableName() . " AS i |
||
159 | ON a.email = i.email |
||
160 | LEFT JOIN " . $this->getTableName() . " AS u |
||
161 | ON i.account_id = u.id |
||
162 | ORDER BY a.id DESC LIMIT ?,?"); |
||
163 | if ($this->checkStmt($stmt) && $stmt->bind_param("ii", $start, $limit) && $stmt->execute() && $result = $stmt->get_result()) { |
||
164 | return $result->fetch_all(MYSQLI_ASSOC); |
||
165 | } |
||
166 | } |
||
167 | |||
168 | /** |
||
169 | * Fetch Top 10 Inviters |
||
170 | * @param none |
||
171 | * @return data array All users with db columns as array fields |
||
172 | **/ |
||
173 | View Code Duplication | public function getTopInviters($limit=10,$start=0) { |
|
174 | $this->debug->append("STA " . __METHOD__, 4); |
||
175 | $invitation = new Invitation(); |
||
176 | $invitation->setMysql($this->mysqli); |
||
177 | $invitation->setDebug($this->debug); |
||
178 | $invitation->setLog($this->log); |
||
179 | $stmt = $this->mysqli->prepare(" |
||
180 | SELECT COUNT(i.account_id) AS invitationcount,a.id,a.username,a.email, |
||
181 | (SELECT COUNT(account_id) FROM " . $invitation->getTableName() . " WHERE account_id = i.account_id AND is_activated = 1 GROUP BY account_id) AS activated |
||
182 | FROM " . $invitation->getTableName() . " AS i |
||
183 | LEFT JOIN " . $this->getTableName() . " AS a |
||
184 | ON a.id = i.account_id |
||
185 | GROUP BY i.account_id |
||
186 | ORDER BY invitationcount ASC |
||
187 | LIMIT ?,?"); |
||
188 | if ($this->checkStmt($stmt) && $stmt->bind_param("ii", $start, $limit) && $stmt->execute() && $result = $stmt->get_result()) { |
||
189 | return $result->fetch_all(MYSQLI_ASSOC); |
||
190 | } |
||
191 | } |
||
192 | |||
193 | /** |
||
194 | * Check user login |
||
195 | * @param username string Username |
||
196 | * @param password string Password |
||
197 | * @return bool |
||
198 | **/ |
||
199 | public function checkLogin($username, $password) { |
||
0 ignored issues
–
show
|
|||
200 | $this->debug->append("STA " . __METHOD__, 4); |
||
201 | $this->debug->append("Checking login for $username with password $password", 2); |
||
202 | if (empty($username) || empty($password)) { |
||
203 | $this->setErrorMessage("Invalid username or password."); |
||
204 | return false; |
||
205 | } |
||
206 | if (!filter_var($username, FILTER_VALIDATE_EMAIL)) { |
||
207 | $this->debug->append("Not an e-mail address, rejecting login", 2); |
||
208 | $this->setErrorMessage("Please login with your e-mail address"); |
||
209 | return false; |
||
210 | } else { |
||
211 | $this->debug->append("Username is an e-mail: $username", 2); |
||
212 | if (!$username = $this->getUserNameByEmail($username)) { |
||
213 | $this->setErrorMessage("Invalid username or password."); |
||
214 | return false; |
||
215 | } |
||
216 | } |
||
217 | if ($this->isLocked($this->getUserId($username))) { |
||
218 | $this->setErrorMessage('Account locked. Please Check your Email for instructions to unlock.'); |
||
219 | return false; |
||
220 | } |
||
221 | if ($this->checkUserPassword($username, $password)) { |
||
222 | // delete notification cookies |
||
223 | setcookie("motd-box", "", time()-3600); |
||
224 | setcookie("lastlogin-box", "", time()-3600); |
||
225 | setcookie("backend-box", "", time()-3600); |
||
226 | // rest of login process |
||
227 | $uid = $this->getUserId($username); |
||
228 | $lastLoginTime = $this->getLastLogin($uid); |
||
229 | $this->updateLoginTimestamp($uid); |
||
230 | $getIPAddress = $this->getUserIp($uid); |
||
231 | if ($getIPAddress !== $this->getCurrentIP()) { |
||
232 | $this->log->log("warn", "$username has logged in with a different IP, saved is [$getIPAddress]"); |
||
233 | } |
||
234 | $setIPAddress = $this->setUserIp($uid, $_SERVER['REMOTE_ADDR']); |
||
235 | $this->createSession($username, $getIPAddress, $lastLoginTime); |
||
236 | if ($setIPAddress) { |
||
237 | // send a notification if success_login is active |
||
238 | $uid = $this->getUserId($username); |
||
239 | $notifs = new Notification(); |
||
240 | $notifs->setDebug($this->debug); |
||
241 | $notifs->setMysql($this->mysqli); |
||
242 | $notifs->setSmarty($this->smarty); |
||
243 | $notifs->setConfig($this->config); |
||
244 | $notifs->setSetting($this->setting); |
||
245 | $notifs->setErrorCodes($this->aErrorCodes); |
||
246 | $ndata = $notifs->getNotificationSettings($uid); |
||
247 | if ((array_key_exists('push_success_lo', $ndata) && $ndata['push_success_lo']) || (array_key_exists('success_login', $ndata) && $ndata['success_login'])){ |
||
248 | // seems to be active, let's send it |
||
249 | $aDataN['username'] = $username; |
||
250 | $aDataN['email'] = $this->getUserEmail($username); |
||
251 | $aDataN['subject'] = 'Successful login notification'; |
||
252 | $aDataN['LOGINIP'] = $this->getCurrentIP(); |
||
253 | $aDataN['LOGINUSER'] = $username; |
||
254 | $aDataN['LOGINTIME'] = date('m/d/y H:i:s'); |
||
255 | $notifs->sendNotification($uid, 'success_login', $aDataN); |
||
256 | } |
||
257 | return true; |
||
258 | } |
||
259 | } |
||
260 | $this->setErrorMessage("Invalid username or password"); |
||
261 | $this->log->log('error', "Authentication failed for $username"); |
||
262 | View Code Duplication | if ($id = $this->getUserId($username)) { |
|
263 | $this->incUserFailed($id); |
||
264 | // Check if this account should be locked |
||
265 | if (isset($this->config['maxfailed']['login']) && $this->getUserFailed($id) >= $this->config['maxfailed']['login']) { |
||
266 | $this->setLocked($id, 1); |
||
267 | $this->log->log("warn", "$username locked due to failed logins, saved is [".$this->getUserIp($this->getUserId($username))."]"); |
||
268 | if ($token = $this->token->createToken('account_unlock', $id)) { |
||
269 | $aData['token'] = $token; |
||
270 | $aData['username'] = $username; |
||
271 | $aData['email'] = $this->getUserEmail($username); |
||
272 | $aData['subject'] = 'Account auto-locked'; |
||
273 | $this->mail->sendMail('notifications/locked', $aData); |
||
274 | } |
||
275 | } |
||
276 | } |
||
277 | |||
278 | return false; |
||
279 | } |
||
280 | |||
281 | /** |
||
282 | * Check the users PIN for confirmation |
||
283 | * @param userID int User ID |
||
284 | * @param pin int PIN to check |
||
285 | * @return bool |
||
286 | **/ |
||
287 | public function checkPin($userId, $pin='') { |
||
288 | $this->debug->append("STA " . __METHOD__, 4); |
||
289 | $this->debug->append("Confirming PIN for $userId and pin $pin", 2); |
||
290 | $strPinHash = $this->getUserPinHashById($userId); |
||
291 | $aPin = explode('$', $strPinHash); |
||
292 | count($aPin) == 1 ? $pin_hash = $this->getHash($pin, 0) : $pin_hash = $this->getHash($pin, $aPin[1], $aPin[2]); |
||
293 | $stmt = $this->mysqli->prepare("SELECT pin FROM $this->table WHERE id = ? AND pin = ? LIMIT 1"); |
||
294 | if ($stmt->bind_param('is', $userId, $pin_hash) && $stmt->execute() && $stmt->bind_result($row_pin) && $stmt->fetch()) { |
||
295 | $this->setUserPinFailed($userId, 0); |
||
296 | return ($pin_hash === $row_pin); |
||
297 | } |
||
298 | $this->log->log('info', $this->getUserName($userId).' incorrect pin'); |
||
299 | $this->incUserPinFailed($userId); |
||
300 | // Check if this account should be locked |
||
301 | View Code Duplication | if (isset($this->config['maxfailed']['pin']) && $this->getUserPinFailed($userId) >= $this->config['maxfailed']['pin']) { |
|
302 | $this->setLocked($userId, 1); |
||
303 | $this->log->log("warn", $this->getUserName($userId)." was locked due to incorrect pins"); |
||
304 | if ($token = $this->token->createToken('account_unlock', $userId)) { |
||
305 | $username = $this->getUserName($userId); |
||
306 | $aData['token'] = $token; |
||
307 | $aData['username'] = $username; |
||
308 | $aData['email'] = $this->getUserEmail($username); |
||
309 | $aData['subject'] = 'Account auto-locked'; |
||
310 | $this->mail->sendMail('notifications/locked', $aData); |
||
311 | } |
||
312 | $this->logoutUser(); |
||
313 | } |
||
314 | return false; |
||
315 | } |
||
316 | |||
317 | public function generatePin($userID, $current) { |
||
318 | $this->debug->append("STA " . __METHOD__, 4); |
||
319 | $username = $this->getUserName($userID); |
||
320 | $email = $this->getUserEmail($username); |
||
321 | $strPasswordHash = $this->getUserPasswordHashById($userID); |
||
322 | $aPassword = explode('$', $strPasswordHash); |
||
323 | count($aPassword) == 1 ? $password_hash = $this->getHash($current, 0) : $password_hash = $this->getHash($current, $aPassword[1], $aPassword[2]); |
||
324 | $newpin = intval( '0' . rand(1,9) . rand(0,9) . rand(0,9) . rand(0,9) ); |
||
325 | $aData['username'] = $username; |
||
326 | $aData['email'] = $email; |
||
327 | $aData['pin'] = $newpin; |
||
328 | $newpin = $this->getHash($newpin, HASH_VERSION, bin2hex(openssl_random_pseudo_bytes(32))); |
||
329 | $aData['subject'] = 'PIN Reset Request'; |
||
330 | $stmt = $this->mysqli->prepare("UPDATE $this->table SET pin = ? WHERE ( id = ? AND pass = ? )"); |
||
331 | if ($this->checkStmt($stmt) && $stmt->bind_param('sis', $newpin, $userID, $password_hash) && $stmt->execute()) { |
||
332 | if ($stmt->errno == 0 && $stmt->affected_rows === 1) { |
||
333 | if ($this->mail->sendMail('pin/reset', $aData)) { |
||
334 | $this->log->log("info", "$username was sent a pin reset e-mail"); |
||
335 | return true; |
||
336 | } else { |
||
337 | $this->log->log("warn", "$username request a pin reset but failed to send mail"); |
||
338 | $this->setErrorMessage('Unable to send mail to your address'); |
||
339 | return false; |
||
340 | } |
||
341 | } |
||
342 | } |
||
343 | $this->log->log("warn", "$username incorrect pin reset attempt"); |
||
344 | $this->setErrorMessage( 'Unable to generate PIN, current password incorrect?' ); |
||
345 | return false; |
||
346 | } |
||
347 | |||
348 | /** |
||
349 | * Get all users that have auto payout setup |
||
350 | * @param none |
||
351 | * @return data array All users with payout setup |
||
352 | **/ |
||
353 | public function getAllAutoPayout() { |
||
354 | $this->debug->append("STA " . __METHOD__, 4); |
||
355 | $stmt = $this->mysqli->prepare(" |
||
356 | SELECT |
||
357 | a.id, a.username, ca.coin_address AS coin_address, ca.ap_threshold |
||
358 | FROM " . $this->getTableName() . " AS a |
||
359 | LEFT JOIN " . $this->coin_address->getTableName() . " AS ca |
||
360 | ON a.id = ca.account_id |
||
361 | WHERE ca.ap_threshold > 0 AND ca.currency = ? |
||
362 | AND ca.coin_address IS NOT NULL |
||
363 | "); |
||
364 | View Code Duplication | if ( $this->checkStmt($stmt) && $stmt->bind_param('s', $this->config['currency']) && $stmt->execute() && $result = $stmt->get_result()) { |
|
365 | return $result->fetch_all(MYSQLI_ASSOC); |
||
366 | } |
||
367 | $this->debug->append("Unable to fetch users with AP set"); |
||
368 | return false; |
||
369 | } |
||
370 | |||
371 | /** |
||
372 | * Fetch users donation value |
||
373 | * @param userID int UserID |
||
374 | * @return data string Coin Address |
||
375 | **/ |
||
376 | public function getDonatePercent($userID) { |
||
377 | $this->debug->append("STA " . __METHOD__, 4); |
||
378 | $dPercent = $this->getSingle($userID, 'donate_percent', 'id'); |
||
379 | if ($dPercent > 100) $dPercent = 100; |
||
380 | if ($dPercent < 0) $dPercent = 0; |
||
381 | return $dPercent; |
||
382 | } |
||
383 | |||
384 | /** |
||
385 | * Send e-mail to confirm a change for 2fa |
||
386 | * @param strType string Token type name |
||
387 | * @param userID int User ID |
||
388 | * @return bool |
||
389 | */ |
||
390 | public function sendChangeConfigEmail($strType, $userID) { |
||
391 | $exists = $this->token->doesTokenExist($strType, $userID); |
||
392 | if ($exists == 0) { |
||
393 | $token = $this->token->createToken($strType, $userID); |
||
394 | $aData['token'] = $token; |
||
395 | $aData['username'] = $this->getUserName($userID); |
||
396 | $aData['email'] = $this->getUserEmail($aData['username']); |
||
397 | switch ($strType) { |
||
398 | case 'account_edit': |
||
399 | $aData['subject'] = 'Account detail change confirmation'; |
||
400 | break; |
||
401 | case 'change_pw': |
||
402 | $aData['subject'] = 'Account password change confirmation'; |
||
403 | break; |
||
404 | case 'withdraw_funds': |
||
405 | $aData['subject'] = 'Manual payout request confirmation'; |
||
406 | break; |
||
407 | default: |
||
408 | $aData['subject'] = ''; |
||
409 | } |
||
410 | $this->log->log("info", $aData['username']." was sent a $strType token e-mail"); |
||
411 | if ($this->mail->sendMail('notifications/'.$strType, $aData)) { |
||
412 | return true; |
||
413 | } else { |
||
414 | $this->setErrorMessage('Failed to send the notification'); |
||
415 | $this->log->log("warn", $aData['username']." requested a $strType token but sending mail failed"); |
||
416 | return false; |
||
417 | } |
||
418 | } |
||
419 | $this->log->log("warn", $this->getUserName($userID)." attempted to request multiple $strType tokens"); |
||
420 | $this->setErrorMessage('A request has already been sent to your e-mail address. Please wait an hour for it to expire.'); |
||
421 | return false; |
||
422 | } |
||
423 | |||
424 | /** |
||
425 | * Update the accounts password |
||
426 | * @param userID int User ID |
||
427 | * @param current string Current password |
||
428 | * @param new1 string New password |
||
429 | * @param new2 string New password confirmation |
||
430 | * @param strToken string Token for confirmation |
||
431 | * @return bool |
||
432 | **/ |
||
433 | public function updatePassword($userID, $current, $new1, $new2, $strToken) { |
||
434 | $this->debug->append("STA " . __METHOD__, 4); |
||
435 | if ($new1 !== $new2) { |
||
436 | $this->setErrorMessage( 'New passwords do not match' ); |
||
437 | return false; |
||
438 | } |
||
439 | if ( strlen($new1) < 8 ) { |
||
440 | $this->setErrorMessage( 'New password is too short, please use more than 8 chars' ); |
||
441 | return false; |
||
442 | } |
||
443 | $strPasswordHash = $this->getUserPasswordHashById($userID); |
||
444 | $aPassword = explode('$', $strPasswordHash); |
||
445 | count($aPassword) == 1 ? $password_hash = $this->getHash($current, 0) : $password_hash = $this->getHash($current, $aPassword[1], $aPassword[2]); |
||
446 | $new = $this->getHash($new1, HASH_VERSION, bin2hex(openssl_random_pseudo_bytes(32))); |
||
447 | View Code Duplication | if ($this->config['twofactor']['enabled'] && $this->config['twofactor']['options']['changepw']) { |
|
448 | $tValid = $this->token->isTokenValid($userID, $strToken, 6); |
||
449 | if ($tValid) { |
||
450 | if ($this->token->deleteToken($strToken)) { |
||
451 | $this->log->log("info", $this->getUserName($userID)." deleted change password token"); |
||
452 | // token deleted, continue |
||
453 | } else { |
||
454 | $this->log->log("warn", $this->getUserName($userID)." failed to delete the change password token"); |
||
455 | $this->setErrorMessage('Token deletion failed'); |
||
456 | return false; |
||
457 | } |
||
458 | } else { |
||
459 | $this->log->log("error", $this->getUserName($userID)." attempted to use an invalid change password token"); |
||
460 | $this->setErrorMessage('Invalid token'); |
||
461 | return false; |
||
462 | } |
||
463 | } |
||
464 | $stmt = $this->mysqli->prepare("UPDATE $this->table SET pass = ? WHERE ( id = ? AND pass = ? )"); |
||
465 | if ($this->checkStmt($stmt)) { |
||
466 | $stmt->bind_param('sis', $new, $userID, $password_hash); |
||
467 | $stmt->execute(); |
||
468 | if ($stmt->errno == 0 && $stmt->affected_rows === 1) { |
||
469 | $this->log->log("info", $this->getUserName($userID)." updated password"); |
||
470 | return true; |
||
471 | } |
||
472 | $stmt->close(); |
||
473 | } |
||
474 | $this->log->log("warn", $this->getUserName($userID)." incorrect password update attempt"); |
||
475 | $this->setErrorMessage( 'Unable to update password, current password wrong?' ); |
||
476 | return false; |
||
477 | } |
||
478 | |||
479 | /** |
||
480 | * Update account information from the edit account page |
||
481 | * @param userID int User ID |
||
482 | * @param address string new coin address |
||
483 | * @param threshold float auto payout threshold |
||
484 | * @param donat float donation % of income |
||
485 | * @param strToken string Token for confirmation |
||
486 | * @return bool |
||
487 | **/ |
||
488 | public function updateAccount($userID, $address, $threshold, $donate, $email, $timezone, $is_anonymous, $strToken) { |
||
489 | $this->debug->append("STA " . __METHOD__, 4); |
||
490 | $bUser = false; |
||
491 | $donate = round($donate, 2); |
||
492 | // number validation checks |
||
493 | if (!is_numeric($threshold)) { |
||
494 | $this->setErrorMessage('Invalid input for auto-payout'); |
||
495 | return false; |
||
496 | } else if ($threshold < $this->config['ap_threshold']['min'] && $threshold != 0) { |
||
497 | $this->setErrorMessage('Threshold below configured minimum of ' . $this->config['ap_threshold']['min']); |
||
498 | return false; |
||
499 | } else if ($threshold > $this->config['ap_threshold']['max']) { |
||
500 | $this->setErrorMessage('Threshold above configured maximum of ' . $this->config['ap_threshold']['max']); |
||
501 | return false; |
||
502 | } |
||
503 | if (!is_numeric($donate)) { |
||
504 | $this->setErrorMessage('Invalid input for donation'); |
||
505 | return false; |
||
506 | } else if ($donate < $this->config['donate_threshold']['min'] && $donate != 0) { |
||
507 | $this->setErrorMessage('Donation below allowed ' . $this->config['donate_threshold']['min'] . '% limit'); |
||
508 | return false; |
||
509 | } else if ($donate > 100) { |
||
510 | $this->setErrorMessage('Donation above allowed 100% limit'); |
||
511 | return false; |
||
512 | } |
||
513 | if ($email != 'hidden' && $email != NULL && !filter_var($email, FILTER_VALIDATE_EMAIL)) { |
||
514 | $this->setErrorMessage('Invalid email address'); |
||
515 | return false; |
||
516 | } |
||
517 | if (!empty($address)) { |
||
518 | View Code Duplication | if ($address != $this->coin_address->getCoinAddress($userID) && $this->coin_address->existsCoinAddress($address)) { |
|
519 | $this->setErrorMessage('Address is already in use'); |
||
520 | return false; |
||
521 | } |
||
522 | if ($this->bitcoin->can_connect() === true) { |
||
523 | if (!$this->bitcoin->validateaddress($address)) { |
||
524 | $this->setErrorMessage('Invalid coin address'); |
||
525 | return false; |
||
526 | } |
||
527 | } else { |
||
528 | $this->setErrorMessage('Unable to connect to RPC server for coin address validation'); |
||
529 | return false; |
||
530 | } |
||
531 | } else { |
||
532 | $address = NULL; |
||
533 | } |
||
534 | |||
535 | // Number sanitizer, just in case we fall through above |
||
536 | $threshold = min($this->config['ap_threshold']['max'], max(0, floatval($threshold))); |
||
537 | $donate = min(100, max(0, floatval($donate))); |
||
538 | |||
539 | // twofactor - consume the token if it is enabled and valid |
||
540 | View Code Duplication | if ($this->config['twofactor']['enabled'] && $this->config['twofactor']['options']['details']) { |
|
541 | $tValid = $this->token->isTokenValid($userID, $strToken, 5); |
||
542 | if ($tValid) { |
||
543 | if ($this->token->deleteToken($strToken)) { |
||
544 | $this->log->log("info", $this->getUserName($userID)." deleted account update token"); |
||
545 | } else { |
||
546 | $this->setErrorMessage('Token deletion failed'); |
||
547 | $this->log->log("warn", $this->getUserName($userID)." updated their account details but failed to delete token"); |
||
548 | return false; |
||
549 | } |
||
550 | } else { |
||
551 | $this->setErrorMessage('Invalid token'); |
||
552 | $this->log->log("warn", $this->getUserName($userID)." attempted to use an invalid token account update token"); |
||
553 | return false; |
||
554 | } |
||
555 | } |
||
556 | |||
557 | // If we hide our email or it's not set, fetch current one to update |
||
558 | if ($email == 'hidden' || $email == NULL) |
||
559 | $email = $this->getUserEmailById($userID); |
||
560 | // We passed all validation checks so update the account |
||
561 | $stmt = $this->mysqli->prepare("UPDATE $this->table SET donate_percent = ?, email = ?, timezone = ?, is_anonymous = ? WHERE id = ?"); |
||
562 | if ($this->checkStmt($stmt) && $stmt->bind_param('dssii', $donate, $email, $timezone, $is_anonymous, $userID) && $stmt->execute()) { |
||
563 | $this->log->log("info", $this->getUserName($userID)." updated their account details"); |
||
564 | // Update coin address and ap_threshold if coin_address is set |
||
565 | if ($address) { |
||
566 | if ($this->coin_address->update($userID, $address, $threshold)) { |
||
567 | return true; |
||
568 | } |
||
569 | } else { |
||
570 | if ($this->coin_address->remove($userID, $address)) { |
||
571 | return true; |
||
572 | } |
||
573 | } |
||
574 | } |
||
575 | // Catchall |
||
576 | $this->setErrorMessage('Failed to update your account'); |
||
577 | $this->debug->append('Account update failed: ' . $this->mysqli->error); |
||
578 | return false; |
||
579 | } |
||
580 | |||
581 | /** |
||
582 | * Check API key for authentication |
||
583 | * @param key string API key hash |
||
584 | * @return bool |
||
585 | **/ |
||
586 | public function checkApiKey($key) { |
||
587 | $this->debug->append("STA " . __METHOD__, 4); |
||
588 | if (!is_string($key)) return false; |
||
589 | $stmt = $this->mysqli->prepare("SELECT api_key, id FROM $this->table WHERE api_key = ? LIMIT 1"); |
||
590 | if ($this->checkStmt($stmt) && $stmt->bind_param("s", $key) && $stmt->execute() && $stmt->bind_result($api_key, $id) && $stmt->fetch()) { |
||
591 | if ($api_key === $key) |
||
592 | return $id; |
||
593 | } |
||
594 | header("HTTP/1.1 401 Unauthorized"); |
||
595 | die('Access denied'); |
||
596 | } |
||
597 | |||
598 | /** |
||
599 | * Check a password for a user |
||
600 | * @param username string Username |
||
601 | * @param password string Password |
||
602 | * @return bool |
||
603 | **/ |
||
604 | private function checkUserPassword($username, $password) { |
||
605 | $this->debug->append("STA " . __METHOD__, 4); |
||
606 | $user = array(); |
||
607 | $stmt = $this->mysqli->prepare("SELECT username, pass, id, timezone, is_admin FROM $this->table WHERE LOWER(username) = LOWER(?) LIMIT 1"); |
||
608 | if ($this->checkStmt($stmt) && $stmt->bind_param('s', $username) && $stmt->execute() && $stmt->bind_result($row_username, $row_password, $row_id, $row_timezone, $row_admin)) { |
||
609 | $stmt->fetch(); |
||
610 | $stmt->close(); |
||
611 | $aPassword = explode('$', $row_password); |
||
612 | count($aPassword) == 1 ? $password_hash = $this->getHash($password, 0) : $password_hash = $this->getHash($password, $aPassword[1], $aPassword[2]); |
||
613 | // Store the basic login information |
||
614 | $this->user = array('username' => $row_username, 'id' => $row_id, 'timezone' => $row_timezone, 'is_admin' => $row_admin); |
||
615 | return $password_hash === $row_password && strtolower($username) === strtolower($row_username); |
||
616 | } |
||
617 | return $this->sqlError(); |
||
618 | } |
||
619 | |||
620 | /** |
||
621 | * Create a PHP session for a user |
||
622 | * @param username string Username to create session for |
||
623 | * @return none |
||
624 | **/ |
||
625 | private function createSession($username, $lastIP='', $lastLoginTime='') { |
||
0 ignored issues
–
show
createSession uses the super-global variable $_SESSION which is generally not recommended.
Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable: // Bad
class Router
{
public function generate($path)
{
return $_SERVER['HOST'].$path;
}
}
// Better
class Router
{
private $host;
public function __construct($host)
{
$this->host = $host;
}
public function generate($path)
{
return $this->host.$path;
}
}
class Controller
{
public function myAction(Request $request)
{
// Instead of
$page = isset($_GET['page']) ? intval($_GET['page']) : 1;
// Better (assuming you use the Symfony2 request)
$page = $request->query->get('page', 1);
}
}
Loading history...
createSession uses the super-global variable $_SERVER which is generally not recommended.
Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable: // Bad
class Router
{
public function generate($path)
{
return $_SERVER['HOST'].$path;
}
}
// Better
class Router
{
private $host;
public function __construct($host)
{
$this->host = $host;
}
public function generate($path)
{
return $this->host.$path;
}
}
class Controller
{
public function myAction(Request $request)
{
// Instead of
$page = isset($_GET['page']) ? intval($_GET['page']) : 1;
// Better (assuming you use the Symfony2 request)
$page = $request->query->get('page', 1);
}
}
Loading history...
|
|||
626 | $this->debug->append("STA " . __METHOD__, 4); |
||
627 | $this->debug->append("Log in user to _SESSION", 2); |
||
628 | if (!empty($lastIP) && (!empty($lastLoginTime))) { |
||
629 | $_SESSION['last_ip_pop'] = array($lastIP, $lastLoginTime); |
||
630 | } |
||
631 | session_regenerate_id(true); |
||
632 | $_SESSION['AUTHENTICATED'] = '1'; |
||
633 | // $this->user from checkUserPassword |
||
634 | $_SESSION['USERDATA'] = $this->user; |
||
635 | if ($this->config['protect_session_state']) { |
||
636 | $_SESSION['STATE'] = md5($_SESSION['USERDATA']['username'].$_SESSION['USERDATA']['id'].@$_SERVER['HTTP_USER_AGENT']); |
||
637 | } |
||
638 | } |
||
639 | |||
640 | /** |
||
641 | * Update users last_login timestamp |
||
642 | * @param id int UserID |
||
643 | * @return bool true of false |
||
644 | **/ |
||
645 | private function updateLoginTimestamp($id) { |
||
646 | $field = array('name' => 'last_login', 'type' => 'i', 'value' => time()); |
||
647 | return $this->updateSingle($id, $field); |
||
648 | } |
||
649 | |||
650 | /** |
||
651 | * Log out current user, destroy the session |
||
652 | * @param none |
||
653 | * @return true |
||
654 | **/ |
||
655 | public function logoutUser() { |
||
0 ignored issues
–
show
logoutUser uses the super-global variable $_SESSION which is generally not recommended.
Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable: // Bad
class Router
{
public function generate($path)
{
return $_SERVER['HOST'].$path;
}
}
// Better
class Router
{
private $host;
public function __construct($host)
{
$this->host = $host;
}
public function generate($path)
{
return $this->host.$path;
}
}
class Controller
{
public function myAction(Request $request)
{
// Instead of
$page = isset($_GET['page']) ? intval($_GET['page']) : 1;
// Better (assuming you use the Symfony2 request)
$page = $request->query->get('page', 1);
}
}
Loading history...
logoutUser uses the super-global variable $_SERVER which is generally not recommended.
Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable: // Bad
class Router
{
public function generate($path)
{
return $_SERVER['HOST'].$path;
}
}
// Better
class Router
{
private $host;
public function __construct($host)
{
$this->host = $host;
}
public function generate($path)
{
return $this->host.$path;
}
}
class Controller
{
public function myAction(Request $request)
{
// Instead of
$page = isset($_GET['page']) ? intval($_GET['page']) : 1;
// Better (assuming you use the Symfony2 request)
$page = $request->query->get('page', 1);
}
}
Loading history...
|
|||
656 | $this->debug->append("STA " . __METHOD__, 4); |
||
657 | // Unset all of the session variables |
||
658 | $_SESSION = array(); |
||
659 | // As we're killing the sesison, also kill the cookie! |
||
660 | setcookie(session_name(), '', time() - 42000); |
||
661 | // Destroy the session. |
||
662 | session_destroy(); |
||
663 | // Enforce generation of a new Session ID and delete the old |
||
664 | session_regenerate_id(true); |
||
665 | |||
666 | // Enforce a page reload and point towards login with referrer included, if supplied |
||
667 | $port = ($_SERVER["SERVER_PORT"] == "80" || $_SERVER["SERVER_PORT"] == "443") ? "" : (":".$_SERVER["SERVER_PORT"]); |
||
668 | $pushto = $_SERVER['SCRIPT_NAME'].'?page=login'; |
||
669 | $location = (@$_SERVER['HTTPS'] == 'on') ? 'https://' . $_SERVER['SERVER_NAME'] . $port . $pushto : 'http://' . $_SERVER['SERVER_NAME'] . $port . $pushto; |
||
670 | if (!headers_sent()) header('Location: ' . $location); |
||
671 | exit('<meta http-equiv="refresh" content="0; url=' . $location . '"/>'); |
||
672 | } |
||
673 | |||
674 | /** |
||
675 | * Get all users for admin panel |
||
676 | **/ |
||
677 | public function getAllUsers($filter='%') { |
||
678 | $this->debug->append("STA " . __METHOD__, 4); |
||
679 | $stmt = $this->mysqli->prepare(" |
||
680 | SELECT |
||
681 | a.id AS id, |
||
682 | a.username AS username |
||
683 | FROM " . $this->getTableName() . " AS a |
||
684 | WHERE a.username LIKE ? |
||
685 | GROUP BY username"); |
||
686 | View Code Duplication | if ($this->checkStmt($stmt) && $stmt->bind_param('s', $filter) && $stmt->execute() && $result = $stmt->get_result()) { |
|
687 | while ($row = $result->fetch_assoc()) { |
||
688 | $aData[$row['id']] = $row['username']; |
||
689 | } |
||
690 | return $aData; |
||
691 | } |
||
692 | return false; |
||
693 | } |
||
694 | |||
695 | /** |
||
696 | * Fetch this classes table name |
||
697 | * @return table string This classes table name |
||
698 | **/ |
||
699 | public function getTableName() { |
||
700 | $this->debug->append("STA " . __METHOD__, 4); |
||
701 | return $this->table; |
||
702 | } |
||
703 | |||
704 | /** |
||
705 | * Fetch some basic user information to store for later user |
||
706 | * @param userID int User ID |
||
707 | * return data array Database fields as used in SELECT |
||
708 | **/ |
||
709 | public function getUserData($userID) { |
||
710 | $this->debug->append("STA " . __METHOD__, 4); |
||
711 | $this->debug->append("Fetching user information for user id: $userID"); |
||
712 | $stmt = $this->mysqli->prepare(" |
||
713 | SELECT |
||
714 | id AS id, username, pin, api_key, is_admin, is_anonymous, email, timezone, no_fees, |
||
715 | IFNULL(donate_percent, '0') as donate_percent |
||
716 | FROM " . $this->getTableName() . " |
||
717 | WHERE id = ? LIMIT 0,1"); |
||
718 | if ($this->checkStmt($stmt) && $stmt->bind_param('i', $userID) && $stmt->execute() && $result = $stmt->get_result()) { |
||
719 | $aData = $result->fetch_assoc(); |
||
720 | $aData['coin_address'] = $this->coin_address->getCoinAddress($userID); |
||
721 | if (! $aData['ap_threshold'] = $this->coin_address->getAPThreshold($userID)) |
||
722 | $aData['ap_threshold'] = 0; |
||
723 | $stmt->close(); |
||
724 | return $aData; |
||
725 | } |
||
726 | $this->debug->append("Failed to fetch user information for $userID"); |
||
727 | return $this->sqlError(); |
||
728 | } |
||
729 | |||
730 | /** |
||
731 | * Register a new user in the system |
||
732 | * @param username string Username |
||
733 | * @param password1 string Password |
||
734 | * @param password2 string Password verification |
||
735 | * @param pin int 4 digit PIN code |
||
736 | * @param email1 string Email address |
||
737 | * @param email2 string Email confirmation |
||
738 | * @return bool |
||
739 | **/ |
||
740 | public function register($username, $coinaddress, $password1, $password2, $pin, $email1='', $email2='', $tac='', $strToken='') { |
||
741 | $this->debug->append("STA " . __METHOD__, 4); |
||
742 | if ($tac != 1) { |
||
743 | $this->setErrorMessage('You need to accept our <a href="'.$_SERVER['SCRIPT_NAME'].'?page=tac" target="_blank">Terms and Conditions</a>'); |
||
744 | return false; |
||
745 | } |
||
746 | if (strlen($username) > 40) { |
||
747 | $this->setErrorMessage('Username exceeding character limit'); |
||
748 | return false; |
||
749 | } |
||
750 | if (!is_null($coinaddress)) { |
||
751 | if ($this->coin_address->existsCoinAddress($coinaddress)) { |
||
752 | $this->setErrorMessage('Coin address is already taken'); |
||
753 | return false; |
||
754 | } |
||
755 | if (!$this->bitcoin->validateaddress($coinaddress)) { |
||
756 | $this->setErrorMessage('Coin address is not valid'); |
||
757 | return false; |
||
758 | } |
||
759 | } |
||
760 | if (preg_match('/[^a-z_\-0-9]/i', $username)) { |
||
761 | $this->setErrorMessage('Username may only contain alphanumeric characters'); |
||
762 | return false; |
||
763 | } |
||
764 | if ($this->getEmail($email1)) { |
||
765 | $this->setErrorMessage( 'This e-mail address is already taken' ); |
||
766 | return false; |
||
767 | } |
||
768 | if (strlen($password1) < 8) { |
||
769 | $this->setErrorMessage( 'Password is too short, minimum of 8 characters required' ); |
||
770 | return false; |
||
771 | } |
||
772 | if ($password1 !== $password2) { |
||
773 | $this->setErrorMessage( 'Password do not match' ); |
||
774 | return false; |
||
775 | } |
||
776 | View Code Duplication | if (empty($email1) || !filter_var($email1, FILTER_VALIDATE_EMAIL)) { |
|
777 | $this->setErrorMessage( 'Invalid e-mail address' ); |
||
778 | return false; |
||
779 | } |
||
780 | if ($email1 !== $email2) { |
||
781 | $this->setErrorMessage( 'E-mail do not match' ); |
||
782 | return false; |
||
783 | } |
||
784 | if (!is_numeric($pin) || strlen($pin) > 4 || strlen($pin) < 4) { |
||
785 | $this->setErrorMessage( 'Invalid PIN' ); |
||
786 | return false; |
||
787 | } |
||
788 | if (isset($strToken) && !empty($strToken)) { |
||
789 | if ( ! $aToken = $this->token->getToken($strToken, 'invitation')) { |
||
790 | $this->setErrorMessage('Unable to find token'); |
||
791 | return false; |
||
792 | } |
||
793 | // Circle dependency, so we create our own object here |
||
794 | $invitation = new Invitation(); |
||
795 | $invitation->setMysql($this->mysqli); |
||
796 | $invitation->setDebug($this->debug); |
||
797 | $invitation->setLog($this->log); |
||
798 | $invitation->setUser($this); |
||
799 | $invitation->setConfig($this->config); |
||
800 | if (!$invitation->setActivated($aToken['id'])) { |
||
801 | $this->setErrorMessage('Unable to activate your invitation'); |
||
802 | return false; |
||
803 | } |
||
804 | if (!$this->token->deleteToken($strToken)) { |
||
805 | $this->setErrorMessage('Unable to remove used token'); |
||
806 | $this->log->log("warn", "$username tried to register but failed to delete the invitation token"); |
||
807 | return false; |
||
808 | } |
||
809 | } |
||
810 | if ($this->mysqli->query("SELECT id FROM $this->table LIMIT 1")->num_rows > 0) { |
||
811 | ! $this->setting->getValue('accounts_confirm_email_disabled') ? $is_locked = 1 : $is_locked = 0; |
||
812 | $is_admin = 0; |
||
813 | $stmt = $this->mysqli->prepare(" |
||
814 | INSERT INTO $this->table (username, pass, email, signup_timestamp, pin, api_key, is_locked) |
||
815 | VALUES (?, ?, ?, ?, ?, ?, ?) |
||
816 | "); |
||
817 | } else { |
||
818 | $is_locked = 0; |
||
819 | $is_admin = 1; |
||
820 | $stmt = $this->mysqli->prepare(" |
||
821 | INSERT INTO $this->table (username, pass, email, signup_timestamp, pin, api_key, is_admin, is_locked) |
||
822 | VALUES (?, ?, ?, ?, ?, ?, 1, ?) |
||
823 | "); |
||
824 | } |
||
825 | |||
826 | // Create hashed strings using original string and salt |
||
827 | $password_hash = $this->getHash($password1, HASH_VERSION, bin2hex(openssl_random_pseudo_bytes(32))); |
||
828 | $pin_hash = $this->getHash($pin, HASH_VERSION, bin2hex(openssl_random_pseudo_bytes(32))); |
||
829 | $apikey_hash = $this->getHash($username, 0); |
||
830 | $username_clean = strip_tags($username); |
||
831 | $signup_time = time(); |
||
832 | |||
833 | if ($this->checkStmt($stmt) && $stmt->bind_param('sssissi', $username_clean, $password_hash, $email1, $signup_time, $pin_hash, $apikey_hash, $is_locked) && $stmt->execute()) { |
||
834 | $new_account_id = $this->mysqli->insert_id; |
||
835 | if (!is_null($coinaddress)) $this->coin_address->add($new_account_id, $coinaddress); |
||
836 | if (! $this->setting->getValue('accounts_confirm_email_disabled') && $is_admin != 1) { |
||
837 | if ($token = $this->token->createToken('confirm_email', $stmt->insert_id)) { |
||
838 | $aData['username'] = $username_clean; |
||
839 | $aData['token'] = $token; |
||
840 | $aData['email'] = $email1; |
||
841 | $aData['subject'] = 'E-Mail verification'; |
||
842 | View Code Duplication | if (!$this->mail->sendMail('register/confirm_email', $aData)) { |
|
843 | $this->setErrorMessage('Unable to request email confirmation: ' . $this->mail->getError()); |
||
844 | return false; |
||
845 | } |
||
846 | return true; |
||
847 | } else { |
||
848 | $this->setErrorMessage('Failed to create confirmation token'); |
||
849 | $this->debug->append('Unable to create confirm_email token: ' . $this->token->getError()); |
||
850 | return false; |
||
851 | } |
||
852 | } else { |
||
853 | return true; |
||
854 | } |
||
855 | } else { |
||
856 | $this->setErrorMessage( 'Unable to register' ); |
||
857 | $this->debug->append('Failed to insert user into DB: ' . $this->mysqli->error); |
||
858 | echo $this->mysqli->error; |
||
859 | if ($stmt->sqlstate == '23000') $this->setErrorMessage( 'Username or email already registered' ); |
||
860 | return false; |
||
861 | } |
||
862 | return false; |
||
863 | } |
||
864 | |||
865 | /** |
||
866 | * User a one time token to reset a password |
||
867 | * @param token string one time token |
||
868 | * @param new1 string New password |
||
869 | * @param new2 string New password verification |
||
870 | * @return bool |
||
871 | **/ |
||
872 | public function resetPassword($token, $new1, $new2) { |
||
873 | $this->debug->append("STA " . __METHOD__, 4); |
||
874 | if ($aToken = $this->token->getToken($token, 'password_reset')) { |
||
875 | if ($new1 !== $new2) { |
||
876 | $this->setErrorMessage( 'New passwords do not match' ); |
||
877 | return false; |
||
878 | } |
||
879 | if ( strlen($new1) < 8 ) { |
||
880 | $this->setErrorMessage( 'New password is too short, please use more than 8 chars' ); |
||
881 | return false; |
||
882 | } |
||
883 | $new_hash = $this->getHash($new1, HASH_VERSION, bin2hex(openssl_random_pseudo_bytes(32))); |
||
884 | $stmt = $this->mysqli->prepare("UPDATE $this->table SET pass = ? WHERE id = ?"); |
||
885 | if ($this->checkStmt($stmt) && $stmt->bind_param('si', $new_hash, $aToken['account_id']) && $stmt->execute() && $stmt->affected_rows === 1) { |
||
886 | if ($this->token->deleteToken($aToken['token'])) { |
||
887 | return true; |
||
888 | } else { |
||
889 | $this->setErrorMessage('Unable to invalidate used token'); |
||
890 | } |
||
891 | } else { |
||
892 | $this->setErrorMessage('Unable to set new password or you chose the same password. Please use a different one.'); |
||
893 | } |
||
894 | } else { |
||
895 | $this->setErrorMessage('Invalid token: ' . $this->token->getError()); |
||
896 | } |
||
897 | $this->debug->append('Failed to update password:' . $this->mysqli->error); |
||
898 | return false; |
||
899 | } |
||
900 | |||
901 | /** |
||
902 | * Reset a password by sending a password reset mail |
||
903 | * @param username string Username to reset password for |
||
904 | * @return bool |
||
905 | **/ |
||
906 | public function initResetPassword($username) { |
||
0 ignored issues
–
show
initResetPassword uses the super-global variable $_SERVER which is generally not recommended.
Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable: // Bad
class Router
{
public function generate($path)
{
return $_SERVER['HOST'].$path;
}
}
// Better
class Router
{
private $host;
public function __construct($host)
{
$this->host = $host;
}
public function generate($path)
{
return $this->host.$path;
}
}
class Controller
{
public function myAction(Request $request)
{
// Instead of
$page = isset($_GET['page']) ? intval($_GET['page']) : 1;
// Better (assuming you use the Symfony2 request)
$page = $request->query->get('page', 1);
}
}
Loading history...
|
|||
907 | $this->debug->append("STA " . __METHOD__, 4); |
||
908 | // Fetch the users mail address |
||
909 | if (empty($username)) { |
||
910 | $this->setErrorMessage("Username must not be empty"); |
||
911 | return false; |
||
912 | } |
||
913 | if (filter_var($username, FILTER_VALIDATE_EMAIL)) { |
||
914 | $this->debug->append("Username is an e-mail: $username", 2); |
||
915 | if (!$username = $this->getUserNameByEmail($username)) { |
||
916 | $this->setErrorMessage("Invalid username or password."); |
||
917 | return false; |
||
918 | } |
||
919 | } |
||
920 | if (!$aData['email'] = $this->getUserEmail($username, true)) { |
||
921 | $this->setErrorMessage("Please check your mail account to finish your password reset"); |
||
922 | return false; |
||
923 | } |
||
924 | View Code Duplication | if (!$aData['token'] = $this->token->createToken('password_reset', $this->getUserId($username, true))) { |
|
925 | $this->setErrorMessage('Unable to setup token for password reset'); |
||
926 | return false; |
||
927 | } |
||
928 | $aData['username'] = $this->getUserName($this->getUserId($username, true)); |
||
929 | $aData['subject'] = 'Password Reset Request'; |
||
930 | if ($_SERVER['REMOTE_ADDR'] !== $this->getUserIp($this->getUserId($username, true))) { |
||
931 | $this->log->log("warn", "$username requested password reset, saved IP is [".$this->getUserIp($this->getUserId($username, true))."]"); |
||
932 | } else { |
||
933 | $this->log->log("info", "$username requested password reset, saved IP is [".$this->getUserIp($this->getUserId($username, true))."]"); |
||
934 | } |
||
935 | View Code Duplication | if ($this->mail->sendMail('password/reset', $aData)) { |
|
936 | return true; |
||
937 | } else { |
||
938 | $this->setErrorMessage('Unable to send mail to your address'); |
||
939 | return false; |
||
940 | } |
||
941 | return false; |
||
942 | } |
||
943 | |||
944 | /** |
||
945 | * Check if a user is authenticated and allowed to login |
||
946 | * Checks the $_SESSION for existing data |
||
947 | * Destroys the session if account is now locked |
||
948 | * @param none |
||
949 | * @return bool |
||
950 | **/ |
||
951 | public function isAuthenticated($logout=true) { |
||
0 ignored issues
–
show
isAuthenticated uses the super-global variable $_SESSION which is generally not recommended.
Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable: // Bad
class Router
{
public function generate($path)
{
return $_SERVER['HOST'].$path;
}
}
// Better
class Router
{
private $host;
public function __construct($host)
{
$this->host = $host;
}
public function generate($path)
{
return $this->host.$path;
}
}
class Controller
{
public function myAction(Request $request)
{
// Instead of
$page = isset($_GET['page']) ? intval($_GET['page']) : 1;
// Better (assuming you use the Symfony2 request)
$page = $request->query->get('page', 1);
}
}
Loading history...
isAuthenticated uses the super-global variable $_SERVER which is generally not recommended.
Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable: // Bad
class Router
{
public function generate($path)
{
return $_SERVER['HOST'].$path;
}
}
// Better
class Router
{
private $host;
public function __construct($host)
{
$this->host = $host;
}
public function generate($path)
{
return $this->host.$path;
}
}
class Controller
{
public function myAction(Request $request)
{
// Instead of
$page = isset($_GET['page']) ? intval($_GET['page']) : 1;
// Better (assuming you use the Symfony2 request)
$page = $request->query->get('page', 1);
}
}
Loading history...
|
|||
952 | $this->debug->append("STA " . __METHOD__, 4); |
||
953 | if ( @$_SESSION['AUTHENTICATED'] == true && |
||
954 | !$this->isLocked($_SESSION['USERDATA']['id']) && |
||
955 | $this->getUserIp($_SESSION['USERDATA']['id']) == $_SERVER['REMOTE_ADDR'] && |
||
956 | ( ! $this->config['protect_session_state'] || |
||
957 | ( |
||
958 | $this->config['protect_session_state'] && $_SESSION['STATE'] == md5($_SESSION['USERDATA']['username'].$_SESSION['USERDATA']['id'].@$_SERVER['HTTP_USER_AGENT']) |
||
959 | ) |
||
960 | ) |
||
961 | ) return true; |
||
962 | // Catchall |
||
963 | $this->log->log('warn', 'Forcing logout, user is locked or IP changed mid session [hijack attempt?]'); |
||
964 | if ($logout == true) $this->logoutUser(); |
||
965 | return false; |
||
966 | } |
||
967 | |||
968 | /** |
||
969 | * Convenience function to get IP address, no params is the same as REMOTE_ADDR |
||
970 | * @param trustremote bool must be FALSE to checkcloudflare, checkclient or checkforwarded |
||
971 | * @param checkcloudflare bool check HTTP_CF_CONNECTING_IP for a valid ip first |
||
972 | * @param checkclient bool check HTTP_CLIENT_IP for a valid ip first |
||
973 | * @param checkforwarded bool check HTTP_X_FORWARDED_FOR for a valid ip first |
||
974 | * @return string IP address |
||
975 | */ |
||
976 | public function getCurrentIP($trustremote=false, $checkcloudflare=true, $checkclient=false, $checkforwarded=true) { |
||
977 | $cf = (isset($_SERVER['HTTP_CF_CONNECTING_IP'])) ? $_SERVER['HTTP_CF_CONNECTING_IP'] : false; |
||
978 | $client = (isset($_SERVER['HTTP_CLIENT_IP'])) ? $_SERVER['HTTP_CLIENT_IP'] : false; |
||
979 | $fwd = (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) ? $_SERVER['HTTP_X_FORWARDED_FOR'] : false; |
||
980 | $remote = (isset($_SERVER['REMOTE_ADDR'])) ? $_SERVER['REMOTE_ADDR'] : @$_SERVER['REMOTE_ADDR']; |
||
981 | // shared internet |
||
982 | if (!$trustremote && $checkcloudflare && filter_var($cf, FILTER_VALIDATE_IP)) { |
||
983 | // cloudflare |
||
984 | return $cf; |
||
985 | } else if (!$trustremote && $checkclient && filter_var($client, FILTER_VALIDATE_IP)) { |
||
986 | return $client; |
||
987 | } else if (!$trustremote && $checkforwarded && strpos($fwd, ',') !== false) { |
||
988 | // multiple proxies |
||
989 | $ips = explode(',', $fwd); |
||
990 | return $ips[0]; |
||
991 | } else if (!$trustremote && $checkforwarded && filter_var($fwd, FILTER_VALIDATE_IP)) { |
||
992 | // single |
||
993 | return $fwd; |
||
994 | } else { |
||
995 | // as usual |
||
996 | return $remote; |
||
997 | } |
||
998 | } |
||
999 | } |
||
1000 | |||
1001 | // Make our class available automatically |
||
1002 | $user = new User($config); |
||
1003 | $user->setDebug($debug); |
||
1004 | $user->setLog($log); |
||
1005 | $user->setMysql($mysqli); |
||
1006 | $user->setSalt($config['SALT']); |
||
1007 | $user->setSmarty($smarty); |
||
1008 | $user->setMail($mail); |
||
1009 | $user->setToken($oToken); |
||
1010 | $user->setBitcoin($bitcoin); |
||
1011 | $user->setSetting($setting); |
||
1012 | $user->setCoinAddress($coin_address); |
||
1013 | $user->setErrorCodes($aErrorCodes); |
||
1014 |
Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable: