Forums / Developer / node/group assignment : how to remove correctly ?

node/group assignment : how to remove correctly ?

Author Message

Artturi Markko

Sunday 14 May 2006 3:36:29 am

Hello,

I am working on a modified version of ldapusermanage.php in order to add/remove ldap originated users in the right ez groups (according to defined mappings, see post :
http://ez.no/community/forum/developer/contrib_ldap_group_mappings
)

Adding a user to a group seems to be ok but not for the removal.

Here's a code snippet :

                $newVersion = $contentObject->createNewVersion();
                $newVersionNr = $newVersion->attribute( 'version' );
                $nodeAssignmentList =& $newVersion->attribute( 'node_assignments' );
                //var_dump($nodeAssignmentList);
                foreach ( array_keys( $nodeAssignmentList ) as $key  )
                {
                    $nodeAssignment =& $nodeAssignmentList[$key];
                    $nodeAssignment->remove();              
                }

I'm able to find out which groups each user has to be deleted from, even if that's not so important as the foreach loop mentioned above removes all nodeAssignments.
In the new version of the user, all assignments are "re-created" from what has been computed in the script.

My problem is that it does not seem to be taken into account at 100%.

1) When I connect to the Admin interface, in the Users section, I still see the users in the groups they should no longer be member of

2) On the other hand, when I check the user's properties, in the "location" box, removed groups don't show up.

Seems the cleaning in only half way done. Anyone have a hint about how to finish that ?

Thanks in advance,

Artturi

Kristof Coomans

Sunday 14 May 2006 5:07:31 am

Hi Markko

1) Are the links to those users still valid? What do you get when you click on such a child node that is supposed to be removed? Maybe it's some caching issue.

The full code you're using could give us some more information about what went wrong.

independent eZ Publish developer and service provider | http://blog.coomanskristof.be | http://ezpedia.org

Artturi Markko

Monday 15 May 2006 12:27:40 pm

Hi Kristof,

Yes, the links are still valid

> What do you get when you click on such a child node that is supposed to be removed?
I get the user properties. In those, I can see under "Locations [2]" a list of the groups the user belongs to (and this list is correct)

> Maybe it's some caching issue.
I've cleared all the cache but user is still where it should not.

Anyway, i DO have caching issues with my ldap search. When I modify a group on the ldap server, I have to clear "content", "content node" and "content subtree" cache in order to detect my modifications.
Any idea about that ?

Here's the code if it can help :

$db->begin();
foreach ( array_keys ( $LDAPUsers ) as $key )
{
    $LDAPUser =& $LDAPUsers[$key];
    $login = $LDAPUser['login'];
    $userID = $LDAPUser['contentobject_id'];

    $LDAPFilter = "( &";
    if ( count( $LDAPFilters ) > 0 )
    {
        foreach ( array_keys( $LDAPFilters ) as $key )
        {
            $LDAPFilter .= "(" . $LDAPFilters[$key] . ")";
        }
    }
    $LDAPFilter .= "($LDAPLogin=$login)";
    $LDAPFilter .= ")";
    $LDAPFilter = str_replace( $LDAPEqualSign, "=", $LDAPFilter );
    if ( $LDAPSearchScope == "one" )
        $sr = ldap_list( $ds, $LDAPBaseDN, $LDAPFilter, $attributeArray );
    else if ( $LDAPSearchScope == "base" )
        $sr = ldap_read( $ds, $LDAPBaseDN, $LDAPFilter, $attributeArray );
    else
        $sr = ldap_search( $ds, $LDAPBaseDN, $LDAPFilter, $attributeArray );
    $info = ldap_get_entries( $ds, $sr );
    if ( $info["count"] != 1 )
    {
        $cli->output( "Disable user " . $cli->stylize( 'emphasize', $login ) );
        // Disable the user
        $userSetting = eZUserSetting::fetch( $userID );
        $userSetting->setAttribute( "is_enabled", false );
        $userSetting->store();
    }
    else
    {
        // Update user information
        $contentObject =& eZContentObject::fetch( $userID );

        $parentNodeID = $contentObject->attribute( 'main_parent_node_id' );
        $currentVersion = $contentObject->attribute( 'current_version' );

        $version =& $contentObject->attribute( 'current' );
        $contentObjectAttributes =& $version->contentObjectAttributes();

        if ( $isUtf8Encoding )
        {
            $firstName = utf8_decode( $info[0][$LDAPFirstNameAttribute][0] );
            $lastName = utf8_decode( $info[0][$LDAPLastNameAttribute][0] );
            $ldapEMail = utf8_decode( $info[0][$LDAPEmailAttribute][0] );
        }
        else
        {
            $firstName = $info[0][$LDAPFirstNameAttribute][0];
            $lastName = $info[0][$LDAPLastNameAttribute][0];
            $ldapEMail = $info[0][$LDAPEmailAttribute][0];
        }

        $contentObjectAttributes[0]->setAttribute( 'data_text', $firstName );
        $contentObjectAttributes[0]->store();

        $contentObjectAttributes[1]->setAttribute( 'data_text', $lastName );
        $contentObjectAttributes[1]->store();

        $contentClass =& $contentObject->attribute( 'content_class' );
        $name = $contentClass->contentObjectName( $contentObject );
        $contentObject->setName( $name );

        $existUser = eZUser::fetch(  $userID );
        $existUser->setAttribute('email', $ldapEMail );
        $existUser->setAttribute('password_hash', "" );
        $existUser->setAttribute('password_hash_type', 0 );
        $existUser->store();

/** DETECT if mappings are defined in ldap.ini **/
        if ( $LDAPUserGroupAM != null )
        {
            $republishRequired = false;
            $IsLDAPMain = true;
            $hasOtherNodeType = false;
            $hasLDAPNodeType = false;
            $otherNodeArray = array();
            $LDAPNodeArray = array();
            $newLDAPNodeArray = array();
            $parentNodes =& $contentObject->parentNodes( $currentVersion );;
            foreach( array_keys( $parentNodes ) as $key )
            {
                $parentNode =& $parentNodes[$key];
                $parentNodeID = $parentNode->attribute( 'node_id' );
                $parentNodeName = $parentNode->attribute( 'name' );
                $nodeAssignment = eZNodeAssignment::fetch( $contentObject->attribute( 'id' ), $currentVersion, $parentNodeID );
                $isMain = $nodeAssignment->attribute( 'is_main' );
                $remoteID = $nodeAssignment->attribute( 'parent_remote_id' );
                if ( preg_match( "/LDAP/i", $remoteID ) )
                {
                    $LDAPNodeArray[] = array( 'parent_node_name' => $parentNodeName, 'parent_node_id' => $parentNodeID, 'is_main' => $isMain );
                }
                else
                {
                    $otherNodeArray[] = array( 'parent_node_name' => $parentNodeName, 'parent_node_id' => $parentNodeID, 'is_main' => $isMain );
                    $hasOtherNodeType = true;
                    if ( $isMain )
                    {
                        $IsLDAPMain = false;
                    }
                }
            }
            /* Aim of the query : get all the node_id of the mapped groups in ldap.ini */
            $query =  "SELECT ezcontentobject_tree.node_id
                             FROM ezcontentobject, ezcontentobject_tree
                            WHERE ezcontentobject.name IN ( ";
            
            foreach ( $LDAPUserGroupAML as $value)
            {
                $r = explode("--", $value);
                $ldap2ez[$r[0]] = $r[1];
                $query .= "'" . $r[1] . "',";
            }
            $query = substr($query,0, -1) . ") AND ezcontentobject.id=ezcontentobject_tree.contentobject_id AND ezcontentobject.contentclass_id=3";                        
            $allMappedGroups = $db->arrayQuery( $query );
            
            $extraNodeAssignments = array();
            $removeAssignments = array();            
            $LDAPUserGroupCount = count( $LDAPNodeArray );

            $filter = "(&(objectClass=group)(member=" . $info[0]['dn'] . "))";
            mapInEzGroups($filter, $LDAPBaseDN, $ds, $db, $ldap2ez, $extraNodeAssignments);
            
            /** Find out which groups the user must not belong to **/
            foreach ( array_keys($allMappedGroups) as $key )
            {
                if ( in_array($allMappedGroups[$key]['node_id'], $extraNodeAssignments) )
                {
                    continue;                
                }
                foreach ( array_keys($LDAPNodeArray) as $key2 )
                {
                    if ( in_array($allMappedGroups[$key]['node_id'], $LDAPNodeArray[$key2]) )
                    {
                        $removeAssignments[] = $allMappedGroups[$key]['node_id'];
                    }                    
                }
            }
            var_dump($extraNodeAssignments, $removeAssignments);
            
            // count the number of "ez mapped groups" the user has to belong to
            $groupCount = count( $extraNodeAssignments );    
            for ( $i = 0; $i < $groupCount ; $i++ )
            {         
/* check for all "ldap originated groups" the user already belongs to, 
  with the assumption it does not exist in $extraNodeAssignments */
                $exist = false; 
                foreach( $LDAPNodeArray as $LDAPNode )  
                {
                    $existGroupName = $LDAPNode['parent_node_name'];
                    $existGroupID = $LDAPNode['parent_node_id'];
                    if ( strcasecmp( $existGroupID, $extraNodeAssignments[$i] )  == 0 )
                    {
                        $exist = true;
                        $hasLDAPNodeType = true;
                        if ( $IsLDAPMain and count( $newLDAPNodeArray ) == 0 )
                        {
                            $newLDAPNodeArray[] = array( 'parent_node_name' => $existGroupName, 'parent_node_id' => $existGroupID, 'is_main' => 1 );
                        }
                        else
                        {
                            $newLDAPNodeArray[] = array( 'parent_node_name' => $existGroupName, 'parent_node_id' => $existGroupID, 'is_main' => 0 );
                        }
                        $LDAPUserGroupCount--;
                    }
                }

                if ( $exist == false )
                {
                    // Get the name from the id : this differs in order to use already written 'mapInEzGroups' function which returns an array of ids
                    $groupQuery = "SELECT ezcontentobject.name 
                                     FROM ezcontentobject, ezcontentobject_tree
                                    WHERE ezcontentobject_tree.node_id=$extraNodeAssignments[$i]
                                      AND ezcontentobject.id=ezcontentobject_tree.contentobject_id
                                      AND ezcontentobject.contentclass_id=3";
                    $groupObject = $db->arrayQuery( $groupQuery );

                    if ( count( $groupObject ) > 0 )
                    {
                        $hasLDAPNodeType = true;
                        if ( $IsLDAPMain and count( $newLDAPNodeArray ) == 0 )
                        {
                            $newLDAPNodeArray[] = array( 'parent_node_name' => $groupObject[0]['name'], 'parent_node_id' => $extraNodeAssignments[$i], 'is_main' => 1 );
                        }
                        else
                        {
                            $newLDAPNodeArray[] = array( 'parent_node_name' => $groupObject[0]['name'], 'parent_node_id' => $extraNodeAssignments[$i], 'is_main' => 0 );
                        }
                        $republishRequired = true;
                    }
                }
            }

            if ( $LDAPUserGroupCount != 0 || count($removeAssignments) > 0)
            {
                $republishRequired = true;
            }
            

            if ( $republishRequired )
            {
                $newVersion = $contentObject->createNewVersion();
                $newVersionNr = $newVersion->attribute( 'version' );
                $nodeAssignmentList =& $newVersion->attribute( 'node_assignments' );
                // ok, here should be the BIG CLEANUP, but seems incomplete by juding of the result in admin interface (user still sits in the group).
                foreach ( array_keys( $nodeAssignmentList ) as $key  )
                {
                    $nodeAssignment =& $nodeAssignmentList[$key];
                    $nodeAssignment->remove();
                }

                if ( $hasOtherNodeType )
                {
                    foreach ( $otherNodeArray as $otherNode )
                    {
                        $newVersion->assignToNode( $otherNode['parent_node_id'], $otherNode['is_main'] );
                    }
                }

                if ( $hasLDAPNodeType )
                {
                    foreach ( $newLDAPNodeArray as $newLDAPNode )
                    {
                        $newVersion->assignToNode( $newLDAPNode['parent_node_id'], $newLDAPNode['is_main'] );
                        $assignment = eZNodeAssignment::fetch( $contentObject->attribute( 'id' ), $newVersionNr, $newLDAPNode['parent_node_id'] );
                        $assignment->setAttribute( 'parent_remote_id', "LDAP_" . $newLDAPNode['parent_node_id'] );
                        $assignment->store();
                    }
                }

                if ( !$hasOtherNodeType and !$hasLDAPNodeType )
                {
                    $newVersion->assignToNode( $defaultUserPlacement, 1 );
                }
                include_once( 'lib/ezutils/classes/ezoperationhandler.php' );
                $operationResult = eZOperationHandler::execute( 'content', 'publish', array( 'object_id' => $userID,
                                                                                             'version' => $newVersionNr ) );
                $cli->output( $cli->stylize( 'emphasize', $existUser->attribute('login') ) . " has changed group, updated." );
            }
        }
    }
}
$db->commit();

Kristof Coomans

Monday 15 May 2006 11:06:59 pm

Can you do a var_dump of $operationResult and tell us what you get?

Do you get any PHP warnings/errors while executing the script?

Which version of eZ are you using?

independent eZ Publish developer and service provider | http://blog.coomanskristof.be | http://ezpedia.org

Artturi Markko

Tuesday 16 May 2006 2:27:26 pm

>Can you do a var_dump of $operationResult and tell us what you get?

For both addition or removal to a group, I get :

array(1) {
["status"]=>
int(1)
}

> Do you get any PHP warnings/errors while executing the script?

No, I don't any message (I've added "ini_set('error_reporting', E_ALL);" in the beginning to ensure they don't get hidden).

>Which version of eZ are you using?

3.7.5

Anyone could tell me where the source code for group membership removal is ? It could be helpful to compare the way ldapusermanage.php handles that against it.

Best regards,

Artturi

Kristof Coomans

Monday 22 May 2006 3:59:07 am

Hi Markko

I could locate the problem. The publish operation will call eZContentObjectTreeNode::removeSubtrees to remove nodes for which no node assignments exist any longer for the version we're publishing. That function will check if you have the right permissions to remove the node. But the cronjob is executed as the anonymous user, so probably you won't have the permission to do so and the node doesn't get removed.

You can add this at the beginning of your cronjob script to login as the admin user:

include_once( 'kernel/classes/datatypes/ezuser/ezuser.php' );
$user = eZUser::fetchByName('admin');
eZUser::setCurrentlyLoggedInUser( $user, $user->attribute( 'contentobject_id' ) );

This is probably an issue with the default ldapusermanage cronjob too. I will report it as a bug ( http://ez.no/bugs/view/8328 ).

independent eZ Publish developer and service provider | http://blog.coomanskristof.be | http://ezpedia.org

Artturi Markko

Thursday 25 May 2006 9:33:10 am

Hello Kristof,

Thanks a lot for your help, it now works like a charm !

Best regards,

Artturi

Andrew Kelly

Wednesday 23 August 2006 4:47:10 am

Any reason why
$user = eZUser::fetchByName('admin');
would cause the script to silently die?

Andrew Kelly

Wednesday 23 August 2006 5:00:05 am

Any reason why
$user = eZUser::fetchByName('admin');
would cause the script to silently die?

Kristof Coomans

Wednesday 23 August 2006 5:45:17 am

Hi Andy

Put on debug output, you should see an error then.

independent eZ Publish developer and service provider | http://blog.coomanskristof.be | http://ezpedia.org

Andrew Kelly

Thursday 24 August 2006 2:50:16 am

Sorry, Kristof, not getting any debug output at all.
It simply dies in the function eZUser::fetchByName without any noise.

Andy