source: branches/1.3/libs/UserAuth.php @ 1744

Revision 1744, 17.9 KB checked in by nick_ramsay, 3 years ago (diff)

[Branch 1.3] First round of updating getUser/getUserBasic calls.

Line 
1<?php
2/**
3 * Functions for authnticating, logging in and registering users
4 *
5 * PHP version 5
6 *
7 * LICENSE: Hotaru CMS is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation, either version 3 of
10 * the License, or (at your option) any later version.
11 *
12 * Hotaru CMS is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with Hotaru CMS. If not, see http://www.gnu.org/licenses/.
18 *
19 * @category  Content Management System
20 * @package   HotaruCMS
21 * @author    Nick Ramsay <admin@hotarucms.org>
22 * @copyright Copyright (c) 2010, Hotaru CMS
23 * @license   http://www.gnu.org/copyleft/gpl.html GNU General Public License
24 * @link      http://www.hotarucms.org/
25 */
26class UserAuth extends UserBase
27{
28        /**
29         * check cookie and log in
30         *
31         * @return bool
32         */
33        public function checkCookie($h)
34        {
35                // Check for a cookie. If present then the user is logged in.
36                $h_user = $h->cage->cookie->testUsername('hotaru_user');
37               
38                if((!$h_user) || (!$h->cage->cookie->keyExists('hotaru_key'))) {
39                    $this->setLoggedOutUser($h);
40                    return false;
41                }
42               
43                $user_info=explode(":", base64_decode($h->cage->cookie->getRaw('hotaru_key')));
44               
45                if (($h_user != $user_info[0]) || ($h->currentUser->generateHash($h_user, md5(BASEURL)) != $user_info[1])) {
46                    $this->setLoggedOutUser($h);
47                    return false;
48                }
49               
50                $this->name = $h_user;
51                if ($h_user)
52                {
53                        $valid = false;
54                       
55                        // Read the user from the database
56                        $user_exists = $this->getUser($h, 0, $this->name);
57               
58                        // validate the user's password
59                        if ($user_info[2] != md5($user_exists->user_password)) {
60                                $user_exists = false;
61                        } else {
62                                $valid = true;
63                        }
64                       
65                        // Log the user in if valid
66                        if ($valid) {
67                                $this->loggedIn = true;
68                                if (!session_id()) { $this->updateUserLastVisit($h); } // update user_lastvisit field when a new session is created
69                                $h->pluginHook('userauth_checkcookie_success'); // user_signin throws out killspammed, banned and suspended users
70                               
71                                // SUCCESS!!!
72                                return true;
73                        } else {
74                                $h->currentUser->destroyCookieAndSession(); // removes cookie and session for physically deleted users
75                        }
76                }
77               
78                // otherwise, give them "logged out" permissions
79                $this->setLoggedOutUser($h);
80                return false;
81        }
82       
83       
84        /**
85         * Log a user in if their username and password are valid
86         *
87         * @param string $username
88         * @param string $password
89         * @return bool
90         */
91        public function loginCheck($h, $username = '', $password = '')
92        {
93                // Read the current user's basic details
94                $userX = $this->getUserBasic($h, 0, $username);
95                if (!$userX) { return false; }
96               
97                // destroy the cookie for the following usergroups:
98                $no_cookie = array('killspammed', 'banned', 'suspended');
99                if (in_array($userX->user_role, $no_cookie)) {
100                        $this->destroyCookieAndSession();
101                        return false;
102                }
103               
104                $salt_length = 9;
105                $result = '';
106               
107                // Allow plugin to bypass the password check with their own methods, e.g. RPX
108                $plugin_result = $h->pluginHook('userbase_logincheck', '', array($username, $password));
109               
110                if (!$plugin_result)
111                {
112                        // nothing or (false) was returned from the plugins, so confirm the username and password match:
113                        $password = $this->generateHash($password, substr($userX->user_password, 0, $salt_length));
114                        $sql = "SELECT user_username, user_password FROM " . TABLE_USERS . " WHERE user_username = %s AND user_password = %s";
115                        $result = $h->db->get_row($h->db->prepare($sql, $username, $password));
116                }
117                elseif ($plugin_result)
118                {
119                        // a positive result was returned from the plugin(s)
120                        // let's hope the plugin did its own authentication because we've skipped the usual username/passowrd check!
121                        $result = true;
122                }
123               
124                if ($result) { return true; } else { return false; }
125        }
126       
127       
128        /**
129         * Generate a hash for the password
130         *
131         * @param string $plainText - the password
132         * @param mixed $salt
133         *
134         * Note: Adapted from SocialWebCMS
135         */
136        public function generateHash($plainText, $salt = null)
137        {
138                $salt_length = 9;
139                if ($salt === null) {
140                        $salt = substr(md5(uniqid(rand(), true)), 0, $salt_length); }
141                else {
142                        $salt = substr($salt, 0, $salt_length);
143                }
144                return $salt . sha1($salt . $plainText);
145        }
146       
147       
148        /**
149         * Give logged out user default permissions
150         */   
151        public function setLoggedOutUser($h)
152        {
153                $default_perms = $this->getDefaultPermissions($h);
154                unset($default_perms['options']);  // don't need this for individual users
155                $this->setAllPermissions($default_perms);
156        }
157       
158       
159        /**
160         * Update last login
161         *
162         * @return bool
163         */
164        public function updateUserLastLogin($h)
165        {
166                if ($this->id != 0) {
167                        $sql = "UPDATE " . TABLE_USERS . " SET user_lastlogin = CURRENT_TIMESTAMP WHERE user_id = %d";
168                        $h->db->query($h->db->prepare($sql, $this->id));
169                        return true;
170                } else {
171                        return false;
172                }
173        }
174       
175       
176        /**
177         * Update last visit (new session started)
178         *
179         * @return bool
180         */
181        public function updateUserLastVisit($h, $user_id = 0)
182        {
183                if ($this->id != 0) {
184                        if (!$user_id) { $user_id = $this->id; }
185                        $sql = "UPDATE " . TABLE_USERS . " SET user_lastvisit = CURRENT_TIMESTAMP WHERE user_id = %d";
186                        $h->db->query($h->db->prepare($sql, $user_id));
187                        return true;
188                } else {
189                        return false;
190                }
191        }
192       
193       
194        /**
195         * Set a 30-day cookie
196         *
197         * @param string $remember checkbox with value "checked" or empty
198         * @return bool
199         */
200        public function setCookie($h, $remember)
201        {
202                if (!$this->name)
203                {
204                        echo $h->lang['main_userbase_cookie_error'];
205                        return false;
206                } else {
207                        $strCookie=base64_encode(
208                                join(':', array($this->name,
209                                $h->currentUser->generateHash($this->name, md5(BASEURL)),
210                                md5($this->password)))
211                        );
212                       
213                        if ($remember) {
214                                // 2592000 = 60 seconds * 60 mins * 24 hours * 30 days
215                                $month = 2592000 + time();
216                        } else {
217                                $month = 0;
218                        }
219                       
220                        if (strpos(BASEURL, "localhost") !== false) {
221                                setcookie("hotaru_user", $this->name, $month, "/");
222                                setcookie("hotaru_key", $strCookie, $month, "/");
223                        } else {
224                                $parsed = parse_url(BASEURL);
225                               
226                                // now we need a dot in front of that so cookies work across subdomains:
227                                setcookie("hotaru_user", $this->name, $month, "/", "." . $parsed['host']);
228                                setcookie("hotaru_key", $strCookie, $month, "/", "." . $parsed['host']);
229                        }
230                        return true;
231                }
232        }
233       
234       
235        /**
236         * Delete cookie and destroy session
237         */
238        public function destroyCookieAndSession()
239        {
240                // setting a cookie with a negative time expires it
241               
242                if (strpos(BASEURL, "localhost") !== false) {
243                        setcookie("hotaru_user", "", time()-3600, "/");
244                        setcookie("hotaru_key", "", time()-3600, "/");
245                } else {
246                        $parsed = parse_url(BASEURL);
247                       
248                        // now we need a dot in front of that so cookies are cleared across subdomains:
249                        setcookie("hotaru_user", "", time()-3600, "/", "." . $parsed['host']);
250                        setcookie("hotaru_key", "", time()-3600, "/", "." . $parsed['host']);
251                }
252               
253                session_destroy(); // sessions are used in CSRF
254               
255                $this->loggedIn = false;
256        }
257       
258       
259         /**
260         * Change username or email
261         *
262         * @param int $userid
263         * @return bool
264         */
265        public function updateAccount($h, $userid = 0)
266        {
267                // $viewee is the person whose account is being modified
268               
269                $viewee = new UserBase($h);
270               
271                // Get the details of the account to show.
272                // If no account is specified, assume it's your own.
273               
274                if (!$userid) {
275                    $userid = $this->id;
276                }
277               
278                $viewee->getUser($h, $userid);
279               
280                $error = 0;
281               
282                // fill checks
283                $checks['userid_check'] = '';
284                $checks['username_check'] = '';
285                $checks['email_check'] = '';
286                $checks['role_check'] = '';
287                $checks['password_check_old'] = '';
288                $checks['password_check_new'] = '';
289                $checks['password_check_new2'] = '';
290               
291                // Updating account info (username and email address)
292                if ($h->cage->post->testAlnumLines('update_type') == 'update_general') {
293               
294                        // check CSRF key
295                        if (!$h->csrf()) {
296                                $h->messages[$h->lang['error_csrf']] = 'red';
297                                $error = 1;
298                        }
299                       
300                        $username_check = $h->cage->post->testUsername('username'); // alphanumeric, dashes and underscores okay, case insensitive
301                        if (!$username_check) {
302                                $h->messages[$h->lang['main_user_account_update_username_error']] = 'red';
303                                $error = 1;
304                        } elseif($h->nameExists($username_check, '', $viewee->id) || $h->isBlocked('user', $username_check)) {
305                                $h->messages[$h->lang['main_user_account_update_username_exists']] = 'red';
306                                $error = 1;
307                        } else {
308                                //success
309                                $viewee->name = $username_check;
310                        }
311                       
312                        $email_check = $h->cage->post->testEmail('email');
313                        if (!$email_check) {
314                                $h->messages[$h->lang['main_user_account_update_email_error']] = 'red';
315                                $error = 1;
316                        } elseif($h->emailExists($email_check, '', $viewee->id) || $h->isBlocked('email', $email_check)) {
317                                $h->messages[$h->lang['main_user_account_update_email_exists']] = 'red';
318                                $error = 1;
319                        } else {
320                                //success
321                                $viewee->email = $email_check;
322                        }
323                       
324                        $role_check = $h->cage->post->testAlnumLines('user_role'); // from Users plugin account page
325                        // compare with current role and update if different
326                        if (!$error && $role_check && ($role_check != $viewee->role)) {
327                                $viewee->role = $role_check;
328                                $new_perms = $viewee->getDefaultPermissions($h, $role_check);
329                                $viewee->setAllPermissions($new_perms);
330                                $viewee->updatePermissions($h);
331                                if ($role_check == 'killspammed' || $role_check == 'deleted') {
332                                        $h->deleteComments($viewee->id); // includes child comments from *other* users
333                                        $h->deletePosts($viewee->id); // includes tags and votes for self-submitted posts
334                                       
335                                        $h->pluginHook('userbase_killspam', '', array('target_user' => $viewee->id));
336                                       
337                                        if ($role_check == 'deleted') {
338                                                $h->deleteUser($viewee->id);
339                                                $checks['username_check'] = 'deleted';
340                                                $h->message = $h->lang["users_account_deleted"];
341                                                $h->messageType = 'red';
342                                                return $checks; // This will then show a red "deleted" notice
343                                        }
344                                }
345                        }
346                       
347                        // If we've just edited our own account, let's refresh the cookie so it uses our latest username:
348                        if ($h->currentUser->id == $h->cage->post->testInt('userid')) {
349                                $h->currentUser->setCookie($h, false);           // delete the cookie
350                                $h->currentUser->getUser($h, $h->currentUser->id, '', true);    // re-read the database record to get updated info
351                                $h->currentUser->setCookie($h, true);            // create a new, updated cookie
352                        }
353                }
354               
355                if (!isset($username_check) && !isset($email_check)) {
356                        $username_check = $viewee->name;
357                        $email_check = $viewee->email;
358                        $role_check = $viewee->role;
359                        // do nothing
360                } elseif ($error == 0) {
361                        $exists = $h->userExists(0, $username_check, $email_check);
362                        if (($exists != 'no') && ($exists != 'error')) { // user exists
363                                //success
364                                $viewee->updateUserBasic($h, $userid);
365                                // only update the cookie if it's your own account:
366                                if ($userid == $this->id) {
367                                $h->currentUser->setCookie($h, false);           // delete the cookie
368                                $h->currentUser->getUser($h, $h->currentUser->id, '', true);    // re-read the database record to get updated info
369                                $h->currentUser->setCookie($h, true);            // create a new, updated cookie
370                                }
371                                $h->messages[$h->lang['main_user_account_update_success']] = 'green';
372                        } else {
373                                //fail
374                                $h->messages[$h->lang["main_user_account_update_unexpected_error"]] = 'red';
375                        }
376                } else {
377                        // error must = 1 so fall through and display the form again
378                }
379               
380                //update checks
381                $this->updatePassword($h, $userid);
382                $userid_check = $viewee->id;
383                $checks['userid_check'] = $userid_check;
384                $checks['username_check'] = $username_check;
385                $checks['email_check'] = $email_check;
386                $checks['role_check'] = $role_check;
387               
388                return $checks;
389        }
390       
391       
392         /**
393         * Enable a user to change their password
394         *
395         * @return bool
396         */
397        public function updatePassword($h, $userid)
398        {
399                // we don't want to edit the password if this isn't our own account.
400                if ($userid != $this->id) { return false; }
401               
402                $error = 0;
403               
404                // Updating password
405                if ($h->cage->post->testAlnumLines('update_type') == 'update_password') {
406               
407                        // check CSRF key
408                        if (!$h->csrf()) {
409                                $h->messages[$h->lang['error_csrf']] = 'red';
410                                $error = 1;
411                        }
412                       
413                       
414                        $password_check_old = $h->cage->post->noTags('password_old');
415                       
416                        if ($this->loginCheck($h, $this->name, $password_check_old)) {
417                                // safe, the old password matches the password for this user.
418                        } else {
419                                $h->messages[$h->lang['main_user_account_update_password_error_old']] = 'red';
420                                $error = 1;
421                        }
422                       
423                        $password_check_new = $h->cage->post->testPassword('password_new');   
424                        if ($password_check_new) {
425                                $password_check_new2 = $h->cage->post->testPassword('password_new2');   
426                                if ($password_check_new2) {
427                                        if ($password_check_new == $password_check_new2) {
428                                                // safe, the two new password fields match
429                                        } else {
430                                                $h->messages[$h->lang['main_user_account_update_password_error_match']] = 'red';
431                                                $error = 1;
432                                        }
433                                } else {
434                                        $h->messages[$h->lang['main_user_account_update_password_error_new']] = 'red';
435                                        $error = 1;
436                                }
437                        } else {
438                                $h->messages[$h->lang['main_user_account_update_password_error_not_provided']] = 'red';
439                                $error = 1;
440                        }
441               
442                }
443               
444                if (!isset($password_check_old) && !isset($password_check_new) && !isset($password_check_new2)) {
445                        $password_check_old = "";
446                        $password_check_new = "";
447                        $password_check_new2 = "";
448                        // do nothing
449                } elseif ($error == 0) {
450                        $exists = $h->userExists(0, $this->name, $this->email);
451                        if (($exists != 'no') && ($exists != 'error')) { // user exists
452                                //success
453                                $this->password = $this->generateHash($password_check_new);
454                                $this->updateUserBasic($h, $this->id); // update the database record for this user
455                                $this->setCookie($h, false);           // delete the cookie
456                                $this->getUser($h, $this->id, '', true);    // re-read the database record to get updated info
457                                $this->setCookie($h, true);            // create a new, updated cookie
458                                $h->messages[$h->lang['main_user_account_update_password_success']] = 'green';
459                        } else {
460                                //fail
461                                $h->messages[$h->lang["main_user_account_update_unexpected_error"]] = 'red';
462                        }
463                } else {
464                        // error must = 1 so fall through and display the form again
465                }
466        }
467       
468       
469         /**
470         * Send a confirmation code to a user who has forgotten his/her password
471         *
472         * @param string $email - already validated above
473         */
474        public function sendPasswordConf($h, $userid, $email)
475        {
476                // generate the email confirmation code
477                $pass_conf = md5(crypt(md5($email),md5($email)));
478               
479                // store the hash in the user table
480                $sql = "UPDATE " . TABLE_USERS . " SET user_password_conf = %s WHERE user_id = %d";
481                $h->db->query($h->db->prepare($sql, $pass_conf, $userid));
482               
483                $line_break = "\r\n\r\n";
484                $next_line = "\r\n";
485               
486                if ($h->isActive('signin')) {
487                        $url = BASEURL . 'index.php?page=login&plugin=user_signin&userid=' . $userid . '&passconf=' . $pass_conf;
488                } else {
489                        $url = BASEURL . 'admin_index.php?page=admin_login&userid=' . $userid . '&passconf=' . $pass_conf;
490                }
491               
492                // send email
493                $subject = $h->lang['main_user_email_password_conf_subject'];
494                $body = $h->lang['main_user_email_password_conf_body_hello'] . " " . $h->getUserNameFromId($userid);
495                $body .= $line_break;
496                $body .= $h->lang['main_user_email_password_conf_body_welcome'];
497                $body .= $h->lang['main_user_email_password_conf_body_click'];
498                $body .= $line_break;
499                $body .= $url;
500                $body .= $line_break;
501                $body .= $h->lang['main_user_email_password_conf_body_no_request'];
502                $body .= $line_break;
503                $body .= $h->lang['main_user_email_password_conf_body_regards'];
504                $body .= $next_line;
505                $body .= $h->lang['main_user_email_password_conf_body_sign'];
506                $to = $email;
507               
508                $h->email($to, $subject, $body);   
509               
510                return true;
511        }
512       
513       
514         /**
515         * Reset the user's password to soemthing random and email it.
516         *
517         * @param string $passconf - confirmation code clicked in email
518         */
519        public function newRandomPassword($h, $userid, $passconf)
520        {
521                $email = $h->getEmailFromId($userid);
522               
523                // check the email and confirmation code are a pair
524                $pass_conf_check = md5(crypt(md5($email),md5($email)));
525                if ($pass_conf_check != $passconf) {
526                        return false;
527                }
528               
529                // update the password to something random
530                $temp_pass = random_string(10);
531                $sql = "UPDATE " . TABLE_USERS . " SET user_password = %s WHERE user_id = %d";
532                $h->db->query($h->db->prepare($sql, $this->generateHash($temp_pass), $userid));
533                $line_break = "\r\n\r\n";
534                $next_line = "\r\n";
535               
536                if ($h->isActive('signin')) {
537                        $url = BASEURL . 'index.php?page=login&plugin=user_signin';
538                } else {
539                        $url = BASEURL . 'admin_index.php?page=admin_login';
540                }
541               
542                $username = $h->getUserNameFromId($userid);
543               
544                // send email
545                $subject = $h->lang['main_user_email_new_password_subject'];
546                $body = $h->lang['main_user_email_password_conf_body_hello'] . " " . $username;
547                $body .= $line_break;
548                $body .= $h->lang['main_user_email_password_conf_body_requested'];
549                $body .= $line_break;
550                $body .= $username;
551                $body .= $next_line;
552                $body .= $temp_pass;
553                $body .= $line_break;
554                $body .= $h->lang['main_user_email_password_conf_body_remember'];
555                $body .= $line_break;
556                $body .= $h->lang['main_user_email_password_conf_body_pass_change'];
557                $body .= $line_break;
558                $body .= $url;
559                $body .= $line_break;
560                $body .= $h->lang['main_user_email_password_conf_body_regards'];
561                $body .= $next_line;
562                $body .= $h->lang['main_user_email_password_conf_body_sign'];
563                $to = $email;
564               
565                $h->email($to, $subject, $body);   
566               
567                return true;
568        }
569}
Note: See TracBrowser for help on using the repository browser.