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

Last change on this file was 755, checked in by anonymous, 2 years ago

Minor backporting

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