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/tests/unit-tests-testng/src/server/org/opends/server/core/GroupManagerTestCase.java | 500 ++++++++++++++++++++++++++++++++++
opends/src/server/org/opends/server/extensions/StaticGroup.java | 251 ++++++++++++++---
opends/src/server/org/opends/server/core/GroupManager.java | 45 ++
opends/src/server/org/opends/server/messages/ExtensionsMessages.java | 41 ++
4 files changed, 778 insertions(+), 59 deletions(-)
diff --git a/opends/src/server/org/opends/server/core/GroupManager.java b/opends/src/server/org/opends/server/core/GroupManager.java
index cf84c84..236edb0 100644
--- a/opends/src/server/org/opends/server/core/GroupManager.java
+++ b/opends/src/server/org/opends/server/core/GroupManager.java
@@ -104,6 +104,10 @@
private static final DebugTracer TRACER = getTracer();
+ //Used by group instances to determine if new groups have been
+ //registered or groups deleted.
+ private long refreshToken=0;
+
// A mapping between the DNs of the config entries and the associated
// group implementations.
@@ -711,8 +715,11 @@
}
}
}
-
- createAndRegisterGroup(entry);
+ synchronized (groupInstances)
+ {
+ createAndRegisterGroup(entry);
+ refreshToken++;
+ }
}
@@ -735,8 +742,11 @@
}
}
}
-
- groupInstances.remove(entry.getDN());
+ synchronized (groupInstances)
+ {
+ groupInstances.remove(entry.getDN());
+ refreshToken++;
+ }
}
@@ -773,6 +783,7 @@
}
createAndRegisterGroup(newEntry);
+ refreshToken++;
}
}
}
@@ -807,6 +818,7 @@
{
createAndRegisterGroup(newEntry);
groupInstances.remove(oldEntry.getDN());
+ refreshToken++;
}
}
}
@@ -855,5 +867,30 @@
{
groupInstances.clear();
}
+
+
+ /**
+ * Compare the specified token against the current group manager
+ * token value. Can be used to reload cached group instances if there has
+ * been a group instance change.
+ *
+ * @param token The current token that the group class holds.
+ *
+ * @return {@code true} if the group class should reload its nested groups,
+ * or {@code false} if it shouldn't.
+ */
+ public boolean hasInstancesChanged(long token) {
+ return token != this.refreshToken;
+ }
+
+ /**
+ * Return the current refresh token value. Can be used to
+ * reload cached group instances if there has been a group instance change.
+ *
+ * @return The current token value.
+ */
+ public long refreshToken() {
+ return this.refreshToken;
+ }
}
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;
+ }
}
}
diff --git a/opends/src/server/org/opends/server/messages/ExtensionsMessages.java b/opends/src/server/org/opends/server/messages/ExtensionsMessages.java
index e59aa07..b6d0c38 100644
--- a/opends/src/server/org/opends/server/messages/ExtensionsMessages.java
+++ b/opends/src/server/org/opends/server/messages/ExtensionsMessages.java
@@ -5583,6 +5583,36 @@
CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_MILD_ERROR | 537;
+ /**
+ * The message ID for the message that will be used if an attempt is made to
+ * add a nested group to a static group that already includes that nested
+ * group. This takes two arguments, which is the DN of the nested group
+ * being added and the DN of the group.
+ */
+ public static final int MSGID_STATICGROUP_ADD_NESTED_GROUP_ALREADY_EXISTS =
+ CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_MILD_ERROR | 538;
+
+
+
+ /**
+ * The message ID for the message that will be used if an attempt is made to
+ * remove a nested group from a static group that does not include that group.
+ * This takes two arguments, which is the DN of the nested group being removed
+ * and the DN of the group.
+ */
+ public static final int MSGID_STATICGROUP_REMOVE_NESTED_GROUP_NO_SUCH_GROUP =
+ CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_MILD_ERROR | 539;
+
+
+
+ /**
+ * The message ID for the message that will be used if an attempt is made to
+ * refresh a static group member list using a group instance that has been
+ * removed from the group manager via a ldap operation. This takes one
+ * arguments, which is the DN of invalid or stale nested group instance.
+ */
+ public static final int MSGID_STATICGROUP_GROUP_INSTANCE_INVALID =
+ CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_MILD_ERROR | 540;
/**
* Associates a set of generic messages with the message IDs defined in this
@@ -8024,6 +8054,17 @@
registerMessage(MSGID_REGEXMAP_SEARCH_FAILED,
"An internal failure occurred while attempting to " +
"resolve processed ID string %s to a user entry: %s");
+ registerMessage(MSGID_STATICGROUP_ADD_NESTED_GROUP_ALREADY_EXISTS,
+ "Cannot add group %s as a new nested group of static " +
+ "group %s because that group is already in the nested " +
+ "group list for the group");
+ registerMessage(MSGID_STATICGROUP_REMOVE_NESTED_GROUP_NO_SUCH_GROUP,
+ "Cannot remove group %s as a nested group of static " +
+ "group %s because that group is not included in the " +
+ "nested group list for the group");
+ registerMessage(MSGID_STATICGROUP_GROUP_INSTANCE_INVALID,
+ "Group instance with DN %s has been deleted and is no " +
+ "longer valid");
}
}
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/core/GroupManagerTestCase.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/core/GroupManagerTestCase.java
index 1a8c231..8473b4d 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/core/GroupManagerTestCase.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/core/GroupManagerTestCase.java
@@ -35,12 +35,11 @@
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
+import org.testng.annotations.AfterClass;
import org.opends.server.TestCaseUtils;
+import org.opends.server.admin.std.server.GroupImplementationCfg;
import org.opends.server.api.Group;
-import org.opends.server.core.DeleteOperation;
-import org.opends.server.core.ModifyOperation;
-import org.opends.server.core.ModifyDNOperationBasis;
import org.opends.server.extensions.DynamicGroup;
import org.opends.server.extensions.StaticGroup;
import org.opends.server.extensions.VirtualStaticGroup;
@@ -62,7 +61,7 @@
import org.opends.server.types.SearchScope;
import static org.testng.Assert.*;
-
+import static org.testng.Assert.assertTrue;
/**
@@ -84,7 +83,11 @@
TestCaseUtils.startServer();
}
-
+ @AfterClass()
+ public void cleanUp() {
+ GroupManager groupManager = DirectoryServer.getGroupManager();
+ groupManager.deregisterAllGroups();
+ }
/**
* Tests the {@code GroupManager.getGroupImplementations} method to ensure
@@ -113,6 +116,382 @@
"Unexpected group class(es) registered: " + groupClasses);
}
+ /**
+ * Test static group nesting with some of the groups pointing to each
+ * other in a circular fashion. Once this situation is detected the
+ * membership check should return false.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+
+ @Test()
+ public void testStaticGroupCircularNested() throws Exception {
+ TestCaseUtils.initializeTestBackend(true);
+ GroupManager groupManager = DirectoryServer.getGroupManager();
+ groupManager.deregisterAllGroups();
+ addNestedGroupTestEntries();
+ DN group1DN = DN.decode("cn=group 1,ou=Groups,o=test");
+ DN group2DN = DN.decode("cn=group 2,ou=Groups,o=test");
+ DN group3DN = DN.decode("cn=group 3,ou=Groups,o=test");
+ DN user1DN = DN.decode("uid=user.1,ou=People,o=test");
+ DN user2DN = DN.decode("uid=user.2,ou=People,o=test");
+ DN user3DN = DN.decode("uid=user.3,ou=People,o=test");
+ DN user4DN = DN.decode("uid=user.4,ou=People,o=test");
+ DN user5DN = DN.decode("uid=user.5,ou=People,o=test");
+ Entry user1Entry = DirectoryServer.getEntry(user1DN);
+ Entry user2Entry = DirectoryServer.getEntry(user2DN);
+ Entry user3Entry = DirectoryServer.getEntry(user3DN);
+ Entry user4Entry = DirectoryServer.getEntry(user4DN);
+ Group group1Instance = groupManager.getGroupInstance(group1DN);
+ Group group2Instance = groupManager.getGroupInstance(group2DN);
+ Group group3Instance = groupManager.getGroupInstance(group3DN);
+ assertNotNull(group1Instance);
+ assertNotNull(group2Instance);
+ assertNotNull(group3Instance);
+ group1Instance.addNestedGroup(group2DN);
+ group2Instance.addNestedGroup(group3DN);
+ //Add circular nested group definition by adding group 1 to group 3
+ //nested list. Group 1 contains group 2, which contains group 3, which
+ //contains group 1.
+ group3Instance.addNestedGroup(group1DN);
+ group1Instance.addMember(user1Entry);
+ group2Instance.addMember(user2Entry);
+ group3Instance.addMember(user3Entry);
+ group2Instance.addMember(user4Entry);
+ //Search for DN not in any of the groups/
+ assertFalse(group1Instance.isMember(user5DN));
+ }
+
+ /**
+ * Test static group nesting wit one of the nested groups being a
+ * dynamic group.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testStaticGroupDynamicNested() throws Exception {
+ TestCaseUtils.initializeTestBackend(true);
+ GroupManager groupManager = DirectoryServer.getGroupManager();
+ groupManager.deregisterAllGroups();
+ addNestedGroupTestEntries();
+ DN group1DN = DN.decode("cn=group 1,ou=Groups,o=test");
+ DN group2DN = DN.decode("cn=group 2,ou=Groups,o=test");
+ DN group3DN = DN.decode("cn=group 3,ou=Groups,o=test");
+ DN group4DN = DN.decode("cn=group 4,ou=Groups,o=test");
+ DN user1DN = DN.decode("uid=user.1,ou=People,o=test");
+ DN user2DN = DN.decode("uid=user.2,ou=People,o=test");
+ DN user3DN = DN.decode("uid=user.3,ou=People,o=test");
+ DN user4DN = DN.decode("uid=user.4,ou=People,o=test");
+ DN user5DN = DN.decode("uid=user.5,ou=People,o=test");
+ Entry user1Entry = DirectoryServer.getEntry(user1DN);
+ Entry user2Entry = DirectoryServer.getEntry(user2DN);
+ Entry user3Entry = DirectoryServer.getEntry(user3DN);
+ Entry user4Entry = DirectoryServer.getEntry(user4DN);
+ //User 5 is not added to any group, it matches the URL of the dynamic
+ //group "group 4".
+ Group group1Instance = groupManager.getGroupInstance(group1DN);
+ Group group2Instance = groupManager.getGroupInstance(group2DN);
+ Group group3Instance = groupManager.getGroupInstance(group3DN);
+ //Group 4 is a dynamic group.
+ Group group4Instance = groupManager.getGroupInstance(group4DN);
+ assertNotNull(group1Instance);
+ assertNotNull(group2Instance);
+ assertNotNull(group3Instance);
+ assertNotNull(group4Instance);
+ group1Instance.addNestedGroup(group2DN);
+ group2Instance.addNestedGroup(group3DN);
+ //Dynamic group 4 is added to nested list of group 3.
+ group3Instance.addNestedGroup(group4DN);
+ group1Instance.addMember(user1Entry);
+ group2Instance.addMember(user2Entry);
+ group3Instance.addMember(user3Entry);
+ group2Instance.addMember(user4Entry);
+ //Check membership of user 5 through group 1. User 5 is a member of the
+ //dynamic group "group 4" which is nested.
+ assertTrue(group1Instance.isMember(user5DN));
+ }
+
+ /**
+ * Invokes membership and nested group APIs using a group instance that has
+ * been changed by the group manager via ldap modify.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testStaticGroupInstanceChange() throws Exception {
+ TestCaseUtils.initializeTestBackend(true);
+ GroupManager groupManager = DirectoryServer.getGroupManager();
+ groupManager.deregisterAllGroups();
+ addNestedGroupTestEntries();
+ DN group1DN = DN.decode("cn=group 1,ou=Groups,o=test");
+ DN group2DN = DN.decode("cn=group 2,ou=Groups,o=test");
+ DN group3DN = DN.decode("cn=group 3,ou=Groups,o=test");
+ DN group4DN = DN.decode("cn=group 4,ou=Groups,o=test");
+ DN user1DN = DN.decode("uid=user.1,ou=People,o=test");
+ DN user2DN = DN.decode("uid=user.2,ou=People,o=test");
+ DN user3DN = DN.decode("uid=user.3,ou=People,o=test");
+ DN user4DN = DN.decode("uid=user.4,ou=People,o=test");
+ DN user5DN = DN.decode("uid=user.5,ou=People,o=test");
+ Entry user1Entry = DirectoryServer.getEntry(user1DN);
+ Entry user2Entry = DirectoryServer.getEntry(user2DN);
+ Entry user3Entry = DirectoryServer.getEntry(user3DN);
+ Entry user4Entry = DirectoryServer.getEntry(user4DN);
+ Entry user5Entry = DirectoryServer.getEntry(user5DN);
+ Group<? extends GroupImplementationCfg> group1Instance =
+ (Group<? extends GroupImplementationCfg>)
+ groupManager.getGroupInstance(group1DN);
+ assertNotNull(group1Instance);
+ //Add even numbered groups.
+ group1Instance.addNestedGroup(group2DN);
+ group1Instance.addNestedGroup(group4DN);
+ //Add even numbered members.
+ group1Instance.addMember(user2Entry);
+ group1Instance.addMember(user4Entry);
+ //Switch things around, change groups and members to odd numbered nested
+ //groups and odd numbered members via ldap modify.
+ LinkedList<Modification> mods = new LinkedList<Modification>();
+ Attribute g1 = new Attribute("member", "cn=group 1,ou=Groups,o=test");
+ Attribute g2 = new Attribute("member", "cn=group 2,ou=Groups,o=test");
+ Attribute g3 = new Attribute("member", "cn=group 3,ou=Groups,o=test");
+ Attribute g4 = new Attribute("member", "cn=group 4,ou=Groups,o=test");
+ Attribute u1 = new Attribute("member", "uid=user.1,ou=People,o=test");
+ Attribute u2 = new Attribute("member", "uid=user.2,ou=People,o=test");
+ Attribute u3 = new Attribute("member", "uid=user.3,ou=People,o=test");
+ Attribute u4 = new Attribute("member", "uid=user.4,ou=People,o=test");
+ Attribute u5 = new Attribute("member", "uid=user.5,ou=People,o=test");
+ //Delete even groups and users.
+ mods.add(new Modification(ModificationType.DELETE, g2));
+ mods.add(new Modification(ModificationType.DELETE, g4));
+ mods.add(new Modification(ModificationType.DELETE, u2));
+ mods.add(new Modification(ModificationType.DELETE, u4));
+ //Add odd groups and users.
+ mods.add(new Modification(ModificationType.ADD, g1));
+ mods.add(new Modification(ModificationType.ADD, g3));
+ mods.add(new Modification(ModificationType.ADD, u1));
+ mods.add(new Modification(ModificationType.ADD, u3));
+ mods.add(new Modification(ModificationType.ADD, u5));
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ ModifyOperation modifyOperation =
+ conn.processModify(group1Instance.getGroupDN(), mods);
+ assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
+ //Check that the user membership changes were picked up.
+ assertFalse(group1Instance.isMember(user2Entry));
+ assertFalse(group1Instance.isMember(user4Entry));
+ assertTrue(group1Instance.isMember(user1Entry));
+ assertTrue(group1Instance.isMember(user3Entry));
+ assertTrue(group1Instance.isMember(user5Entry));
+ assertFalse(group1Instance.isMember(group2DN));
+ assertFalse(group1Instance.isMember(group4DN));
+ assertTrue(group1Instance.isMember(group1DN));
+ assertTrue(group1Instance.isMember(group3DN));
+ //Check get members picked up everything.
+ MemberList memberList = group1Instance.getMembers();
+ while (memberList.hasMoreMembers())
+ {
+ DN memberDN = memberList.nextMemberDN();
+ assertTrue(memberDN.equals(group1DN) || memberDN.equals(group3DN) ||
+ memberDN.equals(user1DN) ||
+ memberDN.equals(user3DN) ||
+ memberDN.equals(user5DN));
+ }
+ //Check that the nested group changes were picked up.
+ List<DN> nestedGroups=group1Instance.getNestedGroupDNs();
+ assertFalse(nestedGroups.isEmpty());
+ assertTrue(nestedGroups.contains(group1DN));
+ assertFalse(nestedGroups.contains(group2DN));
+ assertTrue(nestedGroups.contains(group3DN));
+ assertFalse(nestedGroups.contains(group4DN));
+ }
+
+ /**
+ * Invokes membership and nested group APIs using a group instance that has
+ * been removed from the group manager via ldap delete.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testStaticGroupInstanceInvalid() throws Exception {
+ TestCaseUtils.initializeTestBackend(true);
+ GroupManager groupManager = DirectoryServer.getGroupManager();
+ groupManager.deregisterAllGroups();
+ addNestedGroupTestEntries();
+ DN group1DN = DN.decode("cn=group 1,ou=Groups,o=test");
+ DN group2DN = DN.decode("cn=group 2,ou=Groups,o=test");
+ DN group3DN = DN.decode("cn=group 3,ou=Groups,o=test");
+ DN group4DN = DN.decode("cn=group 4,ou=Groups,o=test");
+ DN user1DN = DN.decode("uid=user.1,ou=People,o=test");
+ Entry user1Entry = DirectoryServer.getEntry(user1DN);
+ Group<? extends GroupImplementationCfg> group1Instance =
+ (Group<? extends GroupImplementationCfg>)
+ groupManager.getGroupInstance(group1DN);
+ Group<? extends GroupImplementationCfg> group2Instance =
+ (Group<? extends GroupImplementationCfg>)
+ groupManager.getGroupInstance(group2DN);
+ assertNotNull(group1Instance);
+ //Add some nested groups and members.
+ group1Instance.addNestedGroup(group2DN);
+ group1Instance.addMember(user1Entry);
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ //Delete the group.
+ DeleteOperation deleteOperation = conn.processDelete(group1DN);
+ assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
+ assertNull(groupManager.getGroupInstance(group1DN));
+ //Membership check should throw an exception.
+ try
+ {
+ group1Instance.isMember(user1DN);
+ throw new AssertionError("Expected isMember to fail but " +
+ "it didn't");
+ } catch (DirectoryException ex) {}
+ //Nested groups should be empty.
+ List<DN> nestedGroups=group1Instance.getNestedGroupDNs();
+ assertTrue(nestedGroups.isEmpty());
+ try
+ {
+ MemberList memberList=group1Instance.getMembers();
+ throw new AssertionError("Expected getMembers to fail but " +
+ "it didn't");
+ } catch (DirectoryException ex) {}
+ }
+
+ /**
+ * Invokes nested group API methods on various nested group
+ * scenerios.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testStaticGroupNestedAPI() throws Exception {
+ TestCaseUtils.initializeTestBackend(true);
+ GroupManager groupManager = DirectoryServer.getGroupManager();
+ groupManager.deregisterAllGroups();
+ addNestedGroupTestEntries();
+ DN group1DN = DN.decode("cn=group 1,ou=Groups,o=test");
+ DN group2DN = DN.decode("cn=group 2,ou=Groups,o=test");
+ DN group3DN = DN.decode("cn=group 3,ou=Groups,o=test");
+ DN group4DN = DN.decode("cn=group 4,ou=Groups,o=test");
+ DN bogusGroup = DN.decode("cn=bogus group,ou=Groups,o=test");
+ DN user1DN = DN.decode("uid=user.1,ou=People,o=test");
+ DN user2DN = DN.decode("uid=user.2,ou=People,o=test");
+ DN user3DN = DN.decode("uid=user.3,ou=People,o=test");
+ DN user4DN = DN.decode("uid=user.4,ou=People,o=test");
+ DN user5DN = DN.decode("uid=user.5,ou=People,o=test");
+ Entry user1Entry = DirectoryServer.getEntry(user1DN);
+ Entry user2Entry = DirectoryServer.getEntry(user2DN);
+ Entry user3Entry = DirectoryServer.getEntry(user3DN);
+ Entry user4Entry = DirectoryServer.getEntry(user4DN);
+ Entry user5Entry = DirectoryServer.getEntry(user5DN);
+ //These casts are needed so there isn't a unchecked assignment
+ //compile warning in the getNestedGroupDNs calls below. Some IDEs
+ //will give a unchecked cast warning.
+ Group<? extends GroupImplementationCfg> group1Instance =
+ (Group<? extends GroupImplementationCfg>)
+ groupManager.getGroupInstance(group1DN);
+ Group<? extends GroupImplementationCfg> group2Instance =
+ (Group<? extends GroupImplementationCfg>)
+ groupManager.getGroupInstance(group2DN);
+ Group group3Instance = groupManager.getGroupInstance(group3DN);
+ assertNotNull(group1Instance);
+ assertNotNull(group2Instance);
+ assertNotNull(group3Instance);
+ //Add nested groups.
+ group1Instance.addNestedGroup(group2DN);
+ group2Instance.addNestedGroup(group3DN);
+ //Add some members.
+ group1Instance.addMember(user1Entry);
+ group2Instance.addMember(user2Entry);
+ group3Instance.addMember(user3Entry);
+ group2Instance.addMember(user4Entry);
+ group3Instance.addMember(user5Entry);
+ //Check if group 3 shows up in the group 2 membership list.
+ MemberList memberList = group2Instance.getMembers();
+ boolean found=false;
+ while (memberList.hasMoreMembers())
+ {
+ if( memberList.nextMemberDN().equals(group3DN)) {
+ found=true;
+ break;
+ }
+ }
+ assertTrue(found);
+ //Check membership via group 1 using nesting of group 2 and group 3.
+ //User 5 is in group 3.
+ assertTrue(group1Instance.isMember(user5DN));
+ group2Instance.removeNestedGroup(group3DN);
+ //Check group 3 is removed from group 2 membership list.
+ memberList = group2Instance.getMembers();
+ found=false;
+ while (memberList.hasMoreMembers())
+ {
+ if(memberList.nextMemberDN().equals(user3DN)){
+ found=true;
+ break;
+ }
+ }
+ assertFalse(found);
+ //Check membership via group 1 should fail now, since nested group 3
+ //was removed from group 2.
+ assertFalse(group1Instance.isMember(user5DN));
+ group2Instance.addNestedGroup(group3DN);
+ //Check remove member call also removes DN from nested group list.
+ group2Instance.removeMember(group3DN);
+ assertFalse(group2Instance.getNestedGroupDNs().contains(group3DN));
+ group1Instance.removeNestedGroup(group2DN);
+ List<DN> nestedGroups=group1Instance.getNestedGroupDNs();
+ assertTrue(nestedGroups.isEmpty());
+ //Add nested groups to group 1
+ group1Instance.addNestedGroup(group2DN);
+ group1Instance.addNestedGroup(group3DN);
+ group1Instance.addNestedGroup(group4DN);
+ //Check get nested groups DNs list returns correct DN list.
+ List<DN> nestedGroups1=group1Instance.getNestedGroupDNs();
+ assertFalse(nestedGroups1.isEmpty());
+ assertTrue(nestedGroups1.contains(group2DN));
+ assertTrue(nestedGroups1.contains(group3DN));
+ assertTrue(nestedGroups1.contains(group4DN));
+ //Check removing a group not in the nested group list fails.
+ try
+ {
+ group1Instance.removeNestedGroup(bogusGroup);
+ throw new AssertionError("Expected removeNestedGroup to fail but " +
+ "it didn't");
+ } catch (DirectoryException ex) {}
+ //Check adding a nested group already in the nested group list fails.
+ try
+ {
+ group1Instance.addNestedGroup(group2DN);
+ throw new AssertionError("Expected addNestedGroup to fail but " +
+ "it didn't");
+ } catch (DirectoryException ex) {}
+ //Modify list via ldap modify.
+ LinkedList<Modification> mods = new LinkedList<Modification>();
+ Attribute a2 = new Attribute("member", "cn=group 2,ou=Groups,o=test");
+ Attribute a3 = new Attribute("member", "cn=group 1,ou=Groups,o=test");
+ mods.add(new Modification(ModificationType.DELETE, a2));
+ mods.add(new Modification(ModificationType.ADD, a3));
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ ModifyOperation modifyOperation =
+ conn.processModify(group1Instance.getGroupDN(), mods);
+ assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
+ //Check removing a group already removed via ldap modify fails.
+ try
+ {
+ group1Instance.removeNestedGroup(group2DN);
+ throw new AssertionError("Expected removeNestedGroup to fail but " +
+ "it didn't");
+ } catch (DirectoryException ex) {}
+ //Check adding a group added via ldap modify fails.
+ try
+ {
+ group1Instance.addNestedGroup(group1DN);
+ throw new AssertionError("Expected addNestedGroup to fail but " +
+ "it didn't");
+ } catch (DirectoryException ex) {}
+ }
+
/**
@@ -192,23 +571,25 @@
assertTrue(groupInstance.isMember(user2DN));
assertFalse(groupInstance.isMember(user3DN));
- assertFalse(groupInstance.supportsNestedGroups());
+ assertTrue(groupInstance.supportsNestedGroups());
assertTrue(groupInstance.getNestedGroupDNs().isEmpty());
try
{
groupInstance.addNestedGroup(DN.decode("uid=test,ou=People,o=test"));
- throw new AssertionError("Expected addNestedGroup to fail but it " +
- "didn't");
- } catch (UnsupportedOperationException uoe) {}
+ } catch (DirectoryException ex) {
+ throw new AssertionError("Expected addNestedGroup to succeed but" +
+ " it didn't");
+ }
try
{
groupInstance.removeNestedGroup(
DN.decode("uid=test,ou=People,o=test"));
- throw new AssertionError("Expected removeNestedGroup to fail but " +
- "it didn't");
- } catch (UnsupportedOperationException uoe) {}
+ } catch (DirectoryException ex) {
+ throw new AssertionError("Expected removeNestedGroup to succeed " +
+ "but it didn't");
+ }
assertTrue(groupInstance.mayAlterMemberList());
@@ -1691,5 +2072,100 @@
assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
assertNull(groupManager.getGroupInstance(groupDN));
}
+
+ /**
+ * Adds nested group entries.
+ *
+ * @throws Exception If a problem adding the entries occurs.
+ */
+ private void addNestedGroupTestEntries() throws Exception {
+
+ TestCaseUtils.addEntries(
+ "dn: ou=People,o=test",
+ "objectClass: top",
+ "objectClass: organizationalUnit",
+ "ou: People",
+ "",
+ "dn: ou=Groups,o=test",
+ "objectClass: top",
+ "objectClass: organizationalUnit",
+ "ou: Groups",
+ "",
+ "dn: cn=group 1,ou=Groups,o=test",
+ "objectClass: top",
+ "objectClass: groupOfNames",
+ "cn: group 1",
+ "",
+ "dn: cn=group 2,ou=Groups,o=test",
+ "objectClass: top",
+ "objectClass: groupOfNames",
+ "cn: group 2",
+ "",
+ "dn: cn=group 3,ou=Groups,o=test",
+ "objectClass: top",
+ "objectClass: groupOfNames",
+ "cn: group 3",
+ "",
+ "dn: cn=group 4,ou=Groups,o=test",
+ "objectClass: top",
+ "objectClass: groupOfURLs",
+ "cn: group 4",
+ "memberURL: ldap:///ou=people,o=test??sub?(sn>=5)",
+ "",
+ "dn: uid=user.1,ou=People,o=test",
+ "objectClass: top",
+ "objectClass: person",
+ "objectClass: organizationalPerson",
+ "objectClass: inetOrgPerson",
+ "uid: user.1",
+ "givenName: User",
+ "sn: 1",
+ "cn: User 1",
+ "userPassword: password",
+ "",
+ "dn: uid=user.2,ou=People,o=test",
+ "objectClass: top",
+ "objectClass: person",
+ "objectClass: organizationalPerson",
+ "objectClass: inetOrgPerson",
+ "uid: user.2",
+ "givenName: User",
+ "sn: 2",
+ "cn: User 2",
+ "userPassword: password",
+ "",
+ "dn: uid=user.3,ou=People,o=test",
+ "objectClass: top",
+ "objectClass: person",
+ "objectClass: organizationalPerson",
+ "objectClass: inetOrgPerson",
+ "uid: user.3",
+ "givenName: User",
+ "sn: 3",
+ "cn: User 3",
+ "userPassword: password",
+ "",
+ "dn: uid=user.4,ou=People,o=test",
+ "objectClass: top",
+ "objectClass: person",
+ "objectClass: organizationalPerson",
+ "objectClass: inetOrgPerson",
+ "uid: user.4",
+ "givenName: User",
+ "sn: 4",
+ "cn: User 4",
+ "userPassword: password",
+ "",
+ "dn: uid=user.5,ou=People,o=test",
+ "objectClass: top",
+ "objectClass: person",
+ "objectClass: organizationalPerson",
+ "objectClass: inetOrgPerson",
+ "uid: user.5",
+ "givenName: User",
+ "sn: 5",
+ "cn: User 5",
+ "userPassword: password");
+ }
}
--
Gitblit v1.10.0