source: branches/dev/data/module/Auth/Auth.php @ 13183

Revision 13183, 35.3 KB checked in by matsumoto, 17 years ago (diff)
Line 
1<?php
2/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker: */
3
4/**
5 * The main include file for Auth package
6 *
7 * PHP versions 4 and 5
8 *
9 * LICENSE: This source file is subject to version 3.01 of the PHP license
10 * that is available through the world-wide-web at the following URI:
11 * http://www.php.net/license/3_01.txt.  If you did not receive a copy of
12 * the PHP License and are unable to obtain it through the web, please
13 * send a note to license@php.net so we can mail you a copy immediately.
14 *
15 * @category   Authentication
16 * @package    Auth
17 * @author     Martin Jansen <mj@php.net>
18 * @author     Adam Ashley <aashley@php.net>
19 * @copyright  2001-2006 The PHP Group
20 * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
21 * @version    CVS: $Id: Auth.php,v 1.115 2007/02/02 00:41:14 aashley Exp $
22 * @link       http://pear.php.net/package/Auth
23 */
24
25/**
26 * Returned if session exceeds idle time
27 */
28define('AUTH_IDLED',                    -1);
29/**
30 * Returned if session has expired
31 */
32define('AUTH_EXPIRED',                  -2);
33/**
34 * Returned if container is unable to authenticate user/password pair
35 */
36define('AUTH_WRONG_LOGIN',              -3);
37/**
38 * Returned if a container method is not supported.
39 */
40define('AUTH_METHOD_NOT_SUPPORTED',     -4);
41/**
42 * Returned if new Advanced security system detects a breach
43 */
44define('AUTH_SECURITY_BREACH',          -5);
45/**
46 * Returned if checkAuthCallback says session should not continue.
47 */
48define('AUTH_CALLBACK_ABORT',           -6);
49
50/**
51 * Auth Log level - INFO
52 */
53define('AUTH_LOG_INFO',     6);
54/**
55 * Auth Log level - DEBUG
56 */
57define('AUTH_LOG_DEBUG',    7);
58
59
60/**
61 * PEAR::Auth
62 *
63 * The PEAR::Auth class provides methods for creating an
64 * authentication system using PHP.
65 *
66 * @category   Authentication
67 * @package    Auth
68 * @author     Martin Jansen <mj@php.net>
69 * @author     Adam Ashley <aashley@php.net>
70 * @copyright  2001-2006 The PHP Group
71 * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
72 * @version    Release: 1.5.1  File: $Revision: 1.115 $
73 * @link       http://pear.php.net/package/Auth
74 */
75class Auth {
76
77    // {{{ properties
78
79    /**
80     * Auth lifetime in seconds
81     *
82     * If this variable is set to 0, auth never expires
83     *
84     * @var  integer
85     * @see  setExpire(), checkAuth()
86     */
87    var $expire = 0;
88
89    /**
90     * Has the auth session expired?
91     *
92     * @var   bool
93     * @see   checkAuth()
94     */
95    var $expired = false;
96
97    /**
98     * Maximum idletime in seconds
99     *
100     * The difference to $expire is, that the idletime gets
101     * refreshed each time checkAuth() is called. If this
102     * variable is set to 0, idletime is never checked.
103     *
104     * @var integer
105     * @see setIdle(), checkAuth()
106     */
107    var $idle = 0;
108
109    /**
110     * Is the maximum idletime over?
111     *
112     * @var boolean
113     * @see checkAuth()
114     */
115    var $idled = false;
116
117    /**
118     * Storage object
119     *
120     * @var object
121     * @see Auth(), validateLogin()
122     */
123    var $storage = '';
124
125    /**
126     * User-defined function that creates the login screen
127     *
128     * @var string
129     */
130    var $loginFunction = '';
131
132    /**
133     * Should the login form be displayed
134     *
135     * @var   bool
136     * @see   setShowlogin()
137     */
138    var $showLogin = true;
139   
140    /**
141      * Is Login Allowed from this page
142      *
143      * @var  bool
144      * @see setAllowLogin
145      */
146    var $allowLogin = true;
147
148    /**
149     * Current authentication status
150     *
151     * @var string
152     */
153    var $status = '';
154
155    /**
156     * Username
157     *
158     * @var string
159     */
160    var $username = '';
161
162    /**
163     * Password
164     *
165     * @var string
166     */
167    var $password = '';
168
169    /**
170     * checkAuth callback function name
171     *
172     * @var string
173     * @see setCheckAuthCallback()
174     */
175    var $checkAuthCallback = '';
176
177    /**
178     * Login callback function name
179     *
180     * @var string
181     * @see setLoginCallback()
182     */
183    var $loginCallback = '';
184
185    /**
186     * Failed Login callback function name
187     *
188     * @var string
189     * @see setFailedLoginCallback()
190     */
191    var $loginFailedCallback = '';
192
193    /**
194     * Logout callback function name
195     *
196     * @var string
197     * @see setLogoutCallback()
198     */
199    var $logoutCallback = '';
200
201    /**
202     * Auth session-array name
203     *
204     * @var string
205     */
206    var $_sessionName = '_authsession';
207
208    /**
209     * Package Version
210     *
211     * @var string
212     */
213    var $version = "@version@";
214
215    /**
216     * Flag to use advanced security
217     * When set extra checks will be made to see if the
218     * user's IP or useragent have changed across requests.
219     * Turned off by default to preserve BC.
220     *
221     * @var boolean
222     */     
223    var $advancedsecurity = false;
224
225    /**
226     * Username key in POST array
227     *
228     * @var string
229     */
230    var $_postUsername = 'username';
231
232    /**
233     * Password key in POST array
234     *
235     * @var string
236     */
237    var $_postPassword = 'password';
238
239    /**
240     * Holds a reference to the session auth variable
241     * @var array
242     */
243    var $session;
244
245    /**
246     * Holds a reference to the global server variable
247     * @var array
248     */
249    var $server;
250
251    /**
252     * Holds a reference to the global post variable
253     * @var array
254     */
255    var $post;
256
257    /**
258     * Holds a reference to the global cookie variable
259     * @var array
260     */
261    var $cookie;
262
263    /**
264     * A hash to hold various superglobals as reference
265     * @var array
266     */
267    var $authdata;
268   
269    /**
270      * How many times has checkAuth been called
271      * @var int
272      */
273    var $authChecks = 0;
274
275    /**
276     * PEAR::Log object
277     *
278     * @var object Log
279     */
280    var $logger = null;
281
282    /**
283     * Whether to enable logging of behaviour
284     *
285     * @var boolean
286     */
287    var $enableLogging = false;
288
289    // }}}
290    // {{{ Auth() [constructor]
291
292    /**
293     * Constructor
294     *
295     * Set up the storage driver.
296     *
297     * @param string    Type of the storage driver
298     * @param mixed     Additional options for the storage driver
299     *                  (example: if you are using DB as the storage
300     *                   driver, you have to pass the dsn string here)
301     *
302     * @param string    Name of the function that creates the login form
303     * @param boolean   Should the login form be displayed if neccessary?
304     * @return void
305     */
306    function Auth($storageDriver, $options = '', $loginFunction = '', $showLogin = true)
307    {
308        $this->applyAuthOptions($options);
309
310        // Start the session suppress error if already started
311        if(!session_id()){
312            @session_start();
313            if(!session_id()) {
314                // Throw error
315                include_once 'PEAR.php';
316                PEAR::throwError('Session could not be started by Auth, '
317                        .'possibly headers are already sent, try putting '
318                        .'ob_start in the beginning of your script');
319            }
320        }
321
322        // Make Sure Auth session variable is there
323        if(!isset($_SESSION[$this->_sessionName])) {
324            $_SESSION[$this->_sessionName] = array();
325        }
326
327        // Assign Some globals to internal references, this will replace _importGlobalVariable
328        $this->session =& $_SESSION[$this->_sessionName];
329        $this->server =& $_SERVER;
330        $this->post =& $_POST;
331        $this->cookie =& $_COOKIE;
332
333        if ($loginFunction != '' && is_callable($loginFunction)) {
334            $this->loginFunction = $loginFunction;
335        }
336
337        if (is_bool($showLogin)) {
338            $this->showLogin = $showLogin;
339        }
340
341        if (is_object($storageDriver)) {
342            $this->storage =& $storageDriver;
343            // Pass a reference to auth to the container, ugly but works
344            // this is used by the DB container to use method setAuthData not staticaly.
345            $this->storage->_auth_obj =& $this;
346        } else {
347            // $this->storage = $this->_factory($storageDriver, $options);
348            //
349            $this->storage_driver = $storageDriver;
350            $this->storage_options =& $options;
351        }
352    }
353
354    // }}}
355    // {{{ applyAuthOptions()
356
357    /**
358      * Set the Auth options
359      *
360      * Some options which are Auth specific will be applied
361      * the rest will be left for usage by the container
362      *
363      * @param array    An array of Auth options
364      * @return array   The options which were not applied
365      * @access private
366      */
367    function &applyAuthOptions(&$options)
368    {
369        if(is_array($options)){
370            if (!empty($options['sessionName'])) {
371                $this->_sessionName = $options['sessionName'];
372                unset($options['sessionName']);
373            }
374            if (isset($options['allowLogin'])) {
375                $this->allowLogin = $options['allowLogin'];
376                unset($options['allowLogin']);
377            }
378            if (!empty($options['postUsername'])) {
379                $this->_postUsername = $options['postUsername'];
380                unset($options['postUsername']);
381            }
382            if (!empty($options['postPassword'])) {
383                $this->_postPassword = $options['postPassword'];
384                unset($options['postPassword']);
385            }
386            if (isset($options['advancedsecurity'])) {
387                $this->advancedsecurity = $options['advancedsecurity'];
388                unset($options['advancedsecurity']);
389            }
390            if (isset($options['enableLogging'])) {
391                $this->enableLogging = $options['enableLogging'];
392                unset($options['enableLogging']);
393            }
394        }
395        return($options);
396    }
397
398    // }}}
399    // {{{ _loadStorage()
400   
401    /**
402      * Load Storage Driver if not already loaded
403      *
404      * Suspend storage instantiation to make Auth lighter to use
405      * for calls which do not require login
406      *
407      * @return bool    True if the conainer is loaded, false if the container
408      *                 is already loaded
409      * @access private
410      */
411    function _loadStorage()
412    {
413        if(!is_object($this->storage)) {
414            $this->storage =& $this->_factory($this->storage_driver,
415                    $this->storage_options);
416            $this->storage->_auth_obj =& $this;
417            $this->log('Loaded storage container ('.$this->storage_driver.')', AUTH_LOG_DEBUG);
418            return(true);
419        }
420        return(false);
421    }
422
423    // }}}
424    // {{{ _factory()
425
426    /**
427     * Return a storage driver based on $driver and $options
428     *
429     * @static
430     * @param  string $driver  Type of storage class to return
431     * @param  string $options Optional parameters for the storage class
432     * @return object Object   Storage object
433     * @access private
434     */
435    function &_factory($driver, $options = '')
436    {
437        $storage_class = 'Auth_Container_' . $driver;
438        include_once 'Auth/Container/' . $driver . '.php';
439        $obj =& new $storage_class($options);
440        return $obj;
441    }
442
443    // }}}
444    // {{{ assignData()
445
446    /**
447     * Assign data from login form to internal values
448     *
449     * This function takes the values for username and password
450     * from $HTTP_POST_VARS/$_POST and assigns them to internal variables.
451     * If you wish to use another source apart from $HTTP_POST_VARS/$_POST,
452     * you have to derive this function.
453     *
454     * @global $HTTP_POST_VARS, $_POST
455     * @see    Auth
456     * @return void
457     * @access private
458     */
459    function assignData()
460    {
461        $this->log('Auth::assignData() called.', AUTH_LOG_DEBUG);
462
463        if (   isset($this->post[$this->_postUsername])
464            && $this->post[$this->_postUsername] != '') {
465            $this->username = (get_magic_quotes_gpc() == 1
466                    ? stripslashes($this->post[$this->_postUsername])
467                    : $this->post[$this->_postUsername]);
468        }
469        if (   isset($this->post[$this->_postPassword])
470            && $this->post[$this->_postPassword] != '') {
471            $this->password = (get_magic_quotes_gpc() == 1
472                    ? stripslashes($this->post[$this->_postPassword])
473                    : $this->post[$this->_postPassword] );
474        }
475    }
476
477    // }}}
478    // {{{ start()
479
480    /**
481     * Start new auth session
482     *
483     * @return void
484     * @access public
485     */
486    function start()
487    {
488        $this->log('Auth::start() called.', AUTH_LOG_DEBUG);
489
490        $this->assignData();
491        if (!$this->checkAuth() && $this->allowLogin) {
492            $this->login();
493        }
494    }
495
496    // }}}
497    // {{{ login()
498
499    /**
500     * Login function
501     *
502     * @return void
503     * @access private
504     */
505    function login()
506    {
507        $this->log('Auth::login() called.', AUTH_LOG_DEBUG);
508
509        $login_ok = false;
510        $this->_loadStorage();
511       
512        // Check if using challenge response
513        (isset($this->post['authsecret']) && $this->post['authsecret'] == 1)
514            ? $usingChap = true
515            : $usingChap = false;
516
517       
518        // When the user has already entered a username, we have to validate it.
519        if (!empty($this->username)) {
520            if (true === $this->storage->fetchData($this->username, $this->password, $usingChap)) {
521                $this->session['challengekey'] = md5($this->username.$this->password);
522                $login_ok = true;
523                $this->log('Successful login.', AUTH_LOG_INFO);
524            }
525        }
526
527        if (!empty($this->username) && $login_ok) {
528            $this->setAuth($this->username);
529            if (is_callable($this->loginCallback)) {
530                $this->log('Calling loginCallback ('.$this->loginCallback.').', AUTH_LOG_DEBUG);
531                call_user_func_array($this->loginCallback, array($this->username, &$this));
532            }
533        }
534
535        // If the login failed or the user entered no username,
536        // output the login screen again.
537        if (!empty($this->username) && !$login_ok) {
538            $this->log('Incorrect login.', AUTH_LOG_INFO);
539            $this->status = AUTH_WRONG_LOGIN;
540            if (is_callable($this->loginFailedCallback)) {
541                $this->log('Calling loginFailedCallback ('.$this->loginFailedCallback.').', AUTH_LOG_DEBUG);
542                call_user_func_array($this->loginFailedCallback, array($this->username, &$this));
543            }
544        }
545
546        if ((empty($this->username) || !$login_ok) && $this->showLogin) {
547            $this->log('Rendering Login Form.', AUTH_LOG_INFO);
548            if (is_callable($this->loginFunction)) {
549                $this->log('Calling loginFunction ('.$this->loginFunction.').', AUTH_LOG_DEBUG);
550                call_user_func_array($this->loginFunction, array($this->username, $this->status, &$this));
551            } else {
552                // BC fix Auth used to use drawLogin for this
553                // call is sub classes implement this
554                if (is_callable(array($this, 'drawLogin'))) {
555                    $this->log('Calling Auth::drawLogin()', AUTH_LOG_DEBUG);
556                    return $this->drawLogin($this->username, $this);
557                }
558
559                $this->log('Using default Auth_Frontend_Html', AUTH_LOG_DEBUG);
560
561                // New Login form
562                include_once 'Auth/Frontend/Html.php';
563                return Auth_Frontend_Html::render($this, $this->username);
564            }
565        } else {
566            return;
567        }
568    }
569
570    // }}}
571    // {{{ setExpire()
572
573    /**
574     * Set the maximum expire time
575     *
576     * @param  integer time in seconds
577     * @param  bool    add time to current expire time or not
578     * @return void
579     * @access public
580     */
581    function setExpire($time, $add = false)
582    {
583        $add ? $this->expire += $time : $this->expire = $time;
584    }
585
586    // }}}
587    // {{{ setIdle()
588
589    /**
590     * Set the maximum idle time
591     *
592     * @param  integer time in seconds
593     * @param  bool    add time to current maximum idle time or not
594     * @return void
595     * @access public
596     */
597    function setIdle($time, $add = false)
598    {
599        $add ? $this->idle += $time : $this->idle = $time;
600    }
601
602    // }}}
603    // {{{ setSessionName()
604
605    /**
606     * Set name of the session to a customized value.
607     *
608     * If you are using multiple instances of PEAR::Auth
609     * on the same domain, you can change the name of
610     * session per application via this function.
611     * This will chnage the name of the session variable
612     * auth uses to store it's data in the session
613     *
614     * @param  string New name for the session
615     * @return void
616     * @access public
617     */
618    function setSessionName($name = 'session')
619    {
620        $this->_sessionName = '_auth_'.$name;
621        $this->session =& $_SESSION[$this->_sessionName];
622    }
623
624    // }}}
625    // {{{ setShowLogin()
626
627    /**
628     * Should the login form be displayed if neccessary?
629     *
630     * @param  bool    show login form or not
631     * @return void
632     * @access public
633     */
634    function setShowLogin($showLogin = true)
635    {
636        $this->showLogin = $showLogin;
637    }
638
639    // }}}
640    // {{{ setAllowLogin()
641
642    /**
643     * Should the login form be displayed if neccessary?
644     *
645     * @param  bool    show login form or not
646     * @return void
647     * @access public
648     */
649    function setAllowLogin($allowLogin = true)
650    {
651        $this->allowLogin = $allowLogin;
652    }
653
654    // }}}
655    // {{{ setCheckAuthCallback()
656
657    /**
658     * Register a callback function to be called whenever the validity of the login is checked
659     * The function will receive two parameters, the username and a reference to the auth object.
660     *
661     * @param  string  callback function name
662     * @return void
663     * @access public
664     * @since Method available since Release 1.4.3
665     */
666    function setCheckAuthCallback($checkAuthCallback)
667    {
668        $this->checkAuthCallback = $checkAuthCallback;
669    }
670
671    // }}}
672    // {{{ setLoginCallback()
673   
674    /**
675     * Register a callback function to be called on user login.
676     * The function will receive two parameters, the username and a reference to the auth object.
677     *
678     * @param  string  callback function name
679     * @return void
680     * @see    setLogoutCallback()
681     * @access public
682     */
683    function setLoginCallback($loginCallback)
684    {
685        $this->loginCallback = $loginCallback;
686    }
687
688    // }}}
689    // {{{ setFailedLoginCallback()
690
691    /**
692     * Register a callback function to be called on failed user login.
693     * The function will receive two parameters, the username and a reference to the auth object.
694     *
695     * @param  string  callback function name
696     * @return void
697     * @access public
698     */
699    function setFailedLoginCallback($loginFailedCallback)
700    {
701        $this->loginFailedCallback = $loginFailedCallback;
702    }
703
704    // }}}
705    // {{{ setLogoutCallback()
706
707    /**
708     * Register a callback function to be called on user logout.
709     * The function will receive three parameters, the username and a reference to the auth object.
710     *
711     * @param  string  callback function name
712     * @return void
713     * @see    setLoginCallback()
714     * @access public
715     */
716    function setLogoutCallback($logoutCallback)
717    {
718        $this->logoutCallback = $logoutCallback;
719    }
720
721    // }}}
722    // {{{ setAuthData()
723
724    /**
725     * Register additional information that is to be stored
726     * in the session.
727     *
728     * @param  string  Name of the data field
729     * @param  mixed   Value of the data field
730     * @param  boolean Should existing data be overwritten? (default
731     *                 is true)
732     * @return void
733     * @access public
734     */
735    function setAuthData($name, $value, $overwrite = true)
736    {
737        if (!empty($this->session['data'][$name]) && $overwrite == false) {
738            return;
739        }
740        $this->session['data'][$name] = $value;
741    }
742
743    // }}}
744    // {{{ getAuthData()
745
746    /**
747     * Get additional information that is stored in the session.
748     *
749     * If no value for the first parameter is passed, the method will
750     * return all data that is currently stored.
751     *
752     * @param  string Name of the data field
753     * @return mixed  Value of the data field.
754     * @access public
755     */
756    function getAuthData($name = null)
757    {
758        if (!isset($this->session['data'])) {
759            return null;
760        }   
761        if(!isset($name)) {
762            return $this->session['data'];
763        }
764        if (isset($name) && isset($this->session['data'][$name])) {
765            return $this->session['data'][$name];
766        }
767        return null;       
768    }
769
770    // }}}
771    // {{{ setAuth()
772
773    /**
774     * Register variable in a session telling that the user
775     * has logged in successfully
776     *
777     * @param  string Username
778     * @return void
779     * @access public
780     */
781    function setAuth($username)
782    {
783        $this->log('Auth::setAuth() called.', AUTH_LOG_DEBUG);
784   
785        // #2021 - Change the session id to avoid session fixation attacks php 4.3.3 >
786        session_regenerate_id(true);
787
788        if (!isset($this->session) || !is_array($this->session)) {
789            $this->session = array();
790        }
791
792        if (!isset($this->session['data'])) {
793            $this->session['data'] = array();
794        }
795
796        $this->session['sessionip'] = isset($this->server['REMOTE_ADDR'])
797            ? $this->server['REMOTE_ADDR']
798            : '';
799        $this->session['sessionuseragent'] = isset($this->server['HTTP_USER_AGENT'])
800            ? $this->server['HTTP_USER_AGENT']
801            : '';
802        $this->session['sessionforwardedfor'] = isset($this->server['HTTP_X_FORWARDED_FOR'])
803            ? $this->server['HTTP_X_FORWARDED_FOR']
804            : '';
805
806        // This should be set by the container to something more safe
807        // Like md5(passwd.microtime)
808        if(empty($this->session['challengekey'])) {
809            $this->session['challengekey'] = md5($username.microtime());
810        }
811
812        $this->session['challengecookie'] = md5($this->session['challengekey'].microtime());
813        setcookie('authchallenge', $this->session['challengecookie']);
814
815        $this->session['registered'] = true;
816        $this->session['username']   = $username;
817        $this->session['timestamp']  = time();
818        $this->session['idle']       = time();
819    }
820
821    // }}}
822    // {{{ setAdvancedSecurity()
823   
824    /**
825      * Enables advanced security checks
826      *
827      * Currently only ip change and useragent change
828      * are detected
829      * @todo Add challenge cookies - Create a cookie which changes every time
830      *       and contains some challenge key which the server can verify with
831      *       a session var cookie might need to be crypted (user pass)
832      * @param bool Enable or disable
833      * @return void
834      * @access public
835      */
836    function setAdvancedSecurity($flag=true)
837    {
838        $this->advancedsecurity = $flag;
839    }
840
841    // }}}
842    // {{{ checkAuth()
843
844    /**
845     * Checks if there is a session with valid auth information.
846     *
847     * @access public
848     * @return boolean  Whether or not the user is authenticated.
849     */
850    function checkAuth()
851    {
852        $this->log('Auth::checkAuth() called.', AUTH_LOG_DEBUG);
853        $this->authChecks++;
854        if (isset($this->session)) {
855            // Check if authentication session is expired
856            if (   $this->expire > 0
857                && isset($this->session['timestamp'])
858                && ($this->session['timestamp'] + $this->expire) < time()) {
859                $this->log('Session Expired', AUTH_LOG_INFO);
860                $this->expired = true;
861                $this->status = AUTH_EXPIRED;
862                $this->logout();
863                return false;
864            }
865
866            // Check if maximum idle time is reached
867            if (   $this->idle > 0
868                && isset($this->session['idle'])
869                && ($this->session['idle'] + $this->idle) < time()) {
870                $this->log('Session Idle Time Reached', AUTH_LOG_INFO);
871                $this->idled = true;
872                $this->status = AUTH_IDLED;
873                $this->logout();
874                return false;
875            }
876
877            if (   isset($this->session['registered'])
878                && isset($this->session['username'])
879                && $this->session['registered'] == true
880                && $this->session['username'] != '') {
881                Auth::updateIdle();
882
883                if ($this->advancedsecurity) {
884                    $this->log('Advanced Security Mode Enabled.', AUTH_LOG_DEBUG);
885                   
886                    // Only Generate the challenge once
887                    if($this->authChecks == 1) {
888                        $this->log('Generating new Challenge Cookie.', AUTH_LOG_DEBUG);
889                        $this->session['challengecookieold'] = $this->session['challengecookie'];
890                        $this->session['challengecookie'] = md5($this->session['challengekey'].microtime());
891                        setcookie('authchallenge', $this->session['challengecookie']);
892                    }
893                   
894                    // Check for ip change
895                    if (   isset($this->server['REMOTE_ADDR'])
896                        && $this->session['sessionip'] != $this->server['REMOTE_ADDR']) {
897                        $this->log('Security Breach. Remote IP Address changed.', AUTH_LOG_INFO);
898                        // Check if the IP of the user has changed, if so we
899                        // assume a man in the middle attack and log him out
900                        $this->expired = true;
901                        $this->status = AUTH_SECURITY_BREACH;
902                        $this->logout();
903                        return false;
904                    }
905
906                    // Check for ip change (if connected via proxy)
907                    if (   isset($this->server['HTTP_X_FORWARDED_FOR'])
908                        && $this->session['sessionforwardedfor'] != $this->server['HTTP_X_FORWARDED_FOR']) {
909                        $this->log('Security Breach. Forwarded For IP Address changed.', AUTH_LOG_INFO);
910                        // Check if the IP of the user connecting via proxy has
911                        // changed, if so we assume a man in the middle attack
912                        // and log him out.
913                        $this->expired = true;
914                        $this->status = AUTH_SECURITY_BREACH;
915                        $this->logout();
916                        return false;
917                    }
918                   
919                    // Check for useragent change
920                    if (   isset($this->server['HTTP_USER_AGENT'])
921                        && $this->session['sessionuseragent'] != $this->server['HTTP_USER_AGENT']) {
922                        $this->log('Security Breach. User Agent changed.', AUTH_LOG_INFO);
923                        // Check if the User-Agent of the user has changed, if
924                        // so we assume a man in the middle attack and log him out
925                        $this->expired = true;
926                        $this->status = AUTH_SECURITY_BREACH;
927                        $this->logout();
928                        return false;
929                    }
930   
931                    // Check challenge cookie here, if challengecookieold is not set
932                    // this is the first time and check is skipped
933                    // TODO when user open two pages similtaneuly (open in new window,open
934                    // in tab) auth breach is caused find out a way around that if possible
935                    if (   isset($this->session['challengecookieold'])
936                        && $this->session['challengecookieold'] != $this->cookie['authchallenge']) {
937                        $this->log('Security Breach. Challenge Cookie mismatch.', AUTH_LOG_INFO);
938                        $this->expired = true;
939                        $this->status = AUTH_SECURITY_BREACH;
940                        $this->logout();
941                        $this->login();
942                        return false;
943                    }
944                }
945
946                if (is_callable($this->checkAuthCallback)) {
947                    $this->log('Calling checkAuthCallback ('.$this->checkAuthCallback.').', AUTH_LOG_DEBUG);
948                    $checkCallback = call_user_func_array($this->checkAuthCallback, array($this->username, &$this));
949                    if ($checkCallback == false) {
950                        $this->log('checkAuthCallback failed.', AUTH_LOG_INFO);
951                        $this->expired = true;
952                        $this->status = AUTH_CALLBACK_ABORT;
953                        $this->logout();
954                        return false;
955                    }
956                }
957
958                $this->log('Session OK.', AUTH_LOG_INFO);
959                return true;
960            }
961        }
962        $this->log('Unable to locate session storage.', AUTH_LOG_DEBUG);
963        return false;
964    }
965
966    // }}}
967    // {{{ staticCheckAuth() [static]
968
969    /**
970     * Statically checks if there is a session with valid auth information.
971     *
972     * @access public
973     * @see checkAuth
974     * @return boolean  Whether or not the user is authenticated.
975     * @static
976     */
977    function staticCheckAuth($options = null)
978    {
979        static $staticAuth;
980        if(!isset($staticAuth)) {
981            $staticAuth = new Auth('null', $options);
982        }
983        $staticAuth->log('Auth::staticCheckAuth() called', AUTH_LOG_DEBUG);
984        return $staticAuth->checkAuth();
985    }
986
987    // }}}
988    // {{{ getAuth()
989
990    /**
991     * Has the user been authenticated?
992     *
993     * @access public
994     * @return bool  True if the user is logged in, otherwise false.
995     */
996    function getAuth()
997    {
998        $this->log('Auth::getAuth() called.', AUTH_LOG_DEBUG);
999        return $this->checkAuth();
1000    }
1001
1002    // }}}
1003    // {{{ logout()
1004
1005    /**
1006     * Logout function
1007     *
1008     * This function clears any auth tokens in the currently
1009     * active session and executes the logout callback function,
1010     * if any
1011     *
1012     * @access public
1013     * @return void
1014     */
1015    function logout()
1016    {
1017        $this->log('Auth::logout() called.', AUTH_LOG_DEBUG);
1018
1019        if (is_callable($this->logoutCallback)) {
1020            $this->log('Calling logoutCallback ('.$this->logoutCallback.').', AUTH_LOG_DEBUG);
1021            call_user_func_array($this->logoutCallback, array($this->session['username'], &$this));
1022        }
1023
1024        $this->username = '';
1025        $this->password = '';
1026       
1027        $this->session = null;
1028    }
1029
1030    // }}}
1031    // {{{ updateIdle()
1032
1033    /**
1034     * Update the idletime
1035     *
1036     * @access private
1037     * @return void
1038     */
1039    function updateIdle()
1040    {
1041        $this->session['idle'] = time();
1042    }
1043
1044    // }}}
1045    // {{{ getUsername()
1046
1047    /**
1048     * Get the username
1049     *
1050     * @return string
1051     * @access public
1052     */
1053    function getUsername()
1054    {
1055        if (isset($this->session['username'])) {
1056            return($this->session['username']);
1057        }
1058        return('');
1059    }
1060
1061    // }}}
1062    // {{{ getStatus()
1063
1064    /**
1065     * Get the current status
1066     *
1067     * @return string
1068     * @access public
1069     */
1070    function getStatus()
1071    {
1072        return $this->status;
1073    }
1074
1075    // }}}
1076    // {{{ getPostUsernameField()
1077   
1078    /**
1079     * Gets the post varible used for the username
1080     *
1081     * @return string
1082     * @access public
1083     */
1084    function getPostUsernameField()
1085    {
1086        return($this->_postUsername);
1087    }
1088
1089    // }}}
1090    // {{{ getPostPasswordField()
1091
1092    /**
1093     * Gets the post varible used for the username
1094     *
1095     * @return string
1096     * @access public
1097     */
1098    function getPostPasswordField()
1099    {
1100        return($this->_postPassword);
1101    }
1102
1103    // }}}
1104    // {{{ sessionValidThru()
1105
1106    /**
1107     * Returns the time up to the session is valid
1108     *
1109     * @access public
1110     * @return integer
1111     */
1112    function sessionValidThru()
1113    {
1114        if (!isset($this->session['idle'])) {
1115            return 0;
1116        }
1117        if ($this->idle == 0) {
1118            return 0;
1119        }
1120        return ($this->session['idle'] + $this->idle);
1121    }
1122
1123    // }}}
1124    // {{{ listUsers()
1125
1126    /**
1127     * List all users that are currently available in the storage
1128     * container
1129     *
1130     * @access public
1131     * @return array
1132     */
1133    function listUsers()
1134    {
1135        $this->log('Auth::listUsers() called.', AUTH_LOG_DEBUG);
1136        $this->_loadStorage();
1137        return $this->storage->listUsers();
1138    }
1139
1140    // }}}
1141    // {{{ addUser()
1142
1143    /**
1144     * Add user to the storage container
1145     *
1146     * @access public
1147     * @param  string Username
1148     * @param  string Password
1149     * @param  mixed  Additional parameters
1150     * @return mixed  True on success, PEAR error object on error
1151     *                and AUTH_METHOD_NOT_SUPPORTED otherwise.
1152     */
1153    function addUser($username, $password, $additional = '')
1154    {
1155        $this->log('Auth::addUser() called.', AUTH_LOG_DEBUG);
1156        $this->_loadStorage();
1157        return $this->storage->addUser($username, $password, $additional);
1158    }
1159
1160    // }}}
1161    // {{{ removeUser()
1162
1163    /**
1164     * Remove user from the storage container
1165     *
1166     * @access public
1167     * @param  string Username
1168     * @return mixed  True on success, PEAR error object on error
1169     *                and AUTH_METHOD_NOT_SUPPORTED otherwise.
1170     */
1171    function removeUser($username)
1172    {
1173        $this->log('Auth::removeUser() called.', AUTH_LOG_DEBUG);
1174        $this->_loadStorage();
1175        return $this->storage->removeUser($username);
1176    }
1177
1178    // }}}
1179    // {{{ changePassword()
1180
1181    /**
1182     * Change password for user in the storage container
1183     *
1184     * @access public
1185     * @param string Username
1186     * @param string The new password
1187     * @return mixed True on success, PEAR error object on error
1188     *               and AUTH_METHOD_NOT_SUPPORTED otherwise.
1189     */
1190    function changePassword($username, $password)
1191    {
1192        $this->log('Auth::changePassword() called', AUTH_LOG_DEBUG);
1193        $this->_loadStorage();
1194        return $this->storage->changePassword($username, $password);
1195    }
1196
1197    // }}}
1198    // {{{ log()
1199
1200    /**
1201     * Log a message from the Auth system
1202     *
1203     * @access public
1204     * @param string The message to log
1205     * @param string The log level to log the message under. See the Log documentation for more info.
1206     * @return boolean
1207     */
1208    function log($message, $level = AUTH_LOG_DEBUG)
1209    {
1210        if (!$this->enableLogging) return false;
1211
1212        $this->_loadLogger();
1213
1214        $this->logger->log('AUTH: '.$message, $level);
1215    }
1216
1217    // }}}
1218    // {{{ _loadLogger()
1219
1220    /**
1221      * Load Log object if not already loaded
1222      *
1223      * Suspend logger instantiation to make Auth lighter to use
1224      * for calls which do not require logging
1225      *
1226      * @return bool    True if the logger is loaded, false if the logger
1227      *                 is already loaded
1228      * @access private
1229      */
1230    function _loadLogger()
1231    {
1232        if(is_null($this->logger)) {
1233            if (!class_exists('Log')) {
1234                include_once 'Log.php';
1235            }
1236            $this->logger =& Log::singleton('null',
1237                    null,
1238                    'auth['.getmypid().']',
1239                    array(),
1240                    AUTH_LOG_DEBUG);
1241            return(true);
1242        }
1243        return(false);
1244    }
1245
1246    // }}}
1247    // {{{ attachLogObserver()
1248
1249    /**
1250     * Attach an Observer to the Auth Log Source
1251     *
1252     * @param object Log_Observer A Log Observer instance
1253     * @return boolean
1254     */
1255    function attachLogObserver(&$observer) {
1256
1257        $this->_loadLogger();
1258
1259        return $this->logger->attach($observer);
1260
1261    }
1262
1263    // }}}
1264
1265}
1266?>
Note: See TracBrowser for help on using the repository browser.