From bc11734a34d63204bfcc6e9087e0664a9772fae3 Mon Sep 17 00:00:00 2001
From: Ludovic Poitou <ludovic.poitou@forgerock.com>
Date: Tue, 07 Oct 2014 21:25:21 +0000
Subject: [PATCH] Port to the DJ3 dev branch the fix for OPENDJ-1586 - Nested Groups fail to return indirect members with db's larger than 10 entries.

---
 opendj3-server-dev/src/server/org/opends/server/extensions/IsMemberOfVirtualAttributeProvider.java                                 |   82 ++++++++++++++++++++++++++++++++--------
 opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/extensions/IsMemberOfVirtualAttributeProviderTestCase.java |   16 ++++---
 2 files changed, 74 insertions(+), 24 deletions(-)

diff --git a/opendj3-server-dev/src/server/org/opends/server/extensions/IsMemberOfVirtualAttributeProvider.java b/opendj3-server-dev/src/server/org/opends/server/extensions/IsMemberOfVirtualAttributeProvider.java
index bb2870c..e69a4ad 100644
--- a/opendj3-server-dev/src/server/org/opends/server/extensions/IsMemberOfVirtualAttributeProvider.java
+++ b/opendj3-server-dev/src/server/org/opends/server/extensions/IsMemberOfVirtualAttributeProvider.java
@@ -26,7 +26,9 @@
  */
 package org.opends.server.extensions;
 
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 import org.forgerock.i18n.slf4j.LocalizedLogger;
 import org.forgerock.opendj.ldap.ByteString;
@@ -248,32 +250,32 @@
   public void processSearch(VirtualAttributeRule rule,
                             SearchOperation searchOperation)
   {
-    SearchFilter filter = searchOperation.getFilter();
-    Group<?> group = extractGroup(rule.getAttributeType(), filter);
+    Group<?> group = extractGroup(rule.getAttributeType(), searchOperation.getFilter());
     if (group == null)
     {
       return;
     }
 
-    DN          baseDN = searchOperation.getBaseDN();
-    SearchScope scope  = searchOperation.getScope();
     try
     {
-      MemberList  memberList = group.getMembers();
-      while (memberList.hasMoreMembers())
+      // Check for nested groups to see if we need to keep track of returned entries
+      List<DN> nestedGroupsDNs = group.getNestedGroupDNs();
+      HashSet<String> returnedDNs = null;
+      if (!nestedGroupsDNs.isEmpty())
       {
-        try
+        returnedDNs = new HashSet<String>();
+      }
+      if (!returnGroupMembers(searchOperation, group.getMembers(), returnedDNs))
+      {
+        return;
+      }
+      // Now check members of nested groups
+      for (DN dn : nestedGroupsDNs)
+      {
+        group = DirectoryServer.getGroupManager().getGroupInstance(dn);
+        if (!returnGroupMembers(searchOperation, group.getMembers(), returnedDNs))
         {
-          Entry e = memberList.nextMemberEntry();
-          if (e.matchesBaseAndScope(baseDN, scope) &&
-              filter.matchesEntry(e))
-          {
-            searchOperation.returnEntry(e, null);
-          }
-        }
-        catch (Exception e)
-        {
-          logger.traceException(e);
+          return;
         }
       }
     }
@@ -283,6 +285,52 @@
     }
   }
 
+  /**
+   *
+   * @param searchOperation the search operation being processed.
+   * @param memberList the list of members of the group being processed.
+   * @param returnedDNs a set to store the DNs of entries already returned,
+   *                    null if there's no need to track for entries.
+   * @return  <CODE>true</CODE> if the caller should continue processing the
+   *          search request and sending additional entries and references, or
+   *          <CODE>false</CODE> if not for some reason (e.g., the size limit
+   *          has been reached or the search has been abandoned).
+   * @throws DirectoryException If a problem occurs while attempting to send
+   *          the entry to the client and the search should be terminated.
+   */
+  private boolean returnGroupMembers(SearchOperation searchOperation,
+                                  MemberList memberList, Set<String> returnedDNs)
+          throws DirectoryException
+  {
+    DN baseDN = searchOperation.getBaseDN();
+    SearchScope scope  = searchOperation.getScope();
+    SearchFilter filter = searchOperation.getFilter();
+    while (memberList.hasMoreMembers())
+    {
+      try
+      {
+        Entry e = memberList.nextMemberEntry();
+        if (e.matchesBaseAndScope(baseDN, scope) &&
+            filter.matchesEntry(e))
+        {
+          if (returnedDNs == null
+              || returnedDNs.add(e.getName().toNormalizedString()))
+          {
+            if (!searchOperation.returnEntry(e, null))
+            {
+              return false;
+            }
+          }
+        }
+      }
+      catch (Exception e)
+      {
+        logger.traceException(e);
+      }
+    }
+    return true;
+  }
+
 
 
   /**
diff --git a/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/extensions/IsMemberOfVirtualAttributeProviderTestCase.java b/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/extensions/IsMemberOfVirtualAttributeProviderTestCase.java
index 011c23d..4d3afb7 100644
--- a/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/extensions/IsMemberOfVirtualAttributeProviderTestCase.java
+++ b/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/extensions/IsMemberOfVirtualAttributeProviderTestCase.java
@@ -942,28 +942,30 @@
     builder.append("\nobjectClass: domain");
     builder.append("\ndc: example");
 
-    builder.append("\n\ndn: ou=People"+SUFFIX);
+    builder.append("\n\ndn: ou=People").append(SUFFIX);
     builder.append("\nobjectClass: organizationalunit");
     builder.append("\nou: People");
     //Go beyond ALL ID threshold.
     for(int i=0;i<4001;i++)
     {
-      builder.append("\n\ndn: cn=user." + i + ",ou=People"+SUFFIX);
+      builder.append("\n\ndn: cn=user.").append(i)
+             .append(",ou=People").append(SUFFIX);
       builder.append("\nobjectclass: person");
-      builder.append("\ncn: user." + i);
-      builder.append("\nsn: " + i);
+      builder.append("\ncn: user.").append(i);
+      builder.append("\nsn: ").append(i);
     }
 
     //Add the group information.
-    builder.append("\n\ndn: ou=Groups"+SUFFIX);
+    builder.append("\n\ndn: ou=Groups").append(SUFFIX);
     builder.append("\nobjectclass: organizationalunit");
     builder.append("\nou: Groups");
 
     //Dynamic group.
-    builder.append("\n\ndn: cn=MyDGrp,ou=Groups"+SUFFIX);
+    builder.append("\n\ndn: cn=MyDGrp,ou=Groups").append(SUFFIX);
     builder.append("\nobjectClass: groupOfURLs");
     builder.append("\ncn: MyDGrp");
-    builder.append("\nmemberURL: ldap:///ou=people"+SUFFIX+"??sub?(objectclass=person)");
+    builder.append("\nmemberURL: ldap:///ou=people").append(SUFFIX)
+           .append("??sub?(objectclass=person)");
     TestCaseUtils.addEntries(builder.toString());
     //Verify the entry.
     Entry e =

--
Gitblit v1.10.0