eZ Publish  [4.1]
ezuser.php
Go to the documentation of this file.
00001 <?php
00002 //
00003 // Definition of eZUser class
00004 //
00005 // Created on: <10-Jun-2002 17:03:15 bf>
00006 //
00007 // ## BEGIN COPYRIGHT, LICENSE AND WARRANTY NOTICE ##
00008 // SOFTWARE NAME: eZ Publish
00009 // SOFTWARE RELEASE: 4.1.x
00010 // COPYRIGHT NOTICE: Copyright (C) 1999-2009 eZ Systems AS
00011 // SOFTWARE LICENSE: GNU General Public License v2.0
00012 // NOTICE: >
00013 //   This program is free software; you can redistribute it and/or
00014 //   modify it under the terms of version 2.0  of the GNU General
00015 //   Public License as published by the Free Software Foundation.
00016 //
00017 //   This program is distributed in the hope that it will be useful,
00018 //   but WITHOUT ANY WARRANTY; without even the implied warranty of
00019 //   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00020 //   GNU General Public License for more details.
00021 //
00022 //   You should have received a copy of version 2.0 of the GNU General
00023 //   Public License along with this program; if not, write to the Free
00024 //   Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
00025 //   MA 02110-1301, USA.
00026 //
00027 //
00028 // ## END COPYRIGHT, LICENSE AND WARRANTY NOTICE ##
00029 //
00030 
00031 /*!
00032   \class eZUser ezuser.php
00033   \brief eZUser handles eZ Publish user accounts
00034   \ingroup eZDatatype
00035 
00036 */
00037 
00038 class eZUser extends eZPersistentObject
00039 {
00040     /// MD5 of password
00041     const PASSWORD_HASH_MD5_PASSWORD = 1;
00042     /// MD5 of user and password
00043     const PASSWORD_HASH_MD5_USER = 2;
00044     /// MD5 of site, user and password
00045     const PASSWORD_HASH_MD5_SITE = 3;
00046     /// Legacy support for mysql hashed passwords
00047     const PASSWORD_HASH_MYSQL = 4;
00048     /// Passwords in plaintext, should not be used for real sites
00049     const PASSWORD_HASH_PLAINTEXT = 5;
00050     // Crypted passwords
00051     const PASSWORD_HASH_CRYPT = 6;
00052 
00053     /// Authenticate by matching the login field
00054     const AUTHENTICATE_LOGIN = 1;
00055     /// Authenticate by matching the email field
00056     const AUTHENTICATE_EMAIL = 2;
00057 
00058     const AUTHENTICATE_ALL = 3; //EZ_USER_AUTHENTICATE_LOGIN | EZ_USER_AUTHENTICATE_EMAIL;
00059 
00060     protected static $anonymousId = null;
00061 
00062     function eZUser( $row )
00063     {
00064         $this->eZPersistentObject( $row );
00065         $this->OriginalPassword = false;
00066         $this->OriginalPasswordConfirm = false;
00067     }
00068 
00069     static function definition()
00070     {
00071         return array( 'fields' => array( 'contentobject_id' => array( 'name' => 'ContentObjectID',
00072                                                                       'datatype' => 'integer',
00073                                                                       'default' => 0,
00074                                                                       'required' => true,
00075                                                                       'foreign_class' => 'eZContentObject',
00076                                                                       'foreign_attribute' => 'id',
00077                                                                       'multiplicity' => '0..1' ),
00078                                          'login' => array( 'name' => 'Login',
00079                                                            'datatype' => 'string',
00080                                                            'default' => '',
00081                                                            'required' => true ),
00082                                          'email' => array( 'name' => 'Email',
00083                                                            'datatype' => 'string',
00084                                                            'default' => '',
00085                                                            'required' => true ),
00086                                          'password_hash' => array( 'name' => 'PasswordHash',
00087                                                                    'datatype' => 'string',
00088                                                                    'default' => '',
00089                                                                    'required' => true ),
00090                                          'password_hash_type' => array( 'name' => 'PasswordHashType',
00091                                                                         'datatype' => 'integer',
00092                                                                         'default' => 1,
00093                                                                         'required' => true ) ),
00094                       'keys' => array( 'contentobject_id' ),
00095                       'sort' => array( 'contentobject_id' => 'asc' ),
00096                       'function_attributes' => array( 'contentobject' => 'contentObject',
00097                                                       'groups' => 'groups',
00098                                                       'has_stored_login' => 'hasStoredLogin',
00099                                                       'original_password' => 'originalPassword',
00100                                                       'original_password_confirm' => 'originalPasswordConfirm',
00101                                                       'roles' => 'roles',
00102                                                       'role_id_list' => 'roleIDList',
00103                                                       'limited_assignment_value_list' => 'limitValueList',
00104                                                       'is_logged_in' => 'isLoggedIn',
00105                                                       'is_enabled' => 'isEnabled',
00106                                                       'is_locked' => 'isLocked',
00107                                                       'last_visit' => 'lastVisit',
00108                                                       'login_count' => 'loginCount',
00109                                                       'has_manage_locations' => 'hasManageLocations' ),
00110                       'relations' => array( 'contentobject_id' => array( 'class' => 'ezcontentobject',
00111                                                                          'field' => 'id' ) ),
00112                       'class_name' => 'eZUser',
00113                       'name' => 'ezuser' );
00114     }
00115 
00116     /*!
00117      \return a textual identifier for the hash type $id
00118     */
00119     static function passwordHashTypeName( $id )
00120     {
00121         switch ( $id )
00122         {
00123             case self::PASSWORD_HASH_MD5_PASSWORD:
00124             {
00125                 return 'md5_password';
00126             } break;
00127             case self::PASSWORD_HASH_MD5_USER:
00128             {
00129                 return 'md5_user';
00130             } break;
00131             case self::PASSWORD_HASH_MD5_SITE:
00132             {
00133                 return 'md5_site';
00134             } break;
00135             case self::PASSWORD_HASH_MYSQL:
00136             {
00137                 return 'mysql';
00138             } break;
00139             case self::PASSWORD_HASH_PLAINTEXT:
00140             {
00141                 return 'plaintext';
00142             } break;
00143             case self::PASSWORD_HASH_CRYPT:
00144             {
00145                 return 'crypt';
00146             } break;
00147         }
00148     }
00149 
00150     /*!
00151      \return the hash type for the textual identifier $identifier
00152     */
00153     static function passwordHashTypeID( $identifier )
00154     {
00155         switch ( $identifier )
00156         {
00157             case 'md5_password':
00158             {
00159                 return self::PASSWORD_HASH_MD5_PASSWORD;
00160             } break;
00161             default:
00162             case 'md5_user':
00163             {
00164                 return self::PASSWORD_HASH_MD5_USER;
00165             } break;
00166             case 'md5_site':
00167             {
00168                 return self::PASSWORD_HASH_MD5_SITE;
00169             } break;
00170             case 'mysql':
00171             {
00172                 return self::PASSWORD_HASH_MYSQL;
00173             } break;
00174             case 'plaintext':
00175             {
00176                 return self::PASSWORD_HASH_PLAINTEXT;
00177             } break;
00178             case 'crypt':
00179             {
00180                 return self::PASSWORD_HASH_CRYPT;
00181             } break;
00182         }
00183     }
00184 
00185     /*!
00186      Check if current user has "content/manage_locations" access
00187     */
00188     function hasManageLocations()
00189     {
00190         $accessResult = $this->hasAccessTo( 'content', 'manage_locations' );
00191         if ( $accessResult['accessWord'] != 'no' )
00192         {
00193             return true;
00194         }
00195 
00196         return false;
00197     }
00198 
00199     static function create( $contentObjectID )
00200     {
00201         $row = array(
00202             'contentobject_id' => $contentObjectID,
00203             'login' => null,
00204             'email' => null,
00205             'password_hash' => null,
00206             'password_hash_type' => null
00207             );
00208         return new eZUser( $row );
00209     }
00210 
00211     function store( $fieldFilters = null )
00212     {
00213         $this->Email = trim( $this->Email );
00214         eZExpiryHandler::registerShutdownFunction();
00215         $handler = eZExpiryHandler::instance();
00216         $handler->setTimestamp( 'user-info-cache', time() );
00217         $handler->setTimestamp( 'user-groups-cache', time() );
00218         $handler->setTimestamp( 'user-access-cache', time() );
00219         $handler->store();
00220         $userID = $this->attribute( 'contentobject_id' );
00221         // Clear memory cache
00222         unset( $GLOBALS['eZUserObject_' . $userID] );
00223         $GLOBALS['eZUserObject_' . $userID] = $this;
00224         eZPersistentObject::store( $fieldFilters );
00225     }
00226 
00227     function originalPassword()
00228     {
00229         return $this->OriginalPassword;
00230     }
00231 
00232     function setOriginalPassword( $password )
00233     {
00234         $this->OriginalPassword = $password;
00235     }
00236 
00237     function originalPasswordConfirm()
00238     {
00239         return $this->OriginalPasswordConfirm;
00240     }
00241 
00242     function setOriginalPasswordConfirm( $password )
00243     {
00244         $this->OriginalPasswordConfirm = $password;
00245     }
00246 
00247     function hasStoredLogin()
00248     {
00249         $db = eZDB::instance();
00250         $contentObjectID = $this->attribute( 'contentobject_id' );
00251         $sql = "SELECT * FROM ezuser WHERE contentobject_id='$contentObjectID' AND LENGTH( login ) > 0";
00252         $rows = $db->arrayQuery( $sql );
00253         return count( $rows ) > 0;
00254     }
00255 
00256     /*!
00257      Fills in the \a $id, \a $login, \a $email and \a $password for the user
00258      and creates the proper password hash.
00259     */
00260     function setInformation( $id, $login, $email, $password, $passwordConfirm = false )
00261     {
00262         $this->setAttribute( "contentobject_id", $id );
00263         $this->setAttribute( "email", $email );
00264         $this->setAttribute( "login", $login );
00265         if ( eZUser::validatePassword( $password ) and
00266              $password == $passwordConfirm ) // Cannot change login or password_hash without login and password
00267         {
00268             $this->setAttribute( "password_hash", eZUser::createHash( $login, $password, eZUser::site(),
00269                                                                       eZUser::hashType() ) );
00270             $this->setAttribute( "password_hash_type", eZUser::hashType() );
00271         }
00272         else
00273         {
00274             $this->setOriginalPassword( $password );
00275             $this->setOriginalPasswordConfirm( $passwordConfirm );
00276         }
00277     }
00278 
00279     static function fetch( $id, $asObject = true )
00280     {
00281         if ( !$id )
00282             return null;
00283         return eZPersistentObject::fetchObject( eZUser::definition(),
00284                                                 null,
00285                                                 array( 'contentobject_id' => $id ),
00286                                                 $asObject );
00287     }
00288 
00289     static function fetchByName( $login, $asObject = true )
00290     {
00291         return eZPersistentObject::fetchObject( eZUser::definition(),
00292                                                 null,
00293                                                 array( 'LOWER( login )' => strtolower( $login ) ),
00294                                                 $asObject );
00295     }
00296 
00297     static function fetchByEmail( $email, $asObject = true )
00298     {
00299         return eZPersistentObject::fetchObject( eZUser::definition(),
00300                                                   null,
00301                                                   array( 'LOWER( email )' => strtolower( $email ) ),
00302                                                   $asObject );
00303     }
00304 
00305     /*!
00306      \static
00307      \return a list of the logged in users.
00308      \param $asObject If false it will return a list with only the names of the users as elements and user ID as key,
00309                       otherwise each entry is a eZUser object.
00310      \sa fetchLoggedInCount
00311     */
00312     static function fetchLoggedInList( $asObject = false, $offset = false, $limit = false, $sortBy = false )
00313     {
00314         $db = eZDB::instance();
00315         $time = time();
00316         $ini = eZINI::instance();
00317         $activityTimeout = $ini->variable( 'Session', 'ActivityTimeout' );
00318         $sessionTimeout = $ini->variable( 'Session', 'SessionTimeout' );
00319         $time = $time + $sessionTimeout - $activityTimeout;
00320 
00321         $parameters = array();
00322         if ( $offset )
00323             $parameters['offset'] =(int) $offset;
00324         if ( $limit )
00325             $parameters['limit'] =(int) $limit;
00326         $sortText = '';
00327         if ( $asObject )
00328         {
00329             $selectArray = array( "distinct ezuser.*" );
00330         }
00331         else
00332         {
00333             $selectArray = array( "ezuser.contentobject_id as user_id", "ezcontentobject.name" );
00334         }
00335         if ( $sortBy !== false )
00336         {
00337             $sortElements = array();
00338             if ( !is_array( $sortBy ) )
00339             {
00340                 $sortBy = array( array( $sortBy, true ) );
00341             }
00342             else if ( !is_array( $sortBy[0] ) )
00343                 $sortBy = array( $sortBy );
00344             $sortColumns = array();
00345             foreach ( $sortBy as $sortElements )
00346             {
00347                 $sortColumn = $sortElements[0];
00348                 $sortOrder = $sortElements[1];
00349                 $orderText = $sortOrder ? 'asc' : 'desc';
00350                 switch ( $sortColumn )
00351                 {
00352                     case 'user_id':
00353                     {
00354                         $sortColumn = "ezuser.contentobject_id $orderText";
00355                     } break;
00356 
00357                     case 'login':
00358                     {
00359                         $sortColumn = "ezuser.login $orderText";
00360                     } break;
00361 
00362                     case 'activity':
00363                     {
00364                         $selectArray[] = "( ezsession.expiration_time -  " . ( $sessionTimeout - $activityTimeout ) . " ) AS activity";
00365                         $sortColumn = "activity $orderText";
00366                     } break;
00367 
00368                     case 'email':
00369                     {
00370                         $sortColumn = "ezuser.email $orderText";
00371                     } break;
00372 
00373                     default:
00374                     {
00375                         eZDebug::writeError( "Unkown sort column '$sortColumn'", 'eZUser::fetchLoggedInList' );
00376                         $sortColumn = false;
00377                     } break;
00378                 }
00379                 if ( $sortColumn )
00380                     $sortColumns[] = $sortColumn;
00381             }
00382             if ( count( $sortColumns ) > 0 )
00383                 $sortText = "ORDER BY " . implode( ', ', $sortColumns );
00384         }
00385         if ( $asObject )
00386         {
00387             $selectText = implode( ', ',  $selectArray );
00388             $sql = "SELECT $selectText
00389 FROM ezsession, ezuser
00390 WHERE ezsession.user_id != '" . self::anonymousId() . "' AND
00391       ezsession.expiration_time > '$time' AND
00392       ezuser.contentobject_id = ezsession.user_id
00393 $sortText";
00394             $rows = $db->arrayQuery( $sql, $parameters );
00395             $list = array();
00396             foreach ( $rows as $row )
00397             {
00398                 $list[] = new eZUser( $row );
00399             }
00400         }
00401         else
00402         {
00403             $selectText = implode( ', ',  $selectArray );
00404             $sql = "SELECT $selectText
00405 FROM ezsession, ezuser, ezcontentobject
00406 WHERE ezsession.user_id != '" . self::anonymousId() . "' AND
00407       ezsession.expiration_time > '$time' AND
00408       ezuser.contentobject_id = ezsession.user_id AND
00409       ezcontentobject.id = ezuser.contentobject_id
00410 $sortText";
00411             $rows = $db->arrayQuery( $sql, $parameters );
00412             $list = array();
00413             foreach ( $rows as $row )
00414             {
00415                 $list[$row['user_id']] = $row['name'];
00416             }
00417         }
00418         return $list;
00419     }
00420 
00421     /*!
00422      \return the number of logged in users in the system.
00423      \note The count will be cached for the current page if caching is allowed.
00424      \sa fetchAnonymousCount
00425     */
00426     static function fetchLoggedInCount()
00427     {
00428         if ( isset( $GLOBALS['eZSiteBasics']['no-cache-adviced'] ) and
00429              !$GLOBALS['eZSiteBasics']['no-cache-adviced'] and
00430              isset( $GLOBALS['eZUserLoggedInCount'] ) )
00431             return $GLOBALS['eZUserLoggedInCount'];
00432         $db = eZDB::instance();
00433         $time = time();
00434         $ini = eZINI::instance();
00435         $activityTimeout = $ini->variable( 'Session', 'ActivityTimeout' );
00436         $sessionTimeout = $ini->variable( 'Session', 'SessionTimeout' );
00437         $time = $time + $sessionTimeout - $activityTimeout;
00438 
00439         $sql = "SELECT count( DISTINCT user_id ) as count
00440 FROM ezsession
00441 WHERE user_id != '" . self::anonymousId() . "' AND
00442       user_id > 0 AND
00443       expiration_time > '$time'";
00444         $rows = $db->arrayQuery( $sql );
00445         $count = ( count( $rows ) > 0 ) ? $rows[0]['count'] : 0;
00446         $GLOBALS['eZUserLoggedInCount'] = $count;
00447         return $count;
00448     }
00449 
00450     /*!
00451      \static
00452      \return the number of anonymous users in the system.
00453      \sa fetchLoggedInCount
00454     */
00455     static function fetchAnonymousCount()
00456     {
00457         if ( isset( $GLOBALS['eZSiteBasics']['no-cache-adviced'] ) and
00458              !$GLOBALS['eZSiteBasics']['no-cache-adviced'] and
00459              isset( $GLOBALS['eZUserAnonymousCount'] ) )
00460             return $GLOBALS['eZUserAnonymousCount'];
00461         $db = eZDB::instance();
00462         $time = time();
00463         $ini = eZINI::instance();
00464         $activityTimeout = $ini->variable( 'Session', 'ActivityTimeout' );
00465         $sessionTimeout = $ini->variable( 'Session', 'SessionTimeout' );
00466         $time = $time + $sessionTimeout - $activityTimeout;
00467 
00468         $sql = "SELECT count( session_key ) as count
00469 FROM ezsession
00470 WHERE user_id = '" . self::anonymousId() . "' AND
00471       expiration_time > '$time'";
00472         $rows = $db->arrayQuery( $sql );
00473         $count = ( count( $rows ) > 0 ) ? $rows[0]['count'] : 0;
00474         $GLOBALS['eZUserAnonymousCount'] = $count;
00475         return $count;
00476     }
00477 
00478     /*!
00479      \static
00480      \return true if the user with ID $userID is currently logged into the system.
00481      \note The information will be cached for the current page if caching is allowed.
00482      \sa fetchLoggedInList
00483     */
00484     static function isUserLoggedIn( $userID )
00485     {
00486         $userID = (int)$userID;
00487         if ( isset( $GLOBALS['eZSiteBasics']['no-cache-adviced'] ) and
00488              !$GLOBALS['eZSiteBasics']['no-cache-adviced'] and
00489              isset( $GLOBALS['eZUserLoggedInMap'][$userID] ) )
00490             return $GLOBALS['eZUserLoggedInMap'][$userID];
00491         $db = eZDB::instance();
00492         $time = time();
00493         $ini = eZINI::instance();
00494         $activityTimeout = $ini->variable( 'Session', 'ActivityTimeout' );
00495         $sessionTimeout = $ini->variable( 'Session', 'SessionTimeout' );
00496         $time = $time + $sessionTimeout - $activityTimeout;
00497 
00498         $sql = "SELECT DISTINCT user_id
00499 FROM ezsession
00500 WHERE user_id = '" . $userID . "' AND
00501       expiration_time > '$time'";
00502         $rows = $db->arrayQuery( $sql, array( 'limit' => 2 ) );
00503         $isLoggedIn = count( $rows ) > 0;
00504         $GLOBALS['eZUserLoggedInMap'][$userID] = $isLoggedIn;
00505         return $isLoggedIn;
00506     }
00507 
00508     /*!
00509      \static
00510      Removes any cached session information, this is:
00511      - logged in user count
00512      - anonymous user count
00513      - logged in user map
00514     */
00515     static function clearSessionCache()
00516     {
00517         unset( $GLOBALS['eZUserLoggedInCount'] );
00518         unset( $GLOBALS['eZUserAnonymousCount'] );
00519         unset( $GLOBALS['eZUserLoggedInMap'] );
00520     }
00521 
00522     /*!
00523      \static
00524      Remove session data for user \a $userID.
00525     */
00526     static function removeSessionData( $userID )
00527     {
00528         eZUser::clearSessionCache();
00529         $db = eZDB::instance();
00530         $userID = (int)$userID;
00531         $db->query( 'DELETE FROM ezsession WHERE user_id = \'' . $userID . '\'' );
00532     }
00533 
00534     /*!
00535      Removes the user from the ezuser table.
00536      \note Will also remove any notifications and session related to the user.
00537     */
00538     static function removeUser( $userID )
00539     {
00540         $user = eZUser::fetch( $userID );
00541         if ( !$user )
00542         {
00543             eZDebug::writeError( "unable to find user with ID $userID", 'eZUser::removeUser()' );
00544             return false;
00545         }
00546 
00547         eZUser::removeSessionData( $userID );
00548 
00549         eZSubtreeNotificationRule::removeByUserID( $userID );
00550         eZCollaborationNotificationRule::removeByUserID( $userID );
00551         eZUserSetting::removeByUserID( $userID );
00552         eZUserAccountKey::removeByUserID( $userID );
00553         eZForgotPassword::removeByUserID( $userID );
00554         eZWishList::removeByUserID( $userID );
00555 
00556         // only remove general digest setting if there are no other users with the same e-mail
00557         $email = $user->attribute( 'email' );
00558         $usersWithEmailCount = eZPersistentObject::count( eZUser::definition(), array( 'email' => $email ) );
00559         if ( $usersWithEmailCount == 1 )
00560         {
00561             eZGeneralDigestUserSettings::removeByAddress( $email );
00562         }
00563 
00564         eZPersistentObject::removeObject( eZUser::definition(),
00565                                           array( 'contentobject_id' => $userID ) );
00566         return true;
00567     }
00568 
00569     /*!
00570      \return a list of valid and enabled users, the data returned is an array
00571              with ezcontentobject database data.
00572     */
00573     static function fetchContentList()
00574     {
00575         $contentObjectStatus = eZContentObject::STATUS_PUBLISHED;
00576         $query = "SELECT ezcontentobject.*
00577                   FROM ezuser, ezcontentobject, ezuser_setting
00578                   WHERE ezcontentobject.status = '$contentObjectStatus' AND
00579                         ezuser_setting.is_enabled = 1 AND
00580                         ezcontentobject.id = ezuser.contentobject_id AND
00581                         ezuser_setting.user_id = ezuser.contentobject_id";
00582         $db = eZDB::instance();
00583         $rows = $db->arrayQuery( $query );
00584         return $rows;
00585     }
00586 
00587     /*!
00588      \static
00589      \return the default hash type which is specified in UserSettings/HashType in site.ini
00590     */
00591     static function hashType()
00592     {
00593         $ini = eZINI::instance();
00594         $type = strtolower( $ini->variable( 'UserSettings', 'HashType' ) );
00595         if ( $type == 'md5_site' )
00596             return self::PASSWORD_HASH_MD5_SITE;
00597         else if ( $type == 'md5_user' )
00598             return self::PASSWORD_HASH_MD5_USER;
00599         else if ( $type == 'plaintext' )
00600             return self::PASSWORD_HASH_PLAINTEXT;
00601         else if ( $type == 'crypt' )
00602             return self::PASSWORD_HASH_CRYPT;
00603         else
00604             return self::PASSWORD_HASH_MD5_PASSWORD;
00605     }
00606 
00607     /*!
00608      \static
00609      \return the site name used in password hashing.
00610     */
00611     static function site()
00612     {
00613         $ini = eZINI::instance();
00614         return $ini->variable( 'UserSettings', 'SiteName' );
00615     }
00616 
00617     /*!
00618      Fetches a builtin user and returns it, this helps avoid special cases where
00619      user is not logged in.
00620     */
00621     static function fetchBuiltin( $id )
00622     {
00623         if ( !in_array( $id, $GLOBALS['eZUserBuiltins'] ) )
00624             $id = self::anonymousId();
00625         if ( empty( $GLOBALS["eZUserBuilitinInstance-$id"] ) )
00626         {
00627             $GLOBALS["eZUserBuilitinInstance-$id"] = eZUser::fetch( self::anonymousId() );
00628         }
00629         return $GLOBALS["eZUserBuilitinInstance-$id"];
00630     }
00631 
00632 
00633     /*!
00634      \return the user id.
00635     */
00636     function id()
00637     {
00638         return $this->ContentObjectID;
00639     }
00640 
00641     /*!
00642      \return a bitfield which decides the authenticate methods.
00643     */
00644     static function authenticationMatch()
00645     {
00646         $ini = eZINI::instance();
00647         $matchArray = $ini->variableArray( 'UserSettings', 'AuthenticateMatch' );
00648         $match = 0;
00649         foreach ( $matchArray as $matchItem )
00650         {
00651             switch ( $matchItem )
00652             {
00653                 case "login":
00654                 {
00655                     $match = ( $match | self::AUTHENTICATE_LOGIN );
00656                 } break;
00657                 case "email":
00658                 {
00659                     $match = ( $match | self::AUTHENTICATE_EMAIL );
00660                 } break;
00661             }
00662         }
00663         return $match;
00664     }
00665 
00666     /*!
00667      \return \c true if there can only be one instance of an email address on the site.
00668     */
00669     static function requireUniqueEmail()
00670     {
00671         $ini = eZINI::instance();
00672         return $ini->variable( 'UserSettings', 'RequireUniqueEmail' ) == 'true';
00673     }
00674 
00675     /**
00676      * Logs in the user if applied username and password is valid.
00677      *
00678      * @param string $login
00679      * @param string $password
00680      * @param bool $authenticationMatch
00681      * @return mixed eZUser on success, bool false on failure
00682      */
00683     public static function loginUser( $login, $password, $authenticationMatch = false )
00684     {
00685         $user = self::_loginUser( $login, $password, $authenticationMatch );
00686 
00687         if ( is_object( $user ) )
00688         {
00689             self::loginSucceeded( $user );
00690             return $user;
00691         }
00692         else
00693         {
00694             self::loginFailed( $user, $login );
00695             return false;
00696         }
00697     }
00698 
00699     /**
00700      * Does some house keeping work once a log in has succeeded.
00701      *
00702      * @param eZUser $user
00703      */
00704     protected static function loginSucceeded( $user )
00705     {
00706         $userID = $user->attribute( 'contentobject_id' );
00707 
00708         // if audit is enabled logins should be logged
00709         eZAudit::writeAudit( 'user-login', array( 'User id' => $userID, 'User login' => $user->attribute( 'login' ) ) );
00710 
00711         eZUser::updateLastVisit( $userID, true );
00712         eZUser::setCurrentlyLoggedInUser( $user, $userID );
00713 
00714         // Reset number of failed login attempts
00715         eZUser::setFailedLoginAttempts( $userID, 0 );
00716     }
00717 
00718     /**
00719      * Does some house keeping work when a log in has failed.
00720      *
00721      * @param mixed $userID
00722      * @param string $login
00723      */
00724      protected static function loginFailed( $userID = false, $login )
00725     {
00726         $loginEscaped = eZDB::instance()->escapeString( $login );
00727 
00728         // Failed login attempts should be logged
00729         eZAudit::writeAudit( 'user-failed-login', array( 'User login' => $loginEscaped,
00730                                                          'Comment' => 'Failed login attempt: eZUser::loginUser()' ) );
00731 
00732         // Increase number of failed login attempts.
00733         if ( $userID )
00734             eZUser::setFailedLoginAttempts( $userID );
00735     }
00736 
00737     /**
00738      * Logs in an user if applied login and password is valid.
00739      *
00740      * This method does not do any house keeping work anymore (writing audits, etc).
00741      * When you call this method make sure to call loginSucceeded() or loginFailed()
00742      * depending on the success of the login.
00743      *
00744      * @param string $login
00745      * @param string $password
00746      * @param bool $authenticationMatch
00747      * @return mixed eZUser object on log in success, int userID if the username
00748      *         exists but log in failed, or false if the username doesn't exists.
00749      */
00750     protected static function _loginUser( $login, $password, $authenticationMatch = false )
00751     {
00752         $http = eZHTTPTool::instance();
00753         $db = eZDB::instance();
00754 
00755         if ( $authenticationMatch === false )
00756             $authenticationMatch = eZUser::authenticationMatch();
00757 
00758         $loginEscaped = $db->escapeString( $login );
00759         $passwordEscaped = $db->escapeString( $password );
00760 
00761         $loginArray = array();
00762         if ( $authenticationMatch & self::AUTHENTICATE_LOGIN )
00763             $loginArray[] = "login='$loginEscaped'";
00764         if ( $authenticationMatch & self::AUTHENTICATE_EMAIL )
00765         {
00766             if ( eZMail::validate( $login ) )
00767             {
00768                 $loginArray[] = "email='$loginEscaped'";
00769             }
00770         }
00771         if ( count( $loginArray ) == 0 )
00772             $loginArray[] = "login='$loginEscaped'";
00773         $loginText = implode( ' OR ', $loginArray );
00774 
00775         $contentObjectStatus = eZContentObject::STATUS_PUBLISHED;
00776 
00777         $ini = eZINI::instance();
00778         $databaseName = $db->databaseName();
00779         // if mysql
00780         if ( $databaseName === 'mysql' )
00781         {
00782             $query = "SELECT contentobject_id, password_hash, password_hash_type, email, login
00783                       FROM ezuser, ezcontentobject
00784                       WHERE ( $loginText ) AND
00785                         ezcontentobject.status='$contentObjectStatus' AND
00786                         ezcontentobject.id=contentobject_id AND
00787                         ( ( password_hash_type!=4 ) OR
00788                           ( password_hash_type=4 AND
00789                               ( $loginText ) AND
00790                           password_hash=PASSWORD('$passwordEscaped') ) )";
00791         }
00792         else
00793         {
00794             $query = "SELECT contentobject_id, password_hash,
00795                              password_hash_type, email, login
00796                       FROM   ezuser, ezcontentobject
00797                       WHERE  ( $loginText )
00798                       AND    ezcontentobject.status='$contentObjectStatus'
00799                       AND    ezcontentobject.id=contentobject_id";
00800         }
00801 
00802         $users = $db->arrayQuery( $query );
00803         $exists = false;
00804         if ( $users !== false and count( $users ) >= 1 )
00805         {
00806             $ini = eZINI::instance();
00807             foreach ( $users as $userRow )
00808             {
00809                 $userID = $userRow['contentobject_id'];
00810                 $hashType = $userRow['password_hash_type'];
00811                 $hash = $userRow['password_hash'];
00812                 $exists = eZUser::authenticateHash( $userRow['login'], $password, eZUser::site(),
00813                                                     $hashType,
00814                                                     $hash );
00815 
00816                 // If hash type is MySql
00817                 if ( $hashType == self::PASSWORD_HASH_MYSQL and $databaseName === 'mysql' )
00818                 {
00819                     $queryMysqlUser = "SELECT contentobject_id, password_hash, password_hash_type, email, login
00820                               FROM ezuser, ezcontentobject
00821                               WHERE ezcontentobject.status='$contentObjectStatus' AND
00822                                     password_hash_type=4 AND ( $loginText ) AND password_hash=PASSWORD('$passwordEscaped') ";
00823                     $mysqlUsers = $db->arrayQuery( $queryMysqlUser );
00824                     if ( count( $mysqlUsers ) >= 1 )
00825                         $exists = true;
00826 
00827                 }
00828 
00829                 eZDebugSetting::writeDebug( 'kernel-user', eZUser::createHash( $userRow['login'], $password, eZUser::site(),
00830                                                                                $hashType, $hash ), "check hash" );
00831                 eZDebugSetting::writeDebug( 'kernel-user', $hash, "stored hash" );
00832                  // If current user has been disabled after a few failed login attempts.
00833                 $canLogin = eZUser::isEnabledAfterFailedLogin( $userID );
00834 
00835                 if ( $exists )
00836                 {
00837                     // We should store userID for warning message.
00838                     $GLOBALS['eZFailedLoginAttemptUserID'] = $userID;
00839 
00840                     $userSetting = eZUserSetting::fetch( $userID );
00841                     $isEnabled = $userSetting->attribute( "is_enabled" );
00842                     if ( $hashType != eZUser::hashType() and
00843                          strtolower( $ini->variable( 'UserSettings', 'UpdateHash' ) ) == 'true' )
00844                     {
00845                         $hashType = eZUser::hashType();
00846                         $hash = eZUser::createHash( $userRow['login'], $password, eZUser::site(),
00847                                                     $hashType );
00848                         $db->query( "UPDATE ezuser SET password_hash='$hash', password_hash_type='$hashType' WHERE contentobject_id='$userID'" );
00849                     }
00850                     break;
00851                 }
00852             }
00853         }
00854 
00855         if ( $exists and $isEnabled and $canLogin )
00856         {
00857             return new eZUser( $userRow );
00858         }
00859         else
00860         {
00861             return isset( $userID ) ? $userID : false;
00862         }
00863     }
00864 
00865     /*!
00866      \static
00867      Checks if IP address of current user is in \a $ipList.
00868     */
00869     static function isUserIPInList( $ipList )
00870     {
00871         $ipAddress = eZSys::serverVariable( 'REMOTE_ADDR', true );
00872         if ( $ipAddress )
00873         {
00874             $result = false;
00875             foreach( $ipList as $itemToMatch )
00876             {
00877                 if ( preg_match("/^(([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+))(\/([0-9]+)$|$)/", $itemToMatch, $matches ) )
00878                 {
00879                     if ( $matches[6] )
00880                     {
00881                         if ( eZDebug::isIPInNet( $ipAddress, $matches[1], $matches[7] ) )
00882                         {
00883                             $result = true;
00884                             break;
00885                         }
00886                     }
00887                     else
00888                     {
00889                         if ( $matches[1] == $ipAddress )
00890                         {
00891                             $result = true;
00892                             break;
00893                         }
00894                     }
00895                 }
00896             }
00897         }
00898         else
00899         {
00900             $result = (
00901                 in_array( 'commandline', $ipList ) &&
00902                 ( php_sapi_name() == 'cli' )
00903             );
00904         }
00905         return $result;
00906     }
00907 
00908     /*!
00909      \static
00910       Returns true if current user is trusted user.
00911     */
00912     static function isTrusted()
00913     {
00914         $ini = eZINI::instance();
00915 
00916         // Check if current user is trusted user.
00917         $trustedIPs = $ini->hasVariable( 'UserSettings', 'TrustedIPList' ) ? $ini->variable( 'UserSettings', 'TrustedIPList' ) : array();
00918 
00919         // Check if IP address of current user is in $trustedIPs array.
00920         $trustedUser = eZUser::isUserIPInList( $trustedIPs );
00921         if ( $trustedUser )
00922             return true;
00923 
00924         return false;
00925     }
00926 
00927     /*!
00928      \static
00929      Returns max number of failed login attempts.
00930     */
00931     static function maxNumberOfFailedLogin()
00932     {
00933         $ini = eZINI::instance();
00934 
00935         $maxNumberOfFailedLogin = $ini->hasVariable( 'UserSettings', 'MaxNumberOfFailedLogin' ) ? $ini->variable( 'UserSettings', 'MaxNumberOfFailedLogin' ) : '0';
00936         return $maxNumberOfFailedLogin;
00937     }
00938 
00939     /*
00940      \static
00941      Returns true if the user can login
00942      If user has number of failed login attempts more than eZUser::maxNumberOfFailedLogin()
00943      and user is not trusted
00944      the user will not be allowed to login.
00945     */
00946     static function isEnabledAfterFailedLogin( $userID, $ignoreTrusted = false )
00947     {
00948         if ( !is_numeric( $userID ) )
00949             return true;
00950 
00951         $userObject = eZUser::fetch( $userID );
00952         if ( !$userObject )
00953             return true;
00954 
00955         $trustedUser = eZUser::isTrusted();
00956         // If user is trusted we should stop processing
00957         if ( $trustedUser and !$ignoreTrusted )
00958             return true;
00959 
00960         $maxNumberOfFailedLogin = eZUser::maxNumberOfFailedLogin();
00961 
00962         if ( $maxNumberOfFailedLogin == '0' )
00963             return true;
00964 
00965         $failedLoginAttempts = $userObject->failedLoginAttempts();
00966         if ( $failedLoginAttempts > $maxNumberOfFailedLogin )
00967             return false;
00968 
00969         return true;
00970     }
00971 
00972     /*!
00973      \protected
00974      Makes sure the user \a $user is set as the currently logged in user by
00975      updating the session and setting the necessary global variables.
00976 
00977      All login handlers should use this function to ensure that the process
00978      is executed properly.
00979     */
00980     static function setCurrentlyLoggedInUser( $user, $userID )
00981     {
00982         $http = eZHTTPTool::instance();
00983 
00984         $GLOBALS["eZUserGlobalInstance_$userID"] = $user;
00985         // Set/overwrite the global user, this will be accessed from
00986         // instance() when there is no ID passed to the function.
00987         $GLOBALS["eZUserGlobalInstance_"] = $user;
00988         eZSession::setUserID( $userID );
00989         $http->setSessionVariable( 'eZUserLoggedInID', $userID );
00990         self::cleanup();
00991         eZSession::regenerate();
00992     }
00993 
00994     /*!
00995      \virtual
00996      Used by login handler to clean up session variables
00997     */
00998     function sessionCleanup()
00999     {
01000     }
01001 
01002     /*!
01003      \static
01004      Cleans up any cache or session variables that are set.
01005      This at least called on login and logout but can be used other places
01006      where you must ensure that the cache user values are refetched.
01007      \param deprecated
01008     */
01009     static function cleanup()
01010     {
01011         $http = eZHTTPTool::instance();
01012         $http->setSessionVariable( 'eZUserGroupsCache_Timestamp', false );
01013         $http->removeSessionVariable( 'eZUserGroupsCache' );
01014 
01015         $http->removeSessionVariable( 'eZUserInfoCache' );
01016 
01017         $http->removeSessionVariable( 'AccessArray' );
01018         $http->removeSessionVariable( 'CanInstantiateClassesCachedForUser' );
01019         $http->removeSessionVariable( 'CanInstantiateClassList' );
01020         $http->removeSessionVariable( 'ClassesCachedForUser' );
01021         $http->removeSessionVariable( 'eZRoleIDList' );
01022         $http->setSessionVariable( 'eZRoleIDList_Timestamp', 0 );
01023         $http->removeSessionVariable( 'eZRoleLimitationValueList' );
01024         $http->setSessionVariable( 'eZRoleLimitationValueList_Timestamp', 0 );
01025 
01026         // Note: This must be done more generic with an internal
01027         //       callback system.
01028         eZPreferences::sessionCleanup();
01029     }
01030 
01031     /*!
01032      \return logs in the current user object
01033     */
01034     function loginCurrent()
01035     {
01036         $this->setCurrentlyLoggedInUser( $this, $this->ContentObjectID );
01037     }
01038 
01039     /*!
01040      \static
01041      Logs out the current user
01042     */
01043     static function logoutCurrent()
01044     {
01045         $http = eZHTTPTool::instance();
01046         $id = false;
01047         $GLOBALS["eZUserGlobalInstance_$id"] = false;
01048         $contentObjectID = $http->sessionVariable( 'eZUserLoggedInID' );
01049 
01050         // reset session data
01051         $newUserID = self::anonymousId();
01052         eZSession::setUserID( $newUserID );
01053         $http->setSessionVariable( 'eZUserLoggedInID', $newUserID );
01054         
01055         // Clear current basket if necessary
01056         $db = eZDB::instance();
01057         $db->begin();
01058         eZBasket::cleanupCurrentBasket();
01059         $db->commit();
01060 
01061         if ( $contentObjectID )
01062             eZUser::cleanup();
01063 
01064         // give user new session id
01065         eZSession::regenerate();
01066         
01067         // set the property used to prevent SSO from running again
01068         self::$userHasLoggedOut = true;
01069     }
01070 
01071     /*!
01072      Finds the user with the id \a $id and returns the unique instance of it.
01073      If the user instance is not created yet it tries to either fetch it from the
01074      database with eZUser::fetch(). If $id is false or the user was not found, the
01075      default user is returned. This is a site.ini setting under UserSettings:AnonymousUserID.
01076      The instance is then returned.
01077      If \a $id is false then the current user is fetched.
01078     */
01079     static function instance( $id = false )
01080     {
01081         if ( !empty( $GLOBALS["eZUserGlobalInstance_$id"] ) )
01082         {
01083             return $GLOBALS["eZUserGlobalInstance_$id"];
01084         }
01085 
01086         $userId = $id;
01087         $currentUser = null;
01088         $http = eZHTTPTool::instance();
01089         // If not specified get the current user
01090         if ( $userId === false )
01091         {
01092             $userId = $http->sessionVariable( 'eZUserLoggedInID' );
01093 
01094             if ( !is_numeric( $userId ) )
01095             {
01096                 $userId = self::anonymousId();
01097                 eZSession::setUserID( $userId );
01098                 $http->setSessionVariable( 'eZUserLoggedInID', $userId );
01099             }
01100         }
01101 
01102         $fetchFromDB = true;
01103 
01104         // Check session cache
01105         eZExpiryHandler::registerShutdownFunction();
01106         $handler = eZExpiryHandler::instance();
01107         $expiredTimeStamp = 0;
01108         if ( $handler->hasTimestamp( 'user-info-cache' ) )
01109             $expiredTimeStamp = $handler->timestamp( 'user-info-cache' );
01110 
01111         $userArrayTimestamp = $http->sessionVariable( 'eZUserInfoCache_Timestamp' );
01112 
01113         if ( $userArrayTimestamp > $expiredTimeStamp )
01114         {
01115             $userInfo = array();
01116             if ( $http->hasSessionVariable( 'eZUserInfoCache' ) )
01117                 $userInfo = $http->sessionVariable( 'eZUserInfoCache' );
01118 
01119             if ( isset( $userInfo[$userId] ) )
01120             {
01121                 $userArray = $userInfo[$userId];
01122 
01123                 if ( is_numeric( $userArray['contentobject_id'] ) )
01124                 {
01125                     $currentUser = new eZUser( $userArray );
01126                     $fetchFromDB = false;
01127                 }
01128             }
01129         }
01130 
01131         if ( $fetchFromDB == true )
01132         {
01133             $currentUser = eZUser::fetch( $userId );
01134 
01135             if ( $currentUser )
01136             {
01137                 $userInfo = array();
01138                 $userInfo[$userId] = array( 'contentobject_id' => $currentUser->attribute( 'contentobject_id' ),
01139                                         'login' => $currentUser->attribute( 'login' ),
01140                                         'email' => $currentUser->attribute( 'email' ),
01141                                         'password_hash' => $currentUser->attribute( 'password_hash' ),
01142                                         'password_hash_type' => $currentUser->attribute( 'password_hash_type' )
01143                                         );
01144                 eZSession::setUserID( $userId );
01145                 $http->setSessionVariable( 'eZUserInfoCache', $userInfo );
01146                 $http->setSessionVariable( 'eZUserInfoCache_Timestamp', time() );
01147             }
01148         }
01149 
01150         $ini = eZINI::instance();
01151 
01152         // Check if:
01153         // - the user has not logged out,
01154         // - the user is not logged in,
01155         // - and if a automatic single sign on plugin is enabled.
01156         if ( !self::$userHasLoggedOut and is_object( $currentUser ) and !$currentUser->isLoggedIn() )
01157         {
01158             $ssoHandlerArray = $ini->variable( 'UserSettings', 'SingleSignOnHandlerArray' );
01159             if ( count( $ssoHandlerArray ) > 0 )
01160             {
01161                 $ssoUser = false;
01162                 foreach ( $ssoHandlerArray as $ssoHandler )
01163                 {
01164                     // Load handler
01165                     $handlerFile = 'kernel/classes/ssohandlers/ez' . strtolower( $ssoHandler ) . 'ssohandler.php';
01166                     if ( file_exists( $handlerFile ) )
01167                     {
01168                         include_once( $handlerFile );
01169                         $className = 'eZ' . $ssoHandler . 'SSOHandler';
01170                         $impl = new $className();
01171                         $ssoUser = $impl->handleSSOLogin();
01172                     }
01173                     else // check in extensions
01174                     {
01175                         $ini = eZINI::instance();
01176                         $extensionDirectories = $ini->variable( 'UserSettings', 'ExtensionDirectory' );
01177                         $directoryList = eZExtension::expandedPathList( $extensionDirectories, 'sso_handler' );
01178                         foreach( $directoryList as $directory )
01179                         {
01180                             $handlerFile = $directory . '/ez' . strtolower( $ssoHandler ) . 'ssohandler.php';
01181                             if ( file_exists( $handlerFile ) )
01182                             {
01183                                 include_once( $handlerFile );
01184                                 $className = 'eZ' . $ssoHandler . 'SSOHandler';
01185                                 $impl = new $className();
01186                                 $ssoUser = $impl->handleSSOLogin();
01187                             }
01188                         }
01189                     }
01190                 }
01191                 // If a user was found via SSO, then use it
01192                 if ( $ssoUser !== false )
01193                 {
01194                     $currentUser = $ssoUser;
01195 
01196                     $userInfo = array();
01197                     $userInfo[$userId] = array( 'contentobject_id' => $currentUser->attribute( 'contentobject_id' ),
01198                                             'login' => $currentUser->attribute( 'login' ),
01199                                             'email' => $currentUser->attribute( 'email' ),
01200                                             'password_hash' => $currentUser->attribute( 'password_hash' ),
01201                                             'password_hash_type' => $currentUser->attribute( 'password_hash_type' )
01202                                             );
01203                     eZSession::setUserID( $userId );
01204                     $http->setSessionVariable( 'eZUserInfoCache', $userInfo );
01205                     $http->setSessionVariable( 'eZUserInfoCache_Timestamp', time() );
01206                     $http->setSessionVariable( 'eZUserLoggedInID', $userId );
01207 
01208                     eZUser::updateLastVisit( $currentUser->attribute( 'contentobject_id' ) );
01209                     eZUser::setCurrentlyLoggedInUser( $currentUser, $currentUser->attribute( 'contentobject_id' ) );
01210                     eZHTTPTool::redirect( eZSys::wwwDir() . eZSys::indexFile( false ) . eZSys::requestURI(), array(), 302 );
01211 
01212                 }
01213             }
01214         }
01215 
01216         $anonymousUserID = self::anonymousId();
01217         if ( $userId <> $anonymousUserID )
01218         {
01219             $sessionInactivityTimeout = $ini->variable( 'Session', 'ActivityTimeout' );
01220             if ( !isset( $GLOBALS['eZSessionIdleTime'] ) )
01221             {
01222                 eZUser::updateLastVisit( $userId );
01223             }
01224             else
01225             {
01226                 $sessionIdle = $GLOBALS['eZSessionIdleTime'];
01227                 if ( $sessionIdle > $sessionInactivityTimeout )
01228                 {
01229                     eZUser::updateLastVisit( $userId );
01230                 }
01231             }
01232         }
01233 
01234         if ( !$currentUser )
01235         {
01236             $currentUser = eZUser::fetch( self::anonymousId() );
01237             eZDebug::writeWarning( 'User not found, returning anonymous' );
01238         }
01239 
01240         if ( !$currentUser )
01241         {
01242             $currentUser = new eZUser( array( 'id' => -1, 'login' => 'NoUser' ) );
01243 
01244             eZDebug::writeWarning( 'Anonymous user not found, returning NoUser' );
01245         }
01246 
01247         $GLOBALS["eZUserGlobalInstance_$id"] = $currentUser;
01248         return $currentUser;
01249     }
01250 
01251     /*!
01252        Updates the user's last visit timestamp
01253        Optionally updates user login count by setting $updateLoginCount to true
01254     */
01255     static function updateLastVisit( $userID, $updateLoginCount = false )
01256     {
01257         if ( isset( $GLOBALS['eZUserUpdatedLastVisit'] ) )
01258             return;
01259         $db = eZDB::instance();
01260         $userID = (int) $userID;
01261         $userVisitArray = $db->arrayQuery( "SELECT 1 FROM ezuservisit WHERE user_id=$userID" );
01262         $time = time();
01263 
01264         if ( count( $userVisitArray ) == 1 )
01265         {
01266             $loginCountSQL = $updateLoginCount ? ', login_count=login_count+1' : '';
01267             $db->query( "UPDATE ezuservisit SET last_visit_timestamp=current_visit_timestamp, current_visit_timestamp=$time$loginCountSQL WHERE user_id=$userID" );
01268         }
01269         else
01270         {
01271             $intialLoginCount = $updateLoginCount ? 1 : 0;
01272             $db->query( "INSERT INTO ezuservisit ( current_visit_timestamp, last_visit_timestamp, user_id, login_count ) VALUES ( $time, $time, $userID, $intialLoginCount )" );
01273         }
01274         $GLOBALS['eZUserUpdatedLastVisit'] = true;
01275     }
01276 
01277     /*!
01278       Returns the last visit timestamp to the current user.
01279     */
01280     function lastVisit()
01281     {
01282         $db = eZDB::instance();
01283 
01284         $userVisitArray = $db->arrayQuery( "SELECT last_visit_timestamp FROM ezuservisit WHERE user_id=$this->ContentObjectID" );
01285         if ( count( $userVisitArray ) == 1 )
01286         {
01287             return $userVisitArray[0]['last_visit_timestamp'];
01288         }
01289         else
01290         {
01291             return time();
01292         }
01293     }
01294 
01295     /**
01296      * Returns the login count for the current user.
01297      *
01298      * @since Version 4.1
01299      * @return int Login count for current user.
01300      */
01301     function loginCount()
01302     {
01303         $db = eZDB::instance();
01304         $userVisitArray = $db->arrayQuery( "SELECT login_count FROM ezuservisit WHERE user_id=$this->ContentObjectID" );
01305         if ( count( $userVisitArray ) == 1 )
01306         {
01307             return $userVisitArray[0]['login_count'];
01308         }
01309         else
01310         {
01311             return 0;
01312         }
01313     }
01314 
01315     /*!
01316        If \a $value is false will increase the user's number of failed login attempts
01317        otherwise failed_login_attempts will be updated by $value.
01318        \a $setByForce if true checking for trusting or max number of failed login attempts will be ignored.
01319     */
01320     static function setFailedLoginAttempts( $userID, $value = false, $setByForce = false )
01321     {
01322         $trustedUser = eZUser::isTrusted();
01323         // If user is trusted we should stop processing
01324         if ( $trustedUser and !$setByForce )
01325             return true;
01326 
01327         $maxNumberOfFailedLogin = eZUser::maxNumberOfFailedLogin();
01328 
01329         if ( $maxNumberOfFailedLogin == '0' and !$setByForce )
01330             return true;
01331 
01332         $userID = (int) $userID;
01333         $userObject = eZUser::fetch( $userID );
01334         if ( !$userObject )
01335             return true;
01336 
01337         $isEnabled = $userObject->isEnabled();
01338         // If current user is disabled we should not continue
01339         if ( !$isEnabled and !$setByForce )
01340             return true;
01341 
01342         $db = eZDB::instance();
01343         $db->begin();
01344 
01345         $userVisitArray = $db->arrayQuery( "SELECT 1 FROM ezuservisit WHERE user_id=$userID" );
01346 
01347         if ( count( $userVisitArray ) == 1 )
01348         {
01349             if ( $value === false )
01350             {
01351                 $failedLoginAttempts = $userObject->failedLoginAttempts();
01352                 $failedLoginAttempts += 1;
01353             }
01354             else
01355                 $failedLoginAttempts = (int) $value;
01356 
01357             $db->query( "UPDATE ezuservisit SET failed_login_attempts=$failedLoginAttempts WHERE user_id=$userID" );
01358         }
01359         else
01360         {
01361             if ( $value === false )
01362             {
01363                 $failedLoginAttempts = 1;
01364             }
01365             else
01366                 $failedLoginAttempts = (int) $value;
01367 
01368             $db->query( "INSERT INTO ezuservisit ( failed_login_attempts, user_id ) VALUES ( $failedLoginAttempts, $userID )" );
01369         }
01370         $db->commit();
01371 
01372         eZContentCacheManager::clearContentCacheIfNeeded( $userID );
01373         eZContentCacheManager::generateObjectViewCache( $userID );
01374     }
01375 
01376     /*!
01377       Returns the current user's number of failed login attempts.
01378     */
01379     function failedLoginAttempts()
01380     {
01381         return eZUser::failedLoginAttemptsByUserID( $this->attribute( 'contentobject_id' ) );
01382     }
01383 
01384     /*!
01385       Returns the current user's number of failed login attempts.
01386     */
01387     static function failedLoginAttemptsByUserID( $userID )
01388     {
01389         $db = eZDB::instance();
01390         $contentObjectID = (int) $userID;
01391 
01392         $userVisitArray = $db->arrayQuery( "SELECT failed_login_attempts FROM ezuservisit WHERE user_id=$contentObjectID" );
01393 
01394         $failedLoginAttempts = count( $userVisitArray ) == 1 ? $userVisitArray[0]['failed_login_attempts'] : 0;
01395         return $failedLoginAttempts;
01396     }
01397 
01398     /*!
01399      \return \c true if the user is locked (is enabled after failed login) and can be logged on the site.
01400     */
01401     function isLocked()
01402     {
01403         $userID = $this->attribute( 'contentobject_id' );
01404         $isNotLocked = eZUser::isEnabledAfterFailedLogin( $userID, true );
01405         return !$isNotLocked;
01406     }
01407 
01408     /*!
01409      \return \c true if the user is enabled and can be used on the site.
01410     */
01411     function isEnabled()
01412     {
01413         if ( $this == eZUser::currentUser() )
01414         {
01415             return true;
01416         }
01417 
01418         $setting = eZUserSetting::fetch( $this->attribute( 'contentobject_id' ) );
01419         if ( $setting and !$setting->attribute( 'is_enabled' ) )
01420         {
01421             return false;
01422         }
01423         return true;
01424     }
01425 
01426     /*!
01427      \return \c true if the user is the anonymous user.
01428     */
01429     function isAnonymous()
01430     {
01431         if ( $this->attribute( 'contentobject_id' ) != self::anonymousId() )
01432         {
01433             return false;
01434         }
01435         return true;
01436     }
01437 
01438     /*!
01439      \static
01440      Returns the currently logged in user.
01441     */
01442     static function currentUser()
01443     {
01444         $user = eZUser::instance();
01445         return $user;
01446     }
01447 
01448     /*!
01449      \static
01450      Returns the ID of the currently logged in user.
01451     */
01452     static function currentUserID()
01453     {
01454         $user = eZUser::instance();
01455         if ( !$user )
01456             return 0;
01457         return $user->attribute( 'contentobject_id' );
01458     }
01459 
01460     /*!
01461      \static
01462      Creates a hash out of \a $user, \a $password and \a $site according to the type \a $type.
01463      \return true if the generated hash is equal to the supplied hash \a $hash.
01464     */
01465     static function authenticateHash( $user, $password, $site, $type, $hash )
01466     {
01467         return eZUser::createHash( $user, $password, $site, $type, $hash ) === (string) $hash;
01468     }
01469 
01470     /*!
01471      \static
01472      \return an array with characters which are allowed in password.
01473     */
01474     static function passwordCharacterTable()
01475     {
01476         if ( !empty( $GLOBALS['eZUserPasswordCharacterTable'] ) )
01477         {
01478             return $GLOBALS['eZUserPasswordCharacterTable'];
01479         }
01480         $table = array_merge( range( 'a', 'z' ), range( 'A', 'Z' ), range( 0, 9 ) );
01481 
01482         $ini = eZINI::instance();
01483         if ( $ini->variable( 'UserSettings', 'UseSpecialCharacters' ) == 'true' )
01484         {
01485             $specialCharacters = '!#%&{[]}+?;:*';
01486             $table = array_merge( $table, preg_split( '//', $specialCharacters, -1, PREG_SPLIT_NO_EMPTY ) );
01487         }
01488         // Remove some characters that are too similar visually
01489         $table = array_diff( $table, array( 'I', 'l', 'o', 'O', '0' ) );
01490         $tableTmp = $table;
01491         $table = array();
01492         foreach ( $tableTmp as $item )
01493         {
01494             $table[] = $item;
01495         }
01496 
01497         return $GLOBALS['eZUserPasswordCharacterTable'] = $table;
01498     }
01499 
01500     /*!
01501      Checks if the supplied content object is a user object ( contains ezuser datatype )
01502 
01503      \param ContentObject
01504 
01505      \return true or false
01506     */
01507     static function isUserObject( $contentObject )
01508     {
01509         if ( !$contentObject )
01510         {
01511             return false;
01512         }
01513 
01514         eZDataType::loadAndRegisterType( 'ezuser' );
01515 
01516         $contentClass = $contentObject->attribute( 'content_class' );
01517         $classAttributeList = $contentClass->fetchAttributes();
01518         foreach( $classAttributeList as $classAttribute )
01519         {
01520             if ( $classAttribute->attribute( 'data_type_string' ) == eZUserType::DATA_TYPE_STRING )
01521                 return true;
01522         }
01523 
01524         return false;
01525     }
01526 
01527     /*!
01528      \static
01529      Creates a password with number of characters equal to \a $passwordLength and returns it.
01530      If you want pass a value in \a $seed it will be used as basis for the password, if not
01531      it will use the current time value as seed.
01532      \note If \a $passwordLength exceeds 16 it will need to generate new seed for the remaining
01533            characters.
01534     */
01535     static function createPassword( $passwordLength, $seed = false )
01536     {
01537         $chars = 0;
01538         $password = '';
01539         if ( $passwordLength < 1 )
01540             $passwordLength = 1;
01541         $decimal = 0;
01542         while ( $chars < $passwordLength )
01543         {
01544             if ( $seed == false )
01545                 $seed = time() . ":" . mt_rand();
01546             $text = md5( $seed );
01547             $characterTable = eZUser::passwordCharacterTable();
01548             $tableCount = count( $characterTable );
01549             for ( $i = 0; ( $chars < $passwordLength ) and $i < 32; ++$chars, $i += 2 )
01550             {
01551                 $decimal += hexdec( substr( $text, $i, 2 ) );
01552                 $index = ( $decimal % $tableCount );
01553                 $character = $characterTable[$index];
01554                 $password .= $character;
01555             }
01556             $seed = false;
01557         }
01558         return $password;
01559     }
01560 
01561     /*!
01562      \static
01563      Will create a hash of the given string. This is used to store the passwords in the database.
01564     */
01565     static function createHash( $user, $password, $site, $type, $hash = false )
01566     {
01567         $str = '';
01568         if( $type == self::PASSWORD_HASH_MD5_USER )
01569         {
01570             $str = md5( "$user\n$password" );
01571         }
01572         else if ( $type == self::PASSWORD_HASH_MD5_SITE )
01573         {
01574             $str = md5( "$user\n$password\n$site" );
01575         }
01576         else if ( $type == self::PASSWORD_HASH_MYSQL )
01577         {
01578             $db = eZDB::instance();
01579             $hash = $db->escapeString( $password );
01580 
01581             $str = $db->arrayQuery( "SELECT PASSWORD( '$hash' )" );
01582             $hashes = array_values( $str[0] );
01583             $str = $hashes[0];
01584         }
01585         else if ( $type == self::PASSWORD_HASH_PLAINTEXT )
01586         {
01587             $str = $password;
01588         }
01589         else if ( $type == self::PASSWORD_HASH_CRYPT )
01590         {
01591             if ( $hash )
01592             {
01593                 $str = crypt( $password, $hash );
01594             }
01595             else
01596             {
01597                 $str = crypt( $password );
01598             }
01599         }
01600         else // self::PASSWORD_HASH_MD5_PASSWORD
01601         {
01602             $str = md5( $password );
01603         }
01604         eZDebugSetting::writeDebug( 'kernel-user', $str, "ezuser($type)" );
01605         return $str;
01606     }
01607 
01608     /*!
01609      Check if user has got access to the specified module and function
01610 
01611      \param module name
01612      \param funtion name
01613 
01614      \return Array containg result.
01615              Array elements : 'accessWord', yes - access allowed
01616                                             no - access denied
01617                                             limited - access array describing access included
01618                               'policies', array containing the policy limitations
01619                               'accessList', array describing missing access rights
01620     */
01621     function hasAccessTo( $module, $function = false )
01622     {
01623         $accessArray = $this->accessArray();
01624 
01625         $functionArray = array();
01626         if ( isset( $accessArray['*']['*'] ) )
01627         {
01628             $functionArray = $accessArray['*']['*'];
01629         }
01630         if ( isset( $accessArray[$module] ) )
01631         {
01632             if ( isset( $accessArray[$module]['*'] ) )
01633             {
01634                 $functionArray = array_merge_recursive( $functionArray, $accessArray[$module]['*'] );
01635             }
01636             if ( $function and isset( $accessArray[$module][$function] ) and $function != '*' )
01637             {
01638                 $functionArray = array_merge_recursive( $functionArray, $accessArray[$module][$function] );
01639             }
01640         }
01641 
01642         if ( !$functionArray )
01643         {
01644             return array( 'accessWord' => 'no',
01645                           'accessList' => array( 'FunctionRequired' => array ( 'Module' => $module,
01646                                                                                'Function' => $function,
01647                                                                                'ClassID' => '',
01648                                                                                'MainNodeID' => '' ),
01649                                                  'PolicyList' => array() )
01650                           );
01651         }
01652 
01653         if ( isset( $functionArray['*'] ) &&
01654                   ( $functionArray['*'] == '*' || in_array( '*',  $functionArray['*'] ) ) )
01655         {
01656             return array( 'accessWord' => 'yes' );
01657         }
01658 
01659         return array( 'accessWord' => 'limited', 'policies' => $functionArray );
01660     }
01661 
01662     /*
01663      \private
01664      Returns either cached or newly generated accessArray for the user.
01665     */
01666     function accessArray()
01667     {
01668         if ( !isset( $this->AccessArray ) )
01669         {
01670             $ini = eZINI::instance();
01671             $isRoleCachingEnabled = ( $ini->variable( 'RoleSettings', 'EnableCaching' ) == 'true' );
01672 
01673             $userID = $this->attribute( 'contentobject_id' );
01674             $currentUserID = eZUser::currentUserID();
01675 
01676             $accessArray = false;
01677 
01678             if ( $isRoleCachingEnabled )
01679             {
01680                 if ( $userID == $currentUserID )
01681                 {
01682                     $http = eZHTTPTool::instance();
01683                     if ( $http->hasSessionVariable( 'AccessArray' ) and
01684                          $http->hasSessionVariable( 'AccessArrayTimestamp' ) )
01685                     {
01686                         $expiredTimestamp = $this->userInfoExpiry();
01687                         $userAccessTimestamp = $http->sessionVariable( 'AccessArrayTimestamp' );
01688                         if ( $userAccessTimestamp > $expiredTimestamp )
01689                         {
01690                             $accessArray = $http->sessionVariable( 'AccessArray' );
01691                         }
01692                     }
01693                 }
01694 
01695                 if ( !$accessArray )
01696                 {
01697                     $cacheFilePath = eZUser::getCacheFilename( $userID );
01698                     if ( $cacheFilePath )
01699                     {
01700                         $cacheFile = eZClusterFileHandler::instance( $cacheFilePath );
01701                         $accessArray = $cacheFile->processCache( array( $this, 'retrieveAccessArrayFromCache' ),
01702                                                                  array( $this, 'generateAccessArrayForCache' ),
01703                                                                  null,
01704                                                                  $this->userInfoExpiry(),
01705                                                                  $userID );
01706                         if ( $userID == $currentUserID )
01707                         {
01708                             // here is no need to get $http instance again because it is initialized
01709                             // already above by the same condition's case ( userID == currentUserID ).
01710                             $http->setSessionVariable( 'AccessArray', $accessArray );
01711                             $http->setSessionVariable( 'AccessArrayTimestamp', time() );
01712                         }
01713                     }
01714                     else
01715                     {
01716                         // if there is no cache file and no access array was fetched from
01717                         // the current session then generate access array on-the-fly.
01718                         $accessArray = $this->generateAccessArray();
01719                     }
01720                 }
01721             }
01722             else
01723             {
01724                 // if role caching is disabled then generate access array on-the-fly.
01725                 $accessArray = $this->generateAccessArray();
01726             }
01727 
01728             $this->AccessArray = $accessArray;
01729         }
01730         return $this->AccessArray;
01731     }
01732 
01733     /*
01734      \private
01735      Generates the accessArray for the user (for $this).
01736     */
01737     function generateAccessArray()
01738     {
01739         $idList = $this->groups();
01740         $idList[] = $this->attribute( 'contentobject_id' );
01741 
01742         $accessArray = eZRole::accessArrayByUserID( $idList );
01743 
01744         $ini = eZINI::instance( 'module.ini' );
01745         $modules = $ini->variable( 'ModuleSettings', 'ModuleList' );
01746 
01747         // evaluate module and function wildcards in the access array
01748 
01749         foreach ( $accessArray as $moduleName => $assignedFunctions )
01750         {
01751             if ( $moduleName != '*' && isset( $assignedFunctions['*'] ) )
01752             {
01753                 $limitations = $assignedFunctions['*'];
01754 
01755                 $mod = eZModule::exists( $moduleName );
01756 
01757                 if ( !$mod )
01758                 {
01759                     continue;
01760                 }
01761 
01762                 $functions = $mod->attribute( 'available_functions' );
01763 
01764                 if ( count( $functions ) > 0 )
01765                 {
01766                     // remove wildcard
01767                     unset( $accessArray[$moduleName]['*'] );
01768 
01769                     // and add evaluated wildcard instead
01770                     foreach ( $functions as $functionName => $functionValue )
01771                     {
01772                         $accessArray = array_merge_recursive( $accessArray, array( $moduleName => array( $functionName => $limitations ) ) );
01773                     }
01774                 }
01775             }
01776         }
01777 
01778         if ( isset( $accessArray['*'] ) )
01779         {
01780             $limitations = $accessArray['*']['*'];
01781 
01782             // remove wildcard
01783             unset( $accessArray['*'] );
01784 
01785             // add evaluated wildcard instead
01786             foreach ( $modules as $moduleName )
01787             {
01788                 $mod = eZModule::exists( $moduleName );
01789 
01790                 if ( !$mod )
01791                 {
01792                     continue;
01793                 }
01794 
01795                 $functions = $mod->attribute( 'available_functions' );
01796 
01797                 if ( count( $functions ) == 0 )
01798                 {
01799                     $functions = array( '*' => array() );
01800                 }
01801 
01802                 foreach ( $functions as $functionName => $functionValue )
01803                 {
01804                     $accessArray = array_merge_recursive( $accessArray, array( $moduleName => array( $functionName => $limitations ) ) );
01805                 }
01806             }
01807         }
01808 
01809         // add implicit system policy limitations
01810         $lockGroup = eZContentObjectStateGroup::fetchByIdentifier( 'ez_lock' );
01811 
01812         if ( $lockGroup )
01813         {
01814             $lockStates = $lockGroup->states();
01815             if ( count( $lockStates ) > 0 )
01816             {
01817                 $defaultLockState = $lockStates[0];
01818 
01819                 $implicitLimitations = array( 'StateGroup_ez_lock' => array( $defaultLockState->attribute( 'id' ) ) );
01820                 foreach ( array( 'content' => array( 'edit', 'remove' ) ) as $moduleName => $functions )
01821                 {
01822                     if ( !isset( $accessArray[$moduleName] ) )
01823                     {
01824                         continue;
01825                     }
01826 
01827                     foreach ( $functions as $functionName )
01828                     {
01829                          if ( isset( $accessArray[$moduleName][$functionName] ) )
01830                          {
01831                             foreach( $accessArray[$moduleName][$functionName] as $key => $limitations )
01832                             {
01833                                 $accessArray[$moduleName][$functionName][$key] = is_array( $limitations ) ?
01834                                     array_merge( $limitations, $implicitLimitations ) : $implicitLimitations;
01835                             }
01836                          }
01837                     }
01838                 }
01839             }
01840         }
01841 
01842         return $accessArray;
01843     }
01844 
01845     /*!
01846      \private
01847      \static
01848      Callback which figures out global expiry and returns it.
01849      */
01850     function userInfoExpiry()
01851     {
01852         /* Figure out when the last update was done */
01853         eZExpiryHandler::registerShutdownFunction();
01854         $handler = eZExpiryHandler::instance();
01855         if ( $handler->hasTimestamp( 'user-access-cache' ) )
01856         {
01857             $expiredTimestamp = $handler->timestamp( 'user-access-cache' );
01858         }
01859         else
01860         {
01861             $expiredTimestamp = time();
01862             $handler->setTimestamp( 'user-access-cache', $expiredTimestamp );
01863         }
01864 
01865         return $expiredTimestamp;
01866     }
01867 
01868     /*!
01869      \private
01870      \static
01871      Callback which fetches access array from local file.
01872      */
01873     function retrieveAccessArrayFromCache( $filePath, $mtime, $userID )
01874     {
01875         return include( $filePath );
01876     }
01877 
01878     /*!
01879      \private
01880      Callback which generates the accessarray for the current user.
01881      */
01882     function generateAccessArrayForCache( $filePath, $userID )
01883     {
01884         return array( 'content'  => $this->generateAccessArray(),
01885                       'scope'    => 'user-info-cache',
01886                       'datatype' => 'php',
01887                       'store'    => true );
01888     }
01889 
01890 
01891     /*
01892      Returns list of sections which are allowed to assign to the given content object by the user.
01893     */
01894     function canAssignToObjectSectionList( $contentObject )
01895     {
01896         $access = $this->hasAccessTo( 'section', 'assign' );
01897 
01898         if ( $access['accessWord'] == 'yes' )
01899         {
01900             return array( '*' );
01901         }
01902         else if ( $access['accessWord'] == 'limited' )
01903         {
01904             $userID = $this->attribute( 'contentobject_id' );
01905             $classID = $contentObject->attribute( 'contentclass_id' );
01906             $ownerID = $contentObject->attribute( 'owner_id' );
01907             $sectionID = $contentObject->attribute( 'section_id' );
01908 
01909             $allowedSectionIDList = array();
01910             foreach ( $access['policies'] as $policy )
01911             {
01912                 if ( ( isset( $policy['Class'] ) and !in_array( $classID, $policy['Class'] ) ) or
01913                      ( isset( $policy['Owner']  ) and in_array( 1, $policy['Owner'] ) and $userID != $ownerID ) or
01914                      ( isset( $policy['Section'] ) and !in_array( $sectionID, $policy['Section'] ) ) )
01915                 {
01916                     continue;
01917                 }
01918                 if ( isset( $policy['NewSection'] ) and count( $policy['NewSection'] > 0 ) )
01919                 {
01920                     $allowedSectionIDList = array_merge( $allowedSectionIDList, $policy['NewSection'] );
01921                 }
01922                 else
01923                 {
01924                     return array( '*' );
01925                 }
01926             }
01927             $allowedSectionIDList = array_unique( $allowedSectionIDList );
01928             return $allowedSectionIDList;
01929         }
01930         return array();
01931     }
01932 
01933     /*
01934      Checks whether user can assign the section to the given content object or not.
01935     */
01936     function canAssignSectionToObject( $checkSectionID, $contentObject )
01937     {
01938         $access = $this->hasAccessTo( 'section', 'assign' );
01939 
01940         if ( $access['accessWord'] == 'yes' )
01941         {
01942             return true;
01943         }
01944         else if ( $access['accessWord'] == 'limited' )
01945         {
01946             $hasSubtreeLimitation = false;
01947             $subtreeLimitationResult = false;
01948 
01949             // Trying first to discover if a subtree limitation exists.
01950             // Only the main node of the object is considered!
01951             foreach ( $access['policies'] as $policy )
01952             {
01953                 if ( isset( $policy['User_Subtree'] ) )
01954                 {
01955                     $hasSubtreeLimitation = true;
01956                     $nodePath = $contentObject->mainNode()->PathString;
01957 
01958                     foreach ( $policy['User_Subtree'] as $path )
01959                     {
01960                         if ( strpos( $nodePath, $path ) !== false )
01961                         {
01962                             $subtreeLimitationResult = true;
01963                         }
01964                     }
01965                     continue;
01966                 }
01967             }
01968 
01969             // If a subtree limitation exists and none of the path corresponds then the user have not enough rights.
01970             if ( $hasSubtreeLimitation && !$subtreeLimitationResult )
01971             {
01972                 return false;
01973             }
01974 
01975             unset( $hasSubtreeLimitation, $subtreeLimitationResult, $nodePath );
01976 
01977             $userID = $this->attribute( 'contentobject_id' );
01978             $classID = $contentObject->attribute( 'contentclass_id' );
01979             $ownerID = $contentObject->attribute( 'owner_id' );
01980             $sectionID = $contentObject->attribute( 'section_id' );
01981 
01982             foreach ( $access['policies'] as $policy )
01983             {
01984                 if ( ( isset( $policy['Class'] ) and !in_array( $classID, $policy['Class'] ) ) or
01985                      ( isset( $policy['Owner']  ) and in_array( 1, $policy['Owner'] ) and $userID != $ownerID ) or
01986                      ( isset( $policy['Section'] ) and !in_array( $sectionID, $policy['Section'] ) ) )
01987                 {
01988                     continue;
01989                 }
01990                 if ( isset( $policy['NewSection'] ) )
01991                 {
01992                     if ( is_array( $policy['NewSection'] ) and in_array( $checkSectionID, $policy['NewSection'] ) )
01993                     {
01994                         return true;
01995                     }
01996                 }
01997                 else
01998                 {
01999                     return true;
02000                 }
02001             }
02002         }
02003         return false;
02004     }
02005 
02006     /*
02007      Checks whether user has privileges to assign the section or not at all.
02008     */
02009     function canAssignSection( $checkSectionID )
02010     {
02011         $access = $this->hasAccessTo( 'section', 'assign' );
02012 
02013         if ( $access['accessWord'] == 'yes' )
02014         {
02015             return true;
02016         }
02017         else if ( $access['accessWord'] == 'limited' )
02018         {
02019             foreach ( $access['policies'] as $policy )
02020             {
02021                 if ( isset( $policy['NewSection'] ) )
02022                 {
02023                     if ( in_array( $checkSectionID, $policy['NewSection'] ) )
02024                     {
02025                         return true;
02026                     }
02027                 }
02028                 else
02029                 {
02030                     return true;
02031                 }
02032             }
02033         }
02034         return false;
02035     }
02036 
02037     /*
02038      Returns list of sections allowed to assign for the user.
02039     */
02040     function canAssignSectionList()
02041     {
02042         $access = $this->hasAccessTo( 'section', 'assign' );
02043 
02044         if ( $access['accessWord'] == 'yes' )
02045         {
02046             return array( '*' );
02047         }
02048         else if ( $access['accessWord'] == 'limited' )
02049         {
02050             $allowedSectionIDList = array();
02051             foreach ( $access['policies'] as $policy )
02052             {
02053                 if ( isset( $policy['NewSection'] ) )
02054                 {
02055                     if ( is_array( $policy['NewSection'] ) and count( $policy['NewSection'] ) > 0 )
02056                     {
02057                         $allowedSectionIDList = array_merge( $allowedSectionIDList, $policy['NewSection'] );
02058                     }
02059                 }
02060                 else
02061                 {
02062                     return array( '*' );
02063                 }
02064             }
02065             $allowedSectionIDList = array_unique( $allowedSectionIDList );
02066             return $allowedSectionIDList;
02067         }
02068         return array();
02069     }
02070 
02071     /*
02072      Returns list of classes allowed to assign to the given section for the user.
02073     */
02074     function canAssignSectionToClassList( $checkSectionID )
02075     {
02076         $access = $this->hasAccessTo( 'section', 'assign' );
02077 
02078         if ( $access['accessWord'] == 'yes' )
02079         {
02080             return array( '*' );
02081         }
02082         else if ( $access['accessWord'] == 'limited' )
02083         {
02084             $allowedClassList = array();
02085             foreach ( $access['policies'] as $policy )
02086             {
02087                 if ( !isset( $policy['NewSection'] ) or in_array( $checkSectionID, $policy['NewSection'] ) )
02088                 {
02089                     if ( isset( $policy['Class'] ) )
02090                     {
02091                         $allowedClassList = array_merge( $allowedClassList, $policy['Class'] );
02092                     }
02093                     else
02094                     {
02095                         return array( '*' );
02096                     }
02097                 }
02098             }
02099 
02100             if ( count( $allowedClassList ) > 0 )
02101             {
02102                 // Now we are trying to fetch classes by collected ids list to return
02103                 // class list consisting of existing classes's identifiers only.
02104                 $allowedClassList = array_unique( $allowedClassList );
02105                 // include_once( 'kernel/classes/ezcontentclass.php' );
02106                 $classList = eZContentClass::fetchList( eZContentClass::VERSION_STATUS_DEFINED, false, false, null, null, $allowedClassList );
02107                 if ( is_array( $classList ) and count( $classList ) > 0 )
02108                 {
02109                     $classIdentifierList = array();
02110                     foreach( $classList as $class )
02111                     {
02112                         $classIdentifierList[] = $class['identifier'];
02113                     }
02114                     return $classIdentifierList;
02115                 }
02116             }
02117         }
02118         return array();
02119     }
02120 
02121     /*
02122      Evaluates if $this user has access to the view $viewName based on its policy functions and
02123      checks if the assigned to the view functions expression is valid and handles errors if it is not.
02124      Returns true if access is allowed, false if access is denied.
02125     */
02126     function hasAccessToView( $module, $viewName, &$params )
02127     {
02128         $validView = false;
02129         $accessAllowed = false;
02130         if ( $module->singleFunction() )
02131         {
02132             $info = $module->attribute( 'info' );
02133             if ( isset( $info['function'] ) )
02134             {
02135                 $validView = true;
02136                 if ( isset( $info['function']['functions'] ) && !empty( $info['function']['functions'] ) )
02137                 {
02138                     $functions = $info['function']['functions'];
02139                 }
02140             }
02141         }
02142         else
02143         {
02144             $views = $module->attribute( 'views' );
02145             if ( isset( $views[$viewName] ) )
02146             {
02147                 $view = $views[$viewName];
02148                 $validView = true;
02149                 if ( isset( $view['functions'] ) && !empty( $view['functions'] ) )
02150                 {
02151                     $functions = $view['functions'];
02152                 }
02153             }
02154         }
02155 
02156 
02157         if ( $validView )
02158         {
02159             if ( isset( $functions ) )
02160             {
02161                 if ( is_array( $functions ) )
02162                 {
02163                     $funcExpression = false;
02164                     $accessAllowed = true;
02165                     foreach ( $functions as $function )
02166                     {
02167                         if ( empty( $function ) )
02168                         {
02169                             $funcExpression = false;
02170                             $accessAllowed = false;
02171                             break;
02172                         }
02173                         else if ( is_string( $function ) )
02174                         {
02175                             if ( $funcExpression )
02176                             {
02177                                 $funcExpression .= ' and ';
02178                             }
02179                             $funcExpression .= '( ' . $function . ' )';
02180                         }
02181                     }
02182                 }
02183                 else if ( is_string( $functions ) )
02184                 {
02185                     $funcExpression = $functions;
02186                 }
02187                 else
02188                 {
02189                     $funcExpression = false;
02190                     $accessAllowed = true;
02191                 }
02192 
02193                 if ( $funcExpression )
02194                 {
02195                     // Validate and evaluate functions expression.
02196                     // Lets construct functions's expression ready for evaluating first.
02197                     $pS = '/(?<=\b)';
02198                     $pE = '(?=\b)/';
02199 
02200                     $moduleName = $module->attribute( 'name' );
02201                     $availableFunctions = $module->attribute( 'available_functions' );
02202                     if ( is_array( $availableFunctions ) and
02203                          count( $availableFunctions ) > 0 )
02204                     {
02205                         $pattern = $pS . '(' . implode( '|', array_keys( $availableFunctions ) ) . ')' . $pE;
02206                         $matches = array();
02207                         if ( preg_match_all( $pattern, ' ' . $funcExpression . ' ', $matches ) > 0 )
02208                         {
02209                             $patterns = array();
02210                             $replacements = array();
02211                             $matches = array_unique( $matches[1] );
02212                             foreach ( $matches as $match )
02213                             {
02214                                 if ( !isset( $replacements[$match] ) )
02215                                 {
02216                                     $accessResult = $this->hasAccessTo( $moduleName, $match );
02217                                     if ( $accessResult['accessWord'] == 'no' )
02218                                     {
02219                                         $replacements[$match] = 'false';
02220                                         $params['accessList'] = $accessResult['accessList'];
02221                                     }
02222                                     else
02223                                     {
02224                                         $replacements[$match] = 'true';
02225                                         if ( $accessResult['accessWord'] == 'limited' )
02226                                         {
02227                                             $params['Limitation'] = $accessResult['policies'];
02228                                             $GLOBALS['ezpolicylimitation_list'][$this->ContentObjectID][$moduleName][$match] = $params['Limitation'];
02229                                         }
02230                                     }
02231                                     $patterns[$match] = $pS . $match . $pE;
02232                                 }
02233                             }
02234                             $funcExpression = preg_replace( $patterns, $replacements, ' ' . $funcExpression . ' ' );
02235                         }
02236                     }
02237                     $funcExpressionForEval = $funcExpression;
02238 
02239                     // continue to validate expression
02240                     $words = array();
02241                     $words[] = $pS . 'and' . $pE;
02242                     $words[] = $pS . 'or' . $pE;
02243                     $words[] = $pS . 'true' . $pE;
02244                     $words[] = $pS . 'false' . $pE;
02245                     $pS = '/(?<=[^&|])';
02246                     $pE = '(?=[^&|])/';
02247                     $words[] = $pS . '\|\|' . $pE;
02248                     $words[] = $pS . '&&' . $pE;
02249                     $words[] = '/[\(\)]/';
02250 
02251                     $replacement = '';
02252                     $funcExpression = preg_replace( $words, $replacement, ' ' . $funcExpression . ' ' );
02253 
02254                     $funcExpression = trim( $funcExpression );
02255 
02256                     if ( empty( $funcExpression ) )
02257                     {
02258                         // if expression is valid then evaluate value of the $functionsToEvaluate string
02259                         ob_start();
02260                         $ret = eval( "\$accessAllowed = ( bool ) ( $funcExpressionForEval );" );
02261                         $buffer = ob_get_contents();
02262                         ob_end_clean();
02263 
02264                         // if we get any error while evaluating then set result to false
02265                         if ( !empty( $buffer ) or $ret === false )
02266                         {
02267                             eZDebug::writeError( "There was an error while evaluating the policy functions value of the '$moduleName/$viewName' view. " .
02268                                                  "Please check the '$moduleName/module.php' file." );
02269                             $accessAllowed = false;
02270                         }
02271                     }
02272                     else
02273                     {
02274                         eZDebug::writeError( "There is a mistake in the functions array data of the '$moduleName/$viewName' view. " .
02275                                              "Please check the '$moduleName/module.php' file." );
02276                         $accessAllowed = false;
02277                     }
02278                 }
02279             }
02280             else
02281             {
02282                 $moduleName = $module->attribute( 'name' );
02283                 $accessResult = $this->hasAccessTo( $moduleName );
02284                 if ( $accessResult['accessWord'] == 'no' )
02285                 {
02286                     $params['accessList'] = $accessResult['accessList'];
02287                     $accessAllowed = false;
02288                 }
02289                 else
02290                 {
02291                     $accessAllowed = true;
02292                     if ( $accessResult['accessWord'] == 'limited' )
02293                     {
02294                         $params['Limitation'] = $accessResult['policies'];
02295                         $GLOBALS['ezpolicylimitation_list'][$this->ContentObjectID][$moduleName]['*'] = $params['Limitation'];
02296                     }
02297                 }
02298             }
02299         }
02300 
02301         return $accessAllowed;
02302     }
02303 
02304     /*!
02305      \return an array of roles which the user is assigned to
02306     */
02307     function roles()
02308     {
02309         $groups = $this->attribute( 'groups' );
02310         $groups[] = $this->attribute( 'contentobject_id' );
02311         return eZRole::fetchByUser( $groups );
02312     }
02313 
02314     /*!
02315      \return an array of role ids which the user is assigned to
02316     */
02317     function roleIDList()
02318     {
02319         $http = eZHTTPTool::instance();
02320 
02321         // If the user object is not the currently logged in user we cannot use the session cache
02322         $useCache = ( $this->ContentObjectID == $http->sessionVariable( 'eZUserLoggedInID' ) );
02323 
02324         if ( $useCache )
02325         {
02326             eZExpiryHandler::registerShutdownFunction();
02327             $handler = eZExpiryHandler::instance();
02328             $expiredTimeStamp = 0;
02329             $roleIDListTimestamp = $http->sessionVariable( 'eZRoleIDList_Timestamp' );
02330             if ( $handler->hasTimestamp( 'user-info-cache' ) )
02331                 $expiredTimeStamp = $handler->timestamp( 'user-info-cache' );
02332 
02333             if ( $roleIDListTimestamp > $expiredTimeStamp )
02334             {
02335                 if ( $http->hasSessionVariable( 'eZRoleIDList' ) )
02336                 {
02337                     return $http->sessionVariable( 'eZRoleIDList' );
02338                 }
02339             }
02340         }
02341 
02342         $groups = $this->attribute( 'groups' );
02343         $groups[] = $this->attribute( 'contentobject_id' );
02344         $roleList = eZRole::fetchIDListByUser( $groups );
02345 
02346         if ( $useCache )
02347         {
02348             $http->setSessionVariable( 'eZRoleIDList', $roleList );
02349             $http->setSessionVariable( 'eZRoleIDList_Timestamp', time() );
02350         }
02351         return $roleList;
02352     }
02353 
02354     /*!
02355      \return an array of limited assignments
02356     */
02357     function limitList()
02358     {
02359         $groups = $this->groups( false );
02360         $groups[] = $this->attribute( 'contentobject_id' );
02361         $groups = implode( ', ', $groups );
02362 
02363         $db = eZDB::instance();
02364 
02365         $limitationsArray = $db->arrayQuery( "SELECT DISTINCT limit_identifier, limit_value
02366                                               FROM ezuser_role
02367                                               WHERE contentobject_id IN ( $groups )" );
02368 
02369         return $limitationsArray;
02370     }
02371 
02372     /*!
02373      \return an array of values of limited assignments
02374     */
02375     function limitValueList()
02376     {
02377         $limitValueList = array();
02378 
02379         $http = eZHTTPTool::instance();
02380 
02381         // If the user object is not the currently logged in user we cannot use the session cache
02382         $useCache = ( $this->ContentObjectID == $http->sessionVariable( 'eZUserLoggedInID' ) );
02383 
02384         if ( $useCache )
02385         {
02386             eZExpiryHandler::registerShutdownFunction();
02387             $handler = eZExpiryHandler::instance();
02388             $expiredTimeStamp = 0;
02389             $roleLimitationValueListTimeStamp = $http->sessionVariable( 'eZRoleLimitationValueList_Timestamp' );
02390             if ( $handler->hasTimestamp( 'user-info-cache' ) )
02391             {
02392                 $expiredTimeStamp = $handler->timestamp( 'user-info-cache' );
02393             }
02394 
02395             if ( $roleLimitationValueListTimeStamp > $expiredTimeStamp &&
02396                  $http->hasSessionVariable( 'eZRoleLimitationValueList' ) )
02397             {
02398                 return $http->sessionVariable( 'eZRoleLimitationValueList' );
02399             }
02400         }
02401 
02402         $limitList = $this->limitList();
02403         foreach ( $limitList as $limit )
02404         {
02405             $limitValueList[] = $limit['limit_value'];
02406         }
02407 
02408         if ( $useCache )
02409         {
02410             $http->setSessionVariable( 'eZRoleLimitationValueList', $limitValueList );
02411             $http->setSessionVariable( 'eZRoleLimitationValueList_Timestamp', time() );
02412         }
02413 
02414         return $limitValueList;
02415     }
02416 
02417     function contentObject()
02418     {
02419         if ( isset( $this->ContentObjectID ) and $this->ContentObjectID )
02420         {
02421             return eZContentObject::fetch( $this->ContentObjectID );
02422         }
02423         return null;
02424     }
02425 
02426     /*!
02427      Returns true if it's a real user which is logged in. False if the user
02428      is the default user or the fallback buildtin user.
02429     */
02430     function isLoggedIn()
02431     {
02432         if ( $this->ContentObjectID == self::anonymousId() or
02433              $this->ContentObjectID == -1 )
02434         {
02435             return false;
02436         }
02437         return true;
02438     }
02439 
02440     /*!
02441      \return an array of id's with all the groups the user belongs to.
02442     */
02443     function groups( $asObject = false )
02444     {
02445         $db = eZDB::instance();
02446         $http = eZHTTPTool::instance();
02447 
02448         if ( $asObject == true )
02449         {
02450             $this->Groups = array();
02451             if ( !isset( $this->GroupsAsObjects ) )
02452             {
02453                 $contentobjectID = $this->attribute( 'contentobject_id' );
02454                 $userGroups = $db->arrayQuery( "SELECT d.*, c.path_string
02455                                                 FROM ezcontentobject_tree  b,
02456                                                      ezcontentobject_tree  c,
02457                                                      ezcontentobject d
02458                                                 WHERE b.contentobject_id='$contentobjectID' AND
02459                                                       b.parent_node_id = c.node_id AND
02460                                                       d.id = c.contentobject_id
02461                                                 ORDER BY c.contentobject_id  ");
02462                 $userGroupArray = array();
02463                 $pathArray = array();
02464                 foreach ( $userGroups as $group )
02465                 {
02466                     $pathItems = explode( '/', $group["path_string"] );
02467                     array_pop( $pathItems );
02468                     array_pop( $pathItems );
02469                     foreach ( $pathItems as $pathItem )
02470                     {
02471                         if ( $pathItem != '' && $pathItem > 1 )
02472                             $pathArray[] = $pathItem;
02473                     }
02474                     $userGroupArray[] = new eZContentObject( $group );
02475                 }
02476                 $pathArray = array_unique( $pathArray );
02477 
02478                 if ( count( $pathArray ) != 0 )
02479                 {
02480                     $extraGroups = $db->arrayQuery( "SELECT d.*
02481                                                 FROM ezcontentobject_tree  c,
02482                                                      ezcontentobject d
02483                                                 WHERE c.node_id in ( " . implode( ', ', $pathArray ) . " ) AND
02484                                                       d.id = c.contentobject_id
02485                                                 ORDER BY c.contentobject_id  ");
02486                     foreach ( $extraGroups as $group )
02487                     {
02488                         $userGroupArray[] = new eZContentObject( $group );
02489                     }
02490                 }
02491 
02492                 $this->GroupsAsObjects = $userGroupArray;
02493             }
02494             return $this->GroupsAsObjects;
02495         }
02496         else
02497         {
02498             if ( !isset( $this->Groups ) )
02499             {
02500                 // If the user object is not the currently logged in user we cannot use the session cache
02501                 $useCache = ( $this->ContentObjectID == $http->sessionVariable( 'eZUserLoggedInID' ) );
02502 
02503                 if ( $useCache )
02504                 {
02505                     $userGroupTimestamp = $http->sessionVariable( 'eZUserGroupsCache_Timestamp' );
02506 
02507                     eZExpiryHandler::registerShutdownFunction();
02508                     $handler = eZExpiryHandler::instance();
02509                     $expiredTimeStamp = 0;
02510                     if ( $handler->hasTimestamp( 'user-info-cache' ) )
02511                         $expiredTimeStamp = $handler->timestamp( 'user-info-cache' );
02512 
02513                     if ( $userGroupTimestamp > $expiredTimeStamp )
02514                     {
02515                         if ( $http->hasSessionVariable( 'eZUserGroupsCache' ) )
02516                         {
02517                             $this->Groups = $http->sessionVariable( 'eZUserGroupsCache' );
02518                             return $this->Groups;
02519                         }
02520                     }
02521                 }
02522 
02523                 $contentobjectID = $this->attribute( 'contentobject_id' );
02524 
02525                 $userGroups = false;
02526 
02527                 $userGroups = $db->arrayQuery( "SELECT  c.contentobject_id as id,c.path_string
02528                                                 FROM ezcontentobject_tree  b,
02529                                                      ezcontentobject_tree  c
02530                                                 WHERE b.contentobject_id='$contentobjectID' AND
02531                                                       b.parent_node_id = c.node_id
02532                                                 ORDER BY c.contentobject_id  ");
02533                 $userGroupArray = array();
02534 
02535                 $pathArray = array();
02536                 foreach ( $userGroups as $group )
02537                 {
02538                     $pathItems = explode( '/', $group["path_string"] );
02539                     array_pop( $pathItems );
02540                     array_pop( $pathItems );
02541                     foreach ( $pathItems as $pathItem )
02542                     {
02543                         if ( $pathItem != '' && $pathItem > 1 )
02544                             $pathArray[] = $pathItem;
02545                     }
02546                     $userGroupArray[] = $group['id'];
02547                 }
02548 
02549                 if ( count( $pathArray ) > 0 )
02550                 {
02551                     $pathArray = array_unique ($pathArray);
02552                     $extraGroups = $db->arrayQuery( "SELECT c.contentobject_id as id
02553                                                     FROM ezcontentobject_tree  c,
02554                                                          ezcontentobject d
02555                                                     WHERE c.node_id in ( " . implode( ', ', $pathArray ) . " ) AND
02556                                                           d.id = c.contentobject_id
02557                                                     ORDER BY c.contentobject_id  ");
02558                     foreach ( $extraGroups as $group )
02559                     {
02560                         $userGroupArray[] = $group['id'];
02561                     }
02562                 }
02563 
02564                 if ( $useCache )
02565                 {
02566                     $http->setSessionVariable( 'eZUserGroupsCache', $userGroupArray );
02567                     $http->setSessionVariable( 'eZUserGroupsCache_Timestamp', time() );
02568                 }
02569                 $this->Groups = $userGroupArray;
02570             }
02571             return $this->Groups;
02572         }
02573     }
02574 
02575     /*!
02576      Checks if user is logged in, if not and the site requires user login for access
02577      a module redirect is returned.
02578 
02579      \return null, user login not required.
02580     */
02581     function checkUser( &$siteBasics, $uri )
02582     {
02583         $ini = eZINI::instance();
02584         $http = eZHTTPTool::instance();
02585         $check = array( "module" => "user",
02586                         "function" => "login" );
02587         if ( $http->hasSessionVariable( "eZUserLoggedInID" ) and
02588              $http->sessionVariable( "eZUserLoggedInID" ) != '' and
02589              $http->sessionVariable( "eZUserLoggedInID" ) != $ini->variable( 'UserSettings', 'AnonymousUserID' ) )
02590         {
02591             $currentUser = eZUser::currentUser();
02592             if ( !$currentUser->isEnabled() )
02593             {
02594                 eZUser::logoutCurrent();
02595                 $currentUser = eZUser::currentUser();
02596             }
02597             else
02598             {
02599                 return null;
02600             }
02601         }
02602 
02603         $moduleName = $uri->element();
02604         $viewName = $uri->element( 1 );
02605         $anonymousAccessList = $ini->variable( "SiteAccessSettings", "AnonymousAccessList" );
02606         foreach ( $anonymousAccessList as $anonymousAccess )
02607         {
02608             $elements = explode( '/', $anonymousAccess );
02609             if ( count( $elements ) == 1 )
02610             {
02611                 if ( $moduleName == $elements[0] )
02612                 {
02613                     return null;
02614                 }
02615             }
02616             else
02617             {
02618                 if ( $moduleName == $elements[0] and
02619                      $viewName == $elements[1] )
02620                 {
02621                     return null;
02622                 }
02623             }
02624         }
02625 
02626         return $check;
02627     }
02628 
02629     /*!
02630      Funtion performed before user login info is collected.
02631      It's optional to implement this function in new login handler.
02632 
02633      \return @see eZUserLoginHandler::checkUser()
02634     */
02635     function preCollectUserInfo()
02636     {
02637         return array( 'module' => 'user', 'function' => 'login' );
02638     }
02639 
02640     /*!
02641      Function performed after user login info has been collected.
02642      Store login data as array:
02643      array( 'login' => <username>,
02644             'password' = <password> )
02645      to session variable EZ_LOGIN_HANDLER_USER_INFO for automatic processing of login data.
02646 
02647      \return @see eZUserLoginHandler::checkUser()
02648      */
02649     function postCollectUserInfo()
02650     {
02651         return true;
02652     }
02653 
02654     /*!
02655      Check if login handler require special login URI
02656 
02657      \return Special login uri. If false, use system standard login uri.
02658     */
02659     function loginURI()
02660     {
02661         return false;
02662     }
02663 
02664     /*!
02665      Check if login handler require forced login at user check.
02666 
02667      \return true if force login on user check, false if not.
02668     */
02669     function forceLogin()
02670     {
02671         return false;
02672     }
02673 
02674     /*!
02675      Creates the cache path if it doesn't exist, and returns the cache
02676      directory. The $id parameter is used to create multi-level directory names
02677      \static
02678      \return filename of the cachefile
02679     */
02680     static function getCacheDir( $id = 0 )
02681     {
02682         $sys = eZSys::instance();
02683         $dir = $sys->cacheDirectory() . '/user-info' . eZDir::createMultilevelPath( $id, 2 );
02684 
02685         if ( !is_dir( $dir ) )
02686         {
02687             eZDir::mkdir( $dir, false, true );
02688             // var_dump("MADE DIRECTORY $dir");
02689         }
02690         return $dir;
02691     }
02692 
02693     static function cleanupCache()
02694     {
02695         eZExpiryHandler::registerShutdownFunction();
02696         $handler = eZExpiryHandler::instance();
02697         $handler->setTimestamp( 'user-access-cache', time() );
02698         $handler->setTimestamp( 'user-info-cache', time() );
02699         $handler->store();
02700     }
02701 
02702     /*!
02703      Returns the filename for a cache file with user information
02704      \static
02705      \return filename of the cachefile, or false when the user should not be cached
02706     */
02707     static function getCacheFilename( $id )
02708     {
02709         $ini = eZINI::instance();
02710         $cacheUserPolicies = $ini->variable( 'RoleSettings', 'UserPolicyCache' );
02711         if ( $cacheUserPolicies == 'enabled' )
02712         {
02713             // var_dump("BUILD FILENAME FOR $id");
02714             return eZUser::getCacheDir( $id ). '/user-'. $id . '.cache.php';
02715         }
02716         else if ( $cacheUserPolicies != 'disabled' )
02717         {
02718             $cachableIDs = explode( ',', $cacheUserPolicies );
02719             if ( in_array( $id, $cachableIDs ) )
02720             {
02721                 // var_dump("BUILD FILENAME FOR $id");
02722                 return eZUser::getCacheDir( $id ). '/user-'. $id . '.cache.php';
02723             }
02724         }
02725         // var_dump("NO CACHE FOR $id");
02726         return false;
02727     }
02728 
02729     static function fetchUserClassList( $asObject = false, $fields = false )
02730     {
02731         // Get names of user classes
02732         if ( !$asObject and
02733              is_array( $fields ) and
02734              count( $fields ) > 0 )
02735         {
02736             $fieldsFilter = '';
02737             $i = 0;
02738             foreach ( $fields as $fieldName )
02739             {
02740                 if ( $i > 0 )
02741                     $fieldsFilter .= ', ';
02742                 $fieldsFilter .= 'ezcontentclass.' . $fieldName;
02743                 $i++;
02744             }
02745         }
02746         else
02747         {
02748             $fieldsFilter = 'ezcontentclass.*';
02749         }
02750         $db = eZDB::instance();
02751         $userClasses = $db->arrayQuery( "SELECT $fieldsFilter
02752                                          FROM ezcontentclass, ezcontentclass_attribute
02753                                          WHERE ezcontentclass.id = ezcontentclass_attribute.contentclass_id AND
02754                                                ezcontentclass.version = " . eZContentClass::VERSION_STATUS_DEFINED ." AND
02755                                                ezcontentclass_attribute.version = 0 AND
02756                                                ezcontentclass_attribute.data_type_string = 'ezuser'" );
02757 
02758         return eZPersistentObject::handleRows( $userClasses, "eZContentClass", $asObject );
02759     }
02760 
02761     static function fetchUserClassNames()
02762     {
02763         $userClassNames = array();
02764         $userClasses = eZUser::fetchUserClassList( false, array( 'identifier' ) );
02765         foreach ( $userClasses as $class )
02766         {
02767             $userClassNames[] = $class[ 'identifier' ];
02768         }
02769         return $userClassNames;
02770     }
02771 
02772     static function fetchUserGroupClassNames()
02773     {
02774         // Get names of user classes
02775         $userClassNames = array();
02776         $userClasses = eZUser::fetchUserClassList( false, array( 'identifier' ) );
02777         foreach ( $userClasses as $class )
02778         {
02779             $userClassNames[] = $class[ 'identifier' ];
02780         }
02781 
02782         // Get names of all allowed content-classes for the Users subtree
02783         $contentIni = eZINI::instance( "content.ini" );
02784         $userGroupClassNames = array();
02785         if ( $contentIni->hasVariable( 'ClassGroupIDs', 'Users' ) and
02786              is_numeric( $usersClassGroupID = $contentIni->variable( 'ClassGroupIDs', 'Users' ) ) and
02787              count( $usersClassList = eZContentClassClassGroup::fetchClassList( eZContentClass::VERSION_STATUS_DEFINED, $usersClassGroupID ) ) > 0 )
02788         {
02789             foreach ( $usersClassList as $userClass )
02790             {
02791                 $userGroupClassNames[] = $userClass->attribute( 'identifier' );
02792             }
02793         }
02794 
02795         // Get names of user-group classes
02796         $groupClassNames = array_diff( $userGroupClassNames, $userClassNames );
02797         return $groupClassNames;
02798     }
02799 
02800     /*!
02801      Checks the password for validity
02802      \static
02803      \return true when password is valid by length and not empty, false if not
02804     */
02805     static function validatePassword( $password )
02806     {
02807         $ini = eZINI::instance();
02808         $minPasswordLength = $ini->hasVariable( 'UserSettings', 'MinPasswordLength' ) ? $ini->variable( 'UserSettings', 'MinPasswordLength' ) : 3;
02809         if ( $password !== false and
02810              $password !== null and
02811              strlen( $password ) >= (int) $minPasswordLength )
02812         {
02813             return true;
02814         }
02815 
02816         return false;
02817     }
02818 
02819     /**
02820      * Validates user login name using site.ini[UserSettings]UserNameValidationRegex[]
02821      *
02822      * @static
02823      * @since Version 4.1
02824      * @param string $loginName that we want to validate.
02825      * @param string $errorText by reference for details if validation fails.
02826      * @return bool Indicates if validation failed (false) or not (true).
02827      */
02828     static function validateLoginName( $loginName, &$errorText )
02829     {
02830         $ini = eZINI::instance();
02831         $regexList = $ini->variable( 'UserSettings', 'UserNameValidationRegex' );
02832         $errorTextList = $ini->variable( 'UserSettings', 'UserNameValidationErrorText' );
02833         foreach ( $regexList as $key => $regex )
02834         {
02835             if( preg_match( $regex, $loginName) )
02836             {
02837                 if ( isset( $errorTextList[$key] ) )
02838                     $errorText = $errorTextList[$key];
02839                 else
02840                     $errorText = $ini->variable( 'UserSettings', 'DefaultUserNameValidationErrorText' );
02841                 return false;
02842             }
02843         }
02844         return true;
02845     }
02846 
02847     /**
02848      * Gets the id of the anonymous user.
02849      *
02850      * @static
02851      * @return int User id of anonymous user.
02852      */
02853     public static function anonymousId()
02854     {
02855         if ( self::$anonymousId === null )
02856         {
02857             $ini = eZINI::instance();
02858             self::$anonymousId = (int)$ini->variable( 'UserSettings', 'AnonymousUserID' );
02859             $GLOBALS['eZUserBuiltins'] = array( self::$anonymousId );
02860         }
02861         return self::$anonymousId;
02862     }
02863 
02864     /*!
02865       Returns the IDs of content classes that contain user accounts
02866     */
02867     public static function contentClassIDs()
02868     {
02869         $userContentClassIDs = array();
02870 
02871         $ini = eZINI::instance( 'content.ini' );
02872         $userDatatypes = $ini->variable( "DataTypeSettings", "UserDataTypes" );
02873 
02874         $userContentClassIDs = array();
02875         foreach ( $userDatatypes as $datatypeIdentifier )
02876         {
02877             $userContentClassIDs = array_merge( $userContentClassIDs, eZContentClass::fetchIDListContainingDatatype( $datatypeIdentifier ) );
02878         }
02879 
02880         return $userContentClassIDs;
02881     }
02882 
02883     public function canLoginToSiteAccess( $access )
02884     {
02885         $siteAccessResult = $this->hasAccessTo( 'user', 'login' );
02886         $hasAccessToSite = false;
02887 
02888         if ( $siteAccessResult[ 'accessWord' ] == 'limited' )
02889         {
02890             $siteNameCRC = eZSys::ezcrc32( $access[ 'name' ] );
02891             $policyChecked = false;
02892             foreach ( $siteAccessResult['policies'] as $policy )
02893             {
02894                 if ( isset( $policy['SiteAccess'] ) )
02895                 {
02896                     $policyChecked = true;
02897                     if ( in_array( $siteNameCRC, $policy['SiteAccess'] ) )
02898                     {
02899                         $hasAccessToSite = true;
02900                         break;
02901                     }
02902                 }
02903             }
02904 
02905             if ( !$policyChecked )
02906             {
02907                 $hasAccessToSite = true;
02908             }
02909         }
02910         else if ( $siteAccessResult[ 'accessWord' ] == 'yes' )
02911         {
02912             $hasAccessToSite = true;
02913         }
02914 
02915         return $hasAccessToSite;
02916     }
02917 
02918     /// \privatesection
02919     public $Login;
02920     public $Email;
02921     public $PasswordHash;
02922     public $PasswordHashType;
02923     public $Groups;
02924     public $OriginalPassword;
02925     public $OriginalPasswordConfirm;
02926     
02927     /**
02928     * Used to keep track that a logout was performed, and therefore prevent
02929     * auto-login from happening if an SSO is used
02930     * @var bool
02931     * @since 4.3
02932     **/
02933     protected static $userHasLoggedOut = false;
02934 }
02935 
02936 ?>