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