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

dugan
10.02.2007 3067b8e17912839c60c6352fc4bbb37b7f156708
Add ACI support for proxy right. Issue #1489.
13 files modified
542 ■■■■■ changed files
opends/src/server/org/opends/server/api/AccessControlHandler.java 29 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/authorization/BasicAccessControlHandler.java 18 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/authorization/dseecompat/Aci.java 2 ●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/authorization/dseecompat/AciContainer.java 94 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/authorization/dseecompat/AciHandler.java 82 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/core/AddOperation.java 20 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/core/CompareOperation.java 20 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/core/DefaultAccessControlProvider.java 9 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/core/DeleteOperation.java 20 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/core/ModifyDNOperation.java 21 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/core/ModifyOperation.java 21 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/core/SearchOperation.java 21 ●●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/authorization/dseecompat/AciTests.java 185 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/api/AccessControlHandler.java
@@ -28,17 +28,10 @@
import org.opends.server.core.AddOperation;
import org.opends.server.core.BindOperation;
import org.opends.server.core.CompareOperation;
import org.opends.server.core.DeleteOperation;
import org.opends.server.core.ExtendedOperation;
import org.opends.server.core.ModifyDNOperation;
import org.opends.server.core.ModifyOperation;
import org.opends.server.core.SearchOperation;
import org.opends.server.core.*;
import org.opends.server.types.SearchResultEntry;
import org.opends.server.types.SearchResultReference;
import org.opends.server.types.Entry;
/**
@@ -237,5 +230,23 @@
   */
  public abstract boolean maySend(SearchOperation searchOperation,
                               SearchResultReference searchReference);
  /**
   * Indicates whether a proxied authorization control is allowed
   * based on the current operation and the new authorization
   * entry.
   *
   * @param operation
   *        The operation with which the proxied authorization
   *        control is associated.
   * @param newAuthorizationEntry
   *        The new authorization entry related to the
   *        proxied authorization control authorization ID.
   * @return  <CODE>true</CODE> if the operation should be allowed by
   *         the access control configuration, or <CODE>false</CODE>
   *         if not.
   */
  public abstract boolean isProxiedAuthAllowed(Operation operation,
                                        Entry newAuthorizationEntry);
}
opends/src/server/org/opends/server/authorization/BasicAccessControlHandler.java
@@ -28,16 +28,10 @@
import org.opends.server.api.AccessControlHandler;
import org.opends.server.core.AddOperation;
import org.opends.server.core.BindOperation;
import org.opends.server.core.CompareOperation;
import org.opends.server.core.DeleteOperation;
import org.opends.server.core.ExtendedOperation;
import org.opends.server.core.ModifyDNOperation;
import org.opends.server.core.ModifyOperation;
import org.opends.server.core.SearchOperation;
import org.opends.server.core.*;
import org.opends.server.types.SearchResultEntry;
import org.opends.server.types.SearchResultReference;
import org.opends.server.types.Entry;
/**
 * This class provides the implementation of the basic access control
@@ -169,4 +163,12 @@
    return true;
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public boolean isProxiedAuthAllowed(Operation operation, Entry entry) {
     return true;
  }
}
opends/src/server/org/opends/server/authorization/dseecompat/Aci.java
@@ -194,7 +194,7 @@
    public static final int ACI_ALL = 0x007F;
    /**
     * ACI_PROXY is used for the PROXY right. Currently not used.
     * ACI_PROXY is used for the PROXY right.
     */
    public static final int ACI_PROXY = 0x0080;
opends/src/server/org/opends/server/authorization/dseecompat/AciContainer.java
@@ -36,6 +36,7 @@
import org.opends.server.extensions.TLSConnectionSecurityProvider;
import java.net.InetAddress;
import java.util.LinkedList;
import static org.opends.server.authorization.dseecompat.AciHandler.*;
/**
 *  The AciContainer class contains all of the needed information to perform
@@ -109,6 +110,41 @@
     */
    private boolean targAttrFiltersMatch=false;
    /*
     * The authorization entry currently being evaluated. If proxied
     * authorization is being used and the handler is doing a proxy access
     * check, then this entry will switched to the original authorization entry
     * rather than the proxy ID entry. If the check succeeds, it will be
     * switched back for non-proxy access checking. If proxied authentication
     * is not being used then this entry never changes.
     */
    private Entry authorizationEntry;
    /*
     * Used to save the current authorization entry when the authorization
     * entry is switched during a proxy access check.
     */
    private Entry saveAuthorizationEntry;
    /*
     * This entry is only used if proxied authorization is being used.  It is
     * the original authorization entry before the proxied authorization change.
     */
    private Entry origAuthorizationEntry=null;
    /*
     * True if proxied authorization is being used.
     */
    private boolean proxiedAuthorization=false;
    /*
     * Used by proxied authorization processing. True if the entry has already
     * been processed by an access proxy check. Some operations might perform
     * several access checks on the same entry (modify DN), this
     * flag is used to bypass the proxy check after the initial evaluation.
     */
    private boolean seenEntry=false;
    /**
     * This constructor is used by all currently supported LDAP operations.
     *
@@ -123,9 +159,63 @@
      this.clientConnection=operation.getClientConnection();
      if(operation instanceof AddOperation)
          this.isAddOp=true;
      //If the proxied authorization control was processed, then the operation
      //will contain an attachment containing the original authorization entry.
      this.origAuthorizationEntry =
                      (Entry) operation.getAttachment(ORIG_AUTH_ENTRY);
      if(origAuthorizationEntry != null)
         this.proxiedAuthorization=true;
      this.authorizationEntry=operation.getAuthorizationEntry();
      //Reference the current authorization entry, so it can be put back
      //if an access proxy check was performed.
      this.saveAuthorizationEntry=this.authorizationEntry;
      this.rights = rights;
    }
  /**
   * Returns true if an entry has already been processed by an access proxy
   * check.
   * @return True if an entry has already been processed by an access proxy
   * check.
   */
   public boolean hasSeenEntry() {
      return this.seenEntry;
    }
  /**
   * Set to true if an entry has already been processsed by an access proxy
   * check.
   * @param val The value to set the seenEntry boolean to.
   */
    public void setSeenEntry(boolean val) {
     this.seenEntry=val;
    }
  /**
   * Returns true if proxied authorization is being used.
   * @return  True if proxied authorization is being used.
   */
    public boolean isProxiedAuthorization() {
         return this.proxiedAuthorization;
    }
  /**
   * If the specified value is true, then the original authorization entry,
   * which is the  entry before the switch performed by the proxied
   * authorization control processing should be set to the current
   * authorization entry. If the specified value is false then the proxied
   * authorization entry is switched back using the saved copy.
   * @param val The value used to select the authorization entry to use.
   */
    public void useOrigAuthorizationEntry(boolean val) {
      if(val)
        authorizationEntry=origAuthorizationEntry;
      else
        authorizationEntry=saveAuthorizationEntry;
    }
    /**
     * The list of deny ACIs. These are all of the applicable
     * ACIs that have a deny permission. Note that an ACI can
@@ -226,7 +316,7 @@
     * @return The client entry.
     */
    public Entry getClientEntry() {
      return operation.getAuthorizationEntry();
      return this.authorizationEntry;
    }
    /**
@@ -275,7 +365,7 @@
     * @return  The client's authorization DN.
     */
    public DN getClientDN() {
      return operation.getAuthorizationDN();
      return this.authorizationEntry.getDN();
    }
    /**
opends/src/server/org/opends/server/authorization/dseecompat/AciHandler.java
@@ -72,6 +72,12 @@
    public static AttributeType globalAciType;
    /**
     * String used to save the original authorization entry in an operation
     * attachment if a proxied authorization control was seen.
     */
    public static String ORIG_AUTH_ENTRY="origAuthorizationEntry";
    /**
     * This constructor instantiates the ACI handler class that performs the
     * main processing for the dseecompat ACI package. It does the following
     * initializations:
@@ -443,6 +449,32 @@
             return false;
          }
        }
        //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.hasRights(ACI_PROXY) &&
             container.isProxiedAuthorization()) {
              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.
         */
@@ -714,7 +746,6 @@
     * @return  True if access is allowed.
     */
   public boolean isAllowed(CompareOperation operation) {
       AciLDAPOperationContainer operationContainer =
               new AciLDAPOperationContainer(operation, ACI_COMPARE);
       String baseName;
@@ -803,10 +834,12 @@
   */
  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;
      if(!skipAccessCheck(operation)) {
          returnEntry=accessAllowedAttrs(operationContainer);
@@ -816,10 +849,10 @@
  }
  /**
   * Perform all needed RDN checks for the modifyDN operation. These checks
   * are:
   * 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 entry.
   *  - 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.
@@ -829,18 +862,21 @@
   *
   * @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(ModifyDNOperation operation) {
  private boolean aciCheckRDNs(ModifyDNOperation 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,operation.getNewRDN(),operationContainer);
          ret=checkRDN(ACI_WRITE_ADD, newRDN, operationContainer);
      if(ret && operation.deleteOldRDN()) {
          RDN oldRDN=operation.getOriginalEntry().getDN().getRDN();
          ret =
            checkRDN(ACI_WRITE_DELETE, oldRDN, operationContainer);
      }
@@ -923,6 +959,8 @@
  public boolean isAllowed(ModifyDNOperation 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.
@@ -933,21 +971,41 @@
               ret=false;
             }
          }
          //Perform the RDN access checks.
          if(ret)
              ret=aciCheckRDNs(operation);
          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());
                                             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;
  }
  //TODO Check access to control, issue #452.
  /**
   * Called when a proxied authorization control was decoded. Currently used
   * to save the current authorization entry in an operation attachment, but
   * eventually will be used to check access to the actual control.
   * @param operation The operation to save the attachment to.
   * @param entry  The new authorization entry.
   * @return  True if the control is allowed access.
   */
  public boolean isProxiedAuthAllowed(Operation operation, Entry entry) {
    operation.setAttachment(ORIG_AUTH_ENTRY, operation.getAuthorizationEntry());
    return true;
  }
  //Not planned to be implemented methods.
   /**
opends/src/server/org/opends/server/core/AddOperation.java
@@ -1767,7 +1767,17 @@
                break addProcessing;
              }
              if (AccessControlConfigManager.getInstance()
                      .getAccessControlHandler().isProxiedAuthAllowed(this,
                      authorizationEntry) == false) {
                setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS);
                int msgID = MSGID_ADD_AUTHZ_INSUFFICIENT_ACCESS_RIGHTS;
                appendErrorMessage(getMessage(msgID, String.valueOf(entryDN)));
                skipPostOperation = true;
                break addProcessing;
              }
              setAuthorizationEntry(authorizationEntry);
            }
            else if (oid.equals(OID_PROXIED_AUTH_V2))
@@ -1827,7 +1837,17 @@
                break addProcessing;
              }
              if (AccessControlConfigManager.getInstance()
                      .getAccessControlHandler().isProxiedAuthAllowed(this,
                      authorizationEntry) == false) {
                setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS);
                int msgID = MSGID_ADD_AUTHZ_INSUFFICIENT_ACCESS_RIGHTS;
                appendErrorMessage(getMessage(msgID, String.valueOf(entryDN)));
                skipPostOperation = true;
                break addProcessing;
              }
              setAuthorizationEntry(authorizationEntry);
            }
opends/src/server/org/opends/server/core/CompareOperation.java
@@ -824,7 +824,17 @@
                break compareProcessing;
              }
              if (AccessControlConfigManager.getInstance()
                      .getAccessControlHandler().isProxiedAuthAllowed(this,
                                                 authorizationEntry) == false) {
                setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS);
                int msgID = MSGID_COMPARE_AUTHZ_INSUFFICIENT_ACCESS_RIGHTS;
                appendErrorMessage(getMessage(msgID, String.valueOf(entryDN)));
                skipPostOperation = true;
                break compareProcessing;
              }
              setAuthorizationEntry(authorizationEntry);
            }
            else if (oid.equals(OID_PROXIED_AUTH_V2))
@@ -884,7 +894,17 @@
                break compareProcessing;
              }
              if (AccessControlConfigManager.getInstance()
                      .getAccessControlHandler().isProxiedAuthAllowed(this,
                                                 authorizationEntry) == false) {
                setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS);
                int msgID = MSGID_COMPARE_AUTHZ_INSUFFICIENT_ACCESS_RIGHTS;
                appendErrorMessage(getMessage(msgID, String.valueOf(entryDN)));
                skipPostOperation = true;
                break compareProcessing;
              }
              setAuthorizationEntry(authorizationEntry);
            }
opends/src/server/org/opends/server/core/DefaultAccessControlProvider.java
@@ -33,6 +33,7 @@
import org.opends.server.types.InitializationException;
import org.opends.server.types.SearchResultEntry;
import org.opends.server.types.SearchResultReference;
import org.opends.server.types.Entry;
/**
 * This class implements a default access control provider for the
@@ -197,5 +198,13 @@
      return true;
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public boolean isProxiedAuthAllowed(Operation operation, Entry entry) {
     return true;
    }
  }
}
opends/src/server/org/opends/server/core/DeleteOperation.java
@@ -817,7 +817,17 @@
                break deleteProcessing;
              }
              if (AccessControlConfigManager.getInstance()
                      .getAccessControlHandler().isProxiedAuthAllowed(this,
                      authorizationEntry) == false) {
                setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS);
                int msgID = MSGID_DELETE_AUTHZ_INSUFFICIENT_ACCESS_RIGHTS;
                appendErrorMessage(getMessage(msgID, String.valueOf(entryDN)));
                skipPostOperation = true;
                break deleteProcessing;
              }
              setAuthorizationEntry(authorizationEntry);
            }
            else if (oid.equals(OID_PROXIED_AUTH_V2))
@@ -876,7 +886,17 @@
                break deleteProcessing;
              }
              if (AccessControlConfigManager.getInstance()
                  .getAccessControlHandler().isProxiedAuthAllowed(this,
                                                authorizationEntry) == false) {
                setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS);
                int msgID = MSGID_DELETE_AUTHZ_INSUFFICIENT_ACCESS_RIGHTS;
                appendErrorMessage(getMessage(msgID, String.valueOf(entryDN)));
                skipPostOperation = true;
                break deleteProcessing;
              }
              setAuthorizationEntry(authorizationEntry);
            }
opends/src/server/org/opends/server/core/ModifyDNOperation.java
@@ -1277,7 +1277,17 @@
                break modifyDNProcessing;
              }
              if (AccessControlConfigManager.getInstance()
                      .getAccessControlHandler().isProxiedAuthAllowed(this,
                      authorizationEntry) == false) {
                setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS);
                int msgID = MSGID_MODDN_AUTHZ_INSUFFICIENT_ACCESS_RIGHTS;
                appendErrorMessage(getMessage(msgID, String.valueOf(entryDN)));
                skipPostOperation = true;
                break modifyDNProcessing;
              }
              setAuthorizationEntry(authorizationEntry);
            }
            else if (oid.equals(OID_PROXIED_AUTH_V2))
@@ -1336,6 +1346,17 @@
                break modifyDNProcessing;
              }
              if (AccessControlConfigManager.getInstance()
                  .getAccessControlHandler().isProxiedAuthAllowed(this,
                                                authorizationEntry) == false) {
                setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS);
                int msgID = MSGID_MODDN_AUTHZ_INSUFFICIENT_ACCESS_RIGHTS;
                appendErrorMessage(getMessage(msgID, String.valueOf(entryDN)));
                skipPostOperation = true;
                break modifyDNProcessing;
              }
              setAuthorizationEntry(authorizationEntry);
opends/src/server/org/opends/server/core/ModifyOperation.java
@@ -101,6 +101,7 @@
import static org.opends.server.loggers.Error.*;
import static org.opends.server.messages.CoreMessages.*;
import static org.opends.server.messages.MessageHandler.*;
import static org.opends.server.messages.MessageHandler.getMessage;
import static org.opends.server.util.ServerConstants.*;
import static org.opends.server.util.StaticUtils.*;
@@ -1080,7 +1081,17 @@
                break modifyProcessing;
              }
              if (AccessControlConfigManager.getInstance().
                      getAccessControlHandler().isProxiedAuthAllowed(this,
                      authorizationEntry) == false) {
                setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS);
                int msgID = MSGID_MODIFY_AUTHZ_INSUFFICIENT_ACCESS_RIGHTS;
                appendErrorMessage(getMessage(msgID, String.valueOf(entryDN)));
                skipPostOperation = true;
                break modifyProcessing;
              }
              setAuthorizationEntry(authorizationEntry);
            }
@@ -1141,7 +1152,17 @@
                break modifyProcessing;
              }
              if (AccessControlConfigManager.getInstance().
                      getAccessControlHandler().isProxiedAuthAllowed(this,
                      authorizationEntry) == false) {
                setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS);
                int msgID = MSGID_MODIFY_AUTHZ_INSUFFICIENT_ACCESS_RIGHTS;
                appendErrorMessage(getMessage(msgID, String.valueOf(entryDN)));
                skipPostOperation = true;
                break modifyProcessing;
              }
              setAuthorizationEntry(authorizationEntry);
            }
opends/src/server/org/opends/server/core/SearchOperation.java
@@ -1758,7 +1758,17 @@
              break searchProcessing;
            }
            if (AccessControlConfigManager.getInstance().
                 getAccessControlHandler().isProxiedAuthAllowed(this,
                                                 authorizationEntry) == false) {
              setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS);
              int msgID = MSGID_SEARCH_AUTHZ_INSUFFICIENT_ACCESS_RIGHTS;
              appendErrorMessage(getMessage(msgID, String.valueOf(baseDN)));
              skipPostOperation = true;
              break searchProcessing;
            }
            setAuthorizationEntry(authorizationEntry);
          }
          else if (oid.equals(OID_PROXIED_AUTH_V2))
@@ -1818,6 +1828,17 @@
              break searchProcessing;
            }
            if (AccessControlConfigManager.getInstance()
                .getAccessControlHandler().isProxiedAuthAllowed(this,
                                                 authorizationEntry) == false) {
              setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS);
              int msgID = MSGID_SEARCH_AUTHZ_INSUFFICIENT_ACCESS_RIGHTS;
              appendErrorMessage(getMessage(msgID, String.valueOf(baseDN)));
              skipPostOperation = true;
              break searchProcessing;
            }
            setAuthorizationEntry(authorizationEntry);
          }
opends/tests/unit-tests-testng/src/server/org/opends/server/authorization/dseecompat/AciTests.java
@@ -182,6 +182,8 @@
  private static final String LEVEL_1_USER_DN = "cn=level1 user," + OU_BASE_DN;
  private static final String LEVEL_2_USER_DN = "cn=level2 user," + OU_INNER_DN;
  private static final String LEVEL_3_USER_DN = "cn=level3 user," + OU_LEAF_DN;
  //The proxy DN.
  private static final String PROXY_USER_DN = "cn=proxy user," + OU_BASE_DN;
  // We need to delete all of these between each test.  This list needs to be
  // bottom up so that it can be handed to LDAPDelete.
@@ -189,6 +191,7 @@
    SALES_USER_1,
    SALES_USER_2,
    SALES_USER_3,
    PROXY_USER_DN,
    LEVEL_3_USER_DN,
    LEVEL_2_USER_DN,
    LEVEL_1_USER_DN,
@@ -209,6 +212,10 @@
  private static final String BIND_RULE_USERDN_ALL = "userdn=\"ldap:///all\"";
  private static final String BIND_RULE_USERDN_ADMIN = "userdn=\"ldap:///" + ADMIN_DN + "\"";
  private static final String BIND_RULE_USERDN_LEVEL_1 = "userdn=\"ldap:///" + LEVEL_1_USER_DN + "\"";
  //The proxy userdn bind rule.
  private static final String BIND_RULE_USERDN_PROXY =
                                     "userdn=\"ldap:///" + PROXY_USER_DN + "\"";
  private static final String BIND_RULE_USERDN_ANYONE = "userdn=\"ldap:///anyone\"";
  private static final String BIND_RULE_USERDN_PARENT = "userdn=\"ldap:///parent\"";
  private static final String BIND_RULE_USERDN_CN_RDN = "userdn=\"ldap:///CN=*,dc=example,dc=com\"";
@@ -306,6 +313,36 @@
  private static final String ALLOW_ALL_TO_COMPARE =
             buildAciValue("name", "allow compare", "targetattr", "*", "target", "ldap:///cn=*," + OU_LEAF_DN, "allow(compare)", BIND_RULE_USERDN_ALL);
  //The ACIs for the proxy tests.
  private static final String ALLOW_PROXY_TO_IMPORT_MGR_NEW =
          buildAciValue("name", "allow proxy import new mgr new tree", "target",
                     MGR_NEW_DN_URL, "allow(import)", BIND_RULE_USERDN_PROXY);
  private static final String ALLOW_PROXY_TO_IMPORT_MGR=
             buildAciValue("name", "allow proxy import mgr tree", "target",
                     MGR_DN_URL, "allow(import)", BIND_RULE_USERDN_PROXY);
  private static final String ALLOW_PROXY_TO_EXPORT_MGR_NEW =
          buildAciValue("name", "allow proxy export new mgr new tree", "target",
                     MGR_NEW_DN_URL, "allow(export)", BIND_RULE_USERDN_PROXY);
  private static final String ALLOW_PROXY_TO_EXPORT_MGR=
             buildAciValue("name", "allow proxy export mgr tree", "target",
                     MGR_DN_URL, "allow(export)", BIND_RULE_USERDN_PROXY);
  private static final String ALLOW_PROXY_TO_WRITE_RDN_ATTRS=
           buildAciValue("name", "allow proxy write to RDN attrs", "targetattr",
                     "uid || cn || sn", "allow(write)", BIND_RULE_USERDN_PROXY);
  private static final String ALLOW_PROXY_TO_MOVED_ENTRY =
          buildAciValue("name", "allow proxy to moved entry", "targetattr", "*",
                     "allow(search,read)", BIND_RULE_USERDN_PROXY);
   private static final String ALLOW_PROXY_TO_LEVEL1 =
          buildAciValue("name", "allow proxy to userdn level1", "targetattr", "*",
                     "allow(proxy)", BIND_RULE_USERDN_LEVEL_1);
  private static final String ALLOW_ALL_TO_IMPORT_MGR_NEW =
             buildAciValue("name", "allow import mgr new tree", "target", MGR_NEW_DN_URL, "allow(import)", BIND_RULE_USERDN_ALL);
@@ -959,6 +996,8 @@
  private static final String LEVEL_1_USER_LDIF__SEARCH_TESTS = makeUserLdif(LEVEL_1_USER_DN, "level1", "user", "pa$$word");
  private static final String LEVEL_2_USER_LDIF__SEARCH_TESTS = makeUserLdif(LEVEL_2_USER_DN, "level2", "user", "pa$$word");
  private static final String LEVEL_3_USER_LDIF__SEARCH_TESTS = makeUserLdif(LEVEL_3_USER_DN, "level3", "user", "pa$$word");
  private static final String PROXY_USER_LDIF__SEARCH_TESTS =
                       makeUserLdif(PROXY_USER_DN, "proxy", "user", "pa$$word");
    private static final String SALES_USER_1__SEARCH_TESTS =
@@ -1050,7 +1089,7 @@
  String SELFWRITE_ACI =  makeAddAciLdif(OU_GROUP_1_DN,
                                        ALLOW_ALL_TO_SELFWRITE);
  //ACIs used for modDN tests (export, import)
  //ACIs used for standard modDN tests (export, import)
 private static final  String ACI_IMPORT_MGR_NEW =
                   makeAddAciLdif(OU_BASE_DN, ALLOW_ALL_TO_IMPORT_MGR_NEW);
@@ -1064,12 +1103,35 @@
 private static final  String ACI_EXPORT_MGR =
                   makeAddAciLdif(OU_BASE_DN, ALLOW_ALL_TO_EXPORT_MGR);
  private static final String ACI_WRITE_RDN_ATTRS =
 private static final String ACI_WRITE_RDN_ATTRS =
                   makeAddAciLdif(OU_BASE_DN, ALLOW_ALL_TO_WRITE_RDN_ATTRS);
   private static final String ACI_MOVED_ENTRY =
 private static final String ACI_MOVED_ENTRY =
                   makeAddAciLdif(SALES_USER_1, ALLOW_ALL_TO_MOVED_ENTRY);
//ACIs used for proxied auth modDN tests
  private static final  String ACI_PROXY_IMPORT_MGR_NEW =
                   makeAddAciLdif(OU_BASE_DN, ALLOW_PROXY_TO_IMPORT_MGR_NEW);
 private static final  String ACI_PROXY_IMPORT_MGR =
                   makeAddAciLdif(OU_BASE_DN, ALLOW_PROXY_TO_IMPORT_MGR);
 private static final  String ACI_PROXY_EXPORT_MGR_NEW =
                   makeAddAciLdif(OU_BASE_DN, ALLOW_PROXY_TO_EXPORT_MGR_NEW);
 private static final  String ACI_PROXY_EXPORT_MGR =
                   makeAddAciLdif(OU_BASE_DN, ALLOW_PROXY_TO_EXPORT_MGR);
 private static final String ACI_PROXY_WRITE_RDN_ATTRS =
                   makeAddAciLdif(OU_BASE_DN, ALLOW_PROXY_TO_WRITE_RDN_ATTRS);
 private static final String ACI_PROXY_LEVEL_1=
                   makeAddAciLdif(OU_BASE_DN, ALLOW_PROXY_TO_LEVEL1);
 private static final String ACI_PROXY_MOVED_ENTRY =
                   makeAddAciLdif(SALES_USER_1, ALLOW_PROXY_TO_MOVED_ENTRY);
//ACI used in testing the groupdn/roledn bind rule keywords.
 private static final
@@ -1116,6 +1178,7 @@
            GROUP_1_LDIF__SEARCH_TESTS +
            GROUP_2_LDIF__SEARCH_TESTS +
            LEVEL_1_USER_LDIF__SEARCH_TESTS +
            PROXY_USER_LDIF__SEARCH_TESTS +
            INNER_OU_FULL_LDIF__SEARCH_TESTS;
  private static final String NO_ACIS_LDIF = "";
@@ -1484,6 +1547,7 @@
  private static class SingleSearchParams {
    private final String _bindDn;
    private final String _bindPw;
    private final String _proxyDN;
    private final String _searchBaseDn;
    private final String _searchFilter;
    private final String _searchScope;
@@ -1491,9 +1555,25 @@
    private final String _initialDitLdif;
    private final String _aciLdif;
    public SingleSearchParams(String bindDn, String bindPw, String proxyDN,
                              String searchBaseDn, String searchFilter,
                              String searchScope, String expectedResultsLdif,
                              String initialDitLdif, String aciLdif) {
      _bindDn = bindDn;
      _bindPw = bindPw;
      _proxyDN=proxyDN;
      _searchBaseDn = searchBaseDn;
      _searchFilter = searchFilter;
      _searchScope = searchScope;
      _expectedResultsLdif = expectedResultsLdif;
      _initialDitLdif = initialDitLdif;
      _aciLdif = aciLdif;
    }
    public SingleSearchParams(String bindDn, String bindPw, String searchBaseDn, String searchFilter, String searchScope, String expectedResultsLdif, String initialDitLdif, String aciLdif) {
      _bindDn = bindDn;
      _bindPw = bindPw;
      _proxyDN = null;
      _searchBaseDn = searchBaseDn;
      _searchFilter = searchFilter;
      _searchScope = searchScope;
@@ -1505,6 +1585,7 @@
    public SingleSearchParams(SingleSearchParams that, String initialDitLdif, String aciLdif) {
      _bindDn = that._bindDn;
      _bindPw = that._bindPw;
      _proxyDN = null;
      _searchBaseDn = that._searchBaseDn;
      _searchFilter = that._searchFilter;
      _searchScope = that._searchScope;
@@ -1525,6 +1606,16 @@
          "-b", _searchBaseDn,
          "-s", _searchScope,
          _searchFilter};
      } else if(_proxyDN != null) {
        return new String[]{
          "-h", "127.0.0.1",
          "-p", getServerLdapPort(),
          "-D", _bindDn,
          "-w", _bindPw,
          "-b", _searchBaseDn,
          "-s", _searchScope,
          "-Y", "dn:" + _proxyDN,
          _searchFilter};
      } else {
        return new String[]{
          "-h", "127.0.0.1",
@@ -1659,6 +1750,58 @@
      }
  }
  /**
   * Test proxy keyword using modify DN. Exact test as testModDN, except using
   * proxied authorization for modifies and searches.
   *
   * Add a set of ACIs to allow exports, imports and write rights to the
   * proxy user PROXY_USER_DN. Also add an aci low in the DIT, with search and
   * read rights to the proxy user. This is ACI is to test the
   * ACI list after a move has been made. Add an ACI that allows LEVEL_1_USER_DN
   * proxy authorization rights (proxy).
   *
   * Move the subtree binding as LEVEL_1_USER_DN using proxied authorization,
   * search with base at new DN binding as LEVEL_1_USER_DN proxied
   * authorization, then move the tree back binding as LEVEL_1_USER_DN using
   * proxied authorization and lastly re-search with base at orig DN
   * binding as LEVEL_1_USER_DN using proxied authorization.
   * @throws Throwable
   */
  @Test
  public void testProxyModDN() throws Throwable {
    SingleSearchParams userParamOrig = new SingleSearchParams(LEVEL_1_USER_DN,
                                      "pa$$word",PROXY_USER_DN, SALES_USER_1,
                                      OBJECTCLASS_STAR, SCOPE_BASE,
                                      null, null, null);
    SingleSearchParams userParamNew = new SingleSearchParams(LEVEL_1_USER_DN,
                                     "pa$$word",PROXY_USER_DN, SALES_USER_NEW_1,
                                      OBJECTCLASS_STAR, SCOPE_BASE,
                                      null, null, null);
    try {
      addEntries(BASIC_LDIF__GROUP_SEARCH_TESTS, DIR_MGR_DN, DIR_MGR_PW);
      modEntries(ACI_PROXY_IMPORT_MGR, DIR_MGR_DN, DIR_MGR_PW);
      modEntries(ACI_PROXY_IMPORT_MGR_NEW, DIR_MGR_DN, DIR_MGR_PW);
      modEntries(ACI_PROXY_EXPORT_MGR, DIR_MGR_DN, DIR_MGR_PW);
      modEntries(ACI_PROXY_EXPORT_MGR_NEW, DIR_MGR_DN, DIR_MGR_PW);
      modEntries(ACI_PROXY_WRITE_RDN_ATTRS, DIR_MGR_DN, DIR_MGR_PW);
      modEntries(ACI_PROXY_MOVED_ENTRY, DIR_MGR_DN, DIR_MGR_PW);
      modEntries(ACI_PROXY_LEVEL_1, DIR_MGR_DN, DIR_MGR_PW);
      String modrdnLdif =
              makeModDN(SALES_DN, "cn=sales dept", "0", MANAGER_NEW_DN);
      modEntries(modrdnLdif, LEVEL_1_USER_DN, "pa$$word", PROXY_USER_DN);
      String userNewResults = ldapSearch(userParamNew.getLdapSearchArgs());
      Assert.assertFalse(userNewResults.equals(""));
      String modrdnLdif1 =
                makeModDN(SALES_NEW_DN, "cn=sales dept", "0", MANAGER_DN);
      modEntries(modrdnLdif1, LEVEL_1_USER_DN, "pa$$word", PROXY_USER_DN);
      String userOrigResults = ldapSearch(userParamOrig.getLdapSearchArgs());
      Assert.assertFalse(userOrigResults.equals(""));
    } catch (Throwable e)  {
      throw e;
    }
  }
  /**
   * Test modify DN. Add a set of ACIs to allow exports, imports and write
   * rights. Also add an aci low in the DIT to test the ACI list after a move
@@ -1997,20 +2140,22 @@
    Assert.assertEquals(0, retVal,  "Non-zero return code because, error: " + getOutputStreamContents());
    return getOutputStreamContents();
  }
  /**
   *
   */
  private void
  modEntries(String ldif, String bindDn, String bindPassword)
          throws Exception {
    modEntries(ldif, bindDn, bindPassword, true, false);
    modEntries(ldif, bindDn, bindPassword, null, true, false);
  }
  /**
   *
   */
  private void modEntriesExpectFailure(String ldif, String bindDn, String bindPassword) throws Exception {
    modEntries(ldif, bindDn, bindPassword, false, false);
  private void
  modEntries(String ldif, String bindDn, String bindPassword, String proxyDN)
          throws Exception {
    modEntries(ldif, bindDn, bindPassword, proxyDN, true, false);
  }
  private void modEntriesExpectFailure(String ldif, String bindDn,
                                       String bindPassword) throws Exception {
    modEntries(ldif, bindDn, bindPassword, null, false, false);
  }
  private void _modEntries(String ldif, String bindDn, String bindPassword,
@@ -2031,7 +2176,8 @@
  }
    private void modEntries(String ldif, String bindDn, String bindPassword,
                          boolean expectSuccess, boolean contFlag)
                          String proxyDN, boolean expectSuccess,
                          boolean contFlag)
    throws Exception {
    File tempFile = getTemporaryLdifFile();
    TestCaseUtils.writeFile(tempFile, ldif);
@@ -2048,6 +2194,10 @@
    argList.add(tempFile.getAbsolutePath());
    if(contFlag)
        argList.add("-c");
    if(proxyDN != null) {
        argList.add("-Y");
        argList.add("dn:" + proxyDN);
    }
    String[] args = new String[argList.size()];
    ldapModify(argList.toArray(args), expectSuccess);
  }
@@ -2065,7 +2215,7 @@
                "changetype: modify",
                "delete: " + attr,
                attr + ":" + val));
        modEntries(ldif.toString(), bindDN, pwd, errorOk, false);
        modEntries(ldif.toString(), bindDN, pwd, null, errorOk, false);
    }
@@ -2090,7 +2240,7 @@
                  "dn: "  + dn,
                  "changetype: modify",
                  "delete: " + attr));
          modEntries(ldif.toString(), DIR_MGR_DN, DIR_MGR_PW, errorOk, false);
          modEntries(ldif.toString(), DIR_MGR_DN, DIR_MGR_PW,  null, errorOk, false);
      }
    private void deleteEntries(String[] entries) throws Exception {
@@ -2102,7 +2252,7 @@
                    "changetype: delete"
            ));
        }
        modEntries(ldif.toString(), DIR_MGR_DN, DIR_MGR_PW, true, true);
        modEntries(ldif.toString(), DIR_MGR_DN, DIR_MGR_PW, null, true, true);
    }
  /**
@@ -2214,7 +2364,8 @@
            "cn: " +  cn,
            "sn: " + sn,
            "givenName: " + givenName,
            "userpassword: " + password);
            "userpassword: " + password,
            "ds-privilege-name: proxied-auth");
  }
    private static String makeUserLdif(String dn, String givenName, String sn,