From b52ab703908a5d76a22169f35781b52e90302591 Mon Sep 17 00:00:00 2001
From: dugan <dugan@localhost>
Date: Mon, 13 Aug 2007 11:57:26 +0000
Subject: [PATCH] Add support for nested static groups and nesting of a dynamic groups in a static group. This implementation does not use a different attribute to define a nested group. Issue 423

---
 opends/src/server/org/opends/server/extensions/StaticGroup.java |  251 +++++++++++++++++++++++++++++++++++++++++--------
 1 files changed, 208 insertions(+), 43 deletions(-)

diff --git a/opends/src/server/org/opends/server/extensions/StaticGroup.java b/opends/src/server/org/opends/server/extensions/StaticGroup.java
index 338b0d4..9a119ec 100644
--- a/opends/src/server/org/opends/server/extensions/StaticGroup.java
+++ b/opends/src/server/org/opends/server/extensions/StaticGroup.java
@@ -28,42 +28,21 @@
 
 
 
-import java.util.Collections;
-import java.util.LinkedHashSet;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Set;
+import java.util.*;
 
 import org.opends.server.admin.std.server.GroupImplementationCfg;
 import org.opends.server.api.Group;
 import org.opends.server.core.ModifyOperationBasis;
+import org.opends.server.core.DirectoryServer;
 import org.opends.server.config.ConfigException;
 import org.opends.server.protocols.internal.InternalClientConnection;
-import org.opends.server.types.Attribute;
-import org.opends.server.types.AttributeType;
-import org.opends.server.types.AttributeValue;
-import org.opends.server.types.Control;
-import org.opends.server.types.DirectoryConfig;
-import org.opends.server.types.DirectoryException;
-import org.opends.server.types.DN;
-import org.opends.server.types.Entry;
-import org.opends.server.types.ErrorLogCategory;
-import org.opends.server.types.ErrorLogSeverity;
-import org.opends.server.types.InitializationException;
-import org.opends.server.types.MemberList;
-import org.opends.server.types.Modification;
-import org.opends.server.types.ModificationType;
-import org.opends.server.types.ObjectClass;
-import org.opends.server.types.ResultCode;
-import org.opends.server.types.SearchFilter;
-import org.opends.server.types.SearchScope;
 
-import org.opends.server.types.DebugLogLevel;
+import org.opends.server.types.*;
 import static org.opends.server.loggers.ErrorLogger.*;
 import static org.opends.server.loggers.debug.DebugLogger.*;
 import org.opends.server.loggers.debug.DebugTracer;
 import static org.opends.server.messages.ExtensionsMessages.*;
-import static org.opends.server.messages.MessageHandler.*;
+import static org.opends.server.messages.MessageHandler.getMessage;
 import static org.opends.server.util.ServerConstants.*;
 import static org.opends.server.util.Validator.*;
 
@@ -94,7 +73,13 @@
   // The set of the DNs of the members for this group.
   private LinkedHashSet<DN> memberDNs;
 
+  //The list of nested group DNs for this group.
+  private LinkedList<DN> nestedGroups = new LinkedList<DN>();
 
+  //Passed to the group manager to see if the nested group list needs to be
+  //refreshed.
+  private long nestedGroupRefreshToken =
+                              DirectoryServer.getGroupManager().refreshToken();
 
   /**
    * Creates a new, uninitialized static group instance.  This is intended for
@@ -311,8 +296,7 @@
   @Override()
   public boolean supportsNestedGroups()
   {
-    // FIXME -- We should add support for nested groups.
-    return false;
+    return true;
   }
 
 
@@ -323,8 +307,12 @@
   @Override()
   public List<DN> getNestedGroupDNs()
   {
-    // FIXME -- We should add support for nested groups.
-    return Collections.<DN>emptyList();
+    try {
+       reloadIfNeeded();
+    } catch (DirectoryException ex) {
+      return Collections.<DN>emptyList();
+    }
+    return nestedGroups;
   }
 
 
@@ -336,8 +324,62 @@
   public void addNestedGroup(DN nestedGroupDN)
          throws UnsupportedOperationException, DirectoryException
   {
-    // FIXME -- We should add support for nested groups.
-    throw new UnsupportedOperationException();
+     ensureNotNull(nestedGroupDN);
+
+    synchronized (this)
+    {
+      if (nestedGroups.contains(nestedGroupDN))
+      {
+        int    msgID   = MSGID_STATICGROUP_ADD_NESTED_GROUP_ALREADY_EXISTS;
+        String message = getMessage(msgID,String.valueOf(nestedGroupDN),
+                                    String.valueOf(groupEntryDN));
+        throw new DirectoryException(ResultCode.ATTRIBUTE_OR_VALUE_EXISTS,
+                                     message, msgID);
+      }
+
+      LinkedHashSet<AttributeValue> values =
+           new LinkedHashSet<AttributeValue>(1);
+      values.add(new AttributeValue(memberAttributeType,
+                                    nestedGroupDN.toString()));
+
+      Attribute attr = new Attribute(memberAttributeType,
+                                     memberAttributeType.getNameOrOID(),
+                                     values);
+
+      LinkedList<Modification> mods = new LinkedList<Modification>();
+      mods.add(new Modification(ModificationType.ADD, attr));
+
+      LinkedList<Control> requestControls = new LinkedList<Control>();
+      requestControls.add(new Control(OID_INTERNAL_GROUP_MEMBERSHIP_UPDATE,
+                                      false));
+
+      InternalClientConnection conn =
+           InternalClientConnection.getRootConnection();
+      ModifyOperationBasis modifyOperation =
+           new ModifyOperationBasis(conn,
+                   InternalClientConnection.nextOperationID(),
+                   InternalClientConnection.nextMessageID(), requestControls,
+                               groupEntryDN, mods);
+      modifyOperation.run();
+      if (modifyOperation.getResultCode() != ResultCode.SUCCESS)
+      {
+        int    msgID   = MSGID_STATICGROUP_ADD_MEMBER_UPDATE_FAILED;
+        String message = getMessage(msgID, String.valueOf(nestedGroupDN),
+                              String.valueOf(groupEntryDN),
+                              modifyOperation.getErrorMessage().toString());
+        throw new DirectoryException(modifyOperation.getResultCode(), message,
+                                     msgID);
+      }
+
+
+      LinkedList<DN> newNestedGroups = new LinkedList<DN>(nestedGroups);
+      newNestedGroups.add(nestedGroupDN);
+      nestedGroups = newNestedGroups;
+      //Add it to the member DN list.
+      LinkedHashSet<DN> newMemberDNs = new LinkedHashSet<DN>(memberDNs);
+      newMemberDNs.add(nestedGroupDN);
+      memberDNs = newMemberDNs;
+    }
   }
 
 
@@ -349,8 +391,62 @@
   public void removeNestedGroup(DN nestedGroupDN)
          throws UnsupportedOperationException, DirectoryException
   {
-    // FIXME -- We should add support for nested groups.
-    throw new UnsupportedOperationException();
+    ensureNotNull(nestedGroupDN);
+
+    synchronized (this)
+    {
+      if (! nestedGroups.contains(nestedGroupDN))
+      {
+        int    msgID   = MSGID_STATICGROUP_REMOVE_NESTED_GROUP_NO_SUCH_GROUP;
+        String message = getMessage(msgID, String.valueOf(nestedGroupDN),
+                                    String.valueOf(groupEntryDN));
+        throw new DirectoryException(ResultCode.NO_SUCH_ATTRIBUTE, message,
+                                     msgID);
+      }
+
+      LinkedHashSet<AttributeValue> values =
+           new LinkedHashSet<AttributeValue>(1);
+      values.add(new AttributeValue(memberAttributeType,
+                                                     nestedGroupDN.toString()));
+
+      Attribute attr = new Attribute(memberAttributeType,
+                                     memberAttributeType.getNameOrOID(),
+                                     values);
+
+      LinkedList<Modification> mods = new LinkedList<Modification>();
+      mods.add(new Modification(ModificationType.DELETE, attr));
+
+      LinkedList<Control> requestControls = new LinkedList<Control>();
+      requestControls.add(new Control(OID_INTERNAL_GROUP_MEMBERSHIP_UPDATE,
+                                      false));
+
+      InternalClientConnection conn =
+           InternalClientConnection.getRootConnection();
+      ModifyOperationBasis modifyOperation =
+           new ModifyOperationBasis(conn,
+                   InternalClientConnection.nextOperationID(),
+                   InternalClientConnection.nextMessageID(), requestControls,
+                   groupEntryDN, mods);
+      modifyOperation.run();
+      if (modifyOperation.getResultCode() != ResultCode.SUCCESS)
+      {
+        int    msgID   = MSGID_STATICGROUP_REMOVE_MEMBER_UPDATE_FAILED;
+        String message = getMessage(msgID,String.valueOf(nestedGroupDN),
+                              String.valueOf(groupEntryDN),
+                              modifyOperation.getErrorMessage().toString());
+        throw new DirectoryException(modifyOperation.getResultCode(), message,
+                                     msgID);
+      }
+
+
+      LinkedList<DN> newNestedGroups = new LinkedList<DN>(nestedGroups);
+      newNestedGroups.remove(nestedGroupDN);
+      nestedGroups = newNestedGroups;
+      //Remove it from the member DN list.
+      LinkedHashSet<DN> newMemberDNs = new LinkedHashSet<DN>(memberDNs);
+      newMemberDNs.remove(nestedGroupDN);
+      memberDNs = newMemberDNs;
+    }
   }
 
 
@@ -360,14 +456,31 @@
    */
   @Override()
   public boolean isMember(DN userDN, Set<DN> examinedGroups)
-         throws DirectoryException
+          throws DirectoryException
   {
-    if (! examinedGroups.add(getGroupDN()))
+    reloadIfNeeded();
+    if(memberDNs.contains(userDN))
+    {
+      return true;
+    }
+    else if (! examinedGroups.add(getGroupDN()))
     {
       return false;
     }
-
-    return memberDNs.contains(userDN);
+    else
+    {
+      for(DN nestedGroupDN : nestedGroups)
+      {
+        Group<? extends GroupImplementationCfg> g =
+             (Group<? extends GroupImplementationCfg>)
+              DirectoryServer.getGroupManager().getGroupInstance(nestedGroupDN);
+        if((g != null) && (g.isMember(userDN, examinedGroups)))
+        {
+          return true;
+        }
+      }
+    }
+    return false;
   }
 
 
@@ -379,23 +492,68 @@
   public boolean isMember(Entry userEntry, Set<DN> examinedGroups)
          throws DirectoryException
   {
-    if (! examinedGroups.add(getGroupDN()))
-    {
-      return false;
-    }
-
-    return memberDNs.contains(userEntry.getDN());
+    return isMember(userEntry.getDN(), examinedGroups);
   }
 
 
 
   /**
+   * Check if the group manager has registered a new group instance or removed a
+   * a group instance that might impact this group's membership list.
+   */
+  private void
+  reloadIfNeeded() throws DirectoryException
+  {
+    //Check if group instances have changed by passing the group manager
+    //the current token.
+    if(DirectoryServer.getGroupManager().
+            hasInstancesChanged(nestedGroupRefreshToken))
+    {
+      synchronized (this)
+      {
+        Group thisGroup =
+               DirectoryServer.getGroupManager().getGroupInstance(groupEntryDN);
+        //Check if the group itself has been removed
+        if(thisGroup == null) {
+          int    msgID   = MSGID_STATICGROUP_GROUP_INSTANCE_INVALID;
+          String message = getMessage(msgID, String.valueOf(groupEntryDN));
+          throw new
+               DirectoryException(ResultCode.NO_SUCH_ATTRIBUTE, message, msgID);
+        } else if(thisGroup != this) {
+          LinkedHashSet<DN> newMemberDNs = new LinkedHashSet<DN>();
+          MemberList memberList=thisGroup.getMembers();
+          while (memberList.hasMoreMembers()) {
+            try {
+              newMemberDNs.add(memberList.nextMemberDN());
+            } catch (MembershipException ex) {}
+          }
+          memberDNs=newMemberDNs;
+        }
+        LinkedList<DN> newNestedGroups = new LinkedList<DN>();
+        for(DN dn : memberDNs)
+        {
+          Group gr=DirectoryServer.getGroupManager().getGroupInstance(dn);
+          if(gr != null)
+          {
+            newNestedGroups.add(gr.getGroupDN());
+          }
+        }
+        nestedGroupRefreshToken =
+                DirectoryServer.getGroupManager().refreshToken();
+        nestedGroups=newNestedGroups;
+      }
+    }
+  }
+
+
+  /**
    * {@inheritDoc}
    */
   @Override()
   public MemberList getMembers()
          throws DirectoryException
   {
+    reloadIfNeeded();
     return new SimpleStaticGroupMemberList(groupEntryDN, memberDNs);
   }
 
@@ -409,6 +567,7 @@
                                SearchFilter filter)
          throws DirectoryException
   {
+    reloadIfNeeded();
     if ((baseDN == null) && (filter == null))
     {
       return new SimpleStaticGroupMemberList(groupEntryDN, memberDNs);
@@ -554,6 +713,12 @@
       LinkedHashSet<DN> newMemberDNs = new LinkedHashSet<DN>(memberDNs);
       newMemberDNs.remove(userDN);
       memberDNs = newMemberDNs;
+      //If it is in the nested group list remove it.
+      if(nestedGroups.contains(userDN)) {
+        LinkedList<DN> newNestedGroups = new LinkedList<DN>(nestedGroups);
+        newNestedGroups.remove(userDN);
+        nestedGroups = newNestedGroups;
+      }
     }
   }
 

--
Gitblit v1.10.0