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

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

Q - Finished integrating singleton methods into existing code. Renamed SessionCache? to Cache, and renamed methods in Cache and Prefs

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