From 898c594c58fc6f019e732c46d14fda4cb8be05b5 Mon Sep 17 00:00:00 2001
From: matthew_swift <matthew_swift@localhost>
Date: Thu, 23 Apr 2009 18:44:44 +0000
Subject: [PATCH] Fix formatting.

---
 opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciHandler.java | 2564 +++++++++++++++++++++++++++++++++-------------------------
 1 files changed, 1,455 insertions(+), 1,109 deletions(-)

diff --git a/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciHandler.java b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciHandler.java
index ee94d81..8e8d4c7 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciHandler.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciHandler.java
@@ -24,60 +24,101 @@
  *
  *      Copyright 2008-2009 Sun Microsystems, Inc.
  */
-
 package org.opends.server.authorization.dseecompat;
-import org.opends.messages.Message;
 
-import static org.opends.server.authorization.dseecompat.Aci.*;
-import static org.opends.server.config.ConfigConstants.ATTR_AUTHZ_GLOBAL_ACI;
-import static org.opends.server.loggers.ErrorLogger.logError;
-import static org.opends.server.loggers.debug.DebugLogger.debugEnabled;
-import static org.opends.server.loggers.debug.DebugLogger.getTracer;
+
+
 import static org.opends.messages.AccessControlMessages.*;
-import static org.opends.server.schema.SchemaConstants.SYNTAX_DN_OID;
+import static org.opends.server.authorization.dseecompat.Aci.*;
+import static org.opends.server.config.ConfigConstants.*;
+import static org.opends.server.loggers.ErrorLogger.*;
+import static org.opends.server.loggers.debug.DebugLogger.*;
+import static org.opends.server.schema.SchemaConstants.*;
 import static org.opends.server.util.ServerConstants.*;
-import static org.opends.server.util.StaticUtils.toLowerCase;
-import java.util.*;
+import static org.opends.server.util.StaticUtils.*;
+
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.SortedSet;
 import java.util.concurrent.locks.Lock;
+
+import org.opends.messages.Message;
 import org.opends.server.admin.std.server.DseeCompatAccessControlHandlerCfg;
 import org.opends.server.api.AccessControlHandler;
 import org.opends.server.api.ClientConnection;
+import org.opends.server.backends.jeb.EntryContainer;
 import org.opends.server.config.ConfigException;
-import org.opends.server.core.*;
+import org.opends.server.controls.GetEffectiveRightsRequestControl;
+import org.opends.server.core.DirectoryServer;
+import org.opends.server.core.ExtendedOperation;
+import org.opends.server.core.SearchOperation;
 import org.opends.server.loggers.debug.DebugTracer;
 import org.opends.server.protocols.internal.InternalClientConnection;
 import org.opends.server.protocols.internal.InternalSearchOperation;
 import org.opends.server.protocols.ldap.LDAPControl;
-import org.opends.server.types.*;
+import org.opends.server.types.Attribute;
+import org.opends.server.types.AttributeBuilder;
+import org.opends.server.types.AttributeType;
+import org.opends.server.types.AttributeValue;
+import org.opends.server.types.AttributeValues;
+import org.opends.server.types.AuthenticationInfo;
+import org.opends.server.types.Control;
+import org.opends.server.types.DN;
+import org.opends.server.types.DebugLogLevel;
+import org.opends.server.types.DereferencePolicy;
+import org.opends.server.types.DirectoryException;
+import org.opends.server.types.Entry;
+import org.opends.server.types.InitializationException;
+import org.opends.server.types.LockManager;
+import org.opends.server.types.Modification;
+import org.opends.server.types.ModificationType;
+import org.opends.server.types.Operation;
+import org.opends.server.types.Privilege;
+import org.opends.server.types.RDN;
+import org.opends.server.types.SearchFilter;
+import org.opends.server.types.SearchResultEntry;
+import org.opends.server.types.SearchResultReference;
+import org.opends.server.types.SearchScope;
 import org.opends.server.workflowelement.localbackend.*;
-import org.opends.server.controls.GetEffectiveRightsRequestControl;
-import org.opends.server.backends.jeb.EntryContainer;
+
 
 
 /**
- * The AciHandler class performs the main processing for the dseecompat package.
+ * The AciHandler class performs the main processing for the dseecompat
+ * package.
  */
-public class AciHandler
-       extends AccessControlHandler<DseeCompatAccessControlHandlerCfg>
+public class AciHandler extends
+    AccessControlHandler<DseeCompatAccessControlHandlerCfg>
 {
   /**
-   * The tracer object for the debug logger.
+   * 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.
    */
-  private static final DebugTracer TRACER = getTracer();
-
+  public static final String ALL_ATTRS_RESOURCE_ENTRY =
+      "allAttrsResourceEntry";
 
   /**
-   * The list that holds that ACIs keyed by the DN of the entry
-   * holding the ACI.
+   * String used to indicate that the evaluating ACI had a all
+   * operational attributes targetattr match (targetattr="+").
    */
-  private AciList aciList;
+  public static final String ALL_OP_ATTRS_MATCHED = "allOpAttrsMatched";
 
   /**
-   * The listener that handles ACI changes caused by LDAP operations, ACI
-   * decode failure alert logging and backend initialization ACI list
-   * adjustment.
+   * String used to indicate that the evaluating ACI had a all user
+   * attributes targetattr match (targetattr="*").
    */
-  private AciListenerManager aciListenerMgr;
+  public static final String ALL_USER_ATTRS_MATCHED =
+      "allUserAttrsMatched";
+
+  /**
+   * String used to save the original authorization entry in an
+   * operation attachment if a proxied authorization control was seen.
+   */
+  public static final String ORIG_AUTH_ENTRY = "origAuthorizationEntry";
 
   /**
    * Attribute type corresponding to "aci" attribute.
@@ -85,122 +126,156 @@
   static AttributeType aciType;
 
   /**
-   * Attribute type corresponding to global "ds-cfg-global-aci" attribute.
+   * Attribute type corresponding to global "ds-cfg-global-aci"
+   * attribute.
    */
   static AttributeType globalAciType;
 
   /**
    * Attribute type corresponding to "debugsearchindex" attribute.
    */
-  static AttributeType debugSearchIndex;
+  private static AttributeType debugSearchIndex;
+
+  /*
+   * DN corresponding to "debugsearchindex" attribute type.
+   */
+  private static DN debugSearchIndexDN;
 
   /**
-   * Attribute type corresponding to the "ref" attribute type. Used in the
-   * search reference access check.
+   * Attribute type corresponding to the "ref" attribute type. Used in
+   * the search reference access check.
    */
-  static AttributeType refAttrType;
-
- /*
-  * DN corresponding to "debugsearchindex" attribute type.
-  */
-  static DN debugSearchIndexDN;
-
+  private static AttributeType refAttrType;
 
   /**
-   * String used to save the original authorization entry in an operation
-   * attachment if a proxied authorization control was seen.
+   * The tracer object for the debug logger.
    */
-  public static final String ORIG_AUTH_ENTRY="origAuthorizationEntry";
+  private static final DebugTracer TRACER = getTracer();
 
-  /**
-   * 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";
+  static
+  {
+    initStatics();
+  }
 
-  /**
-   * String used to indicate that the evaluating ACI had a all user attributes
-   * targetattr match (targetattr="*").
-   */
-   public static final String ALL_USER_ATTRS_MATCHED = "allUserAttrsMatched";
 
-  /**
-   * String used to indicate that the evaluating ACI had a all operational
-   * attributes targetattr match (targetattr="+").
-   */
-   public static final String ALL_OP_ATTRS_MATCHED = "allOpAttrsMatched";
 
-   static {
-     initStatics();
-   }
-
-  // We initialize these for each new AciHandler so that we can clear out
-  // the stale references that can occur during an in-core restart.
+  // We initialize these for each new AciHandler so that we can clear
+  // out the stale references that can occur during an in-core restart.
   private static void initStatics()
   {
-    if((aciType = DirectoryServer.getAttributeType("aci")) == null)
+    if ((aciType = DirectoryServer.getAttributeType("aci")) == null)
     {
       aciType = DirectoryServer.getDefaultAttributeType("aci");
     }
 
-    if((globalAciType =
-            DirectoryServer.getAttributeType(ATTR_AUTHZ_GLOBAL_ACI)) == null)
+    if ((globalAciType =
+        DirectoryServer.getAttributeType(ATTR_AUTHZ_GLOBAL_ACI)) == null)
     {
       globalAciType =
-              DirectoryServer.getDefaultAttributeType(ATTR_AUTHZ_GLOBAL_ACI);
+          DirectoryServer
+              .getDefaultAttributeType(ATTR_AUTHZ_GLOBAL_ACI);
     }
 
-     if((debugSearchIndex =
-          DirectoryServer.
-              getAttributeType(EntryContainer.ATTR_DEBUG_SEARCH_INDEX)) == null)
-     {
-       debugSearchIndex =
-       DirectoryServer.
-               getDefaultAttributeType(EntryContainer.ATTR_DEBUG_SEARCH_INDEX);
-     }
+    if ((debugSearchIndex =
+        DirectoryServer
+            .getAttributeType(EntryContainer.ATTR_DEBUG_SEARCH_INDEX)) == null)
+    {
+      debugSearchIndex =
+          DirectoryServer
+              .getDefaultAttributeType(EntryContainer.ATTR_DEBUG_SEARCH_INDEX);
+    }
 
-     if((refAttrType =
-             DirectoryServer.
-                     getAttributeType(ATTR_REFERRAL_URL)) == null) {
-       refAttrType =
-               DirectoryServer.
-                       getDefaultAttributeType(ATTR_REFERRAL_URL);
-     }
-     try {
-       debugSearchIndexDN=DN.decode("cn=debugsearch");
-     } catch (DirectoryException ex) {
-       //Should never happen.
-     }
+    if ((refAttrType =
+        DirectoryServer.getAttributeType(ATTR_REFERRAL_URL)) == null)
+    {
+      refAttrType =
+          DirectoryServer.getDefaultAttributeType(ATTR_REFERRAL_URL);
+    }
+    try
+    {
+      debugSearchIndexDN = DN.decode("cn=debugsearch");
+    }
+    catch (DirectoryException ex)
+    {
+      // Should never happen.
+    }
   }
 
+
+
+  /**
+   * The list that holds that ACIs keyed by the DN of the entry holding
+   * the ACI.
+   */
+  private AciList aciList;
+
+  /**
+   * The listener that handles ACI changes caused by LDAP operations,
+   * ACI decode failure alert logging and backend initialization ACI
+   * list adjustment.
+   */
+  private AciListenerManager aciListenerMgr;
+
+
+
   /**
    * Creates a new DSEE-compatible access control handler.
    */
   public AciHandler()
   {
-    // No implementation required.  All initialization should be done in the
-    // intializeAccessControlHandler method.
+    // No implementation required. All initialization should be done in
+    // the intializeAccessControlHandler method.
   }
 
 
 
-  /**
-   * {@inheritDoc}
+  /*
+   * TODO Rename this method. Needs to be changed in SearchOperation. I
+   * find the name of the filterEntry method to be misleading because it
+   * works on a search operation but has nothing to do with the search
+   * filter. Something like "removeDisallowedAttributes" would be
+   * clearer.
    */
-  @Override()
-  public void initializeAccessControlHandler(
-                   DseeCompatAccessControlHandlerCfg configuration)
-         throws ConfigException, InitializationException
+
+  /**
+   * Checks access on each attribute in an entry. It removes those
+   * attributes that fail access check.
+   *
+   * @param operation
+   *          The search operation class containing information to check
+   *          access on.
+   * @param entry
+   *          The entry containing the attributes.
+   * @return The entry to return minus filtered attributes.
+   */
+  @Override
+  public SearchResultEntry filterEntry(SearchOperation operation,
+      SearchResultEntry entry)
   {
-    initStatics();
-    DN configurationDN=configuration.dn();
-    aciList = new AciList(configurationDN);
-    aciListenerMgr = new AciListenerManager(aciList, configurationDN);
-    processGlobalAcis(configuration);
-    processConfigAcis();
-    DirectoryServer.registerSupportedControl(OID_GET_EFFECTIVE_RIGHTS);
+    AciLDAPOperationContainer operationContainer =
+        new AciLDAPOperationContainer(operation, (ACI_READ), entry);
+    // 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);
+    }
+    else
+    {
+      returnEntry = entry;
+    }
+    if (operationContainer.hasGetEffectiveRightsControl())
+    {
+      returnEntry =
+          AciEffectiveRights.addRightsToEntry(this, operation
+              .getAttributes(), operationContainer, returnEntry,
+              skipCheck);
+    }
+    return returnEntry;
   }
 
 
@@ -213,1086 +288,1357 @@
   {
     aciListenerMgr.finalizeListenerManager();
     AciEffectiveRights.finalizeOnShutdown();
-    DirectoryServer.deregisterSupportedControl(OID_GET_EFFECTIVE_RIGHTS);
+    DirectoryServer
+        .deregisterSupportedControl(OID_GET_EFFECTIVE_RIGHTS);
   }
 
 
 
-    /**
-     * Process all global ACI attribute types found in the configuration
-     * entry and adds them to that ACI list cache. It also logs messages about
-     * the number of ACI attribute types added to the cache. This method is
-     * called once at startup.  It also will put the server into  lockdown
-     * mode if needed.
-     *
-     * @param configuration   The config handler containing the ACI
-     *  configuration information.
-     * @throws InitializationException If there is an error reading
-     * the global ACIs from the configuration entry.
-     */
-    private void processGlobalAcis(
-            DseeCompatAccessControlHandlerCfg configuration)
-            throws InitializationException {
-      SortedSet<Aci> globalAcis = configuration.getGlobalACI();
-      try {
-        if (globalAcis != null)   {
-          aciList.addAci(DN.nullDN(),globalAcis);
-          Message message = INFO_ACI_ADD_LIST_GLOBAL_ACIS.get(
-                  Integer.toString(globalAcis.size()));
-          logError(message);
-        }
-      }  catch (Exception e) {
-        if (debugEnabled())
-          TRACER.debugCaught(DebugLogLevel.ERROR, e);
-        Message message = INFO_ACI_HANDLER_FAIL_PROCESS_GLOBAL_ACI.
-                get(String.valueOf(configuration.dn()));
-        throw new InitializationException(message, e);
-      }
-    }
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public void initializeAccessControlHandler(
+      DseeCompatAccessControlHandlerCfg configuration)
+      throws ConfigException, InitializationException
+  {
+    initStatics();
+    DN configurationDN = configuration.dn();
+    aciList = new AciList(configurationDN);
+    aciListenerMgr = new AciListenerManager(aciList, configurationDN);
+    processGlobalAcis(configuration);
+    processConfigAcis();
+    DirectoryServer.registerSupportedControl(OID_GET_EFFECTIVE_RIGHTS);
+  }
 
-    /**
-     * Process all ACIs under the "cn=config" naming context and adds them to
-     * the ACI list cache. It also logs messages about the number of ACIs added
-     * to the cache. This method is called once at startup.  It will put the
-     * server in lockdown mode if needed.
-     *
-     * @throws InitializationException If there is an error searching for
-     * the ACIs in the naming context.
-     */
-    private void processConfigAcis() throws InitializationException {
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public boolean isAllowed(DN entryDN, Operation op, Control control)
+  {
+    boolean ret;
+    if (!(ret = skipAccessCheck(op)))
+    {
+      Entry e = new Entry(entryDN, null, null, null);
+      AciLDAPOperationContainer operationContainer =
+          new AciLDAPOperationContainer(op, e, control,
+              (ACI_READ | ACI_CONTROL));
+      ret = accessAllowed(operationContainer);
+    }
+    if (control.getOID().equals(OID_PROXIED_AUTH_V2)
+        || control.getOID().equals(OID_PROXIED_AUTH_V1))
+    {
+      op.setAttachment(ORIG_AUTH_ENTRY, op.getAuthorizationEntry());
+    }
+    else if (control.getOID().equals(OID_GET_EFFECTIVE_RIGHTS))
+    {
       try
       {
-        DN configDN=DN.decode("cn=config");
-        LinkedHashSet<String> attrs = new LinkedHashSet<String>(1);
-        attrs.add("aci");
-        LinkedList<Message>failedACIMsgs=new LinkedList<Message>();
-        InternalClientConnection conn =
-                InternalClientConnection.getRootConnection();
-        InternalSearchOperation op = conn.processSearch(configDN,
-                SearchScope.WHOLE_SUBTREE,
-                DereferencePolicy.NEVER_DEREF_ALIASES, 0, 0, false,
-                SearchFilter.createFilterFromString("aci=*"), attrs);
-        if(!op.getSearchEntries().isEmpty()) {
-          int validAcis =
-                  aciList.addAci(op.getSearchEntries(), failedACIMsgs);
-          if(!failedACIMsgs.isEmpty())
-            aciListenerMgr.logMsgsSetLockDownMode(failedACIMsgs);
-          Message message = INFO_ACI_ADD_LIST_ACIS.get(
-                  Integer.toString(validAcis), String.valueOf(configDN));
-          logError(message);
-        }
-      } catch (DirectoryException e) {
-        Message message = INFO_ACI_HANDLER_FAIL_PROCESS_ACI.get();
-        throw new InitializationException(message, e);
-      }
-    }
-
-
-    /**
-     * Checks to see if a LDAP modification is allowed access.
-     *
-     * @param container  The structure containing the LDAP modifications
-     * @param operation The operation to check modify privileges on.
-     * operation to check and the evaluation context to apply the check against.
-     * @param skipAccessCheck True if access checking should be skipped.
-     * @return  True if access is allowed.
-     */
-    private boolean aciCheckMods(AciLDAPOperationContainer container,
-                                 LocalBackendModifyOperation operation,
-                                 boolean skipAccessCheck) {
-        Entry resourceEntry=container.getResourceEntry();
-        DN dn=resourceEntry.getDN();
-        List<Modification> modifications=container.getModifications();
-        for(Modification m : modifications) {
-            Attribute modAttr=m.getAttribute();
-            AttributeType modAttrType=modAttr.getAttributeType();
-
-            if(modAttrType.equals(aciType)) {
-              /*
-               * Check that the operation has modify privileges if
-               * it contains an "aci" attribute type.
-               */
-              if (!operation.getClientConnection().
-                   hasPrivilege(Privilege.MODIFY_ACL, operation)) {
-                Message message = INFO_ACI_MODIFY_FAILED_PRIVILEGE.
-                    get(String.valueOf(container.getResourceDN()),
-                        String.valueOf(container.getClientDN()));
-                logError(message);
-                return false;
-              }
-            }
-            //This access check handles the case where all attributes of this
-            //type are being replaced or deleted. If only a subset is being
-            //deleted than this access check is skipped.
-            ModificationType modType=m.getModificationType();
-            if((modType == ModificationType.DELETE &&
-                modAttr.isEmpty()) ||
-               (modType == ModificationType.REPLACE ||
-                modType == ModificationType.INCREMENT)) {
-                                /*
-                 * Check if we have rights to delete all values of
-                 * an attribute type in the resource entry.
-                 */
-              if(resourceEntry.hasAttribute(modAttrType)) {
-                container.setCurrentAttributeType(modAttrType);
-                List<Attribute> attrList =
-                   resourceEntry.getAttribute(modAttrType,modAttr.getOptions());
-                if(attrList != null) {
-                  for (Attribute a : attrList) {
-                    for (AttributeValue v : a) {
-                      container.setCurrentAttributeValue(v);
-                      container.setRights(ACI_WRITE_DELETE);
-                      if(!skipAccessCheck &&
-                              !accessAllowed(container))
-                        return false;
-                    }
-                  }
-                }
-              }
-            }
-
-            if(!modAttr.isEmpty()) {
-               for(AttributeValue v : modAttr) {
-                   container.setCurrentAttributeType(modAttrType);
-                   switch (m.getModificationType())
-                   {
-                     case ADD:
-                     case REPLACE:
-                       container.setCurrentAttributeValue(v);
-                       container.setRights(ACI_WRITE_ADD);
-                       if(!skipAccessCheck && !accessAllowed(container))
-                           return false;
-                       break;
-                     case DELETE:
-                       container.setCurrentAttributeValue(v);
-                       container.setRights(ACI_WRITE_DELETE);
-                       if(!skipAccessCheck && !accessAllowed(container))
-                           return false;
-                       break;
-                     case INCREMENT:
-                       Entry modifiedEntry = operation.getModifiedEntry();
-                       List<Attribute> modifiedAttrs =
-                            modifiedEntry.getAttribute(modAttrType,
-                                                       modAttr.getOptions());
-                       if (modifiedAttrs != null)
-                       {
-                         for (Attribute attr : modifiedAttrs)
-                         {
-                           for (AttributeValue val : attr)
-                           {
-                             container.setCurrentAttributeValue(val);
-                             container.setRights(ACI_WRITE_ADD);
-                             if(!skipAccessCheck && !accessAllowed(container))
-                                 return false;
-                           }
-                         }
-                       }
-                       break;
-                   }
-                  /*
-                   Check if the modification type has an "aci" attribute type.
-                   If so, check the syntax of that attribute value. Fail the
-                   the operation if the syntax check fails.
-                   */
-                   if(modAttrType.equals(aciType)  ||
-                      modAttrType.equals(globalAciType)) {
-                       try {
-                           //A global ACI needs a NULL DN, not the DN of the
-                           //modification.
-                           if(modAttrType.equals(globalAciType))
-                               dn=DN.nullDN();
-                           Aci.decode(v.getValue(),dn);
-                       } catch (AciException ex) {
-                           Message message = WARN_ACI_MODIFY_FAILED_DECODE.get(
-                               String.valueOf(dn), ex.getMessage());
-                           logError(message);
-                           return false;
-                       }
-                   }
-               }
-            }
-        }
-        return true;
-    }
-
-    /**
-     * Performs the test of the deny and allow access lists using the
-     * provided evaluation context. The deny list is checked first.
-     *
-     * @param evalCtx  The evaluation context to use.
-     * @return  True if access is allowed.
-     */
-    private boolean testApplicableLists(AciEvalContext evalCtx) {
-        EnumEvalResult res;
-        evalCtx.setEvalReason(EnumEvalReason.NO_REASON);
-        LinkedList<Aci>denys=evalCtx.getDenyList();
-        LinkedList<Aci>allows=evalCtx.getAllowList();
-        //If allows list is empty and not doing geteffectiverights return
-        //false.
-        if(allows.isEmpty() && !(evalCtx.isGetEffectiveRightsEval() &&
-              !evalCtx.hasRights(ACI_SELF) &&
-              evalCtx.isTargAttrFilterMatchAciEmpty())) {
-          evalCtx.setEvalReason(EnumEvalReason.NO_ALLOW_ACIS);
-          evalCtx.setDecidingAci(null);
-          return false;
-        }
-        evalCtx.setDenyEval(true);
-        for(Aci denyAci : denys) {
-           res=Aci.evaluate(evalCtx, denyAci);
-            //Failure could be returned if a system limit is hit or
-            //search fails
-           if(res.equals(EnumEvalResult.FAIL)) {
-              evalCtx.setEvalReason(EnumEvalReason.EVALUATED_DENY_ACI);
-              evalCtx.setDecidingAci(denyAci);
-              return false;
-          } else if (res.equals(EnumEvalResult.TRUE)) {
-              if(evalCtx.isGetEffectiveRightsEval() &&
-                 !evalCtx.hasRights(ACI_SELF) &&
-                 !evalCtx.isTargAttrFilterMatchAciEmpty()) {
-                  //Iterate to next only if deny ACI contains a targattrfilters
-                  //keyword.
-                  if(AciEffectiveRights.setTargAttrAci(evalCtx, denyAci, true))
-                    continue;
-                evalCtx.setEvalReason(EnumEvalReason.EVALUATED_DENY_ACI);
-                evalCtx.setDecidingAci(denyAci);
-                return false;
-              } else {
-                evalCtx.setEvalReason(EnumEvalReason.EVALUATED_DENY_ACI);
-                evalCtx.setDecidingAci(denyAci);
-                return false;
-              }
-           }
-        }
-        //Now check the allows -- flip the deny flag to false first.
-        evalCtx.setDenyEval(false);
-        for(Aci allowAci : allows) {
-        res=Aci.evaluate(evalCtx, allowAci);
-          if(res.equals(EnumEvalResult.TRUE)) {
-            if(evalCtx.isGetEffectiveRightsEval() &&
-               !evalCtx.hasRights(ACI_SELF) &&
-               !evalCtx.isTargAttrFilterMatchAciEmpty()) {
-               //Iterate to next only if deny ACI contains a targattrfilters
-               //keyword.
-               if(AciEffectiveRights.setTargAttrAci(evalCtx, allowAci, false))
-                  continue;
-               evalCtx.setEvalReason(EnumEvalReason.EVALUATED_ALLOW_ACI);
-               evalCtx.setDecidingAci(allowAci);
-               return true;
-            } else {
-              evalCtx.setEvalReason(EnumEvalReason.EVALUATED_ALLOW_ACI);
-              evalCtx.setDecidingAci(allowAci);
-              return true;
-            }
-          }
-        }
-        //Nothing matched fall through.
-        evalCtx.setEvalReason(EnumEvalReason.NO_MATCHED_ALLOWS_ACIS);
-        evalCtx.setDecidingAci(null);
-        return false;
-    }
-
-    /**
-     * Creates the allow and deny ACI lists based on the provided target
-     * match context. These lists are stored in the evaluation context.
-     * @param candidates  List of all possible ACI candidates.
-     * @param targetMatchCtx Target matching context to use for testing each
-     * ACI.
-     */
-    private void createApplicableList(LinkedList<Aci> candidates,
-                                      AciTargetMatchContext targetMatchCtx)
-    {
-        LinkedList<Aci>denys=new LinkedList<Aci>();
-        LinkedList<Aci>allows=new LinkedList<Aci>();
-        for(Aci aci : candidates) {
-            if(Aci.isApplicable(aci, targetMatchCtx)) {
-                if (aci.hasAccessType(EnumAccessType.DENY)) {
-                    denys.add(aci);
-                }
-                if(aci.hasAccessType(EnumAccessType.ALLOW)) {
-                   allows.add(aci);
-                }
-            }
-           if(targetMatchCtx.getTargAttrFiltersMatch())
-              targetMatchCtx.setTargAttrFiltersMatch(false);
-        }
-        targetMatchCtx.setAllowList(allows);
-        targetMatchCtx.setDenyList(denys);
-    }
-
-
-    /**
-     * Check to see if the client entry has BYPASS_ACL privileges
-     * for this operation.
-     * @param operation The operation to check privileges on.
-     * @return True if access checking can be skipped because
-     * the operation client connection has BYPASS_ACL privileges.
-     */
-    private boolean skipAccessCheck(Operation operation) {
-        return operation.getClientConnection().
-                hasPrivilege(Privilege.BYPASS_ACL, operation);
-    }
-
-    /**
-     * Check to see if the specified entry has the specified privilege.
-     *
-     * @param e The entry to check privileges on.
-     * @return  {@code true} if the entry has the
-     *          specified privilege, or {@code false} if not.
-     */
-    private boolean skipAccessCheck(Entry e) {
-        return ClientConnection.hasPrivilege(e, Privilege.BYPASS_ACL);
-    }
-
-    /**
-     * Check access using the specified container. This container will have all
-     * of the information to gather applicable ACIs and perform evaluation on
-     * them.
-     *
-     * @param container An ACI operation container which has all of the
-     * information needed to check access.
-     *
-     * @return True if access is allowed.
-     */
-     boolean accessAllowed(AciContainer container)
-    {
-        DN dn = container.getResourceEntry().getDN();
-        //For ACI_WRITE_ADD and ACI_WRITE_DELETE set the ACI_WRITE
-        //right.
-        if(container.hasRights(ACI_WRITE_ADD) ||
-           container.hasRights(ACI_WRITE_DELETE))
-                container.setRights(container.getRights() | ACI_WRITE);
-        //Check if the ACI_SELF right needs to be set (selfwrite right).
-        //Only done if the right is ACI_WRITE,  an attribute value is set and
-        //that attribute value is a DN.
-        if((container.getCurrentAttributeValue() != null) &&
-           (container.hasRights(ACI_WRITE)) &&
-           (isAttributeDN(container.getCurrentAttributeType())))  {
-          String DNString=null;
-          try {
-            DNString =
-                container.getCurrentAttributeValue().getValue().toString();
-            DN tmpDN = DN.decode(DNString);
-            //Have a valid DN, compare to clientDN to see if the ACI_SELF
-            //right should be set.
-            if(tmpDN.equals(container.getClientDN())) {
-              container.setRights(container.getRights() | ACI_SELF);
-            }
-          } catch (DirectoryException ex) {
-             //Log a message and keep going.
-             Message message = WARN_ACI_NOT_VALID_DN.get(DNString);
-             logError(message);
-          }
-        }
-
-        //Check proxy authorization only if the entry has not already been
-        //processed (working on a new entry). If working on a new entry, then
-        //only do a proxy check if the right is not set to ACI_PROXY and the
-        //proxied authorization control has been decoded.
-        if(!container.hasSeenEntry()) {
-          if(container.isProxiedAuthorization() &&
-             !container.hasRights(ACI_PROXY) &&
-             !container.hasRights(ACI_SKIP_PROXY_CHECK)) {
-              int currentRights=container.getRights();
-              //Save the current rights so they can be put back if on success.
-              container.setRights(ACI_PROXY);
-              //Switch to the original authorization entry, not the proxied one.
-              container.useOrigAuthorizationEntry(true);
-              if(!accessAllowed(container))
-                  return false;
-              //Access is ok, put the original rights back.
-              container.setRights(currentRights);
-              //Put the proxied authorization entry back to the current
-              //authorization entry.
-              container.useOrigAuthorizationEntry(false);
-          }
-          //Set the seen flag so proxy processing is not performed for this
-          //entry again.
-          container.setSeenEntry(true);
-       }
-
-        /*
-         * First get all allowed candidate ACIs.
-         */
-        LinkedList<Aci>candidates = aciList.getCandidateAcis(dn);
-        /*
-         * Create an applicable list of ACIs by target matching each
-         * candidate ACI against the container's target match view.
-         */
-        createApplicableList(candidates,container);
-        /*
-         * Evaluate the applicable list.
-         */
-        boolean ret=testApplicableLists(container);
-        //Build summary string if doing geteffectiverights eval.
-        if(container.isGetEffectiveRightsEval())
-          AciEffectiveRights.createSummary(container, ret, "main");
-        return ret;
-    }
-
-    /**
-     * Check if the specified attribute type is a DN by checking if its syntax
-     * OID is equal to the DN syntax OID.
-     * @param attribute The attribute type to check.
-     * @return True if the attribute type syntax OID is equal to a DN syntax
-     *         OID.
-     */
-    private boolean isAttributeDN(AttributeType attribute) {
-      return (attribute.getSyntaxOID().equals(SYNTAX_DN_OID));
-    }
-
-    /**
-     * 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.
-     */
-    private SearchResultEntry
-    accessAllowedAttrs(AciLDAPOperationContainer container) {
-        Entry e=container.getResourceEntry();
-        List<AttributeType> typeList=getAllAttrs(e);
-        for(AttributeType attrType : typeList) {
-            if(container.hasAllUserAttributes() && !attrType.isOperational())
-                continue;
-            if(container.hasAllOpAttributes() && attrType.isOperational())
-                continue;
-            container.setCurrentAttributeType(attrType);
-            if(!accessAllowed(container))
-                e.removeAttribute(attrType);
-        }
-        return container.getSearchResultEntry();
-    }
-
-    /**
-     * Gathers all of the attribute types in an entry along with the
-     * "objectclass" attribute type in a List. The "objectclass" attribute is
-     * added to the list first so it is evaluated first.
-     *
-     * @param e Entry to gather the attributes for.
-     * @return List containing the attribute types.
-     */
-    private List<AttributeType> getAllAttrs(Entry e) {
-        Map<AttributeType,List<Attribute>> attrMap = e.getUserAttributes();
-        Map<AttributeType,List<Attribute>> opAttrMap =
-                                                   e.getOperationalAttributes();
-        List<AttributeType> typeList=new LinkedList<AttributeType>();
-        Attribute attr=e.getObjectClassAttribute();
-        /*
-         * When a search is not all attributes returned, the "objectclass"
-         * attribute type is missing from the entry.
-         */
-        if(attr != null) {
-           AttributeType ocType=attr.getAttributeType();
-           typeList.add(ocType);
-        }
-        typeList.addAll(attrMap.keySet());
-        typeList.addAll(opAttrMap.keySet());
-        return typeList;
-    }
-
-    /*
-     * TODO Evaluate performance of this method.
-     * TODO Evaluate security concerns of this method. Logic from this method
-     * taken almost directly from DS6 implementation.
-     *
-     *  I find the work done in the accessAllowedEntry method, particularly
-     *  with regard to the entry test evaluation, to be very confusing and
-     *  potentially pretty inefficient.  I'm also concerned that the "return
-     *  "true" inside the for loop could potentially allow access when it
-     *  should be denied.
-     */
-    /**
-     * Check if access is allowed on an entry. Access is checked by iterating
-     * through each attribute of an entry, starting with the "objectclass"
-     * attribute type.
-     *
-     * If access is allowed on the entry based on one of it's attribute types,
-     * then a possible second access check is performed. This second check is
-     * only performed if an entry test ACI was found during the earlier
-     * successful access check. An entry test ACI has no "targetattrs" keyword,
-     * so allowing access based on an attribute type only would be incorrect.
-     *
-     * @param container ACI search container containing all of the information
-     * needed to check access.
-     *
-     * @return True if access is allowed.
-     */
-     boolean accessAllowedEntry(AciLDAPOperationContainer container) {
-        boolean ret=false;
-        //set flag that specifies this is the first attribute evaluated
-        //in the entry
-        container.setIsFirstAttribute(true);
-        List<AttributeType> typeList=getAllAttrs(container.getResourceEntry());
-        for(AttributeType attrType : typeList) {
-            container.setCurrentAttributeType(attrType);
-            /*
-             * Check if access is allowed. If true, then check to see if an
-             * entry test rule was found (no targetattrs) during target match
-             * evaluation. If such a rule was found, set the current attribute
-             * type to "null" and check access again so that rule is applied.
-             */
-            if(accessAllowed(container)) {
-                if(container.hasEntryTestRule()) {
-                    container.setCurrentAttributeType(null);
-                    if(!accessAllowed(container)) {
-                        /*
-                         * If we failed because of a deny permission-bind rule,
-                         * we need to stop and return false.
-                         */
-                        if(container.isDenyEval()) {
-                            return false;
-                        }
-                        /*
-                         * If we failed because there was no explicit
-                         * allow rule, then we grant implicit access to the
-                         * entry.
-                         */
-                    }
-                }
-                return true;
-            }
-        }
-        return ret;
-    }
-
-    /**
-     * Test the attribute types of the search filter for access. This method
-     * supports the search right.
-     *
-     * @param container  The container used in the access evaluation.
-     * @param filter The filter to check access on.
-     * @return  True if all attribute types in the filter have access.
-     * @throws DirectoryException If there is a problem matching the entry
-     *                            using the provided filter.
-     */
-    private boolean
-    testFilter(AciLDAPOperationContainer container, SearchFilter filter)
-    throws DirectoryException {
-        boolean ret=true;
-        //If the resource entry has a dn equal to "cn=debugsearch" and it
-        //contains the special attribute type "debugsearchindex", then the
-        //resource entry is a pseudo entry created for debug purposes. Return
-        //true if that is the case.
-        if(debugSearchIndexDN.equals(container.getResourceDN()) &&
-           container.getResourceEntry().hasAttribute(debugSearchIndex))
-          return true;
-        switch (filter.getFilterType()) {
-            case AND:
-            case OR: {
-                for (SearchFilter f : filter.getFilterComponents())
-                    if(!testFilter(container, f))
-                        return false ;
-                break;
-            }
-            case NOT: {
-                ret=false;
-                SearchFilter f = filter.getNotComponent();
-                if(f.matchesEntry(container.getResourceEntry()))
-                  ret=true;
-                if(ret)
-                  ret=testFilter(container, f);
-                ret=!ret;
-                break;
-            }
-            default: {
-                AttributeType attrType=filter.getAttributeType();
-                container.setCurrentAttributeType(attrType);
-                ret=accessAllowed(container);
-            }
-        }
-        return ret;
-    }
-
-    /**
-     * Check access using the accessAllowed method. The
-     * LDAP add, compare, modify and delete operations use this function.
-     * The other supported LDAP operations have more specialized checks.
-     * @param operationContainer  The container containing the information
-     * needed to evaluate this operation.
-     * @param operation The operation being evaluated.
-     * @return True if this operation is allowed access.
-     */
-    private boolean isAllowed(AciLDAPOperationContainer operationContainer,
-                              Operation operation) {
-        return skipAccessCheck(operation) || accessAllowed(operationContainer);
-    }
-
-    /**
-     * Evaluate an entry to be added to see if it has any "aci"
-     * attribute type. If it does, examines each "aci" attribute type
-     * value for syntax errors. All of the "aci" attribute type values
-     * must pass syntax check for the add operation to proceed. Any
-     * entry with an "aci" attribute type must have "modify-acl"
-     * privileges.
-     *
-     * @param entry  The entry to be examined.
-     * @param operation The operation to to check privileges on.
-     * @param clientDN The authorization DN.
-     * @return True if the entry has no ACI attributes or if all of the "aci"
-     * attributes values pass ACI syntax checking.
-     */
-    private boolean
-       verifySyntax(Entry entry, Operation operation, DN clientDN) {
-      if(entry.hasOperationalAttribute(aciType)) {
-        /*
-         * Check that the operation has "modify-acl" privileges since the
-         * entry to be added has an "aci" attribute type.
-         */
-        if (!operation.getClientConnection().
-             hasPrivilege(Privilege.MODIFY_ACL, operation))  {
-          Message message = INFO_ACI_ADD_FAILED_PRIVILEGE.get(
-              String.valueOf(entry.getDN()), String.valueOf(clientDN));
-          logError(message);
-          return false;
-        }
-        List<Attribute> attributeList =
-             entry.getOperationalAttribute(aciType, null);
-        for (Attribute attribute : attributeList)
+        GetEffectiveRightsRequestControl getEffectiveRightsControl;
+        if (control instanceof LDAPControl)
         {
-          for (AttributeValue value : attribute)
-          {
-            try {
-              DN dn=entry.getDN();
-              Aci.decode(value.getValue(),dn);
-            } catch (AciException ex) {
-              Message message = WARN_ACI_ADD_FAILED_DECODE.get(
-                  String.valueOf(entry.getDN()), ex.getMessage());
-              logError(message);
-              return false;
-            }
-          }
+          getEffectiveRightsControl =
+              GetEffectiveRightsRequestControl.DECODER.decode(control
+                  .isCritical(), ((LDAPControl) control).getValue());
         }
-      }
-    return true;
-  }
-
-    /**
-     * Check access on add operations.
-     *
-     * @param operation The add operation to check access on.
-     * @return  True if access is allowed.
-     */
-    public boolean isAllowed(LocalBackendAddOperation operation) {
-        AciLDAPOperationContainer operationContainer =
-                new AciLDAPOperationContainer(operation, ACI_ADD);
-        boolean ret=isAllowed(operationContainer,operation);
-
-        //LDAP add needs a verify ACI syntax step in case any
-        //"aci" attribute types are being added.
-        if(ret)
-          ret=verifySyntax(operation.getEntryToAdd(), operation,
-                           operationContainer.getClientDN());
-        return ret;
-    }
-
-   /**
-     * Check access on compare operations. Note that the attribute
-     * type is unavailable at this time, so this method partially
-     * parses the raw attribute string to get the base attribute
-     * type. Options are ignored.
-     *
-     * @param operation The compare operation to check access on.
-     * @return  True if access is allowed.
-     */
-   public boolean isAllowed(LocalBackendCompareOperation operation) {
-       AciLDAPOperationContainer operationContainer =
-               new AciLDAPOperationContainer(operation, ACI_COMPARE);
-       String baseName;
-       String rawAttributeType=operation.getRawAttributeType();
-       int  semicolonPosition=rawAttributeType.indexOf(';');
-       if (semicolonPosition > 0)
-         baseName =
-             toLowerCase(rawAttributeType.substring(0, semicolonPosition));
-       else
-         baseName = toLowerCase(rawAttributeType);
-       AttributeType attributeType;
-       if((attributeType =
-           DirectoryServer.getAttributeType(baseName)) == null)
-           attributeType = DirectoryServer.getDefaultAttributeType(baseName);
-       AttributeValue attributeValue =
-           AttributeValues.create(attributeType,
-               operation.getAssertionValue());
-       operationContainer.setCurrentAttributeType(attributeType);
-       operationContainer.setCurrentAttributeValue(attributeValue);
-       return isAllowed(operationContainer, operation);
-   }
-
-   /**
-     * Check access on delete operations.
-     *
-     * @param operation The delete operation to check access on.
-     * @return  True if access is allowed.
-     */
-   public boolean isAllowed(LocalBackendDeleteOperation operation) {
-       AciLDAPOperationContainer operationContainer=
-               new AciLDAPOperationContainer(operation, ACI_DELETE);
-       return isAllowed(operationContainer, operation);
-   }
-
-   /**
-    * Check access on modify operations.
-    *
-    * @param operation The modify operation to check access on.
-    * @return  True if access is allowed.
-    */
-
-  public boolean isAllowed(LocalBackendModifyOperation operation) {
-      AciLDAPOperationContainer operationContainer=
-              new AciLDAPOperationContainer(operation, ACI_NULL);
-      return aciCheckMods(operationContainer, operation,
-                          skipAccessCheck(operation));
-  }
-
-  /**
-   * 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.
-   */
-  public boolean
-  maySend(SearchOperation operation, SearchResultEntry entry) {
-      AciLDAPOperationContainer operationContainer =
-              new AciLDAPOperationContainer(operation,
-                      (ACI_SEARCH), entry);
-      boolean ret;
-      if(!(ret=skipAccessCheck(operation))) {
-          try {
-            ret=testFilter(operationContainer, operation.getFilter());
-          } catch (DirectoryException ex)  {
-            ret=false;
-          }
-          if (ret) {
-              operationContainer.clearEvalAttributes(ACI_NULL);
-              operationContainer.setRights(ACI_READ);
-              ret=accessAllowedEntry(operationContainer);
-            if(ret) {
-              if(!operationContainer.hasEvalUserAttributes())
-                operation.setAttachment(ALL_USER_ATTRS_MATCHED,
-                        ALL_USER_ATTRS_MATCHED);
-              if(!operationContainer.hasEvalOpAttributes())
-                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;
-  }
-
-  /*
-   * TODO Rename this method. Needs to be changed in SearchOperation.
-   *
-   * I find the name of the filterEntry method to be misleading because
-   * it works on a search operation but has nothing to do with the search
-   * filter.  Something like "removeDisallowedAttributes" would be clearer.
-   */
-  /**
-   * Checks access on each attribute in an entry. It removes those attributes
-   * that fail access check.
-   *
-   * @param operation The search operation class containing information to
-   * check access on.
-   * @param entry   The entry containing the attributes.
-   * @return    The entry to return minus filtered attributes.
-   */
-  public SearchResultEntry filterEntry(SearchOperation operation,
-                                       SearchResultEntry entry) {
-      AciLDAPOperationContainer operationContainer =
-              new AciLDAPOperationContainer(operation,
-                                            (ACI_READ), entry);
-      //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);
-      } else
-          returnEntry=entry;
-      if(operationContainer.hasGetEffectiveRightsControl()) {
-          returnEntry =
-            AciEffectiveRights.addRightsToEntry(this, operation.getAttributes(),
-                                               operationContainer, returnEntry,
-                                               skipCheck);
-      }
-      return returnEntry;
-  }
-
-  /**
-   * Perform all needed RDN checks for the modifyDN operation. The old RDN is
-   * not equal to the new RDN. The access checks are:
-   *
-   *  - Verify WRITE access to the original entry.
-   *  - Verfiy WRITE_ADD access on each RDN component of the new RDN. The
-   *    WRITE_ADD access is used because this access could be restricted by
-   *    the targattrfilters keyword.
-   *  - If the deleteOLDRDN flag is set, verify WRITE_DELETE access on the
-   *    old RDN. The WRITE_DELETE access is used because this access could be
-   *    restricted by the targattrfilters keyword.
-   *
-   * @param operation   The ModifyDN operation class containing information to
-   * check access on.
-   * @param oldRDN      The old RDN component.
-   * @param newRDN      The new RDN component.
-   * @return True if access is allowed.
-   */
-  private boolean aciCheckRDNs(LocalBackendModifyDNOperation operation,
-                               RDN oldRDN,
-                               RDN newRDN) {
-      boolean ret;
-
-      AciLDAPOperationContainer operationContainer =
-              new AciLDAPOperationContainer(operation, (ACI_WRITE),
-                      operation.getOriginalEntry());
-      ret=accessAllowed(operationContainer);
-      if(ret)
-          ret=checkRDN(ACI_WRITE_ADD, newRDN, operationContainer);
-      if(ret && operation.deleteOldRDN()) {
-          ret =
-            checkRDN(ACI_WRITE_DELETE, oldRDN, operationContainer);
-      }
-      return ret;
-  }
-
-
-  /**
-   * Check access on each attribute-value pair component of the specified RDN.
-   * There may be more than one attribute-value pair if the RDN is multi-valued.
-   *
-   * @param right  The access right to check for.
-   * @param rdn  The RDN to examine the attribute-value pairs of.
-   * @param container The container containing the information needed to
-   * evaluate the specified RDN.
-   * @return  True if access is allowed for all attribute-value pairs.
-   */
-  private boolean checkRDN(int right, RDN rdn, AciContainer container) {
-        boolean ret=false;
-        int numAVAs = rdn.getNumValues();
-        container.setRights(right);
-        for (int i = 0; i < numAVAs; i++){
-            AttributeType type=rdn.getAttributeType(i);
-            AttributeValue value=rdn.getAttributeValue(i);
-            container.setCurrentAttributeType(type);
-            container.setCurrentAttributeValue(value);
-            if(!(ret=accessAllowed(container)))
-                break;
+        else
+        {
+          getEffectiveRightsControl =
+              (GetEffectiveRightsRequestControl) control;
         }
-        return ret;
-  }
-
-  /**
-   * Check access on the new superior entry if it exists. If the entry does not
-   * exist or the DN cannot be locked then false is returned.
-   *
-   * @param superiorDN The DN of the new superior entry.
-   * @param op The modifyDN operation to check access on.
-   * @return True if access is granted to the new superior entry.
-   * @throws DirectoryException  If a problem occurs while trying to
-   *                             retrieve the new superior entry.
-   */
-  private boolean aciCheckSuperiorEntry(DN superiorDN,
-      LocalBackendModifyDNOperation op)
-  throws DirectoryException {
-    boolean ret=false;
-    Lock entryLock = null;
-    for (int i=0; i < 3; i++)  {
-      entryLock = LockManager.lockRead(superiorDN);
-      if (entryLock != null)
-        break;
-    }
-    if (entryLock == null) {
-      Message message = WARN_ACI_HANDLER_CANNOT_LOCK_NEW_SUPERIOR_USER.get(
-          String.valueOf(superiorDN));
-      logError(message);
-      return false;
-    }
-    try {
-      Entry superiorEntry=DirectoryServer.getEntry(superiorDN);
-      if(superiorEntry!= null) {
-        AciLDAPOperationContainer operationContainer =
-                new AciLDAPOperationContainer(op, (ACI_IMPORT),
-                        superiorEntry);
-        ret=accessAllowed(operationContainer);
+        op.setAttachment(OID_GET_EFFECTIVE_RIGHTS,
+            getEffectiveRightsControl);
       }
-    }  finally {
-          LockManager.unlock(superiorDN, entryLock);
+      catch (DirectoryException de)
+      {
+        Message message =
+            WARN_ACI_SYNTAX_DECODE_EFFECTIVERIGHTS_FAIL.get(de
+                .getMessage());
+        logError(message);
+        ret = false;
+      }
     }
     return ret;
   }
 
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public boolean isAllowed(ExtendedOperation operation)
+  {
+    boolean ret;
+    if (!(ret = skipAccessCheck(operation)))
+    {
+      Entry e =
+          new Entry(operation.getAuthorizationDN(), null, null, null);
+      AciLDAPOperationContainer operationContainer =
+          new AciLDAPOperationContainer(operation, e,
+              (ACI_READ | ACI_EXT_OP));
+      ret = accessAllowed(operationContainer);
+    }
+    return ret;
+  }
+
+
+
+  /**
+   * Check access on add operations.
+   *
+   * @param operation
+   *          The add operation to check access on.
+   * @return True if access is allowed.
+   */
+  @Override
+  public boolean isAllowed(LocalBackendAddOperation operation)
+  {
+    AciLDAPOperationContainer operationContainer =
+        new AciLDAPOperationContainer(operation, ACI_ADD);
+    boolean ret = isAllowed(operationContainer, operation);
+
+    // LDAP add needs a verify ACI syntax step in case any
+    // "aci" attribute types are being added.
+    if (ret)
+    {
+      ret =
+          verifySyntax(operation.getEntryToAdd(), operation,
+              operationContainer.getClientDN());
+    }
+    return ret;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public boolean isAllowed(LocalBackendBindOperation bindOperation)
+  {
+    // Not planned to be implemented.
+    return true;
+  }
+
+
+
+  /**
+   * Check access on compare operations. Note that the attribute type is
+   * unavailable at this time, so this method partially parses the raw
+   * attribute string to get the base attribute type. Options are
+   * ignored.
+   *
+   * @param operation
+   *          The compare operation to check access on.
+   * @return True if access is allowed.
+   */
+  @Override
+  public boolean isAllowed(LocalBackendCompareOperation operation)
+  {
+    AciLDAPOperationContainer operationContainer =
+        new AciLDAPOperationContainer(operation, ACI_COMPARE);
+    String baseName;
+    String rawAttributeType = operation.getRawAttributeType();
+    int semicolonPosition = rawAttributeType.indexOf(';');
+    if (semicolonPosition > 0)
+    {
+      baseName =
+          toLowerCase(rawAttributeType.substring(0, semicolonPosition));
+    }
+    else
+    {
+      baseName = toLowerCase(rawAttributeType);
+    }
+    AttributeType attributeType;
+    if ((attributeType = DirectoryServer.getAttributeType(baseName)) == null)
+    {
+      attributeType = DirectoryServer.getDefaultAttributeType(baseName);
+    }
+    AttributeValue attributeValue =
+        AttributeValues.create(attributeType, operation
+            .getAssertionValue());
+    operationContainer.setCurrentAttributeType(attributeType);
+    operationContainer.setCurrentAttributeValue(attributeValue);
+    return isAllowed(operationContainer, operation);
+  }
+
+
+
+  /**
+   * Check access on delete operations.
+   *
+   * @param operation
+   *          The delete operation to check access on.
+   * @return True if access is allowed.
+   */
+  @Override
+  public boolean isAllowed(LocalBackendDeleteOperation operation)
+  {
+    AciLDAPOperationContainer operationContainer =
+        new AciLDAPOperationContainer(operation, ACI_DELETE);
+    return isAllowed(operationContainer, operation);
+  }
+
+
+
   /**
    * Checks access on a modifyDN operation.
    *
-   * @param operation The modifyDN operation to check access on.
+   * @param operation
+   *          The modifyDN operation to check access on.
    * @return True if access is allowed.
+   */
+  @Override
+  public boolean isAllowed(LocalBackendModifyDNOperation operation)
+  {
+    boolean ret = true;
+    DN newSuperiorDN;
+    RDN oldRDN = operation.getOriginalEntry().getDN().getRDN();
+    RDN newRDN = operation.getNewRDN();
+    if (!skipAccessCheck(operation))
+    {
+      // If this is a modifyDN move to a new superior, then check if the
+      // superior DN has import accesss.
+      if ((newSuperiorDN = operation.getNewSuperior()) != null)
+      {
+        try
+        {
+          ret = aciCheckSuperiorEntry(newSuperiorDN, operation);
+        }
+        catch (DirectoryException ex)
+        {
+          ret = false;
+        }
+      }
+      boolean rdnEquals = oldRDN.equals(newRDN);
+      // Perform the RDN access checks only if the RDNs are not equal.
+      if (ret && !rdnEquals)
+      {
+        ret = aciCheckRDNs(operation, oldRDN, newRDN);
+      }
+
+      // If this is a modifyDN move to a new superior, then check if the
+      // original entry DN has export access.
+      if (ret && (newSuperiorDN != null))
+      {
+        AciLDAPOperationContainer operationContainer =
+            new AciLDAPOperationContainer(operation, (ACI_EXPORT),
+                operation.getOriginalEntry());
+        // The RDNs are not equal, skip the proxy check since it was
+        // already performed in the aciCheckRDNs call above.
+        if (!rdnEquals)
+        {
+          operationContainer.setSeenEntry(true);
+        }
+        ret = accessAllowed(operationContainer);
+      }
+    }
+    return ret;
+  }
+
+
+
+  /**
+   * Check access on modify operations.
    *
+   * @param operation
+   *          The modify operation to check access on.
+   * @return True if access is allowed.
    */
-  public boolean isAllowed(LocalBackendModifyDNOperation operation) {
-      boolean ret=true;
-      DN newSuperiorDN;
-      RDN oldRDN=operation.getOriginalEntry().getDN().getRDN();
-      RDN newRDN=operation.getNewRDN();
-      if(!skipAccessCheck(operation)) {
-          //If this is a modifyDN move to a new superior, then check if the
-          //superior DN has import accesss.
-          if((newSuperiorDN=operation.getNewSuperior()) != null) {
-             try {
-               ret=aciCheckSuperiorEntry(newSuperiorDN, operation);
-             } catch (DirectoryException ex) {
-               ret=false;
-             }
-          }
-          boolean rdnEquals=oldRDN.equals(newRDN);
-          //Perform the RDN access checks only if the RDNs are not equal.
-          if(ret && !rdnEquals)
-              ret=aciCheckRDNs(operation, oldRDN, newRDN);
 
-          //If this is a modifyDN move to a new superior, then check if the
-          //original entry DN has export access.
-          if(ret && (newSuperiorDN != null)) {
-              AciLDAPOperationContainer operationContainer =
-                      new AciLDAPOperationContainer(operation, (ACI_EXPORT),
-                                             operation.getOriginalEntry());
-                 //The RDNs are not equal, skip the proxy check since it was
-                 //already performed in the aciCheckRDNs call above.
-                 if(!rdnEquals)
-                     operationContainer.setSeenEntry(true);
-                 ret=accessAllowed(operationContainer);
-          }
-      }
-      return ret;
+  @Override
+  public boolean isAllowed(LocalBackendModifyOperation operation)
+  {
+    AciLDAPOperationContainer operationContainer =
+        new AciLDAPOperationContainer(operation, ACI_NULL);
+    return aciCheckMods(operationContainer, operation,
+        skipAccessCheck(operation));
   }
 
 
+
   /**
    * {@inheritDoc}
    */
   @Override
-  public boolean isAllowed(DN entryDN, Operation op, Control control) {
-    boolean ret;
-    if(!(ret=skipAccessCheck(op))) {
-      Entry e = new Entry(entryDN, null, null, null);
-      AciLDAPOperationContainer operationContainer =
-              new AciLDAPOperationContainer(op, e, control,
-                                            (ACI_READ | ACI_CONTROL));
-      ret=accessAllowed(operationContainer);
-    }
-    if(control.getOID().equals(OID_PROXIED_AUTH_V2) ||
-            control.getOID().equals(OID_PROXIED_AUTH_V1))
-      op.setAttachment(ORIG_AUTH_ENTRY, op.getAuthorizationEntry());
-    else if(control.getOID().equals(OID_GET_EFFECTIVE_RIGHTS)) {
-      try {
-        GetEffectiveRightsRequestControl getEffectiveRightsControl;
-        if(control instanceof LDAPControl)
-        {
-          getEffectiveRightsControl = GetEffectiveRightsRequestControl.DECODER
-              .decode(control.isCritical(), ((LDAPControl) control).getValue());
-        }
-        else
-        {
-          getEffectiveRightsControl = (GetEffectiveRightsRequestControl)control;
-        }
-        op.setAttachment(OID_GET_EFFECTIVE_RIGHTS, getEffectiveRightsControl);
-      } catch  (DirectoryException de)  {
-        Message message =
-            WARN_ACI_SYNTAX_DECODE_EFFECTIVERIGHTS_FAIL.get(de.getMessage());
-        logError(message);
-        ret=false;
-      }
-    }
-    return ret;
+  public boolean isAllowed(LocalBackendSearchOperation searchOperation)
+  {
+    // Not planned to be implemented.
+    return true;
   }
 
 
+
   /**
    * {@inheritDoc}
    */
   @Override
-  public boolean isAllowed(ExtendedOperation operation) {
+  public boolean mayProxy(Entry proxyUser, Entry proxiedUser,
+      Operation op)
+  {
     boolean ret;
-    if(!(ret=skipAccessCheck(operation))) {
-      Entry e = new Entry(operation.getAuthorizationDN(), null, null, null);
+    if (!(ret = skipAccessCheck(proxyUser)))
+    {
+      AuthenticationInfo authInfo =
+          new AuthenticationInfo(proxyUser, DirectoryServer
+              .isRootDN(proxyUser.getDN()));
       AciLDAPOperationContainer operationContainer =
-         new AciLDAPOperationContainer(operation, e, (ACI_READ | ACI_EXT_OP));
-      ret=accessAllowed(operationContainer);
+          new AciLDAPOperationContainer(op, proxiedUser, authInfo,
+              ACI_PROXY);
+      ret = accessAllowedEntry(operationContainer);
     }
     return ret;
   }
 
 
+
   /**
    * {@inheritDoc}
    */
   @Override
   public boolean maySend(DN dn, SearchOperation operation,
-                         SearchResultReference reference) {
+      SearchResultReference reference)
+  {
     boolean ret;
-    if(!(ret=skipAccessCheck(operation))) {
+    if (!(ret = skipAccessCheck(operation)))
+    {
       Entry e = new Entry(dn, null, null, null);
       AttributeBuilder builder =
-        new AttributeBuilder(refAttrType, ATTR_REFERRAL_URL);
+          new AttributeBuilder(refAttrType, ATTR_REFERRAL_URL);
       List<String> URLStrings = reference.getReferralURLs();
 
       // Load the values, a bind rule might want to evaluate them.
-      for (String URLString : URLStrings) {
+      for (String URLString : URLStrings)
+      {
         builder.add(AttributeValues.create(refAttrType, URLString));
       }
 
-      e.addAttribute(builder.toAttribute(),null);
-      SearchResultEntry se=new  SearchResultEntry(e);
+      e.addAttribute(builder.toAttribute(), null);
+      SearchResultEntry se = new SearchResultEntry(e);
       AciLDAPOperationContainer operationContainer =
-              new AciLDAPOperationContainer(operation,
-                                           (ACI_READ), se);
+          new AciLDAPOperationContainer(operation, (ACI_READ), se);
       operationContainer.setCurrentAttributeType(refAttrType);
-      ret=accessAllowed(operationContainer);
+      ret = accessAllowed(operationContainer);
     }
     return ret;
   }
 
 
+
   /**
-   * {@inheritDoc}
+   * 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.
    */
   @Override
-  public boolean
-  mayProxy(Entry proxyUser, Entry proxiedUser, Operation op) {
-      boolean ret;
-      if(!(ret=skipAccessCheck(proxyUser))) {
-          AuthenticationInfo authInfo =
-              new AuthenticationInfo(proxyUser,
-                     DirectoryServer.isRootDN(proxyUser.getDN()));
-          AciLDAPOperationContainer operationContainer =
-              new AciLDAPOperationContainer(op, proxiedUser,
-                                            authInfo, ACI_PROXY);
-          ret=accessAllowedEntry(operationContainer);
+  public boolean maySend(SearchOperation operation,
+      SearchResultEntry entry)
+  {
+    AciLDAPOperationContainer operationContainer =
+        new AciLDAPOperationContainer(operation, (ACI_SEARCH), entry);
+    boolean ret;
+    if (!(ret = skipAccessCheck(operation)))
+    {
+      try
+      {
+        ret = testFilter(operationContainer, operation.getFilter());
       }
-      return ret;
+      catch (DirectoryException ex)
+      {
+        ret = false;
+      }
+      if (ret)
+      {
+        operationContainer.clearEvalAttributes(ACI_NULL);
+        operationContainer.setRights(ACI_READ);
+        ret = accessAllowedEntry(operationContainer);
+        if (ret)
+        {
+          if (!operationContainer.hasEvalUserAttributes())
+          {
+            operation.setAttachment(ALL_USER_ATTRS_MATCHED,
+                ALL_USER_ATTRS_MATCHED);
+          }
+          if (!operationContainer.hasEvalOpAttributes())
+          {
+            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;
   }
 
 
-  /**
-   * {@inheritDoc}
-   */
-  @Override
-  public boolean isAllowed(LocalBackendBindOperation bindOperation) {
-      //Not planned to be implemented.
-      return true;
-  }
 
   /**
-   * {@inheritDoc}
+   * Check access using the specified container. This container will
+   * have all of the information to gather applicable ACIs and perform
+   * evaluation on them.
+   *
+   * @param container
+   *          An ACI operation container which has all of the
+   *          information needed to check access.
+   * @return True if access is allowed.
    */
-  @Override
-  public boolean isAllowed(LocalBackendSearchOperation searchOperation) {
-      //Not planned to be implemented.
+  boolean accessAllowed(AciContainer container)
+  {
+    DN dn = container.getResourceEntry().getDN();
+    // For ACI_WRITE_ADD and ACI_WRITE_DELETE set the ACI_WRITE
+    // right.
+    if (container.hasRights(ACI_WRITE_ADD)
+        || container.hasRights(ACI_WRITE_DELETE))
+    {
+      container.setRights(container.getRights() | ACI_WRITE);
+    }
+    // Check if the ACI_SELF right needs to be set (selfwrite right).
+    // Only done if the right is ACI_WRITE, an attribute value is set
+    // and that attribute value is a DN.
+    if ((container.getCurrentAttributeValue() != null)
+        && (container.hasRights(ACI_WRITE))
+        && (isAttributeDN(container.getCurrentAttributeType())))
+    {
+      String DNString = null;
+      try
+      {
+        DNString =
+            container.getCurrentAttributeValue().getValue().toString();
+        DN tmpDN = DN.decode(DNString);
+        // Have a valid DN, compare to clientDN to see if the ACI_SELF
+        // right should be set.
+        if (tmpDN.equals(container.getClientDN()))
+        {
+          container.setRights(container.getRights() | ACI_SELF);
+        }
+      }
+      catch (DirectoryException ex)
+      {
+        // Log a message and keep going.
+        Message message = WARN_ACI_NOT_VALID_DN.get(DNString);
+        logError(message);
+      }
+    }
+
+    // Check proxy authorization only if the entry has not already been
+    // processed (working on a new entry). If working on a new entry,
+    // then only do a proxy check if the right is not set to ACI_PROXY
+    // and the proxied authorization control has been decoded.
+    if (!container.hasSeenEntry())
+    {
+      if (container.isProxiedAuthorization()
+          && !container.hasRights(ACI_PROXY)
+          && !container.hasRights(ACI_SKIP_PROXY_CHECK))
+      {
+        int currentRights = container.getRights();
+        // Save the current rights so they can be put back if on
+        // success.
+        container.setRights(ACI_PROXY);
+        // Switch to the original authorization entry, not the proxied
+        // one.
+        container.useOrigAuthorizationEntry(true);
+        if (!accessAllowed(container))
+        {
+          return false;
+        }
+        // Access is ok, put the original rights back.
+        container.setRights(currentRights);
+        // Put the proxied authorization entry back to the current
+        // authorization entry.
+        container.useOrigAuthorizationEntry(false);
+      }
+      // Set the seen flag so proxy processing is not performed for this
+      // entry again.
+      container.setSeenEntry(true);
+    }
+
+    /*
+     * First get all allowed candidate ACIs.
+     */
+    LinkedList<Aci> candidates = aciList.getCandidateAcis(dn);
+    /*
+     * Create an applicable list of ACIs by target matching each
+     * candidate ACI against the container's target match view.
+     */
+    createApplicableList(candidates, container);
+    /*
+     * Evaluate the applicable list.
+     */
+    boolean ret = testApplicableLists(container);
+    // Build summary string if doing geteffectiverights eval.
+    if (container.isGetEffectiveRightsEval())
+    {
+      AciEffectiveRights.createSummary(container, ret, "main");
+    }
+    return ret;
+  }
+
+
+
+  /*
+   * TODO Evaluate performance of this method. TODO Evaluate security
+   * concerns of this method. Logic from this method taken almost
+   * directly from DS6 implementation. I find the work done in the
+   * accessAllowedEntry method, particularly with regard to the entry
+   * test evaluation, to be very confusing and potentially pretty
+   * inefficient. I'm also concerned that the "return "true" inside the
+   * for loop could potentially allow access when it should be denied.
+   */
+
+  /**
+   * Check if access is allowed on an entry. Access is checked by
+   * iterating through each attribute of an entry, starting with the
+   * "objectclass" attribute type. If access is allowed on the entry
+   * based on one of it's attribute types, then a possible second access
+   * check is performed. This second check is only performed if an entry
+   * test ACI was found during the earlier successful access check. An
+   * entry test ACI has no "targetattrs" keyword, so allowing access
+   * based on an attribute type only would be incorrect.
+   *
+   * @param container
+   *          ACI search container containing all of the information
+   *          needed to check access.
+   * @return True if access is allowed.
+   */
+  boolean accessAllowedEntry(AciLDAPOperationContainer container)
+  {
+    boolean ret = false;
+    // set flag that specifies this is the first attribute evaluated
+    // in the entry
+    container.setIsFirstAttribute(true);
+    List<AttributeType> typeList =
+        getAllAttrs(container.getResourceEntry());
+    for (AttributeType attrType : typeList)
+    {
+      container.setCurrentAttributeType(attrType);
+      /*
+       * Check if access is allowed. If true, then check to see if an
+       * entry test rule was found (no targetattrs) during target match
+       * evaluation. If such a rule was found, set the current attribute
+       * type to "null" and check access again so that rule is applied.
+       */
+      if (accessAllowed(container))
+      {
+        if (container.hasEntryTestRule())
+        {
+          container.setCurrentAttributeType(null);
+          if (!accessAllowed(container))
+          {
+            /*
+             * If we failed because of a deny permission-bind rule, we
+             * need to stop and return false.
+             */
+            if (container.isDenyEval())
+            {
+              return false;
+            }
+            /*
+             * If we failed because there was no explicit allow rule,
+             * then we grant implicit access to the entry.
+             */
+          }
+        }
+        return true;
+      }
+    }
+    return ret;
+  }
+
+
+
+  /**
+   * 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.
+   */
+  private SearchResultEntry accessAllowedAttrs(
+      AciLDAPOperationContainer container)
+  {
+    Entry e = container.getResourceEntry();
+    List<AttributeType> typeList = getAllAttrs(e);
+    for (AttributeType attrType : typeList)
+    {
+      if (container.hasAllUserAttributes() && !attrType.isOperational())
+      {
+        continue;
+      }
+      if (container.hasAllOpAttributes() && attrType.isOperational())
+      {
+        continue;
+      }
+      container.setCurrentAttributeType(attrType);
+      if (!accessAllowed(container))
+      {
+        e.removeAttribute(attrType);
+      }
+    }
+    return container.getSearchResultEntry();
+  }
+
+
+
+  /**
+   * Checks to see if a LDAP modification is allowed access.
+   *
+   * @param container
+   *          The structure containing the LDAP modifications
+   * @param operation
+   *          The operation to check modify privileges on. operation to
+   *          check and the evaluation context to apply the check
+   *          against.
+   * @param skipAccessCheck
+   *          True if access checking should be skipped.
+   * @return True if access is allowed.
+   */
+  private boolean aciCheckMods(AciLDAPOperationContainer container,
+      LocalBackendModifyOperation operation, boolean skipAccessCheck)
+  {
+    Entry resourceEntry = container.getResourceEntry();
+    DN dn = resourceEntry.getDN();
+    List<Modification> modifications = container.getModifications();
+    for (Modification m : modifications)
+    {
+      Attribute modAttr = m.getAttribute();
+      AttributeType modAttrType = modAttr.getAttributeType();
+
+      if (modAttrType.equals(aciType))
+      {
+        /*
+         * Check that the operation has modify privileges if it contains
+         * an "aci" attribute type.
+         */
+        if (!operation.getClientConnection().hasPrivilege(
+            Privilege.MODIFY_ACL, operation))
+        {
+          Message message =
+              INFO_ACI_MODIFY_FAILED_PRIVILEGE.get(String
+                  .valueOf(container.getResourceDN()), String
+                  .valueOf(container.getClientDN()));
+          logError(message);
+          return false;
+        }
+      }
+      // This access check handles the case where all attributes of this
+      // type are being replaced or deleted. If only a subset is being
+      // deleted than this access check is skipped.
+      ModificationType modType = m.getModificationType();
+      if (((modType == ModificationType.DELETE) && modAttr.isEmpty())
+          || ((modType == ModificationType.REPLACE)
+              || (modType == ModificationType.INCREMENT)))
+      {
+        /*
+         * Check if we have rights to delete all values of an attribute
+         * type in the resource entry.
+         */
+        if (resourceEntry.hasAttribute(modAttrType))
+        {
+          container.setCurrentAttributeType(modAttrType);
+          List<Attribute> attrList =
+              resourceEntry.getAttribute(modAttrType, modAttr
+                  .getOptions());
+          if (attrList != null)
+          {
+            for (Attribute a : attrList)
+            {
+              for (AttributeValue v : a)
+              {
+                container.setCurrentAttributeValue(v);
+                container.setRights(ACI_WRITE_DELETE);
+                if (!skipAccessCheck && !accessAllowed(container))
+                {
+                  return false;
+                }
+              }
+            }
+          }
+        }
+      }
+
+      if (!modAttr.isEmpty())
+      {
+        for (AttributeValue v : modAttr)
+        {
+          container.setCurrentAttributeType(modAttrType);
+          switch (m.getModificationType())
+          {
+          case ADD:
+          case REPLACE:
+            container.setCurrentAttributeValue(v);
+            container.setRights(ACI_WRITE_ADD);
+            if (!skipAccessCheck && !accessAllowed(container))
+            {
+              return false;
+            }
+            break;
+          case DELETE:
+            container.setCurrentAttributeValue(v);
+            container.setRights(ACI_WRITE_DELETE);
+            if (!skipAccessCheck && !accessAllowed(container))
+            {
+              return false;
+            }
+            break;
+          case INCREMENT:
+            Entry modifiedEntry = operation.getModifiedEntry();
+            List<Attribute> modifiedAttrs =
+                modifiedEntry.getAttribute(modAttrType, modAttr
+                    .getOptions());
+            if (modifiedAttrs != null)
+            {
+              for (Attribute attr : modifiedAttrs)
+              {
+                for (AttributeValue val : attr)
+                {
+                  container.setCurrentAttributeValue(val);
+                  container.setRights(ACI_WRITE_ADD);
+                  if (!skipAccessCheck && !accessAllowed(container))
+                  {
+                    return false;
+                  }
+                }
+              }
+            }
+            break;
+          }
+          /*
+           * Check if the modification type has an "aci" attribute type.
+           * If so, check the syntax of that attribute value. Fail the
+           * the operation if the syntax check fails.
+           */
+          if (modAttrType.equals(aciType)
+              || modAttrType.equals(globalAciType))
+          {
+            try
+            {
+              // A global ACI needs a NULL DN, not the DN of the
+              // modification.
+              if (modAttrType.equals(globalAciType))
+              {
+                dn = DN.nullDN();
+              }
+              Aci.decode(v.getValue(), dn);
+            }
+            catch (AciException ex)
+            {
+              Message message =
+                  WARN_ACI_MODIFY_FAILED_DECODE.get(String.valueOf(dn),
+                      ex.getMessage());
+              logError(message);
+              return false;
+            }
+          }
+        }
+      }
+    }
+    return true;
+  }
+
+
+
+  /**
+   * Perform all needed RDN checks for the modifyDN operation. The old
+   * RDN is not equal to the new RDN. The access checks are: - Verify
+   * WRITE access to the original entry. - Verfiy WRITE_ADD access on
+   * each RDN component of the new RDN. The WRITE_ADD access is used
+   * because this access could be restricted by the targattrfilters
+   * keyword. - If the deleteOLDRDN flag is set, verify WRITE_DELETE
+   * access on the old RDN. The WRITE_DELETE access is used because this
+   * access could be restricted by the targattrfilters keyword.
+   *
+   * @param operation
+   *          The ModifyDN operation class containing information to
+   *          check access on.
+   * @param oldRDN
+   *          The old RDN component.
+   * @param newRDN
+   *          The new RDN component.
+   * @return True if access is allowed.
+   */
+  private boolean aciCheckRDNs(LocalBackendModifyDNOperation operation,
+      RDN oldRDN, RDN newRDN)
+  {
+    boolean ret;
+
+    AciLDAPOperationContainer operationContainer =
+        new AciLDAPOperationContainer(operation, (ACI_WRITE), operation
+            .getOriginalEntry());
+    ret = accessAllowed(operationContainer);
+    if (ret)
+    {
+      ret = checkRDN(ACI_WRITE_ADD, newRDN, operationContainer);
+    }
+    if (ret && operation.deleteOldRDN())
+    {
+      ret = checkRDN(ACI_WRITE_DELETE, oldRDN, operationContainer);
+    }
+    return ret;
+  }
+
+
+
+  /**
+   * Check access on the new superior entry if it exists. If the entry
+   * does not exist or the DN cannot be locked then false is returned.
+   *
+   * @param superiorDN
+   *          The DN of the new superior entry.
+   * @param op
+   *          The modifyDN operation to check access on.
+   * @return True if access is granted to the new superior entry.
+   * @throws DirectoryException
+   *           If a problem occurs while trying to retrieve the new
+   *           superior entry.
+   */
+  private boolean aciCheckSuperiorEntry(DN superiorDN,
+      LocalBackendModifyDNOperation op) throws DirectoryException
+  {
+    boolean ret = false;
+    Lock entryLock = null;
+    for (int i = 0; i < 3; i++)
+    {
+      entryLock = LockManager.lockRead(superiorDN);
+      if (entryLock != null)
+      {
+        break;
+      }
+    }
+    if (entryLock == null)
+    {
+      Message message =
+          WARN_ACI_HANDLER_CANNOT_LOCK_NEW_SUPERIOR_USER.get(String
+              .valueOf(superiorDN));
+      logError(message);
+      return false;
+    }
+    try
+    {
+      Entry superiorEntry = DirectoryServer.getEntry(superiorDN);
+      if (superiorEntry != null)
+      {
+        AciLDAPOperationContainer operationContainer =
+            new AciLDAPOperationContainer(op, (ACI_IMPORT),
+                superiorEntry);
+        ret = accessAllowed(operationContainer);
+      }
+    }
+    finally
+    {
+      LockManager.unlock(superiorDN, entryLock);
+    }
+    return ret;
+  }
+
+
+
+  /**
+   * Check access on each attribute-value pair component of the
+   * specified RDN. There may be more than one attribute-value pair if
+   * the RDN is multi-valued.
+   *
+   * @param right
+   *          The access right to check for.
+   * @param rdn
+   *          The RDN to examine the attribute-value pairs of.
+   * @param container
+   *          The container containing the information needed to
+   *          evaluate the specified RDN.
+   * @return True if access is allowed for all attribute-value pairs.
+   */
+  private boolean checkRDN(int right, RDN rdn, AciContainer container)
+  {
+    boolean ret = false;
+    int numAVAs = rdn.getNumValues();
+    container.setRights(right);
+    for (int i = 0; i < numAVAs; i++)
+    {
+      AttributeType type = rdn.getAttributeType(i);
+      AttributeValue value = rdn.getAttributeValue(i);
+      container.setCurrentAttributeType(type);
+      container.setCurrentAttributeValue(value);
+      if (!(ret = accessAllowed(container)))
+      {
+        break;
+      }
+    }
+    return ret;
+  }
+
+
+
+  /**
+   * Creates the allow and deny ACI lists based on the provided target
+   * match context. These lists are stored in the evaluation context.
+   *
+   * @param candidates
+   *          List of all possible ACI candidates.
+   * @param targetMatchCtx
+   *          Target matching context to use for testing each ACI.
+   */
+  private void createApplicableList(LinkedList<Aci> candidates,
+      AciTargetMatchContext targetMatchCtx)
+  {
+    LinkedList<Aci> denys = new LinkedList<Aci>();
+    LinkedList<Aci> allows = new LinkedList<Aci>();
+    for (Aci aci : candidates)
+    {
+      if (Aci.isApplicable(aci, targetMatchCtx))
+      {
+        if (aci.hasAccessType(EnumAccessType.DENY))
+        {
+          denys.add(aci);
+        }
+        if (aci.hasAccessType(EnumAccessType.ALLOW))
+        {
+          allows.add(aci);
+        }
+      }
+      if (targetMatchCtx.getTargAttrFiltersMatch())
+      {
+        targetMatchCtx.setTargAttrFiltersMatch(false);
+      }
+    }
+    targetMatchCtx.setAllowList(allows);
+    targetMatchCtx.setDenyList(denys);
+  }
+
+
+
+  /**
+   * Gathers all of the attribute types in an entry along with the
+   * "objectclass" attribute type in a List. The "objectclass" attribute
+   * is added to the list first so it is evaluated first.
+   *
+   * @param e
+   *          Entry to gather the attributes for.
+   * @return List containing the attribute types.
+   */
+  private List<AttributeType> getAllAttrs(Entry e)
+  {
+    Map<AttributeType, List<Attribute>> attrMap = e.getUserAttributes();
+    Map<AttributeType, List<Attribute>> opAttrMap =
+        e.getOperationalAttributes();
+    List<AttributeType> typeList = new LinkedList<AttributeType>();
+    Attribute attr = e.getObjectClassAttribute();
+    /*
+     * When a search is not all attributes returned, the "objectclass"
+     * attribute type is missing from the entry.
+     */
+    if (attr != null)
+    {
+      AttributeType ocType = attr.getAttributeType();
+      typeList.add(ocType);
+    }
+    typeList.addAll(attrMap.keySet());
+    typeList.addAll(opAttrMap.keySet());
+    return typeList;
+  }
+
+
+
+  /**
+   * Check access using the accessAllowed method. The LDAP add, compare,
+   * modify and delete operations use this function. The other supported
+   * LDAP operations have more specialized checks.
+   *
+   * @param operationContainer
+   *          The container containing the information needed to
+   *          evaluate this operation.
+   * @param operation
+   *          The operation being evaluated.
+   * @return True if this operation is allowed access.
+   */
+  private boolean isAllowed(
+      AciLDAPOperationContainer operationContainer, Operation operation)
+  {
+    return skipAccessCheck(operation)
+        || accessAllowed(operationContainer);
+  }
+
+
+
+  /**
+   * Check if the specified attribute type is a DN by checking if its
+   * syntax OID is equal to the DN syntax OID.
+   *
+   * @param attribute
+   *          The attribute type to check.
+   * @return True if the attribute type syntax OID is equal to a DN
+   *         syntax OID.
+   */
+  private boolean isAttributeDN(AttributeType attribute)
+  {
+    return (attribute.getSyntaxOID().equals(SYNTAX_DN_OID));
+  }
+
+
+
+  /**
+   * Process all ACIs under the "cn=config" naming context and adds them
+   * to the ACI list cache. It also logs messages about the number of
+   * ACIs added to the cache. This method is called once at startup. It
+   * will put the server in lockdown mode if needed.
+   *
+   * @throws InitializationException
+   *           If there is an error searching for the ACIs in the naming
+   *           context.
+   */
+  private void processConfigAcis() throws InitializationException
+  {
+    try
+    {
+      DN configDN = DN.decode("cn=config");
+      LinkedHashSet<String> attrs = new LinkedHashSet<String>(1);
+      attrs.add("aci");
+      LinkedList<Message> failedACIMsgs = new LinkedList<Message>();
+      InternalClientConnection conn =
+          InternalClientConnection.getRootConnection();
+      InternalSearchOperation op =
+          conn.processSearch(configDN, SearchScope.WHOLE_SUBTREE,
+              DereferencePolicy.NEVER_DEREF_ALIASES, 0, 0, false,
+              SearchFilter.createFilterFromString("aci=*"), attrs);
+      if (!op.getSearchEntries().isEmpty())
+      {
+        int validAcis =
+            aciList.addAci(op.getSearchEntries(), failedACIMsgs);
+        if (!failedACIMsgs.isEmpty())
+        {
+          aciListenerMgr.logMsgsSetLockDownMode(failedACIMsgs);
+        }
+        Message message =
+            INFO_ACI_ADD_LIST_ACIS.get(Integer.toString(validAcis),
+                String.valueOf(configDN));
+        logError(message);
+      }
+    }
+    catch (DirectoryException e)
+    {
+      Message message = INFO_ACI_HANDLER_FAIL_PROCESS_ACI.get();
+      throw new InitializationException(message, e);
+    }
+  }
+
+
+
+  /**
+   * Process all global ACI attribute types found in the configuration
+   * entry and adds them to that ACI list cache. It also logs messages
+   * about the number of ACI attribute types added to the cache. This
+   * method is called once at startup. It also will put the server into
+   * lockdown mode if needed.
+   *
+   * @param configuration
+   *          The config handler containing the ACI configuration
+   *          information.
+   * @throws InitializationException
+   *           If there is an error reading the global ACIs from the
+   *           configuration entry.
+   */
+  private void processGlobalAcis(
+      DseeCompatAccessControlHandlerCfg configuration)
+      throws InitializationException
+  {
+    SortedSet<Aci> globalAcis = configuration.getGlobalACI();
+    try
+    {
+      if (globalAcis != null)
+      {
+        aciList.addAci(DN.nullDN(), globalAcis);
+        Message message =
+            INFO_ACI_ADD_LIST_GLOBAL_ACIS.get(Integer
+                .toString(globalAcis.size()));
+        logError(message);
+      }
+    }
+    catch (Exception e)
+    {
+      if (debugEnabled())
+      {
+        TRACER.debugCaught(DebugLogLevel.ERROR, e);
+      }
+      Message message =
+          INFO_ACI_HANDLER_FAIL_PROCESS_GLOBAL_ACI.get(String
+              .valueOf(configuration.dn()));
+      throw new InitializationException(message, e);
+    }
+  }
+
+
+
+  /**
+   * Check to see if the specified entry has the specified privilege.
+   *
+   * @param e
+   *          The entry to check privileges on.
+   * @return {@code true} if the entry has the specified privilege, or
+   *         {@code false} if not.
+   */
+  private boolean skipAccessCheck(Entry e)
+  {
+    return ClientConnection.hasPrivilege(e, Privilege.BYPASS_ACL);
+  }
+
+
+
+  /**
+   * Check to see if the client entry has BYPASS_ACL privileges for this
+   * operation.
+   *
+   * @param operation
+   *          The operation to check privileges on.
+   * @return True if access checking can be skipped because the
+   *         operation client connection has BYPASS_ACL privileges.
+   */
+  private boolean skipAccessCheck(Operation operation)
+  {
+    return operation.getClientConnection().hasPrivilege(
+        Privilege.BYPASS_ACL, operation);
+  }
+
+
+
+  /**
+   * Performs the test of the deny and allow access lists using the
+   * provided evaluation context. The deny list is checked first.
+   *
+   * @param evalCtx
+   *          The evaluation context to use.
+   * @return True if access is allowed.
+   */
+  private boolean testApplicableLists(AciEvalContext evalCtx)
+  {
+    EnumEvalResult res;
+    evalCtx.setEvalReason(EnumEvalReason.NO_REASON);
+    LinkedList<Aci> denys = evalCtx.getDenyList();
+    LinkedList<Aci> allows = evalCtx.getAllowList();
+    // If allows list is empty and not doing geteffectiverights return
+    // false.
+    if (allows.isEmpty()
+        && !(evalCtx.isGetEffectiveRightsEval()
+            && !evalCtx.hasRights(ACI_SELF) && evalCtx
+            .isTargAttrFilterMatchAciEmpty()))
+    {
+      evalCtx.setEvalReason(EnumEvalReason.NO_ALLOW_ACIS);
+      evalCtx.setDecidingAci(null);
+      return false;
+    }
+    evalCtx.setDenyEval(true);
+    for (Aci denyAci : denys)
+    {
+      res = Aci.evaluate(evalCtx, denyAci);
+      // Failure could be returned if a system limit is hit or
+      // search fails
+      if (res.equals(EnumEvalResult.FAIL))
+      {
+        evalCtx.setEvalReason(EnumEvalReason.EVALUATED_DENY_ACI);
+        evalCtx.setDecidingAci(denyAci);
+        return false;
+      }
+      else if (res.equals(EnumEvalResult.TRUE))
+      {
+        if (evalCtx.isGetEffectiveRightsEval()
+            && !evalCtx.hasRights(ACI_SELF)
+            && !evalCtx.isTargAttrFilterMatchAciEmpty())
+        {
+          // Iterate to next only if deny ACI contains a targattrfilters
+          // keyword.
+          if (AciEffectiveRights.setTargAttrAci(evalCtx, denyAci, true))
+          {
+            continue;
+          }
+          evalCtx.setEvalReason(EnumEvalReason.EVALUATED_DENY_ACI);
+          evalCtx.setDecidingAci(denyAci);
+          return false;
+        }
+        else
+        {
+          evalCtx.setEvalReason(EnumEvalReason.EVALUATED_DENY_ACI);
+          evalCtx.setDecidingAci(denyAci);
+          return false;
+        }
+      }
+    }
+    // Now check the allows -- flip the deny flag to false first.
+    evalCtx.setDenyEval(false);
+    for (Aci allowAci : allows)
+    {
+      res = Aci.evaluate(evalCtx, allowAci);
+      if (res.equals(EnumEvalResult.TRUE))
+      {
+        if (evalCtx.isGetEffectiveRightsEval()
+            && !evalCtx.hasRights(ACI_SELF)
+            && !evalCtx.isTargAttrFilterMatchAciEmpty())
+        {
+          // Iterate to next only if deny ACI contains a targattrfilters
+          // keyword.
+          if (AciEffectiveRights.setTargAttrAci(evalCtx, allowAci,
+              false))
+          {
+            continue;
+          }
+          evalCtx.setEvalReason(EnumEvalReason.EVALUATED_ALLOW_ACI);
+          evalCtx.setDecidingAci(allowAci);
+          return true;
+        }
+        else
+        {
+          evalCtx.setEvalReason(EnumEvalReason.EVALUATED_ALLOW_ACI);
+          evalCtx.setDecidingAci(allowAci);
+          return true;
+        }
+      }
+    }
+    // Nothing matched fall through.
+    evalCtx.setEvalReason(EnumEvalReason.NO_MATCHED_ALLOWS_ACIS);
+    evalCtx.setDecidingAci(null);
+    return false;
+  }
+
+
+
+  /**
+   * Test the attribute types of the search filter for access. This
+   * method supports the search right.
+   *
+   * @param container
+   *          The container used in the access evaluation.
+   * @param filter
+   *          The filter to check access on.
+   * @return True if all attribute types in the filter have access.
+   * @throws DirectoryException
+   *           If there is a problem matching the entry using the
+   *           provided filter.
+   */
+  private boolean testFilter(AciLDAPOperationContainer container,
+      SearchFilter filter) throws DirectoryException
+  {
+    boolean ret = true;
+    // If the resource entry has a dn equal to "cn=debugsearch" and it
+    // contains the special attribute type "debugsearchindex", then the
+    // resource entry is a pseudo entry created for debug purposes.
+    // Return true if that is the case.
+    if (debugSearchIndexDN.equals(container.getResourceDN())
+        && container.getResourceEntry().hasAttribute(debugSearchIndex))
+    {
       return true;
+    }
+    switch (filter.getFilterType())
+    {
+    case AND:
+    case OR:
+    {
+      for (SearchFilter f : filter.getFilterComponents())
+      {
+        if (!testFilter(container, f))
+        {
+          return false;
+        }
+      }
+      break;
+    }
+    case NOT:
+    {
+      ret = false;
+      SearchFilter f = filter.getNotComponent();
+      if (f.matchesEntry(container.getResourceEntry()))
+      {
+        ret = true;
+      }
+      if (ret)
+      {
+        ret = testFilter(container, f);
+      }
+      ret = !ret;
+      break;
+    }
+    default:
+    {
+      AttributeType attrType = filter.getAttributeType();
+      container.setCurrentAttributeType(attrType);
+      ret = accessAllowed(container);
+    }
+    }
+    return ret;
+  }
+
+
+
+  /**
+   * Evaluate an entry to be added to see if it has any "aci" attribute
+   * type. If it does, examines each "aci" attribute type value for
+   * syntax errors. All of the "aci" attribute type values must pass
+   * syntax check for the add operation to proceed. Any entry with an
+   * "aci" attribute type must have "modify-acl" privileges.
+   *
+   * @param entry
+   *          The entry to be examined.
+   * @param operation
+   *          The operation to to check privileges on.
+   * @param clientDN
+   *          The authorization DN.
+   * @return True if the entry has no ACI attributes or if all of the
+   *         "aci" attributes values pass ACI syntax checking.
+   */
+  private boolean verifySyntax(Entry entry, Operation operation,
+      DN clientDN)
+  {
+    if (entry.hasOperationalAttribute(aciType))
+    {
+      /*
+       * Check that the operation has "modify-acl" privileges since the
+       * entry to be added has an "aci" attribute type.
+       */
+      if (!operation.getClientConnection().hasPrivilege(
+          Privilege.MODIFY_ACL, operation))
+      {
+        Message message =
+            INFO_ACI_ADD_FAILED_PRIVILEGE.get(String.valueOf(entry
+                .getDN()), String.valueOf(clientDN));
+        logError(message);
+        return false;
+      }
+      List<Attribute> attributeList =
+          entry.getOperationalAttribute(aciType, null);
+      for (Attribute attribute : attributeList)
+      {
+        for (AttributeValue value : attribute)
+        {
+          try
+          {
+            DN dn = entry.getDN();
+            Aci.decode(value.getValue(), dn);
+          }
+          catch (AciException ex)
+          {
+            Message message =
+                WARN_ACI_ADD_FAILED_DECODE.get(String.valueOf(entry
+                    .getDN()), ex.getMessage());
+            logError(message);
+            return false;
+          }
+        }
+      }
+    }
+    return true;
   }
 }
-

--
Gitblit v1.10.0