source: branches/2.0singleton/lib/Auth_File.inc.php @ 127

Last change on this file since 127 was 127, checked in by scdev, 18 years ago

Updated App.inc.php thru Hierarchy.inc.php

File size: 13.0 KB
Line 
1<?php
2/**
3 * The Auth_File:: class provides a htpasswd file implementation for
4 * authentication.
5 *
6 * @author  Quinn Comendant <quinn@strangecode.com>
7 * @version 1.2
8 */
9 
10// Usage example:
11// $auth = new Auth_File();
12// $auth->setParam(array(
13//     'htpasswd_file' => COMMON_BASE . '/global/site_users.htpasswd',
14//     'login_timeout' => 21600,
15//     'idle_timeout' => 3600,
16//     'login_url' => '/login.php'
17// ));
18
19// Available encryption types for class Auth_SQL.
20define('AUTH_ENCRYPT_MD5', 'md5');
21define('AUTH_ENCRYPT_CRYPT', 'crypt');
22define('AUTH_ENCRYPT_SHA1', 'sha1');
23define('AUTH_ENCRYPT_PLAINTEXT', 'plaintext');
24
25class Auth_File {
26
27    var $_auth = '';
28    var $_sess = '_auth_';
29    var $_params = array();
30    var $_default_params = array(
31       
32        // Full path to htpasswd file.
33        'htpasswd_file' => null,
34
35        // The type of encryption to use for passwords stored in the db_table. Use one of the AUTH_ENCRYPT_* types specified above.
36        'encryption_type' => AUTH_ENCRYPT_CRYPT,
37
38        // The URL to the login script.
39        'login_url' => '/',
40
41        // The maximum amount of time a user is allowed to be logged in. They will be forced to login again if they expire.
42        // This applies to admins and users. In seconds. 21600 seconds = 6 hours.
43        'login_timeout' => 21600,
44
45        // 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.
46        // This applies to admins and users. In seconds. 3600 seconds = 1 hour.
47        'idle_timeout' => 3600,
48
49        // An array of IP blocks that are bypass the remote_ip comparison check. Useful for dynamic IPs or those behind proxy servers.
50        'trusted_networks' => array(),
51    );
52
53    // Associative array of usernames to hashed passwords.
54    var $_users = array();
55
56    /**
57     * Constructs a new htpasswd authentication object.
58     *
59     * @access public
60     *
61     * @param optional array $params  A hash containing parameters.
62     */
63    function Auth_File($auth_name=null)
64    {
65        if (isset($auth_name)) {
66            $this->_auth = $auth_name;
67            $this->_sess .= $auth_name;
68        }
69
70        // Initialize default parameters.
71        $this->setParam($this->_default_params);
72    }
73
74    /**
75     * Set the params of an auth object.
76     *
77     * @param  array $params   Array of parameter keys and value to set.
78     * @return bool true on success, false on failure
79     */
80    function setParam($params)
81    {
82        if (isset($params) && is_array($params)) {
83            // Merge new parameters with old overriding only those passed.
84            $this->_params = array_merge($this->_params, $params);
85        }
86    }
87
88    /**
89     * Return the value of a parameter, if it exists.
90     *
91     * @access public
92     * @param string $param        Which parameter to return.
93     * @return mixed               Configured parameter value.
94     */
95    function getParam($param)
96    {
97        $app =& App::getInstance();
98   
99        if (isset($this->_params[$param])) {
100            return $this->_params[$param];
101        } else {
102            $app->logMsg(sprintf('Parameter is not set: %s', $param), LOG_DEBUG, __FILE__, __LINE__);
103            return null;
104        }
105    }
106
107    /**
108     * Clear any authentication tokens in the current session. A.K.A. logout.
109     *
110     * @access public
111     */
112    function clearAuth()
113    {
114        $_SESSION[$this->_sess] = array('authenticated' => false);
115    }
116
117
118    /**
119     * Sets a variable into a registered auth session.
120     *
121     * @access public
122     * @param mixed $key      Which value to set.
123     * @param mixed $val      Value to set variable to.
124     */
125    function setVal($key, $val)
126    {
127        if (!isset($_SESSION[$this->_sess]['user_data'])) {
128            $_SESSION[$this->_sess]['user_data'] = array();
129        }
130        $_SESSION[$this->_sess]['user_data'][$key] = $val;
131    }
132
133    /**
134     * Returns a specified value from a registered auth session.
135     *
136     * @access public
137     * @param mixed $key      Which value to return.
138     * @param mixed $default  Value to return if key not found in user_data.
139     * @return mixed          Value stored in session.
140     */
141    function getVal($key, $default='')
142    {
143        if (isset($_SESSION[$this->_sess][$key])) {
144            return $_SESSION[$this->_sess][$key];
145        } else if (isset($_SESSION[$this->_sess]['user_data'][$key])) {
146            return $_SESSION[$this->_sess]['user_data'][$key];
147        } else {
148            return $default;
149        }
150    }
151    /**
152     * Find out if a set of login credentials are valid. Only supports
153     * htpasswd files with DES passwords right now.
154     *
155     * @access public
156     *
157     * @param string $username      The username to check.
158     * @param array $password      The password to compare to username.
159     *
160     * @return boolean  Whether or not the credentials are valid.
161     */
162    function authenticate($username, $password)
163    {
164        $app =& App::getInstance();
165   
166        if ('' == trim($password)) {
167            $app->logMsg(_("No password provided for authentication."), LOG_INFO, __FILE__, __LINE__);
168            return false;
169        }
170       
171        // Load users file.
172        $this->_loadHTPasswdFile();
173
174        if (!isset($this->_users[$username])) {
175            $app->logMsg(_("User ID provided does not exist."), LOG_INFO, __FILE__, __LINE__);
176            return false;
177        }
178
179        if ($this->_encrypt($password, $this->_users[$username]) != $this->_users[$username]) {
180            $app->logMsg(sprintf('Authentication failed for user %s', $username), LOG_INFO, __FILE__, __LINE__);
181            return false;
182        }
183       
184        // Authentication successful!
185        return true;
186    }
187
188    /**
189     * If user passes authentication create authenticated session.
190     *
191     * @access public
192     *
193     * @param string $username     The username to check.
194     * @param array $password     The password to compare to username.
195     *
196     * @return boolean  Whether or not the credentials are valid.
197     */
198    function login($username, $password)
199    {
200        $username = strtolower(trim($username));
201
202        $this->clearAuth();
203
204        if (!$this->authenticate($username, $password)) {
205            // No login: failed authentication!
206            return false;
207        }
208       
209        $_SESSION[$this->_sess] = array(
210            'authenticated' => true,
211            'username' => $username,
212            'login_datetime' => date('Y-m-d H:i:s'),
213            'last_access_datetime' => date('Y-m-d H:i:s'),
214            'remote_ip' => getRemoteAddr()
215        );
216
217        // We're logged-in!
218        return true;
219    }
220
221    /**
222     * Test if user has a currently logged-in session.
223     *  - authentication flag set to true
224     *  - username not empty
225     *  - total logged-in time is not greater than login_timeout
226     *  - idle time is not greater than idle_timeout
227     *  - remote address is the same as the login remote address.
228     *
229     * @access public
230     */
231    function isLoggedIn()
232    {
233        $app =& App::getInstance();
234   
235        // Some users will access from networks with a changing IP number (i.e. behind a proxy server). These users must be allowed entry by adding their IP to the list of trusted_networks.
236        if ($trusted_net = ipInRange(getRemoteAddr(), $this->_params['trusted_networks'])) {
237            $user_in_trusted_network = true;
238            $app->logMsg(sprintf('User %s accessing from trusted network %s', $_SESSION[$this->_sess]['username'], $trusted_net), LOG_DEBUG, __FILE__, __LINE__);
239        } else if (preg_match('/proxy.aol.com$/i', getRemoteAddr(true))) {
240            $user_in_trusted_network = true;
241            $app->logMsg(sprintf('User %s accessing from trusted network proxy.aol.com', $_SESSION[$this->_sess]['username']), LOG_DEBUG, __FILE__, __LINE__);
242        } else {
243            $user_in_trusted_network = false;
244        }
245
246        // Test login with information stored in session. Skip IP matching for users from trusted networks.
247        if (isset($_SESSION[$this->_sess])
248            && true === $_SESSION[$this->_sess]['authenticated']
249            && !empty($_SESSION[$this->_sess]['username'])
250            && strtotime($_SESSION[$this->_sess]['login_datetime']) > time() - $this->_params['login_timeout']
251            && strtotime($_SESSION[$this->_sess]['last_access_datetime']) > time() - $this->_params['idle_timeout']
252            && ($_SESSION[$this->_sess]['remote_ip'] == getRemoteAddr() || $user_in_trusted_network)
253        ) {
254            // User is authenticated!
255            $_SESSION[$this->_sess]['last_access_datetime'] = date('Y-m-d H:i:s');
256            return true;
257        } else if (isset($_SESSION[$this->_sess]) && true === $_SESSION[$this->_sess]['authenticated']) {
258            if (strtotime($_SESSION[$this->_sess]['last_access_datetime']) > time() - 43200) {
259                // Only raise message if last session is less than 12 hours old.
260                $app->raiseMsg(_("Your session has closed. You need to log-in again."), MSG_NOTICE, __FILE__, __LINE__);
261            }
262
263            // Log the reason for login expiration.
264            $expire_reasons = array();
265            if (empty($_SESSION[$this->_sess]['username'])) {
266                $expire_reasons[] = 'username not found';
267            }
268            if (strtotime($_SESSION[$this->_sess]['login_datetime']) <= time() - $this->_params['login_timeout']) {
269                $expire_reasons[] = 'login_timeout expired';
270            }
271            if (strtotime($_SESSION[$this->_sess]['last_access_datetime']) <= time() - $this->_params['idle_timeout']) {
272                $expire_reasons[] = 'idle_timeout expired';
273            }
274            if ($_SESSION[$this->_sess]['remote_ip'] != getRemoteAddr() && !$user_in_trusted_network) {
275                $expire_reasons[] = sprintf('remote_ip not matched (%s != %s)', $_SESSION[$this->_sess]['remote_ip'], getRemoteAddr());
276            }
277            $app->logMsg(sprintf('User %s session expired: %s', $_SESSION[$this->_sess]['username'], join(', ', $expire_reasons)), LOG_INFO, __FILE__, __LINE__);
278        }
279
280        return false;
281    }
282
283    /**
284     * Redirect user to login page if they are not logged in.
285     *
286     * @param string $message The text description of a message to raise.
287     * @param int    $type    The type of message: MSG_NOTICE,
288     *                        MSG_SUCCESS, MSG_WARNING, or MSG_ERR.
289     * @param string $file    __FILE__.
290     * @param string $line    __LINE__.
291     * @access public
292     */
293    function requireLogin($message='', $type=MSG_NOTICE, $file=null, $line=null)
294    {
295        $app =& App::getInstance();
296   
297        if (!$this->isLoggedIn()) {
298            // Display message for requiring login. (RaiseMsg will ignore empty strings.)
299            $app->raiseMsg($message, $type, $file, $line);
300
301            // Login scripts must have the same 'login' tag for boomerangURL verification/manipulation.
302            $app->setBoomerangURL(absoluteMe(), 'login');
303            $app->dieURL($this->_params['login_url']);
304        }
305    }
306   
307    /*
308    * Reads the configured htpasswd file into the _users array.
309    *
310    * @access   public
311    * @return   false on error, true on success.
312    * @author   Quinn Comendant <quinn@strangecode.com>
313    * @version  1.0
314    * @since    18 Apr 2006 18:17:48
315    */
316    function _loadHTPasswdFile()
317    {
318        $app =& App::getInstance();
319   
320        static $users = null;
321       
322        if (!file_exists($this->_params['htpasswd_file'])) {
323            $app->logMsg(sprintf('htpasswd file missing or not specified: %s', $this->_params['htpasswd_file']), LOG_ERR, __FILE__, __LINE__);
324            return false;
325        }
326       
327        if (!isset($users)) {
328            if (false === ($users = file($this->_params['htpasswd_file']))) {
329                $app->logMsg(sprintf(_("Could not read htpasswd file: %s"), $this->_params['htpasswd_file']), LOG_ERR, __FILE__, __LINE__);
330                return false;
331            }
332        }
333
334        if (is_array($users)) {
335            foreach ($users as $u) {
336                list($user, $pass) = explode(':', $u, 2);
337                $this->_users[trim($user)] = trim($pass);
338            }
339            return true;
340        }
341        return false;
342    }
343
344    /**
345     * Hash a given password according to the configured encryption
346     * type.
347     *
348     * @param string $password              The password to encrypt.
349     * @param string $encrypted_password    The currently encrypted password to use as salt, if needed.
350     *
351     * @return string  The hashed password.
352     */
353    function _encrypt($password, $encrypted_password=null)
354    {
355        switch ($this->_params['encryption_type']) {
356        case AUTH_ENCRYPT_PLAINTEXT :
357            return $password;
358            break;
359
360        case AUTH_ENCRYPT_SHA1 :
361            return sha1($password);
362            break;
363
364        case AUTH_ENCRYPT_MD5 :
365            return md5($password);
366            break;
367
368        case AUTH_ENCRYPT_CRYPT :
369        default :
370            return crypt($password, $encrypted_password);
371            break;
372        }
373    }
374
375} // end class
376?>
Note: See TracBrowser for help on using the repository browser.