eZ Publish  [4.1]
ezldapuser.php
Go to the documentation of this file.
00001 <?php
00002 //
00003 // Definition of eZLDAPUser class
00004 //
00005 // Created on: <24-Jul-2003 15:48:06 wy>
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 /*! \file
00032 */
00033 
00034 /*!
00035   \class eZLDAPUser ezldapuser.php
00036   \ingroup eZDatatype
00037   \brief The class eZLDAPUser does
00038 
00039 */
00040 class eZLDAPUser extends eZUser
00041 {
00042     /*!
00043      Constructor
00044     */
00045     function eZLDAPUser()
00046     {
00047     }
00048 
00049     /*!
00050     \static
00051      Logs in the user if applied username and password is
00052      valid. The userID is returned if succesful, false if not.
00053     */
00054     static function loginUser( $login, $password, $authenticationMatch = false )
00055     {
00056         $http = eZHTTPTool::instance();
00057         $db = eZDB::instance();
00058 
00059         if ( $authenticationMatch === false )
00060             $authenticationMatch = eZUser::authenticationMatch();
00061 
00062         $loginEscaped = $db->escapeString( $login );
00063         $passwordEscaped = $db->escapeString( $password );
00064 
00065         $loginLdapEscaped = self::ldap_escape( $login );
00066 
00067         $loginArray = array();
00068         if ( $authenticationMatch & eZUser::AUTHENTICATE_LOGIN )
00069             $loginArray[] = "login='$loginEscaped'";
00070         if ( $authenticationMatch & eZUser::AUTHENTICATE_EMAIL )
00071             $loginArray[] = "email='$loginEscaped'";
00072         if ( count( $loginArray ) == 0 )
00073             $loginArray[] = "login='$loginEscaped'";
00074         $loginText = implode( ' OR ', $loginArray );
00075 
00076         $contentObjectStatus = eZContentObject::STATUS_PUBLISHED;
00077 
00078         $ini = eZINI::instance();
00079         $LDAPIni = eZINI::instance( 'ldap.ini' );
00080         $databaseName = $db->databaseName();
00081         // if mysql
00082         if ( $databaseName === 'mysql' )
00083         {
00084             $query = "SELECT contentobject_id, password_hash, password_hash_type, email, login
00085                       FROM ezuser, ezcontentobject
00086                       WHERE ( $loginText ) AND
00087                             ezcontentobject.status='$contentObjectStatus' AND
00088                             ( ezcontentobject.id=contentobject_id OR ( password_hash_type=4 AND ( $loginText ) AND password_hash=PASSWORD('$passwordEscaped') ) )";
00089         }
00090         else
00091         {
00092             $query = "SELECT contentobject_id, password_hash, password_hash_type, email, login
00093                       FROM ezuser, ezcontentobject
00094                       WHERE ( $loginText ) AND
00095                             ezcontentobject.status='$contentObjectStatus' AND
00096                             ezcontentobject.id=contentobject_id";
00097         }
00098 
00099         $users = $db->arrayQuery( $query );
00100         $exists = false;
00101         if ( count( $users ) >= 1 )
00102         {
00103             foreach ( $users as $userRow )
00104             {
00105                 $userID = $userRow['contentobject_id'];
00106                 $hashType = $userRow['password_hash_type'];
00107                 $hash = $userRow['password_hash'];
00108                 $exists = eZUser::authenticateHash( $userRow['login'], $password, eZUser::site(),
00109                                                     $hashType,
00110                                                     $hash );
00111 
00112                 // If hash type is MySql
00113                 if ( $hashType == eZUser::PASSWORD_HASH_MYSQL and $databaseName === 'mysql' )
00114                 {
00115                     $queryMysqlUser = "SELECT contentobject_id, password_hash, password_hash_type, email, login
00116                                        FROM ezuser, ezcontentobject
00117                                        WHERE ezcontentobject.status='$contentObjectStatus' AND
00118                                              password_hash_type=4 AND ( $loginText ) AND password_hash=PASSWORD('$passwordEscaped') ";
00119                     $mysqlUsers = $db->arrayQuery( $queryMysqlUser );
00120                     if ( count( $mysqlUsers ) >= 1 )
00121                         $exists = true;
00122                 }
00123 
00124                 eZDebugSetting::writeDebug( 'kernel-user', eZUser::createHash( $userRow['login'], $password, eZUser::site(),
00125                                                                                $hashType ), "check hash" );
00126                 eZDebugSetting::writeDebug( 'kernel-user', $hash, "stored hash" );
00127                  // If current user has been disabled after a few failed login attempts.
00128                 $canLogin = eZUser::isEnabledAfterFailedLogin( $userID );
00129 
00130                 if ( $exists )
00131                 {
00132                     // We should store userID for warning message.
00133                     $GLOBALS['eZFailedLoginAttemptUserID'] = $userID;
00134 
00135                     $userSetting = eZUserSetting::fetch( $userID );
00136                     $isEnabled = $userSetting->attribute( "is_enabled" );
00137                     if ( $hashType != eZUser::hashType() and
00138                          strtolower( $ini->variable( 'UserSettings', 'UpdateHash' ) ) == 'true' )
00139                     {
00140                         $hashType = eZUser::hashType();
00141                         $hash = eZUser::createHash( $login, $password, eZUser::site(),
00142                                                     $hashType );
00143                         $db->query( "UPDATE ezuser SET password_hash='$hash', password_hash_type='$hashType' WHERE contentobject_id='$userID'" );
00144                     }
00145                     break;
00146                 }
00147             }
00148         }
00149         if ( $exists and $isEnabled and $canLogin )
00150         {
00151             eZDebugSetting::writeDebug( 'kernel-user', $userRow, 'user row' );
00152             $user = new eZUser( $userRow );
00153             eZDebugSetting::writeDebug( 'kernel-user', $user, 'user' );
00154             $userID = $user->attribute( 'contentobject_id' );
00155 
00156             eZUser::updateLastVisit( $userID );
00157             eZUser::setCurrentlyLoggedInUser( $user, $userID );
00158 
00159             // Reset number of failed login attempts
00160             eZUser::setFailedLoginAttempts( $userID, 0 );
00161 
00162             return $user;
00163         }
00164         else if ( $LDAPIni->variable( 'LDAPSettings', 'LDAPEnabled' ) === 'true' )
00165         {
00166             // read LDAP ini settings
00167             // and then try to bind to the ldap server
00168 
00169             $LDAPDebugTrace         = $LDAPIni->variable( 'LDAPSettings', 'LDAPDebugTrace' ) === 'enabled';
00170             $LDAPVersion            = $LDAPIni->variable( 'LDAPSettings', 'LDAPVersion' );
00171             $LDAPServer             = $LDAPIni->variable( 'LDAPSettings', 'LDAPServer' );
00172             $LDAPPort               = $LDAPIni->variable( 'LDAPSettings', 'LDAPPort' );
00173             $LDAPFollowReferrals    = (int) $LDAPIni->variable( 'LDAPSettings', 'LDAPFollowReferrals' );
00174             $LDAPBaseDN             = $LDAPIni->variable( 'LDAPSettings', 'LDAPBaseDn' );
00175             $LDAPBindUser           = $LDAPIni->variable( 'LDAPSettings', 'LDAPBindUser' );
00176             $LDAPBindPassword       = $LDAPIni->variable( 'LDAPSettings', 'LDAPBindPassword' );
00177             $LDAPSearchScope        = $LDAPIni->variable( 'LDAPSettings', 'LDAPSearchScope' );
00178 
00179             $LDAPLoginAttribute     = strtolower( $LDAPIni->variable( 'LDAPSettings', 'LDAPLoginAttribute' ) );
00180             $LDAPFirstNameAttribute = strtolower( $LDAPIni->variable( 'LDAPSettings', 'LDAPFirstNameAttribute' ) );
00181             $LDAPFirstNameIsCN      = $LDAPIni->variable( 'LDAPSettings', 'LDAPFirstNameIsCommonName' ) === 'true';
00182             $LDAPLastNameAttribute  = strtolower( $LDAPIni->variable( 'LDAPSettings', 'LDAPLastNameAttribute' ) );
00183             $LDAPEmailAttribute     = strtolower( $LDAPIni->variable( 'LDAPSettings', 'LDAPEmailAttribute' ) );
00184 
00185             $defaultUserPlacement   = $ini->variable( "UserSettings", "DefaultUserPlacement" );
00186 
00187             $LDAPUserGroupAttributeType = strtolower( $LDAPIni->variable( 'LDAPSettings', 'LDAPUserGroupAttributeType' ) );
00188             $LDAPUserGroupAttribute     = strtolower( $LDAPIni->variable( 'LDAPSettings', 'LDAPUserGroupAttribute' ) );
00189 
00190             if ( $LDAPIni->hasVariable( 'LDAPSettings', 'Utf8Encoding' ) )
00191             {
00192                 $Utf8Encoding = $LDAPIni->variable( 'LDAPSettings', 'Utf8Encoding' );
00193                 if ( $Utf8Encoding == "true" )
00194                     $isUtf8Encoding = true;
00195                 else
00196                     $isUtf8Encoding = false;
00197             }
00198             else
00199             {
00200                 $isUtf8Encoding = false;
00201             }
00202 
00203             if ( $LDAPIni->hasVariable( 'LDAPSettings', 'LDAPSearchFilters' ) )
00204             {
00205                 $LDAPFilters = $LDAPIni->variable( 'LDAPSettings', 'LDAPSearchFilters' );
00206             }
00207             if ( $LDAPIni->hasVariable( 'LDAPSettings', 'LDAPUserGroupType' ) and  $LDAPIni->hasVariable( 'LDAPSettings', 'LDAPUserGroup' ) )
00208             {
00209                 $LDAPUserGroupType = $LDAPIni->variable( 'LDAPSettings', 'LDAPUserGroupType' );
00210                 $LDAPUserGroup = $LDAPIni->variable( 'LDAPSettings', 'LDAPUserGroup' );
00211             }
00212 
00213             $LDAPFilter = "( &";
00214             if ( count( $LDAPFilters ) > 0 )
00215             {
00216                 foreach ( array_keys( $LDAPFilters ) as $key )
00217                 {
00218                     $LDAPFilter .= "(" . $LDAPFilters[$key] . ")";
00219                 }
00220             }
00221             $LDAPEqualSign = trim($LDAPIni->variable( 'LDAPSettings', "LDAPEqualSign" ) );
00222             $LDAPBaseDN    = str_replace( $LDAPEqualSign, "=", $LDAPBaseDN );
00223             $LDAPFilter    = str_replace( $LDAPEqualSign, "=", $LDAPFilter );
00224             $LDAPBindUser  = str_replace( $LDAPEqualSign, "=", $LDAPBindUser );
00225 
00226             if ( $LDAPDebugTrace )
00227             {
00228                 $debugArray = array( 'stage' => '1/5: Connecting and Binding to LDAP server',
00229                                      'LDAPServer' => $LDAPServer,
00230                                      'LDAPPort' => $LDAPPort,
00231                                      'LDAPBindUser' => $LDAPBindUser,
00232                                      'LDAPVersion' => $LDAPVersion
00233                 );
00234                 // Set debug trace mode for ldap connections
00235                 if ( function_exists( 'ldap_set_option' ) )
00236                     ldap_set_option(NULL, LDAP_OPT_DEBUG_LEVEL, 7);
00237                 eZDebug::writeNotice( var_export( $debugArray, true ), 'eZLDAPUser::loginUser' );
00238             }
00239 
00240             if ( function_exists( 'ldap_connect' ) )
00241                 $ds = ldap_connect( $LDAPServer, $LDAPPort );
00242             else
00243                 $ds = false;
00244 
00245             if ( $ds )
00246             {
00247                 ldap_set_option( $ds, LDAP_OPT_PROTOCOL_VERSION, $LDAPVersion );
00248                 ldap_set_option( $ds, LDAP_OPT_REFERRALS, $LDAPFollowReferrals );
00249                 if ( $LDAPBindUser == '' )
00250                 {
00251                     $r = ldap_bind( $ds );
00252                 }
00253                 else
00254                 {
00255                     $r = ldap_bind( $ds, $LDAPBindUser, $LDAPBindPassword );
00256                 }
00257                 if ( !$r )
00258                 {
00259                     // Increase number of failed login attempts.
00260                     eZDebug::writeError( 'Cannot bind to LDAP server, might be something wronge with connetion or bind user!', 'eZLDAPUser::loginUser()' );
00261                     if ( isset( $userID ) )
00262                         eZUser::setFailedLoginAttempts( $userID );
00263 
00264                     $user = false;
00265                     return $user;
00266                 }
00267 
00268                 $LDAPFilter .= "($LDAPLoginAttribute=$loginLdapEscaped)";
00269                 $LDAPFilter .= ")";
00270 
00271                 ldap_set_option( $ds, LDAP_OPT_SIZELIMIT, 0 );
00272                 ldap_set_option( $ds, LDAP_OPT_TIMELIMIT, 0 );
00273 
00274                 $retrieveAttributes = array( $LDAPLoginAttribute,
00275                                              $LDAPFirstNameAttribute,
00276                                              $LDAPLastNameAttribute,
00277                                              $LDAPEmailAttribute );
00278                 if ( $LDAPUserGroupAttributeType )
00279                     $retrieveAttributes[] = $LDAPUserGroupAttribute;
00280 
00281                 if ( $LDAPDebugTrace )
00282                 {
00283                     $debugArray = array( 'stage' => '2/5: finding user',
00284                                          'LDAPFilter' => $LDAPFilter,
00285                                          'retrieveAttributes' => $retrieveAttributes,
00286                                          'LDAPSearchScope' => $LDAPSearchScope,
00287                                          'LDAPBaseDN' => $LDAPBaseDN
00288                     );
00289                     eZDebug::writeNotice( var_export( $debugArray, true ), 'eZLDAPUser::loginUser' );
00290                 }
00291 
00292                 if ( $LDAPSearchScope == "one" )
00293                     $sr = ldap_list( $ds, $LDAPBaseDN, $LDAPFilter, $retrieveAttributes );
00294                 else if ( $LDAPSearchScope == "base" )
00295                     $sr = ldap_read( $ds, $LDAPBaseDN, $LDAPFilter, $retrieveAttributes );
00296                 else
00297                     $sr = ldap_search( $ds, $LDAPBaseDN, $LDAPFilter, $retrieveAttributes );
00298 
00299                 $info = ldap_get_entries( $ds, $sr ) ;
00300                 if ( $info['count'] > 1 )
00301                 {
00302                     // More than one user with same uid, not allow login.
00303                     eZDebug::writeWarning( 'More then one user with same uid, not allowed to login!', 'eZLDAPUser::loginUser()' );
00304                     $user = false;
00305                     return $user;
00306                 }
00307                 else if ( $info['count'] < 1 )
00308                 {
00309                     // Increase number of failed login attempts.
00310                     if ( isset( $userID ) )
00311                         eZUser::setFailedLoginAttempts( $userID );
00312 
00313                     // user DN was not found
00314                     eZDebug::writeWarning( 'User DN was not found!', 'eZLDAPUser::loginUser()' );
00315                     $user = false;
00316                     return $user;
00317                 }
00318                 else if ( $LDAPDebugTrace )
00319                 {
00320                     $debugArray = array( 'stage' => '3/5: real authentication of user',
00321                                          'info' => $info
00322                     );
00323                     eZDebug::writeNotice( var_export( $debugArray, true ), 'eZLDAPUser::loginUser' );
00324                 }
00325 
00326                 if( !$password )
00327                 {
00328                     $password = crypt( microtime() );
00329                 }
00330 
00331                 // is it real authenticated LDAP user?
00332                 if  ( !@ldap_bind( $ds, $info[0]['dn'], $password ) )
00333                 {
00334                     // Increase number of failed login attempts.
00335                     if ( isset( $userID ) )
00336                         eZUser::setFailedLoginAttempts( $userID );
00337 
00338                     eZDebug::writeWarning( "User $userID failed to login!", 'eZLDAPUser::loginUser()' );
00339                     $user = false;
00340                     return $user;
00341                 }
00342 
00343                 $extraNodeAssignments = array();
00344                 $userGroupClassID = $ini->variable( "UserSettings", "UserGroupClassID" );
00345 
00346                 // default user group assigning
00347                 if ( $LDAPUserGroupType != null )
00348                 {
00349                     if ( $LDAPUserGroupType == "name" )
00350                     {
00351                         if ( is_array( $LDAPUserGroup ) )
00352                         {
00353                             foreach ( array_keys( $LDAPUserGroup ) as $key )
00354                             {
00355                                 $groupName = $LDAPUserGroup[$key];
00356                                 $groupQuery = "SELECT ezcontentobject_tree.node_id
00357                                                  FROM ezcontentobject, ezcontentobject_tree
00358                                                 WHERE ezcontentobject.name like '$groupName'
00359                                                   AND ezcontentobject.id=ezcontentobject_tree.contentobject_id
00360                                                   AND ezcontentobject.contentclass_id=$userGroupClassID";
00361                                 $groupObject = $db->arrayQuery( $groupQuery );
00362                                 if ( count( $groupObject ) > 0 and $key == 0 )
00363                                 {
00364                                     $defaultUserPlacement = $groupObject[0]['node_id'];
00365                                 }
00366                                 else if ( count( $groupObject ) > 0 )
00367                                 {
00368                                     $extraNodeAssignments[] = $groupObject[0]['node_id'];
00369                                 }
00370                             }
00371                         }
00372                         else
00373                         {
00374                             $groupName = $LDAPUserGroup;
00375                             $groupQuery = "SELECT ezcontentobject_tree.node_id
00376                                              FROM ezcontentobject, ezcontentobject_tree
00377                                             WHERE ezcontentobject.name like '$groupName'
00378                                               AND ezcontentobject.id=ezcontentobject_tree.contentobject_id
00379                                               AND ezcontentobject.contentclass_id=$userGroupClassID";
00380                             $groupObject = $db->arrayQuery( $groupQuery );
00381 
00382                             if ( count( $groupObject ) > 0  )
00383                             {
00384                                 $defaultUserPlacement = $groupObject[0]['node_id'];
00385                             }
00386                         }
00387                     }
00388                     else if ( $LDAPUserGroupType == "id" )
00389                     {
00390                         if ( is_array( $LDAPUserGroup ) )
00391                         {
00392                             foreach ( array_keys( $LDAPUserGroup ) as $key )
00393                             {
00394                                 $groupID = $LDAPUserGroup[$key];
00395                                 $groupQuery = "SELECT ezcontentobject_tree.node_id
00396                                                  FROM ezcontentobject, ezcontentobject_tree
00397                                                 WHERE ezcontentobject.id='$groupID'
00398                                                   AND ezcontentobject.id=ezcontentobject_tree.contentobject_id
00399                                                   AND ezcontentobject.contentclass_id=$userGroupClassID";
00400                                 $groupObject = $db->arrayQuery( $groupQuery );
00401                                 if ( count( $groupObject ) > 0 and $key == 0 )
00402                                 {
00403                                     $defaultUserPlacement = $groupObject[0]['node_id'];
00404                                 }
00405                                 else if ( count( $groupObject ) > 0 )
00406                                 {
00407                                     $extraNodeAssignments[] = $groupObject[0]['node_id'];
00408                                 }
00409                             }
00410                         }
00411                         else
00412                         {
00413                             $groupID = $LDAPUserGroup;
00414                             $groupQuery = "SELECT ezcontentobject_tree.node_id
00415                                              FROM ezcontentobject, ezcontentobject_tree
00416                                             WHERE ezcontentobject.id='$groupID'
00417                                               AND ezcontentobject.id=ezcontentobject_tree.contentobject_id
00418                                               AND ezcontentobject.contentclass_id=$userGroupClassID";
00419                             $groupObject = $db->arrayQuery( $groupQuery );
00420 
00421                             if ( count( $groupObject ) > 0  )
00422                             {
00423                                 $defaultUserPlacement = $groupObject[0]['node_id'];
00424                             }
00425                         }
00426                     }
00427                 }
00428 
00429                 // read group mapping LDAP settings
00430                 $LDAPGroupMappingType = $LDAPIni->variable( 'LDAPSettings', 'LDAPGroupMappingType' );
00431                 $LDAPUserGroupMap     = $LDAPIni->variable( 'LDAPSettings', 'LDAPUserGroupMap' );
00432 
00433                 if ( !is_array( $LDAPUserGroupMap ) )
00434                     $LDAPUserGroupMap = array();
00435 
00436                 // group mapping constants
00437                 $ByMemberAttribute             = 'SimpleMapping'; // by group's member attributes (with mapping)
00438                 $ByMemberAttributeHierarhicaly = 'GetGroupsTree'; // by group's member attributes hierarhically
00439                 $ByGroupAttribute              = 'UseGroupAttribute'; // by user's group attribute (old style)
00440                 $groupMappingTypes = array( $ByMemberAttribute,
00441                                             $ByMemberAttributeHierarhicaly,
00442                                             $ByGroupAttribute);
00443 
00444                 $userData =& $info[ 0 ];
00445 
00446                 // default mapping using old style
00447                 if ( !in_array( $LDAPGroupMappingType, $groupMappingTypes ) )
00448                 {
00449                     $LDAPGroupMappingType = $ByGroupAttribute;
00450                 }
00451 
00452                 if ( $LDAPDebugTrace )
00453                 {
00454                     $debugArray = array( 'stage' => '4/5: group mapping init',
00455                                          'LDAPUserGroupType' => $LDAPUserGroupType,
00456                                          'LDAPGroupMappingType' => $LDAPGroupMappingType,
00457                                          'LDAPUserGroup' => $LDAPUserGroup,
00458                                          'defaultUserPlacement' => $defaultUserPlacement,
00459                                          'extraNodeAssignments' => $extraNodeAssignments
00460                     );
00461                     eZDebug::writeNotice( var_export( $debugArray, true ), 'eZLDAPUser::loginUser' );
00462                 }
00463 
00464                 if ( $LDAPGroupMappingType == $ByMemberAttribute or
00465                      $LDAPGroupMappingType == $ByMemberAttributeHierarhicaly )
00466                 {
00467                     $LDAPGroupBaseDN          = $LDAPIni->variable( 'LDAPSettings', 'LDAPGroupBaseDN' );
00468                     $LDAPGroupBaseDN          = str_replace( $LDAPEqualSign, '=', $LDAPGroupBaseDN );
00469                     $LDAPGroupClass           = $LDAPIni->variable( 'LDAPSettings', 'LDAPGroupClass' );
00470 
00471                     $LDAPGroupNameAttribute   = strtolower( $LDAPIni->variable( 'LDAPSettings', 'LDAPGroupNameAttribute' ) );
00472                     $LDAPGroupMemberAttribute = strtolower( $LDAPIni->variable( 'LDAPSettings', 'LDAPGroupMemberAttribute' ) );
00473                     $LDAPGroupDescriptionAttribute = strtolower( $LDAPIni->variable( 'LDAPSettings', 'LDAPGroupDescriptionAttribute' ) );
00474 
00475                     $groupSearchingDepth = ( $LDAPGroupMappingType == '1' ) ? 1 : 1000;
00476 
00477                     // now, get all parents for currently ldap authenticated user
00478                     $requiredParams = array();
00479                     $requiredParams[ 'LDAPLoginAttribute' ]       = $LDAPLoginAttribute;
00480                     $requiredParams[ 'LDAPGroupBaseDN' ]          = $LDAPGroupBaseDN;
00481                     $requiredParams[ 'LDAPGroupClass' ]           = $LDAPGroupClass;
00482                     $requiredParams[ 'LDAPGroupNameAttribute' ]   = $LDAPGroupNameAttribute;
00483                     $requiredParams[ 'LDAPGroupMemberAttribute' ] = $LDAPGroupMemberAttribute;
00484                     $requiredParams[ 'LDAPGroupDescriptionAttribute' ] = $LDAPGroupDescriptionAttribute;
00485                     $requiredParams[ 'ds' ] =& $ds;
00486                     if ( $LDAPIni->variable( 'LDAPSettings', 'LDAPGroupRootNodeId' ) !== '' )
00487                         $requiredParams[ 'TopUserGroupNodeID' ] = $LDAPIni->variable( 'LDAPSettings', 'LDAPGroupRootNodeId' );
00488                     else
00489                         $requiredParams[ 'TopUserGroupNodeID' ] = 5;
00490 
00491                     $groupsTree = array();
00492                     $stack = array();
00493                     $newfilter = '(&(objectClass=' . $LDAPGroupClass . ')(' . $LDAPGroupMemberAttribute . '=' . $userData['dn'] . '))';
00494 
00495                     $groupsTree[ $userData['dn'] ] = array( 'data' => & $userData,
00496                                                                 'parents' => array(),
00497                                                                 'children' => array() );
00498 
00499                     eZLDAPUser::getUserGroupsTree( $requiredParams, $newfilter, $userData['dn'], $groupsTree, $stack, $groupSearchingDepth );
00500                     $userRecord =& $groupsTree[ $userData['dn'] ];
00501 
00502                     if ( $LDAPGroupMappingType == $ByMemberAttribute )
00503                     {
00504                         if ( count( $userRecord[ 'parents' ] ) > 0 )
00505                         {
00506                             $remappedGroupNames = array();
00507                             foreach ( array_keys( $userRecord[ 'parents' ] ) as $key )
00508                             {
00509                                 $parentGroup =& $userRecord[ 'parents' ][ $key ];
00510                                 if ( isset( $parentGroup[ 'data' ][ $LDAPGroupNameAttribute ] ) )
00511                                 {
00512                                     $ldapGroupName = $parentGroup[ 'data' ][ $LDAPGroupNameAttribute ];
00513                                     if ( is_array( $ldapGroupName ) )
00514                                     {
00515                                         $ldapGroupName = ( $ldapGroupName[ 'count' ] > 0 ) ? $ldapGroupName[ 0 ] : '';
00516                                     }
00517 
00518                                     // remap group name and check that group exists
00519                                     if ( array_key_exists( $ldapGroupName, $LDAPUserGroupMap ) )
00520                                     {
00521                                         $remmapedGroupName = $LDAPUserGroupMap[ $ldapGroupName ];
00522                                         $groupQuery = "SELECT ezcontentobject_tree.node_id
00523                                                          FROM ezcontentobject, ezcontentobject_tree
00524                                                         WHERE ezcontentobject.name like '$remmapedGroupName'
00525                                                           AND ezcontentobject.id=ezcontentobject_tree.contentobject_id
00526                                                           AND ezcontentobject.contentclass_id=$userGroupClassID";
00527                                         $groupRow = $db->arrayQuery( $groupQuery );
00528 
00529                                         if ( count( $groupRow ) > 0 )
00530                                         {
00531                                             $userRecord['new_parents'][] = $groupRow[ 0 ][ 'node_id' ];
00532                                         }
00533                                     }
00534                                 }
00535                             }
00536                         }
00537                     }
00538                     else if ( $LDAPGroupMappingType == $ByMemberAttributeHierarhicaly )
00539                     {
00540                         $stack = array();
00541                         self::goAndPublishGroups( $requiredParams, $userData['dn'], $groupsTree, $stack, $groupSearchingDepth, true );
00542                     }
00543                     if ( isset( $userRecord['new_parents'] ) and
00544                          count( $userRecord['new_parents'] ) > 0 )
00545                     {
00546                         $defaultUserPlacement = $userRecord['new_parents'][0];
00547                         $extraNodeAssignments = array_merge( $extraNodeAssignments, $userRecord['new_parents'] );
00548                     }
00549                 }
00550                 else if ( $LDAPGroupMappingType == $ByGroupAttribute ) // old style mapping by group (employeetype) attribute
00551                 {
00552                     if ( $LDAPUserGroupAttributeType )
00553                     {
00554                         $groupAttributeCount = $info[0][$LDAPUserGroupAttribute]['count'];
00555                         if ( $LDAPUserGroupAttributeType == "name" )
00556                         {
00557                             for ( $i = 0; $i < $groupAttributeCount; $i++ )
00558                             {
00559                                 if ( $isUtf8Encoding )
00560                                 {
00561                                     $groupName = utf8_decode( $info[0][$LDAPUserGroupAttribute][$i] );
00562                                 }
00563                                 else
00564                                 {
00565                                     $groupName = $info[0][$LDAPUserGroupAttribute][$i];
00566                                 }
00567                                 if ( $groupName != null )
00568                                 {
00569                                     $groupQuery = "SELECT ezcontentobject_tree.node_id
00570                                                      FROM ezcontentobject, ezcontentobject_tree
00571                                                     WHERE ezcontentobject.name like '$groupName'
00572                                                       AND ezcontentobject.id=ezcontentobject_tree.contentobject_id
00573                                                       AND ezcontentobject.contentclass_id=$userGroupClassID";
00574                                     $groupObject = $db->arrayQuery( $groupQuery );
00575 
00576                                     if ( count( $groupObject ) > 0 and $i == 0 )
00577                                     {
00578                                         $defaultUserPlacement = $groupObject[0]['node_id'];
00579                                     }
00580                                     else if ( count( $groupObject ) > 0 )
00581                                     {
00582                                         $extraNodeAssignments[] = $groupObject[0]['node_id'];
00583                                     }
00584                                 }
00585                             }
00586                         }
00587                         else if ( $LDAPUserGroupAttributeType == "id" )
00588                         {
00589                             for ( $i = 0; $i < $groupAttributeCount; $i++ )
00590                             {
00591                                 if ( $isUtf8Encoding )
00592                                 {
00593                                     $groupID = utf8_decode( $info[0][$LDAPUserGroupAttribute][$i] );
00594                                 }
00595                                 else
00596                                 {
00597                                     $groupID = $info[0][$LDAPUserGroupAttribute][$i];
00598                                 }
00599 
00600                                 if ( $groupID != null )
00601                                 {
00602                                     $groupName = "LDAP " . $groupID;
00603                                     $groupQuery = "SELECT ezcontentobject_tree.node_id
00604                                                      FROM ezcontentobject, ezcontentobject_tree
00605                                                     WHERE ezcontentobject.name like '$groupName'
00606                                                       AND ezcontentobject.id=ezcontentobject_tree.contentobject_id
00607                                                       AND ezcontentobject.contentclass_id=$userGroupClassID";
00608                                     $groupObject = $db->arrayQuery( $groupQuery );
00609 
00610                                     if ( count( $groupObject ) > 0 and $i == 0 )
00611                                     {
00612                                         $defaultUserPlacement = $groupObject[0]['node_id'];
00613                                     }
00614                                     else if ( count( $groupObject ) > 0 )
00615                                     {
00616                                         $extraNodeAssignments[] = $groupObject[0]['node_id'];
00617                                     }
00618                                 }
00619                             }
00620                         }
00621                     }
00622                 }
00623 
00624                 // remove ' last_name' from first_name if cn is used for first name
00625                 if ( $LDAPFirstNameIsCN && isset( $userData[ $LDAPFirstNameAttribute ] ) && isset( $userData[ $LDAPLastNameAttribute ] ) )
00626                 {
00627                     $userData[ $LDAPFirstNameAttribute ][0] = str_replace( ' ' . $userData[ $LDAPLastNameAttribute ][0], '', $userData[ $LDAPFirstNameAttribute ][0] );
00628                 }
00629 
00630                 if ( isset( $userData[ $LDAPEmailAttribute ] ) )
00631                     $LDAPuserEmail = $userData[ $LDAPEmailAttribute ][0];
00632                 else if( trim( $LDAPIni->variable( 'LDAPSettings', 'LDAPEmailEmptyAttributeSuffix' ) ) )
00633                     $LDAPuserEmail = $login . $LDAPIni->variable( 'LDAPSettings', 'LDAPEmailEmptyAttributeSuffix' );
00634                 else
00635                     $LDAPuserEmail = false;
00636 
00637 
00638                 $userAttributes = array( 'login'      => $login,
00639                                          'first_name' => isset( $userData[ $LDAPFirstNameAttribute ] ) ? $userData[ $LDAPFirstNameAttribute ][0] : false,
00640                                          'last_name'  => isset( $userData[ $LDAPLastNameAttribute ] ) ? $userData[ $LDAPLastNameAttribute ][0] : false,
00641                                          'email'      => $LDAPuserEmail );
00642 
00643                 if ( $LDAPDebugTrace )
00644                 {
00645                     $debugArray = array( 'stage' => '5/5: storing user',
00646                                          'userAttributes' => $userAttributes,
00647                                          'isUtf8Encoding' => $isUtf8Encoding,
00648                                          'defaultUserPlacement' => $defaultUserPlacement,
00649                                          'extraNodeAssignments' => $extraNodeAssignments
00650                     );
00651                     eZDebug::writeNotice( var_export( $debugArray, true ), 'eZLDAPUser::loginUser' );
00652                 }
00653 
00654                 $oldUser = clone eZUser::currentUser();
00655                 $existingUser = eZLDAPUser::publishUpdateUser( $extraNodeAssignments, $defaultUserPlacement, $userAttributes, $isUtf8Encoding );
00656 
00657                 if ( is_object( $existingUser ) )
00658                 {
00659                     eZUser::setCurrentlyLoggedInUser( $existingUser, $existingUser->attribute( 'contentobject_id' ) );
00660                 }
00661                 else
00662                 {
00663                     eZUser::setCurrentlyLoggedInUser( $oldUser, $oldUser->attribute( 'contentobject_id' ) );
00664                 }
00665 
00666                 ldap_close( $ds );
00667                 return $existingUser;
00668             }
00669             else
00670             {
00671                 eZDebug::writeError( 'Cannot initialize connection for LDAP server', 'eZLDAPUser::loginUser()' );
00672                 $user = false;
00673                 return $user;
00674             }
00675         }
00676         else
00677         {
00678             // Increase number of failed login attempts.
00679             if ( isset( $userID ) )
00680                 eZUser::setFailedLoginAttempts( $userID );
00681 
00682             eZDebug::writeWarning( 'User does not exist or LDAP is not enabled in php', 'eZLDAPUser::loginUser()' );
00683             $user = false;
00684             return $user;
00685         }
00686     }
00687 
00688     /*
00689         Static method, for internal usage only.
00690         Publishes new or update existing user
00691     */
00692     static function publishUpdateUser( $parentNodeIDs, $defaultUserPlacement, $userAttributes, $isUtf8Encoding = false )
00693     {
00694         $thisFunctionErrorLabel = 'eZLDAPUser.php, function publishUpdateUser()';
00695 
00696         if ( !is_array( $userAttributes ) or
00697              !isset( $userAttributes[ 'login' ] ) or empty( $userAttributes[ 'login' ] ) )
00698         {
00699             eZDebug::writeWarning( 'Empty user login passed.',
00700                                    $thisFunctionErrorLabel );
00701             return false;
00702         }
00703 
00704         if ( ( !is_array( $parentNodeIDs ) or count( $parentNodeIDs ) < 1 ) and
00705              !is_numeric( $defaultUserPlacement ) )
00706         {
00707             eZDebug::writeWarning( 'No one parent node IDs was passed for publishing new user (login = "' .
00708                                    $userAttributes[ 'login' ] . '")',
00709                                    $thisFunctionErrorLabel );
00710             return false;
00711         }
00712         $parentNodeIDs[] = $defaultUserPlacement;
00713         $parentNodeIDs = array_unique( $parentNodeIDs );
00714 
00715 
00716         $login      = $userAttributes[ 'login' ];
00717         $first_name = $userAttributes[ 'first_name' ];
00718         $last_name  = $userAttributes[ 'last_name' ];
00719         $email      = $userAttributes[ 'email' ];
00720 
00721         if ( $isUtf8Encoding )
00722         {
00723             $first_name = utf8_decode( $first_name );
00724             $last_name = utf8_decode( $last_name );
00725         }
00726 
00727         $user = eZUser::fetchByName( $login );
00728         $createNewUser = ( is_object( $user ) ) ? false : true;
00729 
00730         if ( $createNewUser )
00731         {
00732             if ( !isset( $first_name ) or empty( $first_name ) or
00733                  !isset( $last_name ) or empty( $last_name ) or
00734                  !isset( $email ) or empty( $email ) )
00735             {
00736                 eZDebug::writeWarning( 'Cannot create user with empty first name (last name or email).',
00737                                        $thisFunctionErrorLabel );
00738                 return false;
00739             }
00740 
00741             $ini = eZINI::instance();
00742             $userClassID = $ini->variable( "UserSettings", "UserClassID" );
00743             $userCreatorID = $ini->variable( "UserSettings", "UserCreatorID" );
00744             $defaultSectionID = $ini->variable( "UserSettings", "DefaultSectionID" );
00745 
00746             $class = eZContentClass::fetch( $userClassID );
00747             $contentObject = $class->instantiate( $userCreatorID, $defaultSectionID );
00748 
00749             $contentObject->store();
00750 
00751             $userID = $contentObjectID = $contentObject->attribute( 'id' );
00752 
00753             $version = $contentObject->version( 1 );
00754             $version->setAttribute( 'modified', time() );
00755             $version->setAttribute( 'status', eZContentObjectVersion::STATUS_DRAFT );
00756             $version->store();
00757 
00758             $user = eZLDAPUser::create( $userID );
00759             $user->setAttribute( 'login', $login );
00760         }
00761         else
00762         {
00763             $userID = $contentObjectID = $user->attribute( 'contentobject_id' );
00764             $contentObject = eZContentObject::fetch( $userID );
00765             $version = $contentObject->attribute( 'current' );
00766         }
00767 
00768         //================= common part 1: start ========================
00769         $contentObjectAttributes = $version->contentObjectAttributes();
00770 
00771         // find and set 'name' and 'description' attributes (as standard user group class)
00772         $firstNameIdentifier = 'first_name';
00773         $lastNameIdentifier = 'last_name';
00774         $firstNameAttribute = null;
00775         $lastNameAttribute = null;
00776 
00777         foreach ( $contentObjectAttributes as $attribute )
00778         {
00779             if ( $attribute->attribute( 'contentclass_attribute_identifier' ) == $firstNameIdentifier )
00780             {
00781                 $firstNameAttribute = $attribute;
00782             }
00783             else if ( $attribute->attribute( 'contentclass_attribute_identifier' ) == $lastNameIdentifier )
00784             {
00785                 $lastNameAttribute = $attribute;
00786             }
00787         }
00788         //================= common part 1: end ==========================
00789 
00790         // If we are updating an existing user, we must find out if some data should be changed.
00791         // In that case, we must create a new version and publish it.
00792         if ( !$createNewUser )
00793         {
00794             $userDataChanged = false;
00795             $firstNameChanged = false;
00796             $lastNameChanged = false;
00797             $emailChanged = false;
00798 
00799             if ( $firstNameAttribute and $firstNameAttribute->attribute( 'data_text' ) != $first_name )
00800             {
00801                 $firstNameChanged = true;
00802             }
00803             $firstNameAttribute = false; // We will load this again from the new version we will create, if it has changed
00804             if ( $lastNameAttribute and $lastNameAttribute->attribute( 'data_text' ) != $last_name )
00805             {
00806                 $lastNameChanged = true;
00807             }
00808             $lastNameAttribute = false; // We will load this again from the new version we will create, if it has changed
00809             if ( $user->attribute( 'email' ) != $email )
00810             {
00811                 $emailChanged = true;
00812             }
00813 
00814             if ( $firstNameChanged or $lastNameChanged or $emailChanged )
00815             {
00816                 $userDataChanged = true;
00817                 // Create new version
00818                 $version = $contentObject->createNewVersion();
00819                 $contentObjectAttributes = $version->contentObjectAttributes();
00820                 foreach ( $contentObjectAttributes as $attribute )
00821                 {
00822                     if ( $attribute->attribute( 'contentclass_attribute_identifier' ) == $firstNameIdentifier )
00823                     {
00824                         $firstNameAttribute = $attribute;
00825                     }
00826                     else if ( $attribute->attribute( 'contentclass_attribute_identifier' ) == $lastNameIdentifier )
00827                     {
00828                         $lastNameAttribute = $attribute;
00829                     }
00830                 }
00831             }
00832         }
00833 
00834         //================= common part 2: start ========================
00835         if ( $firstNameAttribute )
00836         {
00837             $firstNameAttribute->setAttribute( 'data_text', $first_name );
00838             $firstNameAttribute->store();
00839         }
00840         if ( $lastNameAttribute )
00841         {
00842             $lastNameAttribute->setAttribute( 'data_text', $last_name );
00843             $lastNameAttribute->store();
00844         }
00845 
00846         if ( !isset( $userDataChanged ) or $userDataChanged === true )
00847         {
00848             $contentClass = $contentObject->attribute( 'content_class' );
00849             $name = $contentClass->contentObjectName( $contentObject );
00850             $contentObject->setName( $name );
00851         }
00852 
00853         if ( !isset( $emailChanged ) or $emailChanged === true )
00854         {
00855             $user->setAttribute( 'email', $email );
00856         }
00857 
00858         $user->setAttribute( 'password_hash', "" );
00859         $user->setAttribute( 'password_hash_type', 0 );
00860         $user->store();
00861 
00862         $debugArray = array( 'Updating user data',
00863                              'createNewUser' => $createNewUser,
00864                              'userDataChanged' => isset( $userDataChanged ) ? $userDataChanged : null,
00865                              'login' => $login,
00866                              'first_name' => $first_name,
00867                              'last_name' => $last_name,
00868                              'email' => $email,
00869                              'firstNameAttribute is_object' => is_object( $firstNameAttribute ),
00870                              'lastNameAttribute is_object' => is_object( $lastNameAttribute ),
00871                              'content object id' => $contentObjectID,
00872                              'version id' => $version->attribute( 'version' )
00873         );
00874         eZDebug::writeNotice( var_export( $debugArray, true ), __METHOD__ );
00875         //================= common part 2: end ==========================
00876 
00877         if ( $createNewUser )
00878         {
00879             reset( $parentNodeIDs );
00880             // prepare node assignments for publishing new user
00881             foreach( $parentNodeIDs as $parentNodeID )
00882             {
00883                 $newNodeAssignment = eZNodeAssignment::create( array( 'contentobject_id' => $contentObjectID,
00884                                                                       'contentobject_version' => 1,
00885                                                                       'parent_node' => $parentNodeID,
00886                                                                       'is_main' => ( $defaultUserPlacement == $parentNodeID ? 1 : 0 ) ) );
00887                 $newNodeAssignment->setAttribute( 'parent_remote_id', uniqid( 'LDAP_' ) );
00888                 $newNodeAssignment->store();
00889             }
00890 
00891             $operationResult = eZOperationHandler::execute( 'content', 'publish', array( 'object_id' => $contentObjectID,
00892                                                                                          'version' => 1 ) );
00893         }
00894         else
00895         {
00896             if ( $userDataChanged )
00897             {
00898                 // Publish object
00899                 $operationResult = eZOperationHandler::execute( 'content', 'publish', array( 'object_id' => $contentObjectID,
00900                                                                                              'version' => $version->attribute( 'version' ) ) );
00901                 // Refetch object
00902                 $contentObject = eZContentObject::fetch( $contentObjectID );
00903                 $version = $contentObject->attribute( 'current' );
00904             }
00905 
00906             $LDAPIni = eZINI::instance( 'ldap.ini' );
00907             $keepGroupAssignment = ( $LDAPIni->hasVariable( 'LDAPSettings', 'KeepGroupAssignment' ) ) ?
00908                 ( $LDAPIni->variable( 'LDAPSettings', 'KeepGroupAssignment' ) == "enabled" ) : false;
00909 
00910             if ( $keepGroupAssignment == false )
00911             {
00912                 $objectIsChanged = false;
00913 
00914                 $db = eZDB::instance();
00915                 $db->begin();
00916 
00917                 // First check existing assignments, remove any that should not exist
00918                 $assignedNodesList = $contentObject->assignedNodes();
00919                 $existingParentNodeIDs = array();
00920                 foreach ( $assignedNodesList as $node )
00921                 {
00922                     $parentNodeID = $node->attribute( 'parent_node_id' );
00923                     if ( !in_array( $parentNodeID, $parentNodeIDs ) )
00924                     {
00925                         $node->removeThis();
00926                         $objectIsChanged = true;
00927                     }
00928                     else
00929                     {
00930                         $existingParentNodeIDs[] = $parentNodeID;
00931                     }
00932                 }
00933 
00934                 // Then check assignments that should exist, add them if they are missing
00935                 foreach( $parentNodeIDs as $parentNodeID )
00936                 {
00937                     if ( !in_array( $parentNodeID, $existingParentNodeIDs ) )
00938                     {
00939                         $newNode = $contentObject->addLocation( $parentNodeID, true );
00940                         $newNode->updateSubTreePath();
00941                         $newNode->setAttribute( 'contentobject_is_published', 1 );
00942                         $newNode->sync();
00943                         $existingParentNodeIDs[] = $parentNodeID;
00944                         $objectIsChanged = true;
00945                     }
00946                 }
00947 
00948                 // Then ensure that the main node is correct
00949                 $currentMainParentNodeID = $contentObject->attribute( 'main_parent_node_id' );
00950                 if ( $currentMainParentNodeID != $defaultUserPlacement )
00951                 {
00952                     $existingNode = eZContentObjectTreeNode::fetchNode( $contentObjectID, $defaultUserPlacement );
00953                     if ( !is_object( $existingNode ) )
00954                     {
00955                         eZDebug::writeError( "Cannot find assigned node as $defaultUserPlacement's child.", __METHOD__ );
00956                     }
00957                     else
00958                     {
00959                         $existingNodeID = $existingNode->attribute( 'node_id' );
00960                         $versionNum = $version->attribute( 'version' );
00961                         eZContentObjectTreeNode::updateMainNodeID( $existingNodeID, $contentObjectID, $versionNum, $defaultUserPlacement );
00962                         $objectIsChanged = true;
00963                     }
00964                 }
00965 
00966                 $db->commit();
00967 
00968                 // Finally, clear object view cache if something was changed
00969                 if ( $objectIsChanged )
00970                 {
00971                     eZContentCacheManager::clearObjectViewCache( $contentObjectID, true );
00972                 }
00973             }
00974         }
00975 
00976         eZUser::updateLastVisit( $userID );
00977         //eZUser::setCurrentlyLoggedInUser( $user, $userID );
00978         // Reset number of failed login attempts
00979         eZUser::setFailedLoginAttempts( $userID, 0 );
00980         return $user;
00981     }
00982 
00983     /*
00984         Static method, for internal usage only.
00985         Note: used user group class (see 'UserGroupClassID' ini setting, in 'UserSettings' section)
00986               must have name attribute with indentifier equal 'name'
00987     */
00988     static function publishNewUserGroup( $parentNodeIDs, $newGroupAttributes, $isUtf8Encoding = false )
00989     {
00990         $thisFunctionErrorLabel = 'eZLDAPUser.php, function publishNewUserGroup()';
00991         $newNodeIDs = array();
00992 
00993         if ( !is_array( $newGroupAttributes ) or
00994              !isset( $newGroupAttributes[ 'name' ] ) or
00995              empty( $newGroupAttributes[ 'name' ] ) )
00996         {
00997             eZDebug::writeWarning( 'Cannot create user group with empty name.',
00998                                    $thisFunctionErrorLabel );
00999             return $newNodeIDs;
01000         }
01001         if ( !is_array( $parentNodeIDs ) or count( $parentNodeIDs ) < 1 )
01002         {
01003             eZDebug::writeWarning( 'No one parent node IDs was passed for publishing new group (group name = "' .
01004                                    $newGroupAttributes[ 'name' ] . '")',
01005                                    $thisFunctionErrorLabel );
01006             return $newNodeIDs;
01007         }
01008 
01009         $ini = eZINI::instance();
01010         $userGroupClassID = $ini->variable( "UserSettings", "UserGroupClassID" );
01011         $userCreatorID = $ini->variable( "UserSettings", "UserCreatorID" );
01012         $defaultSectionID = $ini->variable( "UserSettings", "DefaultSectionID" );
01013 
01014         $userGroupClass = eZContentClass::fetch( $userGroupClassID );
01015         $contentObject = $userGroupClass->instantiate( $userCreatorID, $defaultSectionID );
01016 
01017         $contentObject->store();
01018 
01019         $contentObjectID = $contentObject->attribute( 'id' );
01020 
01021         reset( $parentNodeIDs );
01022         $defaultPlacement = current( $parentNodeIDs );
01023         array_shift( $parentNodeIDs );
01024 
01025         $nodeAssignment = eZNodeAssignment::create( array( 'contentobject_id' => $contentObjectID,
01026                                                            'contentobject_version' => 1,
01027                                                            'parent_node' => $defaultPlacement,
01028                                                            'is_main' => 1 ) );
01029         $nodeAssignment->setAttribute( 'parent_remote_id', uniqid( 'LDAP_' ) );
01030         $nodeAssignment->store();
01031 
01032         foreach( $parentNodeIDs as $parentNodeID )
01033         {
01034             $newNodeAssignment = eZNodeAssignment::create( array( 'contentobject_id' => $contentObjectID,
01035                                                                   'contentobject_version' => 1,
01036                                                                   'parent_node' => $parentNodeID,
01037                                                                   'is_main' => 0 ) );
01038             $newNodeAssignment->setAttribute( 'parent_remote_id', uniqid( 'LDAP_' ) );
01039             $newNodeAssignment->store();
01040         }
01041 
01042         $version = $contentObject->version( 1 );
01043         $version->setAttribute( 'modified', time() );
01044         $version->setAttribute( 'status', eZContentObjectVersion::STATUS_DRAFT );
01045         $version->store();
01046 
01047         $contentObjectAttributes = $version->contentObjectAttributes();
01048 
01049         // find ant set 'name' and 'description' attributes (as standard user group class)
01050         $nameIdentifier = 'name';
01051         $descIdentifier = 'description';
01052         $nameContentAttribute = null;
01053         $descContentAttribute = null;
01054         foreach( $contentObjectAttributes as $attribute )
01055         {
01056             if ( $attribute->attribute( 'contentclass_attribute_identifier' ) == $nameIdentifier )
01057             {
01058                 $nameContentAttribute = $attribute;
01059             }
01060             else if ( $attribute->attribute( 'contentclass_attribute_identifier' ) == $descIdentifier )
01061             {
01062                 $descContentAttribute = $attribute;
01063             }
01064         }
01065         if ( $nameContentAttribute )
01066         {
01067             if ( $isUtf8Encoding )
01068                 $newGroupAttributes[ 'name' ] = utf8_decode( $newGroupAttributes[ 'name' ] );
01069             $nameContentAttribute->setAttribute( 'data_text', $newGroupAttributes[ 'name' ] );
01070             $nameContentAttribute->store();
01071         }
01072         if ( $descContentAttribute and
01073              isset( $newGroupAttributes[ 'description' ] ) )
01074         {
01075             if ( $isUtf8Encoding )
01076                 $newGroupAttributes[ 'description' ] = utf8_decode( $newGroupAttributes[ 'description' ] );
01077             $descContentAttribute->setAttribute( 'data_text', $newGroupAttributes[ 'description' ] );
01078             $descContentAttribute->store();
01079         }
01080 
01081         $operationResult = eZOperationHandler::execute( 'content', 'publish', array( 'object_id' => $contentObjectID,
01082                                                                                      'version' => 1 ) );
01083         $newNodes = eZContentObjectTreeNode::fetchByContentObjectID( $contentObjectID, true, 1 );
01084         foreach ( $newNodes as $newNode )
01085         {
01086             $newNodeIDs[] = $newNode->attribute( 'node_id' );
01087         }
01088 
01089         return $newNodeIDs;
01090     }
01091 
01092     /*
01093         Static method, for internal usage only.
01094         Recursive, publishes groups by prepared tree of groups returned by getUserGroupsTree() method
01095     */
01096     static function goAndPublishGroups( &$requiredParams,
01097                                  $curDN,
01098                                  &$groupsTree,
01099                                  &$stack,
01100                                  $depth,
01101                                  $isUser = false )
01102     {
01103         $thisFunctionErrorLabel = 'eZLDAPUser.php, function goAndPublishGroups()';
01104         if ( !isset( $groupsTree[ $curDN ] ) )
01105         {
01106             eZDebug::writeError( 'Passed $curDN is not in result tree array.',
01107                                  $thisFunctionErrorLabel );
01108             return false;
01109         }
01110 
01111         array_push( $stack, $curDN );
01112         $current =& $groupsTree[ $curDN ];
01113 
01114         // check the name
01115         if ( $isUser )
01116         {
01117             $currentName = $current[ 'data' ][ $requiredParams[ 'LDAPLoginAttribute' ] ];
01118         }
01119         else
01120         {
01121             $currentName = $current[ 'data' ][ $requiredParams[ 'LDAPGroupNameAttribute' ] ];
01122         }
01123 
01124         if ( is_array( $currentName ) and //count( $currentName ) > 1 and
01125              isset( $currentName[ 'count' ] ) and $currentName[ 'count' ] > 0 )
01126         {
01127             $currentName = $currentName[ 0 ];
01128         }
01129 
01130         if ( empty( $currentName ) )
01131         {
01132             eZDebug::writeWarning( "Cannot create/use group with empty name (dn = $curDN)",
01133                                    $thisFunctionErrorLabel );
01134             return false;
01135         }
01136 
01137         // go through parents
01138         if ( is_array( $current['parents'] ) and count( $current['parents'] ) > 0 )
01139         {
01140             foreach( array_keys( $current['parents'] ) as $key )
01141             {
01142                 $parent =& $groupsTree[ $key ];
01143 
01144                 if ( in_array( $parent['data']['dn'], $stack ) )
01145                 {
01146                     $groupsTree[ '_recursion_detected_' ] = true;
01147                     eZDebug::writeError( 'Recursion is detected in the user-groups tree while getting parent groups for ' . $curDN,
01148                                          $thisFunctionErrorLabel );
01149                     return false;
01150                 }
01151                 if ( isset( $parent[ 'nodes' ] ) and count( $parent[ 'nodes' ] ) > 0 )
01152                 {
01153                     continue;
01154                 }
01155                 $ret = self::goAndPublishGroups( $requiredParams,
01156                                                  $parent['data']['dn'],
01157                                                  $groupsTree,
01158                                                  $stack,
01159                                                  $depth - 1 );
01160                 if ( isset( $groupsTree[ '_recursion_detected_' ] ) and $groupsTree[ '_recursion_detected_' ] )
01161                 {
01162                     return false;
01163                 }
01164             }
01165         }
01166         else
01167         {
01168             // We've reached a top node
01169             if ( !isset( $groupsTree[ 'root' ] ) )
01170             {
01171                 $groupsTree[ 'root' ] = array( 'data' => null,
01172                                                'parents' => null,
01173                                                'children' => array(),
01174                                                'nodes' => array( $requiredParams[ 'TopUserGroupNodeID' ] ) );
01175             }
01176             if ( !isset( $groupsTree[ 'root' ][ 'children' ][ $curDN ] ) )
01177                 $groupsTree[ 'root' ][ 'children' ][ $curDN ] =& $current;
01178             if ( !isset( $current[ 'parents' ][ 'root' ] ) )
01179                 $current[ 'parents' ][ 'root' ] =& $groupsTree[ 'root' ];
01180         }
01181 
01182         if ( !isset( $current[ 'nodes' ] ) )
01183             $current[ 'nodes' ] = array();
01184 
01185         $parentNodesForNew = array();
01186         foreach( array_keys( $current[ 'parents' ] ) as $key )
01187         {
01188             $parent =& $groupsTree[ $key ];
01189             if ( is_array( $parent[ 'nodes' ] ) and count( $parent[ 'nodes' ] ) > 0 )
01190             {
01191                 foreach ( $parent[ 'nodes' ] as $parentNodeID )
01192                 {
01193                     // fetch current parent node
01194                     $parentNode = eZContentObjectTreeNode::fetch( $parentNodeID );
01195                     if ( is_object( $parentNode ) )
01196                     {
01197                         $params = array( 'Depth' => 1,
01198                                          'AttributeFilter' => array( array( 'name', '=', $currentName ) ),
01199                                          'Limitation' => array() );
01200                         $nodes = eZContentObjectTreeNode::subTreeByNodeID( $params, $parentNodeID );
01201 
01202                         if ( is_array( $nodes ) and count( $nodes ) > 0 and !$isUser )
01203                         {
01204                             // if group with given name already exist under $parentNode then get fetch
01205                             // group node and remember its ID
01206                             $node =& $nodes[ 0 ];
01207                             $nodeID = $node->attribute( 'node_id' );
01208                             $current[ 'nodes' ][] = $nodeID;
01209                         }
01210                         else
01211                         {
01212                             // if not exist then remember $parentNodeID to publish a new one
01213                             $parentNodesForNew[] = $parentNodeID;
01214                         }
01215                     }
01216                     else
01217                     {
01218                         eZDebug::writeError( 'Cannot fetch parent node for creating new user group ' . $parentNodeID,
01219                                              $thisFunctionErrorLabel );
01220                     }
01221                 }
01222             }
01223             else
01224             {
01225                 eZDebug::writeError( "Cannot get any published parent group for group/user with name = '$currentName'" .
01226                                      " (dn = '" . $current[ 'data' ]['dn'] . "')",
01227                                      $thisFunctionErrorLabel );
01228             }
01229         }
01230 
01231         if ( count( $parentNodesForNew ) > 0 )
01232         {
01233             if ( $isUser )
01234             {
01235                 $current[ 'new_parents' ] = $parentNodesForNew;
01236                 $newNodeIDs = array();
01237             }
01238             else
01239             {
01240                 $newNodeIDs = eZLDAPUser::publishNewUserGroup( $parentNodesForNew, array( 'name' => $currentName,
01241                                                                                           'description' => '' ) );
01242             }
01243             $current[ 'nodes' ] = array_merge( $current[ 'nodes' ], $newNodeIDs );
01244         }
01245 
01246         array_pop( $stack );
01247         return true;
01248     }
01249 
01250     /*
01251         Static method, for internal usage only
01252         Recursive method, which parses tree of groups from ldap server
01253     */
01254     static function getUserGroupsTree( &$requiredParams,
01255                                 $filter,
01256                                 $curDN,
01257                                 &$groupsTree,
01258                                 &$stack,            // stack for recursion checking
01259                                 $depth = 0 )
01260     {
01261         if ( $depth == 0 )
01262         {
01263             return false;
01264         }
01265         $thisFunctionErrorLabel = 'eZLDAPUser.php, function getUserGroupsTree()';
01266 
01267         if ( !isset( $requiredParams[ 'LDAPGroupBaseDN' ] ) or empty( $requiredParams[ 'LDAPGroupBaseDN' ] ) or
01268              !isset( $requiredParams[ 'LDAPGroupClass' ] ) or empty( $requiredParams[ 'LDAPGroupClass' ] ) or
01269              !isset( $requiredParams[ 'LDAPGroupNameAttribute' ] ) or empty( $requiredParams[ 'LDAPGroupNameAttribute' ] ) or
01270              !isset( $requiredParams[ 'LDAPGroupMemberAttribute' ] ) or empty( $requiredParams[ 'LDAPGroupMemberAttribute' ] ) or
01271              !isset( $requiredParams[ 'ds' ] ) or !$requiredParams[ 'ds' ] )
01272         {
01273             eZDebug::writeError( 'Missing one of required parameters.',
01274                                  $thisFunctionErrorLabel );
01275             return false;
01276         }
01277         if ( !isset( $groupsTree[ $curDN ] ) )
01278         {
01279             eZDebug::writeError( 'Passed $curDN is not in result tree array. Algorithm\'s usage error.',
01280                                  $thisFunctionErrorLabel );
01281             return false;
01282         }
01283         array_push( $stack, $curDN );
01284 
01285         $LDAPGroupBaseDN          =& $requiredParams[ 'LDAPGroupBaseDN' ];
01286         $LDAPGroupClass           =& $requiredParams[ 'LDAPGroupClass' ];
01287         $LDAPGroupNameAttribute   =& $requiredParams[ 'LDAPGroupNameAttribute' ];
01288         $LDAPGroupMemberAttribute =& $requiredParams[ 'LDAPGroupMemberAttribute' ];
01289         $LDAPGroupDescriptionAttribute =& $requiredParams[ 'LDAPGroupDescriptionAttribute' ];
01290         $ds                       =& $requiredParams[ 'ds' ];
01291 
01292         $current =& $groupsTree[ $curDN ];
01293 
01294         $retrieveAttributes = array( $LDAPGroupNameAttribute,
01295                                      $LDAPGroupMemberAttribute );
01296         $sr = ldap_search( $ds, $LDAPGroupBaseDN, $filter, $retrieveAttributes );
01297         $entries = ldap_get_entries( $ds, $sr );
01298 
01299         if ( is_array( $entries ) and
01300              isset( $entries[ 'count' ] ) and $entries[ 'count' ] > 0 )
01301         {
01302             $newfilter = '(&(objectClass=' . $LDAPGroupClass . ')';
01303 
01304             for ( $i = 0; $i < $entries[ 'count' ]; $i++ )
01305             {
01306                 $parent =& $entries[ $i ];
01307                 if ( is_null( $parent ) )
01308                    continue;
01309 
01310                 $parentDN =& $parent['dn'];
01311                 if ( in_array( $parentDN, $stack ) )
01312                 {
01313                     $requiredParams[ 'LDAPGroupNameAttribute' ];
01314 
01315                     eZDebug::writeError( 'Recursion is detected in the user-groups tree while getting parent groups for ' . $curDN,
01316                                          $thisFunctionErrorLabel );
01317                     $groupsTree[ '_recursion_detected_' ] = true;
01318                     return false;
01319                 }
01320 
01321                 if ( !isset( $groupsTree[ $parentDN ] ) )
01322                 {
01323                     $groupsTree[ $parentDN ] = array( 'data' => $parent,
01324                                                       'parents' => array(),
01325                                                       'children' => array() );
01326                 }
01327                 $groupsTree[ $parentDN ][ 'children' ][ $curDN ] =& $current;
01328                 $current[ 'parents' ][ $parentDN ] =& $groupsTree[ $parentDN ];
01329                 $newfilter1 = $newfilter . '(' . $LDAPGroupMemberAttribute . '=' . $parentDN . '))';
01330                 $ret = eZLDAPUser::getUserGroupsTree( $requiredParams,
01331                                                       $newfilter1,
01332                                                       $parentDN,
01333                                                       $groupsTree,
01334                                                       $stack,
01335                                                       $depth - 1 );
01336                 if ( isset( $groupsTree[ '_recursion_detected_' ] ) and
01337                      $groupsTree[ '_recursion_detected_' ] )
01338                 {
01339                     return false;
01340                 }
01341             }
01342         }
01343         else
01344         {
01345             // We've reached a top node
01346             if ( !isset( $groupsTree[ 'root' ] ) )
01347             {
01348                 $groupsTree[ 'root' ] = array( 'data' => null,
01349                                                'parents' => null,
01350                                                'children' => array(),
01351                                                'nodes' => array( $requiredParams[ 'TopUserGroupNodeID' ] ) );
01352             }
01353             if ( !isset( $groupsTree[ 'root' ][ 'children' ][ $curDN ] ) )
01354                 $groupsTree[ 'root' ][ 'children' ][ $curDN ] =& $current;
01355             if ( !isset( $current[ 'parents' ][ 'root' ] ) )
01356                 $current[ 'parents' ][ 'root' ] =& $groupsTree[ 'root' ];
01357         }
01358 
01359         array_pop( $stack );
01360         return true;
01361     }
01362 
01363 
01364     /*
01365         Based on a similar function suggested at: http://php.net/manual/en/function.ldap-search.php
01366     */
01367     static function ldap_escape( $str, $for_dn = false )
01368     {
01369         // see: RFC2254
01370         // http://msdn.microsoft.com/en-us/library/ms675768(VS.85).aspx
01371         // http://www-03.ibm.com/systems/i/software/ldap/underdn.html
01372 
01373         if ( $for_dn )
01374         {
01375             $metaChars = array( ',', '=', '+', '<', '>', ';', '\\', '"', '#' );
01376             $quotedMetaChars = array( '\2c', '\3d', '\2b', '\3c', '\3e', '\3b', '\5c', '\22', '\23' );
01377         }
01378         else
01379         {
01380             $metaChars = array( '*', '(', ')', '\\', chr(0) );
01381             $quotedMetaChars = array( '\2a', '\28', '\29', '\5c', '\00' );
01382         }
01383 
01384         return str_replace( $metaChars, $quotedMetaChars, $str );
01385     }
01386 
01387 }
01388 
01389 ?>