mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

Matthew Swift
02.45.2011 91fdf0048df4c43fe3b7412ccb7f862eab5f7669
Fix issue OPENDJ-24: Fix OpenDS issue 4583: during a search op, ACI with targetfilter and targetattrs gets evaluated wrongly 
https://bugster.forgerock.org/jira/browse/OPENDJ-24

* pass in original entry to filterEntry method as well as entry to be filtered
* use similar logic for pre/post read controls
* also invoke maySend for pre-post read controls
* refactor pre/post read control processing to remove duplicate code.
* enable unit test from previous commit.
16 files modified
1186 ■■■■■ changed files
opends/src/server/org/opends/server/api/AccessControlHandler.java 61 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/authorization/dseecompat/AciContainer.java 55 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/authorization/dseecompat/AciEffectiveRights.java 258 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/authorization/dseecompat/AciEvalContext.java 18 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/authorization/dseecompat/AciHandler.java 137 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/authorization/dseecompat/AciLDAPOperationContainer.java 44 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/authorization/dseecompat/UserAttr.java 4 ●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/core/DefaultAccessControlHandler.java 33 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/core/SearchOperationBasis.java 32 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendAddOperation.java 72 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendCompareOperation.java 2 ●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendDeleteOperation.java 63 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendModifyDNOperation.java 118 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendModifyOperation.java 117 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElement.java 170 ●●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/authorization/dseecompat/AciTests.java 2 ●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/api/AccessControlHandler.java
@@ -23,6 +23,7 @@
 *
 *
 *      Copyright 2006-2009 Sun Microsystems, Inc.
 *      Portions Copyright 2011 ForgeRock AS
 */
package org.opends.server.api;
@@ -347,36 +348,18 @@
   * the client. Implementations <b>must not under any
   * circumstances</b> modify the search entry in any way.
   *
   * @param searchOperation
   *          The search operation with which the provided entry is
   *          associated.
   * @param searchEntry
   *          The search result entry for which to make the
   *          determination.
   * @param operation
   *          The operation currently being processed (this will
   *          usually be a search, but may be other types of operation
   *          when pre/post read controls are used).
   * @param unfilteredEntry
   *          The result entry before any attribute filtering.
   * @return {@code true} if the access control configuration allows
   *         the entry to be returned to the client, or {@code false}
   *         if not.
   */
  public abstract boolean maySend(SearchOperation searchOperation,
      SearchResultEntry searchEntry);
  /**
   * Filter the contents of the provided entry such that it no longer
   * contains any attributes or values that the client is not
   * permitted to access.
   *
   * @param searchOperation
   *          The search operation with which the provided entry is
   *          associated.
   * @param searchEntry
   *          The search result entry to be filtered.
   * @return Returns the entry with filtered attributes and values
   *         removed.
   */
  public abstract SearchResultEntry filterEntry(
      SearchOperation searchOperation, SearchResultEntry searchEntry);
  public abstract boolean maySend(Operation operation,
      SearchResultEntry unfilteredEntry);
@@ -386,15 +369,18 @@
   * permitted to access.
   *
   * @param operation
   *          The operation with which the provided entry is
   *          associated.
   * @param entry
   *          The entry to be filtered.
   * @return Returns the entry with filtered attributes and values
   *         removed.
   *          The operation currently being processed (this will
   *          usually be a search, but may be other types of operation
   *          when pre/post read controls are used).
   * @param unfilteredEntry
   *          The result entry before any attribute filtering.
   * @param filteredEntry
   *          The partially filtered result entry being returned to
   *          the client.
   */
  public abstract SearchResultEntry filterEntry(
      Operation operation, Entry entry);
  public abstract void filterEntry(Operation operation,
      SearchResultEntry unfilteredEntry,
      SearchResultEntry filteredEntry);
@@ -404,8 +390,8 @@
   *
   * @param dn
   *          A DN that can be used in the access determination.
   * @param searchOperation
   *          The search operation with which the provided reference
   * @param operation
   *          The operation with which the provided reference
   *          is associated.
   * @param searchReference
   *          The search result reference for which to make the
@@ -414,8 +400,7 @@
   *         the reference to be returned to the client, or {@code
   *         false} if not.
   */
  public abstract boolean maySend(DN dn,
                               SearchOperation searchOperation,
  public abstract boolean maySend(DN dn, Operation operation,
                               SearchResultReference searchReference);
opends/src/server/org/opends/server/authorization/dseecompat/AciContainer.java
@@ -23,6 +23,7 @@
 *
 *
 *      Copyright 2008 Sun Microsystems, Inc.
 *      Portions Copyright 2011 ForgeRock AS
 */
package org.opends.server.authorization.dseecompat;
@@ -103,13 +104,6 @@
    private Entry resourceEntry;
    /*
     * Saves the resource entry. Used in geteffectiverights evaluation to
     * restore the current resource entry state after a read right was
     * evaluated.
     */
    private final Entry saveResourceEntry;
    /*
     * The client connection information.
     */
    private final ClientConnection clientConnection;
@@ -187,12 +181,6 @@
    private List<AttributeType> specificAttrs=null;
    /*
     * The entry with all of its attributes available. Used in
     * geteffectiverights read entry level evaluation.
     */
    private Entry fullEntry=null;
    /*
     * Table of ACIs that have targattrfilter keywords that matched. Used
     * in geteffectiverights attributeLevel write evaluation.
     */
@@ -278,26 +266,29 @@
      if(origAuthorizationEntry != null)
         this.proxiedAuthorization=true;
      this.authorizationEntry=operation.getAuthorizationEntry();
      //The ACI_READ right at constructor time can only be the result of the
      //AciHandler.filterEntry method. This method processes the
      //geteffectiverights control, so it needs to check for it.  There are
      //two other checks done, because the resource entry passed to that method
      //is filtered (it may not contain enough attribute information
      //to evaluate correctly). See the the comments below.
      if(operation instanceof SearchOperation && (rights == ACI_READ)) {
      if (rights == ACI_READ) {
        //Checks if a geteffectiverights control was sent and
        //sets up the structures needed.
        GetEffectiveRightsRequestControl getEffectiveRightsControl =
              (GetEffectiveRightsRequestControl)
                      operation.getAttachment(OID_GET_EFFECTIVE_RIGHTS);
        if(getEffectiveRightsControl != null) {
        if (getEffectiveRightsControl != null
            && operation instanceof SearchOperation)
        {
          hasGetEffectiveRightsControl=true;
          if(getEffectiveRightsControl.getAuthzDN() == null)
            this.authzid=getClientDN();
          else
            this.authzid=getEffectiveRightsControl.getAuthzDN();
          else this.authzid = getEffectiveRightsControl.getAuthzDN();
          this.specificAttrs=getEffectiveRightsControl.getAttributes();
        }
        //If an ACI evaluated because of an Targetattr="*", then the
        //AciHandler.maySend method signaled this via adding this attachment
        //string.
@@ -311,16 +302,11 @@
        String allOpAttrs=(String)operation.getAttachment(ALL_OP_ATTRS_MATCHED);
        if(allOpAttrs != null)
          evalAllAttributes |= ACI_OP_ATTR_PLUS_MATCHED;
      }
        //The AciHandler.maySend method also adds the full attribute version of
        //the resource entry in this attachment.
        fullEntry=(Entry)operation.getAttachment(ALL_ATTRS_RESOURCE_ENTRY);
      } else
        fullEntry=this.resourceEntry;
      //Reference the current authorization entry, so it can be put back
      //if an access proxy check was performed.
      this.saveAuthorizationEntry=this.authorizationEntry;
      this.saveResourceEntry=this.resourceEntry;
      this.rightsMask = rights;
    }
@@ -341,7 +327,6 @@
        this.authInfo = authInfo;
        this.authorizationEntry = authInfo.getAuthorizationEntry();
        this.saveAuthorizationEntry=this.authorizationEntry;
        this.saveResourceEntry=this.resourceEntry;
        this.rightsMask = rights;
    }
  /**
@@ -420,28 +405,6 @@
    }
  /**
   * During the geteffectiverights entrylevel read evaluation, an entry with all
   * of the attributes used in the AciHandler's maysend method evaluation is
   * needed to perform the evaluation over again. This entry was saved
   * in the operation's attachment mechanism when the container was created
   * during the SearchOperation read evaluation.
   *
   * This method is used to replace the current resource entry with that saved
   * entry to perform the entrylevel read evaluation described above and to
   * switch back to the current resource entry when needed.
   *
   * @param val Specifies if the saved entry should be used or not. True if it
   * should be used, false if the original resource entry should be used.
   *
   */
    public void useFullResourceEntry(boolean val) {
      if(val)
        resourceEntry=fullEntry;
      else
        resourceEntry=saveResourceEntry;
    }
   /**
    * {@inheritDoc}
    */
    public void addTargAttrFiltersMatchAci(Aci aci) {
opends/src/server/org/opends/server/authorization/dseecompat/AciEffectiveRights.java
@@ -23,6 +23,7 @@
 *
 *
 *      Copyright 2008 Sun Microsystems, Inc.
 *      Portions Copyright 2011 ForgeRock AS
 */
package org.opends.server.authorization.dseecompat;
@@ -186,123 +187,148 @@
   * @param e The entry to add the rights attributes to.
   * @param skipCheck  True if ACI evaluation was skipped because bypass-acl
   *                   privilege was found.
   * @return  A SearchResultEntry with geteffectiverights information possibly
   *          added to it.
   */
  public static SearchResultEntry
  addRightsToEntry(AciHandler handler, LinkedHashSet<String> searchAttributes,
            AciLDAPOperationContainer container,SearchResultEntry e,
            boolean skipCheck) {
  public static void addRightsToEntry(AciHandler handler,
      LinkedHashSet<String> searchAttributes,
      AciLDAPOperationContainer container, final Entry e,
      boolean skipCheck)
  {
    if (aclRights == null)
    {
      aclRights = DirectoryServer.getAttributeType(aclRightsAttrStr
          .toLowerCase());
    }
    if (aclRightsInfo == null)
    {
      aclRightsInfo = DirectoryServer.getAttributeType(aclRightsInfoAttrStr
          .toLowerCase());
    }
    if (dnAttributeType == null)
    {
      dnAttributeType = DirectoryServer.getAttributeType(dnAttrStr);
    }
    // Check if the attributes aclRights and aclRightsInfo were requested and
    // add attributes less those two attributes to a new list of attribute
    // types.
    List<AttributeType> nonRightsAttrs = new LinkedList<AttributeType>();
    int attrMask=ACI_NULL;
    if(aclRights == null)
      aclRights =
               DirectoryServer.getAttributeType(aclRightsAttrStr.toLowerCase());
    if(aclRightsInfo == null)
      aclRightsInfo =
           DirectoryServer.getAttributeType(aclRightsInfoAttrStr.toLowerCase());
    if(dnAttributeType == null)
      dnAttributeType = DirectoryServer.getAttributeType(dnAttrStr);
    //Check if the attributes aclRights and aclRightsInfo were requested and
    //add attributes less those two attributes to a new list of attribute types.
    for(String a : searchAttributes) {
    for (String a : searchAttributes)
    {
      if(a.equalsIgnoreCase(aclRightsAttrStr))
      {
        attrMask |= ACL_RIGHTS;
      }
      else if(a.equalsIgnoreCase(aclRightsInfoAttrStr))
      {
        attrMask |= ACL_RIGHTS_INFO;
      else {
      }
      else
      {
          //Check for shorthands for user attributes "*" or operational "+".
          if(a.equals("*")) {
        if (a.equals("*"))
        {
              //Add objectclass.
              AttributeType ocType =
                      DirectoryServer.getObjectClassAttributeType();
          AttributeType ocType = DirectoryServer.getObjectClassAttributeType();
              nonRightsAttrs.add(ocType);
              nonRightsAttrs.addAll(e.getUserAttributes().keySet());
          } else if (a.equals("+"))
        }
        else if (a.equals("+"))
        {
              nonRightsAttrs.addAll(e.getOperationalAttributes().keySet());
          else {
              AttributeType attrType;
              if((attrType =
                  DirectoryServer.getAttributeType(a.toLowerCase())) == null)
                  attrType =
                      DirectoryServer.getDefaultAttributeType(a.toLowerCase());
        }
        else
        {
          AttributeType attrType = DirectoryServer.getAttributeType(a
              .toLowerCase());
          if (attrType == null)
            attrType = DirectoryServer.getDefaultAttributeType(a.toLowerCase());
              nonRightsAttrs.add(attrType);
          }
      }
    }
      //If the special geteffectiverights attributes were not found or
    //the user does not have both bypass-acl privs and is not allowed to
    //perform rights evalation -- return the entry unchanged.
    if(attrMask == ACI_NULL ||
      (!skipCheck && !rightsAccessAllowed(container,handler,attrMask)))
       return e;
    //From here on out, geteffectiverights evaluation is being performed and the
    //container will be manipulated. First set the flag that geteffectiverights
    //evaluation's underway and to use the authZid for authorizationDN (they
    //might be the same).
    if (attrMask == ACI_NULL
        || (!skipCheck && !rightsAccessAllowed(container, handler, attrMask)))
    {
      return;
    }
    // From here on out, geteffectiverights evaluation is being performed and
    // the container will be manipulated. First set the flag that
    // geteffectiverights evaluation's underway and to use the authZid for
    // authorizationDN (they might be the same).
    container.setGetEffectiveRightsEval();
    container.useAuthzid(true);
    //If no attributes were requested return only entryLevel rights, else
    //return attributeLevel rights and entryLevel rights. Always try and
    //return the specific attribute rights if they exist.
    if(nonRightsAttrs.isEmpty()) {
      e=addAttributeLevelRights(container,handler,attrMask,e,
    if (nonRightsAttrs.isEmpty())
    {
      addAttributeLevelRights(container, handler, attrMask, e,
              container.getSpecificAttributes(), skipCheck, true);
      e=addEntryLevelRights(container,handler,attrMask,e, skipCheck);
    } else {
      e=addAttributeLevelRights(container,handler,attrMask,e,
              nonRightsAttrs, skipCheck, false);
      e=addAttributeLevelRights(container,handler,attrMask,e,
      addEntryLevelRights(container, handler, attrMask, e, skipCheck);
    }
    else
    {
      addAttributeLevelRights(container, handler, attrMask, e, nonRightsAttrs,
          skipCheck, false);
      addAttributeLevelRights(container, handler, attrMask, e,
              container.getSpecificAttributes(), skipCheck, true);
      e=addEntryLevelRights(container,handler,attrMask,e,skipCheck);
      addEntryLevelRights(container, handler, attrMask, e, skipCheck);
    }
    return e;
  }
  /**
   * Perform the attributeLevel rights evaluation on a list of specified
   * attribute types. Each attribute has an access check done for the following
   * rights: search, read, compare, add, delete, proxy, selfwrite_add,
   * selfwrite_delete and write.
   * selfwrite_delete and write. The special rights, selfwrite_add and
   * selfwrite_delete, use the authZid as the attribute value to evaluate
   * against the attribute type being evaluated. The selfwrite_add performs the
   * access check using the ACI_WRITE_ADD right and selfwrite_delete uses
   * ACI_WRITE_ADD right. The write right is made complicated by the
   * targattrfilters keyword, which might depend on an unknown value of an
   * attribute type. For this case a dummy attribute value is used to try and
   * determine if a "?" needs to be placed in the rights string. The special
   * flag ACI_SKIP_PROXY_CHECK is always set, so that proxy evaluation is
   * bypassed in the Aci Handler's accessAllowed method.
   *
   * The special rights, selfwrite_add and selfwrite_delete, use the authZid as
   * the attribute value to evaluate against the attribute type being
   * evaluated. The selfwrite_add performs the access check using the
   * ACI_WRITE_ADD right and selfwrite_delete uses ACI_WRITE_ADD right.
   *
   * The write right is made complicated by the targattrfilters keyword, which
   * might depend on an unknown value of an attribute type. For this case a
   * dummy attribute value is used to try and determine if a "?" needs to be
   * placed in the rights string.
   *
   * The special flag ACI_SKIP_PROXY_CHECK is always set, so that proxy
   * evaluation is bypassed in the Aci Handler's accessAllowed method.
   *
   * @param container The LDAP operation container to use in the evaluations.
   * @param handler  The Aci Handler to use in the access evaluations.
   * @param mask  Mask specifing what rights attribute processing to perform
   * @param container
   *          The LDAP operation container to use in the evaluations.
   * @param handler
   *          The Aci Handler to use in the access evaluations.
   * @param mask
   *          Mask specifing what rights attribute processing to perform
   *              (aclRights or aclRightsInfo or both).
   * @param retEntry  The entry to return.
   * @param attrList The list of attribute types to iterate over.
   * @param skipCheck True if ACI evaluation was skipped because bypass-acl
   *                  privilege was found.
   * @param  specificAttr True if this evaluation is result of specific
   *                      attributes sent in the request.
   * @return  A SearchResultEntry with geteffectiverights attribute level
   *          information added to it.
   * @param retEntry
   *          The entry to return.
   * @param attrList
   *          The list of attribute types to iterate over.
   * @param skipCheck
   *          True if ACI evaluation was skipped because bypass-acl privilege
   *          was found.
   * @param specificAttr
   *          True if this evaluation is result of specific attributes sent in
   *          the request.
   */
  private static
  SearchResultEntry addAttributeLevelRights(AciLDAPOperationContainer container,
                                        AciHandler handler, int mask,
                                        SearchResultEntry retEntry,
                                        List<AttributeType> attrList,
                                        boolean skipCheck,
                                        boolean specificAttr) {
  private static void addAttributeLevelRights(
      AciLDAPOperationContainer container, AciHandler handler, int mask,
      final Entry retEntry, List<AttributeType> attrList,
      boolean skipCheck, boolean specificAttr)
  {
    //The attribute list might be null.
    if(attrList == null)
      return retEntry;
    if (attrList == null) return;
    for(AttributeType a : attrList) {
      StringBuilder evalInfo=new StringBuilder();
      container.setCurrentAttributeType(a);
@@ -371,33 +397,34 @@
    }
    container.setCurrentAttributeValue(null);
    container.setCurrentAttributeType(null);
    return retEntry;
  }
  /**
   * Perform the attributeLevel write rights evaluation. The issue here is that
   * an ACI could contain a targattrfilters keyword that matches the attribute
   * being evaluated.
   * being evaluated. There is no way of knowing if the filter part of the
   * targattrfilter would be successful or not. So if the ACI that allowed
   * access, has an targattrfilter keyword, a "?" is used as the result of the
   * write (depends on attribute value). If the allow ACI doesn't contain a
   * targattrfilters keyword than a "1" is added. If the ACI denies then a "0"
   * is added. If the skipCheck flag is true, then a 1 is used for the write
   * access, since the client DN has bypass privs.
   *
   * There is no way of knowing if the filter part of the targattrfilter would
   * be successful or not. So if the ACI that allowed access, has an
   * targattrfilter keyword, a "?" is used as the result of the write (depends
   * on attribute value).
   *
   * If the allow ACI doesn't contain a targattrfilters keyword than a
   * "1" is added. If the ACI denies then a "0" is added. If the skipCheck flag
   * is true, then a 1 is used for the write access, since the client DN has
   * bypass privs.
   *
   * @param container The LDAP operation container to use in the evaluations.
   * @param handler The Aci Handler to use in the access evaluations.
   * @param skipCheck True if ACI evaluation was skipped because bypass-acl
   *                  privilege was found.
   * @param container
   *          The LDAP operation container to use in the evaluations.
   * @param handler
   *          The Aci Handler to use in the access evaluations.
   * @param skipCheck
   *          True if ACI evaluation was skipped because bypass-acl privilege
   *          was found.
   * @return A string representing the rights information.
   */
  private static
  String attributeLevelWriteRights(AciLDAPOperationContainer container,
                                   AciHandler handler,  boolean skipCheck){
  private static String attributeLevelWriteRights(
      AciLDAPOperationContainer container, AciHandler handler,
      boolean skipCheck)
  {
    boolean addRet=false, delRet=false;
    StringBuilder resString=new  StringBuilder();
    //If the user has bypass-acl privs and the authzid is equal to the
@@ -443,27 +470,31 @@
    return resString.toString();
  }
  /**
   * Perform entryLevel rights evaluation. The rights string is added to the
   * entry if the aclRights attribute was seen in the search's requested
   * attribute set.
   *
   * @param container The LDAP operation container to use in the evaluations.
   * @param handler The Aci Handler to use in the access evaluations.
   * @param mask Mask specifing what rights attribute processing to perform
   * @param container
   *          The LDAP operation container to use in the evaluations.
   * @param handler
   *          The Aci Handler to use in the access evaluations.
   * @param mask
   *          Mask specifing what rights attribute processing to perform
   *              (aclRights or aclRightsInfo or both).
   * @param retEntry The entry to return.
   * @param skipCheck True if ACI evaluation was skipped because bypass-acl
   *                  privilege was found.
   * @return A SearchResultEntry with geteffectiverights entryLevel rights
   *          information added to it.
   * @param retEntry
   *          The entry to return.
   * @param skipCheck
   *          True if ACI evaluation was skipped because bypass-acl privilege
   *          was found.
   */
  private static SearchResultEntry
  addEntryLevelRights(AciLDAPOperationContainer container,
                                           AciHandler handler,
                                           int mask, SearchResultEntry retEntry,
                                           boolean skipCheck) {
  private static void addEntryLevelRights(AciLDAPOperationContainer container,
      AciHandler handler, int mask, final Entry retEntry,
      boolean skipCheck)
  {
    //Perform access evaluations for rights: add, delete, read, write, proxy.
    StringBuilder evalInfo=new StringBuilder();
    container.setCurrentAttributeType(null);
@@ -479,13 +510,11 @@
    container.setCurrentAttributeType(null);
    //The read right needs the entry with the full set of attributes. This was
    //saved in the Aci Handlers maysend method.
    container.useFullResourceEntry(true);
    container.setRights(ACI_READ | ACI_SKIP_PROXY_CHECK);
    evalInfo.append(rightsString(container, handler, skipCheck, "read"));
    addEntryLevelRightsInfo(container, mask, retEntry, "read");
    evalInfo.append(',');
    //Switch back to the entry from the Aci Handler's filterentry method.
    container.useFullResourceEntry(false);
    container.setCurrentAttributeType(null);
    container.setRights(ACI_WRITE| ACI_SKIP_PROXY_CHECK);
    evalInfo.append(rightsString(container, handler, skipCheck, "write"));
@@ -502,7 +531,6 @@
          .toString());
      retEntry.addAttribute(attr,null);
    }
    return retEntry;
  }
  /**
@@ -593,7 +621,7 @@
   */
  private static
  void addAttrLevelRightsInfo(AciLDAPOperationContainer container, int mask,
                     AttributeType aType, SearchResultEntry retEntry,
                     AttributeType aType, Entry retEntry,
                     String rightStr) {
    //Check if the aclRightsInfo attribute was requested.
@@ -625,7 +653,7 @@
   */
  private static
   void addEntryLevelRightsInfo(AciLDAPOperationContainer container, int mask,
                       SearchResultEntry retEntry,
                       Entry retEntry,
                      String rightStr) {
     //Check if the aclRightsInfo attribute was requested.
opends/src/server/org/opends/server/authorization/dseecompat/AciEvalContext.java
@@ -23,6 +23,7 @@
 *
 *
 *      Copyright 2008-2010 Sun Microsystems, Inc.
 *      Portions Copyright 2011 ForgeRock AS
 */
package org.opends.server.authorization.dseecompat;
@@ -310,23 +311,6 @@
   */
    public String getTargAttrFiltersAciName();
  /**
   * The full entry with all of the attributes was saved
   * in the operation's attachment mechanism when the container was created
   * during the SearchOperation read evaluation. Some operations need the full
   * entry and not the filtered entry to perform their evaluations, because they
   * might depend attribute types and values filtered out.
   *
   * This method is used to replace the current resource entry with that saved
   * entry and back.
   *
   * @param val Specifies if the saved entry should be used or not. {@code true}
   * if it should be used, {@code false} if the original resource entry should
   * be used.
   *
   */
    public void useFullResourceEntry(boolean val);
    /**
     * Return the current SSF (Security Strength Factor) of the underlying
opends/src/server/org/opends/server/authorization/dseecompat/AciHandler.java
@@ -23,6 +23,7 @@
 *
 *
 *      Copyright 2008-2010 Sun Microsystems, Inc.
 *      Portions Copyright 2011 ForgeRock AS
 */
package org.opends.server.authorization.dseecompat;
@@ -94,15 +95,6 @@
    AccessControlHandler<DseeCompatAccessControlHandlerCfg>
{
  /**
   * String used to save a resource entry containing all the attributes
   * in the SearchOperation attachment list. This is only used during
   * geteffectiverights read right processing when all of an entry'ss
   * attributes need to examined.
   */
  public static final String ALL_ATTRS_RESOURCE_ENTRY =
      "allAttrsResourceEntry";
  /**
   * String used to indicate that the evaluating ACI had a all
   * operational attributes targetattr match (targetattr="+").
   */
@@ -234,46 +226,29 @@
   * {@inheritDoc}
   */
  @Override
  public SearchResultEntry filterEntry(SearchOperation operation,
      SearchResultEntry entry)
  public void filterEntry(Operation operation,
      SearchResultEntry unfilteredEntry, SearchResultEntry filteredEntry)
  {
    AciLDAPOperationContainer operationContainer =
        new AciLDAPOperationContainer(operation, (ACI_READ), entry);
      new AciLDAPOperationContainer(operation, (ACI_READ), unfilteredEntry);
    // Proxy access check has already been done for this entry in the
    // maySend method, set the seen flag to true to bypass any proxy
    // check.
    operationContainer.setSeenEntry(true);
    SearchResultEntry returnEntry;
    boolean skipCheck = skipAccessCheck(operation);
    if (!skipCheck)
    {
      returnEntry = accessAllowedAttrs(operationContainer);
      filterEntry(operationContainer, filteredEntry);
    }
    else
    {
      returnEntry = entry;
    }
    if (operationContainer.hasGetEffectiveRightsControl())
    {
      returnEntry =
          AciEffectiveRights.addRightsToEntry(this, operation
              .getAttributes(), operationContainer, returnEntry,
              skipCheck);
      AciEffectiveRights.addRightsToEntry(this,
          ((SearchOperation) operation).getAttributes(), operationContainer,
          filteredEntry, skipCheck);
    }
    return returnEntry;
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public SearchResultEntry filterEntry(Operation operation, Entry entry)
  {
    AciLDAPOperationContainer operationContainer =
        new AciLDAPOperationContainer(operation, (ACI_READ), entry);
    return accessAllowedAttrs(operationContainer);
  }
@@ -595,7 +570,7 @@
   * {@inheritDoc}
   */
  @Override
  public boolean maySend(DN dn, SearchOperation operation,
  public boolean maySend(DN dn, Operation operation,
      SearchResultReference reference)
  {
    boolean ret;
@@ -625,57 +600,55 @@
  /**
   * Checks access on a search operation.
   *
   * @param operation
   *          The search operation class containing information to check
   *          the access on.
   * @param entry
   *          The entry to evaluate access.
   * @return True if access is allowed.
   * {@inheritDoc}
   */
  @Override
  public boolean maySend(SearchOperation operation,
      SearchResultEntry entry)
  public boolean maySend(Operation operation, SearchResultEntry entry)
  {
    if (skipAccessCheck(operation))
    {
      return true;
    }
    AciLDAPOperationContainer operationContainer =
        new AciLDAPOperationContainer(operation, (ACI_SEARCH), entry);
    boolean ret;
    if (!(ret = skipAccessCheck(operation)))
    // Pre/post read controls are associated with other types of operation.
    if (operation instanceof SearchOperation)
    {
      try
      {
        ret = testFilter(operationContainer, operation.getFilter());
        if (!testFilter(operationContainer,
            ((SearchOperation) operation).getFilter()))
        {
          return false;
        }
      }
      catch (DirectoryException ex)
      {
        ret = false;
        return false;
      }
      if (ret)
      {
    }
        operationContainer.clearEvalAttributes(ACI_NULL);
        operationContainer.setRights(ACI_READ);
        ret = accessAllowedEntry(operationContainer);
        if (ret)
    if (!accessAllowedEntry(operationContainer))
        {
      return false;
    }
          if (!operationContainer.hasEvalUserAttributes())
          {
            operation.setAttachment(ALL_USER_ATTRS_MATCHED,
                ALL_USER_ATTRS_MATCHED);
      operation.setAttachment(ALL_USER_ATTRS_MATCHED, ALL_USER_ATTRS_MATCHED);
          }
          if (!operationContainer.hasEvalOpAttributes())
          {
            operation.setAttachment(ALL_OP_ATTRS_MATCHED,
                ALL_OP_ATTRS_MATCHED);
      operation.setAttachment(ALL_OP_ATTRS_MATCHED, ALL_OP_ATTRS_MATCHED);
          }
        }
      }
    }
    // Save a copy of the full resource entry for possible
    // userattr bind rule or geteffectiveright's evaluations in the
    // filterEnty method.
    operation.setAttachment(ALL_ATTRS_RESOURCE_ENTRY, entry);
    return ret;
    return true;
  }
@@ -855,23 +828,21 @@
  /**
   * Performs an access check against all of the attributes of an entry.
   * The attributes that fail access are removed from the entry. This
   * method performs the processing needed for the filterEntry method
   * processing.
   * Performs an access check against all of the attributes of an entry. The
   * attributes that fail access are removed from the entry. This method
   * performs the processing needed for the filterEntry method processing.
   *
   * @param container
   *          The search or compare container which has all of the
   *          information needed to filter the attributes for this
   *          entry.
   * @return The entry to send back to the client, minus any attribute
   *         types that failed access check.
   *          The search or compare container which has all of the information
   *          needed to filter the attributes for this entry.
   * @param filteredEntry
   *          The partially filtered search result entry being returned to the
   *          client.
   */
  private SearchResultEntry accessAllowedAttrs(
      AciLDAPOperationContainer container)
  private void filterEntry(AciLDAPOperationContainer container,
      Entry filteredEntry)
  {
    Entry e = container.getResourceEntry();
    List<AttributeType> typeList = getAllAttrs(e);
    List<AttributeType> typeList = getAllAttrs(filteredEntry);
    for (AttributeType attrType : typeList)
    {
      if (container.hasAllUserAttributes() && !attrType.isOperational())
@@ -885,10 +856,9 @@
      container.setCurrentAttributeType(attrType);
      if (!accessAllowed(container))
      {
        e.removeAttribute(attrType);
        filteredEntry.removeAttribute(attrType);
      }
    }
    return container.getSearchResultEntry();
  }
@@ -914,7 +884,8 @@
  {
    Entry resourceEntry = container.getResourceEntry();
    DN dn = resourceEntry.getDN();
    List<Modification> modifications = container.getModifications();
    List<Modification> modifications =  operation.getModifications();
    for (Modification m : modifications)
    {
      Attribute modAttr = m.getAttribute();
opends/src/server/org/opends/server/authorization/dseecompat/AciLDAPOperationContainer.java
@@ -23,11 +23,11 @@
 *
 *
 *      Copyright 2008-2009 Sun Microsystems, Inc.
 *      Portions Copyright 2011 ForgeRock AS
 */
package org.opends.server.authorization.dseecompat;
import java.util.List;
import org.opends.server.core.*;
import org.opends.server.types.*;
import org.opends.server.workflowelement.localbackend.*;
@@ -39,17 +39,6 @@
 */
public class AciLDAPOperationContainer extends AciContainer  {
    /*
     * The entry to be returned if this is a LDAP search.
     */
    private SearchResultEntry searchEntry;
    /*
     * The list of modifications if this operation is a LDAP
     * modify.
     */
    private List<Modification>  modifications;
    /**
     * Constructor interface for all currently supported LDAP operations.
     * @param operation The compare operation to evaluate.
@@ -60,7 +49,6 @@
      int rights, Entry entry)
    {
      super(operation, rights, entry);
      this.searchEntry = new SearchResultEntry(entry);
    }
@@ -149,7 +137,6 @@
        int rights)
    {
        super(operation, rights, operation.getCurrentEntry());
        this.modifications=operation.getModifications();
    }
    /**
@@ -163,33 +150,4 @@
                                     Entry entry) {
        super(operation, rights,  entry);
    }
    /**
     * Constructor interface for the LDAP search operation.
     * @param operation The search operation.
     * @param rights The rights of a search operation.
     * @param entry The entry to be evaluated for this search.
     */
    public AciLDAPOperationContainer(SearchOperation operation,
        int rights,
        SearchResultEntry entry)
    {
        super(operation, rights,  entry);
        this.searchEntry = entry;
    }
    /**
     * Retrieve the search result entry of the search operation.
     * @return The search result entry.
     */
    public SearchResultEntry getSearchResultEntry() {
        return this.searchEntry;
    }
    /** Retrieve the list of modifications if this is a LDAP modify.
     * @return The list of LDAP modifications to made on the resource entry.
     */
    public  List<Modification>  getModifications() {
        return modifications;
    }
}
opends/src/server/org/opends/server/authorization/dseecompat/UserAttr.java
@@ -23,6 +23,7 @@
 *
 *
 *      Copyright 2008 Sun Microsystems, Inc.
 *      Portions Copyright 2011 ForgeRock AS
 */
package org.opends.server.authorization.dseecompat;
@@ -179,7 +180,6 @@
       //attribute type that is needed to perform these evaluations. The
       //evalCtx has a copy of the non-filtered entry, switch to it for these
       //evaluations.
       evalCtx.useFullResourceEntry(true);
        switch(userAttrType) {
        case ROLEDN:
        case GROUPDN:
@@ -194,8 +194,6 @@
        default:
            matched=evalVAL(evalCtx);
        }
        //Switch back to the working resource entry.
        evalCtx.useFullResourceEntry(false);
        return matched;
    }
opends/src/server/org/opends/server/core/DefaultAccessControlHandler.java
@@ -23,6 +23,7 @@
 *
 *
 *      Copyright 2006-2009 Sun Microsystems, Inc.
 *      Portions Copyright 2011 ForgeRock AS
 */
package org.opends.server.core;
@@ -48,13 +49,6 @@
      extends AccessControlHandler<AccessControlHandlerCfg>
{
  /**
   * The single handler instance.
   */
  private static DefaultAccessControlHandler instance = null;
  /**
   * Create a new default access control handler.
   */
  public DefaultAccessControlHandler()
@@ -204,22 +198,21 @@
   * {@inheritDoc}
   */
  @Override
  public boolean maySend(SearchOperation searchOperation,
                         SearchResultEntry searchEntry)
  public boolean maySend(Operation operation, SearchResultEntry unfilteredEntry)
  {
    return true;
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public SearchResultEntry filterEntry(SearchOperation searchOperation,
                                       SearchResultEntry searchEntry)
  public void filterEntry(Operation operation,
      SearchResultEntry unfilteredEntry, SearchResultEntry filteredEntry)
  {
    // No implementation required.
    return searchEntry;
    return;
  }
@@ -228,19 +221,7 @@
   * {@inheritDoc}
   */
  @Override
  public SearchResultEntry filterEntry(Operation operation, Entry entry)
  {
    // No implementation required.
    return new SearchResultEntry(entry);
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public boolean maySend(DN dn, SearchOperation searchOperation,
  public boolean maySend(DN dn, Operation operation,
                         SearchResultReference searchReference)
  {
    return true;
opends/src/server/org/opends/server/core/SearchOperationBasis.java
@@ -23,6 +23,7 @@
 *
 *
 *      Copyright 2006-2010 Sun Microsystems, Inc.
 *      Portions Copyright 2011 ForgeRock AS
 */
package org.opends.server.core;
@@ -691,12 +692,13 @@
    }
    // Check to see if the entry can be read by the client.
    SearchResultEntry unfilteredSearchEntry = new SearchResultEntry(entry,
        controls);
    if (evaluateAci)
    {
      SearchResultEntry tmpSearchEntry = new SearchResultEntry(entry,
        controls);
      if (AccessControlConfigManager.getInstance()
          .getAccessControlHandler().maySend(this, tmpSearchEntry) == false) {
      if (AccessControlConfigManager.getInstance().getAccessControlHandler()
          .maySend(this, unfilteredSearchEntry) == false)
      {
        return true;
      }
    }
@@ -705,7 +707,7 @@
    // of requested attributes.
    // NOTE: that this copy will include the objectClass attribute.
    Entry entryToReturn =
    Entry filteredEntry =
        entry.filterEntry(getAttributes(), typesOnly,
            isVirtualAttributesOnly(), isRealAttributesOnly());
@@ -721,7 +723,7 @@
      // dealt with later.
      AttributeType attrType = DirectoryServer.getObjectClassAttributeType();
      Iterator<String> ocIterator =
           entryToReturn.getObjectClasses().values().iterator();
           filteredEntry.getObjectClasses().values().iterator();
      while (ocIterator.hasNext())
      {
        String ocName = ocIterator.next();
@@ -735,7 +737,7 @@
      // Next, the set of user attributes (incl. objectClass attribute).
      for (Map.Entry<AttributeType, List<Attribute>> e : entryToReturn
      for (Map.Entry<AttributeType, List<Attribute>> e : filteredEntry
          .getUserAttributes().entrySet())
      {
        AttributeType t = e.getKey();
@@ -762,7 +764,7 @@
      // Then the set of operational attributes.
      for (Map.Entry<AttributeType, List<Attribute>> e : entryToReturn
      for (Map.Entry<AttributeType, List<Attribute>> e : filteredEntry
          .getOperationalAttributes().entrySet())
      {
        AttributeType t = e.getKey();
@@ -790,8 +792,8 @@
    // Convert the provided entry to a search result entry.
    SearchResultEntry searchEntry = new SearchResultEntry(entryToReturn,
                                                          controls);
    SearchResultEntry filteredSearchEntry = new SearchResultEntry(
        filteredEntry, controls);
    // Strip out any attributes that the client does not have access to.
@@ -799,24 +801,24 @@
    // values that the client is not permitted to see.
    if (evaluateAci)
    {
      searchEntry = AccessControlConfigManager.getInstance()
        .getAccessControlHandler().filterEntry(this, searchEntry);
      AccessControlConfigManager.getInstance().getAccessControlHandler()
          .filterEntry(this, unfilteredSearchEntry, filteredSearchEntry);
    }
    // Invoke any search entry plugins that may be registered with the server.
    PluginResult.IntermediateResponse pluginResult =
         DirectoryServer.getPluginConfigManager().
              invokeSearchResultEntryPlugins(this, searchEntry);
              invokeSearchResultEntryPlugins(this, filteredSearchEntry);
    // Send the entry to the client.
    if (pluginResult.sendResponse())
    {
      // Log the entry sent to the client.
      logSearchResultEntry(this, searchEntry);
      logSearchResultEntry(this, filteredSearchEntry);
      try
      {
        sendSearchEntry(searchEntry);
        sendSearchEntry(filteredSearchEntry);
        incrementEntriesSent();
      }
opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendAddOperation.java
@@ -23,6 +23,7 @@
 *
 *
 *      Copyright 2008-2010 Sun Microsystems, Inc.
 *      Portions Copyright 2011 ForgeRock AS
 */
package org.opends.server.workflowelement.localbackend;
@@ -55,7 +56,6 @@
import org.opends.server.api.plugin.PluginResult;
import org.opends.server.controls.LDAPAssertionRequestControl;
import org.opends.server.controls.LDAPPostReadRequestControl;
import org.opends.server.controls.LDAPPostReadResponseControl;
import org.opends.server.controls.PasswordPolicyErrorType;
import org.opends.server.controls.PasswordPolicyResponseControl;
import org.opends.server.controls.ProxiedAuthV1Control;
@@ -263,7 +263,7 @@
        // Invoke any conflict resolution processing that might be needed by the
        // synchronization provider.
        for (SynchronizationProvider provider :
        for (SynchronizationProvider<?> provider :
             DirectoryServer.getSynchronizationProviders())
        {
          try
@@ -643,7 +643,7 @@
          }
          else
          {
            for (SynchronizationProvider provider :
            for (SynchronizationProvider<?> provider :
                 DirectoryServer.getSynchronizationProviders())
            {
              try
@@ -676,11 +676,8 @@
            backend.addEntry(entry, this);
          }
          if (postReadRequest != null)
          {
            addPostReadResponse();
          }
          LocalBackendWorkflowElement.addPostReadResponse(this,
              postReadRequest, entry);
          if (! noOp)
          {
@@ -700,7 +697,7 @@
      }
      finally
      {
        for (SynchronizationProvider provider :
        for (SynchronizationProvider<?> provider :
          DirectoryServer.getSynchronizationProviders())
        {
          try
@@ -1204,7 +1201,7 @@
      // Encode the password.
      if (passwordPolicy.usesAuthPasswordSyntax())
      {
        for (PasswordStorageScheme s : defaultStorageSchemes)
        for (PasswordStorageScheme<?> s : defaultStorageSchemes)
        {
          ByteString encodedValue = s.encodeAuthPassword(value);
          builder.add(AttributeValues.create(
@@ -1213,7 +1210,7 @@
      }
      else
      {
        for (PasswordStorageScheme s : defaultStorageSchemes)
        for (PasswordStorageScheme<?> s : defaultStorageSchemes)
        {
          ByteString encodedValue = s.encodePasswordWithScheme(value);
          builder.add(AttributeValues.create(
@@ -1609,58 +1606,5 @@
      }
    }
  }
  /**
   * Adds the post-read response control to the response.
   */
  protected void addPostReadResponse()
  {
    Entry addedEntry = entry.duplicate(true);
    if (! postReadRequest.allowsAttribute(
               DirectoryServer.getObjectClassAttributeType()))
    {
      addedEntry.removeAttribute(DirectoryServer.getObjectClassAttributeType());
    }
    if (! postReadRequest.returnAllUserAttributes())
    {
      Iterator<AttributeType> iterator =
           addedEntry.getUserAttributes().keySet().iterator();
      while (iterator.hasNext())
      {
        AttributeType attrType = iterator.next();
        if (! postReadRequest.allowsAttribute(attrType))
        {
          iterator.remove();
        }
      }
    }
    if (! postReadRequest.returnAllOperationalAttributes())
    {
      Iterator<AttributeType> iterator =
           addedEntry.getOperationalAttributes().keySet().iterator();
      while (iterator.hasNext())
      {
        AttributeType attrType = iterator.next();
        if (! postReadRequest.allowsAttribute(attrType))
        {
          iterator.remove();
        }
      }
    }
    // Check access controls on the entry and strip out
    // any not allowed attributes.
    SearchResultEntry searchEntry =
      AccessControlConfigManager.getInstance().
      getAccessControlHandler().filterEntry(this, addedEntry);
    LDAPPostReadResponseControl responseControl =
      new LDAPPostReadResponseControl(searchEntry);
    addResponseControl(responseControl);
  }
}
opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendCompareOperation.java
@@ -23,6 +23,7 @@
 *
 *
 *      Copyright 2008-2009 Sun Microsystems, Inc.
 *      Portions Copyright 2011 ForgeRock AS
 */
package org.opends.server.workflowelement.localbackend;
@@ -52,7 +53,6 @@
import static org.opends.messages.CoreMessages.*;
import static org.opends.server.loggers.debug.DebugLogger.*;
import static org.opends.server.util.ServerConstants.*;
import static org.opends.server.util.StaticUtils.*;
opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendDeleteOperation.java
@@ -23,12 +23,12 @@
 *
 *
 *      Copyright 2008-2009 Sun Microsystems, Inc.
 *      Portions Copyright 2011 ForgeRock AS
 */
package org.opends.server.workflowelement.localbackend;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.locks.Lock;
@@ -46,7 +46,6 @@
import org.opends.server.core.PersistentSearch;
import org.opends.server.core.PluginConfigManager;
import org.opends.server.loggers.debug.DebugTracer;
import org.opends.server.types.AttributeType;
import org.opends.server.types.CanceledOperationException;
import org.opends.server.types.Control;
import org.opends.server.types.DebugLogLevel;
@@ -57,7 +56,6 @@
import org.opends.server.types.Privilege;
import org.opends.server.types.ResultCode;
import org.opends.server.types.SearchFilter;
import org.opends.server.types.SearchResultEntry;
import org.opends.server.types.SynchronizationProviderResult;
import org.opends.server.types.operation.PostOperationDeleteOperation;
import org.opends.server.types.operation.PostResponseDeleteOperation;
@@ -410,7 +408,8 @@
          }
          processPreReadControl();
          LocalBackendWorkflowElement.addPreReadResponse(this,
              preReadRequest, entry);
          if (! noOp)
@@ -662,62 +661,6 @@
  /**
   * Performs any processing needed for the LDAP pre-read control.
   */
  protected void processPreReadControl()
  {
    if (preReadRequest != null)
    {
      Entry entryCopy = entry.duplicate(true);
      if (! preReadRequest.allowsAttribute(
                 DirectoryServer.getObjectClassAttributeType()))
      {
        entryCopy.removeAttribute(
             DirectoryServer.getObjectClassAttributeType());
      }
      if (! preReadRequest.returnAllUserAttributes())
      {
        Iterator<AttributeType> iterator =
             entryCopy.getUserAttributes().keySet().iterator();
        while (iterator.hasNext())
        {
          AttributeType attrType = iterator.next();
          if (! preReadRequest.allowsAttribute(attrType))
          {
            iterator.remove();
          }
        }
      }
      if (! preReadRequest.returnAllOperationalAttributes())
      {
        Iterator<AttributeType> iterator =
             entryCopy.getOperationalAttributes().keySet().iterator();
        while (iterator.hasNext())
        {
          AttributeType attrType = iterator.next();
          if (! preReadRequest.allowsAttribute(attrType))
          {
            iterator.remove();
          }
        }
      }
      // Check access controls on the entry and strip out
      // any not allowed attributes.
      SearchResultEntry searchEntry =
        AccessControlConfigManager.getInstance().
        getAccessControlHandler().filterEntry(this, entryCopy);
      LDAPPreReadResponseControl responseControl =
           new LDAPPreReadResponseControl(preReadRequest.isCritical(),
                                          searchEntry);
      addResponseControl(responseControl);
    }
  }
  /**
   * Handle conflict resolution.
   * @return  {@code true} if processing should continue for the operation, or
   *          {@code false} if not.
opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendModifyDNOperation.java
@@ -23,12 +23,12 @@
 *
 *
 *      Copyright 2008-2010 Sun Microsystems, Inc.
 *      Portions Copyright 2011 ForgeRock AS
 */
package org.opends.server.workflowelement.localbackend;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.locks.Lock;
@@ -42,9 +42,7 @@
import org.opends.server.api.plugin.PluginResult;
import org.opends.server.controls.LDAPAssertionRequestControl;
import org.opends.server.controls.LDAPPostReadRequestControl;
import org.opends.server.controls.LDAPPostReadResponseControl;
import org.opends.server.controls.LDAPPreReadRequestControl;
import org.opends.server.controls.LDAPPreReadResponseControl;
import org.opends.server.controls.ProxiedAuthV1Control;
import org.opends.server.controls.ProxiedAuthV2Control;
import org.opends.server.core.AccessControlConfigManager;
@@ -71,7 +69,6 @@
import org.opends.server.types.RDN;
import org.opends.server.types.ResultCode;
import org.opends.server.types.SearchFilter;
import org.opends.server.types.SearchResultEntry;
import org.opends.server.types.SynchronizationProviderResult;
import org.opends.server.types.operation.PostOperationModifyDNOperation;
import org.opends.server.types.operation.PostResponseModifyDNOperation;
@@ -631,7 +628,10 @@
          // Attach the pre-read and/or post-read controls to the response if
          // appropriate.
          processReadEntryControls();
          LocalBackendWorkflowElement.addPreReadResponse(this,
              preReadRequest, currentEntry);
          LocalBackendWorkflowElement.addPostReadResponse(this,
              postReadRequest, newEntry);
          if (! noOp)
@@ -1077,114 +1077,6 @@
  /**
   * Performs any necessary processing to create the pre-read and/or post-read
   * response controls and attach them to the response.
   */
  protected void processReadEntryControls()
  {
    if (preReadRequest != null)
    {
      Entry entry = currentEntry.duplicate(true);
      if (! preReadRequest.allowsAttribute(
                 DirectoryServer.getObjectClassAttributeType()))
      {
        entry.removeAttribute(
             DirectoryServer.getObjectClassAttributeType());
      }
      if (! preReadRequest.returnAllUserAttributes())
      {
        Iterator<AttributeType> iterator =
             entry.getUserAttributes().keySet().iterator();
        while (iterator.hasNext())
        {
          AttributeType attrType = iterator.next();
          if (! preReadRequest.allowsAttribute(attrType))
          {
            iterator.remove();
          }
        }
      }
      if (! preReadRequest.returnAllOperationalAttributes())
      {
        Iterator<AttributeType> iterator =
             entry.getOperationalAttributes().keySet().iterator();
        while (iterator.hasNext())
        {
          AttributeType attrType = iterator.next();
          if (! preReadRequest.allowsAttribute(attrType))
          {
            iterator.remove();
          }
        }
      }
      // Check access controls on the entry and strip out
      // any not allowed attributes.
      SearchResultEntry searchEntry =
        AccessControlConfigManager.getInstance().
        getAccessControlHandler().filterEntry(this, entry);
      LDAPPreReadResponseControl responseControl =
           new LDAPPreReadResponseControl(preReadRequest.isCritical(),
                                          searchEntry);
      addResponseControl(responseControl);
    }
    if (postReadRequest != null)
    {
      Entry entry = newEntry.duplicate(true);
      if (! postReadRequest.allowsAttribute(
                 DirectoryServer.getObjectClassAttributeType()))
      {
        entry.removeAttribute(
             DirectoryServer.getObjectClassAttributeType());
      }
      if (! postReadRequest.returnAllUserAttributes())
      {
        Iterator<AttributeType> iterator =
             entry.getUserAttributes().keySet().iterator();
        while (iterator.hasNext())
        {
          AttributeType attrType = iterator.next();
          if (! postReadRequest.allowsAttribute(attrType))
          {
            iterator.remove();
          }
        }
      }
      if (! postReadRequest.returnAllOperationalAttributes())
      {
        Iterator<AttributeType> iterator =
             entry.getOperationalAttributes().keySet().iterator();
        while (iterator.hasNext())
        {
          AttributeType attrType = iterator.next();
          if (! postReadRequest.allowsAttribute(attrType))
          {
            iterator.remove();
          }
        }
      }
      // Check access controls on the entry and strip out
      // any not allowed attributes.
      SearchResultEntry searchEntry =
        AccessControlConfigManager.getInstance().
        getAccessControlHandler().filterEntry(this, entry);
      LDAPPostReadResponseControl responseControl =
           new LDAPPostReadResponseControl(searchEntry);
      addResponseControl(responseControl);
    }
  }
  /**
   * Handle conflict resolution.
   * @return  {@code true} if processing should continue for the operation, or
   *          {@code false} if not.
opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendModifyOperation.java
@@ -23,6 +23,7 @@
 *
 *
 *      Copyright 2008-2009 Sun Microsystems, Inc.
 *      Portions Copyright 2011 ForgeRock AS
 */
package org.opends.server.workflowelement.localbackend;
@@ -52,9 +53,7 @@
import org.opends.server.api.plugin.PluginResult;
import org.opends.server.controls.LDAPAssertionRequestControl;
import org.opends.server.controls.LDAPPostReadRequestControl;
import org.opends.server.controls.LDAPPostReadResponseControl;
import org.opends.server.controls.LDAPPreReadRequestControl;
import org.opends.server.controls.LDAPPreReadResponseControl;
import org.opends.server.controls.PasswordPolicyErrorType;
import org.opends.server.controls.PasswordPolicyResponseControl;
import org.opends.server.controls.ProxiedAuthV1Control;
@@ -652,7 +651,10 @@
          // Handle any processing that may be needed for the pre-read and/or
          // post-read controls.
          handleReadEntryProcessing();
          LocalBackendWorkflowElement.addPreReadResponse(this,
              preReadRequest, currentEntry);
          LocalBackendWorkflowElement.addPostReadResponse(this,
              postReadRequest, modifiedEntry);
          if (! noOp)
@@ -2108,115 +2110,6 @@
  /**
   * Handles any processing that is required for the LDAP pre-read and/or
   * post-read controls.
   */
  protected void handleReadEntryProcessing()
  {
    if (preReadRequest != null)
    {
      Entry entry = currentEntry.duplicate(true);
      if (! preReadRequest.allowsAttribute(
                 DirectoryServer.getObjectClassAttributeType()))
      {
        entry.removeAttribute(
                   DirectoryServer.getObjectClassAttributeType());
      }
      if (! preReadRequest.returnAllUserAttributes())
      {
        Iterator<AttributeType> iterator =
             entry.getUserAttributes().keySet().iterator();
        while (iterator.hasNext())
        {
          AttributeType attrType = iterator.next();
          if (! preReadRequest.allowsAttribute(attrType))
          {
            iterator.remove();
          }
        }
      }
      if (! preReadRequest.returnAllOperationalAttributes())
      {
        Iterator<AttributeType> iterator =
             entry.getOperationalAttributes().keySet().iterator();
        while (iterator.hasNext())
        {
          AttributeType attrType = iterator.next();
          if (! preReadRequest.allowsAttribute(attrType))
          {
            iterator.remove();
          }
        }
      }
      // Check access controls on the entry and strip out
      // any not allowed attributes.
      SearchResultEntry searchEntry =
        AccessControlConfigManager.getInstance().
        getAccessControlHandler().filterEntry(this, entry);
      LDAPPreReadResponseControl responseControl =
           new LDAPPreReadResponseControl(preReadRequest.isCritical(),
                                          searchEntry);
      getResponseControls().add(responseControl);
    }
    if (postReadRequest != null)
    {
      Entry entry = modifiedEntry.duplicate(true);
      if (! postReadRequest.allowsAttribute(
                 DirectoryServer.getObjectClassAttributeType()))
      {
        entry.removeAttribute(
                   DirectoryServer.getObjectClassAttributeType());
      }
      if (! postReadRequest.returnAllUserAttributes())
      {
        Iterator<AttributeType> iterator =
             entry.getUserAttributes().keySet().iterator();
        while (iterator.hasNext())
        {
          AttributeType attrType = iterator.next();
          if (! postReadRequest.allowsAttribute(attrType))
          {
            iterator.remove();
          }
        }
      }
      if (! postReadRequest.returnAllOperationalAttributes())
      {
        Iterator<AttributeType> iterator =
             entry.getOperationalAttributes().keySet().iterator();
        while (iterator.hasNext())
        {
          AttributeType attrType = iterator.next();
          if (! postReadRequest.allowsAttribute(attrType))
          {
            iterator.remove();
          }
        }
      }
      // Check access controls on the entry and strip out
      // any not allowed attributes.
      SearchResultEntry searchEntry =
        AccessControlConfigManager.getInstance().
        getAccessControlHandler().filterEntry(this, entry);
      LDAPPostReadResponseControl responseControl =
           new LDAPPostReadResponseControl(searchEntry);
      getResponseControls().add(responseControl);
    }
  }
  /**
   * Handle conflict resolution.
   * @return  {@code true} if processing should continue for the operation, or
   *          {@code false} if not.
opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElement.java
@@ -23,12 +23,14 @@
 *
 *
 *      Copyright 2008-2010 Sun Microsystems, Inc.
 *      Portions Copyright 2011 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;
@@ -41,15 +43,11 @@
import org.opends.server.admin.std.server.RootCfg;
import org.opends.server.api.Backend;
import org.opends.server.config.ConfigException;
import org.opends.server.core.AddOperation;
import org.opends.server.core.BindOperation;
import org.opends.server.core.CompareOperation;
import org.opends.server.core.DeleteOperation;
import org.opends.server.core.DirectoryServer;
import org.opends.server.core.ModifyDNOperation;
import org.opends.server.core.ModifyOperation;
import org.opends.server.core.PersistentSearch;
import org.opends.server.core.SearchOperation;
import org.opends.server.controls.LDAPPostReadRequestControl;
import org.opends.server.controls.LDAPPostReadResponseControl;
import org.opends.server.controls.LDAPPreReadRequestControl;
import org.opends.server.controls.LDAPPreReadResponseControl;
import org.opends.server.core.*;
import org.opends.server.types.*;
import org.opends.server.workflowelement.LeafWorkflowElement;
@@ -326,6 +324,160 @@
  /**
   * Adds the post-read response control to the response if requested.
   *
   * @param operation
   *          The update operation.
   * @param postReadRequest
   *          The request control, if present.
   * @param entry
   *          The post-update entry.
   */
  static void addPostReadResponse(final Operation operation,
      final LDAPPostReadRequestControl postReadRequest, final Entry entry)
  {
    if (postReadRequest == null)
    {
      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);
    if (AccessControlConfigManager.getInstance().getAccessControlHandler()
        .maySend(operation, unfilteredEntry) == false)
    {
      return;
    }
    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);
  }
  /**
   * Adds the pre-read response control to the response if requested.
   *
   * @param operation
   *          The update operation.
   * @param preReadRequest
   *          The request control, if present.
   * @param entry
   *          The pre-update entry.
   */
  static void addPreReadResponse(final Operation operation,
      final LDAPPreReadRequestControl preReadRequest, final Entry entry)
  {
    if (preReadRequest == null)
    {
      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);
    if (AccessControlConfigManager.getInstance().getAccessControlHandler()
        .maySend(operation, unfilteredEntry) == false)
    {
      return;
    }
    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);
  }
  /**
   * Registers a local backend with the server.
   *
   * @param localBackend  the local backend to register with the server
opends/tests/unit-tests-testng/src/server/org/opends/server/authorization/dseecompat/AciTests.java
@@ -2081,7 +2081,7 @@
   * @throws Exception
   *           If an unexpected exception occurred.
   */
  @Test(enabled=false)
  @Test
  public void testSearchTargetFilterAndAttributes() throws Exception
  {
    addEntries(BASIC_LDIF__GROUP_SEARCH_TESTS, DIR_MGR_DN, DIR_MGR_PW);