From f59baa7abea806ac6d93c8059bfddb60b3acc06d Mon Sep 17 00:00:00 2001
From: neil_a_wilson <neil_a_wilson@localhost>
Date: Fri, 14 Sep 2007 21:10:37 +0000
Subject: [PATCH] Add support for the groupOfEntries group type as defined in draft-findlay-ldap-groupofentries.  Entries with the groupOfEntries object class should be treated in a manner that is virtually identical to entries with the groupOfNames object class (technically, groupOfNames does not allow zero-member groups, but the OpenDS implementation does allow this).

---
 opendj-sdk/opends/src/server/org/opends/server/extensions/StaticGroup.java                            |   40 +++++++
 opendj-sdk/opends/resource/schema/00-core.ldif                                                        |   11 +
 opendj-sdk/opends/src/server/org/opends/server/util/ServerConstants.java                              |   16 +++
 opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/core/GroupManagerTestCase.java |  214 ++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 273 insertions(+), 8 deletions(-)

diff --git a/opendj-sdk/opends/resource/schema/00-core.ldif b/opendj-sdk/opends/resource/schema/00-core.ldif
index 979bedf..0897463 100644
--- a/opendj-sdk/opends/resource/schema/00-core.ldif
+++ b/opendj-sdk/opends/resource/schema/00-core.ldif
@@ -433,10 +433,10 @@
   SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 X-ORIGIN 'RFC 1274' )
 attributeTypes: ( 0.9.2342.19200300.100.1.31 NAME 'cNAMERecord'
   SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 X-ORIGIN 'RFC 1274' )
-attributeTypes: ( 2.16.840.1.113730.3.1.602 NAME 'entryDN'
-  DESC 'DN of the entry' EQUALITY distinguishedNameMatch
-  SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 SINGLE-VALUE NO-USER-MODIFICATION
-  USAGE directoryOperation X-ORIGIN 'draft-zeilenga-ldap-entrydn' )
+attributeTypes: ( 1.3.6.1.1.20 NAME 'entryDN' DESC 'DN of the entry'
+  EQUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.12
+  SINGLE-VALUE NO-USER-MODIFICATION USAGE directoryOperation
+  X-ORIGIN 'RFC 5020' )
 attributeTypes: ( 1.3.6.1.4.1.453.16.2.103 NAME 'numSubordinates'
   DESC 'Count of immediate subordinates'
   EQUALITY integerMatch ORDERING integerOrderingMatch
@@ -622,4 +622,7 @@
 objectClasses: ( 0.9.2342.19200300.100.4.22 NAME 'qualityLabelledData' SUP top
   MUST dSAQuality MAY ( subtreeMinimumQuality $ subtreeMaximumQuality )
   X-ORIGIN 'RFC 1274' )
+objectClasses: ( 1.2.826.0.1.3458854.2.1.1 NAME 'groupOfEntries' SUP top
+  STRUCTURAL MUST cn MAY ( member $ businessCategory $ seeAlso $ owner $ ou $
+  o $ description ) X-ORIGIN 'draft-findlay-ldap-groupofentries' )
 
diff --git a/opendj-sdk/opends/src/server/org/opends/server/extensions/StaticGroup.java b/opendj-sdk/opends/src/server/org/opends/server/extensions/StaticGroup.java
index 1008313..2e46e86 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/extensions/StaticGroup.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/extensions/StaticGroup.java
@@ -171,11 +171,32 @@
     // Determine whether it is a groupOfNames or groupOfUniqueNames entry.  If
     // neither, then that's a problem.
     AttributeType memberAttributeType;
+    ObjectClass groupOfEntriesClass =
+         DirectoryConfig.getObjectClass(OC_GROUP_OF_ENTRIES_LC, true);
     ObjectClass groupOfNamesClass =
          DirectoryConfig.getObjectClass(OC_GROUP_OF_NAMES_LC, true);
     ObjectClass groupOfUniqueNamesClass =
          DirectoryConfig.getObjectClass(OC_GROUP_OF_UNIQUE_NAMES_LC, true);
-    if (groupEntry.hasObjectClass(groupOfNamesClass))
+    if (groupEntry.hasObjectClass(groupOfEntriesClass))
+    {
+      if (groupEntry.hasObjectClass(groupOfNamesClass))
+      {
+        Message message = ERR_STATICGROUP_INVALID_OC_COMBINATION.
+            get(String.valueOf(groupEntry.getDN()), OC_GROUP_OF_ENTRIES,
+                OC_GROUP_OF_NAMES);
+        throw new DirectoryException(ResultCode.OBJECTCLASS_VIOLATION, message);
+      }
+      else if (groupEntry.hasObjectClass(groupOfUniqueNamesClass))
+      {
+        Message message = ERR_STATICGROUP_INVALID_OC_COMBINATION.
+            get(String.valueOf(groupEntry.getDN()), OC_GROUP_OF_ENTRIES,
+                OC_GROUP_OF_UNIQUE_NAMES);
+        throw new DirectoryException(ResultCode.OBJECTCLASS_VIOLATION, message);
+      }
+
+      memberAttributeType = DirectoryConfig.getAttributeType(ATTR_MEMBER, true);
+    }
+    else if (groupEntry.hasObjectClass(groupOfNamesClass))
     {
       if (groupEntry.hasObjectClass(groupOfUniqueNamesClass))
       {
@@ -247,7 +268,8 @@
     // FIXME -- This needs to exclude enhanced groups once we have support for
     // them.
     String filterString =
-         "(&(|(objectClass=groupOfNames)(objectClass=groupOfUniqueNames))" +
+         "(&(|(objectClass=groupOfNames)(objectClass=groupOfUniqueNames)" +
+            "(objectClass=groupOfEntries))" +
             "(!(objectClass=ds-virtual-static-group)))";
     return SearchFilter.createFilterFromString(filterString);
   }
@@ -271,11 +293,23 @@
       return false;
     }
 
+    ObjectClass groupOfEntriesClass =
+         DirectoryConfig.getObjectClass(OC_GROUP_OF_ENTRIES_LC, true);
     ObjectClass groupOfNamesClass =
          DirectoryConfig.getObjectClass(OC_GROUP_OF_NAMES_LC, true);
     ObjectClass groupOfUniqueNamesClass =
          DirectoryConfig.getObjectClass(OC_GROUP_OF_UNIQUE_NAMES_LC, true);
-    if (entry.hasObjectClass(groupOfNamesClass))
+    if (entry.hasObjectClass(groupOfEntriesClass))
+    {
+      if (entry.hasObjectClass(groupOfNamesClass) ||
+          entry.hasObjectClass(groupOfUniqueNamesClass))
+      {
+        return false;
+      }
+
+      return true;
+    }
+    else if (entry.hasObjectClass(groupOfNamesClass))
     {
       if (entry.hasObjectClass(groupOfUniqueNamesClass))
       {
diff --git a/opendj-sdk/opends/src/server/org/opends/server/util/ServerConstants.java b/opendj-sdk/opends/src/server/org/opends/server/util/ServerConstants.java
index 704801e..cb9efc5 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/util/ServerConstants.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/util/ServerConstants.java
@@ -655,6 +655,22 @@
 
 
   /**
+   * The name of the standard "groupOfEntries" object class, formatted in
+   * camelCase.
+   */
+  public static final String OC_GROUP_OF_ENTRIES = "groupOfEntries";
+
+
+
+  /**
+   * The name of the standard "groupOfEntries" object class, formatted in all
+   * lowercase characters.
+   */
+  public static final String OC_GROUP_OF_ENTRIES_LC = "groupofentries";
+
+
+
+  /**
    * The name of the standard "groupOfNames" object class, formatted in
    * camelCase.
    */
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/core/GroupManagerTestCase.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/core/GroupManagerTestCase.java
index 01a7716..75630c0 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/core/GroupManagerTestCase.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/core/GroupManagerTestCase.java
@@ -1002,6 +1002,200 @@
 
 
   /**
+   * Tests that the server properly handles adding, deleting, and modifying a
+   * static group based on the groupOfEntries object class where that group
+   * contains valid members.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test()
+  public void testValidPopulatedGroupOfEntries()
+         throws Exception
+  {
+    TestCaseUtils.initializeTestBackend(true);
+
+    GroupManager groupManager = DirectoryServer.getGroupManager();
+    groupManager.deregisterAllGroups();
+
+    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: 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");
+
+
+    // Make sure that there aren't any groups registered with the server.
+    assertFalse(groupManager.getGroupInstances().iterator().hasNext());
+
+
+    // Add a new static group to the server and make sure it gets registered
+    // with the group manager.
+    TestCaseUtils.addEntry(
+      "dn: cn=Test Group of Entries,ou=Groups,o=test",
+      "objectClass: top",
+      "objectClass: groupOfEntries",
+      "cn: Test Group of Entries",
+      "member: uid=user.1,ou=People,o=test",
+      "member: uid=user.2,ou=People,o=test");
+
+
+    // Perform a basic set of validation on the group itself.
+    DN groupDN = DN.decode("cn=Test Group of Entries,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");
+
+    Group groupInstance = groupManager.getGroupInstance(groupDN);
+    assertNotNull(groupInstance);
+    assertEquals(groupInstance.getGroupDN(), groupDN);
+    assertTrue(groupInstance.isMember(user1DN));
+    assertTrue(groupInstance.isMember(user2DN));
+    assertFalse(groupInstance.isMember(user3DN));
+
+    MemberList memberList = groupInstance.getMembers();
+    while (memberList.hasMoreMembers())
+    {
+      DN memberDN = memberList.nextMemberDN();
+      assertTrue(memberDN.equals(user1DN) || memberDN.equals(user2DN));
+    }
+
+    SearchFilter filter = SearchFilter.createFilterFromString("(uid=user.1)");
+    memberList = groupInstance.getMembers(DN.decode("o=test"),
+                                          SearchScope.WHOLE_SUBTREE, filter);
+    assertTrue(memberList.hasMoreMembers());
+    DN memberDN = memberList.nextMemberDN();
+    assertTrue(memberDN.equals(user1DN));
+    assertFalse(memberList.hasMoreMembers());
+
+    filter = SearchFilter.createFilterFromString("(uid=user.3)");
+    memberList = groupInstance.getMembers(DN.decode("o=test"),
+                                          SearchScope.WHOLE_SUBTREE, filter);
+    assertFalse(memberList.hasMoreMembers());
+
+
+    // Modify the group and make sure the group manager gets updated
+    // accordingly.
+    LinkedList<Modification> mods = new LinkedList<Modification>();
+    Attribute a2 = new Attribute("member", "uid=user.2,ou=People,o=test");
+    Attribute a3 = new Attribute("member", "uid=user.3,ou=People,o=test");
+    mods.add(new Modification(ModificationType.DELETE, a2));
+    mods.add(new Modification(ModificationType.ADD, a3));
+
+    InternalClientConnection conn =
+         InternalClientConnection.getRootConnection();
+    ModifyOperation modifyOperation = conn.processModify(groupDN, mods);
+    assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
+
+    groupInstance = groupManager.getGroupInstance(groupDN);
+    assertNotNull(groupInstance);
+    assertEquals(groupInstance.getGroupDN(), groupDN);
+    assertTrue(groupInstance.isMember(user1DN));
+    assertFalse(groupInstance.isMember(user2DN));
+    assertTrue(groupInstance.isMember(user3DN));
+
+
+    // Delete the group and make sure the group manager gets updated
+    // accordingly.
+    DeleteOperation deleteOperation = conn.processDelete(groupDN);
+    assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
+    assertNull(groupManager.getGroupInstance(groupDN));
+  }
+
+
+
+  /**
+   * Tests that the server properly handles a groupOfEntries object that doesn't
+   * contain any members.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test()
+  public void testValidEmptyGroupOfEntries()
+         throws Exception
+  {
+    TestCaseUtils.initializeTestBackend(true);
+
+    GroupManager groupManager = DirectoryServer.getGroupManager();
+    groupManager.deregisterAllGroups();
+
+    TestCaseUtils.addEntry(
+      "dn: ou=Groups,o=test",
+      "objectClass: top",
+      "objectClass: organizationalUnit",
+      "ou: Groups");
+
+
+    // Make sure that there aren't any groups registered with the server.
+    assertFalse(groupManager.getGroupInstances().iterator().hasNext());
+
+
+    // Add a new static group to the server and make sure it gets registered
+    // with the group manager.
+    TestCaseUtils.addEntry(
+      "dn: cn=Test Group of Entries,ou=Groups,o=test",
+      "objectClass: top",
+      "objectClass: groupOfEntries",
+      "cn: Test Group of Names");
+
+
+    // Make sure that the group exists but doesn't have any members.
+    DN groupDN = DN.decode("cn=Test Group of Entries,ou=Groups,o=test");
+    Group groupInstance = groupManager.getGroupInstance(groupDN);
+    assertNotNull(groupInstance);
+    assertEquals(groupInstance.getGroupDN(), groupDN);
+    assertFalse(groupInstance.getMembers().hasMoreMembers());
+
+
+    // Delete the group and make sure the group manager gets updated
+    // accordingly.
+    InternalClientConnection conn =
+         InternalClientConnection.getRootConnection();
+    DeleteOperation deleteOperation = conn.processDelete(groupDN);
+    assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
+    assertNull(groupManager.getGroupInstance(groupDN));
+  }
+
+
+
+  /**
    * Verifies that the group manager properly handles modify DN operations on
    * static group entries.
    *
@@ -1170,13 +1364,21 @@
       "objectClass: groupOfUniqueNames",
       "cn: Group 3",
       "uniqueMember: uid=user.1,ou=People,o=test",
-      "uniqueMember: uid=user.3,ou=People,o=test");
+      "uniqueMember: uid=user.3,ou=People,o=test",
+      "",
+      "dn: cn=Group 4,ou=Groups,o=test",
+      "objectClass: top",
+      "objectClass: groupOfEntries",
+      "cn: Group 4",
+      "member: uid=user.1,ou=People,o=test",
+      "member: uid=user.2,ou=People,o=test");
 
 
     // Perform basic validation on the groups.
     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");
@@ -1184,6 +1386,7 @@
     Group group1 = groupManager.getGroupInstance(group1DN);
     Group group2 = groupManager.getGroupInstance(group2DN);
     Group group3 = groupManager.getGroupInstance(group3DN);
+    Group group4 = groupManager.getGroupInstance(group4DN);
 
     assertNotNull(group1);
     assertTrue(group1.isMember(user1DN));
@@ -1200,6 +1403,11 @@
     assertFalse(group3.isMember(user2DN));
     assertTrue(group3.isMember(user3DN));
 
+    assertNotNull(group4);
+    assertTrue(group4.isMember(user1DN));
+    assertTrue(group4.isMember(user2DN));
+    assertFalse(group4.isMember(user3DN));
+
 
     // Get a client connection authenticated as user1 and make sure it handles
     // group operations correctly.
@@ -1332,6 +1540,10 @@
     deleteOperation = conn.processDelete(group3DN);
     assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
     assertNull(groupManager.getGroupInstance(group3DN));
+
+    deleteOperation = conn.processDelete(group4DN);
+    assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
+    assertNull(groupManager.getGroupInstance(group3DN));
   }
 
 

--
Gitblit v1.10.0