From 2d9c97d306084b1e3d4daf140508a09b26ebb1c8 Mon Sep 17 00:00:00 2001
From: abobrov <abobrov@localhost>
Date: Tue, 20 Oct 2009 15:57:03 +0000
Subject: [PATCH] - RFC 3672 Subentries Control implementation : make earlier drafts based implementation obsolete; keep ldapSubEntry OC search matching criteria for backward compatibility.

---
 opendj-sdk/opends/src/server/org/opends/server/workflowelement/ndb/NDBSearchOperation.java                   |    5 
 opendj-sdk/opends/src/server/org/opends/server/tools/LDAPSearch.java                                         |   14 ++
 opendj-sdk/opends/src/server/org/opends/server/tools/LDAPToolUtils.java                                      |    6 -
 opendj-sdk/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendSearchOperation.java |    5 
 opendj-sdk/opends/src/messages/messages/tools.properties                                                     |    2 
 opendj-sdk/opends/src/server/org/opends/server/controls/SubentriesControl.java                               |  164 ++++++++++++++++++++++++++++++++
 opendj-sdk/opends/src/server/org/opends/server/tools/ToolConstants.java                                      |   10 ++
 opendj-sdk/opends/src/messages/messages/protocol.properties                                                  |    5 +
 opendj-sdk/opends/src/server/org/opends/server/core/SearchOperationBasis.java                                |   37 ++++--
 opendj-sdk/opends/src/server/org/opends/server/util/ServerConstants.java                                     |    2 
 opendj-sdk/opends/src/server/org/opends/server/workflowelement/externalchangelog/ECLSearchOperation.java     |    5 
 opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPSearchTestCase.java         |   11 +-
 12 files changed, 237 insertions(+), 29 deletions(-)

diff --git a/opendj-sdk/opends/src/messages/messages/protocol.properties b/opendj-sdk/opends/src/messages/messages/protocol.properties
index c3dc95d..28363e7 100644
--- a/opendj-sdk/opends/src/messages/messages/protocol.properties
+++ b/opendj-sdk/opends/src/messages/messages/protocol.properties
@@ -1364,6 +1364,11 @@
 SEVERE_ERR_LDIF_CONNHANDLER_CANNOT_DELETE_456=An error occurred while the \
  LDIF connection handler was attempting to delete processed file %s:  %s
 SEVERE_ERR_CONNHANDLER_ADDRESS_INUSE_457=Address already in use
+MILD_ERR_SUBENTRIES_NO_CONTROL_VALUE_458=Cannot decode the provided \
+ subentries control because it does not have a value
+MILD_ERR_SUBENTRIES_CANNOT_DECODE_VALUE_459=Cannot decode the provided \
+ subentries control because an error occurred while attempting \
+ to decode the control value:  %s
 INFO_SNMP_CONNHANDLER_DESCRIPTION_LISTEN_PORT_1458=SNMP port on \
  which this connection handler accepts SNMP requests.  Changes \
  to this configuration attribute will not take effect until the connection \
diff --git a/opendj-sdk/opends/src/messages/messages/tools.properties b/opendj-sdk/opends/src/messages/messages/tools.properties
index 5406a92..88029ec 100644
--- a/opendj-sdk/opends/src/messages/messages/tools.properties
+++ b/opendj-sdk/opends/src/messages/messages/tools.properties
@@ -2534,3 +2534,5 @@
 for index scratch files during index rebuilding
 SEVERE_ERR_REBUILDINDEX_REBUILD_ALL_ERROR_1699=Index "-i" option cannot be \
 specified with the "--rebuildAll" option
+INFO_DESCRIPTION_SUBENTRIES_1700=Use subentries control to specify that \
+ subentries are visible and normal entries are not
diff --git a/opendj-sdk/opends/src/server/org/opends/server/controls/SubentriesControl.java b/opendj-sdk/opends/src/server/org/opends/server/controls/SubentriesControl.java
new file mode 100644
index 0000000..7297330
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/controls/SubentriesControl.java
@@ -0,0 +1,164 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ *      Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ *      Copyright 2009 Sun Microsystems, Inc.
+ */
+package org.opends.server.controls;
+import org.opends.messages.Message;
+
+import java.io.IOException;
+
+import org.opends.server.protocols.asn1.*;
+import org.opends.server.loggers.debug.DebugTracer;
+import org.opends.server.types.*;
+
+import static org.opends.messages.ProtocolMessages.*;
+import static org.opends.server.protocols.asn1.ASN1Constants.
+    UNIVERSAL_OCTET_STRING_TYPE;
+import static org.opends.server.loggers.debug.DebugLogger.*;
+import static org.opends.server.util.ServerConstants.*;
+import static org.opends.server.util.StaticUtils.*;
+
+/**
+ * This class implements Subentries Control as defined in RFC 3672. It makes
+ * it possible to control the visibility of entries and subentries which are
+ * within scope of specific operation.
+ */
+public class SubentriesControl
+       extends Control
+{
+  /**
+   * ControlDecoder implentation to decode this control from a ByteString.
+   */
+  private static final class Decoder
+      implements ControlDecoder<SubentriesControl>
+  {
+    /**
+     * {@inheritDoc}
+     */
+    public SubentriesControl decode(boolean isCritical, ByteString value)
+        throws DirectoryException
+    {
+      if (value == null)
+      {
+        Message message = ERR_SUBENTRIES_NO_CONTROL_VALUE.get();
+        throw new DirectoryException(ResultCode.PROTOCOL_ERROR, message);
+      }
+
+      ASN1Reader reader = ASN1.getReader(value);
+      boolean visibility;
+      try
+      {
+        visibility = reader.readBoolean();
+      }
+      catch (Exception e)
+      {
+        if (debugEnabled())
+        {
+          TRACER.debugCaught(DebugLogLevel.ERROR, e);
+        }
+
+        Message message =
+            ERR_SUBENTRIES_CANNOT_DECODE_VALUE.get(getExceptionMessage(e));
+        throw new DirectoryException(ResultCode.PROTOCOL_ERROR, message, e);
+      }
+
+      return new SubentriesControl(isCritical, visibility);
+    }
+
+    public String getOID()
+    {
+      return OID_LDAP_SUBENTRIES;
+    }
+
+  }
+
+  /**
+   * The Control Decoder that can be used to decode this control.
+   */
+  public static final ControlDecoder<SubentriesControl> DECODER =
+    new Decoder();
+
+  /**
+   * The tracer object for the debug logger.
+   */
+  private static final DebugTracer TRACER = getTracer();
+
+  // The visibility from the control value.
+  private boolean visibility = false;
+
+  /**
+   * Creates a new instance of the Subentries Control with the provided
+   * information.
+   *
+   * @param  isCritical       Indicates whether support for this control
+   *                          should be considered a critical part of the
+   *                          server processing.
+   * @param  visibility       The visibility flag from the control value.
+   */
+  public SubentriesControl(boolean isCritical, boolean visibility)
+  {
+    super(OID_LDAP_SUBENTRIES, isCritical);
+    this.visibility = visibility;
+  }
+
+  /**
+   * Writes this control's value to an ASN.1 writer. The value must be
+   * written as an ASN1OctetString.
+   *
+   * @param writer The ASN.1 writer to use.
+   * @throws IOException If a problem occurs while writing to the stream.
+   */
+  @Override
+  protected void writeValue(ASN1Writer writer) throws IOException
+  {
+    writer.writeStartSequence(UNIVERSAL_OCTET_STRING_TYPE);
+    writer.writeBoolean(visibility);
+    writer.writeEndSequence();
+  }
+
+  /**
+   * Retrieves the visibility for this Subentries Control.
+   *
+   * @return  The visibility for this Subentries Control.
+   */
+  public boolean getVisibility()
+  {
+    return visibility;
+  }
+
+  /**
+   * Appends a string representation of this Subentries Control to the
+   * provided buffer.
+   *
+   * @param  buffer  The buffer to which the information should be appended.
+   */
+  @Override
+  public void toString(StringBuilder buffer)
+  {
+    buffer.append("SubentriesControl(visibility=\"");
+    buffer.append(Boolean.toString(visibility));
+    buffer.append("\")");
+  }
+}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/core/SearchOperationBasis.java b/opendj-sdk/opends/src/server/org/opends/server/core/SearchOperationBasis.java
index 13c5257..5a8d35f 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/core/SearchOperationBasis.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/core/SearchOperationBasis.java
@@ -609,26 +609,35 @@
 
     // Determine whether the provided entry is a subentry and if so whether it
     // should be returned.
-    if ((getScope() != SearchScope.BASE_OBJECT) &&
-        (! isReturnLDAPSubentries()) &&
-        entry.isLDAPSubentry())
+    if (entry.isLDAPSubentry())
     {
-      // Check to see if the filter contains an equality element with the
-      // objectclass attribute type and a value of "ldapSubentry".  If so, then
-      // we'll return it anyway.  Technically, this isn't part of the
-      // specification so we don't need to get carried away with really in-depth
-      // checks.
-      checkFilterForLDAPSubEntry(getFilter(), 0);
-
-      if (! isReturnLDAPSubentries())
+      if ((getScope() != SearchScope.BASE_OBJECT) &&
+              (! isReturnLDAPSubentries()))
       {
-        // We still shouldn't return it even based on the filter.  Just throw it
-        // away without doing anything.
+        // Check to see if the filter contains an equality element with the
+        // objectclass attribute type and a value of "ldapSubentry".  If so,
+        // then we'll return it anyway.  Technically, this isn't part of the
+        // specification so we don't need to get carried away with really in
+        // depth checks. Just do best effort for earlier draft compatibility.
+        checkFilterForLDAPSubEntry(getFilter(), 0);
+
+        if (! isReturnLDAPSubentries())
+        {
+          // We still shouldn't return it even based on the filter.
+          // Just throw it away without doing anything.
+          return true;
+        }
+      }
+    }
+    else
+    {
+      if (isReturnLDAPSubentries())
+      {
+        // Subentries are visible and normal entries are not.
         return true;
       }
     }
 
-
     // Determine whether to include the account usable control.  If so, then
     // create it now.
     if (isIncludeUsableControl())
diff --git a/opendj-sdk/opends/src/server/org/opends/server/tools/LDAPSearch.java b/opendj-sdk/opends/src/server/org/opends/server/tools/LDAPSearch.java
index 9a79ba3..13b131a 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/tools/LDAPSearch.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/tools/LDAPSearch.java
@@ -712,6 +712,7 @@
     StringArgument    effectiveRightsAttrs     = null;
     StringArgument    propertiesFileArgument   = null;
     BooleanArgument   noPropertiesFileArgument = null;
+    BooleanArgument   subEntriesArgument       = null;
 
 
     // Create the command-line argument parser for use with this program.
@@ -984,6 +985,12 @@
       controlStr.setPropertyName("control");
       argParser.addArgument(controlStr);
 
+      subEntriesArgument = new BooleanArgument("subEntries",
+              OPTION_SHORT_SUBENTRIES, OPTION_LONG_SUBENTRIES,
+              INFO_DESCRIPTION_SUBENTRIES.get());
+      useSSL.setPropertyName(OPTION_LONG_SUBENTRIES);
+      argParser.addArgument(subEntriesArgument);
+
       effectiveRightsUser =
               new StringArgument("effectiveRightsUser",
                       OPTION_SHORT_EFFECTIVERIGHTSUSER,
@@ -1599,6 +1606,13 @@
       }
     }
 
+    if (subEntriesArgument.isPresent())
+    {
+      Control subentriesControl =
+          new SubentriesControl(true, true);
+      searchOptions.getControls().add(subentriesControl);
+    }
+
     // Set the connection options.
     connectionOptions.setSASLExternal(saslExternal.isPresent());
     if(saslOptions.isPresent())
diff --git a/opendj-sdk/opends/src/server/org/opends/server/tools/LDAPToolUtils.java b/opendj-sdk/opends/src/server/org/opends/server/tools/LDAPToolUtils.java
index 519b4c3..5cdcf93 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/tools/LDAPToolUtils.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/tools/LDAPToolUtils.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2006-2008 Sun Microsystems, Inc.
+ *      Copyright 2006-2009 Sun Microsystems, Inc.
  */
 package org.opends.server.tools;
 import org.opends.messages.Message;
@@ -95,10 +95,6 @@
     {
       controlOID = OID_LDAP_NOOP_OPENLDAP_ASSIGNED;
     }
-    else if (lowerOID.equals("subentries"))
-    {
-      controlOID = OID_LDAP_SUBENTRIES;
-    }
     else if (lowerOID.equals("managedsait"))
     {
       controlOID = OID_MANAGE_DSAIT_CONTROL;
diff --git a/opendj-sdk/opends/src/server/org/opends/server/tools/ToolConstants.java b/opendj-sdk/opends/src/server/org/opends/server/tools/ToolConstants.java
index 684598e..10323cd 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/tools/ToolConstants.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/tools/ToolConstants.java
@@ -597,6 +597,16 @@
   public static final Character OPTION_SHORT_RECURRING_TASK = null;
 
   /**
+   * Subentries control option long form.
+   */
+  public static final String OPTION_LONG_SUBENTRIES = "subEntries";
+
+  /**
+   * Subentries control option short form.
+   */
+  public static final Character OPTION_SHORT_SUBENTRIES = null;
+
+  /**
    * The value for the long option propertiesFilePAth .
    */
   public static final String OPTION_LONG_PROP_FILE_PATH = "propertiesFilePath";
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 045ec23..b80e4c1 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
@@ -2065,7 +2065,7 @@
    * The OID for the LDAP subentries control used to indicate that matching
    * subentries should be returned.
    */
-  public static final String OID_LDAP_SUBENTRIES = "1.3.6.1.4.1.7628.5.101.1";
+  public static final String OID_LDAP_SUBENTRIES = "1.3.6.1.4.1.4203.1.10.1";
 
 
 
diff --git a/opendj-sdk/opends/src/server/org/opends/server/workflowelement/externalchangelog/ECLSearchOperation.java b/opendj-sdk/opends/src/server/org/opends/server/workflowelement/externalchangelog/ECLSearchOperation.java
index c49158c..76bc2d9 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/workflowelement/externalchangelog/ECLSearchOperation.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/workflowelement/externalchangelog/ECLSearchOperation.java
@@ -59,6 +59,7 @@
 import org.opends.server.controls.PersistentSearchControl;
 import org.opends.server.controls.ProxiedAuthV1Control;
 import org.opends.server.controls.ProxiedAuthV2Control;
+import org.opends.server.controls.SubentriesControl;
 import org.opends.server.core.AccessControlConfigManager;
 import org.opends.server.core.AddOperation;
 import org.opends.server.core.DirectoryServer;
@@ -549,7 +550,9 @@
         }
         else if (oid.equals(OID_LDAP_SUBENTRIES))
         {
-          setReturnLDAPSubentries(true);
+          SubentriesControl subentriesControl =
+                  getRequestControl(SubentriesControl.DECODER);
+          setReturnLDAPSubentries(subentriesControl.getVisibility());
         }
         else if (oid.equals(OID_MATCHED_VALUES))
         {
diff --git a/opendj-sdk/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendSearchOperation.java b/opendj-sdk/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendSearchOperation.java
index 3413328..6e6a124 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendSearchOperation.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendSearchOperation.java
@@ -38,6 +38,7 @@
 import org.opends.server.controls.PersistentSearchControl;
 import org.opends.server.controls.ProxiedAuthV1Control;
 import org.opends.server.controls.ProxiedAuthV2Control;
+import org.opends.server.controls.SubentriesControl;
 import org.opends.server.core.AccessControlConfigManager;
 import org.opends.server.core.DirectoryServer;
 import org.opends.server.core.PersistentSearch;
@@ -500,7 +501,9 @@
         }
         else if (oid.equals(OID_LDAP_SUBENTRIES))
         {
-          setReturnLDAPSubentries(true);
+          SubentriesControl subentriesControl =
+                  getRequestControl(SubentriesControl.DECODER);
+          setReturnLDAPSubentries(subentriesControl.getVisibility());
         }
         else if (oid.equals(OID_MATCHED_VALUES))
         {
diff --git a/opendj-sdk/opends/src/server/org/opends/server/workflowelement/ndb/NDBSearchOperation.java b/opendj-sdk/opends/src/server/org/opends/server/workflowelement/ndb/NDBSearchOperation.java
index fcc42d3..6983c90 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/workflowelement/ndb/NDBSearchOperation.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/workflowelement/ndb/NDBSearchOperation.java
@@ -35,6 +35,7 @@
 import org.opends.server.controls.MatchedValuesControl;
 import org.opends.server.controls.ProxiedAuthV1Control;
 import org.opends.server.controls.ProxiedAuthV2Control;
+import org.opends.server.controls.SubentriesControl;
 import org.opends.server.core.AccessControlConfigManager;
 import org.opends.server.core.DirectoryServer;
 import org.opends.server.core.PluginConfigManager;
@@ -411,7 +412,9 @@
         }
         else if (oid.equals(OID_LDAP_SUBENTRIES))
         {
-          setReturnLDAPSubentries(true);
+          SubentriesControl subentriesControl =
+                  getRequestControl(SubentriesControl.DECODER);
+          setReturnLDAPSubentries(subentriesControl.getVisibility());
         }
         else if (oid.equals(OID_MATCHED_VALUES))
         {
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPSearchTestCase.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPSearchTestCase.java
index 6de452a..36754b7 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPSearchTestCase.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPSearchTestCase.java
@@ -1654,11 +1654,11 @@
       "-s", "sub",
       "--countEntries",
       "--noPropertiesFile",
-      "-J", OID_LDAP_SUBENTRIES + ":true",
+      "--subEntries",
       "(objectClass=*)"
     };
 
-    assertEquals(LDAPSearch.mainSearch(args, false, true, null, System.err), 2);
+    assertEquals(LDAPSearch.mainSearch(args, false, true, null, System.err), 1);
 
     args = new String[]
     {
@@ -1666,15 +1666,14 @@
       "-w", "password",
       "-h", "127.0.0.1",
       "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
-      "-b", "o=test",
-      "-s", "sub",
+      "-b", "cn=test,o=test",
+      "-s", "base",
       "--countEntries",
       "--noPropertiesFile",
-      "-J", "subentries:true",
       "(objectClass=*)"
     };
 
-    assertEquals(LDAPSearch.mainSearch(args, false, true, null, System.err), 2);
+    assertEquals(LDAPSearch.mainSearch(args, false, true, null, System.err), 1);
   }
 
 

--
Gitblit v1.10.0