Subversion Repositories Applications.papyrus

Rev

Rev 1713 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2150 mathias 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.119 2007/07/02 03:38:52 aashley Exp $
22
 * @link       http://pear.php.net/package/Auth
23
 */
24
 
25
/**
26
 * Returned if session exceeds idle time
27
 */
28
define('AUTH_IDLED',                    -1);
29
/**
30
 * Returned if session has expired
31
 */
32
define('AUTH_EXPIRED',                  -2);
33
/**
34
 * Returned if container is unable to authenticate user/password pair
35
 */
36
define('AUTH_WRONG_LOGIN',              -3);
37
/**
38
 * Returned if a container method is not supported.
39
 */
40
define('AUTH_METHOD_NOT_SUPPORTED',     -4);
41
/**
42
 * Returned if new Advanced security system detects a breach
43
 */
44
define('AUTH_SECURITY_BREACH',          -5);
45
/**
46
 * Returned if checkAuthCallback says session should not continue.
47
 */
48
define('AUTH_CALLBACK_ABORT',           -6);
49
 
50
/**
51
 * Auth Log level - INFO
52
 */
53
define('AUTH_LOG_INFO',     6);
54
/**
55
 * Auth Log level - DEBUG
56
 */
57
define('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.4  File: $Revision: 1.119 $
73
 * @link       http://pear.php.net/package/Auth
74
 */
75
class 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
     * Whether to regenerate session id everytime start is called
291
     *
292
     * @var boolean
293
     */
294
    var $regenerateSessionId = false;
295
 
296
    // }}}
297
    // {{{ Auth() [constructor]
298
 
299
    /**
300
     * Constructor
301
     *
302
     * Set up the storage driver.
303
     *
304
     * @param string    Type of the storage driver
305
     * @param mixed     Additional options for the storage driver
306
     *                  (example: if you are using DB as the storage
307
     *                   driver, you have to pass the dsn string here)
308
     *
309
     * @param string    Name of the function that creates the login form
310
     * @param boolean   Should the login form be displayed if neccessary?
311
     * @return void
312
     */
313
    function Auth($storageDriver, $options = '', $loginFunction = '', $showLogin = true)
314
    {
315
        $this->applyAuthOptions($options);
316
 
317
        // Start the session suppress error if already started
318
        if(!session_id()){
319
            @session_start();
320
            if(!session_id()) {
321
                // Throw error
322
                include_once 'PEAR.php';
323
                PEAR::throwError('Session could not be started by Auth, '
324
                        .'possibly headers are already sent, try putting '
325
                        .'ob_start in the beginning of your script');
326
            }
327
        }
328
 
329
        // Make Sure Auth session variable is there
330
        if(!isset($_SESSION[$this->_sessionName])) {
331
            $_SESSION[$this->_sessionName] = array();
332
        }
333
 
334
        // Assign Some globals to internal references, this will replace _importGlobalVariable
335
        $this->session =& $_SESSION[$this->_sessionName];
336
        $this->server =& $_SERVER;
337
        $this->post =& $_POST;
338
        $this->cookie =& $_COOKIE;
339
 
340
        if ($loginFunction != '' && is_callable($loginFunction)) {
341
            $this->loginFunction = $loginFunction;
342
        }
343
 
344
        if (is_bool($showLogin)) {
345
            $this->showLogin = $showLogin;
346
        }
347
 
348
        if (is_object($storageDriver)) {
349
            $this->storage =& $storageDriver;
350
            // Pass a reference to auth to the container, ugly but works
351
            // this is used by the DB container to use method setAuthData not staticaly.
352
            $this->storage->_auth_obj =& $this;
353
        } else {
354
            // $this->storage = $this->_factory($storageDriver, $options);
355
            //
356
            $this->storage_driver = $storageDriver;
357
            $this->storage_options =& $options;
358
        }
359
    }
360
 
361
    // }}}
362
    // {{{ applyAuthOptions()
363
 
364
    /**
365
      * Set the Auth options
366
      *
367
      * Some options which are Auth specific will be applied
368
      * the rest will be left for usage by the container
369
      *
370
      * @param array    An array of Auth options
371
      * @return array   The options which were not applied
372
      * @access private
373
      */
374
    function &applyAuthOptions(&$options)
375
    {
376
        if(is_array($options)){
377
            if (!empty($options['sessionName'])) {
378
                $this->_sessionName = $options['sessionName'];
379
                unset($options['sessionName']);
380
            }
381
            if (isset($options['allowLogin'])) {
382
                $this->allowLogin = $options['allowLogin'];
383
                unset($options['allowLogin']);
384
            }
385
            if (!empty($options['postUsername'])) {
386
                $this->_postUsername = $options['postUsername'];
387
                unset($options['postUsername']);
388
            }
389
            if (!empty($options['postPassword'])) {
390
                $this->_postPassword = $options['postPassword'];
391
                unset($options['postPassword']);
392
            }
393
            if (isset($options['advancedsecurity'])) {
394
                $this->advancedsecurity = $options['advancedsecurity'];
395
                unset($options['advancedsecurity']);
396
            }
397
            if (isset($options['enableLogging'])) {
398
                $this->enableLogging = $options['enableLogging'];
399
                unset($options['enableLogging']);
400
            }
401
            if (isset($options['regenerateSessionId']) && is_bool($options['regenerateSessionId'])) {
402
                $this->regenerateSessionId = $options['regenerateSessionId'];
403
            }
404
        }
405
        return($options);
406
    }
407
 
408
    // }}}
409
    // {{{ _loadStorage()
410
 
411
    /**
412
      * Load Storage Driver if not already loaded
413
      *
414
      * Suspend storage instantiation to make Auth lighter to use
415
      * for calls which do not require login
416
      *
417
      * @return bool    True if the conainer is loaded, false if the container
418
      *                 is already loaded
419
      * @access private
420
      */
421
    function _loadStorage()
422
    {
423
        if(!is_object($this->storage)) {
424
            $this->storage =& $this->_factory($this->storage_driver,
425
                    $this->storage_options);
426
            $this->storage->_auth_obj =& $this;
427
            $this->log('Loaded storage container ('.$this->storage_driver.')', AUTH_LOG_DEBUG);
428
            return(true);
429
        }
430
        return(false);
431
    }
432
 
433
    // }}}
434
    // {{{ _factory()
435
 
436
    /**
437
     * Return a storage driver based on $driver and $options
438
     *
439
     * @static
440
     * @param  string $driver  Type of storage class to return
441
     * @param  string $options Optional parameters for the storage class
442
     * @return object Object   Storage object
443
     * @access private
444
     */
445
    function &_factory($driver, $options = '')
446
    {
447
        $storage_class = 'Auth_Container_' . $driver;
448
        include_once 'Auth/Container/' . $driver . '.php';
449
        $obj =& new $storage_class($options);
450
        return $obj;
451
    }
452
 
453
    // }}}
454
    // {{{ assignData()
455
 
456
    /**
457
     * Assign data from login form to internal values
458
     *
459
     * This function takes the values for username and password
460
     * from $HTTP_POST_VARS/$_POST and assigns them to internal variables.
461
     * If you wish to use another source apart from $HTTP_POST_VARS/$_POST,
462
     * you have to derive this function.
463
     *
464
     * @global $HTTP_POST_VARS, $_POST
465
     * @see    Auth
466
     * @return void
467
     * @access private
468
     */
469
    function assignData()
470
    {
471
        $this->log('Auth::assignData() called.', AUTH_LOG_DEBUG);
472
 
473
        if (   isset($this->post[$this->_postUsername])
474
            && $this->post[$this->_postUsername] != '') {
475
            $this->username = (get_magic_quotes_gpc() == 1
476
                    ? stripslashes($this->post[$this->_postUsername])
477
                    : $this->post[$this->_postUsername]);
478
        }
479
        if (   isset($this->post[$this->_postPassword])
480
            && $this->post[$this->_postPassword] != '') {
481
            $this->password = (get_magic_quotes_gpc() == 1
482
                    ? stripslashes($this->post[$this->_postPassword])
483
                    : $this->post[$this->_postPassword] );
484
        }
485
    }
486
 
487
    // }}}
488
    // {{{ start()
489
 
490
    /**
491
     * Start new auth session
492
     *
493
     * @return void
494
     * @access public
495
     */
496
    function start()
497
    {
498
        $this->log('Auth::start() called.', AUTH_LOG_DEBUG);
499
 
500
        // #10729 - Regenerate session id here if we are generating it on every
501
        //          page load.
502
        if ($this->regenerateSessionId) {
503
            session_regenerate_id(true);
504
        }
505
 
506
        $this->assignData();
507
        if (!$this->checkAuth() && $this->allowLogin) {
508
            $this->login();
509
        }
510
    }
511
 
512
    // }}}
513
    // {{{ login()
514
 
515
    /**
516
     * Login function
517
     *
518
     * @return void
519
     * @access private
520
     */
521
    function login()
522
    {
523
        $this->log('Auth::login() called.', AUTH_LOG_DEBUG);
524
 
525
        $login_ok = false;
526
        $this->_loadStorage();
527
 
528
        // Check if using challenge response
529
        (isset($this->post['authsecret']) && $this->post['authsecret'] == 1)
530
            ? $usingChap = true
531
            : $usingChap = false;
532
 
533
 
534
        // When the user has already entered a username, we have to validate it.
535
        if (!empty($this->username)) {
536
            if (true === $this->storage->fetchData($this->username, $this->password, $usingChap)) {
537
                $this->session['challengekey'] = md5($this->username.$this->password);
538
                $login_ok = true;
539
                $this->log('Successful login.', AUTH_LOG_INFO);
540
            }
541
        }
542
 
543
        if (!empty($this->username) && $login_ok) {
544
            $this->setAuth($this->username);
545
            if (is_callable($this->loginCallback)) {
546
                $this->log('Calling loginCallback ('.$this->loginCallback.').', AUTH_LOG_DEBUG);
547
                call_user_func_array($this->loginCallback, array($this->username, &$this));
548
            }
549
        }
550
 
551
        // If the login failed or the user entered no username,
552
        // output the login screen again.
553
        if (!empty($this->username) && !$login_ok) {
554
            $this->log('Incorrect login.', AUTH_LOG_INFO);
555
            $this->status = AUTH_WRONG_LOGIN;
556
            if (is_callable($this->loginFailedCallback)) {
557
                $this->log('Calling loginFailedCallback ('.$this->loginFailedCallback.').', AUTH_LOG_DEBUG);
558
                call_user_func_array($this->loginFailedCallback, array($this->username, &$this));
559
            }
560
        }
561
 
562
        if ((empty($this->username) || !$login_ok) && $this->showLogin) {
563
            $this->log('Rendering Login Form.', AUTH_LOG_INFO);
564
            if (is_callable($this->loginFunction)) {
565
                $this->log('Calling loginFunction ('.$this->loginFunction.').', AUTH_LOG_DEBUG);
566
                call_user_func_array($this->loginFunction, array($this->username, $this->status, &$this));
567
            } else {
568
                // BC fix Auth used to use drawLogin for this
569
                // call is sub classes implement this
570
                if (is_callable(array($this, 'drawLogin'))) {
571
                    $this->log('Calling Auth::drawLogin()', AUTH_LOG_DEBUG);
572
                    return $this->drawLogin($this->username, $this);
573
                }
574
 
575
                $this->log('Using default Auth_Frontend_Html', AUTH_LOG_DEBUG);
576
 
577
                // New Login form
578
                include_once 'Auth/Frontend/Html.php';
579
                return Auth_Frontend_Html::render($this, $this->username);
580
            }
581
        } else {
582
            return;
583
        }
584
    }
585
 
586
    // }}}
587
    // {{{ setExpire()
588
 
589
    /**
590
     * Set the maximum expire time
591
     *
592
     * @param  integer time in seconds
593
     * @param  bool    add time to current expire time or not
594
     * @return void
595
     * @access public
596
     */
597
    function setExpire($time, $add = false)
598
    {
599
        $add ? $this->expire += $time : $this->expire = $time;
600
    }
601
 
602
    // }}}
603
    // {{{ setIdle()
604
 
605
    /**
606
     * Set the maximum idle time
607
     *
608
     * @param  integer time in seconds
609
     * @param  bool    add time to current maximum idle time or not
610
     * @return void
611
     * @access public
612
     */
613
    function setIdle($time, $add = false)
614
    {
615
        $add ? $this->idle += $time : $this->idle = $time;
616
    }
617
 
618
    // }}}
619
    // {{{ setSessionName()
620
 
621
    /**
622
     * Set name of the session to a customized value.
623
     *
624
     * If you are using multiple instances of PEAR::Auth
625
     * on the same domain, you can change the name of
626
     * session per application via this function.
627
     * This will chnage the name of the session variable
628
     * auth uses to store it's data in the session
629
     *
630
     * @param  string New name for the session
631
     * @return void
632
     * @access public
633
     */
634
    function setSessionName($name = 'session')
635
    {
636
        $this->_sessionName = '_auth_'.$name;
637
        // Make Sure Auth session variable is there
638
        if(!isset($_SESSION[$this->_sessionName])) {
639
            $_SESSION[$this->_sessionName] = array();
640
        }
641
        $this->session =& $_SESSION[$this->_sessionName];
642
    }
643
 
644
    // }}}
645
    // {{{ setShowLogin()
646
 
647
    /**
648
     * Should the login form be displayed if neccessary?
649
     *
650
     * @param  bool    show login form or not
651
     * @return void
652
     * @access public
653
     */
654
    function setShowLogin($showLogin = true)
655
    {
656
        $this->showLogin = $showLogin;
657
    }
658
 
659
    // }}}
660
    // {{{ setAllowLogin()
661
 
662
    /**
663
     * Should the login form be displayed if neccessary?
664
     *
665
     * @param  bool    show login form or not
666
     * @return void
667
     * @access public
668
     */
669
    function setAllowLogin($allowLogin = true)
670
    {
671
        $this->allowLogin = $allowLogin;
672
    }
673
 
674
    // }}}
675
    // {{{ setCheckAuthCallback()
676
 
677
    /**
678
     * Register a callback function to be called whenever the validity of the login is checked
679
     * The function will receive two parameters, the username and a reference to the auth object.
680
     *
681
     * @param  string  callback function name
682
     * @return void
683
     * @access public
684
     * @since Method available since Release 1.4.3
685
     */
686
    function setCheckAuthCallback($checkAuthCallback)
687
    {
688
        $this->checkAuthCallback = $checkAuthCallback;
689
    }
690
 
691
    // }}}
692
    // {{{ setLoginCallback()
693
 
694
    /**
695
     * Register a callback function to be called on user login.
696
     * The function will receive two parameters, the username and a reference to the auth object.
697
     *
698
     * @param  string  callback function name
699
     * @return void
700
     * @see    setLogoutCallback()
701
     * @access public
702
     */
703
    function setLoginCallback($loginCallback)
704
    {
705
        $this->loginCallback = $loginCallback;
706
    }
707
 
708
    // }}}
709
    // {{{ setFailedLoginCallback()
710
 
711
    /**
712
     * Register a callback function to be called on failed user login.
713
     * The function will receive two parameters, the username and a reference to the auth object.
714
     *
715
     * @param  string  callback function name
716
     * @return void
717
     * @access public
718
     */
719
    function setFailedLoginCallback($loginFailedCallback)
720
    {
721
        $this->loginFailedCallback = $loginFailedCallback;
722
    }
723
 
724
    // }}}
725
    // {{{ setLogoutCallback()
726
 
727
    /**
728
     * Register a callback function to be called on user logout.
729
     * The function will receive three parameters, the username and a reference to the auth object.
730
     *
731
     * @param  string  callback function name
732
     * @return void
733
     * @see    setLoginCallback()
734
     * @access public
735
     */
736
    function setLogoutCallback($logoutCallback)
737
    {
738
        $this->logoutCallback = $logoutCallback;
739
    }
740
 
741
    // }}}
742
    // {{{ setAuthData()
743
 
744
    /**
745
     * Register additional information that is to be stored
746
     * in the session.
747
     *
748
     * @param  string  Name of the data field
749
     * @param  mixed   Value of the data field
750
     * @param  boolean Should existing data be overwritten? (default
751
     *                 is true)
752
     * @return void
753
     * @access public
754
     */
755
    function setAuthData($name, $value, $overwrite = true)
756
    {
757
        if (!empty($this->session['data'][$name]) && $overwrite == false) {
758
            return;
759
        }
760
        $this->session['data'][$name] = $value;
761
    }
762
 
763
    // }}}
764
    // {{{ getAuthData()
765
 
766
    /**
767
     * Get additional information that is stored in the session.
768
     *
769
     * If no value for the first parameter is passed, the method will
770
     * return all data that is currently stored.
771
     *
772
     * @param  string Name of the data field
773
     * @return mixed  Value of the data field.
774
     * @access public
775
     */
776
    function getAuthData($name = null)
777
    {
778
 
779
        if (!isset($this->session['data'])) {
780
            return null;
781
        }
782
        if(!isset($name)) {
783
            return $this->session['data'];
784
        }
785
        if (isset($name) && isset($this->session['data'][$name])) {
786
            return $this->session['data'][$name];
787
        }
788
        return null;
789
    }
790
 
791
    // }}}
792
    // {{{ setAuth()
793
 
794
    /**
795
     * Register variable in a session telling that the user
796
     * has logged in successfully
797
     *
798
     * @param  string Username
799
     * @return void
800
     * @access public
801
     */
802
    function setAuth($username)
803
    {
804
        $this->log('Auth::setAuth() called.', AUTH_LOG_DEBUG);
805
 
806
        // #10729 - Regenerate session id here only if generating at login only
807
        //          Don't do it if we are regenerating on every request so we don't
808
        //          regenerate it twice in one request.
809
        if (!$this->regenerateSessionId) {
810
            // #2021 - Change the session id to avoid session fixation attacks php 4.3.3 >
811
            session_regenerate_id(true);
812
        }
813
 
814
        if (!isset($this->session) || !is_array($this->session)) {
815
            $this->session = array();
816
        }
817
 
818
        if (!isset($this->session['data'])) {
819
            $this->session['data'] = array();
820
        }
821
 
822
        $this->session['sessionip'] = isset($this->server['REMOTE_ADDR'])
823
            ? $this->server['REMOTE_ADDR']
824
            : '';
825
        $this->session['sessionuseragent'] = isset($this->server['HTTP_USER_AGENT'])
826
            ? $this->server['HTTP_USER_AGENT']
827
            : '';
828
        $this->session['sessionforwardedfor'] = isset($this->server['HTTP_X_FORWARDED_FOR'])
829
            ? $this->server['HTTP_X_FORWARDED_FOR']
830
            : '';
831
 
832
        // This should be set by the container to something more safe
833
        // Like md5(passwd.microtime)
834
        if(empty($this->session['challengekey'])) {
835
            $this->session['challengekey'] = md5($username.microtime());
836
        }
837
 
838
        $this->session['challengecookie'] = md5($this->session['challengekey'].microtime());
839
        setcookie('authchallenge', $this->session['challengecookie']);
840
 
841
        $this->session['registered'] = true;
842
        $this->session['username']   = $username;
843
        $this->session['timestamp']  = time();
844
        $this->session['idle']       = time();
845
    }
846
 
847
    // }}}
848
    // {{{ setAdvancedSecurity()
849
 
850
    /**
851
      * Enables advanced security checks
852
      *
853
      * Currently only ip change and useragent change
854
      * are detected
855
      * @todo Add challenge cookies - Create a cookie which changes every time
856
      *       and contains some challenge key which the server can verify with
857
      *       a session var cookie might need to be crypted (user pass)
858
      * @param bool Enable or disable
859
      * @return void
860
      * @access public
861
      */
862
    function setAdvancedSecurity($flag=true)
863
    {
864
        $this->advancedsecurity = $flag;
865
    }
866
 
867
    // }}}
868
    // {{{ checkAuth()
869
 
870
    /**
871
     * Checks if there is a session with valid auth information.
872
     *
873
     * @access public
874
     * @return boolean  Whether or not the user is authenticated.
875
     */
876
    function checkAuth()
877
    {
878
        $this->log('Auth::checkAuth() called.', AUTH_LOG_DEBUG);
879
        $this->authChecks++;
880
        if (isset($this->session)) {
881
            // Check if authentication session is expired
882
            if (   $this->expire > 0
883
                && isset($this->session['timestamp'])
884
                && ($this->session['timestamp'] + $this->expire) < time()) {
885
                $this->log('Session Expired', AUTH_LOG_INFO);
886
                $this->expired = true;
887
                $this->status = AUTH_EXPIRED;
888
                $this->logout();
889
                return false;
890
            }
891
 
892
            // Check if maximum idle time is reached
893
            if (   $this->idle > 0
894
                && isset($this->session['idle'])
895
                && ($this->session['idle'] + $this->idle) < time()) {
896
                $this->log('Session Idle Time Reached', AUTH_LOG_INFO);
897
                $this->idled = true;
898
                $this->status = AUTH_IDLED;
899
                $this->logout();
900
                return false;
901
            }
902
 
903
            if (   isset($this->session['registered'])
904
                && isset($this->session['username'])
905
                && $this->session['registered'] == true
906
                && $this->session['username'] != '') {
907
                Auth::updateIdle();
908
 
909
                if ($this->advancedsecurity) {
910
                    $this->log('Advanced Security Mode Enabled.', AUTH_LOG_DEBUG);
911
 
912
                    // Only Generate the challenge once
913
                    if($this->authChecks == 1) {
914
                        $this->log('Generating new Challenge Cookie.', AUTH_LOG_DEBUG);
915
                        $this->session['challengecookieold'] = $this->session['challengecookie'];
916
                        $this->session['challengecookie'] = md5($this->session['challengekey'].microtime());
917
                        setcookie('authchallenge', $this->session['challengecookie']);
918
                    }
919
 
920
                    // Check for ip change
921
                    if (   isset($this->server['REMOTE_ADDR'])
922
                        && $this->session['sessionip'] != $this->server['REMOTE_ADDR']) {
923
                        $this->log('Security Breach. Remote IP Address changed.', AUTH_LOG_INFO);
924
                        // Check if the IP of the user has changed, if so we
925
                        // assume a man in the middle attack and log him out
926
                        $this->expired = true;
927
                        $this->status = AUTH_SECURITY_BREACH;
928
                        $this->logout();
929
                        return false;
930
                    }
931
 
932
                    // Check for ip change (if connected via proxy)
933
                    if (   isset($this->server['HTTP_X_FORWARDED_FOR'])
934
                        && $this->session['sessionforwardedfor'] != $this->server['HTTP_X_FORWARDED_FOR']) {
935
                        $this->log('Security Breach. Forwarded For IP Address changed.', AUTH_LOG_INFO);
936
                        // Check if the IP of the user connecting via proxy has
937
                        // changed, if so we assume a man in the middle attack
938
                        // and log him out.
939
                        $this->expired = true;
940
                        $this->status = AUTH_SECURITY_BREACH;
941
                        $this->logout();
942
                        return false;
943
                    }
944
 
945
                    // Check for useragent change
946
                    if (   isset($this->server['HTTP_USER_AGENT'])
947
                        && $this->session['sessionuseragent'] != $this->server['HTTP_USER_AGENT']) {
948
                        $this->log('Security Breach. User Agent changed.', AUTH_LOG_INFO);
949
                        // Check if the User-Agent of the user has changed, if
950
                        // so we assume a man in the middle attack and log him out
951
                        $this->expired = true;
952
                        $this->status = AUTH_SECURITY_BREACH;
953
                        $this->logout();
954
                        return false;
955
                    }
956
 
957
                    // Check challenge cookie here, if challengecookieold is not set
958
                    // this is the first time and check is skipped
959
                    // TODO when user open two pages similtaneuly (open in new window,open
960
                    // in tab) auth breach is caused find out a way around that if possible
961
                    if (   isset($this->session['challengecookieold'])
962
                        && $this->session['challengecookieold'] != $this->cookie['authchallenge']) {
963
                        $this->log('Security Breach. Challenge Cookie mismatch.', AUTH_LOG_INFO);
964
                        $this->expired = true;
965
                        $this->status = AUTH_SECURITY_BREACH;
966
                        $this->logout();
967
                        $this->login();
968
                        return false;
969
                    }
970
                }
971
 
972
                if (is_callable($this->checkAuthCallback)) {
973
                    $this->log('Calling checkAuthCallback ('.$this->checkAuthCallback.').', AUTH_LOG_DEBUG);
974
                    $checkCallback = call_user_func_array($this->checkAuthCallback, array($this->username, &$this));
975
                    if ($checkCallback == false) {
976
                        $this->log('checkAuthCallback failed.', AUTH_LOG_INFO);
977
                        $this->expired = true;
978
                        $this->status = AUTH_CALLBACK_ABORT;
979
                        $this->logout();
980
                        return false;
981
                    }
982
                }
983
 
984
                $this->log('Session OK.', AUTH_LOG_INFO);
985
                return true;
986
            }
987
        }
988
        $this->log('Unable to locate session storage.', AUTH_LOG_DEBUG);
989
        return false;
990
    }
991
 
992
    // }}}
993
    // {{{ staticCheckAuth() [static]
994
 
995
    /**
996
     * Statically checks if there is a session with valid auth information.
997
     *
998
     * @access public
999
     * @see checkAuth
1000
     * @return boolean  Whether or not the user is authenticated.
1001
     * @static
1002
     */
1003
    function staticCheckAuth($options = null)
1004
    {
1005
        static $staticAuth;
1006
        if(!isset($staticAuth)) {
1007
            $staticAuth = new Auth('null', $options);
1008
        }
1009
        $staticAuth->log('Auth::staticCheckAuth() called', AUTH_LOG_DEBUG);
1010
        return $staticAuth->checkAuth();
1011
    }
1012
 
1013
    // }}}
1014
    // {{{ getAuth()
1015
 
1016
    /**
1017
     * Has the user been authenticated?
1018
     *
1019
     * @access public
1020
     * @return bool  True if the user is logged in, otherwise false.
1021
     */
1022
    function getAuth()
1023
    {
1024
        $this->log('Auth::getAuth() called.', AUTH_LOG_DEBUG);
1025
        return $this->checkAuth();
1026
    }
1027
 
1028
    // }}}
1029
    // {{{ logout()
1030
 
1031
    /**
1032
     * Logout function
1033
     *
1034
     * This function clears any auth tokens in the currently
1035
     * active session and executes the logout callback function,
1036
     * if any
1037
     *
1038
     * @access public
1039
     * @return void
1040
     */
1041
    function logout()
1042
    {
1043
        $this->log('Auth::logout() called.', AUTH_LOG_DEBUG);
1044
 
1045
        if (is_callable($this->logoutCallback) && isset($this->session['username'])) {
1046
            $this->log('Calling logoutCallback ('.$this->logoutCallback.').', AUTH_LOG_DEBUG);
1047
            call_user_func_array($this->logoutCallback, array($this->session['username'], &$this));
1048
        }
1049
 
1050
        $this->username = '';
1051
        $this->password = '';
1052
 
1053
        $this->session = null;
1054
    }
1055
 
1056
    // }}}
1057
    // {{{ updateIdle()
1058
 
1059
    /**
1060
     * Update the idletime
1061
     *
1062
     * @access private
1063
     * @return void
1064
     */
1065
    function updateIdle()
1066
    {
1067
        $this->session['idle'] = time();
1068
    }
1069
 
1070
    // }}}
1071
    // {{{ getUsername()
1072
 
1073
    /**
1074
     * Get the username
1075
     *
1076
     * @return string
1077
     * @access public
1078
     */
1079
    function getUsername()
1080
    {
1081
        if (isset($this->session['username'])) {
1082
            return($this->session['username']);
1083
        }
1084
        return('');
1085
    }
1086
 
1087
    // }}}
1088
    // {{{ getStatus()
1089
 
1090
    /**
1091
     * Get the current status
1092
     *
1093
     * @return string
1094
     * @access public
1095
     */
1096
    function getStatus()
1097
    {
1098
        return $this->status;
1099
    }
1100
 
1101
    // }}}
1102
    // {{{ getPostUsernameField()
1103
 
1104
    /**
1105
     * Gets the post varible used for the username
1106
     *
1107
     * @return string
1108
     * @access public
1109
     */
1110
    function getPostUsernameField()
1111
    {
1112
        return($this->_postUsername);
1113
    }
1114
 
1115
    // }}}
1116
    // {{{ getPostPasswordField()
1117
 
1118
    /**
1119
     * Gets the post varible used for the username
1120
     *
1121
     * @return string
1122
     * @access public
1123
     */
1124
    function getPostPasswordField()
1125
    {
1126
        return($this->_postPassword);
1127
    }
1128
 
1129
    // }}}
1130
    // {{{ sessionValidThru()
1131
 
1132
    /**
1133
     * Returns the time up to the session is valid
1134
     *
1135
     * @access public
1136
     * @return integer
1137
     */
1138
    function sessionValidThru()
1139
    {
1140
        if (!isset($this->session['idle'])) {
1141
            return 0;
1142
        }
1143
        if ($this->idle == 0) {
1144
            return 0;
1145
        }
1146
        return ($this->session['idle'] + $this->idle);
1147
    }
1148
 
1149
    // }}}
1150
    // {{{ listUsers()
1151
 
1152
    /**
1153
     * List all users that are currently available in the storage
1154
     * container
1155
     *
1156
     * @access public
1157
     * @return array
1158
     */
1159
    function listUsers()
1160
    {
1161
        $this->log('Auth::listUsers() called.', AUTH_LOG_DEBUG);
1162
        $this->_loadStorage();
1163
        return $this->storage->listUsers();
1164
    }
1165
 
1166
    // }}}
1167
    // {{{ addUser()
1168
 
1169
    /**
1170
     * Add user to the storage container
1171
     *
1172
     * @access public
1173
     * @param  string Username
1174
     * @param  string Password
1175
     * @param  mixed  Additional parameters
1176
     * @return mixed  True on success, PEAR error object on error
1177
     *                and AUTH_METHOD_NOT_SUPPORTED otherwise.
1178
     */
1179
    function addUser($username, $password, $additional = '')
1180
    {
1181
        $this->log('Auth::addUser() called.', AUTH_LOG_DEBUG);
1182
        $this->_loadStorage();
1183
        return $this->storage->addUser($username, $password, $additional);
1184
    }
1185
 
1186
    // }}}
1187
    // {{{ removeUser()
1188
 
1189
    /**
1190
     * Remove user from the storage container
1191
     *
1192
     * @access public
1193
     * @param  string Username
1194
     * @return mixed  True on success, PEAR error object on error
1195
     *                and AUTH_METHOD_NOT_SUPPORTED otherwise.
1196
     */
1197
    function removeUser($username)
1198
    {
1199
        $this->log('Auth::removeUser() called.', AUTH_LOG_DEBUG);
1200
        $this->_loadStorage();
1201
        return $this->storage->removeUser($username);
1202
    }
1203
 
1204
    // }}}
1205
    // {{{ changePassword()
1206
 
1207
    /**
1208
     * Change password for user in the storage container
1209
     *
1210
     * @access public
1211
     * @param string Username
1212
     * @param string The new password
1213
     * @return mixed True on success, PEAR error object on error
1214
     *               and AUTH_METHOD_NOT_SUPPORTED otherwise.
1215
     */
1216
    function changePassword($username, $password)
1217
    {
1218
        $this->log('Auth::changePassword() called', AUTH_LOG_DEBUG);
1219
        $this->_loadStorage();
1220
        return $this->storage->changePassword($username, $password);
1221
    }
1222
 
1223
    // }}}
1224
    // {{{ log()
1225
 
1226
    /**
1227
     * Log a message from the Auth system
1228
     *
1229
     * @access public
1230
     * @param string The message to log
1231
     * @param string The log level to log the message under. See the Log documentation for more info.
1232
     * @return boolean
1233
     */
1234
    function log($message, $level = AUTH_LOG_DEBUG)
1235
    {
1236
        if (!$this->enableLogging) return false;
1237
 
1238
        $this->_loadLogger();
1239
 
1240
        $this->logger->log('AUTH: '.$message, $level);
1241
    }
1242
 
1243
    // }}}
1244
    // {{{ _loadLogger()
1245
 
1246
    /**
1247
      * Load Log object if not already loaded
1248
      *
1249
      * Suspend logger instantiation to make Auth lighter to use
1250
      * for calls which do not require logging
1251
      *
1252
      * @return bool    True if the logger is loaded, false if the logger
1253
      *                 is already loaded
1254
      * @access private
1255
      */
1256
    function _loadLogger()
1257
    {
1258
        if(is_null($this->logger)) {
1259
            if (!class_exists('Log')) {
1260
                include_once 'Log.php';
1261
            }
1262
            $this->logger =& Log::singleton('null',
1263
                    null,
1264
                    'auth['.getmypid().']',
1265
                    array(),
1266
                    AUTH_LOG_DEBUG);
1267
            return(true);
1268
        }
1269
        return(false);
1270
    }
1271
 
1272
    // }}}
1273
    // {{{ attachLogObserver()
1274
 
1275
    /**
1276
     * Attach an Observer to the Auth Log Source
1277
     *
1278
     * @param object Log_Observer A Log Observer instance
1279
     * @return boolean
1280
     */
1281
    function attachLogObserver(&$observer) {
1282
 
1283
        $this->_loadLogger();
1284
 
1285
        return $this->logger->attach($observer);
1286
 
1287
    }
1288
 
1289
    // }}}
1290
 
1291
}
1292
?>