Subversion Repositories Applications.papyrus

Rev

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

Rev Author Line No. Line
1712 jp_milcent 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.2.4.2 2007-11-19 14:54:06 jp_milcent 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.2.4.2 $
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
        if (!isset($this->session['data'])) {
779
            return null;
780
        }
781
        if(!isset($name)) {
782
            return $this->session['data'];
783
        }
784
        if (isset($name) && isset($this->session['data'][$name])) {
785
            return $this->session['data'][$name];
786
        }
787
        return null;
788
    }
789
 
790
    // }}}
791
    // {{{ setAuth()
792
 
793
    /**
794
     * Register variable in a session telling that the user
795
     * has logged in successfully
796
     *
797
     * @param  string Username
798
     * @return void
799
     * @access public
800
     */
801
    function setAuth($username)
802
    {
803
        $this->log('Auth::setAuth() called.', AUTH_LOG_DEBUG);
804
 
805
        // #10729 - Regenerate session id here only if generating at login only
806
        //          Don't do it if we are regenerating on every request so we don't
807
        //          regenerate it twice in one request.
808
        if (!$this->regenerateSessionId) {
809
            // #2021 - Change the session id to avoid session fixation attacks php 4.3.3 >
810
            session_regenerate_id(true);
811
        }
812
 
813
        if (!isset($this->session) || !is_array($this->session)) {
814
            $this->session = array();
815
        }
816
 
817
        if (!isset($this->session['data'])) {
818
            $this->session['data'] = array();
819
        }
820
 
821
        $this->session['sessionip'] = isset($this->server['REMOTE_ADDR'])
822
            ? $this->server['REMOTE_ADDR']
823
            : '';
824
        $this->session['sessionuseragent'] = isset($this->server['HTTP_USER_AGENT'])
825
            ? $this->server['HTTP_USER_AGENT']
826
            : '';
827
        $this->session['sessionforwardedfor'] = isset($this->server['HTTP_X_FORWARDED_FOR'])
828
            ? $this->server['HTTP_X_FORWARDED_FOR']
829
            : '';
830
 
831
        // This should be set by the container to something more safe
832
        // Like md5(passwd.microtime)
833
        if(empty($this->session['challengekey'])) {
834
            $this->session['challengekey'] = md5($username.microtime());
835
        }
836
 
837
        $this->session['challengecookie'] = md5($this->session['challengekey'].microtime());
838
        setcookie('authchallenge', $this->session['challengecookie']);
839
 
840
        $this->session['registered'] = true;
841
        $this->session['username']   = $username;
842
        $this->session['timestamp']  = time();
843
        $this->session['idle']       = time();
844
    }
845
 
846
    // }}}
847
    // {{{ setAdvancedSecurity()
848
 
849
    /**
850
      * Enables advanced security checks
851
      *
852
      * Currently only ip change and useragent change
853
      * are detected
854
      * @todo Add challenge cookies - Create a cookie which changes every time
855
      *       and contains some challenge key which the server can verify with
856
      *       a session var cookie might need to be crypted (user pass)
857
      * @param bool Enable or disable
858
      * @return void
859
      * @access public
860
      */
861
    function setAdvancedSecurity($flag=true)
862
    {
863
        $this->advancedsecurity = $flag;
864
    }
865
 
866
    // }}}
867
    // {{{ checkAuth()
868
 
869
    /**
870
     * Checks if there is a session with valid auth information.
871
     *
872
     * @access public
873
     * @return boolean  Whether or not the user is authenticated.
874
     */
875
    function checkAuth()
876
    {
877
        $this->log('Auth::checkAuth() called.', AUTH_LOG_DEBUG);
878
        $this->authChecks++;
879
        if (isset($this->session)) {
880
            // Check if authentication session is expired
881
            if (   $this->expire > 0
882
                && isset($this->session['timestamp'])
883
                && ($this->session['timestamp'] + $this->expire) < time()) {
884
                $this->log('Session Expired', AUTH_LOG_INFO);
885
                $this->expired = true;
886
                $this->status = AUTH_EXPIRED;
887
                $this->logout();
888
                return false;
889
            }
890
 
891
            // Check if maximum idle time is reached
892
            if (   $this->idle > 0
893
                && isset($this->session['idle'])
894
                && ($this->session['idle'] + $this->idle) < time()) {
895
                $this->log('Session Idle Time Reached', AUTH_LOG_INFO);
896
                $this->idled = true;
897
                $this->status = AUTH_IDLED;
898
                $this->logout();
899
                return false;
900
            }
901
 
902
            if (   isset($this->session['registered'])
903
                && isset($this->session['username'])
904
                && $this->session['registered'] == true
905
                && $this->session['username'] != '') {
906
                Auth::updateIdle();
907
 
908
                if ($this->advancedsecurity) {
909
                    $this->log('Advanced Security Mode Enabled.', AUTH_LOG_DEBUG);
910
 
911
                    // Only Generate the challenge once
912
                    if($this->authChecks == 1) {
913
                        $this->log('Generating new Challenge Cookie.', AUTH_LOG_DEBUG);
914
                        $this->session['challengecookieold'] = $this->session['challengecookie'];
915
                        $this->session['challengecookie'] = md5($this->session['challengekey'].microtime());
916
                        setcookie('authchallenge', $this->session['challengecookie']);
917
                    }
918
 
919
                    // Check for ip change
920
                    if (   isset($this->server['REMOTE_ADDR'])
921
                        && $this->session['sessionip'] != $this->server['REMOTE_ADDR']) {
922
                        $this->log('Security Breach. Remote IP Address changed.', AUTH_LOG_INFO);
923
                        // Check if the IP of the user has changed, if so we
924
                        // 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 for ip change (if connected via proxy)
932
                    if (   isset($this->server['HTTP_X_FORWARDED_FOR'])
933
                        && $this->session['sessionforwardedfor'] != $this->server['HTTP_X_FORWARDED_FOR']) {
934
                        $this->log('Security Breach. Forwarded For IP Address changed.', AUTH_LOG_INFO);
935
                        // Check if the IP of the user connecting via proxy has
936
                        // changed, if so we assume a man in the middle attack
937
                        // and log him out.
938
                        $this->expired = true;
939
                        $this->status = AUTH_SECURITY_BREACH;
940
                        $this->logout();
941
                        return false;
942
                    }
943
 
944
                    // Check for useragent change
945
                    if (   isset($this->server['HTTP_USER_AGENT'])
946
                        && $this->session['sessionuseragent'] != $this->server['HTTP_USER_AGENT']) {
947
                        $this->log('Security Breach. User Agent changed.', AUTH_LOG_INFO);
948
                        // Check if the User-Agent of the user has changed, if
949
                        // so we assume a man in the middle attack and log him out
950
                        $this->expired = true;
951
                        $this->status = AUTH_SECURITY_BREACH;
952
                        $this->logout();
953
                        return false;
954
                    }
955
 
956
                    // Check challenge cookie here, if challengecookieold is not set
957
                    // this is the first time and check is skipped
958
                    // TODO when user open two pages similtaneuly (open in new window,open
959
                    // in tab) auth breach is caused find out a way around that if possible
960
                    if (   isset($this->session['challengecookieold'])
961
                        && $this->session['challengecookieold'] != $this->cookie['authchallenge']) {
962
                        $this->log('Security Breach. Challenge Cookie mismatch.', AUTH_LOG_INFO);
963
                        $this->expired = true;
964
                        $this->status = AUTH_SECURITY_BREACH;
965
                        $this->logout();
966
                        $this->login();
967
                        return false;
968
                    }
969
                }
970
 
971
                if (is_callable($this->checkAuthCallback)) {
972
                    $this->log('Calling checkAuthCallback ('.$this->checkAuthCallback.').', AUTH_LOG_DEBUG);
973
                    $checkCallback = call_user_func_array($this->checkAuthCallback, array($this->username, &$this));
974
                    if ($checkCallback == false) {
975
                        $this->log('checkAuthCallback failed.', AUTH_LOG_INFO);
976
                        $this->expired = true;
977
                        $this->status = AUTH_CALLBACK_ABORT;
978
                        $this->logout();
979
                        return false;
980
                    }
981
                }
982
 
983
                $this->log('Session OK.', AUTH_LOG_INFO);
984
                return true;
985
            }
986
        }
987
        $this->log('Unable to locate session storage.', AUTH_LOG_DEBUG);
988
        return false;
989
    }
990
 
991
    // }}}
992
    // {{{ staticCheckAuth() [static]
993
 
994
    /**
995
     * Statically checks if there is a session with valid auth information.
996
     *
997
     * @access public
998
     * @see checkAuth
999
     * @return boolean  Whether or not the user is authenticated.
1000
     * @static
1001
     */
1002
    function staticCheckAuth($options = null)
1003
    {
1004
        static $staticAuth;
1005
        if(!isset($staticAuth)) {
1006
            $staticAuth = new Auth('null', $options);
1007
        }
1008
        $staticAuth->log('Auth::staticCheckAuth() called', AUTH_LOG_DEBUG);
1009
        return $staticAuth->checkAuth();
1010
    }
1011
 
1012
    // }}}
1013
    // {{{ getAuth()
1014
 
1015
    /**
1016
     * Has the user been authenticated?
1017
     *
1018
     * @access public
1019
     * @return bool  True if the user is logged in, otherwise false.
1020
     */
1021
    function getAuth()
1022
    {
1023
        $this->log('Auth::getAuth() called.', AUTH_LOG_DEBUG);
1024
        return $this->checkAuth();
1025
    }
1026
 
1027
    // }}}
1028
    // {{{ logout()
1029
 
1030
    /**
1031
     * Logout function
1032
     *
1033
     * This function clears any auth tokens in the currently
1034
     * active session and executes the logout callback function,
1035
     * if any
1036
     *
1037
     * @access public
1038
     * @return void
1039
     */
1040
    function logout()
1041
    {
1042
        $this->log('Auth::logout() called.', AUTH_LOG_DEBUG);
1043
 
1044
        if (is_callable($this->logoutCallback) && isset($this->session['username'])) {
1045
            $this->log('Calling logoutCallback ('.$this->logoutCallback.').', AUTH_LOG_DEBUG);
1046
            call_user_func_array($this->logoutCallback, array($this->session['username'], &$this));
1047
        }
1048
 
1049
        $this->username = '';
1050
        $this->password = '';
1051
 
1052
        $this->session = null;
1053
    }
1054
 
1055
    // }}}
1056
    // {{{ updateIdle()
1057
 
1058
    /**
1059
     * Update the idletime
1060
     *
1061
     * @access private
1062
     * @return void
1063
     */
1064
    function updateIdle()
1065
    {
1066
        $this->session['idle'] = time();
1067
    }
1068
 
1069
    // }}}
1070
    // {{{ getUsername()
1071
 
1072
    /**
1073
     * Get the username
1074
     *
1075
     * @return string
1076
     * @access public
1077
     */
1078
    function getUsername()
1079
    {
1080
        if (isset($this->session['username'])) {
1081
            return($this->session['username']);
1082
        }
1083
        return('');
1084
    }
1085
 
1086
    // }}}
1087
    // {{{ getStatus()
1088
 
1089
    /**
1090
     * Get the current status
1091
     *
1092
     * @return string
1093
     * @access public
1094
     */
1095
    function getStatus()
1096
    {
1097
        return $this->status;
1098
    }
1099
 
1100
    // }}}
1101
    // {{{ getPostUsernameField()
1102
 
1103
    /**
1104
     * Gets the post varible used for the username
1105
     *
1106
     * @return string
1107
     * @access public
1108
     */
1109
    function getPostUsernameField()
1110
    {
1111
        return($this->_postUsername);
1112
    }
1113
 
1114
    // }}}
1115
    // {{{ getPostPasswordField()
1116
 
1117
    /**
1118
     * Gets the post varible used for the username
1119
     *
1120
     * @return string
1121
     * @access public
1122
     */
1123
    function getPostPasswordField()
1124
    {
1125
        return($this->_postPassword);
1126
    }
1127
 
1128
    // }}}
1129
    // {{{ sessionValidThru()
1130
 
1131
    /**
1132
     * Returns the time up to the session is valid
1133
     *
1134
     * @access public
1135
     * @return integer
1136
     */
1137
    function sessionValidThru()
1138
    {
1139
        if (!isset($this->session['idle'])) {
1140
            return 0;
1141
        }
1142
        if ($this->idle == 0) {
1143
            return 0;
1144
        }
1145
        return ($this->session['idle'] + $this->idle);
1146
    }
1147
 
1148
    // }}}
1149
    // {{{ listUsers()
1150
 
1151
    /**
1152
     * List all users that are currently available in the storage
1153
     * container
1154
     *
1155
     * @access public
1156
     * @return array
1157
     */
1158
    function listUsers()
1159
    {
1160
        $this->log('Auth::listUsers() called.', AUTH_LOG_DEBUG);
1161
        $this->_loadStorage();
1162
        return $this->storage->listUsers();
1163
    }
1164
 
1165
    // }}}
1166
    // {{{ addUser()
1167
 
1168
    /**
1169
     * Add user to the storage container
1170
     *
1171
     * @access public
1172
     * @param  string Username
1173
     * @param  string Password
1174
     * @param  mixed  Additional parameters
1175
     * @return mixed  True on success, PEAR error object on error
1176
     *                and AUTH_METHOD_NOT_SUPPORTED otherwise.
1177
     */
1178
    function addUser($username, $password, $additional = '')
1179
    {
1180
        $this->log('Auth::addUser() called.', AUTH_LOG_DEBUG);
1181
        $this->_loadStorage();
1182
        return $this->storage->addUser($username, $password, $additional);
1183
    }
1184
 
1185
    // }}}
1186
    // {{{ removeUser()
1187
 
1188
    /**
1189
     * Remove user from the storage container
1190
     *
1191
     * @access public
1192
     * @param  string Username
1193
     * @return mixed  True on success, PEAR error object on error
1194
     *                and AUTH_METHOD_NOT_SUPPORTED otherwise.
1195
     */
1196
    function removeUser($username)
1197
    {
1198
        $this->log('Auth::removeUser() called.', AUTH_LOG_DEBUG);
1199
        $this->_loadStorage();
1200
        return $this->storage->removeUser($username);
1201
    }
1202
 
1203
    // }}}
1204
    // {{{ changePassword()
1205
 
1206
    /**
1207
     * Change password for user in the storage container
1208
     *
1209
     * @access public
1210
     * @param string Username
1211
     * @param string The new password
1212
     * @return mixed True on success, PEAR error object on error
1213
     *               and AUTH_METHOD_NOT_SUPPORTED otherwise.
1214
     */
1215
    function changePassword($username, $password)
1216
    {
1217
        $this->log('Auth::changePassword() called', AUTH_LOG_DEBUG);
1218
        $this->_loadStorage();
1219
        return $this->storage->changePassword($username, $password);
1220
    }
1221
 
1222
    // }}}
1223
    // {{{ log()
1224
 
1225
    /**
1226
     * Log a message from the Auth system
1227
     *
1228
     * @access public
1229
     * @param string The message to log
1230
     * @param string The log level to log the message under. See the Log documentation for more info.
1231
     * @return boolean
1232
     */
1233
    function log($message, $level = AUTH_LOG_DEBUG)
1234
    {
1235
        if (!$this->enableLogging) return false;
1236
 
1237
        $this->_loadLogger();
1238
 
1239
        $this->logger->log('AUTH: '.$message, $level);
1240
    }
1241
 
1242
    // }}}
1243
    // {{{ _loadLogger()
1244
 
1245
    /**
1246
      * Load Log object if not already loaded
1247
      *
1248
      * Suspend logger instantiation to make Auth lighter to use
1249
      * for calls which do not require logging
1250
      *
1251
      * @return bool    True if the logger is loaded, false if the logger
1252
      *                 is already loaded
1253
      * @access private
1254
      */
1255
    function _loadLogger()
1256
    {
1257
        if(is_null($this->logger)) {
1258
            if (!class_exists('Log')) {
1259
                include_once 'Log.php';
1260
            }
1261
            $this->logger =& Log::singleton('null',
1262
                    null,
1263
                    'auth['.getmypid().']',
1264
                    array(),
1265
                    AUTH_LOG_DEBUG);
1266
            return(true);
1267
        }
1268
        return(false);
1269
    }
1270
 
1271
    // }}}
1272
    // {{{ attachLogObserver()
1273
 
1274
    /**
1275
     * Attach an Observer to the Auth Log Source
1276
     *
1277
     * @param object Log_Observer A Log Observer instance
1278
     * @return boolean
1279
     */
1280
    function attachLogObserver(&$observer) {
1281
 
1282
        $this->_loadLogger();
1283
 
1284
        return $this->logger->attach($observer);
1285
 
1286
    }
1287
 
1288
    // }}}
1289
 
1290
}
1291
?>