Subversion Repositories Sites.obs-saisons.fr

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

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