LiveUser
[ class tree: LiveUser ] [ index: LiveUser ] [ all elements ]

Source for file LiveUser.php

Documentation is available at LiveUser.php

  1. <?php
  2. /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
  3.  
  4. /**
  5.  * A framework for authentication and authorization in PHP applications
  6.  *
  7.  * LiveUser is an authentication/permission framework designed
  8.  * to be flexible and easily extendable.
  9.  *
  10.  * Since it is impossible to have a
  11.  * "one size fits all" it takes a container
  12.  * approach which should enable it to
  13.  * be versatile enough to meet most needs.
  14.  *
  15.  * PHP version 4 and 5
  16.  *
  17.  * LICENSE: This library is free software; you can redistribute it and/or
  18.  * modify it under the terms of the GNU Lesser General Public
  19.  * License as published by the Free Software Foundation; either
  20.  * version 2.1 of the License, or (at your option) any later version.
  21.  *
  22.  * This library is distributed in the hope that it will be useful,
  23.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  24.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  25.  * Lesser General Public License for more details.
  26.  *
  27.  * You should have received a copy of the GNU Lesser General Public
  28.  * License along with this library; if not, write to the Free Software
  29.  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
  30.  * MA  02111-1307  USA
  31.  *
  32.  *
  33.  * @category authentication
  34.  * @package LiveUser
  35.  * @author   Markus Wolff <wolff@21st.de>
  36.  * @author   Helgi Þormar Þorbjörnsson <dufuz@php.net>
  37.  * @author   Lukas Smith <smith@pooteeweet.org>
  38.  * @author   Arnaud Limbourg <arnaud@php.net>
  39.  * @author   Pierre-Alain Joye  <pajoye@php.net>
  40.  * @author   Bjoern Kraus <krausbn@php.net>
  41.  * @copyright 2002-2006 Markus Wolff
  42.  * @license http://www.gnu.org/licenses/lgpl.txt
  43.  * @version CVS: $Id: LiveUser.php,v 1.172 2008/01/26 17:49:15 arnaud Exp $
  44.  * @link http://pear.php.net/LiveUser
  45.  */
  46.  
  47. /**
  48.  * Include PEAR_ErrorStack
  49.  * and Event_Dispatcher classes
  50.  */
  51. require_once 'PEAR.php';
  52. require_once 'PEAR/ErrorStack.php';
  53. require_once 'Event/Dispatcher.php';
  54.  
  55. /**#@+
  56.  * Error related constants definition
  57.  *
  58.  * @var int
  59.  */
  60. define('LIVEUSER_ERROR',                        -1);
  61. define('LIVEUSER_ERROR_NOT_SUPPORTED',          -2);
  62. define('LIVEUSER_ERROR_CONFIG',                 -3);
  63. define('LIVEUSER_ERROR_MISSING_DEPS',           -4);
  64. define('LIVEUSER_ERROR_COOKIE',                 -7);
  65. define('LIVEUSER_ERROR_MISSING_FILE',           -8);
  66. define('LIVEUSER_ERROR_FAILED_INSTANTIATION',   -9);
  67. define('LIVEUSER_ERROR_INIT_ERROR',            -10);
  68. define('LIVEUSER_ERROR_MISSING_CLASS',         -11);
  69. define('LIVEUSER_ERROR_WRONG_CREDENTIALS',     -12);
  70. define('LIVEUSER_ERROR_UNKNOWN_EVENT',         -13);
  71. define('LIVEUSER_ERROR_NOT_CALLABLE',          -14);
  72. define('LIVEUSER_ERROR_SESSION_STARTED',       -15);
  73. /**#@-*/
  74.  
  75. /**#@+
  76.  * Statuses of the current object.
  77.  *
  78.  * @see LiveUser::getStatus
  79.  * @var int
  80.  */
  81. define('LIVEUSER_STATUS_OK',              1);
  82. define('LIVEUSER_STATUS_IDLED',          -1);
  83. define('LIVEUSER_STATUS_EXPIRED',        -2);
  84. define('LIVEUSER_STATUS_ISINACTIVE',     -3);
  85. define('LIVEUSER_STATUS_PERMINITERROR',  -4);
  86. define('LIVEUSER_STATUS_AUTHINITERROR',  -5);
  87. define('LIVEUSER_STATUS_UNKNOWN',        -6);
  88. define('LIVEUSER_STATUS_AUTHNOTFOUND',   -7);
  89. define('LIVEUSER_STATUS_LOGGEDOUT',      -8);
  90. define('LIVEUSER_STATUS_AUTHFAILED',     -9);
  91. define('LIVEUSER_STATUS_UNFROZEN',      -10);
  92. define('LIVEUSER_STATUS_EMPTY_HANDLE',  -11);
  93. /**#@-*/
  94.  
  95. /**
  96.  * The higest possible right level.
  97.  *
  98.  * Levels are only used in the complex container.
  99.  *
  100.  * @var int 
  101.  */
  102. define('LIVEUSER_MAX_LEVEL'3);
  103.  
  104. /**#@+
  105.  * Usertypes
  106.  *
  107.  * @var int
  108.  */
  109. /**
  110.  * lowest user type id
  111.  */
  112. define('LIVEUSER_ANONYMOUS_TYPE_ID',   0);
  113. /**
  114.  * User type id
  115.  * It is the highest user type id
  116.  */
  117. define('LIVEUSER_USER_TYPE_ID',        1);
  118. /**
  119.  * lowest admin type id
  120.  */
  121. define('LIVEUSER_ADMIN_TYPE_ID',       2);
  122. /**
  123.  * look up area admin areas to determine which rights are automatically granted
  124.  */
  125. define('LIVEUSER_AREAADMIN_TYPE_ID',   3);
  126. /**
  127.  * from this admin level on all rights are automatically granted
  128.  */
  129. define('LIVEUSER_SUPERADMIN_TYPE_ID',  4);
  130. /**
  131.  * higest admin type id
  132.  */
  133. define('LIVEUSER_MASTERADMIN_TYPE_ID'5);
  134. /**#@-*/
  135.  
  136. /**#@+
  137.  * Section types
  138.  *
  139.  * @var int
  140.  */
  141. define('LIVEUSER_SECTION_APPLICATION',  1);
  142. define('LIVEUSER_SECTION_AREA',         2);
  143. define('LIVEUSER_SECTION_GROUP',        3);
  144. define('LIVEUSER_SECTION_RIGHT',        4);
  145. /**#@-*/
  146.  
  147. // 60 * 60 * 24 == number of seconds in a day
  148. define('LIVEUSER_DAY_SECONDS'86400);
  149.  
  150. // 60 * 60 * 24 * 365 * 30 == number of seconds between 1970 and about 2000
  151. define('LIVEUSER_COOKIE_DELETE_TIME'946080000);
  152.  
  153. /**
  154.  * This is a manager class for a user login system using the LiveUser
  155.  * class. It creates a LiveUser object, takes care of the whole login
  156.  * process and stores the LiveUser object in a session.
  157.  *
  158.  * You can also configure this class to try to connect to more than
  159.  * one server that can store user information - each server requiring
  160.  * a different backend class.
  161.  *
  162.  * An example would be to create a login
  163.  * system for a live website that first queries the local database and
  164.  * if the requested user is not found, it tries to find it in your
  165.  * company's LDAP server. It means you don't have to create several
  166.  * user accounts for your employees so that they can access closed
  167.  * sections of your website - everyone can use one account.
  168.  *
  169.  * NOTE: No browser output may be made before using this class, because
  170.  * it will try to send HTTP headers such as cookies and redirects.
  171.  *
  172.  * Requirements:
  173.  * - Should run on PHP version 4.2.0 (required for PEAR_Errorstack or higher,
  174.  *   tested only from 4.2.1 onwards
  175.  *
  176.  * Thanks to:
  177.  * Bjoern Schotte, Kristian Koehntopp, Antonio Guerra
  178.  *
  179.  * @category authentication
  180.  * @package  LiveUser
  181.  * @author   Markus Wolff       <wolff@21st.de>
  182.  * @author   Bjoern Kraus       <krausbn@php.net>
  183.  * @author   Lukas Smith        <smith@pooteeweet.org>
  184.  * @author   Pierre-Alain Joye  <pajoye@php.net>
  185.  * @author   Arnaud Limbourg    <arnaud@php.net>
  186.  * @copyright 2002-2006 Markus Wolff
  187.  * @license http://www.gnu.org/licenses/lgpl.txt
  188.  * @version Release: @package_version@
  189.  * @link http://pear.php.net/LiveUser
  190.  */
  191. class LiveUser
  192. {
  193.     /**
  194.      * LiveUser options set in the configuration file.
  195.      *
  196.      * @var     array 
  197.      * @access  private
  198.      */
  199.     var $_options = array(
  200.         'debug' => false,
  201.         'session'  => array(
  202.             'name'    => 'PHPSESSID',
  203.             'varname' => 'ludata',
  204.             'force_start' => true,
  205.         ),
  206.         'session_save_handler'  => false,
  207.         'session_cookie_params' => false,
  208.         'cache_perm' => false,
  209.         'login' => array(
  210.             'force'   => false,
  211.             'regenid' => false
  212.         ),
  213.         'logout' => array(
  214.             'destroy' => true
  215.         )
  216.     );
  217.  
  218.     /**
  219.      * The auth container object.
  220.      *
  221.      * @var    object 
  222.      * @access private
  223.      */
  224.     var $_auth = null;
  225.  
  226.     /**
  227.      * The permission container object.
  228.      *
  229.      * @var    object 
  230.      * @access private
  231.      */
  232.     var $_perm = null;
  233.  
  234.     /**
  235.      * Nested array with the auth containers that shall be queried for user information.
  236.      * Format:
  237.      * <code>
  238.      * array('name' => array("option1" => "value", ....))
  239.      * </code>
  240.      * Typical options are:
  241.      * <ul>
  242.      * - server: The adress of the server being queried (ie. "localhost").
  243.      * - handle: The user name used to login for the server.
  244.      * - password: The password used to login for the server.
  245.      * - database: Name of the database containing user information (this is
  246.      *   usually used only by RDBMS).
  247.      * - baseDN: Obviously, this is what you need when using an LDAP server.
  248.      * - connection: Present only if an existing connection shall be used. This
  249.      *   contains a reference to an already existing connection resource or object.
  250.      * - type: The container type. This option must always be present, otherwise
  251.      *   the LiveUser class cannot include the correct container class definition.
  252.      * - name: The name of the auth container. You can freely define this name,
  253.      *   it can be used from within the permission container to see from which
  254.      *   auth container a specific user is coming from.
  255.      *</ul>
  256.      *
  257.      * @var    array 
  258.      * @access private
  259.      */
  260.     var $_authContainers = array();
  261.  
  262.     /**
  263.      * Array of settings the permission container will use to retrieve
  264.      * the user rights.
  265.      * If set to false, no permission container will be used.
  266.      * If that is the case, all calls to checkRight() will return false.
  267.      * The array element 'type' must be present so the LiveUser class can
  268.      * include the correct class definition (example: "DB_Complex").
  269.      *
  270.      * @var    bool|array
  271.      * @access private
  272.      */
  273.     var $_permContainer = false;
  274.  
  275.     /**
  276.      * Current status of the LiveUser object.
  277.      *
  278.      * @var    string 
  279.      * @access private
  280.      * @see    LIVEUSER_STATUS_* constants
  281.      */
  282.     var $_status = LIVEUSER_STATUS_UNKNOWN;
  283.  
  284.     /**
  285.      * Error stack
  286.      *
  287.      * @var    PEAR_ErrorStack 
  288.      * @access private
  289.      */
  290.     var $stack = null;
  291.  
  292.     /**
  293.      * PEAR::Log object
  294.      * used for error logging by ErrorStack.
  295.      *
  296.      * @var    Log 
  297.      * @access public
  298.      */
  299.     var $log = null;
  300.  
  301.     /**
  302.      * Error codes to message mapping array.
  303.      *
  304.      * @var    array 
  305.      * @access private
  306.      */
  307.     var $_errorMessages = array(
  308.         LIVEUSER_ERROR                        => 'Unknown error',
  309.         LIVEUSER_ERROR_NOT_SUPPORTED          => 'Feature not supported by the container: %feature%',
  310.         LIVEUSER_ERROR_CONFIG                 => 'There is an error in the configuration parameters',
  311.         LIVEUSER_ERROR_MISSING_DEPS           => 'Missing package depedencies: %msg%',
  312.         LIVEUSER_ERROR_COOKIE                 => 'There was an error processing the Remember Me cookie',
  313.         LIVEUSER_ERROR_MISSING_FILE           => 'The file %file% is missing',
  314.         LIVEUSER_ERROR_FAILED_INSTANTIATION   => 'Cannot instantiate class %class%',
  315.         LIVEUSER_ERROR_INIT_ERROR             => 'Container was not initialized properly: %container%',
  316.         LIVEUSER_ERROR_MISSING_CLASS          => 'Class %class% does not exist in file %file%',
  317.         LIVEUSER_ERROR_WRONG_CREDENTIALS      => 'The handle and/or password you submitted are not known',
  318.         LIVEUSER_ERROR_UNKNOWN_EVENT          => 'The event %event% is not known',
  319.         LIVEUSER_ERROR_NOT_CALLABLE           => 'Callback %callback% is not callable',
  320.         LIVEUSER_ERROR_SESSION_STARTED        => 'The session cannot be started because the output already begun (i.e. headers were sent)'
  321.     );
  322.  
  323.     /**
  324.      * Stores the event dispatcher which
  325.      * handles notifications.
  326.      *
  327.      * @var    Event_Dispatcher 
  328.      * @access protected
  329.      */
  330.     var $dispatcher = null;
  331.  
  332.     /**
  333.      * Constructor. Use the factory or singleton methods.
  334.      *
  335.      * @param  bool|object $debug   Boolean that indicates if a log instance
  336.      *                               should be created or an instance of a class
  337.      *                               that implements the PEAR:Log interface.
  338.      * @return void 
  339.      * @access protected
  340.      * @see    LiveUser::factory
  341.      * @see    LiveUser::singleton
  342.      */
  343.     function LiveUser(&$debug)
  344.     {
  345.         $this->stack &PEAR_ErrorStack::singleton('LiveUser');
  346.  
  347.         if ($debug{
  348.             $log =LiveUser::PEARLogFactory($debug);
  349.             if ($log{
  350.                 $this->log =$log;
  351.                 $this->stack->setLogger($this->log);
  352.             }
  353.         }
  354.  
  355.         $this->stack->setErrorMessageTemplate($this->_errorMessages);
  356.  
  357.         $this->dispatcher =Event_Dispatcher::getInstance();
  358.     }
  359.  
  360.     /**
  361.      * Returns an instance of the LiveUser class.
  362.      *
  363.      * This array contains private options defined by
  364.      * the following associative keys:
  365.      *
  366.      * <code>
  367.      *
  368.      * array(
  369.      *  'debug' => false/true or an instance of a class that implements the PEAR::Log interface
  370.      *  'session'  => array(
  371.      *      'name'    => 'liveuser session name',
  372.      *      'varname' => 'liveuser session var name'
  373.      *  ),
  374.      * // The session_save_handler options are optional. If they are specified,
  375.      * // session_set_save_handler() will be called with the parameters
  376.      *  'session_save_handler' => array(
  377.      *      'open'    => 'name of the open function/method',
  378.      *      'close'   => 'name of the close function/method',
  379.      *      'read'    => 'name of the read function/method',
  380.      *      'write'   => 'name of the write function/method',
  381.      *      'destroy' => 'name of the destroy function/method',
  382.      *      'gc'      => 'name of the gc function/method',
  383.      *  ),
  384.      * // The session_cookie_params options are optional. If they are specified,
  385.      * // session_set_cookie_params() will be called with the parameters
  386.      *  'session_cookie_params' => array(
  387.      *      'lifetime' => 'Cookie lifetime in days',
  388.      *      'path'     => 'Cookie path',
  389.      *      'domain'   => 'Cookie domain',
  390.      *      'secure'   => 'Cookie send only over secure connections',
  391.      *      'httponly' => 'HHTP only cookie, PHP 5.2.0+ only',
  392.      *  ),
  393.      * 'cache_perm' => if the permission data should be cached inside the session
  394.      *  'login' => array(
  395.      *      'force'    => 'Should the user be forced to login'
  396.      *      'regenid'  => 'Should the session be regenerated on login'
  397.      *  ),
  398.      *  'logout' => array(
  399.      *      'destroy'  => 'Whether to destroy the session on logout' false or true
  400.      *  ),
  401.      * // The cookie options are optional. If they are specified, the Remember Me
  402.      * // feature is activated.
  403.      *  'cookie' => array(
  404.      *      'name'     => 'Name of Remember Me cookie',
  405.      *      'lifetime' => 'Cookie lifetime in days',
  406.      *      'path'     => 'Cookie path',
  407.      *      'domain'   => 'Cookie domain',
  408.      *      'secret'   => 'Secret key used for cookie value encryption',
  409.      *      'savedir'  => '/absolute/path/to/writeable/directory' // No trailing slash (/) !
  410.      *      'secure'   => 'Cookie send only over secure connections',
  411.      *      'httponly' => 'HHTP only cookie, PHP 5.2.0+ only',
  412.      *  ),
  413.      *  'authContainers' => array(
  414.      *      'name' => array(
  415.      *            'type'            => 'auth container name',
  416.      *            'expireTime'      => 'maximum lifetime of a session in seconds',
  417.      *            'idleTime'        => 'maximum amount of time between two request',
  418.      *            'passwordEncryptionMode'=> 'what encryption method to use',
  419.      *            'secret'                => 'secret to use in password encryption',
  420.      *            'storage' => array(
  421.      *                'dbc' => 'db connection object, use this or dsn',
  422.      *                'dsn' => 'database dsn, use this or connection',
  423.      *                'handles' => 'array of handle fields to find a user on login, a user
  424.      *                  can login with his username, email or any field you set here;
  425.      *                  works with DB, MDB, MDB2 and PDO containers',
  426.      *           ),
  427.      *           'externalValues' => array(
  428.      *                  'values'      => 'reference to an array',
  429.      *                  'keysToCheck' => 'array of keys to check in the array passed'
  430.      *           ),
  431.      *      ),
  432.      *  ),
  433.      *  'permContainer' => array(
  434.      *      'type'     => 'perm container name',
  435.      *      'storage'  => array(
  436.      *          'storage container name' => array(
  437.      *              'dbc' => 'db connection object, use this or dsn',
  438.      *              'dsn'        => 'database dsn, use this or connection',
  439.      *              'prefix'    => 'table prefix'
  440.      *              'tables'    => 'array containing additional tables or fields in existing tables',
  441.      *              'fields'    => 'array containing any additional or non-default field types',
  442.      *              'alias'     => 'array containing any additional or non-default field alias',
  443.      *              'force_seq' => 'if the use of (emulated) sequences should forced instead of using autoincrement where applicable',
  444.      *          ),
  445.      *      ),
  446.      *  ),
  447.      *
  448.      * </code>
  449.      *
  450.      * Other options in the configuration file relative to
  451.      * the Auth and Perm containers depend on what the
  452.      * containers expect. Refer to the Containers documentation.
  453.      * The examples for containers provided are just general
  454.      * do not reflect all the options for all containers.
  455.      *
  456.      * @param  array      Config array to configure.
  457.      * @return LiveUser   Returns an object of either LiveUser or false on error
  458.      *                     if so use LiveUser::getErrors() to get the errors
  459.      *
  460.      * @access public
  461.      * @see    LiveUser::getErrors
  462.      */
  463.     function &factory(&$conf)
  464.     {
  465.         $debug = false;
  466.         if (array_key_exists('debug'$conf)) {
  467.             $debug =$conf['debug'];
  468.         }
  469.  
  470.         $obj &new LiveUser($debug);
  471.  
  472.         if (is_array($conf)) {
  473.             $obj->readConfig($conf);
  474.         }
  475.  
  476.         return $obj;
  477.     }
  478.  
  479.     /**
  480.      * This uses the singleton pattern, making sure you have one and
  481.      * only instance of the class.
  482.      *
  483.      * <b>In PHP4 you MUST call this method with the
  484.      *  $var = &LiveUser::singleton() syntax.
  485.      * Without the ampersand (&) in front of the method name, you will not get
  486.      * a reference, you will get a copy.</b>
  487.      *
  488.      * @param  array    Config array to configure.
  489.      * @param  string   Signature by which the given instance can be referenced later
  490.      * @return LiveUser Returns an object of either LiveUser or false on failure
  491.      *
  492.      * @access public
  493.      * @see    LiveUser::factory
  494.      * @see    LiveUser::getErrors
  495.      */
  496.     function &singleton(&$conf$signature = null)
  497.     {
  498.         static $instances;
  499.         if (!isset($instances)) {
  500.             $instances = array();
  501.         }
  502.  
  503.         if (is_null($signature)) {
  504.             if (empty($instances)) {
  505.                 $signature uniqid('LU');
  506.             else {
  507.                 $signature key($instances);
  508.             }
  509.         }
  510.  
  511.         if (!array_key_exists($signature$instances)) {
  512.             $instances[$signature=LiveUser::factory($conf);
  513.         }
  514.  
  515.         return $instances[$signature];
  516.     }
  517.  
  518.     /**
  519.      * Wrapper method to get errors from the Error Stack.
  520.      *
  521.      * @return array|boolan array of the errors or false if there are no errors
  522.      *
  523.      * @access public
  524.      */
  525.     function getErrors()
  526.     {
  527.         if (is_object($this->stack)) {
  528.             return $this->stack->getErrors();
  529.         }
  530.         return false;
  531.     }
  532.  
  533.     /**
  534.      * Loads a PEAR class.
  535.      *
  536.      * @param  string   classname to load
  537.      * @param  bool     if errors should be supressed from the stack
  538.      * @return bool  true success or false on failure
  539.      *
  540.      * @access public
  541.      */
  542.     function loadClass($classname$supress_error = false)
  543.     {
  544.         if (!LiveUser::classExists($classname)) {
  545.             $filename str_replace('_''/'$classname).'.php';
  546.             @include_once($filename);
  547.             if (!LiveUser::classExists($classname&& !$supress_error{
  548.                 if (!LiveUser::fileExists($filename)) {
  549.                     $msg 'File for the class does not exist ' $classname;
  550.                 else {
  551.                     $msg 'Parse error in the file for class' $classname;
  552.                 }
  553.                 PEAR_ErrorStack::staticPush('LiveUser'LIVEUSER_ERROR_CONFIG,
  554.                     'exception'array()$msg);
  555.                 return false;
  556.             }
  557.         }
  558.         return true;
  559.     }
  560.  
  561.     /**
  562.      * Creates an instance of an auth container class.
  563.      *
  564.      * @param  array        Array containing the configuration.
  565.      * @param  string       Name of the container we'll be using.
  566.      * @param  string       Prefix of the class that will be used.
  567.      * @return object|falseReturns an instance of an auth container
  568.      *                       class or false on error
  569.      *
  570.      * @access public
  571.      */
  572.     function &authFactory(&$conf$containerName$classprefix 'LiveUser_')
  573.     {
  574.         $auth = false;
  575.         $classname $classprefix.'Auth_' $conf['type'];
  576.         if (LiveUser::loadClass($classname)) {
  577.             $auth &new $classname();
  578.             if ($auth->init($conf$containerName=== false{
  579.                 $auth = false;
  580.             }
  581.         }
  582.         return $auth;
  583.     }
  584.  
  585.     /**
  586.      * Creates an instance of an perm container class.
  587.      *
  588.      * @param  array         Array containing the configuration.
  589.      * @param  string        Prefix of the class that will be used.
  590.      * @return object|false Returns an instance of a perm container
  591.      *                        class or false on error
  592.      *
  593.      * @access public
  594.      */
  595.     function &permFactory(&$conf$classprefix 'LiveUser_')
  596.     {
  597.         $perm = false;
  598.         $classname $classprefix.'Perm_' $conf['type'];
  599.         if (LiveUser::loadClass($classname)) {
  600.             $perm &new $classname();
  601.             if ($perm->init($conf=== false{
  602.                 $perm = false;
  603.             }
  604.         }
  605.  
  606.         return $perm;
  607.     }
  608.  
  609.     /**
  610.      * Returns an instance of a storage Container class.
  611.      *
  612.      * @param  array         configuration array to pass to the storage container
  613.      * @param  string        Prefix of the class that will be used.
  614.      * @return object|false will return an instance of a Storage container
  615.      *                        or false upon error
  616.      *
  617.      * @access protected
  618.      */
  619.     function &storageFactory(&$confArray$classprefix 'LiveUser_Perm_')
  620.     {
  621.         end($confArray);
  622.         $key key($confArray);
  623.         $count count($confArray);
  624.         $storageName $classprefix.'Storage_' $key;
  625.         if (!LiveUser::loadClass($storageNametrue)) {
  626.             if ($count <= 1{
  627.                 $storage = false;
  628.                 return $storage;
  629.             // if the storage container does not exist try the next one in the stack
  630.             elseif ($count > 1{
  631.                 // since we are using pass by ref we cannot pop from the original array
  632.                 $keys array_keys($confArray);
  633.                 array_pop($keys);
  634.                 $newConfArray = array();
  635.                 foreach ($keys as $key{
  636.                     $newConfArray[$key=$confArray[$key];
  637.                 }
  638.                 $storage =LiveUser::storageFactory($newConfArray$classprefix);
  639.                 return $storage;
  640.             }
  641.         }
  642.         $storageConf =$confArray[$key];
  643.         $newConfArray = array();
  644.         foreach ($confArray as $keyNew => $foo{
  645.             if ($key !== $keyNew{
  646.                 $newConfArray[$keyNew=$confArray[$keyNew];
  647.             }
  648.         }
  649.         $storage &new $storageName();
  650.         if ($storage->init($storageConf$newConfArray=== false{
  651.             $storage = false;
  652.         }
  653.         return $storage;
  654.     }
  655.  
  656.     /**
  657.      * Clobbers two arrays together.
  658.      *
  659.      * Function taken from the user notes of array_merge_recursive function
  660.      * used in LiveUser::readConfig() and may be called statically
  661.      *
  662.      * @param  array        array that should be clobbered
  663.      * @param  array        array that should be clobbered
  664.      * @return array|false array on success and false on error
  665.      *
  666.      * @access public
  667.      * @author kc@hireability.com
  668.      */
  669.     function arrayMergeClobber($a1$a2)
  670.     {
  671.         if (!is_array($a1|| !is_array($a2)) {
  672.             return false;
  673.         }
  674.         foreach ($a2 as $key => $val{
  675.             if (is_array($val&& array_key_exists($key$a1&& is_array($a1[$key])) {
  676.                 $a1[$keyLiveUser::arrayMergeClobber($a1[$key]$val);
  677.             else {
  678.                 $a1[$key$val;
  679.             }
  680.         }
  681.         return $a1;
  682.     }
  683.  
  684.     /**
  685.      * Checks if a file exists in the include path.
  686.      *
  687.      * @param  string  filename
  688.      * @return bool true success and false on error
  689.      *
  690.      * @access public
  691.      */
  692.     function fileExists($file)
  693.     {
  694.         // safe_mode does notwork with is_readable()
  695.         if (ini_get('safe_mode')) {
  696.             $fp @fopen($file'r'true);
  697.             if (is_resource($fp)) {
  698.                 @fclose($fp);
  699.                 return true;
  700.             }
  701.         else {
  702.              $dirs explode(PATH_SEPARATORini_get('include_path'));
  703.              foreach ($dirs as $dir{
  704.                  if (is_readable($dir . DIRECTORY_SEPARATOR . $file)) {
  705.                      return true;
  706.                  }
  707.             }
  708.         }
  709.         return false;
  710.     }
  711.  
  712.     /**
  713.      * Checks if a class exists without triggering __autoload
  714.      *
  715.      * @param  string  classname
  716.      * @return bool true success and false on error
  717.      *
  718.      * @access public
  719.      */
  720.     function classExists($classname)
  721.     {
  722.         if (version_compare(phpversion()"5.0"">=")) {
  723.             return class_exists($classnamefalse);
  724.         }
  725.         return class_exists($classname);
  726.     }
  727.  
  728.     /**
  729.      * Reads the configuration array.
  730.      *
  731.      * @param  array|file Conf array or file path to configuration
  732.      * @param  string      Name of array containing the configuration
  733.      * @return bool        true on success or false on failure
  734.      *
  735.      * @access public
  736.      * @see    LiveUser::factory
  737.      */
  738.     function readConfig($conf)
  739.     {
  740.         if (array_key_exists('authContainers'$conf)) {
  741.             $this->_authContainers =$conf['authContainers'];
  742.         }
  743.         if (array_key_exists('permContainer'$conf)) {
  744.             $this->_permContainer =$conf['permContainer'];
  745.         }
  746.  
  747.         $this->_options LiveUser::arrayMergeClobber($this->_options$conf);
  748.         if (array_key_exists('cookie'$this->_options&& $this->_options['cookie']{
  749.             $cookie_default = array(
  750.                 'name'     => 'ludata',
  751.                 'lifetime' => '365',
  752.                 'path'     => '/',
  753.                 'domain'   => '',
  754.                 'secret'   => 'secret',
  755.                 'httponly' => false,
  756.             );
  757.             if (is_array($this->_options['cookie'])) {
  758.                 $this->_options['cookie'=
  759.                     LiveUser::arrayMergeClobber($cookie_default$this->_options['cookie']);
  760.             else {
  761.                 $this->_options['cookie'$cookie_default;
  762.             }
  763.         }
  764.  
  765.         if (array_key_exists('session_cookie_params'$this->_options&& $this->_options['session_cookie_params']{
  766.             $session_cookie_params_default = array(
  767.                 'httponly' => false,
  768.             );
  769.             if (is_array($this->_options['session_cookie_params'])) {
  770.                 $this->_options['session_cookie_params'=
  771.                     LiveUser::arrayMergeClobber($session_cookie_params_default$this->_options['cookie']);
  772.             else {
  773.                 $this->_options['session_cookie_params'$session_cookie_params_default;
  774.             }
  775.         }
  776.  
  777.         return true;
  778.     }
  779.  
  780.     /**
  781.      * Determines if loading of PEAR::Log is necessary.
  782.      *
  783.      * If an object is passed it is returned, otherwise Log is loaded
  784.      * and instantiated.
  785.      *
  786.      * @param  bool|LogBoolean that indicates if a log instance
  787.      *                   should be created or an instance of a class
  788.      *                   that implements the PEAR:Log interface.
  789.      * @return log instance of the given log class
  790.      *
  791.      * @access protected
  792.      */
  793.     function &PEARLogFactory(&$log)
  794.     {
  795.         if (empty($log|| is_object($log)) {
  796.             return $log;
  797.         }
  798.  
  799.         require_once 'Log.php';
  800.         $log =Log::factory('composite');
  801.         if (!is_a($log'Log_composite')) {
  802.             $this->stack->push(
  803.                 LIVEUSER_ERROR_CONFIG'exception'array(),
  804.                 'Could not create Log instance'
  805.             );
  806.             $return = false;
  807.             return $return;
  808.         }
  809.         $conf = array(
  810.             'colors' => array(
  811.                 PEAR_LOG_EMERG   => 'red',
  812.                 PEAR_LOG_ALERT   => 'orange',
  813.                 PEAR_LOG_CRIT    => 'yellowgreen',
  814.                 PEAR_LOG_ERR     => 'green',
  815.                 PEAR_LOG_WARNING => 'blue',
  816.                 PEAR_LOG_NOTICE  => 'indigo',
  817.                 PEAR_LOG_INFO    => 'violet',
  818.                 PEAR_LOG_DEBUG   => 'black',
  819.             ),
  820.         );
  821.         $winlog =Log::factory('win''LiveUser''LiveUser'$conf);
  822.         if (!is_a($winlog'Log_win')) {
  823.             $this->stack->push(
  824.                 LIVEUSER_ERROR_CONFIG'exception'array(),
  825.                 'Could not create Log "window" instance'
  826.             );
  827.             $return = false;
  828.             return $return;
  829.         }
  830.         $log->addChild($winlog);
  831.  
  832.         return $log;
  833.     }
  834.  
  835.     /**
  836.      * Decrypts a password so that it can be compared with the user input.
  837.      * Uses the algorithm defined in the passwordEncryptionMode parameter.
  838.      *
  839.      * @param  string the encrypted password
  840.      * @param  string the encryption mode
  841.      * @return string The decrypted password
  842.      */
  843.     function decryptPW($encryptedPW$passwordEncryptionMode$secret)
  844.     {
  845.         if (empty($encryptedPW&& $encryptedPW !== 0{
  846.             return '';
  847.         }
  848.  
  849.         $passwordEncryptionMode strtolower($passwordEncryptionMode);
  850.  
  851.         if ($passwordEncryptionMode === 'plain'{
  852.             return $encryptedPW;
  853.         }
  854.  
  855.         if ($passwordEncryptionMode === 'rc4'{
  856.             return LiveUser::cryptRC4($decryptedPW$secretfalse);
  857.         }
  858.  
  859.         PEAR_ErrorStack::staticPush('LiveUser'LIVEUSER_ERROR_NOT_SUPPORTED'error'array(),
  860.             'Could not find the requested decryption function : ' $passwordEncryptionMode);
  861.         return false;
  862.     }
  863.  
  864.     /**
  865.      * Encrypts a password for storage in a backend container.
  866.      * Uses the algorithm defined in the passwordEncryptionMode parameter.
  867.      *
  868.      * @param string  password to encrypt
  869.      * @param  string the encryption mode
  870.      * @param  string token to use to encrypt data
  871.      * @return string The encrypted password
  872.      */
  873.     function encryptPW($plainPW$passwordEncryptionMode$secret)
  874.     {
  875.         if (empty($plainPW&& $plainPW !== 0{
  876.             return '';
  877.         }
  878.  
  879.         $passwordEncryptionMode strtolower($passwordEncryptionMode);
  880.  
  881.         if ($passwordEncryptionMode == 'plain'{
  882.             return $plainPW;
  883.         }
  884.  
  885.         if ($passwordEncryptionMode == 'md5'{
  886.             return md5($plainPW);
  887.         }
  888.  
  889.         if (extension_loaded('hash'&& in_array($passwordEncryptionModehash_algos())) {
  890.             return hash($passwordEncryptionMode$plainPW);
  891.         }
  892.  
  893.         if ($passwordEncryptionMode == 'rc4'{
  894.             return LiveUser::cryptRC4($plainPW$secrettrue);
  895.         }
  896.  
  897.         if (function_exists('sha1'&& $passwordEncryptionMode == 'sha1'{
  898.             return sha1($plainPW);
  899.         }
  900.  
  901.         PEAR_ErrorStack::staticPush('LiveUser'LIVEUSER_ERROR_NOT_SUPPORTED'error'array(),
  902.             'Could not find the requested encryption function : ' $passwordEncryptionMode);
  903.         return false;
  904.     }
  905.  
  906.     /**
  907.      * Creates an instance of the PEAR::Crypt_Rc4 class.
  908.      *
  909.      * @param  string token to use to encrypt data
  910.      * @return Crypt_RC4 returns an instance of the Crypt_RC4 class
  911.      *
  912.      * @access public
  913.      */
  914.     function &cryptRC4Factory($secret)
  915.     {
  916.         $rc4 = false;
  917.         if (LiveUser::loadClass('Crypt_Rc4')) {
  918.             $rc4 =new Crypt_Rc4($secret);
  919.         }
  920.         return $rc4;
  921.     }
  922.  
  923.     /**
  924.      * Crypts data using mcrypt or userland if not available.
  925.      *
  926.      * @param  string   data
  927.      * @param  string   secret key
  928.      * @param  bool     true if it should be crypted,
  929.      *                   false if it should be decrypted
  930.      * @return string   (de-)crypted data
  931.      *
  932.      * @access public
  933.      */
  934.     function cryptRC4($data$secret$crypt = true)
  935.     {
  936.         if (function_exists('mcrypt_module_open')) {
  937.             $td mcrypt_module_open('tripledes''''ecb''');
  938.             $iv mcrypt_create_iv(mcrypt_enc_get_iv_size ($td)MCRYPT_RAND);
  939.             mcrypt_generic_init($td$secret$iv);
  940.             if ($crypt{
  941.                 $data mcrypt_generic($td$data);
  942.             else {
  943.                 $data mdecrypt_generic($td$data);
  944.             }
  945.             mcrypt_generic_deinit($td);
  946.             mcrypt_module_close($td);
  947.         else {
  948.             $rc4 =LiveUser::cryptRC4Factory($secret);
  949.             if (!$rc4{
  950.                 $this->stack->push(
  951.                     LIVEUSER_ERROR_CONFIG'exception'array(),
  952.                     'RememberMe feature requires either the mcrypt extension or PEAR::Crypt_RC4'
  953.                 );
  954.                 return false;
  955.             }
  956.             if ($crypt{
  957.                 $rc4->crypt($data);
  958.             else {
  959.                 $rc4->decrypt($data);
  960.             }
  961.         }
  962.  
  963.         return $data;
  964.     }
  965.  
  966.     /**
  967.      * Sets an option after the configuration array has been loaded.
  968.      *
  969.      * You can override a specific option calling this method.
  970.      *
  971.      * @param  string   option name
  972.      * @param  mixed    value for the option
  973.      * @return bool  true on success or false on failure
  974.      *
  975.      * @access public
  976.      * @see    LiveUser::_options
  977.      */
  978.     function setOption($option$value)
  979.     {
  980.         if (array_key_exists($option$this->_options)) {
  981.             $this->_options[$option$value;
  982.             return true;
  983.         }
  984.         $this->stack->push(LIVEUSER_ERROR_CONFIG'exception'array(),
  985.             "unknown option $option");
  986.         return false;
  987.     }
  988.  
  989.     /**
  990.      * Returns the value of an option from the configuration array.
  991.      *
  992.      * @param  string  option name
  993.      * @return mixed   the option value or false on failure
  994.      *
  995.      * @access public
  996.      */
  997.     function getOption($option)
  998.     {
  999.         if (array_key_exists($option$this->_options)) {
  1000.             return $this->_options[$option];
  1001.         }
  1002.         $this->stack->push(LIVEUSER_ERROR_CONFIG'exception'array(),
  1003.             "unknown option $option");
  1004.         return false;
  1005.     }
  1006.  
  1007.     /**
  1008.      * Sets the session handler and name and starts the session if headers have
  1009.      * not been send yet.
  1010.      *
  1011.      * @return bool  true on success or false on failure
  1012.      *
  1013.      * @access private
  1014.      */
  1015.     function _startSession()
  1016.     {
  1017.         // set session save handler if needed
  1018.         if ($this->_options['session_save_handler']{
  1019.             session_set_save_handler(
  1020.                 $this->_options['session_save_handler']['open'],
  1021.                 $this->_options['session_save_handler']['close'],
  1022.                 $this->_options['session_save_handler']['read'],
  1023.                 $this->_options['session_save_handler']['write'],
  1024.                 $this->_options['session_save_handler']['destroy'],
  1025.                 $this->_options['session_save_handler']['gc']
  1026.             );
  1027.         }
  1028.         if ($this->_options['session_cookie_params']{
  1029.             session_set_cookie_params((
  1030.                 (LIVEUSER_DAY_SECONDS * $this->_options['session_cookie_params']['lifetime'])),
  1031.                 $this->_options['session_cookie_params']['path'],
  1032.                 $this->_options['session_cookie_params']['domain'],
  1033.                 $this->_options['session_cookie_params']['secure'],
  1034.                 $this->_options['session_cookie_params']['httponly']);
  1035.         }
  1036.         // Set the name of the current session
  1037.         session_name($this->_options['session']['name']);
  1038.         // Check if we can safely start the session
  1039.         if (headers_sent()) {
  1040.             $this->stack->push(
  1041.                 LIVEUSER_ERROR_SESSION_STARTED'exception'
  1042.             );
  1043.             return;
  1044.         }
  1045.         // If there's no session yet, start it now
  1046.         @session_start();
  1047.  
  1048.         return true;
  1049.     }
  1050.  
  1051.     /**
  1052.      * Tries to retrieve the auth object from session and checks possible timeouts.
  1053.      *
  1054.      * @return bool  true if init process well, false if something went wrong.
  1055.      *
  1056.      * @access public
  1057.      */
  1058.     function init()
  1059.     {
  1060.         if ($this->_options['session']['force_start']{
  1061.             $this->_startSession();
  1062.         }
  1063.  
  1064.         // Try to fetch auth object from session
  1065.         $isReturningUser $this->_unfreeze();
  1066.  
  1067.         // current timestamp
  1068.         $now time();
  1069.  
  1070.         if ($this->isLoggedIn()) {
  1071.             if ($isReturningUser{
  1072.                 // Check if authentication session is expired.
  1073.                 if ($this->getProperty('expireTime'> 0
  1074.                     && ($this->getProperty('currentLogin'$this->getProperty('expireTime')) $now
  1075.                 {
  1076.                     $this->logout(false);
  1077.                     $this->_status LIVEUSER_STATUS_EXPIRED;
  1078.                     $this->dispatcher->post($this'onExpired');
  1079.                 // Check if maximum idle time is reached.
  1080.                 elseif ($this->getProperty('idleTime'> 0
  1081.                     && array_key_exists('idle'$_SESSION[$this->_options['session']['varname']])
  1082.                     && ($_SESSION[$this->_options['session']['varname']]['idle'$this->getProperty('idleTime')) $now
  1083.                 {
  1084.                     $this->logout(false);
  1085.                     $this->_status LIVEUSER_STATUS_IDLED;
  1086.                     $this->dispatcher->post($this'onIdled');
  1087.                 }
  1088.             }
  1089.         }
  1090.  
  1091.         // set idle time and status
  1092.         if ($this->isLoggedIn()) {
  1093.             $_SESSION[$this->_options['session']['varname']]['idle'$now;
  1094.             $this->_status LIVEUSER_STATUS_OK;
  1095.         // Force user login.
  1096.         elseif ($this->_options['login']['force']{
  1097.             $this->dispatcher->post($this'forceLogin');
  1098.         }
  1099.  
  1100.         return true;
  1101.     }
  1102.  
  1103.     /**
  1104.      * Tries to log the user in by trying all the Auth containers defined
  1105.      * in the configuration file until there is a success or a failure.
  1106.      *
  1107.      * @param  string   handle of the user trying to authenticate
  1108.      * @param  string   password of the user trying to authenticate
  1109.      * @param  bool  set if remember me is set, requires cookie otion
  1110.      * @param  bool|intif the user data should be read using the auth user id
  1111.      * @return bool  true on success or false on failure
  1112.      *
  1113.      * @access public
  1114.      */
  1115.     function login($handle ''$passwd ''$remember = false$auth_user_id = false)
  1116.     {
  1117.         if ($remember && $auth_user_id{
  1118.             $this->_status LIVEUSER_STATUS_AUTHINITERROR;
  1119.             $this->stack->push(LIVEUSER_ERROR'exception',
  1120.                 array('msg' => 'Remember me feature is incompatible logging in via the auth_user_id'));
  1121.             return false;
  1122.         }
  1123.  
  1124.         if (empty($handle&& $remember{
  1125.             $result $this->readRememberCookie();
  1126.             if (!is_array($result)) {
  1127.                 if ($this->_status == LIVEUSER_STATUS_UNKNOWN{
  1128.                     $this->_status LIVEUSER_STATUS_EMPTY_HANDLE;
  1129.                 }
  1130.                 return false;
  1131.             }
  1132.             $handle $result['handle'];
  1133.             $passwd $result['passwd'];
  1134.         }
  1135.  
  1136.         $this->_status LIVEUSER_STATUS_AUTHFAILED;
  1137.         $this->_auth $this->_perm = null;
  1138.  
  1139.         //loop into auth containers
  1140.         $containerNames array_keys($this->_authContainers);
  1141.         foreach ($containerNames as $containerName{
  1142.             $auth =LiveUser::authFactory($this->_authContainers[$containerName]$containerName);
  1143.             if ($auth === false{
  1144.                 $this->_status LIVEUSER_STATUS_AUTHINITERROR;
  1145.                 $this->stack->push(LIVEUSER_ERROR'exception',
  1146.                     array('msg' => 'Could not instanciate auth container: '.$containerName));
  1147.                 return false;
  1148.             }
  1149.             $login $auth->login($handle$passwd$auth_user_id);
  1150.             if ($login === false{
  1151.                 $this->_status LIVEUSER_STATUS_AUTHINITERROR;
  1152.                 $this->stack->push(LIVEUSER_ERROR'exception',
  1153.                     array('msg' => 'Could not execute login method: '.$containerName));
  1154.                 return false;
  1155.             }
  1156.             if ($auth->loggedIn{
  1157.                 $this->_auth =$auth;
  1158.                 if ($remember{
  1159.                     $this->setRememberCookie($handle$passwd);
  1160.                 }
  1161.                 $this->_status LIVEUSER_STATUS_OK;
  1162.                 // Create permission object
  1163.                 if (is_array($this->_permContainer)) {
  1164.                     $perm =LiveUser::permFactory($this->_permContainer);
  1165.                     if ($perm === false{
  1166.                         $this->_status LIVEUSER_STATUS_PERMINITERROR;
  1167.                         $this->stack->push(LIVEUSER_ERROR'exception',
  1168.                             array('msg' => 'Could not instanciate perm container of type: ' $this->_permContainer['type']));
  1169.                         return false;
  1170.                     }
  1171.                     if (!$perm->mapUser($auth->getProperty('auth_user_id')$containerName)) {
  1172.                         $this->dispatcher->post($this'onFailedMapping');
  1173.                     else {
  1174.                         $this->_perm =$perm;
  1175.                     }
  1176.                 }
  1177.                 $this->_freeze();
  1178.                 break;
  1179.             elseif (!is_null($login&& !$auth->getProperty('is_active')) {
  1180.                 $this->_status LIVEUSER_STATUS_ISINACTIVE;
  1181.                 break;
  1182.             }
  1183.         }
  1184.  
  1185.         if (!$this->isLoggedIn()) {
  1186.             $this->dispatcher->post($this'onFailedLogin');
  1187.             return false;
  1188.         }
  1189.  
  1190.         // user has just logged in
  1191.         if (!$this->_options['session']['force_start']{
  1192.             $this->_startSession();
  1193.         }
  1194.         if ($this->_options['login']['regenid']{
  1195.             session_regenerate_id();
  1196.         }
  1197.         $this->dispatcher->post($this'onLogin');
  1198.  
  1199.         return true;
  1200.     }
  1201.  
  1202.     /**
  1203.      * Gets auth and perm container objects back from session and tries
  1204.      * to give them an active database/whatever connection again.
  1205.      *
  1206.      * @return bool true on success or false on failure
  1207.      *
  1208.      * @access private
  1209.      */
  1210.     function _unfreeze()
  1211.     {
  1212.         if (!$this->_options['session']['force_start']{
  1213.             if (!array_key_exists($this->_options['session']['name']$_REQUEST)) {
  1214.                 return false;
  1215.             }
  1216.             $this->_startSession();
  1217.         }
  1218.  
  1219.         if (isset($_SESSION[$this->_options['session']['varname']])
  1220.             && array_key_exists('auth'$_SESSION[$this->_options['session']['varname']])
  1221.             && is_array($_SESSION[$this->_options['session']['varname']]['auth'])
  1222.             && array_key_exists('auth_name'$_SESSION[$this->_options['session']['varname']])
  1223.             && strlen($_SESSION[$this->_options['session']['varname']]['auth_name']> 0
  1224.         {
  1225.             $containerName $_SESSION[$this->_options['session']['varname']]['auth_name'];
  1226.             $auth =LiveUser::authFactory($this->_authContainers[$containerName]$containerName);
  1227.             if ($auth === false{
  1228.                 $this->stack->push(LIVEUSER_ERROR'exception',
  1229.                     array('msg' => 'Could not instanciate auth container: '.$containerName));
  1230.                 return false;
  1231.             }
  1232.             if ($auth->unfreeze($_SESSION[$this->_options['session']['varname']]['auth'])) {
  1233.                 $auth->containerName = $_SESSION[$this->_options['session']['varname']]['auth_name'];
  1234.                 $this->_auth &$auth;
  1235.                 if (array_key_exists('perm'$_SESSION[$this->_options['session']['varname']])
  1236.                     && $_SESSION[$this->_options['session']['varname']]['perm']
  1237.                 {
  1238.                     $perm =LiveUser::permFactory($this->_permContainer);
  1239.                     if ($perm === false{
  1240.                         $this->stack->push(LIVEUSER_ERROR'exception',
  1241.                             array('msg' => 'Could not instanciate perm container of type: ' $this->_permContainer));
  1242.                         return $perm;
  1243.                     }
  1244.                     if ($this->_options['cache_perm']{
  1245.                         $result $perm->unfreeze($this->_options['session']['varname']);
  1246.                     else {
  1247.                         $result $perm->mapUser($auth->getProperty('auth_user_id')$auth->containerName);
  1248.                     }
  1249.                     if ($result{
  1250.                         $this->_perm &$perm;
  1251.                     }
  1252.                 }
  1253.                 $this->_status LIVEUSER_STATUS_UNFROZEN;
  1254.                 $this->dispatcher->post($this'onUnfreeze');
  1255.                 return true;
  1256.             }
  1257.         }
  1258.  
  1259.         return false;
  1260.     }
  1261.  
  1262.     /**
  1263.      * Stores all properties in the session.
  1264.      *
  1265.      * @return  bool true on sucess or false on failure
  1266.      *
  1267.      * @access  private
  1268.      */
  1269.     function _freeze()
  1270.     {
  1271.         if (is_a($this->_auth'LiveUser_Auth_Common'&& $this->isLoggedIn()) {
  1272.             // Bind objects to session
  1273.             $_SESSION[$this->_options['session']['varname']] = array();
  1274.             $_SESSION[$this->_options['session']['varname']]['auth'$this->_auth->freeze();
  1275.             $_SESSION[$this->_options['session']['varname']]['auth_name'$this->_auth->containerName;
  1276.             if (is_a($this->_perm'LiveUser_Perm_Simple')) {
  1277.                 $_SESSION[$this->_options['session']['varname']]['perm'= true;
  1278.                 if ($this->_options['cache_perm']{
  1279.                      $this->_perm->freeze($this->_options['session']['varname']);
  1280.                 }
  1281.             }
  1282.             return true;
  1283.         }
  1284.         $this->stack->push(LIVEUSER_ERROR_CONFIG'exception'array(),
  1285.             'No data available to store inside session');
  1286.         return false;
  1287.     }
  1288.  
  1289.     /**
  1290.      * Properly disconnect resources in the active container.
  1291.      *
  1292.      * @return  bool true on success or false on failure
  1293.      *
  1294.      * @access public
  1295.      */
  1296.     function disconnect()
  1297.     {
  1298.         if (is_a($this->_auth'LiveUser_Auth_Common')) {
  1299.             $result $this->_auth->disconnect();
  1300.             if ($result === false{
  1301.                 return false;
  1302.             }
  1303.             $this->_auth = null;
  1304.         }
  1305.         if (is_a($this->_perm'LiveUser_Perm_Simple')) {
  1306.             $result $this->_perm->disconnect();
  1307.             if ($result === false{
  1308.                 return false;
  1309.             }
  1310.             $this->_perm = null;
  1311.         }
  1312.         return true;
  1313.     }
  1314.  
  1315.     /**
  1316.      * If cookies are allowed, this method checks if the user wanted
  1317.      * a cookie to be set so he doesn't have to enter handle and password
  1318.      * for his next login. If true, it will set the cookie.
  1319.      *
  1320.      * @param  string   handle of the user trying to authenticate
  1321.      * @param  string   password of the user trying to authenticate
  1322.      * @return bool  true if the cookie can be set, false otherwise
  1323.      *
  1324.      * @access public
  1325.      */
  1326.     function setRememberCookie($handle$passwd)
  1327.     {
  1328.         if (!array_key_exists('cookie'$this->_options)) {
  1329.             return false;
  1330.         }
  1331.  
  1332.         $store_id md5($handle $passwd);
  1333.  
  1334.         $dir $this->_options['cookie']['savedir'];
  1335.         $file $dir '/' $store_id '.lu';
  1336.  
  1337.         if (!is_writable($dir)) {
  1338.             $this->stack->push(LIVEUSER_ERROR_CONFIG'exception'array(),
  1339.                 'Cannot create file, please check path and permissions');
  1340.             return false;
  1341.         }
  1342.  
  1343.         $fh @fopen($file'wb');
  1344.         if (!$fh{
  1345.             $this->stack->push(LIVEUSER_ERROR_CONFIG'exception'array(),
  1346.                 'Cannot open file for writting');
  1347.             return false;
  1348.         }
  1349.  
  1350.         $passwd_id md5($passwd);
  1351.         $crypted_data LiveUser::cryptRC4(
  1352.             serialize(array($passwd_id$passwd)),
  1353.             $this->_options['cookie']['secret'],
  1354.             true
  1355.         );
  1356.  
  1357.         $write fwrite($fh$crypted_data);
  1358.         fclose($fh);
  1359.         if (!$write{
  1360.             $this->stack->push(LIVEUSER_ERROR_CONFIG'exception'array(),
  1361.                 'Cannot save cookie data');
  1362.             return false;
  1363.         }
  1364.  
  1365.         $setcookie setcookie(
  1366.             $this->_options['cookie']['name'],
  1367.             $store_id $passwd_id $handle,
  1368.             (time((LIVEUSER_DAY_SECONDS * $this->_options['cookie']['lifetime'])),
  1369.             $this->_options['cookie']['path'],
  1370.             $this->_options['cookie']['domain'],
  1371.             $this->_options['cookie']['secure'],
  1372.             $this->_options['cookie']['httponly']
  1373.         );
  1374.  
  1375.         if (!$setcookie{
  1376.             @unlink($file);
  1377.             $this->stack->push(LIVEUSER_ERROR_CONFIG'exception'array(),
  1378.                 'Unable to set cookie');
  1379.             return false;
  1380.         }
  1381.  
  1382.         return true;
  1383.     }
  1384.  
  1385.     /**
  1386.      * Handles the retrieval of the login data from the rememberMe cookie.
  1387.      *
  1388.      * @return bool true on success or false on failure
  1389.      *
  1390.      * @access public
  1391.      */
  1392.     function readRememberCookie()
  1393.     {
  1394.         if (!array_key_exists('cookie'$this->_options)
  1395.             || !array_key_exists($this->_options['cookie']['name']$_COOKIE)
  1396.         {
  1397.             return false;
  1398.         }
  1399.  
  1400.         if (strlen($_COOKIE[$this->_options['cookie']['name']]< 65
  1401.             || preg_match('/[^a-z0-9]/i'substr($_COOKIE[$this->_options['cookie']['name']]064))
  1402.         {
  1403.             $this->deleteRememberCookie();
  1404.         }
  1405.         $cookieData $_COOKIE[$this->_options['cookie']['name']];
  1406.  
  1407.         $store_id substr($cookieData032);
  1408.         $passwd_id substr($cookieData3232);
  1409.         $handle substr($cookieData64);
  1410.  
  1411.         $dir $this->_options['cookie']['savedir'];
  1412.  
  1413.         $fh @fopen($dir '/' $store_id '.lu''rb');
  1414.         if (!$fh{
  1415.             $this->deleteRememberCookie();
  1416.             $this->stack->push(LIVEUSER_ERROR_CONFIG'exception'array(),
  1417.                 'Cannot open file for reading');
  1418.             return false;
  1419.         }
  1420.  
  1421.         $fields fread($fh4096);
  1422.         fclose($fh);
  1423.         if (!$fields{
  1424.             $this->deleteRememberCookie();
  1425.             $this->stack->push(LIVEUSER_ERROR_CONFIG'exception'array(),
  1426.                 'Cannot read file');
  1427.             return false;
  1428.         }
  1429.  
  1430.         $serverData @unserialize(
  1431.             LiveUser::cryptRC4($fields$this->_options['cookie']['secret']false)
  1432.         );
  1433.  
  1434.         if (!is_array($serverData|| count($serverData!= 2{
  1435.             $this->deleteRememberCookie();
  1436.             $this->stack->push(LIVEUSER_ERROR_COOKIE'exception'array(),
  1437.                 'Incorrect array structure');
  1438.             return false;
  1439.         }
  1440.  
  1441.         if ($serverData[0!= $passwd_id{
  1442.             // Delete cookie if it's not valid, keeping it messes up the
  1443.             // authentication process
  1444.             $this->deleteRememberCookie();
  1445.             $this->stack->push(LIVEUSER_ERROR_COOKIE'error'array(),
  1446.                 'Passwords hashes do not match in cookie in LiveUser::readRememberMeCookie()');
  1447.             return false;
  1448.         }
  1449.  
  1450.         return array('handle' => $handle'passwd' => $serverData[1]);
  1451.     }
  1452.  
  1453.     /**
  1454.      * Deletes the rememberMe cookie.
  1455.      *
  1456.      * @return bool true on success or false on failure
  1457.      *
  1458.      * @access public
  1459.      */
  1460.     function deleteRememberCookie()
  1461.     {
  1462.         if (!array_key_exists('cookie'$this->_options)
  1463.             || !array_key_exists($this->_options['cookie']['name']$_COOKIE)
  1464.         {
  1465.             return false;
  1466.         }
  1467.  
  1468.         if (preg_match('/[^a-z0-9]/i'substr($_COOKIE[$this->_options['cookie']['name']]032))) {
  1469.             $this->stack->push(LIVEUSER_ERROR_COOKIE'error'array(),
  1470.                 'Malformed rememberme cookie identifer in LiveUser::deleteRememberCookie()');
  1471.             return false;
  1472.         }
  1473.         $cookieData $_COOKIE[$this->_options['cookie']['name']];
  1474.  
  1475.         $store_id substr($cookieData032);
  1476.         @unlink($this->_options['cookie']['savedir''/'.$store_id.'.lu');
  1477.  
  1478.         unset($_COOKIE[$this->_options['cookie']['name']]);
  1479.         setcookie($this->_options['cookie']['name'],
  1480.             false,
  1481.             LIVEUSER_COOKIE_DELETE_TIME,
  1482.             $this->_options['cookie']['path'],
  1483.             $this->_options['cookie']['domain'],
  1484.             $this->_options['cookie']['secure']
  1485.         );
  1486.  
  1487.         return true;
  1488.     }
  1489.  
  1490.     /**
  1491.      * This logs the user out and destroys the session object if the
  1492.      * configuration option is set.
  1493.      *
  1494.      * @param bool  set to false if no events should be fired b yhte logout
  1495.      * @return bool true on success or false on failure
  1496.      *
  1497.      * @access public
  1498.      */
  1499.     function logout($direct = true)
  1500.     {
  1501.         $this->_status LIVEUSER_STATUS_LOGGEDOUT;
  1502.  
  1503.         if ($direct{
  1504.             // trigger event 'onLogout' as replacement for logout callback function
  1505.             $this->dispatcher->post($this'onLogout');
  1506.             // If there's a cookie and the session hasn't idled or expired, kill that one too...
  1507.             $this->deleteRememberCookie();
  1508.         }
  1509.  
  1510.         // If the session should be destroyed, do so now...
  1511.         if ($this->_options['logout']['destroy']{
  1512.             session_unset();
  1513.             session_destroy();
  1514.             if ($this->_options['session']['force_start']{
  1515.                 $this->_startSession();
  1516.             }
  1517.         elseif (array_key_exists($this->_options['session']['varname']$_SESSION)) {
  1518.             unset($_SESSION[$this->_options['session']['varname']]);
  1519.         }
  1520.  
  1521.         $this->disconnect();
  1522.  
  1523.         if ($direct{
  1524.             // trigger event 'postLogout', can be used to do a redirect
  1525.             $this->dispatcher->post($this'postLogout');
  1526.         }
  1527.  
  1528.         return true;
  1529.     }
  1530.  
  1531.     /**
  1532.      * Wrapper method for the permission object's own checkRight method.
  1533.      * Use this method to determine if a user has a given right or set
  1534.      * of rights.
  1535.      *
  1536.      * @param  array|int  A right id or an array of rights.
  1537.      * @return int|false level if the user has the right/rights false if not
  1538.      *
  1539.      * @access public
  1540.      */
  1541.     function checkRight($rights)
  1542.     {
  1543.         if (is_null($rights)) {
  1544.             return LIVEUSER_MAX_LEVEL;
  1545.         }
  1546.  
  1547.         if (is_a($this->_perm'LiveUser_Perm_Simple')) {
  1548.             if (is_array($rights)) {
  1549.                 // assume user has the right in order to have min() work
  1550.                 $hasright LIVEUSER_MAX_LEVEL;
  1551.                 foreach ($rights as $currentright{
  1552.                     $level $this->_perm->checkRight($currentright);
  1553.                     if (!$level{
  1554.                         return false;
  1555.                     }
  1556.                     $hasright min($hasright$level);
  1557.                 }
  1558.                 return $hasright;
  1559.             else {
  1560.                 return $this->_perm->checkRight($rights);
  1561.             }
  1562.         elseif ($rights === 0 && is_a($this->_auth'LiveUser_Auth_Common')) {
  1563.             return LIVEUSER_MAX_LEVEL;
  1564.         }
  1565.  
  1566.         return false;
  1567.     }
  1568.  
  1569.     /**
  1570.      * Wrapper method for the permission object's own checkRight and checkLevel methods
  1571.      *
  1572.      * @param  array|int A right id or an array of rights.
  1573.      * @param  array|int Id or array of Ids of the owner of the
  1574.                           ressource for which the right is requested.
  1575.      * @param  array|int Id or array of Ids of the group of the
  1576.      *                     ressource for which the right is requested.
  1577.      * @return bool    true on success or false on failure
  1578.      *
  1579.      * @access public
  1580.      */
  1581.     function checkRightLevel($rights$owner_user_id$owner_group_id)
  1582.     {
  1583.         $level $this->checkRight($rights);
  1584.         if (is_a($this->_perm'LiveUser_Perm_Complex')) {
  1585.             $level $this->_perm->checkLevel($level$owner_user_id$owner_group_id);
  1586.         }
  1587.  
  1588.         return (bool)$level;
  1589.     }
  1590.  
  1591.     /**
  1592.      * Wrapper method for the permission object's own checkGroup method.
  1593.      *
  1594.      * @param  array|int A group id or an array of groups.
  1595.      * @return bool    true on success or false on failure
  1596.      *
  1597.      * @access public
  1598.      */
  1599.     function checkGroup($groups)
  1600.     {
  1601.         if (is_null($groups)) {
  1602.             return true;
  1603.         }
  1604.  
  1605.         if (is_a($this->_perm'LiveUser_Perm_Medium')) {
  1606.             if (is_array($groups)) {
  1607.                 foreach ($groups as $group{
  1608.                     if (!$this->_perm->checkGroup($group)) {
  1609.                         return false;
  1610.                     }
  1611.                 }
  1612.                 return true;
  1613.             else {
  1614.                 return $this->_perm->checkGroup($groups);
  1615.             }
  1616.         }
  1617.  
  1618.         return false;
  1619.     }
  1620.  
  1621.     /**
  1622.      * Checks if a user is logged in.
  1623.      *
  1624.      * @return bool true if user is logged in, false if not
  1625.      *
  1626.      * @access public
  1627.      */
  1628.     function isLoggedIn()
  1629.     {
  1630.         if (!is_a($this->_auth'LiveUser_Auth_Common')) {
  1631.             return false;
  1632.         }
  1633.  
  1634.         return $this->_auth->loggedIn;
  1635.     }
  1636.  
  1637.     /**
  1638.      * Function that determines if the user exists but hasn't yet been declared
  1639.      * "active" by an administrator.
  1640.      *
  1641.      * Use this to check if this was the reason
  1642.      * why a user was not able to login.
  1643.      * true ==  user account is NOT active
  1644.      * false == user account is active
  1645.      *
  1646.      * @return bool true if the user account is *not* active
  1647.      *                  false if the user account *is* active
  1648.      *
  1649.      * @access public
  1650.      */
  1651.     function isInactive()
  1652.     {
  1653.         return $this->_status == LIVEUSER_STATUS_ISINACTIVE;
  1654.     }
  1655.  
  1656.     /**
  1657.      * Wrapper method to access properties from the auth and
  1658.      * permission containers.
  1659.      *
  1660.      * @param  string  Name of the property to be returned.
  1661.      * @param  string  'auth' or 'perm'
  1662.      * @return mixed   a scalarvalue, an object or an array.
  1663.      *
  1664.      * @access public
  1665.      */
  1666.     function getProperty($what$container 'auth')
  1667.     {
  1668.         $that = null;
  1669.         if ($container == 'auth' && is_a($this->_auth'LiveUser_Auth_Common')
  1670.             && !is_null($this->_auth->getProperty($what))
  1671.         {
  1672.             $that $this->_auth->getProperty($what);
  1673.         elseif (is_a($this->_perm'LiveUser_Perm_Simple')
  1674.             && !is_null($this->_perm->getProperty($what))
  1675.         {
  1676.             $that $this->_perm->getProperty($what);
  1677.         }
  1678.         return $that;
  1679.     }
  1680.  
  1681.     /**
  1682.      * Updates the properties of the containers from the original source.
  1683.      *
  1684.      * @param bool if the auth container should be updated
  1685.      * @param bool if the perm container should be updated
  1686.      * @return bool true on success and false on failure
  1687.      *
  1688.      * @access public
  1689.      */
  1690.     function updateProperty($auth$perm = null)
  1691.     {
  1692.         if (!is_a($this->_auth'LiveUser_Auth_Common')) {
  1693.             $this->stack->push(LIVEUSER_ERROR'error'array(),
  1694.                 'Cannot update container if no auth container instance is available');
  1695.             return false;
  1696.         }
  1697.         if ($auth && !$this->_auth->readUserData(nullnull$this->_auth->getProperty('auth_user_id'))) {
  1698.             return false;
  1699.         }
  1700.         if (is_null($perm)) {
  1701.             $perm is_a($this->_perm'LiveUser_Perm_Simple');
  1702.         }
  1703.         if ($perm{
  1704.             if (!is_a($this->_perm'LiveUser_Perm_Simple')) {
  1705.                 $this->stack->push(LIVEUSER_ERROR'error'array(),
  1706.                     'Cannot update container if no perm container instance is available');
  1707.                 return false;
  1708.             }
  1709.             if (!$this->_perm->mapUser($this->_auth->getProperty('auth_user_id')$this->_auth->containerName)) {
  1710.                 return false;
  1711.             }
  1712.         }
  1713.         $this->_freeze();
  1714.         return true;
  1715.     }
  1716.  
  1717.     /**
  1718.      * Get the current status.
  1719.      *
  1720.      * @return int a LIVEUSER_STATUS_* constant
  1721.      *
  1722.      * @access public
  1723.      * @see    LIVEUSER_STATUS_* constants
  1724.      */
  1725.     function getStatus()
  1726.     {
  1727.         return $this->_status;
  1728.     }
  1729.  
  1730.     /**
  1731.      * Make a string representation of the object.
  1732.      *
  1733.      * @return  string  returns a string representation of the class
  1734.      *
  1735.      * @access private
  1736.      */
  1737.     function __toString()
  1738.     {
  1739.         return get_class($this' logged in: ' ($this->isLoggedIn('Yes' 'No');
  1740.     }
  1741.  
  1742.     /**
  1743.      * Return a textual status message for a LiveUser status code.
  1744.      *
  1745.      * @param   int|array  integer status code,
  1746.                                 null to get the current status code-message map,
  1747.                                 or an array with a new status code-message map
  1748.      * @return  string  error message
  1749.      *
  1750.      * @access public
  1751.      */
  1752.     function statusMessage($value = null)
  1753.     {
  1754.         // make the variable static so that it only has to do the defining on the first call
  1755.         static $statusMessages;
  1756.  
  1757.         if (is_array($value)) {
  1758.             $statusMessages $value;
  1759.             return true;
  1760.         }
  1761.  
  1762.         if (!isset($statusMessages)) {
  1763.             $statusMessages = array(
  1764.                 LIVEUSER_STATUS_OK              => 'Authentication OK',
  1765.                 LIVEUSER_STATUS_IDLED           => 'Maximum idle time is reached',
  1766.                 LIVEUSER_STATUS_EXPIRED         => 'User session has expired',
  1767.                 LIVEUSER_STATUS_ISINACTIVE      => 'User account is inactive',
  1768.                 LIVEUSER_STATUS_PERMINITERROR   => 'Cannot instantiate permission container',
  1769.                 LIVEUSER_STATUS_AUTHINITERROR   => 'Cannot instantiate the authentication container, this is a configuration problem',
  1770.                 LIVEUSER_STATUS_AUTHNOTFOUND    => 'Cannot retrieve Auth object from session',
  1771.                 LIVEUSER_STATUS_UNKNOWN         => 'An undefined error occurred or init() was not called',
  1772.                 LIVEUSER_STATUS_LOGGEDOUT       => 'User was logged out correctly',
  1773.                 LIVEUSER_STATUS_AUTHFAILED      => 'Authentication failed, handle/password is probably wrong',
  1774.                 LIVEUSER_STATUS_UNFROZEN        => 'Object fetched from the session, the user was already logged in',
  1775.                 LIVEUSER_STATUS_EMPTY_HANDLE    => 'No handle was passed to LiveUser directly or indirectly',
  1776.             );
  1777.         }
  1778.  
  1779.         if (is_null($value)) {
  1780.             return $statusMessages;
  1781.         }
  1782.  
  1783.         // return the textual error message corresponding to the code
  1784.         return array_key_exists($value$statusMessages)
  1785.             ? $statusMessages[$value$statusMessages[LIVEUSER_STATUS_UNKNOWN];
  1786.     }
  1787. }
  1788. ?>

Documentation generated on Mon, 28 Jan 2008 03:30:20 -0500 by phpDocumentor 1.4.0. PEAR Logo Copyright © PHP Group 2004.