From 74d7af9059994d7c6e1b08316429b8dcb017a70b Mon Sep 17 00:00:00 2001
From: Chris Ridd <chris.ridd@forgerock.com>
Date: Thu, 04 Jun 2015 10:53:55 +0000
Subject: [PATCH] FR-721 OPENDJ-2071 improve aci checks for proxy auth controls

---
 opendj-server-legacy/src/main/java/org/opends/server/core/DeleteOperationWrapper.java                                |   16 -
 opendj-server-legacy/src/main/java/org/opends/server/core/ModifyOperationWrapper.java                                |   15 
 opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendAddOperation.java      |   42 --
 opendj-server-legacy/src/messages/org/opends/messages/protocol.properties                                            |    4 
 opendj-server-legacy/src/main/java/org/opends/server/core/CompareOperationWrapper.java                               |   15 -
 opendj-server-legacy/src/main/java/org/opends/server/authorization/dseecompat/AciHandler.java                        |   30 --
 opendj-server-legacy/src/main/java/org/opends/server/extensions/WhoAmIExtendedOperation.java                         |   11 
 opendj-server-legacy/src/main/java/org/opends/server/core/UnbindOperationBasis.java                                  |   14 
 opendj-server-legacy/src/main/java/org/opends/server/core/SearchOperationWrapper.java                                |   15 
 opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendDeleteOperation.java   |   42 --
 opendj-server-legacy/src/main/java/org/opends/server/core/AddOperationWrapper.java                                   |   16 -
 opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendModifyOperation.java   |   41 --
 opendj-server-legacy/src/test/java/org/opends/server/authorization/dseecompat/ProxyTestCase.java                     |  211 ++++++++++++++
 opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendCompareOperation.java  |   54 ---
 opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendModifyDNOperation.java |   42 --
 opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElement.java   |  101 ++++++
 opendj-server-legacy/src/main/java/org/opends/server/core/BindOperationBasis.java                                    |   15 
 opendj-server-legacy/src/main/java/org/opends/server/core/ExtendedOperationBasis.java                                |   16 +
 opendj-server-legacy/src/main/java/org/opends/server/core/AbandonOperationBasis.java                                 |   17 +
 opendj-server-legacy/src/main/java/org/opends/server/core/ModifyDNOperationWrapper.java                              |   15 
 opendj-server-legacy/src/main/java/org/opends/server/types/Operation.java                                            |   23 +
 opendj-server-legacy/src/test/java/org/opends/server/authorization/dseecompat/AciTestCase.java                       |   26 +
 opendj-server-legacy/src/main/java/org/opends/server/authorization/dseecompat/AciContainer.java                      |   34 --
 opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendSearchOperation.java   |   40 --
 opendj-server-legacy/src/main/java/org/opends/server/core/OperationWrapper.java                                      |   14 
 25 files changed, 468 insertions(+), 401 deletions(-)

diff --git a/opendj-server-legacy/src/main/java/org/opends/server/authorization/dseecompat/AciContainer.java b/opendj-server-legacy/src/main/java/org/opends/server/authorization/dseecompat/AciContainer.java
index 47c8145..aefa568 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/authorization/dseecompat/AciContainer.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/authorization/dseecompat/AciContainer.java
@@ -118,18 +118,6 @@
     private Entry authorizationEntry;
 
     /**
-     * Used to save the current authorization entry when the authorization
-     * entry is switched during a proxy access check.
-     */
-    private final 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;
-
-    /**
      * True if proxied authorization is being used.
      */
     private boolean proxiedAuthorization;
@@ -248,8 +236,7 @@
 
       //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);
+      final Entry origAuthorizationEntry = (Entry) operation.getAttachment(ORIG_AUTH_ENTRY);
       this.proxiedAuthorization = origAuthorizationEntry != null;
       this.authorizationEntry=operation.getAuthorizationEntry();
 
@@ -292,8 +279,7 @@
 
       //Reference the current authorization entry, so it can be put back
       //if an access proxy check was performed.
-      this.saveAuthorizationEntry=this.authorizationEntry;
-      this.rightsMask = rights;
+        this.rightsMask = rights;
     }
 
     /**
@@ -312,7 +298,6 @@
         this.clientConnection=operation.getClientConnection();
         this.authInfo = authInfo;
         this.authorizationEntry = authInfo.getAuthorizationEntry();
-        this.saveAuthorizationEntry=this.authorizationEntry;
         this.rightsMask = rights;
     }
   /**
@@ -489,21 +474,6 @@
      return this.authzid.equals(this.authorizationEntry.getName());
     }
 
-  /**
-   * 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;
-    }
-
     /** {@inheritDoc} */
     @Override
     public void setDenyList(List<Aci> denys) {
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/authorization/dseecompat/AciHandler.java b/opendj-server-legacy/src/main/java/org/opends/server/authorization/dseecompat/AciHandler.java
index 938995e..e59db6d 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/authorization/dseecompat/AciHandler.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/authorization/dseecompat/AciHandler.java
@@ -559,36 +559,6 @@
       }
     }
 
-    // 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.
     List<Aci> candidates = aciList.getCandidateAcis(dn);
     /*
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/core/AbandonOperationBasis.java b/opendj-server-legacy/src/main/java/org/opends/server/core/AbandonOperationBasis.java
index d8b4b81..c05f050 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/core/AbandonOperationBasis.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/core/AbandonOperationBasis.java
@@ -97,6 +97,23 @@
 
   /** {@inheritDoc} */
   @Override
+  public DN getProxiedAuthorizationDN()
+  {
+    return null;
+  }
+
+
+
+  /** {@inheritDoc} */
+  @Override
+  public void setProxiedAuthorizationDN(DN proxiedAuthorizationDN)
+  {
+  }
+
+
+
+  /** {@inheritDoc} */
+  @Override
   public final OperationType getOperationType()
   {
     // Note that no debugging will be done in this method because it is a likely
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/core/AddOperationWrapper.java b/opendj-server-legacy/src/main/java/org/opends/server/core/AddOperationWrapper.java
index 367077b..bd3c7af 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/core/AddOperationWrapper.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/core/AddOperationWrapper.java
@@ -22,7 +22,7 @@
  *
  *
  *      Copyright 2008 Sun Microsystems, Inc.
- *      Portions Copyright 2013-2014 ForgeRock AS
+ *      Portions Copyright 2013-2015 ForgeRock AS
  */
 package org.opends.server.core;
 
@@ -150,18 +150,4 @@
     return getOperation().toString();
   }
 
-  /** {@inheritDoc} */
-  @Override
-  public DN getProxiedAuthorizationDN()
-  {
-    return getOperation().getProxiedAuthorizationDN();
-  }
-
-  /** {@inheritDoc} */
-  @Override
-  public void setProxiedAuthorizationDN(DN proxiedAuthorizationDN)
-  {
-    getOperation().setProxiedAuthorizationDN(proxiedAuthorizationDN);
-  }
-
 }
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/core/BindOperationBasis.java b/opendj-server-legacy/src/main/java/org/opends/server/core/BindOperationBasis.java
index 6f33b29..b2e7b38 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/core/BindOperationBasis.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/core/BindOperationBasis.java
@@ -22,7 +22,7 @@
  *
  *
  *      Copyright 2007-2010 Sun Microsystems, Inc.
- *      Portions Copyright 2013-2014 ForgeRock AS
+ *      Portions Copyright 2013-2015 ForgeRock AS
  */
 package org.opends.server.core;
 
@@ -257,6 +257,19 @@
 
   /** {@inheritDoc} */
   @Override
+  public DN getProxiedAuthorizationDN()
+  {
+    return null;
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public void setProxiedAuthorizationDN(DN proxiedAuthorizationDN)
+  {
+  }
+
+  /** {@inheritDoc} */
+  @Override
   public final AuthenticationType getAuthenticationType()
   {
     return authType;
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/core/CompareOperationWrapper.java b/opendj-server-legacy/src/main/java/org/opends/server/core/CompareOperationWrapper.java
index b9a877a..f7d67db 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/core/CompareOperationWrapper.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/core/CompareOperationWrapper.java
@@ -140,19 +140,4 @@
   }
 
 
-  /** {@inheritDoc} */
-  @Override
-  public DN getProxiedAuthorizationDN()
-  {
-    return getOperation().getProxiedAuthorizationDN();
-  }
-
-
-  /** {@inheritDoc} */
-  @Override
-  public void setProxiedAuthorizationDN(DN proxiedAuthorizationDN)
-  {
-    getOperation().setProxiedAuthorizationDN(proxiedAuthorizationDN);
-  }
-
 }
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/core/DeleteOperationWrapper.java b/opendj-server-legacy/src/main/java/org/opends/server/core/DeleteOperationWrapper.java
index 612333b..e77158b 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/core/DeleteOperationWrapper.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/core/DeleteOperationWrapper.java
@@ -22,7 +22,7 @@
  *
  *
  *      Copyright 2008 Sun Microsystems, Inc.
- *      Portions Copyright 2013-2014 ForgeRock AS
+ *      Portions Copyright 2013-2015 ForgeRock AS
  */
 package org.opends.server.core;
 
@@ -76,18 +76,4 @@
     return getOperation().toString();
   }
 
-  /** {@inheritDoc} */
-  @Override
-  public DN getProxiedAuthorizationDN()
-  {
-    return getOperation().getProxiedAuthorizationDN();
-  }
-
-  /** {@inheritDoc} */
-  @Override
-  public void setProxiedAuthorizationDN(DN proxiedAuthorizationDN)
-  {
-    getOperation().setProxiedAuthorizationDN(proxiedAuthorizationDN);
-  }
-
 }
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/core/ExtendedOperationBasis.java b/opendj-server-legacy/src/main/java/org/opends/server/core/ExtendedOperationBasis.java
index 369738d..a4355b0 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/core/ExtendedOperationBasis.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/core/ExtendedOperationBasis.java
@@ -147,6 +147,22 @@
 
   /** {@inheritDoc} */
   @Override
+  public DN getProxiedAuthorizationDN()
+  {
+    return null;
+  }
+
+
+
+  /** {@inheritDoc} */
+  @Override
+  public void setProxiedAuthorizationDN(DN proxiedAuthorizationDN)
+  {
+  }
+
+
+  /** {@inheritDoc} */
+  @Override
   public final ByteString getRequestValue()
   {
     return requestValue;
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/core/ModifyDNOperationWrapper.java b/opendj-server-legacy/src/main/java/org/opends/server/core/ModifyDNOperationWrapper.java
index 47e6410..d829234 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/core/ModifyDNOperationWrapper.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/core/ModifyDNOperationWrapper.java
@@ -22,7 +22,7 @@
  *
  *
  *      Copyright 2008 Sun Microsystems, Inc.
- *      Portions Copyright 2013-2014 ForgeRock AS
+ *      Portions Copyright 2013-2015 ForgeRock AS
  */
 package org.opends.server.core;
 
@@ -94,12 +94,6 @@
 
   /** {@inheritDoc} */
   @Override
-  public DN getProxiedAuthorizationDN() {
-    return getOperation().getProxiedAuthorizationDN();
-  }
-
-  /** {@inheritDoc} */
-  @Override
   public ByteString getRawEntryDN() {
     return getOperation().getRawEntryDN();
   }
@@ -148,13 +142,6 @@
 
   /** {@inheritDoc} */
   @Override
-  public void setProxiedAuthorizationDN(DN dn)
-  {
-    getOperation().setProxiedAuthorizationDN(dn);
-  }
-
-  /** {@inheritDoc} */
-  @Override
   public DN getNewDN()
   {
     return getOperation().getNewDN();
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/core/ModifyOperationWrapper.java b/opendj-server-legacy/src/main/java/org/opends/server/core/ModifyOperationWrapper.java
index c14a3f8..ab55238 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/core/ModifyOperationWrapper.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/core/ModifyOperationWrapper.java
@@ -22,7 +22,7 @@
  *
  *
  *      Copyright 2008 Sun Microsystems, Inc.
- *      Portions Copyright 2011-2014 ForgeRock AS
+ *      Portions Copyright 2011-2015 ForgeRock AS
  */
 package org.opends.server.core;
 
@@ -114,17 +114,4 @@
     return getOperation().toString();
   }
 
-  /** {@inheritDoc} */
-  @Override
-  public DN getProxiedAuthorizationDN()
-  {
-    return getOperation().getProxiedAuthorizationDN();
-  }
-
-  /** {@inheritDoc} */
-  @Override
-  public void setProxiedAuthorizationDN(DN proxiedAuthorizationDN){
-    getOperation().setProxiedAuthorizationDN(proxiedAuthorizationDN);
-  }
-
 }
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/core/OperationWrapper.java b/opendj-server-legacy/src/main/java/org/opends/server/core/OperationWrapper.java
index 37faadf..d308c95 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/core/OperationWrapper.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/core/OperationWrapper.java
@@ -160,6 +160,20 @@
 
   /** {@inheritDoc} */
   @Override
+  public DN getProxiedAuthorizationDN()
+  {
+    return operation.getProxiedAuthorizationDN();
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public void setProxiedAuthorizationDN(DN proxiedAuthorizationDN)
+  {
+    operation.setProxiedAuthorizationDN(proxiedAuthorizationDN);
+  }
+
+  /** {@inheritDoc} */
+  @Override
   public CancelRequest getCancelRequest()
   {
     return operation.getCancelRequest();
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/core/SearchOperationWrapper.java b/opendj-server-legacy/src/main/java/org/opends/server/core/SearchOperationWrapper.java
index a0bc3f4..dedb840 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/core/SearchOperationWrapper.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/core/SearchOperationWrapper.java
@@ -22,7 +22,7 @@
  *
  *
  *      Copyright 2008-2009 Sun Microsystems, Inc.
- *      Portions Copyright 2011-2014 ForgeRock AS
+ *      Portions Copyright 2011-2015 ForgeRock AS
  */
 package org.opends.server.core;
 
@@ -370,17 +370,4 @@
     return getOperation().sendSearchReference(reference);
   }
 
-  /** {@inheritDoc} */
-  @Override
-  public DN getProxiedAuthorizationDN()
-  {
-    return getOperation().getProxiedAuthorizationDN();
-  }
-
-  /** {@inheritDoc} */
-  @Override
-  public void setProxiedAuthorizationDN(DN proxiedAuthorizationDN){
-    getOperation().setProxiedAuthorizationDN(proxiedAuthorizationDN);
-  }
-
 }
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/core/UnbindOperationBasis.java b/opendj-server-legacy/src/main/java/org/opends/server/core/UnbindOperationBasis.java
index a4d5d19..305a382 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/core/UnbindOperationBasis.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/core/UnbindOperationBasis.java
@@ -78,6 +78,20 @@
     // candidate for being called by the logging subsystem.
     return OperationType.UNBIND;
   }
+
+  /** {@inheritDoc} */
+  @Override
+  public DN getProxiedAuthorizationDN()
+  {
+    return null;
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public void setProxiedAuthorizationDN(DN proxiedAuthorizationDN)
+  {
+  }
+
   /** {@inheritDoc} */
   @Override
   public final List<Control> getResponseControls()
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/extensions/WhoAmIExtendedOperation.java b/opendj-server-legacy/src/main/java/org/opends/server/extensions/WhoAmIExtendedOperation.java
index ad1ebe8..f6d42b1 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/extensions/WhoAmIExtendedOperation.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/extensions/WhoAmIExtendedOperation.java
@@ -35,12 +35,14 @@
 import org.forgerock.opendj.config.server.ConfigException;
 import org.opends.server.controls.ProxiedAuthV1Control;
 import org.opends.server.controls.ProxiedAuthV2Control;
+import org.opends.server.core.AccessControlConfigManager;
 import org.opends.server.core.ExtendedOperation;
 import org.forgerock.i18n.slf4j.LocalizedLogger;
 import org.opends.server.types.*;
 import org.forgerock.opendj.ldap.ResultCode;
 import org.forgerock.opendj.ldap.ByteString;
 import static org.opends.messages.ExtensionMessages.*;
+import static org.opends.messages.ProtocolMessages.ERR_PROXYAUTH_AUTHZ_NOT_PERMITTED;
 import static org.opends.server.util.ServerConstants.*;
 
 /**
@@ -111,6 +113,15 @@
 
           authorizationEntry = proxyControlV1.getAuthorizationEntry();
         }
+        // Check the requester has the authz user in scope of their proxy aci.
+        if (! AccessControlConfigManager.getInstance().getAccessControlHandler()
+                .mayProxy(clientConnection.getAuthenticationInfo().getAuthenticationEntry(),
+                        authorizationEntry, operation))
+        {
+          final DN dn = authorizationEntry.getName();
+          throw new DirectoryException(ResultCode.AUTHORIZATION_DENIED,
+              ERR_PROXYAUTH_AUTHZ_NOT_PERMITTED.get(dn));
+        }
         operation.setAuthorizationEntry(authorizationEntry);
       }
     }
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/types/Operation.java b/opendj-server-legacy/src/main/java/org/opends/server/types/Operation.java
index d423987..12b4e32 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/types/Operation.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/types/Operation.java
@@ -22,7 +22,7 @@
  *
  *
  *      Copyright 2006-2009 Sun Microsystems, Inc.
- *      Portions Copyright 2011-2014 ForgeRock AS.
+ *      Portions Copyright 2011-2015 ForgeRock AS.
  */
 package org.opends.server.types;
 
@@ -459,6 +459,27 @@
   DN getAuthorizationDN();
 
   /**
+   * Retrieves the proxied authorization DN for this operation if proxied
+   * authorization has been requested.
+   *
+   * @return  The proxied authorization DN for this operation if proxied
+   *          authorization has been requested, or {@code null} if proxied
+   *          authorization has not been requested.
+   */
+  DN getProxiedAuthorizationDN();
+
+  /**
+   * Set the proxied authorization DN for this operation if proxied
+   * authorization has been requested.
+   *
+   * @param proxiedAuthorizationDN
+   *          The proxied authorization DN for this operation if proxied
+   *          authorization has been requested, or {@code null} if proxied
+   *          authorization has not been requested.
+   */
+  void setProxiedAuthorizationDN(DN proxiedAuthorizationDN);
+
+  /**
    * Retrieves the set of attachments defined for this operation, as a
    * mapping between the attachment name and the associated object.
    *
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendAddOperation.java b/opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendAddOperation.java
index dad3411..8874250 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendAddOperation.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendAddOperation.java
@@ -49,8 +49,6 @@
 import org.opends.server.controls.LDAPPostReadRequestControl;
 import org.opends.server.controls.PasswordPolicyErrorType;
 import org.opends.server.controls.PasswordPolicyResponseControl;
-import org.opends.server.controls.ProxiedAuthV1Control;
-import org.opends.server.controls.ProxiedAuthV2Control;
 import org.opends.server.core.AccessControlConfigManager;
 import org.opends.server.core.AddOperation;
 import org.opends.server.core.AddOperationWrapper;
@@ -60,7 +58,6 @@
 import org.opends.server.core.PluginConfigManager;
 import org.opends.server.schema.AuthPasswordSyntax;
 import org.opends.server.schema.UserPasswordSyntax;
-import org.opends.server.types.AdditionalLogItem;
 import org.opends.server.types.Attribute;
 import org.opends.server.types.AttributeBuilder;
 import org.opends.server.types.AttributeType;
@@ -1106,44 +1103,9 @@
           postReadRequest =
                 getRequestControl(LDAPPostReadRequestControl.DECODER);
         }
-        else if (OID_PROXIED_AUTH_V1.equals(oid))
+        else if (LocalBackendWorkflowElement.processProxyAuthControls(this, oid))
         {
-          // Log usage of legacy proxy authz V1 control.
-          addAdditionalLogItem(AdditionalLogItem.keyOnly(getClass(),
-              "obsoleteProxiedAuthzV1Control"));
-
-          // The requester must have the PROXIED_AUTH privilege in order to
-          // be able to use this control.
-          if (!getClientConnection().hasPrivilege(Privilege.PROXIED_AUTH, this))
-          {
-            throw new DirectoryException(ResultCode.AUTHORIZATION_DENIED,
-                           ERR_PROXYAUTH_INSUFFICIENT_PRIVILEGES.get());
-          }
-
-          ProxiedAuthV1Control proxyControl =
-              getRequestControl(ProxiedAuthV1Control.DECODER);
-
-          Entry authorizationEntry = proxyControl.getAuthorizationEntry();
-          setAuthorizationEntry(authorizationEntry);
-          setProxiedAuthorizationDN(getName(authorizationEntry));
-        }
-        else if (OID_PROXIED_AUTH_V2.equals(oid))
-        {
-          // The requester must have the PROXIED_AUTH privilege in order to
-          // be able to use this control.
-          if (! getClientConnection().hasPrivilege(Privilege.PROXIED_AUTH,
-                                                   this))
-          {
-            throw new DirectoryException(ResultCode.AUTHORIZATION_DENIED,
-                ERR_PROXYAUTH_INSUFFICIENT_PRIVILEGES.get());
-          }
-
-          ProxiedAuthV2Control proxyControl =
-              getRequestControl(ProxiedAuthV2Control.DECODER);
-
-          Entry authorizationEntry = proxyControl.getAuthorizationEntry();
-          setAuthorizationEntry(authorizationEntry);
-          setProxiedAuthorizationDN(getName(authorizationEntry));
+          continue;
         }
         else if (OID_PASSWORD_POLICY_CONTROL.equals(oid))
         {
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendCompareOperation.java b/opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendCompareOperation.java
index 51de57e..80008b5 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendCompareOperation.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendCompareOperation.java
@@ -35,8 +35,6 @@
 import org.opends.server.api.ClientConnection;
 import org.opends.server.api.plugin.PluginResult;
 import org.opends.server.controls.LDAPAssertionRequestControl;
-import org.opends.server.controls.ProxiedAuthV1Control;
-import org.opends.server.controls.ProxiedAuthV2Control;
 import org.opends.server.core.*;
 import org.forgerock.i18n.slf4j.LocalizedLogger;
 import org.opends.server.types.*;
@@ -414,57 +412,9 @@
                 ERR_COMPARE_CANNOT_PROCESS_ASSERTION_FILTER.get(entryDN, de.getMessageObject()));
           }
         }
-        else if (oid.equals(OID_PROXIED_AUTH_V1))
+        else if (LocalBackendWorkflowElement.processProxyAuthControls(this, oid))
         {
-          // Log usage of legacy proxy authz V1 control.
-          addAdditionalLogItem(AdditionalLogItem.keyOnly(getClass(),
-              "obsoleteProxiedAuthzV1Control"));
-
-          // The requester must have the PROXIED_AUTH privilege in order to
-          // be able to use this control.
-          if (! clientConnection.hasPrivilege(Privilege.PROXIED_AUTH, this))
-          {
-            throw new DirectoryException(ResultCode.AUTHORIZATION_DENIED,
-                           ERR_PROXYAUTH_INSUFFICIENT_PRIVILEGES.get());
-          }
-
-          ProxiedAuthV1Control proxyControl =
-                getRequestControl(ProxiedAuthV1Control.DECODER);
-
-          Entry authorizationEntry = proxyControl.getAuthorizationEntry();
-          setAuthorizationEntry(authorizationEntry);
-          if (authorizationEntry == null)
-          {
-            setProxiedAuthorizationDN(DN.rootDN());
-          }
-          else
-          {
-            setProxiedAuthorizationDN(authorizationEntry.getName());
-          }
-        }
-        else if (oid.equals(OID_PROXIED_AUTH_V2))
-        {
-          // The requester must have the PROXIED_AUTH privilege in order to
-          // be able to use this control.
-          if (! clientConnection.hasPrivilege(Privilege.PROXIED_AUTH, this))
-          {
-            throw new DirectoryException(ResultCode.AUTHORIZATION_DENIED,
-                           ERR_PROXYAUTH_INSUFFICIENT_PRIVILEGES.get());
-          }
-
-          ProxiedAuthV2Control proxyControl =
-              getRequestControl(ProxiedAuthV2Control.DECODER);
-
-          Entry authorizationEntry = proxyControl.getAuthorizationEntry();
-          setAuthorizationEntry(authorizationEntry);
-          if (authorizationEntry == null)
-          {
-            setProxiedAuthorizationDN(DN.rootDN());
-          }
-          else
-          {
-            setProxiedAuthorizationDN(authorizationEntry.getName());
-          }
+          continue;
         }
 
         // NYI -- Add support for additional controls.
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendDeleteOperation.java b/opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendDeleteOperation.java
index b62a56f..df7673e 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendDeleteOperation.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendDeleteOperation.java
@@ -38,21 +38,17 @@
 import org.opends.server.api.plugin.PluginResult;
 import org.opends.server.controls.LDAPAssertionRequestControl;
 import org.opends.server.controls.LDAPPreReadRequestControl;
-import org.opends.server.controls.ProxiedAuthV1Control;
-import org.opends.server.controls.ProxiedAuthV2Control;
 import org.opends.server.core.AccessControlConfigManager;
 import org.opends.server.core.DeleteOperation;
 import org.opends.server.core.DeleteOperationWrapper;
 import org.opends.server.core.DirectoryServer;
 import org.opends.server.core.PersistentSearch;
 import org.opends.server.core.PluginConfigManager;
-import org.opends.server.types.AdditionalLogItem;
 import org.opends.server.types.CanceledOperationException;
 import org.opends.server.types.Control;
 import org.opends.server.types.DN;
 import org.opends.server.types.DirectoryException;
 import org.opends.server.types.Entry;
-import org.opends.server.types.Privilege;
 import org.opends.server.types.SearchFilter;
 import org.opends.server.types.SynchronizationProviderResult;
 import org.opends.server.types.LockManager.DNLock;
@@ -470,43 +466,9 @@
           preReadRequest =
                 getRequestControl(LDAPPreReadRequestControl.DECODER);
         }
-        else if (OID_PROXIED_AUTH_V1.equals(oid))
+        else if (LocalBackendWorkflowElement.processProxyAuthControls(this, oid))
         {
-          // Log usage of legacy proxy authz V1 control.
-          addAdditionalLogItem(AdditionalLogItem.keyOnly(getClass(),
-              "obsoleteProxiedAuthzV1Control"));
-
-          // The requester must have the PROXIED_AUTH privilege in order to
-          // be able to use this control.
-          if (! clientConnection.hasPrivilege(Privilege.PROXIED_AUTH, this))
-          {
-            throw new DirectoryException(ResultCode.AUTHORIZATION_DENIED,
-                           ERR_PROXYAUTH_INSUFFICIENT_PRIVILEGES.get());
-          }
-
-          ProxiedAuthV1Control proxyControl =
-              getRequestControl(ProxiedAuthV1Control.DECODER);
-
-          Entry authorizationEntry = proxyControl.getAuthorizationEntry();
-          setAuthorizationEntry(authorizationEntry);
-          setProxiedAuthorizationDN(getName(authorizationEntry));
-        }
-        else if (OID_PROXIED_AUTH_V2.equals(oid))
-        {
-          // The requester must have the PROXIED_AUTH privilege in order to
-          // be able to use this control.
-          if (! clientConnection.hasPrivilege(Privilege.PROXIED_AUTH, this))
-          {
-            throw new DirectoryException(ResultCode.AUTHORIZATION_DENIED,
-                           ERR_PROXYAUTH_INSUFFICIENT_PRIVILEGES.get());
-          }
-
-          ProxiedAuthV2Control proxyControl =
-              getRequestControl(ProxiedAuthV2Control.DECODER);
-
-          Entry authorizationEntry = proxyControl.getAuthorizationEntry();
-          setAuthorizationEntry(authorizationEntry);
-          setProxiedAuthorizationDN(getName(authorizationEntry));
+          continue;
         }
         // NYI -- Add support for additional controls.
         else if (c.isCritical()
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendModifyDNOperation.java b/opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendModifyDNOperation.java
index ca9c21d..ae33640 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendModifyDNOperation.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendModifyDNOperation.java
@@ -44,15 +44,12 @@
 import org.opends.server.controls.LDAPAssertionRequestControl;
 import org.opends.server.controls.LDAPPostReadRequestControl;
 import org.opends.server.controls.LDAPPreReadRequestControl;
-import org.opends.server.controls.ProxiedAuthV1Control;
-import org.opends.server.controls.ProxiedAuthV2Control;
 import org.opends.server.core.AccessControlConfigManager;
 import org.opends.server.core.DirectoryServer;
 import org.opends.server.core.ModifyDNOperation;
 import org.opends.server.core.ModifyDNOperationWrapper;
 import org.opends.server.core.PersistentSearch;
 import org.opends.server.core.PluginConfigManager;
-import org.opends.server.types.AdditionalLogItem;
 import org.opends.server.types.Attribute;
 import org.opends.server.types.AttributeType;
 import org.opends.server.types.Attributes;
@@ -62,7 +59,6 @@
 import org.opends.server.types.DirectoryException;
 import org.opends.server.types.Entry;
 import org.opends.server.types.Modification;
-import org.opends.server.types.Privilege;
 import org.opends.server.types.RDN;
 import org.opends.server.types.SearchFilter;
 import org.opends.server.types.SynchronizationProviderResult;
@@ -625,43 +621,9 @@
             iter.set(postReadRequest);
           }
         }
-        else if (OID_PROXIED_AUTH_V1.equals(oid))
+        else if (LocalBackendWorkflowElement.processProxyAuthControls(this, oid))
         {
-          // Log usage of legacy proxy authz V1 control.
-          addAdditionalLogItem(AdditionalLogItem.keyOnly(getClass(),
-              "obsoleteProxiedAuthzV1Control"));
-
-          // The requester must have the PROXIED_AUTH privilege in order to
-          // be able to use this control.
-          if (! clientConnection.hasPrivilege(Privilege.PROXIED_AUTH, this))
-          {
-            throw new DirectoryException(ResultCode.AUTHORIZATION_DENIED,
-                           ERR_PROXYAUTH_INSUFFICIENT_PRIVILEGES.get());
-          }
-
-          ProxiedAuthV1Control proxyControl =
-              getRequestControl(ProxiedAuthV1Control.DECODER);
-
-          Entry authorizationEntry = proxyControl.getAuthorizationEntry();
-          setAuthorizationEntry(authorizationEntry);
-          setProxiedAuthorizationDN(getName(authorizationEntry));
-        }
-        else if (OID_PROXIED_AUTH_V2.equals(oid))
-        {
-          // The requester must have the PROXIED_AUTH privilege in order to
-          // be able to use this control.
-          if (! clientConnection.hasPrivilege(Privilege.PROXIED_AUTH, this))
-          {
-            throw new DirectoryException(ResultCode.AUTHORIZATION_DENIED,
-                           ERR_PROXYAUTH_INSUFFICIENT_PRIVILEGES.get());
-          }
-
-          ProxiedAuthV2Control proxyControl =
-              getRequestControl(ProxiedAuthV2Control.DECODER);
-
-          Entry authorizationEntry = proxyControl.getAuthorizationEntry();
-          setAuthorizationEntry(authorizationEntry);
-          setProxiedAuthorizationDN(getName(authorizationEntry));
+          continue;
         }
         else if (c.isCritical()
             && (backend == null || !backend.supportsControl(oid)))
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendModifyOperation.java b/opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendModifyOperation.java
index 292dc79..cbbaa9c 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendModifyOperation.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendModifyOperation.java
@@ -53,8 +53,6 @@
 import org.opends.server.controls.LDAPPreReadRequestControl;
 import org.opends.server.controls.PasswordPolicyErrorType;
 import org.opends.server.controls.PasswordPolicyResponseControl;
-import org.opends.server.controls.ProxiedAuthV1Control;
-import org.opends.server.controls.ProxiedAuthV2Control;
 import org.opends.server.core.AccessControlConfigManager;
 import org.opends.server.core.DirectoryServer;
 import org.opends.server.core.ModifyOperation;
@@ -68,7 +66,6 @@
 import org.opends.server.types.AcceptRejectWarn;
 import org.opends.server.types.AccountStatusNotification;
 import org.opends.server.types.AccountStatusNotificationType;
-import org.opends.server.types.AdditionalLogItem;
 import org.opends.server.types.Attribute;
 import org.opends.server.types.AttributeBuilder;
 import org.opends.server.types.AttributeType;
@@ -741,43 +738,9 @@
             iter.set(postReadRequest);
           }
         }
-        else if (OID_PROXIED_AUTH_V1.equals(oid))
+        else if (LocalBackendWorkflowElement.processProxyAuthControls(this, oid))
         {
-          // Log usage of legacy proxy authz V1 control.
-          addAdditionalLogItem(AdditionalLogItem.keyOnly(getClass(),
-              "obsoleteProxiedAuthzV1Control"));
-
-          // The requester must have the PROXIED_AUTH privilege in order to
-          // be able to use this control.
-          if (! clientConnection.hasPrivilege(Privilege.PROXIED_AUTH, this))
-          {
-            throw new DirectoryException(ResultCode.AUTHORIZATION_DENIED,
-                           ERR_PROXYAUTH_INSUFFICIENT_PRIVILEGES.get());
-          }
-
-          ProxiedAuthV1Control proxyControl =
-                  getRequestControl(ProxiedAuthV1Control.DECODER);
-
-          Entry authorizationEntry = proxyControl.getAuthorizationEntry();
-          setAuthorizationEntry(authorizationEntry);
-          setProxiedAuthorizationDN(getName(authorizationEntry));
-        }
-        else if (OID_PROXIED_AUTH_V2.equals(oid))
-        {
-          // The requester must have the PROXIED_AUTH privilege in order to
-          // be able to use this control.
-          if (! clientConnection.hasPrivilege(Privilege.PROXIED_AUTH, this))
-          {
-            throw new DirectoryException(ResultCode.AUTHORIZATION_DENIED,
-                           ERR_PROXYAUTH_INSUFFICIENT_PRIVILEGES.get());
-          }
-
-          ProxiedAuthV2Control proxyControl =
-              getRequestControl(ProxiedAuthV2Control.DECODER);
-
-          Entry authorizationEntry = proxyControl.getAuthorizationEntry();
-          setAuthorizationEntry(authorizationEntry);
-          setProxiedAuthorizationDN(getName(authorizationEntry));
+          continue;
         }
         else if (OID_PASSWORD_POLICY_CONTROL.equals(oid))
         {
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendSearchOperation.java b/opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendSearchOperation.java
index 8f1299e..d3c2504 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendSearchOperation.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendSearchOperation.java
@@ -22,7 +22,7 @@
  *
  *
  *      Copyright 2008-2010 Sun Microsystems, Inc.
- *      Portions Copyright 2011-2014 ForgeRock AS
+ *      Portions Copyright 2011-2015 ForgeRock AS
  */
 package org.opends.server.workflowelement.localbackend;
 
@@ -377,43 +377,9 @@
                                 de.getMessageObject()), de);
           }
         }
-        else if (OID_PROXIED_AUTH_V1.equals(oid))
+        else if (LocalBackendWorkflowElement.processProxyAuthControls(this, oid))
         {
-          // Log usage of legacy proxy authz V1 control.
-          addAdditionalLogItem(AdditionalLogItem.keyOnly(getClass(),
-              "obsoleteProxiedAuthzV1Control"));
-
-          // The requester must have the PROXIED_AUTH privilege in order to be
-          // able to use this control.
-          if (! clientConnection.hasPrivilege(Privilege.PROXIED_AUTH, this))
-          {
-            throw new DirectoryException(ResultCode.AUTHORIZATION_DENIED,
-                           ERR_PROXYAUTH_INSUFFICIENT_PRIVILEGES.get());
-          }
-
-          ProxiedAuthV1Control proxyControl =
-              getRequestControl(ProxiedAuthV1Control.DECODER);
-
-          Entry authorizationEntry = proxyControl.getAuthorizationEntry();
-          setAuthorizationEntry(authorizationEntry);
-          setProxiedAuthorizationDN(getName(authorizationEntry));
-        }
-        else if (OID_PROXIED_AUTH_V2.equals(oid))
-        {
-          // The requester must have the PROXIED_AUTH privilege in order to be
-          // able to use this control.
-          if (! clientConnection.hasPrivilege(Privilege.PROXIED_AUTH, this))
-          {
-            throw new DirectoryException(ResultCode.AUTHORIZATION_DENIED,
-                           ERR_PROXYAUTH_INSUFFICIENT_PRIVILEGES.get());
-          }
-
-          ProxiedAuthV2Control proxyControl =
-              getRequestControl(ProxiedAuthV2Control.DECODER);
-
-          Entry authorizationEntry = proxyControl.getAuthorizationEntry();
-          setAuthorizationEntry(authorizationEntry);
-          setProxiedAuthorizationDN(getName(authorizationEntry));
+          continue;
         }
         else if (OID_PERSISTENT_SEARCH.equals(oid))
         {
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElement.java b/opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElement.java
index f92d2ed..5d6b17e 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElement.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElement.java
@@ -45,10 +45,14 @@
 import org.opends.server.controls.LDAPPostReadResponseControl;
 import org.opends.server.controls.LDAPPreReadRequestControl;
 import org.opends.server.controls.LDAPPreReadResponseControl;
+import org.opends.server.controls.ProxiedAuthV1Control;
+import org.opends.server.controls.ProxiedAuthV2Control;
 import org.opends.server.core.*;
 import org.opends.server.types.*;
 
 import static org.opends.messages.CoreMessages.*;
+import static org.opends.messages.ProtocolMessages.ERR_PROXYAUTH_AUTHZ_NOT_PERMITTED;
+import static org.opends.server.util.ServerConstants.*;
 
 /**
  * This class defines a local backend workflow element; e-g an entity that
@@ -312,8 +316,20 @@
       for (Iterator<Control> iter = requestControls.iterator(); iter.hasNext();)
       {
         final Control control = iter.next();
+        // The aci check for the proxy auth controls needs to check the authentication DN,
+        // not the operation target.
+        // TODO should we check the authentication DN for all controls?
+        final DN aciTarget;
+        if (OID_PROXIED_AUTH_V1.equals(control.getOID()) || OID_PROXIED_AUTH_V2.equals(control.getOID()))
+        {
+          aciTarget= op.getClientConnection().getAuthenticationInfo().getAuthenticationDN();
+        }
+        else
+        {
+          aciTarget = targetDN;
+        }
 
-        if (!getAccessControlHandler().isAllowed(targetDN, op, control))
+        if (!getAccessControlHandler().isAllowed(aciTarget, op, control))
         {
           // As per RFC 4511 4.1.11.
           if (control.isCritical())
@@ -332,6 +348,89 @@
   }
 
   /**
+   * Check the requester has the PROXIED_AUTH privilege in order to be able to use a proxy auth control.
+   *
+   * @param operation  The operation being checked
+   * @throws DirectoryException  If insufficient privileges are detected
+   */
+  static void checkPrivilegeForProxyAuthControl(Operation operation) throws DirectoryException
+  {
+    if (! operation.getClientConnection().hasPrivilege(Privilege.PROXIED_AUTH, operation))
+    {
+      throw new DirectoryException(ResultCode.AUTHORIZATION_DENIED,
+              ERR_PROXYAUTH_INSUFFICIENT_PRIVILEGES.get());
+    }
+  }
+
+  /**
+   * Check the requester has the authorization user in scope of proxy aci.
+   *
+   * @param operation  The operation being checked
+   * @param authorizationEntry  The entry being authorized as (e.g. from a proxy auth control)
+   * @throws DirectoryException  If no proxy permission is allowed
+   */
+  static void checkAciForProxyAuthControl(Operation operation, Entry authorizationEntry) throws DirectoryException
+  {
+    if (! AccessControlConfigManager.getInstance().getAccessControlHandler()
+            .mayProxy(operation.getClientConnection().getAuthenticationInfo().getAuthenticationEntry(),
+                    authorizationEntry, operation))
+    {
+      throw new DirectoryException(ResultCode.AUTHORIZATION_DENIED,
+              ERR_PROXYAUTH_AUTHZ_NOT_PERMITTED.get(authorizationEntry.getName()));
+    }
+  }
+  /**
+   * Process the operation control with the given oid if it is a proxy auth control.
+   *
+   * Privilege and initial aci checks on the authenticating user are performed. The authenticating
+   * user must have the proxied-auth privilege, and the authz user must be in the scope of aci
+   * allowing the proxy right to the authenticating user.
+   *
+   * @param operation  The operation containing the control(s)
+   * @param oid  The OID of the detected proxy auth control
+   * @return <code>true</code> if the control has been processed, <code>false</code> if not
+   * @throws DirectoryException
+   */
+  static boolean processProxyAuthControls(Operation operation, String oid)
+          throws DirectoryException
+  {
+    final Entry authorizationEntry;
+
+    if (OID_PROXIED_AUTH_V1.equals(oid))
+    {
+      final ProxiedAuthV1Control proxyControlV1 = operation.getRequestControl(ProxiedAuthV1Control.DECODER);
+      // Log usage of legacy proxy authz V1 control.
+      operation.addAdditionalLogItem(AdditionalLogItem.keyOnly(operation.getClass(),
+              "obsoleteProxiedAuthzV1Control"));
+      checkPrivilegeForProxyAuthControl(operation);
+      authorizationEntry = proxyControlV1.getAuthorizationEntry();
+    }
+    else if (OID_PROXIED_AUTH_V2.equals(oid))
+    {
+      final ProxiedAuthV2Control proxyControlV2 = operation.getRequestControl(ProxiedAuthV2Control.DECODER);
+      checkPrivilegeForProxyAuthControl(operation);
+      authorizationEntry = proxyControlV2.getAuthorizationEntry();
+    }
+    else
+    {
+      return false;
+    }
+
+    checkAciForProxyAuthControl(operation, authorizationEntry);
+    operation.setAuthorizationEntry(authorizationEntry);
+
+    if (authorizationEntry == null)
+    {
+      operation.setProxiedAuthorizationDN(DN.NULL_DN);
+    }
+    else
+    {
+      operation.setProxiedAuthorizationDN(authorizationEntry.getName());
+    }
+    return true;
+  }
+
+  /**
    * Returns a new {@link DirectoryException} built from the provided
    * resultCodes and messages. Depending on whether ACIs prevent information
    * disclosure, the provided resultCode and message will be masked and
diff --git a/opendj-server-legacy/src/messages/org/opends/messages/protocol.properties b/opendj-server-legacy/src/messages/org/opends/messages/protocol.properties
index 8a7daca..ddca568 100644
--- a/opendj-server-legacy/src/messages/org/opends/messages/protocol.properties
+++ b/opendj-server-legacy/src/messages/org/opends/messages/protocol.properties
@@ -908,4 +908,6 @@
 ERR_GSER_NO_VALID_IDENTIFIEDCHOICE_1523=The GSER value does not \
  contain a valid IdentifiedChoiceValue at the current position: %s
 INFO_NULL_KEY_PROVIDER_MANAGER_1524=The keystore %s seems to be missing, \
-  this may render the secure port inoperative for '%s'
\ No newline at end of file
+  this may render the secure port inoperative for '%s'
+ERR_PROXYAUTH_AUTHZ_NOT_PERMITTED_1525=Authorization as '%s' specified in \
+ the proxied authorization control is not permitted
diff --git a/opendj-server-legacy/src/test/java/org/opends/server/authorization/dseecompat/AciTestCase.java b/opendj-server-legacy/src/test/java/org/opends/server/authorization/dseecompat/AciTestCase.java
index 5c6ae62..972e12e 100644
--- a/opendj-server-legacy/src/test/java/org/opends/server/authorization/dseecompat/AciTestCase.java
+++ b/opendj-server-legacy/src/test/java/org/opends/server/authorization/dseecompat/AciTestCase.java
@@ -432,6 +432,32 @@
       Assert.assertEquals(LDAPResultCode.SUCCESS, expectedRc, "");
   }
 
+  void proxyModify(String ldif, String bindDn, String bindPassword,
+      String proxyUser, int expectedRc)
+      throws IOException
+  {
+      File tempFile = getTemporaryLdifFile();
+      TestCaseUtils.writeFile(tempFile, ldif);
+
+      ArrayList<String> argList=new ArrayList<>();
+      argList.add("-h");
+      argList.add("127.0.0.1");
+      argList.add("-p");
+      argList.add(String.valueOf(TestCaseUtils.getServerLdapPort()));
+      argList.add("-D");
+      argList.add(bindDn);
+      argList.add("-w");
+      argList.add(bindPassword);
+      if (proxyUser != null) {
+          argList.add("-Y");
+          argList.add("dn:" + proxyUser);
+      }
+      argList.add("-f");
+      argList.add(tempFile.getAbsolutePath());
+      String[] args = new String[argList.size()];
+      ldapModify(argList.toArray(args), expectedRc);
+  }
+
   private void ldapModify(String[] args, int expectedRc)
   {
     oStream.reset();
diff --git a/opendj-server-legacy/src/test/java/org/opends/server/authorization/dseecompat/ProxyTestCase.java b/opendj-server-legacy/src/test/java/org/opends/server/authorization/dseecompat/ProxyTestCase.java
new file mode 100644
index 0000000..2ff5475
--- /dev/null
+++ b/opendj-server-legacy/src/test/java/org/opends/server/authorization/dseecompat/ProxyTestCase.java
@@ -0,0 +1,211 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
+ * or http://forgerock.org/license/CDDLv1.0.html.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at legal-notices/CDDLv1_0.txt.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information:
+ *      Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ *      Copyright 2015 ForgeRock AS
+ */
+package org.opends.server.authorization.dseecompat;
+
+import org.opends.server.TestCaseUtils;
+import org.opends.server.protocols.ldap.LDAPResultCode;
+import org.opends.server.types.Entry;
+import org.testng.Assert;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import java.util.List;
+
+import static org.opends.server.config.ConfigConstants.ATTR_AUTHZ_GLOBAL_ACI;
+
+/**
+ * This class tests ACI behavior with the proxy auth control.
+ */
+public class ProxyTestCase extends AciTestCase
+{
+    static final String TEST_BASE = "o=test";
+    static final String ALICE_DN = "uid=alice,ou=People," + TEST_BASE;
+    static final String BOB_DN = "uid=bob,ou=People," + TEST_BASE;
+    static final String CHARLIE_DN = "uid=charlie,ou=People," + TEST_BASE;
+    static final String PASSWORD = "password";
+
+    @BeforeClass
+    public void setupClass() throws Exception
+    {
+        deleteAttrFromAdminEntry(ACCESS_HANDLER_DN, ATTR_AUTHZ_GLOBAL_ACI);
+        TestCaseUtils.initializeTestBackend(true);
+        TestCaseUtils.addEntries(
+                "dn: ou=People," + TEST_BASE,
+                "objectClass: top",
+                "objectClass: organizationalUnit",
+                "ou: People",
+                "aci: (targetcontrol=\"2.16.840.1.113730.3.4.18\")" +
+                        "(version 3.0; acl \"Allow proxy auth control\"; " +
+                        "allow (read) userdn = \"ldap:///" + ALICE_DN + "\";)",
+                "aci: (target=\"ldap:///" + CHARLIE_DN + "\")(targetattr = \"telephoneNumber\")" +
+                        "(version 3.0; acl \"Allow Bob to write Charlie\"; " +
+                        "allow (write) userdn = \"ldap:///" + BOB_DN + "\";)",
+                "aci: (target=\"ldap:///ou=People," + TEST_BASE + "\")" +
+                        "(version 3.0; acl \"Allow Alice to proxy People\"; " +
+                        "allow (proxy) userdn = \"ldap:///" + ALICE_DN + "\";)",
+                "",
+                "dn: " + ALICE_DN,
+                "objectClass: top",
+                "objectClass: person",
+                "objectClass: organizationalPerson",
+                "objectClass: inetOrgPerson",
+                "uid: alice",
+                "cn: Alice",
+                "sn: User",
+                "ds-privilege-name: proxied-auth",
+                "userPassword: " + PASSWORD,
+                "",
+                "dn: uid=bob,ou=People," + TEST_BASE,
+                "objectClass: top",
+                "objectClass: person",
+                "objectClass: organizationalPerson",
+                "objectClass: inetOrgPerson",
+                "uid: bob",
+                "cn: Bob",
+                "sn: User",
+                "userPassword: " + PASSWORD,
+                "",
+                "dn: uid=charlie,ou=People," + TEST_BASE,
+                "objectClass: top",
+                "objectClass: person",
+                "objectClass: organizationalPerson",
+                "objectClass: inetOrgPerson",
+                "uid: charlie",
+                "cn: Charlie",
+                "sn: User",
+                "userPassword: " + PASSWORD,
+                "",
+                "dn: ou=Groups," + TEST_BASE,
+                "objectClass: top",
+                "objectClass: organizationalUnit",
+                "ou: Groups",
+                "",
+                "dn: cn=Writable by Bob,ou=Groups," + TEST_BASE,
+                "objectClass: top",
+                "objectClass: groupOfEntries",
+                "cn: Writable by Bob",
+                "aci: (targetattr=\"description\")" +
+                        "(version 3.0; acl \"Bob writes\"; " +
+                        "allow(write) userdn=\"ldap:///" + BOB_DN + "\";)",
+                "",
+                "dn: cn=Visible to Bob,ou=Groups," + TEST_BASE,
+                "objectClass: top",
+                "objectClass: groupOfEntries",
+                "cn: Visible to Bob",
+                "description: Bob can read this group",
+                "aci: (targetattr=\"*||+\")" +
+                        "(version 3.0; acl \"Bob visible\"; " +
+                        "allow(read,search) userdn=\"ldap:///" + BOB_DN + "\";)",
+                "",
+                "dn: cn=Invisible to Bob,ou=Groups," + TEST_BASE,
+                "objectClass: top",
+                "objectClass: groupOfEntries",
+                "cn: Invisible to Bob",
+                "description: Bob cannot see this group",
+                "aci: (targetattr=\"*||+\")" +
+                        "(version 3.0; acl \"Bob invisible\"; " +
+                        "deny(read,search) userdn=\"ldap:///" + BOB_DN + "\";)",
+                "");
+    }
+
+
+    @BeforeMethod
+    public void clearBackend() throws Exception
+    {
+        deleteAttrFromAdminEntry(ACCESS_HANDLER_DN, ATTR_AUTHZ_GLOBAL_ACI);
+    }
+
+    /**
+     * Test Alice cannot proxy as Root.
+     *
+     * @throws Exception If an unexpected result is returned.
+     */
+    @Test
+    public void testProxyAsRoot() throws Exception
+    {
+        proxyModify(TestCaseUtils.makeLdif(
+            "dn: " + CHARLIE_DN,
+            "changetype: modify",
+            "replace: telephoneNumber",
+            "telephoneNumber: 999"),
+            ALICE_DN, PASSWORD,
+            DIR_MGR_DN,
+            LDAPResultCode.AUTHORIZATION_DENIED);
+    }
+
+    /**
+     * Test Alice (as Bob) modifies Charlie.
+     *
+     * @throws Exception If an unexpected result is returned.
+     */
+    @Test
+    public void testSimpleProxy() throws Exception
+    {
+        proxyModify(TestCaseUtils.makeLdif(
+             "dn: " + CHARLIE_DN,
+             "changetype: modify",
+             "replace: telephoneNumber",
+             "telephoneNumber: 999"),
+             ALICE_DN, PASSWORD,
+             BOB_DN,
+             LDAPResultCode.SUCCESS);
+    }
+
+    /**
+     * Test Alice (as Bob) modifies an entry outside of the proxy target scope.
+     *
+     * @throws Exception If an unexpected result is returned.
+     */
+    @Test
+    public void testUpdateGroup() throws Exception
+    {
+        proxyModify(TestCaseUtils.makeLdif(
+             "dn: cn=Writable by Bob,ou=Groups," + TEST_BASE,
+             "changetype: modify",
+             "replace: description",
+             "description: written by Alice (Bob)"),
+             ALICE_DN, PASSWORD,
+             BOB_DN,
+             LDAPResultCode.SUCCESS);
+    }
+
+    /**
+     * Test Alice (as Bob) can see entries that Bob can see.
+     * Only "cn=Visible to Bob,ou=Groups,..." should be returned.
+     *
+     * @throws Exception If an unexpected result is returned.
+     */
+    @Test
+    public void testProxiedSearch() throws Exception
+    {
+        String results = LDAPSearchParams(ALICE_DN, PASSWORD, BOB_DN, null, null,
+                TEST_BASE, "(&)", null,
+                false, false, LDAPResultCode.SUCCESS);
+        List<Entry> entries = TestCaseUtils.entriesFromLdifString(results);
+        Assert.assertEquals(entries.size(), 1, "Wrong number of results");
+    }
+}

--
Gitblit v1.10.0