MemberOf Plugin Design


Overview

The memberOf LDAP attribute is an attribute used for grouping of user entries. Typically, when you add a user as a member of a group, you add a “member” attribute that contains the DN of the user to a “groupOfUniqueNames” group entry. This approach makes it easy to get a list of all users who belong to a specific group by searching against the specific group you are interested in for all “member” attributes. If you are interested in knowing all groups that a specific user is a member of, things get more complex on the client side. To eliminate this complexity being pushed onto the client application, the “memberOf” attribute can be used. In addition to adding a “member” attribute to a group entry, you would add a “memberOf” attribute to the user entry that point to the group. This allows you to do a simple search against a specific user entry to find all groups that the user is a member of.

If the “memberOf” and “member” attributes are both being used, you run the risk of having errors in consistency. These inconsistency issues can arise in the case where either the group or user entry is modified, but the other side is not. This would result in different membership results when you check via the group entry versus checking the user entry. To avoid these inconsistency issues, the “memberOf” attribute should be automatically managed by the Directory Server when a change is made to a group entry that affects membership.

Architecture

This feature will be implemented as a SLAPI plug-in that can be optionally enabled. A SLAPI plug-in implementing automatically maintained “memberOf” attribute functionality has been written as part of the FreeIPA project. This plug-in can be used as a starting point, but development work will still be needed.

FreeIPA Plug-in Architecture

The FreeIPA memberOf SLAPI plug-in is currently implemented as a post-operation plug-in. The plug-in takes action when a change is made that affects group membership. The plug-in will then update the “memberOf” attribute where necessary, which is stored just like any other attribute in the database.

Callbacks

The plug-in’s initialization function (ipamo_postop_init) registers the following plug-in callbacks with Directory Server:

static int ipamo_postop_del(Slapi_PBlock *pb );    
static int ipamo_postop_modrdn(Slapi_PBlock *pb );    
static int ipamo_postop_modify(Slapi_PBlock *pb );    
static int ipamo_postop_add(Slapi_PBlock *pb );    
static int ipamo_postop_start(Slapi_PBlock *pb);    
static int ipamo_postop_close(Slapi_PBlock *pb);    

The first four callbacks deal with the core of this feature; that is they appropriately update the “memberOf” attribute for incoming DEL, MODRDN, MOD, and ADD LDAP operations. The ipamo_postop_start and ipamo_postop_close functions deal with startup and cleanup of the plug-in. An important thing to note about the ipamo_postop_start function is that it registers a task handler callback via the (not entirely exposed) SLAPI task interface. This task handler callback is:

int ipamo_task_add(Slapi_PBlock *pb, Slapi_Entry *e,    
                   Slapi_Entry *eAfter, int *returncode, char *returntext,    
                   void *arg)    

This task handler allows an LDAP task entry to be added to the Directory Server to check for a grouping inconsistency and fix it. If everything is working correctly in the plug-in, there would never be a consistency issue to resolve. This task would still be useful if someone directly modified a “memberOf” attribute instead of allowing the plug-in to maintain it, which could cause an inconsistency problem. It would also be useful if you already have an existing database that is not using “memberOf” yet, but you want to populate the “memberOf” attribute.

Handling of LDAP Operations

The basic operation of the plug-in is as follows:

  1. An operation comes in that affects a “member” attribute for a particular group.
  2. We locate a “member” entry that is being somehow modified.
  3. If the “member” is a group, we recurse into that group to locate it’s members.
  4. When we encounter a user entry, we update their “memberOf” attribute appropriately.
  5. We continue the above three steps until all members are processed.

The below four sections will describe how the memberOf plug-in deals with specific incoming operations.

ADD

  1. Plug-in checks if the incoming add is adding a new group. It determines this by testing a filter of “(member=*)” against the entry to be added. If we’re not interested, we just let the operation continue being processed by the server as normal.
  2. Acquire memberOf lock.
  3. Go through each “member” attribute value from the new group, performing the following steps for each:
    1. If this “member” is a nested group, recurse into that group and process it’s members one at a time just as in this subsection.
    2. Add new group’s DN to the “memberOf” attribute of this “member” entry.
    3. Go through each group that has the new group as a “member”, performing the same steps as in this subsection, but adding this parent group DN as a “memberOf” to this “member” entry. This will result in all parent groups (including grandparents, etc.)being added to this “member” entry.
  4. Release memberOf lock.
  5. We let the operation continue being processed by the server as normal.

DEL

  1. Get a copy of the entry before it was deleted.
  2. Acquire memberOf lock.
  3. Look for any entries that have the entry being deleted as a “member” and remove that “member” value.
  4. Check if the we’re deleting a group entry, and if so perform the following for each “member” of the group being deleted:
    1. If this “member” is a nested group, recurse into that group and process it’s members one at a time just as in this subsection.
    2. If we’ve traced into a nested group, verify that the “member” is not a “member” through some other indirect grouping path.
    3. Delete the “memberOf” value for the group from this “member”.
    4. Check if a group has been orphaned (need to research this).
  5. Release memberOf lock.
  6. We let the operation continue being processed by the server as normal.

MOD

MODRDN

Fix-Up Task

The plugin provides a SLAPI task that may be started by administrative clients and that creates the initial memberOf list for imported entries and/or fixes the memberOf list of existing entries that have inconsistent state (for example, if the memberOf attribute was incorrectly edited directly).

To start the memberof task add an entry like:

dn: cn=memberof task 2, cn=memberof task, cn=tasks, cn=config
objectClass: top
objectClass: extensibleObject
cn: sample task
basedn: dc=example, dc=com
filter: (uid=test4)

The “basedn” attribute is required and refers to the top most node to perform the task on, and where “filter” is an optional attribute that provides a filter describing the entries to be worked on.

Issues

Proposed Architecture

The current architecture is pretty sound overall. As with any feature, there are trade-offs to be made with different approaches. The items below are some things that we could change with the architecture, but we need to determine if the trade-offs are acceptable.

Usage

Configuration

To Do

Last modified on 1 March 2024