From e69422e8141e3551e310919a35ecc56cf69a6935 Mon Sep 17 00:00:00 2001
From: Fabio Pistolesi <fabio.pistolesi@forgerock.com>
Date: Wed, 22 Jun 2016 14:54:31 +0000
Subject: [PATCH] OPENDJ-3041 Strictly select the backend where an entry reside taking into account all child backends

---
 opendj-server-legacy/src/test/java/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElementTest.java |   46 +++++++++++++++++++++-
 opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElement.java     |   37 ++++++++++++++++--
 2 files changed, 75 insertions(+), 8 deletions(-)

diff --git a/opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElement.java b/opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElement.java
index faa103f..8487d34 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElement.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElement.java
@@ -20,7 +20,6 @@
 import java.util.Collection;
 import java.util.Iterator;
 import java.util.List;
-import java.util.Map;
 import java.util.TreeMap;
 
 import org.forgerock.i18n.LocalizableMessage;
@@ -903,12 +902,40 @@
     {
       return registeredLocalBackends.get(entryDN);
     }
-    Map.Entry<DN, LocalBackendWorkflowElement> backendWorkflow = registeredLocalBackends.floorEntry(entryDN);
-    if (backendWorkflow.getKey().isSuperiorOrEqualTo(entryDN))
+    /*
+     * Try to minimize the number of lookups in the Map to find the backend containing the entry.
+     * If the DN contains many RDNs it is faster to iterate through the list of registered backends,
+     * otherwise iterating through the parents requires less lookups. It also avoids some attacks
+     * where we would spend time going through the list of all parents to finally decide the
+     * baseDN is absent.
+     */
+    if (entryDN.size() <= registeredLocalBackends.size())
     {
-      return backendWorkflow.getValue();
+      while (!entryDN.isRootDN())
+      {
+        final LocalBackendWorkflowElement workflow = registeredLocalBackends.get(entryDN);
+        if (workflow != null)
+        {
+          return workflow;
+        }
+        entryDN = entryDN.parent();
+      }
+      return null;
     }
-    return null;
+    else
+    {
+      LocalBackendWorkflowElement workflow = null;
+      int currentSize = 0;
+      for (DN backendDN : registeredLocalBackends.keySet())
+      {
+        if (entryDN.isSubordinateOrEqualTo(backendDN) && backendDN.size() > currentSize)
+        {
+          workflow = registeredLocalBackends.get(backendDN);
+          currentSize = backendDN.size();
+        }
+      }
+      return workflow;
+    }
   }
 
   /**
diff --git a/opendj-server-legacy/src/test/java/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElementTest.java b/opendj-server-legacy/src/test/java/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElementTest.java
index a8143c2..998850d 100644
--- a/opendj-server-legacy/src/test/java/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElementTest.java
+++ b/opendj-server-legacy/src/test/java/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElementTest.java
@@ -145,6 +145,37 @@
     }
   }
 
+  @Test
+  public void testParentBackendSelection() throws Exception
+  {
+    String parentID = "parent";
+    String childID1 = "child1";
+    String strangerID = "stranger";
+    String parent = "dc=abc";
+    String child1 = "dc=example,dc=abc";
+    String child2 = "dc=example2,dc=abc";
+    String stranger = "dc=abc1";
+
+    try
+    {
+      TestCaseUtils.clearDataBackends();
+      TestCaseUtils.initializeMemoryBackend(parentID, parent, true);
+      TestCaseUtils.initializeMemoryBackend(childID1, child1, true);
+      TestCaseUtils.initializeMemoryBackend(strangerID, stranger, true);
+
+      assertEquals(getMatchedDN("cn=user," + parent), DN.valueOf(parent));
+      assertEquals(getMatchedDN("cn=user," + child1), DN.valueOf(child1));
+      assertEquals(getMatchedDN("cn=user," + child2), DN.valueOf(parent));
+      assertEquals(getMatchedDN("cn=user," + stranger), DN.valueOf(stranger));
+    }
+    finally
+    {
+      TestCaseUtils.clearMemoryBackend(parentID);
+      TestCaseUtils.clearMemoryBackend(childID1);
+      TestCaseUtils.clearMemoryBackend(strangerID);
+    }
+  }
+
   /**
    * This test checks that the workflow takes into account the subordinate
    * naming context defined in the RootDSEBackend.
@@ -258,9 +289,18 @@
   private void modifyAttribute(String baseDN, ModificationType modType, String attrName, String attrValue)
       throws Exception
   {
-    ModifyRequest modifyRequest = Requests.newModifyRequest(baseDN)
-        .addModification(modType, attrName, attrValue);
-    ModifyOperation modifyOperation = getRootConnection().processModify(modifyRequest);
+    ModifyOperation modifyOperation = getModifyOperation(baseDN, modType, attrName, attrValue);
     assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
   }
+
+  private DN getMatchedDN(String entryDN)
+  {
+    return getModifyOperation(entryDN, ADD, "sn", "dummy").getMatchedDN();
+  }
+
+  private ModifyOperation getModifyOperation(String baseDN, ModificationType modType, String attrName, String attrValue)
+  {
+    ModifyRequest modifyRequest = Requests.newModifyRequest(baseDN).addModification(modType, attrName, attrValue);
+    return getRootConnection().processModify(modifyRequest);
+  }
 }

--
Gitblit v1.10.0