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