From 847807d0d9df48cdcaeacadc2110ae209e500e75 Mon Sep 17 00:00:00 2001
From: Matthew Swift <matthew.swift@forgerock.com>
Date: Fri, 12 Apr 2013 14:45:05 +0000
Subject: [PATCH] Fix issue OPENDJ-861: Etag attribute invalid when used in conjunction with pre- and post- read controls

---
 opendj-sdk/opends/src/server/org/opends/server/loggers/TextAccessLogPublisher.java                                               |    4 
 opendj-sdk/opends/src/server/org/opends/server/types/operation/SearchReferenceSearchOperation.java                               |    5 
 opendj-sdk/opends/src/server/org/opends/server/types/operation/SearchEntrySearchOperation.java                                   |    5 
 opendj-sdk/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElement.java                     |  154 +++-------
 opendj-sdk/opends/src/server/org/opends/server/core/SearchOperationBasis.java                                                    |    9 
 opendj-sdk/opends/src/server/org/opends/server/types/operation/PostOperationSearchOperation.java                                 |    5 
 opendj-sdk/opends/src/server/org/opends/server/types/operation/PostResponseSearchOperation.java                                  |    5 
 opendj-sdk/opends/src/server/org/opends/server/controls/LDAPPreReadRequestControl.java                                           |  239 +++------------
 opendj-sdk/opends/src/server/org/opends/server/core/SearchOperationWrapper.java                                                  |   11 
 opendj-sdk/opends/src/server/org/opends/server/plugins/LDAPADListPlugin.java                                                     |  140 +++++----
 opendj-sdk/opends/src/server/org/opends/server/types/operation/PreOperationSearchOperation.java                                  |    5 
 opendj-sdk/opends/src/server/org/opends/server/types/operation/PreParseSearchOperation.java                                      |    7 
 opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciEffectiveRights.java                                  |    6 
 opendj-sdk/opends/src/server/org/opends/server/controls/LDAPPostReadRequestControl.java                                          |  263 ++++------------
 opendj-sdk/opends/src/server/org/opends/server/core/SearchOperation.java                                                         |    8 
 opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/EntityTagVirtualAttributeProviderTestCase.java |    6 
 16 files changed, 294 insertions(+), 578 deletions(-)

diff --git a/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciEffectiveRights.java b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciEffectiveRights.java
index a09880c..616641b 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciEffectiveRights.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciEffectiveRights.java
@@ -23,7 +23,7 @@
  *
  *
  *      Copyright 2008 Sun Microsystems, Inc.
- *      Portions Copyright 2011 ForgeRock AS
+ *      Portions Copyright 2011-2013 ForgeRock AS
  */
 
 package org.opends.server.authorization.dseecompat;
@@ -32,9 +32,9 @@
 import org.opends.server.core.DirectoryServer;
 import org.opends.server.types.*;
 
-import java.util.LinkedHashSet;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Set;
 
 /**
  * This class implements the dseecompat geteffectiverights evaluation.
@@ -189,7 +189,7 @@
    *                   privilege was found.
    */
   public static void addRightsToEntry(AciHandler handler,
-      LinkedHashSet<String> searchAttributes,
+      Set<String> searchAttributes,
       AciLDAPOperationContainer container, final Entry e,
       boolean skipCheck)
   {
diff --git a/opendj-sdk/opends/src/server/org/opends/server/controls/LDAPPostReadRequestControl.java b/opendj-sdk/opends/src/server/org/opends/server/controls/LDAPPostReadRequestControl.java
index 508f661..aacca50 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/controls/LDAPPostReadRequestControl.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/controls/LDAPPostReadRequestControl.java
@@ -23,20 +23,23 @@
  *
  *
  *      Copyright 2006-2008 Sun Microsystems, Inc.
+ *      Portions copyright 2013 ForgeRock AS.
  */
 package org.opends.server.controls;
-import org.opends.messages.Message;
 
 
+
+import org.opends.messages.Message;
+
 import java.util.Iterator;
 import java.util.LinkedHashSet;
 import java.util.Set;
 import java.io.IOException;
 
-import org.opends.server.core.DirectoryServer;
 import org.opends.server.protocols.asn1.*;
-import static org.opends.server.protocols.asn1.ASN1Constants.
-    UNIVERSAL_OCTET_STRING_TYPE;
+
+import static org.opends.server.plugins.LDAPADListPlugin.*;
+import static org.opends.server.protocols.asn1.ASN1Constants.*;
 import static org.opends.server.loggers.debug.DebugLogger.*;
 import org.opends.server.loggers.debug.DebugTracer;
 import org.opends.server.types.*;
@@ -49,25 +52,23 @@
 /**
  * This class implements the post-read request control as defined in RFC 4527.
  * This control makes it possible to retrieve an entry in the state that it held
- * immediately after an add, modify, or modify DN operation.  It may specify a
- * specific set of attributes that should be included in that entry.  The entry
+ * immediately after an add, modify, or modify DN operation. It may specify a
+ * specific set of attributes that should be included in that entry. The entry
  * will be encoded in a corresponding response control.
  */
-public class LDAPPostReadRequestControl
-       extends Control
+public class LDAPPostReadRequestControl extends Control
 {
   /**
    * ControlDecoder implentation to decode this control from a ByteString.
    */
-  private final static class Decoder
-      implements ControlDecoder<LDAPPostReadRequestControl>
+  private final static class Decoder implements
+      ControlDecoder<LDAPPostReadRequestControl>
   {
     /**
      * {@inheritDoc}
      */
     public LDAPPostReadRequestControl decode(boolean isCritical,
-                                             ByteString value)
-        throws DirectoryException
+        ByteString value) throws DirectoryException
     {
       if (value == null)
       {
@@ -75,13 +76,12 @@
         throw new DirectoryException(ResultCode.PROTOCOL_ERROR, message);
       }
 
-
       ASN1Reader reader = ASN1.getReader(value);
       LinkedHashSet<String> rawAttributes = new LinkedHashSet<String>();
       try
       {
         reader.readStartSequence();
-        while(reader.hasNextElement())
+        while (reader.hasNextElement())
         {
           rawAttributes.add(reader.readOctetStringAsString());
         }
@@ -94,17 +94,16 @@
           TRACER.debugCaught(DebugLogLevel.ERROR, ae);
         }
 
-        Message message =
-            ERR_POSTREADREQ_CANNOT_DECODE_VALUE.get(ae.getMessage());
-        throw new DirectoryException(ResultCode.PROTOCOL_ERROR, message,
-            ae);
+        Message message = ERR_POSTREADREQ_CANNOT_DECODE_VALUE.get(ae
+            .getMessage());
+        throw new DirectoryException(ResultCode.PROTOCOL_ERROR, message, ae);
       }
 
-
-      return new LDAPPostReadRequestControl(isCritical,
-          rawAttributes);
+      return new LDAPPostReadRequestControl(isCritical, rawAttributes);
     }
 
+
+
     public String getOID()
     {
       return OID_LDAP_READENTRY_POSTREAD;
@@ -112,33 +111,24 @@
 
   }
 
+
+
   /**
    * The Control Decoder that can be used to decode this control.
    */
   public static final ControlDecoder<LDAPPostReadRequestControl> DECODER =
-    new Decoder();
+      new Decoder();
 
   /**
    * The tracer object for the debug logger.
    */
   private static final DebugTracer TRACER = getTracer();
 
-
-
-
-  // Indicates whether the request indicates that all operational attributes
-  // should be returned.
-  private boolean returnAllOperationalAttrs;
-
-  // Indicates whether the request indicates that all user attributes should be
-  // returned.
-  private boolean returnAllUserAttrs;
-
   // The set of raw attributes to return in the entry.
   private Set<String> rawAttributes;
 
   // The set of processed attributes to return in the entry.
-  private Set<AttributeType> requestedAttributes;
+  private Set<String> requestedAttributes;
 
 
 
@@ -146,18 +136,17 @@
    * Creates a new instance of this LDAP post-read request control with the
    * provided information.
    *
-   * @param  isCritical     Indicates whether support for this control should be
-   *                        considered a critical part of the server processing.
-   * @param  rawAttributes  The set of raw attributes to return in the entry.
-   *                        A null or empty set will indicates that all user
-   *                        attributes should be returned.
+   * @param isCritical
+   *          Indicates whether support for this control should be considered a
+   *          critical part of the server processing.
+   * @param rawAttributes
+   *          The set of raw attributes to return in the entry. A null or empty
+   *          set will indicates that all user attributes should be returned.
    */
   public LDAPPostReadRequestControl(boolean isCritical,
-                                    Set<String> rawAttributes)
+      Set<String> rawAttributes)
   {
     super(OID_LDAP_READENTRY_POSTREAD, isCritical);
-
-
     if (rawAttributes == null)
     {
       this.rawAttributes = new LinkedHashSet<String>(0);
@@ -166,10 +155,7 @@
     {
       this.rawAttributes = rawAttributes;
     }
-
-    requestedAttributes       = null;
-    returnAllOperationalAttrs = false;
-    returnAllUserAttrs        = false;
+    requestedAttributes = null;
   }
 
 
@@ -178,19 +164,19 @@
    * Creates a new instance of this LDAP post-read request control with the
    * provided information.
    *
-   * @param  oid            The OID to use for this control.
-   * @param  isCritical     Indicates whether support for this control should be
-   *                        considered a critical part of the server processing.
-   * @param  rawAttributes  The set of raw attributes to return in the entry.
-   *                        A null or empty set will indicates that all user
-   *                        attributes should be returned.
+   * @param oid
+   *          The OID to use for this control.
+   * @param isCritical
+   *          Indicates whether support for this control should be considered a
+   *          critical part of the server processing.
+   * @param rawAttributes
+   *          The set of raw attributes to return in the entry. A null or empty
+   *          set will indicates that all user attributes should be returned.
    */
   public LDAPPostReadRequestControl(String oid, boolean isCritical,
-                                    Set<String> rawAttributes)
+      Set<String> rawAttributes)
   {
     super(oid, isCritical);
-
-
     if (rawAttributes == null)
     {
       this.rawAttributes = new LinkedHashSet<String>(0);
@@ -199,43 +185,46 @@
     {
       this.rawAttributes = rawAttributes;
     }
-
-    requestedAttributes       = null;
-    returnAllOperationalAttrs = false;
-    returnAllUserAttrs        = false;
+    requestedAttributes = null;
   }
 
+
+
   /**
    * Writes this control's value to an ASN.1 writer. The value (if any) must be
    * written as an ASN1OctetString.
    *
-   * @param writer The ASN.1 output stream to write to.
-   * @throws IOException If a problem occurs while writing to the stream.
+   * @param writer
+   *          The ASN.1 output stream to write to.
+   * @throws IOException
+   *           If a problem occurs while writing to the stream.
    */
   @Override
-  public void writeValue(ASN1Writer writer) throws IOException {
+  public void writeValue(ASN1Writer writer) throws IOException
+  {
     writer.writeStartSequence(UNIVERSAL_OCTET_STRING_TYPE);
-
-    writer.writeStartSequence();
-    if (rawAttributes != null)
     {
-      for (String attr : rawAttributes)
+      writer.writeStartSequence();
+      if (rawAttributes != null)
       {
-        writer.writeOctetString(attr);
+        for (String attr : rawAttributes)
+        {
+          writer.writeOctetString(attr);
+        }
       }
+      writer.writeEndSequence();
     }
     writer.writeEndSequence();
-
-    writer.writeEndSequence();
   }
 
 
+
   /**
-   * Retrieves the raw, unprocessed set of requested attributes.  It must not
-   * be altered by the caller without calling <CODE>setRawAttributes</CODE> with
+   * Retrieves the raw, unprocessed set of requested attributes. It must not be
+   * altered by the caller without calling <CODE>setRawAttributes</CODE> with
    * the updated set.
    *
-   * @return  The raw, unprocessed set of attributes.
+   * @return The raw, unprocessed set of attributes.
    */
   public Set<String> getRawAttributes()
   {
@@ -243,144 +232,31 @@
   }
 
 
+
   /**
    * Retrieves the set of processed attributes that have been requested for
    * inclusion in the entry that is returned.
    *
-   * @return  The set of processed attributes that have been requested for
-   *          inclusion in the entry that is returned.
+   * @return The set of processed attributes that have been requested for
+   *         inclusion in the entry that is returned.
    */
-  public Set<AttributeType> getRequestedAttributes()
+  public Set<String> getRequestedAttributes()
   {
     if (requestedAttributes == null)
     {
-      returnAllOperationalAttrs = false;
-      returnAllUserAttrs        = (rawAttributes.size() == 0);
-
-      requestedAttributes =
-           new LinkedHashSet<AttributeType>(rawAttributes.size());
-      for (String attr : rawAttributes)
-      {
-        attr = attr.toLowerCase();
-
-        if (attr.equals("*"))
-        {
-          returnAllUserAttrs = true;
-        }
-        else if (attr.equals("+"))
-        {
-          returnAllOperationalAttrs = true;
-        }
-        else if (attr.startsWith("@"))
-        {
-          String ocName = attr.substring(1);
-          ObjectClass oc = DirectoryServer.getObjectClass(ocName);
-          if (oc != null)
-          {
-            requestedAttributes.addAll(oc.getOptionalAttributeChain());
-            requestedAttributes.addAll(oc.getRequiredAttributeChain());
-          }
-        }
-        else
-        {
-          AttributeType at = DirectoryServer.getAttributeType(attr);
-          if (at == null)
-          {
-            at = DirectoryServer.getDefaultAttributeType(attr);
-          }
-
-          requestedAttributes.add(at);
-        }
-      }
+      requestedAttributes = normalizedObjectClasses(rawAttributes);
     }
-
     return requestedAttributes;
   }
 
 
 
   /**
-   * Indicates whether the entry returned should include all user attributes
-   * that the requester has permission to see.
-   *
-   * @return  <CODE>true</CODE> if the entry returned should include all user
-   *          attributes that the requester has permission to see, or
-   *          <CODE>false</CODE> if it should only include user attributes that
-   *          have been explicitly included in the requested attribute list.
-   */
-  public boolean returnAllUserAttributes()
-  {
-    if (requestedAttributes == null)
-    {
-      getRequestedAttributes();
-    }
-
-    return returnAllUserAttrs;
-  }
-
-
-
-  /**
-   * Indicates whether the entry returned should include all operational
-   * attributes that the requester has permission to see.
-   *
-   * @return  <CODE>true</CODE> if the entry returned should include all
-   *          operational attributes that the requester has permission to see,
-   *          or <CODE>false</CODE> if it should only include user attributes
-   *          that have been explicitly included in the requested attribute
-   *          list.
-   */
-  public boolean returnAllOperationalAttributes()
-  {
-    if (requestedAttributes == null)
-    {
-      getRequestedAttributes();
-    }
-
-    return returnAllOperationalAttrs;
-  }
-
-
-
-  /**
-   * Indicates whether the specified attribute type should be included in the
-   * entry for the corresponding response control.
-   *
-   * @param  attrType  The attribute type for which to make the determination.
-   *
-   * @return  <CODE>true</CODE> if the specified attribute type should be
-   *          included in the entry for the corresponding response control, or
-   *          <CODE>false</CODE> if not.
-   */
-  public boolean allowsAttribute(AttributeType attrType)
-  {
-    if (requestedAttributes == null)
-    {
-      getRequestedAttributes();
-    }
-
-    if (requestedAttributes.contains(attrType))
-    {
-      return true;
-    }
-
-    if (attrType.isOperational())
-    {
-      return returnAllOperationalAttrs;
-    }
-    else
-    {
-      return returnAllUserAttrs;
-    }
-  }
-
-
-
-  /**
    * Appends a string representation of this LDAP post-read request control to
    * the provided buffer.
    *
-   * @param  buffer  The buffer to which the information should be appended.
+   * @param buffer
+   *          The buffer to which the information should be appended.
    */
   @Override
   public void toString(StringBuilder buffer)
@@ -389,7 +265,7 @@
     buffer.append(isCritical());
     buffer.append(",attrs=\"");
 
-    if (! rawAttributes.isEmpty())
+    if (!rawAttributes.isEmpty())
     {
       Iterator<String> iterator = rawAttributes.iterator();
       buffer.append(iterator.next());
@@ -404,4 +280,3 @@
     buffer.append("\")");
   }
 }
-
diff --git a/opendj-sdk/opends/src/server/org/opends/server/controls/LDAPPreReadRequestControl.java b/opendj-sdk/opends/src/server/org/opends/server/controls/LDAPPreReadRequestControl.java
index a5827a7..a4ffa67 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/controls/LDAPPreReadRequestControl.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/controls/LDAPPreReadRequestControl.java
@@ -23,20 +23,23 @@
  *
  *
  *      Copyright 2006-2008 Sun Microsystems, Inc.
+ *      Portions copyright 2013 ForgeRock AS.
  */
 package org.opends.server.controls;
-import org.opends.messages.Message;
 
 
+
+import org.opends.messages.Message;
+
 import java.util.Iterator;
 import java.util.LinkedHashSet;
 import java.util.Set;
 import java.io.IOException;
 
-import org.opends.server.core.DirectoryServer;
 import org.opends.server.protocols.asn1.*;
-import static org.opends.server.protocols.asn1.ASN1Constants.
-    UNIVERSAL_OCTET_STRING_TYPE;
+
+import static org.opends.server.plugins.LDAPADListPlugin.*;
+import static org.opends.server.protocols.asn1.ASN1Constants.*;
 import org.opends.server.types.*;
 
 import static org.opends.server.loggers.debug.DebugLogger.*;
@@ -49,27 +52,25 @@
 /**
  * This class implements the pre-read request control as defined in RFC 4527.
  * This control makes it possible to retrieve an entry in the state that it held
- * immediately before a modify, delete, or modify DN operation.  It may specify
- * a specific set of attributes that should be included in that entry.  The
- * entry will be encoded in a corresponding response control.
+ * immediately before a modify, delete, or modify DN operation. It may specify a
+ * specific set of attributes that should be included in that entry. The entry
+ * will be encoded in a corresponding response control.
  */
-public class LDAPPreReadRequestControl
-       extends Control
+public class LDAPPreReadRequestControl extends Control
 {
   /**
    * ControlDecoder implentation to decode this control from a ByteString.
    */
-  private final static class Decoder
-      implements ControlDecoder<LDAPPreReadRequestControl>
+  private final static class Decoder implements
+      ControlDecoder<LDAPPreReadRequestControl>
   {
     /**
      * {@inheritDoc}
      */
     public LDAPPreReadRequestControl decode(boolean isCritical,
-                                            ByteString value)
-        throws DirectoryException
+        ByteString value) throws DirectoryException
     {
-     if (value == null)
+      if (value == null)
       {
         Message message = ERR_PREREADREQ_NO_CONTROL_VALUE.get();
         throw new DirectoryException(ResultCode.PROTOCOL_ERROR, message);
@@ -80,7 +81,7 @@
       try
       {
         reader.readStartSequence();
-        while(reader.hasNextElement())
+        while (reader.hasNextElement())
         {
           rawAttributes.add(reader.readOctetStringAsString());
         }
@@ -93,17 +94,16 @@
           TRACER.debugCaught(DebugLogLevel.ERROR, ae);
         }
 
-        Message message =
-            ERR_PREREADREQ_CANNOT_DECODE_VALUE.get(ae.getMessage());
-        throw new DirectoryException(ResultCode.PROTOCOL_ERROR, message,
-            ae);
+        Message message = ERR_PREREADREQ_CANNOT_DECODE_VALUE.get(ae
+            .getMessage());
+        throw new DirectoryException(ResultCode.PROTOCOL_ERROR, message, ae);
       }
 
-
-      return new LDAPPreReadRequestControl(isCritical,
-          rawAttributes);
+      return new LDAPPreReadRequestControl(isCritical, rawAttributes);
     }
 
+
+
     public String getOID()
     {
       return OID_LDAP_READENTRY_PREREAD;
@@ -111,33 +111,24 @@
 
   }
 
+
+
   /**
    * The Control Decoder that can be used to decode this control.
    */
   public static final ControlDecoder<LDAPPreReadRequestControl> DECODER =
-    new Decoder();
+      new Decoder();
 
   /**
    * The tracer object for the debug logger.
    */
   private static final DebugTracer TRACER = getTracer();
 
-
-
-
-  // Indicates whether the request indicates that all operational attributes
-  // should be returned.
-  private boolean returnAllOperationalAttrs;
-
-  // Indicates whether the request indicates that all user attributes should be
-  // returned.
-  private boolean returnAllUserAttrs;
-
   // The set of raw attributes to return in the entry.
   private Set<String> rawAttributes;
 
   // The set of processed attributes to return in the entry.
-  private Set<AttributeType> requestedAttributes;
+  private Set<String> requestedAttributes;
 
 
 
@@ -145,18 +136,17 @@
    * Creates a new instance of this LDAP pre-read request control with the
    * provided information.
    *
-   * @param  isCritical     Indicates whether support for this control should be
-   *                        considered a critical part of the server processing.
-   * @param  rawAttributes  The set of raw attributes to return in the entry.
-   *                        A null or empty set will indicates that all user
-   *                        attributes should be returned.
+   * @param isCritical
+   *          Indicates whether support for this control should be considered a
+   *          critical part of the server processing.
+   * @param rawAttributes
+   *          The set of raw attributes to return in the entry. A null or empty
+   *          set will indicates that all user attributes should be returned.
    */
   public LDAPPreReadRequestControl(boolean isCritical,
-                                   Set<String> rawAttributes)
+      Set<String> rawAttributes)
   {
     super(OID_LDAP_READENTRY_PREREAD, isCritical);
-
-
     if (rawAttributes == null)
     {
       this.rawAttributes = new LinkedHashSet<String>(0);
@@ -165,10 +155,7 @@
     {
       this.rawAttributes = rawAttributes;
     }
-
-    requestedAttributes       = null;
-    returnAllOperationalAttrs = false;
-    returnAllUserAttrs        = false;
+    requestedAttributes = null;
   }
 
 
@@ -177,34 +164,37 @@
    * Writes this control's value to an ASN.1 writer. The value (if any) must be
    * written as an ASN1OctetString.
    *
-   * @param writer The ASN.1 output stream to write to.
-   * @throws IOException If a problem occurs while writing to the stream.
+   * @param writer
+   *          The ASN.1 output stream to write to.
+   * @throws IOException
+   *           If a problem occurs while writing to the stream.
    */
   @Override
-  public void writeValue(ASN1Writer writer) throws IOException {
+  public void writeValue(ASN1Writer writer) throws IOException
+  {
     writer.writeStartSequence(UNIVERSAL_OCTET_STRING_TYPE);
-
-    writer.writeStartSequence();
-    if (rawAttributes != null)
     {
-      for (String attr : rawAttributes)
+      writer.writeStartSequence();
+      if (rawAttributes != null)
       {
-        writer.writeOctetString(attr);
+        for (String attr : rawAttributes)
+        {
+          writer.writeOctetString(attr);
+        }
       }
+      writer.writeEndSequence();
     }
     writer.writeEndSequence();
-
-    writer.writeEndSequence();
   }
 
 
 
   /**
-   * Retrieves the raw, unprocessed set of requested attributes.  It must not
-   * be altered by the caller without calling <CODE>setRawAttributes</CODE> with
+   * Retrieves the raw, unprocessed set of requested attributes. It must not be
+   * altered by the caller without calling <CODE>setRawAttributes</CODE> with
    * the updated set.
    *
-   * @return  The raw, unprocessed set of attributes.
+   * @return The raw, unprocessed set of attributes.
    */
   public Set<String> getRawAttributes()
   {
@@ -217,140 +207,26 @@
    * Retrieves the set of processed attributes that have been requested for
    * inclusion in the entry that is returned.
    *
-   * @return  The set of processed attributes that have been requested for
-   *          inclusion in the entry that is returned.
+   * @return The set of processed attributes that have been requested for
+   *         inclusion in the entry that is returned.
    */
-  public Set<AttributeType> getRequestedAttributes()
+  public Set<String> getRequestedAttributes()
   {
     if (requestedAttributes == null)
     {
-      returnAllOperationalAttrs = false;
-      returnAllUserAttrs        = (rawAttributes.size() == 0);
-
-      requestedAttributes =
-           new LinkedHashSet<AttributeType>(rawAttributes.size());
-      for (String attr : rawAttributes)
-      {
-        attr = attr.toLowerCase();
-
-        if (attr.equals("*"))
-        {
-          returnAllUserAttrs = true;
-        }
-        else if (attr.equals("+"))
-        {
-          returnAllOperationalAttrs = true;
-        }
-        else if (attr.startsWith("@"))
-        {
-          String ocName = attr.substring(1);
-          ObjectClass oc = DirectoryServer.getObjectClass(ocName);
-          if (oc != null)
-          {
-            requestedAttributes.addAll(oc.getOptionalAttributeChain());
-            requestedAttributes.addAll(oc.getRequiredAttributeChain());
-          }
-        }
-        else
-        {
-          AttributeType at = DirectoryServer.getAttributeType(attr);
-          if (at == null)
-          {
-            at = DirectoryServer.getDefaultAttributeType(attr);
-          }
-
-          requestedAttributes.add(at);
-        }
-      }
+      requestedAttributes = normalizedObjectClasses(rawAttributes);
     }
-
     return requestedAttributes;
   }
 
 
 
   /**
-   * Indicates whether the entry returned should include all user attributes
-   * that the requester has permission to see.
-   *
-   * @return  <CODE>true</CODE> if the entry returned should include all user
-   *          attributes that the requester has permission to see, or
-   *          <CODE>false</CODE> if it should only include user attributes that
-   *          have been explicitly included in the requested attribute list.
-   */
-  public boolean returnAllUserAttributes()
-  {
-    if (requestedAttributes == null)
-    {
-      getRequestedAttributes();
-    }
-
-    return returnAllUserAttrs;
-  }
-
-
-
-  /**
-   * Indicates whether the entry returned should include all operational
-   * attributes that the requester has permission to see.
-   *
-   * @return  <CODE>true</CODE> if the entry returned should include all
-   *          operational attributes that the requester has permission to see,
-   *          or <CODE>false</CODE> if it should only include user attributes
-   *          that have been explicitly included in the requested attribute
-   *          list.
-   */
-  public boolean returnAllOperationalAttributes()
-  {
-    if (requestedAttributes == null)
-    {
-      getRequestedAttributes();
-    }
-
-    return returnAllOperationalAttrs;
-  }
-
-
-
-  /**
-   * Indicates whether the specified attribute type should be included in the
-   * entry for the corresponding response control.
-   *
-   * @param  attrType  The attribute type for which to make the determination.
-   *
-   * @return  <CODE>true</CODE> if the specified attribute type should be
-   *          included in the entry for the corresponding response control, or
-   *          <CODE>false</CODE> if not.
-   */
-  public boolean allowsAttribute(AttributeType attrType)
-  {
-    if (requestedAttributes == null)
-    {
-      getRequestedAttributes();
-    }
-
-    if (requestedAttributes.contains(attrType))
-    {
-      return true;
-    }
-
-    if (attrType.isOperational())
-    {
-      return returnAllOperationalAttrs;
-    }
-    else
-    {
-      return returnAllUserAttrs;
-    }
-  }
-
-
-
-  /**
    * Appends a string representation of this LDAP pre-read request control to
    * the provided buffer.
    *
-   * @param  buffer  The buffer to which the information should be appended.
+   * @param buffer
+   *          The buffer to which the information should be appended.
    */
   @Override
   public void toString(StringBuilder buffer)
@@ -359,7 +235,7 @@
     buffer.append(isCritical());
     buffer.append(",attrs=\"");
 
-    if (! rawAttributes.isEmpty())
+    if (!rawAttributes.isEmpty())
     {
       Iterator<String> iterator = rawAttributes.iterator();
       buffer.append(iterator.next());
@@ -374,4 +250,3 @@
     buffer.append("\")");
   }
 }
-
diff --git a/opendj-sdk/opends/src/server/org/opends/server/core/SearchOperation.java b/opendj-sdk/opends/src/server/org/opends/server/core/SearchOperation.java
index 5a1ec3a..fd86b41 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/core/SearchOperation.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/core/SearchOperation.java
@@ -23,12 +23,12 @@
  *
  *
  *      Copyright 2006-2009 Sun Microsystems, Inc.
- *      Portions Copyright 2011 ForgeRock AS
+ *      Portions Copyright 2011-2013 ForgeRock AS
  */
 package org.opends.server.core;
 
-import java.util.LinkedHashSet;
 import java.util.List;
+import java.util.Set;
 
 import org.opends.server.controls.MatchedValuesControl;
 import org.opends.server.types.*;
@@ -196,7 +196,7 @@
    *
    * @return  The set of requested attributes for this search operation.
    */
-  public abstract LinkedHashSet<String> getAttributes();
+  public abstract Set<String> getAttributes();
 
   /**
    * Specifies the set of requested attributes for this search operation.  It
@@ -205,7 +205,7 @@
    * @param  attributes  The set of requested attributes for this search
    *                     operation.
    */
-  public abstract void setAttributes(LinkedHashSet<String> attributes);
+  public abstract void setAttributes(Set<String> attributes);
 
   /**
    * Retrieves the number of entries sent to the client for this search
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 ef5b606..e5646eb 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
@@ -23,7 +23,7 @@
  *
  *
  *      Copyright 2006-2010 Sun Microsystems, Inc.
- *      Portions Copyright 2011 ForgeRock AS
+ *      Portions Copyright 2011-2013 ForgeRock AS
  */
 package org.opends.server.core;
 
@@ -35,6 +35,7 @@
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 import org.opends.server.api.AuthenticationPolicyState;
@@ -133,7 +134,7 @@
   private RawFilter rawFilter;
 
   // The set of attributes that should be returned in matching entries.
-  private LinkedHashSet<String> attributes;
+  private Set<String> attributes;
 
   // The set of response controls for this search operation.
   private List<Control> responseControls;
@@ -544,7 +545,7 @@
   /**
    * {@inheritDoc}
    */
-  public final LinkedHashSet<String> getAttributes()
+  public final Set<String> getAttributes()
   {
     return attributes;
   }
@@ -552,7 +553,7 @@
   /**
    * {@inheritDoc}
    */
-  public final void setAttributes(LinkedHashSet<String> attributes)
+  public final void setAttributes(Set<String> attributes)
   {
     if (attributes == null)
     {
diff --git a/opendj-sdk/opends/src/server/org/opends/server/core/SearchOperationWrapper.java b/opendj-sdk/opends/src/server/org/opends/server/core/SearchOperationWrapper.java
index de8b75b..76bdd8c 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/core/SearchOperationWrapper.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/core/SearchOperationWrapper.java
@@ -23,13 +23,14 @@
  *
  *
  *      Copyright 2008-2009 Sun Microsystems, Inc.
- *      Portions Copyright 2011 ForgeRock AS
+ *      Portions Copyright 2011-2013 ForgeRock AS
  */
 package org.opends.server.core;
 
 
-import java.util.LinkedHashSet;
 import java.util.List;
+import java.util.Set;
+
 import org.opends.server.controls.MatchedValuesControl;
 import org.opends.server.types.*;
 
@@ -117,7 +118,7 @@
   /**
    * {@inheritDoc}
    */
-  public LinkedHashSet<String> getAttributes()
+  public Set<String> getAttributes()
   {
     return search.getAttributes();
   }
@@ -221,7 +222,7 @@
   /**
    * {@inheritDoc}
    */
-  public void setAttributes(LinkedHashSet<String> attributes)
+  public void setAttributes(Set<String> attributes)
   {
     search.setAttributes(attributes);
   }
@@ -432,7 +433,6 @@
 
   /**
    * {@inheritDoc}
-   * @throws DirectoryException
    */
   public void sendSearchEntry(SearchResultEntry entry)
     throws DirectoryException
@@ -442,7 +442,6 @@
 
   /**
    * {@inheritDoc}
-   * @throws DirectoryException
    */
   public boolean sendSearchReference(SearchResultReference reference)
     throws DirectoryException
diff --git a/opendj-sdk/opends/src/server/org/opends/server/loggers/TextAccessLogPublisher.java b/opendj-sdk/opends/src/server/org/opends/server/loggers/TextAccessLogPublisher.java
index 3da5ddf..c6f1e0d 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/loggers/TextAccessLogPublisher.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/loggers/TextAccessLogPublisher.java
@@ -37,8 +37,8 @@
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Iterator;
-import java.util.LinkedHashSet;
 import java.util.List;
+import java.util.Set;
 
 import org.opends.messages.Message;
 import org.opends.messages.MessageBuilder;
@@ -1400,7 +1400,7 @@
     buffer.append(" filter=\"");
     searchOperation.getRawFilter().toString(buffer);
 
-    final LinkedHashSet<String> attrs = searchOperation.getAttributes();
+    final Set<String> attrs = searchOperation.getAttributes();
     if ((attrs == null) || attrs.isEmpty())
     {
       buffer.append("\" attrs=\"ALL\"");
diff --git a/opendj-sdk/opends/src/server/org/opends/server/plugins/LDAPADListPlugin.java b/opendj-sdk/opends/src/server/org/opends/server/plugins/LDAPADListPlugin.java
index 4ce5a41..1058d2e 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/plugins/LDAPADListPlugin.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/plugins/LDAPADListPlugin.java
@@ -23,6 +23,7 @@
  *
  *
  *      Copyright 2006-2008 Sun Microsystems, Inc.
+ *      Portions copyright 2013 ForgeRock AS.
  */
 package org.opends.server.plugins;
 
@@ -51,6 +52,7 @@
 import org.opends.server.loggers.debug.DebugTracer;
 import static org.opends.messages.PluginMessages.*;
 
+import static org.opends.server.types.DirectoryConfig.getObjectClass;
 import static org.opends.server.util.ServerConstants.*;
 import static org.opends.server.util.StaticUtils.*;
 
@@ -74,6 +76,79 @@
 
 
 
+  /**
+   * Filters the set of attributes provided in a search request or pre- / post-
+   * read controls according to RFC 4529. More specifically, this method
+   * iterates through the requested attributes to see if any of them reference
+   * an object class, as indicated by a "@" prefix, and substitutes the object
+   * class reference with the attribute types contained in the object class, as
+   * well as any of the attribute types contained in any superior object
+   * classes.
+   *
+   * @param attributes
+   *          The attribute list to be normalized.
+   * @return The normalized attribute list.
+   */
+  public static Set<String> normalizedObjectClasses(Set<String> attributes)
+  {
+    boolean foundOC = false;
+    for (String attrName : attributes)
+    {
+      if (attrName.startsWith("@"))
+      {
+        foundOC = true;
+        break;
+      }
+    }
+
+    if (foundOC)
+    {
+      final LinkedHashSet<String> newAttrs = new LinkedHashSet<String>();
+      for (final String attrName : attributes)
+      {
+        if (attrName.startsWith("@"))
+        {
+          final String lowerName = toLowerCase(attrName.substring(1));
+          final ObjectClass oc = getObjectClass(lowerName, false);
+          if (oc == null)
+          {
+            if (debugEnabled())
+            {
+              TRACER.debugWarning("Cannot replace unknown objectclass %s",
+                                  lowerName);
+            }
+          }
+          else
+          {
+            if (debugEnabled())
+            {
+              TRACER.debugInfo("Replacing objectclass %s", lowerName);
+            }
+
+            for (final AttributeType at : oc.getRequiredAttributeChain())
+            {
+              newAttrs.add(at.getNameOrOID());
+            }
+
+            for (final AttributeType at : oc.getOptionalAttributeChain())
+            {
+              newAttrs.add(at.getNameOrOID());
+            }
+          }
+        }
+        else
+        {
+          newAttrs.add(attrName);
+        }
+      }
+      attributes = newAttrs;
+    }
+
+    return attributes;
+  }
+
+
+
   // The current configuration for this plugin.
   private LDAPAttributeDescriptionListPluginCfg currentConfig;
 
@@ -145,68 +220,11 @@
    * {@inheritDoc}
    */
   @Override()
-  public final PluginResult.PreParse
-               doPreParse(PreParseSearchOperation searchOperation)
+  public final PluginResult.PreParse doPreParse(
+      PreParseSearchOperation searchOperation)
   {
-    // Iterate through the requested attributes to see if any of them start with
-    // an "@" symbol.  If not, then we don't need to do anything.  If so, then
-    // keep track of them.
-    LinkedHashSet<String> attributes = searchOperation.getAttributes();
-    boolean foundOC = false;
-    for (String attrName : attributes)
-    {
-      if (attrName.startsWith("@"))
-      {
-        foundOC = true;
-        break;
-      }
-    }
-
-    if (foundOC)
-    {
-      LinkedHashSet<String> newAttrs = new LinkedHashSet<String>();
-      for (String attrName : attributes)
-      {
-        if (attrName.startsWith("@"))
-        {
-          String lowerName = toLowerCase(attrName.substring(1));
-          ObjectClass oc = DirectoryConfig.getObjectClass(lowerName, false);
-          if (oc == null)
-          {
-            if (debugEnabled())
-            {
-              TRACER.debugWarning("Cannot replace unknown objectclass %s",
-                                  lowerName);
-            }
-          }
-          else
-          {
-            if (debugEnabled())
-            {
-              TRACER.debugInfo("Replacing objectclass %s", lowerName);
-            }
-
-            for (AttributeType at : oc.getRequiredAttributeChain())
-            {
-              newAttrs.add(at.getNameOrOID());
-            }
-
-            for (AttributeType at : oc.getOptionalAttributeChain())
-            {
-              newAttrs.add(at.getNameOrOID());
-            }
-          }
-        }
-        else
-        {
-          newAttrs.add(attrName);
-        }
-      }
-
-      searchOperation.setAttributes(newAttrs);
-    }
-
-
+    searchOperation.setAttributes(normalizedObjectClasses(searchOperation
+        .getAttributes()));
     return PluginResult.PreParse.continueOperationProcessing();
   }
 
diff --git a/opendj-sdk/opends/src/server/org/opends/server/types/operation/PostOperationSearchOperation.java b/opendj-sdk/opends/src/server/org/opends/server/types/operation/PostOperationSearchOperation.java
index 1138b88..9ac492c 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/types/operation/PostOperationSearchOperation.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/types/operation/PostOperationSearchOperation.java
@@ -23,12 +23,13 @@
  *
  *
  *      Copyright 2006-2008 Sun Microsystems, Inc.
+ *      Portions copyright 2013 ForgeRock AS.
  */
 package org.opends.server.types.operation;
 
 
 
-import java.util.LinkedHashSet;
+import java.util.Set;
 
 import org.opends.server.types.ByteString;
 import org.opends.server.types.DereferencePolicy;
@@ -151,7 +152,7 @@
    * @return  The set of requested attributes for this search
    *          operation.
    */
-  public LinkedHashSet<String> getAttributes();
+  public Set<String> getAttributes();
 
 
 
diff --git a/opendj-sdk/opends/src/server/org/opends/server/types/operation/PostResponseSearchOperation.java b/opendj-sdk/opends/src/server/org/opends/server/types/operation/PostResponseSearchOperation.java
index 2af4351..52c5c6a 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/types/operation/PostResponseSearchOperation.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/types/operation/PostResponseSearchOperation.java
@@ -23,12 +23,13 @@
  *
  *
  *      Copyright 2006-2008 Sun Microsystems, Inc.
+ *      Portions copyright 2013 ForgeRock AS.
  */
 package org.opends.server.types.operation;
 
 
 
-import java.util.LinkedHashSet;
+import java.util.Set;
 
 import org.opends.server.types.*;
 
@@ -145,7 +146,7 @@
    * @return  The set of requested attributes for this search
    *          operation.
    */
-  public LinkedHashSet<String> getAttributes();
+  public Set<String> getAttributes();
 
 
 
diff --git a/opendj-sdk/opends/src/server/org/opends/server/types/operation/PreOperationSearchOperation.java b/opendj-sdk/opends/src/server/org/opends/server/types/operation/PreOperationSearchOperation.java
index 20fc7db..2495031 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/types/operation/PreOperationSearchOperation.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/types/operation/PreOperationSearchOperation.java
@@ -23,13 +23,14 @@
  *
  *
  *      Copyright 2006-2008 Sun Microsystems, Inc.
+ *      Portions copyright 2013 ForgeRock AS.
  */
 package org.opends.server.types.operation;
 
 
 
-import java.util.LinkedHashSet;
 import java.util.List;
+import java.util.Set;
 
 import org.opends.server.types.ByteString;
 import org.opends.server.types.Control;
@@ -155,7 +156,7 @@
    * @return  The set of requested attributes for this search
    *          operation.
    */
-  public LinkedHashSet<String> getAttributes();
+  public Set<String> getAttributes();
 
 
 
diff --git a/opendj-sdk/opends/src/server/org/opends/server/types/operation/PreParseSearchOperation.java b/opendj-sdk/opends/src/server/org/opends/server/types/operation/PreParseSearchOperation.java
index 2376632c..4ecf4dc 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/types/operation/PreParseSearchOperation.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/types/operation/PreParseSearchOperation.java
@@ -23,13 +23,14 @@
  *
  *
  *      Copyright 2006-2008 Sun Microsystems, Inc.
+ *      Portions copyright 2013 ForgeRock AS.
  */
 package org.opends.server.types.operation;
 
 
 
-import java.util.LinkedHashSet;
 import java.util.List;
+import java.util.Set;
 
 import org.opends.server.types.*;
 
@@ -195,7 +196,7 @@
    * @return  The set of requested attributes for this search
    *          operation.
    */
-  public LinkedHashSet<String> getAttributes();
+  public Set<String> getAttributes();
 
 
 
@@ -206,7 +207,7 @@
    * @param  attributes  The set of requested attributes for this
    *                     search operation.
    */
-  public void setAttributes(LinkedHashSet<String> attributes);
+  public void setAttributes(Set<String> attributes);
 
 
 
diff --git a/opendj-sdk/opends/src/server/org/opends/server/types/operation/SearchEntrySearchOperation.java b/opendj-sdk/opends/src/server/org/opends/server/types/operation/SearchEntrySearchOperation.java
index 5a38422..6f061e3 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/types/operation/SearchEntrySearchOperation.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/types/operation/SearchEntrySearchOperation.java
@@ -23,12 +23,13 @@
  *
  *
  *      Copyright 2006-2008 Sun Microsystems, Inc.
+ *      Portions copyright 2013 ForgeRock AS.
  */
 package org.opends.server.types.operation;
 
 
 
-import java.util.LinkedHashSet;
+import java.util.Set;
 
 import org.opends.server.types.*;
 
@@ -145,6 +146,6 @@
    * @return  The set of requested attributes for this search
    *          operation.
    */
-  public LinkedHashSet<String> getAttributes();
+  public Set<String> getAttributes();
 }
 
diff --git a/opendj-sdk/opends/src/server/org/opends/server/types/operation/SearchReferenceSearchOperation.java b/opendj-sdk/opends/src/server/org/opends/server/types/operation/SearchReferenceSearchOperation.java
index c46e785..1c782c9 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/types/operation/SearchReferenceSearchOperation.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/types/operation/SearchReferenceSearchOperation.java
@@ -23,12 +23,13 @@
  *
  *
  *      Copyright 2006-2008 Sun Microsystems, Inc.
+ *      Portions copyright 2013 ForgeRock AS.
  */
 package org.opends.server.types.operation;
 
 
 
-import java.util.LinkedHashSet;
+import java.util.Set;
 
 import org.opends.server.types.*;
 
@@ -145,6 +146,6 @@
    * @return  The set of requested attributes for this search
    *          operation.
    */
-  public LinkedHashSet<String> getAttributes();
+  public Set<String> getAttributes();
 }
 
diff --git a/opendj-sdk/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElement.java b/opendj-sdk/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElement.java
index 3d70947..095cf4a 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElement.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElement.java
@@ -23,14 +23,13 @@
  *
  *
  *      Copyright 2008-2010 Sun Microsystems, Inc.
- *      Portions Copyright 2011-2012 ForgeRock AS
+ *      Portions Copyright 2011-2013 ForgeRock AS
  */
 package org.opends.server.workflowelement.localbackend;
 
 
 
 import java.util.ArrayList;
-import java.util.Iterator;
 import java.util.List;
 import java.util.TreeMap;
 import java.util.concurrent.CopyOnWriteArrayList;
@@ -384,61 +383,36 @@
       return;
     }
 
-    // Even though the associated update succeeded, we should still check
-    // whether or not we should return the entry.
-    final SearchResultEntry unfilteredEntry =
-      new SearchResultEntry(entry, null);
+    /*
+     * Virtual and collective attributes are only added to an entry when it is
+     * read from the backend, not before it is written, so we need to add them
+     * ourself.
+     */
+    final Entry fullEntry = entry.duplicate(true);
+
+    /*
+     * Even though the associated update succeeded, we should still check
+     * whether or not we should return the entry.
+     */
+    final SearchResultEntry unfilteredSearchEntry = new SearchResultEntry(
+        fullEntry, null);
     if (AccessControlConfigManager.getInstance().getAccessControlHandler()
-        .maySend(operation, unfilteredEntry) == false)
+        .maySend(operation, unfilteredSearchEntry))
     {
-      return;
+      // Filter the entry based on the control's attribute list.
+      final Entry filteredEntry = fullEntry.filterEntry(
+          postReadRequest.getRequestedAttributes(), false, false, false);
+      final SearchResultEntry filteredSearchEntry = new SearchResultEntry(
+          filteredEntry, null);
+
+      // Strip out any attributes which access control denies access to.
+      AccessControlConfigManager.getInstance().getAccessControlHandler()
+          .filterEntry(operation, unfilteredSearchEntry, filteredSearchEntry);
+
+      final LDAPPostReadResponseControl responseControl =
+          new LDAPPostReadResponseControl(filteredSearchEntry);
+      operation.addResponseControl(responseControl);
     }
-
-    final SearchResultEntry filteredEntry = new SearchResultEntry(
-        entry.duplicate(true), null);
-
-    if (!postReadRequest.allowsAttribute(DirectoryServer
-        .getObjectClassAttributeType()))
-    {
-      filteredEntry.removeAttribute(DirectoryServer
-          .getObjectClassAttributeType());
-    }
-
-    if (!postReadRequest.returnAllUserAttributes())
-    {
-      Iterator<AttributeType> iterator = filteredEntry.getUserAttributes()
-          .keySet().iterator();
-      while (iterator.hasNext())
-      {
-        final AttributeType attrType = iterator.next();
-        if (!postReadRequest.allowsAttribute(attrType))
-        {
-          iterator.remove();
-        }
-      }
-    }
-
-    if (!postReadRequest.returnAllOperationalAttributes())
-    {
-      final Iterator<AttributeType> iterator = filteredEntry
-          .getOperationalAttributes().keySet().iterator();
-      while (iterator.hasNext())
-      {
-        AttributeType attrType = iterator.next();
-        if (!postReadRequest.allowsAttribute(attrType))
-        {
-          iterator.remove();
-        }
-      }
-    }
-
-    // Strip out any attributes which access control denies access to.
-    AccessControlConfigManager.getInstance().getAccessControlHandler()
-        .filterEntry(operation, unfilteredEntry, filteredEntry);
-
-    final LDAPPostReadResponseControl responseControl =
-      new LDAPPostReadResponseControl(filteredEntry);
-    operation.addResponseControl(responseControl);
   }
 
 
@@ -461,61 +435,29 @@
       return;
     }
 
-    // Even though the associated update succeeded, we should still check
-    // whether or not we should return the entry.
-    final SearchResultEntry unfilteredEntry =
-      new SearchResultEntry(entry, null);
+    /*
+     * Even though the associated update succeeded, we should still check
+     * whether or not we should return the entry.
+     */
+    final SearchResultEntry unfilteredSearchEntry = new SearchResultEntry(
+        entry, null);
     if (AccessControlConfigManager.getInstance().getAccessControlHandler()
-        .maySend(operation, unfilteredEntry) == false)
+        .maySend(operation, unfilteredSearchEntry))
     {
-      return;
+      // Filter the entry based on the control's attribute list.
+      final Entry filteredEntry = entry.filterEntry(
+          preReadRequest.getRequestedAttributes(), false, false, false);
+      final SearchResultEntry filteredSearchEntry = new SearchResultEntry(
+          filteredEntry, null);
+
+      // Strip out any attributes which access control denies access to.
+      AccessControlConfigManager.getInstance().getAccessControlHandler()
+          .filterEntry(operation, unfilteredSearchEntry, filteredSearchEntry);
+
+      final LDAPPreReadResponseControl responseControl =
+          new LDAPPreReadResponseControl(filteredSearchEntry);
+      operation.addResponseControl(responseControl);
     }
-
-    final SearchResultEntry filteredEntry = new SearchResultEntry(
-        entry.duplicate(true), null);
-
-    if (!preReadRequest.allowsAttribute(DirectoryServer
-        .getObjectClassAttributeType()))
-    {
-      filteredEntry.removeAttribute(DirectoryServer
-          .getObjectClassAttributeType());
-    }
-
-    if (!preReadRequest.returnAllUserAttributes())
-    {
-      Iterator<AttributeType> iterator = filteredEntry.getUserAttributes()
-          .keySet().iterator();
-      while (iterator.hasNext())
-      {
-        final AttributeType attrType = iterator.next();
-        if (!preReadRequest.allowsAttribute(attrType))
-        {
-          iterator.remove();
-        }
-      }
-    }
-
-    if (!preReadRequest.returnAllOperationalAttributes())
-    {
-      final Iterator<AttributeType> iterator = filteredEntry
-          .getOperationalAttributes().keySet().iterator();
-      while (iterator.hasNext())
-      {
-        AttributeType attrType = iterator.next();
-        if (!preReadRequest.allowsAttribute(attrType))
-        {
-          iterator.remove();
-        }
-      }
-    }
-
-    // Strip out any attributes which access control denies access to.
-    AccessControlConfigManager.getInstance().getAccessControlHandler()
-        .filterEntry(operation, unfilteredEntry, filteredEntry);
-
-    final LDAPPreReadResponseControl responseControl =
-      new LDAPPreReadResponseControl(filteredEntry);
-    operation.addResponseControl(responseControl);
   }
 
 
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/EntityTagVirtualAttributeProviderTestCase.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/EntityTagVirtualAttributeProviderTestCase.java
index 0041577..2850a5b 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/EntityTagVirtualAttributeProviderTestCase.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/EntityTagVirtualAttributeProviderTestCase.java
@@ -701,7 +701,7 @@
    * @throws Exception
    *           If an unexpected exception occurred.
    */
-  @Test(enabled = false)
+  @Test
   public void testPreReadControl() throws Exception
   {
     AttributeType etagType = DirectoryServer.getAttributeType(ETAG);
@@ -790,7 +790,7 @@
    * @throws Exception
    *           If an unexpected exception occurred.
    */
-  @Test(enabled = false)
+  @Test
   public void testPostReadControl() throws Exception
   {
     AttributeType etagType = DirectoryServer.getAttributeType(ETAG);
@@ -867,7 +867,7 @@
     assertNotNull(postReadControl);
     String etagPostRead = postReadControl.getSearchEntry().getAttributeValue(
         etagType, DirectoryStringSyntax.DECODER);
-    assertEquals(etagPostRead, etag1);
+    assertEquals(etagPostRead, etag2);
   }
 
 

--
Gitblit v1.10.0