source: branches/1.1dev/lib/AuthSQL.inc.php @ 577

Last change on this file since 577 was 577, checked in by anonymous, 7 years ago

Need to init auth session.

File size: 34.1 KB
Line 
1<?php
2/**
3 * The Auth_File:: class provides a SQL implementation for authentication.
4 *
5 * @author  Quinn Comendant <quinn@strangecode.com>
6 * @inspiration  Horde's Auth class <www.horde.org>
7 * @version 1.0
8 */
9class AuthSQL {
10
11    var $_params = array();
12    var $_auth_name = '_auth';
13    var $_authentication_tested;
14
15    /**
16     * Constructs a new authentication object.
17     *
18     * @access public
19     *
20     * @param optional array $params  A hash containing parameters.
21     */
22    function AuthSQL($params = array())
23    {
24        global $CFG;
25
26        // The name of this auth session.
27        $this->_params['auth_name'] = isset($params['auth_name']) ? $params['auth_name'] : '';
28
29        // The database table containing users to authenticate.
30        $this->_params['user_tbl'] = isset($params['user_tbl']) ? $params['user_tbl'] : 'user_tbl';
31
32        // The name of the primary key for the user_tbl.
33        $this->_params['user_id_column'] = isset($params['user_id_column']) ? $params['user_id_column'] : 'user_id';
34
35        // The name of the username key for the user_tbl.
36        $this->_params['username_column'] = isset($params['username_column']) ? $params['username_column'] : 'username';
37
38        // If using the login_tbl feature, specify the login_tbl. The primary key must match the primary key for the user_tbl.
39        $this->_params['login_tbl'] = isset($params['login_tbl']) ? $params['login_tbl'] : 'login_tbl';
40
41        // The type of encryption to use for passwords stored in the user_tbl. Use 'md5' or 'crypt'.
42        $this->_params['encryption_type'] = isset($params['encryption_type']) ? $params['encryption_type'] : 'md5';
43
44        // The URL the user will be directed if unsuccessfully calling requireLogin.
45        $this->_params['login_url'] = isset($params['login_url']) ? $params['login_url'] : '/';
46
47        // The maximum amount of time a user is allowed to be logged in. They will be forced to login again if they expire.
48        // This applies to admins and users. In seconds. 21600 seconds = 6 hours.
49        $this->_params['login_timeout'] = isset($params['login_timeout']) ? $params['login_timeout'] : $CFG->login_timeout;
50
51        // The maximum amount of time a user is allowed to be idle before their session expires. They will be forced to login again if they expire.
52        // This applies to admins and users. In seconds. 3600 seconds = 1 hour.
53        $this->_params['idle_timeout'] = isset($params['idle_timeout']) ? $params['idle_timeout'] : $CFG->idle_timeout;
54
55        // The period of time to compare login abuse attempts. If a threshold of logins is reached in this amount of time the account is blocked.
56        // Days and hours, like this: 'DD:HH'
57        $this->_params['login_abuse_timeframe'] = isset($params['login_abuse_timeframe']) ? $params['login_abuse_timeframe'] : $CFG->login_abuse_timeframe;
58
59        // The number of warnings a user will receive (and their password reset each time) before their account is completely blocked.
60        $this->_params['login_abuse_warnings'] = isset($params['login_abuse_warnings']) ? $params['login_abuse_warnings'] : $CFG->login_abuse_warnings;
61
62        // The maximum number of IP addresses a user can login with over the timeout period before their account is blocked.
63        $this->_params['login_abuse_max_ips'] = isset($params['login_abuse_max_ips']) ? $params['login_abuse_max_ips'] : $CFG->login_abuse_max_ips;
64
65        // The IP address subnet size threshold. Uses a CIDR notation network mask. Any integar between 0 and 32 is permitted. Setting this
66        // to '24' permits any address in a class C network (255.255.255.0) to be considered the same. Setting to '32' compares each IP absolutely.
67        // Setting to '0' ignores all IPs, thus disabling this feature.
68        $this->_params['login_abuse_ip_bitmask'] = isset($params['login_abuse_ip_bitmask']) ? $params['login_abuse_ip_bitmask'] : $CFG->login_abuse_ip_bitmask;
69
70        // Specify usernames to exclude from the account abuse detection system. This is specified as a hardcoded array provided at
71        // class instantiation time, or can be saved in the user_tbl under the login_abuse_exempt field.
72        $this->_params['login_abuse_exempt_usernames'] = isset($params['login_abuse_exempt_usernames']) && is_array($params['login_abuse_exempt_usernames']) ? $params['login_abuse_exempt_usernames'] : $CFG->login_abuse_exempt_usernames;
73
74        $this->_params['trusted_networks'] = isset($params['trusted_networks']) && is_array($params['trusted_networks']) ? $params['trusted_networks'] : $CFG->trusted_networks;
75
76        // Feature: Allow user accounts to be blocked? Requires the user table to have the columns 'blocked' and 'blocked_reason'
77        $this->_params['features']['blocking'] = isset($params['features']['blocking']) ? $params['features']['blocking'] : false;
78
79        // Feature: Use a login_tbl to detect excessive logins. This requires blocking to be enabled.
80        $this->_params['features']['abuse_detection'] = isset($params['features']['abuse_detection']) ? $params['features']['abuse_detection'] : false;
81
82        // Array of usernames which are exempt from remote_ip matching. Users behind proxy servers should be appended to this array so their shifting remote IP will not log them out.
83        $this->_params['match_remote_ip_exempt_usernames'] = isset($params['match_remote_ip_exempt_usernames']) && is_array($params['match_remote_ip_exempt_usernames']) ? $params['match_remote_ip_exempt_usernames'] : $CFG->match_remote_ip_exempt_usernames;
84
85        // Feature: Match the user's current remote IP against the one they logged in with.
86        $this->_params['features']['match_remote_ip'] = isset($params['features']['match_remote_ip']) ? $params['features']['match_remote_ip'] : true;
87
88        $this->_auth_name = '_auth_' . $this->_params['auth_name'];
89
90        if (!isset($_SESSION[$this->_auth_name])) {
91            $this->clearAuth();
92        }
93    }
94
95    /**
96     * Clear any authentication tokens in the current session. A.K.A. logout.
97     *
98     * @access public
99     */
100    function clearAuth()
101    {
102        dbQuery("
103            UPDATE " . $this->_params['user_tbl'] . " SET
104            seconds_online = seconds_online + (UNIX_TIMESTAMP() - UNIX_TIMESTAMP(last_access_datetime)),
105            last_login_datetime = '0000-00-00 00:00:00'
106            WHERE " . $this->_params['user_id_column'] . " = '" . $this->getVal('user_id') . "'
107        ");
108        $_SESSION[$this->_auth_name] = array();
109        $_SESSION[$this->_auth_name]['authenticated'] = false;
110    }
111
112    /**
113     * Sets a variable into a registered auth session.
114     *
115     * @access public
116     *
117     * @param mixed $key      Which value to set.
118     * @param mixed $val      Value to set variable to.
119     */
120    function setVal($key, $val)
121    {
122        if (!isset($_SESSION[$this->_auth_name]['user_data'])) {
123            $_SESSION[$this->_auth_name]['user_data'] = array();
124        }
125        $_SESSION[$this->_auth_name]['user_data'][$key] = $val;
126    }
127
128    /**
129     * Returns a specified value from a registered auth session.
130     *
131     * @access public
132     *
133     * @param mixed $key      Which value to return.
134     * @param mixed $default  Value to return if key not found in user_data.
135     *
136     * @return mixed          Value stored in session.
137     */
138    function getVal($key, $default='')
139    {
140        if (isset($_SESSION[$this->_auth_name][$key])) {
141            return $_SESSION[$this->_auth_name][$key];
142        } else if (isset($_SESSION[$this->_auth_name]['user_data'][$key])) {
143            return $_SESSION[$this->_auth_name]['user_data'][$key];
144        } else {
145            return $default;
146        }
147    }
148
149    /**
150     * Set the features of an auth object.
151     *
152     * @param  array $features   Array of feature keys and value to set.
153     *
154     * @return bool true on success, false on failure
155     */
156    function setFeature($features=null)
157    {
158        if (isset($features) && is_array($features)) {
159            // Set features for this object.
160            $this->_params['features'] = array_merge($this->_params['features'], $features);
161        }
162    }
163
164    /**
165     * Return the value of a feature configuration. This usually returns a bool value.
166     *
167     * @access public
168     *
169     * @param string $feature      Which feature to return.
170     *
171     * @return mixed               Configured feature value.
172     */
173    function getFeature($feature)
174    {
175        return $this->_params['features'][$feature];
176    }
177
178    /**
179     * Find out if a set of login credentials are valid.
180     *
181     * @access private
182     *
183     * @param string $username      The username to check.
184     * @param string $password      The password to compare to username.
185     *
186     * @return mixed  False if credentials not found in DB, or returns DB row matching credentials.
187     */
188    function authenticate($username, $password)
189    {
190        // Query DB for user matching credentials.
191        $qid = dbQuery("
192            SELECT *, " . $this->_params['user_id_column'] . " AS user_id
193            FROM " . $this->_params['user_tbl'] . "
194            WHERE BINARY username = '" . mysql_real_escape_string($username) . "'
195            AND BINARY userpass = '" . mysql_real_escape_string($this->encryptPassword($password)) . "'
196        ");
197
198        // Return user data if found.
199        if ($user_data = mysql_fetch_assoc($qid)) {
200            return $user_data;
201        } else {
202            return false;
203        }
204    }
205
206    /**
207     * If user authenticated, register login into session.
208     *
209     * @access private
210     *
211     * @param string $username     The username to check.
212     * @param string $password     The password to compare to username.
213     *
214     * @return boolean  Whether or not the credentials are valid.
215     */
216    function login($username, $password)
217    {
218        $this->clearAuth();
219
220        if (!$user_data = $this->authenticate($username, $password)) {
221            // No login: failed authentication!
222            return false;
223        }
224
225        // Register authenticated session.
226        $_SESSION[$this->_auth_name] = array(
227            'authenticated'         => true,
228            'user_id'               => $user_data['user_id'],
229            'auth_name'             => $this->_params['auth_name'],
230            'username'              => $username,
231            'priv'                  => $user_data['priv'],
232            'login_datetime'        => date('Y-m-d H:i:s'),
233            'last_access_datetime'  => date('Y-m-d H:i:s'),
234            'remote_ip'             => getRemoteAddr(),
235            'abuse_warning_level'   => $user_data['abuse_warning_level'],
236            'login_abuse_exempt'    => isset($user_data['login_abuse_exempt']) ? !empty($user_data['login_abuse_exempt']) : in_array(strtolower($username), $this->_params['login_abuse_exempt_usernames']),
237            'match_remote_ip_exempt'=> isset($user_data['match_remote_ip_exempt']) ? !empty($user_data['match_remote_ip_exempt']) : in_array(strtolower($username), $this->_params['match_remote_ip_exempt_usernames']),
238            'user_data'             => $user_data
239        );
240
241        /**
242         * Check if the account is blocked, respond in context to reason. Cancel the login if blocked.
243         */
244        if ($this->getFeature('blocking')) {
245            if (!empty($user_data['blocked'])) {
246
247                logMsg(sprintf('Login failed, blocked account. User: %s (%s) Reason: %s', $user_data['user_id'], $username, $user_data['blocked_reason']), LOG_NOTICE, __FILE__, __LINE__);
248
249                switch ($user_data['blocked_reason']) {
250                    case 'account abuse' :
251                        raiseMsg(sprintf(_("This account has been blocked due to possible account abuse. Please contact us to reactivate."), null), MSG_WARNING, __FILE__, __LINE__);
252                        break;
253                    default :
254                        raiseMsg(sprintf(_("This account is currently not active. %s"), $user_data['blocked_reason']), MSG_WARNING, __FILE__, __LINE__);
255                        break;
256                }
257
258                // No login: user is blocked!
259                $this->clearAuth();
260                return false;
261            }
262        }
263
264        /**
265         * Check the login_tbl for too many logins under this account.
266         * (1) Count the number of unique IP addresses that logged in under this user within the login_abuse_timeframe
267         * (2) If this number exceeds the login_abuse_max_ips, assume multiple people are logging in under the same account.
268        **/
269        if ($this->getFeature('abuse_detection') && !$this->getVal('login_abuse_exempt')) {
270            $qid = dbQuery("
271                SELECT COUNT(DISTINCT LEFT(remote_ip_binary, " . $this->_params['login_abuse_ip_bitmask'] . "))
272                FROM " . $this->_params['login_tbl'] . "
273                WHERE " . $this->_params['user_id_column'] . " = '" . $this->getVal('user_id') . "'
274                AND DATE_ADD(login_datetime, INTERVAL '" . $this->_params['login_abuse_timeframe'] . "' DAY_HOUR) > NOW()
275            ");
276            list($distinct_ips) = mysql_fetch_row($qid);
277            if ($distinct_ips > $this->_params['login_abuse_max_ips']) {
278                if ($this->getVal('abuse_warning_level') < $this->_params['login_abuse_warnings']) {
279                    // Warn the user with a password reset.
280                    $this->resetPassword(null, _("This is a security precaution. We have detected this account has been accessed from multiple computers simultaneously. It is against policy to share your login information with others. If further account abuse is detected your account will be blocked."));
281                    raiseMsg(_("Your password has been reset as a security precaution. Please check your email for more information."), MSG_NOTICE, __FILE__, __LINE__);
282                    logMsg(sprintf('Account abuse detected for user %s from IP %s', $this->getVal('username'), $this->getVal('remote_ip')), LOG_WARNING, __FILE__, __LINE__);
283                } else {
284                    // Block the account with the reason of account abuse.
285                    $this->blockAccount(null, 'account abuse');
286                    raiseMsg(_("Your account has been blocked as a security precaution. Please contact us for more information."), MSG_NOTICE, __FILE__, __LINE__);
287                    logMsg(sprintf('Account blocked for user %s from IP %s', $this->getVal('username'), $this->getVal('remote_ip')), LOG_ALERT, __FILE__, __LINE__);
288                }
289                // Increment user's warning level.
290                dbQuery("UPDATE " . $this->_params['user_tbl'] . " SET abuse_warning_level = abuse_warning_level + 1 WHERE " . $this->_params['user_id_column'] . " = '" . $this->getVal('user_id') . "'");
291                // Reset the login counter for this user.
292                dbQuery("DELETE FROM " . $this->_params['login_tbl'] . " WHERE " . $this->_params['user_id_column'] . " = '" . $this->getVal('user_id') . "'");
293                // No login: reset password because of account abuse!
294                $this->clearAuth();
295                return false;
296            }
297
298            // Update the login counter table with this login access. Convert IP to binary.
299            dbQuery("
300                INSERT INTO " . $this->_params['login_tbl'] . " (
301                    " . $this->_params['user_id_column'] . ",
302                    login_datetime,
303                    remote_ip_binary
304                ) VALUES (
305                    '" . $this->getVal('user_id') . "',
306                    '" . $this->getVal('login_datetime') . "',
307                    '" . sprintf('%032b', ip2long($this->getVal('remote_ip'))) . "'
308                )
309            ");
310        }
311
312        // Update user table with this login.
313        dbQuery("
314            UPDATE " . $this->_params['user_tbl'] . " SET
315                last_login_datetime = '" . $this->getVal('login_datetime') . "',
316                last_access_datetime = '" . $this->getVal('login_datetime') . "',
317                last_login_ip = '" . $this->getVal('remote_ip') . "'
318            WHERE " . $this->_params['user_id_column'] . " = '" . $this->getVal('user_id') . "'
319        ");
320
321        // We're logged-in!
322        return true;
323    }
324
325    /**
326     * Test if user has a currently logged-in session.
327     *  - authentication flag set to true
328     *  - username not empty
329     *  - total logged-in time is not greater than login_timeout
330     *  - idle time is not greater than idle_timeout
331     *  - remote address is the same as the login remote address (aol users excluded).
332     *
333     * @access public
334     */
335    function isLoggedIn($user_id=null)
336    {
337        if (isset($user_id)) {
338            // Check the login status of a specific user.
339            $qid = dbQuery("
340                SELECT 1 FROM " . $this->_params['user_tbl'] . "
341                WHERE " . $this->_params['user_id_column'] . " = '" . mysql_real_escape_string($user_id) . "'
342                AND DATE_ADD(last_login_datetime, INTERVAL '" . $this->_params['login_timeout'] . "' SECOND) > NOW()
343                AND DATE_ADD(last_access_datetime, INTERVAL '" . $this->_params['idle_timeout'] . "' SECOND) > NOW()
344            ");
345            return (mysql_num_rows($qid) > 0);
346        }
347
348        // User login test need only be run once per script execution. We cache the result in the session.
349        if ($this->_authentication_tested && isset($_SESSION[$this->_auth_name]['authenticated'])) {
350            return $_SESSION[$this->_auth_name]['authenticated'];
351        }
352
353        // Tesing login should occur once. This is the first time. Set flag.
354        $this->_authentication_tested = true;
355
356        // Some users will access from networks with changing IP number (i.e. behind a proxy server). These users must be allowed entry be adding their IP to the list of trusted_networks.
357        if ($trusted_net = ipInRange(getRemoteAddr(), $this->_params['trusted_networks'])) {
358            $user_in_trusted_network = true;
359            logMsg(sprintf('%s%s accessing from trusted network %s',
360                ucfirst($this->_params['auth_name']),
361                ($this->getVal('user_id') ? ' ' . $this->getVal('user_id') . ' (' .  $this->getVal('username') . ')' : ''),
362                $trusted_net
363            ), LOG_INFO, __FILE__, __LINE__);
364        } else if (preg_match('/proxy.aol.com$/i', getRemoteAddr(true))) {
365            $user_in_trusted_network = true;
366            logMsg(sprintf('%s%s accessing from trusted network proxy.aol.com',
367                ucfirst($this->_params['auth_name']),
368                ($this->getVal('user_id') ? ' ' . $this->getVal('user_id') . ' (' .  $this->getVal('username') . ')' : '')
369            ), LOG_NOTICE, __FILE__, __LINE__);
370        } else {
371            $user_in_trusted_network = false;
372        }
373
374        // Do we match the user's remote IP at all? Yes, if set in config and not disabled for specific user.
375        if ($this->getFeature('match_remote_ip') && !$this->getVal('match_remote_ip_exempt')) {
376            $remote_ip_is_matched = ($_SESSION[$this->_auth_name]['remote_ip'] == getRemoteAddr() || $user_in_trusted_network);
377        } else {
378            logMsg(sprintf('%s%s exempt from remote_ip match.',
379                ucfirst($this->_params['auth_name']),
380                ($this->getVal('user_id') ? ' ' . $this->getVal('user_id') . ' (' .  $this->getVal('username') . ')' : '')
381            ), LOG_DEBUG, __FILE__, __LINE__);
382            $remote_ip_is_matched = true;
383        }
384
385        // Test login with information stored in session. Skip IP matching for users from trusted networks.
386        if (true === $_SESSION[$this->_auth_name]['authenticated']
387            && !empty($_SESSION[$this->_auth_name]['username'])
388            && strtotime($_SESSION[$this->_auth_name]['login_datetime']) > time() - $this->_params['login_timeout']
389            && strtotime($_SESSION[$this->_auth_name]['last_access_datetime']) > time() - $this->_params['idle_timeout']
390            && $remote_ip_is_matched
391        ) {
392            // User is authenticated!
393            $_SESSION[$this->_auth_name]['last_access_datetime'] = date('Y-m-d H:i:s');
394
395            // Update the DB with the last_access_datetime and increment the seconds_online.
396            dbQuery("
397                UPDATE " . $this->_params['user_tbl'] . " SET
398                seconds_online = seconds_online + (UNIX_TIMESTAMP() - UNIX_TIMESTAMP(last_access_datetime)) + 1,
399                last_access_datetime = '" . $this->getVal('last_access_datetime') . "'
400                WHERE " . $this->_params['user_id_column'] . " = '" . $this->getVal('user_id') . "'
401            ");
402            if (mysql_affected_rows($GLOBALS['dbh']) > 0) {
403                // User record still exists in DB. Do this to ensure user was not delete from DB between accesses. Notice "+ 1" in SQL above to ensure record is modified.
404                return true;
405            } else {
406                logMsg(sprintf('User update failed. Record not found for %s %s (%s).', $this->_params['auth_name'], $this->getVal('user_id'), $this->getVal('username')), LOG_NOTICE, __FILE__, __LINE__);
407            }
408        } else if (true === $_SESSION[$this->_auth_name]['authenticated']) {
409            // User is authenticated, but login has expired.
410            raiseMsg(sprintf(_("Your %s session has closed. You need to log-in again."), strtolower($this->_params['auth_name'])), MSG_NOTICE, __FILE__, __LINE__);
411
412            // Log the reason for login expiration.
413            $expire_reasons = array();
414            if (empty($_SESSION[$this->_auth_name]['username'])) {
415                $expire_reasons[] = 'username not found';
416            }
417            if (strtotime($_SESSION[$this->_auth_name]['login_datetime']) <= time() - $this->_params['login_timeout']) {
418                $expire_reasons[] = 'login_timeout expired';
419            }
420            if (strtotime($_SESSION[$this->_auth_name]['last_access_datetime']) <= time() - $this->_params['idle_timeout']) {
421                $expire_reasons[] = 'idle_timeout expired';
422            }
423            if ($_SESSION[$this->_auth_name]['remote_ip'] != getRemoteAddr()) {
424                if ($this->getFeature('match_remote_ip') && !$this->getVal('match_remote_ip_exempt')) {
425                    $expire_reasons[] = sprintf('remote_ip not matched (%s != %s)', $_SESSION[$this->_auth_name]['remote_ip'], getRemoteAddr());
426                } else {
427                    $expire_reasons[] = sprintf('remote_ip not matched but user was exempt from this check (%s != %s)', $_SESSION[$this->_auth_name]['remote_ip'], getRemoteAddr());
428                }
429            }
430            logMsg(sprintf('%s %s (%s) session expired: %s', ucfirst($this->_params['auth_name']), $this->getVal('user_id'), $this->getVal('username'), join(', ', $expire_reasons)), LOG_DEBUG, __FILE__, __LINE__);
431        }
432
433        // User is not authenticated.
434        $this->clearAuth();
435        return false;
436    }
437
438    /**
439     * Redirect user to login page if they are not logged in.
440     *
441     * @param string $msg     The text description of a message to raise.
442     * @param int    $type    The type of message: MSG_NOTICE,
443     *                        MSG_SUCCESS, MSG_WARNING, or MSG_ERR.
444     * @param string $file    __FILE__.
445     * @param string $line    __LINE__.
446     *
447     * @access public
448     */
449    function requireLogin($msg='', $type=MSG_NOTICE, $file=null, $line=null)
450    {
451        if (!$this->isLoggedIn()) {
452            if ('' != $msg) {
453                raiseMsg($msg, $type, $file, $line);
454            }
455            setBoomerangURL(absoluteMe());
456            dieURL($this->_params['login_url']);
457        }
458    }
459
460    /**
461     * This sets the 'blocked' field for a user in the user_tbl, and also
462     * adds an optional reason
463     *
464     * @param  string   $reason      The reason for blocking the account.
465     */
466    function blockAccount($user_id=null, $reason='')
467    {
468        if ($this->getFeature('blocking')) {
469            if (strlen(mysql_real_escape_string($reason)) > 255) {
470                // blocked_reason field is varchar(255).
471                logMsg(sprintf('Blocked reason provided is greater than 255 characters: %s', $reason), LOG_WARNING, __FILE__, __LINE__);
472            }
473
474            // Get user_id if specified.
475            $user_id = isset($user_id) ? $user_id : $this->getVal('user_id');
476            dbQuery("
477                UPDATE " . $this->_params['user_tbl'] . " SET
478                blocked = 'true',
479                blocked_reason = '" . mysql_real_escape_string($reason) . "'
480                WHERE " . $this->_params['user_id_column'] . " = '" . mysql_real_escape_string($user_id) . "'
481            ");
482        }
483    }
484
485    /**
486     * Unblocks a user in the user_tbl, and clears any blocked_reason.
487     */
488    function unblockAccount($user_id=null)
489    {
490        if ($this->getFeature('blocking')) {
491            // Get user_id if specified.
492            $user_id = isset($user_id) ? $user_id : $this->getVal('user_id');
493            dbQuery("
494                UPDATE " . $this->_params['user_tbl'] . " SET
495                blocked = '',
496                blocked_reason = ''
497                WHERE " . $this->_params['user_id_column'] . " = '" . mysql_real_escape_string($user_id) . "'
498            ");
499        }
500    }
501
502    /**
503     * Returns true if username already exists in database.
504     *
505     * @param  string  $username    Username to look for.
506     *
507     * @return bool                 True if username exists.
508     */
509    function usernameExists($username)
510    {
511        $qid = dbQuery("SELECT 1 FROM " . $this->_params['user_tbl'] . " WHERE username = '" . mysql_real_escape_string($username) . "'");
512        return (mysql_num_rows($qid) > 0);
513    }
514
515    /**
516     * Returns a username for a specified user id.
517     *
518     * @param  string  $user_id     User id to look for.
519     *
520     * @return string               Username, or false if none found.
521     */
522    function getUsername($user_id)
523    {
524        $qid = dbQuery("SELECT " . $this->_params['username_column'] . " FROM " . $this->_params['user_tbl'] . " WHERE " . $this->_params['user_id_column'] . " = '" . mysql_real_escape_string($user_id) . "'");
525        if (list($username) = mysql_fetch_row($qid)) {
526            return $username;
527        } else {
528            return false;
529        }
530    }
531
532    /**
533     * Returns a randomly generated password based on $pattern. The pattern is any
534     * sequence of 'x', 'V', 'C', 'v', 'c', or 'd' and if it is something like 'cvccv' this
535     * function will generate a pronouncable password. Recommend using more complex
536     * patterns, at minimum the US State Department standard: cvcddcvc.
537     *
538     * - x    a random upper or lower alpha character or digit
539     * - C    a random upper or lower consanant
540     * - V    a random upper or lower vowel
541     * - c    a random lowercase consanant
542     * - v    a random lowercase vowel
543     * - d    a random digit
544     *
545     * @param  string $pattern  a sequence of character types, above.
546     *
547     * @return string           a password
548     */
549    function generatePassword($pattern='CvccvCdd')
550    {
551        mt_srand((double) microtime() * 10000000);
552        for ($i=0; $i<strlen($pattern); $i++) {
553            $x = substr('bcdfghjklmnprstvwxzBCDFGHJKLMNPRSTVWXZaeiouyAEIOUY0123456789', (mt_rand() % 60), 1);
554            $c = substr('bcdfghjklmnprstvwxz', (mt_rand() % 19), 1);
555            $C = substr('bcdfghjklmnprstvwxzBCDFGHJKLMNPRSTVWXZ', (mt_rand() % 38), 1);
556            $v = substr('aeiouy', (mt_rand() % 6), 1);
557            $V = substr('aeiouyAEIOUY', (mt_rand() % 12), 1);
558            $d = substr('0123456789', (mt_rand() % 10), 1);
559            $str .= $$pattern{$i};
560        }
561        return $str;
562    }
563
564    /**
565     *
566     */
567    function encryptPassword($password)
568    {
569        switch ($this->_params['encryption_type']) {
570        case 'plain' :
571            return $password;
572            break;
573
574        case 'crypt' :
575            return crypt($password, crypt($password));
576            break;
577
578        case 'sha1' :
579            if (function_exists('sha1')) { // Only in PHP 4.3.0+
580                return sha1($password);
581                break;
582            }
583
584        case 'md5' :
585        default :
586            return md5($password);
587            break;
588        }
589    }
590
591    /**
592     *
593     */
594    function setPassword($user_id=null, $password)
595    {
596        // Get user_id if specified.
597        $user_id = isset($user_id) ? $user_id : $this->getVal('user_id');
598
599        // Issue the password change query.
600        dbQuery("
601            UPDATE " . $this->_params['user_tbl'] . "
602            SET userpass = '" . mysql_real_escape_string($this->encryptPassword($password)) . "'
603            WHERE " . $this->_params['user_id_column'] . " = '" . mysql_real_escape_string($user_id) . "'
604        ");
605    }
606
607    /**
608     * Resets the password for the user with the specified id.
609     *
610     * @param  string $user_id   The id of the user to reset.
611     * @param  string $reason    Additional message to add to the reset email.
612     *
613     * @return string            The user's new password.
614     */
615    function resetPassword($user_id=null, $reason='')
616    {
617        global $CFG;
618
619        // Get user_id if specified.
620        $user_id = isset($user_id) ? $user_id : $this->getVal('user_id');
621
622        // Reset password of a specific user.
623        $qid = dbQuery("
624            SELECT * FROM " . $this->_params['user_tbl'] . "
625            WHERE " . $this->_params['user_id_column'] . " = '" . mysql_real_escape_string($user_id) . "'
626        ");
627        $user_data = mysql_fetch_assoc($qid);
628
629        // Get new password.
630        $password = $this->generatePassword();
631
632        // Issue the password change query.
633        dbQuery("
634            UPDATE " . $this->_params['user_tbl'] . "
635            SET userpass = '" . mysql_real_escape_string($this->encryptPassword($password)) . "'
636            WHERE " . $this->_params['user_id_column'] . " = '" . mysql_real_escape_string($user_id) . "'
637        ");
638
639        // Email the user with the new account information.
640        // $reason is used in this template.
641        if (include 'email_reset_password.ihtml') {
642            mail($user_data['email'], $email_subject, $email_body, "From: $CFG->site_name <$CFG->site_email>\r\n", $CFG->envelope_sender_address);
643        }
644
645        return array('username'=>$user_data['username'], 'userpass'=>$password);
646    }
647
648    /**
649     * If the current user has access to the specified $security_zone, return true.
650     * If the optional $priv is supplied, test that against the zone.
651     *
652     * @param  constant $security_zone   string of comma delimited priviliges for the zone
653     * @param  string   $priv            a privilege that might be found in a zone
654     *
655     * @return bool     true if user is a member of security zone, false otherwise
656     */
657    function inClearanceZone($security_zone, $priv='')
658    {
659        $zone_members = preg_split('/,\s*/', $security_zone);
660        $priv = empty($priv) ? $this->getVal('priv') : $priv;
661
662        // If the current user's privilege level is NOT in that array or if the
663        // user has no privilege, return false. Otherwise the user is clear.
664        if (!in_array($priv, $zone_members) || empty($priv)) {
665            return false;
666        } else {
667            return true;
668        }
669    }
670
671    /**
672     * This function tests a list of arguments $security_zone against the priv that the current user has.
673     * If the user doesn't have one of the supplied privs, die.
674     *
675     * @param  constant $security_zone   string of comma delimited priviliges for the zone
676     */
677    function requireAccessClearance($security_zone, $msg='')
678    {
679        $zone_members = preg_split('/,\s*/', $security_zone);
680
681        /* If the current user's privilege level is NOT in that array or if the
682         * user has no privilege, DIE with a message. */
683        if (!in_array($this->getVal('priv'), $zone_members) || !$this->getVal('priv')) {
684            $msg = empty($msg) ? _("You have insufficient privileges to view that page.") : $msg;
685            raiseMsg($msg, MSG_NOTICE, __FILE__, __LINE__);
686            dieBoomerangURL();
687        }
688    }
689
690} // end class
691
692// CIDR cheatsheet
693//
694// Netmask              Netmask (binary)                 CIDR     Notes
695// _____________________________________________________________________________
696// 255.255.255.255  11111111.11111111.11111111.11111111  /32  Host (single addr)
697// 255.255.255.254  11111111.11111111.11111111.11111110  /31  Unuseable
698// 255.255.255.252  11111111.11111111.11111111.11111100  /30    2  useable
699// 255.255.255.248  11111111.11111111.11111111.11111000  /29    6  useable
700// 255.255.255.240  11111111.11111111.11111111.11110000  /28   14  useable
701// 255.255.255.224  11111111.11111111.11111111.11100000  /27   30  useable
702// 255.255.255.192  11111111.11111111.11111111.11000000  /26   62  useable
703// 255.255.255.128  11111111.11111111.11111111.10000000  /25  126  useable
704// 255.255.255.0    11111111.11111111.11111111.00000000  /24 "Class C" 254 useable
705//
706// 255.255.254.0    11111111.11111111.11111110.00000000  /23    2  Class C's
707// 255.255.252.0    11111111.11111111.11111100.00000000  /22    4  Class C's
708// 255.255.248.0    11111111.11111111.11111000.00000000  /21    8  Class C's
709// 255.255.240.0    11111111.11111111.11110000.00000000  /20   16  Class C's
710// 255.255.224.0    11111111.11111111.11100000.00000000  /19   32  Class C's
711// 255.255.192.0    11111111.11111111.11000000.00000000  /18   64  Class C's
712// 255.255.128.0    11111111.11111111.10000000.00000000  /17  128  Class C's
713// 255.255.0.0      11111111.11111111.00000000.00000000  /16  "Class B"
714//
715// 255.254.0.0      11111111.11111110.00000000.00000000  /15    2  Class B's
716// 255.252.0.0      11111111.11111100.00000000.00000000  /14    4  Class B's
717// 255.248.0.0      11111111.11111000.00000000.00000000  /13    8  Class B's
718// 255.240.0.0      11111111.11110000.00000000.00000000  /12   16  Class B's
719// 255.224.0.0      11111111.11100000.00000000.00000000  /11   32  Class B's
720// 255.192.0.0      11111111.11000000.00000000.00000000  /10   64  Class B's
721// 255.128.0.0      11111111.10000000.00000000.00000000  /9   128  Class B's
722// 255.0.0.0        11111111.00000000.00000000.00000000  /8   "Class A"
723//
724// 254.0.0.0        11111110.00000000.00000000.00000000  /7
725// 252.0.0.0        11111100.00000000.00000000.00000000  /6
726// 248.0.0.0        11111000.00000000.00000000.00000000  /5
727// 240.0.0.0        11110000.00000000.00000000.00000000  /4
728// 224.0.0.0        11100000.00000000.00000000.00000000  /3
729// 192.0.0.0        11000000.00000000.00000000.00000000  /2
730// 128.0.0.0        10000000.00000000.00000000.00000000  /1
731// 0.0.0.0          00000000.00000000.00000000.00000000  /0   IP space
732?>
Note: See TracBrowser for help on using the repository browser.