From 3f02855a9202d146ec2e87a7568180caec938235 Mon Sep 17 00:00:00 2001
From: dugan <dugan@localhost>
Date: Wed, 25 Jul 2007 23:12:49 +0000
Subject: [PATCH] Add new ACI keyword "extop" that can be used to enforce access based on the OID of an extended operation. For example, a new global access extended operation rule is also being added:

---
 opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/EnumTargetKeyword.java                                  |    7 
 opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/authorization/dseecompat/TargetAttrTestCase.java         |    5 
 opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/authorization/dseecompat/GetEffectiveRightsTestCase.java |    3 
 opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/ExtOp.java                                              |  104 +++++++
 opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/authorization/dseecompat/AlternateRootDN.java            |   10 
 opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/Aci.java                                                |   64 +++
 opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciHandler.java                                         |   33 +
 opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciLDAPOperationContainer.java                          |   30 +
 opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/authorization/dseecompat/AciTests.java                   |   29 +
 opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciTargets.java                                         |  116 ++++++--
 opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/TargetControl.java                                      |    2 
 opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciTargetMatchContext.java                              |   21 +
 opendj-sdk/opends/resource/config/config.ldif                                                                                   |    1 
 opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciContainer.java                                       |   26 +
 opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/authorization/dseecompat/ExtOpTestCase.java              |  240 +++++++++++++++++
 opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/authorization/dseecompat/IPTestCase.java                 |    1 
 opendj-sdk/opends/tests/unit-tests-testng/resource/config-changes.ldif                                                          |    6 
 opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/authorization/dseecompat/TargetControlTestCase.java      |   61 +++
 opendj-sdk/opends/src/server/org/opends/server/messages/AciMessages.java                                                        |   17 +
 opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/authorization/dseecompat/AciTestCase.java                |   38 ++
 20 files changed, 719 insertions(+), 95 deletions(-)

diff --git a/opendj-sdk/opends/resource/config/config.ldif b/opendj-sdk/opends/resource/config/config.ldif
index f864c53..d3d2df8 100644
--- a/opendj-sdk/opends/resource/config/config.ldif
+++ b/opendj-sdk/opends/resource/config/config.ldif
@@ -51,6 +51,7 @@
 objectClass: top
 objectClass: ds-cfg-access-control-handler
 objectClass: ds-cfg-dseecompat-access-control-handler
+ds-cfg-global-aci: (extop="1.3.6.1.4.1.26027.1.6.1 || 1.3.6.1.4.1.4203.1.11.1 || 1.3.6.1.4.1.1466.20037 || 1.3.6.1.4.1.4203.1.11.3") (version 3.0; acl "Anonymous extended operation access"; allow(read) userdn="ldap:///anyone";)
 ds-cfg-global-aci: (targetcontrol="2.16.840.1.113730.3.4.2 || 2.16.840.1.113730.3.4.17 || 2.16.840.1.113730.3.4.19 || 1.3.6.1.4.1.4203.1.10.2") (version 3.0; acl "Anonymous control access"; allow(read) userdn="ldap:///anyone";)
 ds-cfg-global-aci: (targetattr!="userPassword||authPassword")(version 3.0; acl "Anonymous read access"; allow (read,search,compare) userdn="ldap:///anyone";)
 ds-cfg-global-aci: (targetattr="*")(version 3.0; acl "Self entry modification"; allow (write) userdn="ldap:///self";)
diff --git a/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/Aci.java b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/Aci.java
index d1ace9d..4af0b45 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/Aci.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/Aci.java
@@ -282,6 +282,16 @@
     public static final int TARGATTRFILTERS_DELETE = 0x2000;
 
     /**
+     * Used by the control evaluation access check.
+     */
+    public static final int ACI_CONTROL = 0x4000;
+
+    /**
+     *  Used by the extended operation access check.
+     */
+    public static final int ACI_EXT_OP = 0x8000;
+
+    /**
      * ACI_ATTR_STAR_MATCHED is the flag set when the evaluation reason of a
      * AciHandler.maysend ACI_READ access evaluation was the result of an
      * ACI targetattr all attributes expression (targetattr="*") target match.
@@ -400,15 +410,40 @@
 
     /**
      * Test if the given ACI is applicable using the target match information
-     * provided. The ACI target can have four keywords at this time:
+     * provided. The ACI target can have seven keywords at this time:
+     *
+     * These two base decision on the resource entry DN:
      *
      *       1. target - checked in isTargetApplicable.
      *       2. targetscope - checked in isTargetApplicable.
+     *
+     * These three base decision on resource entry attributes:
+     *
      *       3. targetfilter - checked in isTargetFilterApplicable.
      *       4. targetattr - checked in isTargetAttrApplicable.
+     *       5. targattrfilters -  checked in isTargAttrFiltersApplicable.
      *
-     * One and two are checked for match first. If they return true, then
-     * three is checked. Lastly four is checked.
+     * These two base decisions on a resource entry built by the ACI handler
+     * that only contains a DN:
+     *       6. targetcontrol - check in isTargetControlApplicable.
+     *       7. extop - check in isExtOpApplicable.
+     *
+     * Six and seven are specific to the check being done: targetcontrol when a
+     * control is being evaluated and extop when an extended operation is
+     * evaluated.  None of the attribute based keywords should be checked
+     * when a control or extended op is being evaluated, because one
+     * of those attribute keywords rule might incorrectly make an ACI
+     * applicable that shouldn't be. This can happen by erroneously basing
+     * their decision on the ACI handler generated stub resource entry. For
+     * example, a "(targetattr != userpassword)" rule would match the generated
+     * stub resource entry, even though a control or extended op might be
+     * denied.
+     *
+     * What is allowed is the target and targetscope keywords, since the DN is
+     * known, so they are checked along with the correct method for the access
+     * check (isTargetControlApplicable for control and
+     * isTExtOpApplicable for extended operations). See comments in code
+     * where these checks are done.
      *
      * @param aci The ACI to test.
      * @param matchCtx The target matching context containing all the info
@@ -417,20 +452,27 @@
      */
     public static boolean
     isApplicable(Aci aci, AciTargetMatchContext matchCtx) {
-        int ctxRights=matchCtx.getRights();
-       //First check if the ACI and context have similar rights.
+      if(matchCtx.hasRights(ACI_EXT_OP)) {
+        //Extended operation is being evaluated.
+         return AciTargets.isTargetApplicable(aci, matchCtx) &&
+                 AciTargets.isExtOpApplicable(aci, matchCtx);
+      } else if(matchCtx.hasRights(ACI_CONTROL)) {
+        //Control is being evaluated.
+         return AciTargets.isTargetApplicable(aci, matchCtx) &&
+                AciTargets.isTargetControlApplicable(aci, matchCtx);
+      } else {
+        int ctxRights = matchCtx.getRights();
+        //First check if the ACI and context have similar rights.
         if(!aci.hasRights(ctxRights)) {
-           //TODO This check might be able to be removed further testing
-           //     is needed.
-           if(!(aci.hasRights(ACI_SEARCH| ACI_READ) &&
-                 matchCtx.hasRights(ACI_SEARCH | ACI_READ)))
-              return false;
+          if(!(aci.hasRights(ACI_SEARCH| ACI_READ) &&
+                  matchCtx.hasRights(ACI_SEARCH | ACI_READ)))
+            return false;
         }
         return AciTargets.isTargetApplicable(aci, matchCtx) &&
-                AciTargets.isTargetControlApplicable(aci, matchCtx) &&
                 AciTargets.isTargetFilterApplicable(aci, matchCtx) &&
                 AciTargets.isTargAttrFiltersApplicable(aci, matchCtx) &&
                 AciTargets.isTargetAttrApplicable(aci, matchCtx);
+      }
     }
 
     /**
diff --git a/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciContainer.java b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciContainer.java
index 54fd4dc..217b3c6 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciContainer.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciContainer.java
@@ -237,10 +237,15 @@
     private int evalAllAttributes=0;
 
    /*
-   * String used to hold a control OID string.
-   */
+    * String used to hold a control OID string.
+    */
     private String controlOID;
 
+   /*
+    * String used to hold an extended operation OID string.
+    */
+    private String extOpOID;
+
   /**
      * This constructor is used by all currently supported LDAP operations.
      *
@@ -743,6 +748,13 @@
       return controlOID;
     }
 
+   /**
+    * {@inheritDoc}
+    */
+    public String getExtOpOID() {
+      return extOpOID;
+    }
+
     /**
      * Set the the controlOID value to the specified oid string.
      *
@@ -752,6 +764,16 @@
       this.controlOID=oid;
     }
 
+
+    /**
+     * Set the extended operation OID value to the specified oid string.
+     *
+     * @param oid  The extended operation oid string.
+     */
+    protected void setExtOpOID(String oid) {
+      this.extOpOID=oid;
+    }
+
     /**
      * {@inheritDoc}
      */
diff --git a/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciHandler.java b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciHandler.java
index e0969f0..77c3ba1 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciHandler.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciHandler.java
@@ -1195,7 +1195,8 @@
     if(!(ret=skipAccessCheck(op))) {
       Entry e = new Entry(entryDN, null, null, null);
       AciLDAPOperationContainer operationContainer =
-              new AciLDAPOperationContainer(op, e, control.getOID());
+              new AciLDAPOperationContainer(op, e, control,
+                                            (ACI_READ | ACI_CONTROL));
       ret=accessAllowed(operationContainer);
     }
     if(control.getOID().equals(OID_PROXIED_AUTH_V2) ||
@@ -1218,6 +1219,27 @@
     return ret;
   }
 
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public boolean isAllowed(ExtendedOperation operation) {
+    boolean ret;
+    if(!(ret=skipAccessCheck(operation))) {
+      Entry e = new Entry(operation.getAuthorizationDN(), null, null, null);
+      AciLDAPOperationContainer operationContainer =
+         new AciLDAPOperationContainer(operation, e, (ACI_READ | ACI_EXT_OP));
+      ret=accessAllowed(operationContainer);
+    }
+    if(operation.getRequestOID().equals(OID_PROXIED_AUTH_V2) ||
+            operation.getRequestOID().equals(OID_PROXIED_AUTH_V1))
+       operation.
+              setAttachment(ORIG_AUTH_ENTRY, operation.getAuthorizationEntry());
+    return ret;
+  }
+
+
   //Not planned to be implemented methods.
 
    /**
@@ -1243,15 +1265,6 @@
    * {@inheritDoc}
    */
   @Override
-  public boolean isAllowed(ExtendedOperation extendedOperation) {
-      //Not planned to be implemented.
-      return true;
-  }
-
-  /**
-   * {@inheritDoc}
-   */
-  @Override
   public boolean isAllowed(LocalBackendSearchOperation searchOperation) {
       //Not planned to be implemented.
       return true;
diff --git a/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciLDAPOperationContainer.java b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciLDAPOperationContainer.java
index a417b61..b5a6398 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciLDAPOperationContainer.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciLDAPOperationContainer.java
@@ -32,7 +32,6 @@
 import org.opends.server.core.*;
 import org.opends.server.types.*;
 import org.opends.server.workflowelement.localbackend.*;
-import static org.opends.server.authorization.dseecompat.Aci.ACI_READ;
 
 /**
  * The AciLDAPOperationContainer is an AciContainer
@@ -64,15 +63,30 @@
     }
 
     /**
-     * Constructor interface for control evaluation.
+     * Constructor interface for evaluation of a control.
      *
-     * @param operation The operation to evaluate.
-     * @param e An entry built especially for control evaluation.
-     * @param oid The control's oid string.
+     * @param operation The operation to use in the evaluation.
+     * @param e An entry built especially for evaluation.
+     * @param c The control to evaluate.
+     * @param rights The rights of a control.
      */
-    public AciLDAPOperationContainer(Operation operation, Entry e, String oid) {
-      super(operation, (ACI_READ), e );
-      setControlOID(oid);
+    public AciLDAPOperationContainer(Operation operation, Entry e, Control c,
+                                     int rights) {
+      super(operation, rights, e );
+      setControlOID(c.getOID());
+    }
+
+    /**
+     * Constructor interface for evaluation of the extended operation.
+     *
+     * @param operation  The extended operation to evaluate.
+     * @param e  An entry built especially for evaluation.
+     * @param rights The rights of a extended operation.
+     */
+    public AciLDAPOperationContainer(ExtendedOperation operation, Entry e,
+                                     int rights) {
+      super(operation, rights, e );
+      setExtOpOID(operation.getRequestOID());
     }
 
     /**
diff --git a/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciTargetMatchContext.java b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciTargetMatchContext.java
index d0a5f1c..420be0f 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciTargetMatchContext.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciTargetMatchContext.java
@@ -118,12 +118,21 @@
      */
     public int getRights();
 
-  /**
-   * Return the oid string of the control being evaluated.
-   *
-   * @return The oid string of the control being evaluated.
-   */
-    public String getControlOID();
+    /**
+     * Return the OID (Object Identifier) string of the control being evaluated.
+     *
+     * @return The OID string of the control being evaluated.
+     */
+      public String getControlOID();
+
+
+   /**
+    * Return The OID (Object Identifier) string of the extended operation being
+    *        evaluated.
+    *
+    * @return The OID string of the extended operation being evaluated.
+    */
+    public String getExtOpOID();
 
     /**
      * Checks if the container's rights has the specified rights.
diff --git a/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciTargets.java b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciTargets.java
index e55aca6..94eb6d4 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciTargets.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciTargets.java
@@ -41,8 +41,8 @@
  * of an ACI before the ACI body and specifies the entry, attributes, or set
  * of entries and attributes which the ACI controls access.
  *
- * The five supported  ACI target keywords are: target, targetattr,
- * targetscope, targetfilter and targattrfilters.
+ * The supported  ACI target keywords are: target, targetattr,
+ * targetscope, targetfilter, targattrfilters, targetcontrol and extop.
  */
 public class AciTargets {
 
@@ -76,6 +76,11 @@
     */
     private TargetControl targetControl=null;
 
+   /**
+    * The ACI syntax has a extop keyword.
+    */
+    private ExtOp extOp=null;
+
     /*
      * The number of regular expression group positions in a valid ACI target
      * expression.
@@ -138,25 +143,29 @@
 
     /**
      * Creates an ACI target from the specified arguments. All of these
-     * may be null -- the ACI has no targets an will use defaults.
-     * @param targetEntry The ACI target keyword if any.
-     * @param targetAttr The ACI targetattr keyword if any.
-     * @param targetFilter The ACI targetfilter keyword if any.
-     * @param targetScope The ACI targetscope keyword if any.
-     * @param targAttrFilters The ACI targAttrFilters keyword if any.
-     * @param targetControl The ACI targetControl keyword if any.
+     * may be null. If the ACI has no targets defaults will be used.
+     *
+     * @param targetEntry The ACI target keyword class.
+     * @param targetAttr The ACI targetattr keyword class.
+     * @param targetFilter The ACI targetfilter keyword class.
+     * @param targetScope The ACI targetscope keyword class.
+     * @param targAttrFilters The ACI targAttrFilters keyword class.
+     * @param targetControl The ACI targetControl keyword class.
+     * @param extOp The ACI extop keyword class.
      */
     private AciTargets(Target targetEntry, TargetAttr targetAttr,
                        TargetFilter targetFilter,
                        SearchScope targetScope,
                        TargAttrFilters targAttrFilters,
-                       TargetControl targetControl) {
+                       TargetControl targetControl,
+                       ExtOp extOp) {
        this.target=targetEntry;
        this.targetAttr=targetAttr;
        this.targetScope=targetScope;
        this.targetFilter=targetFilter;
        this.targAttrFilters=targAttrFilters;
        this.targetControl=targetControl;
+       this.extOp=extOp;
     }
 
     /**
@@ -212,6 +221,16 @@
       return targetControl;
     }
 
+
+   /**
+    * Return the class representing the ACI extop keyword. May be
+    * null.
+    * @return The extop information.
+   */
+    public ExtOp getExtOp() {
+      return extOp;
+    }
+
     /**
      * Decode an ACI's target part of the syntax from the string provided.
      * @param input String representing an ACI target part of syntax.
@@ -226,6 +245,7 @@
         TargetFilter targetFilter=null;
         TargAttrFilters targAttrFilters=null;
         TargetControl targetControl=null;
+        ExtOp extOp=null;
         SearchScope targetScope=SearchScope.WHOLE_SUBTREE;
         Pattern targetPattern = Pattern.compile(targetRegex);
         Matcher targetMatcher = targetPattern.matcher(input);
@@ -273,19 +293,34 @@
             }
             case KEYWORD_TARGETCONTROL:
             {
-                if (targetControl == null){
-                    targetControl =
-                            TargetControl.decode(targetOperator, expression);
-                }
-                else
-                {
-                    int msgID =
+              if (targetControl == null){
+                targetControl =
+                        TargetControl.decode(targetOperator, expression);
+              }
+              else
+              {
+                int msgID =
                         MSGID_ACI_SYNTAX_INVALID_TARGET_DUPLICATE_KEYWORDS;
-                    String message =
+                String message =
                         getMessage(msgID, "targetcontrol", input);
-                    throw new AciException(msgID, message);
-                }
-                break;
+                throw new AciException(msgID, message);
+              }
+              break;
+            }
+            case KEYWORD_EXTOP:
+            {
+              if (extOp == null){
+                extOp =  ExtOp.decode(targetOperator, expression);
+              }
+              else
+              {
+                int msgID =
+                        MSGID_ACI_SYNTAX_INVALID_TARGET_DUPLICATE_KEYWORDS;
+                String message =
+                        getMessage(msgID, "extop", input);
+                throw new AciException(msgID, message);
+              }
+              break;
             }
             case KEYWORD_TARGETATTR:
             {
@@ -353,7 +388,8 @@
             }
         }
         return new AciTargets(target, targetAttr, targetFilter,
-                              targetScope, targAttrFilters, targetControl);
+                              targetScope, targAttrFilters, targetControl,
+                              extOp);
     }
 
     /**
@@ -383,12 +419,12 @@
     }
 
     /**
-     * Checks an ACI's targetfilter information against a target match
+     * Checks an ACI's targetfilter rule information against a target match
      * context.
      * @param aci The ACI to try an match the targetfilter of.
      * @param matchCtx The target match context containing information needed
      * to perform a target match.
-     * @return True if the targetfilter matched the target context.
+     * @return True if the targetfilter rule matched the target context.
      */
     public static boolean isTargetFilterApplicable(Aci aci,
                                               AciTargetMatchContext matchCtx) {
@@ -400,16 +436,16 @@
     }
 
     /**
-     * Check an ACI's targetcontrol against a target match context.
+     * Check an ACI's targetcontrol rule against a target match context.
      *
      * @param aci The ACI to match the targetcontrol against.
      * @param matchCtx The target match context containing the information
      *                 needed to perform the target match.
-     * @return  True if the targetcontrol matched the target context.
+     * @return  True if the targetcontrol rule matched the target context.
      */
     public static boolean isTargetControlApplicable(Aci aci,
                                             AciTargetMatchContext matchCtx) {
-      boolean ret=true;
+      boolean ret=false;
       TargetControl targetControl=aci.getTargets().getTargetControl();
       if(targetControl != null)
         ret=targetControl.isApplicable(matchCtx);
@@ -417,11 +453,30 @@
     }
 
     /**
-     * Check an ACI's targattrfilters against a target match context.
+     * Check an ACI's extop rule against a target match context.
+     *
+     * @param aci The ACI to match the extop rule against.
+     * @param matchCtx The target match context containing the information
+     *                 needed to perform the target match.
+     * @return  True if the extop rule matched the target context.
+     */
+    public static boolean isExtOpApplicable(Aci aci,
+                                              AciTargetMatchContext matchCtx) {
+      boolean ret=false;
+      ExtOp extOp=aci.getTargets().getExtOp();
+      if(extOp != null)
+        ret=extOp.isApplicable(matchCtx);
+      return ret;
+    }
+
+
+    /**
+     * Check an ACI's targattrfilters rule against a target match context.
+     *
      * @param aci The ACI to match the targattrfilters against.
      * @param matchCtx  The target match context containing the information
      * needed to perform the target match.
-     * @return True if the targattrfilters matched the target context.
+     * @return True if the targattrfilters rule matched the target context.
      */
     public static boolean isTargAttrFiltersApplicable(Aci aci,
                                                AciTargetMatchContext matchCtx) {
@@ -449,8 +504,9 @@
      * of method calls over local variables.
      */
     /**
-     * Checks an provided ACI's targetattr information against a target match
+     * Checks an provided ACI's targetattr rule against a target match
      * context.
+     *
      * @param aci The ACI to evaluate.
      * @param targetMatchCtx The target match context to check the ACI against.
      * @return True if the targetattr matched the target context.
diff --git a/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/EnumTargetKeyword.java b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/EnumTargetKeyword.java
index e22c5fd..1f69075 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/EnumTargetKeyword.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/EnumTargetKeyword.java
@@ -61,7 +61,12 @@
      * This enumeration is returned when the target keyword is
      * "targetcontrol".
      */
-    KEYWORD_TARGETCONTROL ("targetcontrol");
+    KEYWORD_TARGETCONTROL ("targetcontrol"),
+      /**
+     * This enumeration is returned when the target keyword is
+     * "extop".
+     */
+    KEYWORD_EXTOP ("extop");
 
     /*
      * The target keyword name.
diff --git a/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/ExtOp.java b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/ExtOp.java
new file mode 100644
index 0000000..a1c4498
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/ExtOp.java
@@ -0,0 +1,104 @@
+/*
+ * 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
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * 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
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  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
+ *
+ *
+ *      Portions Copyright 2007 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.authorization.dseecompat;
+
+import static org.opends.server.messages.AciMessages.*;
+
+import java.util.HashSet;
+
+
+/**
+ * This class represents an ACI's extop keyword rule.
+ */
+
+public class ExtOp {
+
+
+  /*
+   * HashSet of OID strings parsed from the decode.
+   */
+  private HashSet<String> extOpOIDs = new HashSet<String>();
+
+ /*
+  * Enumeration representing the extop operator.
+  */
+
+  private EnumTargetOperator op = EnumTargetOperator.EQUALITY;
+
+  /**
+   * Creates a class that can be used to evaluate a extop rule.
+   *
+   * @param op The operator of the extop expression (=, !=).
+   * @param extOpOIDs  Set of extended operation OIDS to use in the evaluation
+   *                  (wild-card '*' allowed).
+   */
+  private ExtOp(EnumTargetOperator op, HashSet<String> extOpOIDs) {
+    this.extOpOIDs=extOpOIDs;
+    this.op=op;
+  }
+
+
+  /**
+   *  Decode an extop expression string.
+   *
+   * @param operator  An enumeration representing the operator type.
+   * @param expr A string representing the extop expression.
+   * @return  A class representing the extop expression that can be
+   *          used to evaluate an ACI.
+   *
+   * @throws AciException If the specified expression string is invalid.
+   */
+  public static ExtOp decode(EnumTargetOperator operator, String expr)
+          throws AciException {
+    HashSet<String> extOpOIDs =
+          Aci.decodeOID(expr,MSGID_ACI_SYNTAX_INVALID_TARGEXTOP_EXPRESSION);
+    return new ExtOp(operator, extOpOIDs);
+  }
+
+   /**
+   * Check if a extop is applicable based on the provided target match
+   * context.
+   *
+   * @param matchCtx The target match context to use in the check.
+   * @return True if the extop is applicable based on the context.
+   */
+  public boolean isApplicable(AciTargetMatchContext matchCtx) {
+    if(matchCtx.getExtOpOID() == null)
+      return false;
+    boolean ret = false;
+    for(String oid : extOpOIDs)
+      if(oid.equals("*") || matchCtx.getExtOpOID().equals(oid)) {
+        ret=true;
+        break;
+      }
+   if(op.equals(EnumTargetOperator.NOT_EQUALITY))
+          ret = !ret;
+    return ret;
+  }
+
+}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/TargetControl.java b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/TargetControl.java
index 9510888..dbda4cd 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/TargetControl.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/TargetControl.java
@@ -51,7 +51,7 @@
   /**
    * Creates a class that can be used to evaluate a targetcontrol.
    *
-   * @param op The operator of the targetfilter expression (=, !=).
+   * @param op The operator of the targetcontrol expression (=, !=).
    * @param controlOIDS  Set of control OIDS to use in the evaluation (may
    *                     contain wild-card '*').
    */
diff --git a/opendj-sdk/opends/src/server/org/opends/server/messages/AciMessages.java b/opendj-sdk/opends/src/server/org/opends/server/messages/AciMessages.java
index da777e4..71d0cc9 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/messages/AciMessages.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/messages/AciMessages.java
@@ -954,6 +954,16 @@
             MSGID_ACI_SYNTAX_DECODE_EFFECTIVERIGHTS_FAIL =
             CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_SEVERE_WARNING | 94;
 
+    /**
+     * The message ID for the message that will be used if an "aci" attribute
+     * type value parse failed because an extop keyword expression
+     * did not parse.  This takes one argument, which is the extop
+     * expression from the ACI.
+     */
+
+     public static final int MSGID_ACI_SYNTAX_INVALID_TARGEXTOP_EXPRESSION =
+         CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_SEVERE_WARNING | 95;
+
   /**
      * Associates a set of generic messages with the message IDs defined in
      * this class.
@@ -1501,5 +1511,12 @@
                 " geteffectiverights control could not be" +
                 " decoded because of the following reason: \"%s\"");
 
+      registerMessage(MSGID_ACI_SYNTAX_INVALID_TARGEXTOP_EXPRESSION,
+              "The provided Access Control Instruction (ACI) " +
+              "extop expression value \"%s\" is invalid. A valid " +
+              "extop keyword expression value requires one or more " +
+              "valid extended operation request OID strings in the following" +
+              " format: oid [|| oid1] ... [|| oidn]");
+
     }
 }
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/resource/config-changes.ldif b/opendj-sdk/opends/tests/unit-tests-testng/resource/config-changes.ldif
index c5678d5..b70ad9f 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/resource/config-changes.ldif
+++ b/opendj-sdk/opends/tests/unit-tests-testng/resource/config-changes.ldif
@@ -32,6 +32,12 @@
 replace: ds-cfg-trust-manager-provider-dn
 ds-cfg-trust-manager-provider-dn: cn=JKS,cn=Trust Manager Providers,cn=config
 
+dn: cn=Access Control Handler,cn=config
+changeType: modify
+add: ds-cfg-global-aci
+ds-cfg-global-aci: (targetcontrol="*") (version 3.0; acl "Anonymous control access"; allow(read) userdn="ldap:///anyone";)
+ds-cfg-global-aci: (extop="*") (version 3.0; acl "Anonymous extended op access"; allow(read) userdn="ldap:///anyone";)
+
 dn: cn=JMX Connection Handler,cn=Connection Handlers,cn=config
 changeType: modify
 replace: ds-cfg-listen-port
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/authorization/dseecompat/AciTestCase.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/authorization/dseecompat/AciTestCase.java
index c588e5c..1244306 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/authorization/dseecompat/AciTestCase.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/authorization/dseecompat/AciTestCase.java
@@ -85,7 +85,12 @@
 
   protected final static String G_CONTROL =
           "(targetcontrol = \"*\")" +
-          "(version 3.0; acl \"Control\"; " +
+          "(version 3.0; acl \"Anonymous control access\"; " +
+                  "allow (read) userdn=\"ldap:///anyone\";)";
+
+  protected final static String E_EXTEND_OP =
+          "(extop = \"*\")" +
+          "(version 3.0; acl \"Anonymous extend op access\"; " +
                   "allow (read) userdn=\"ldap:///anyone\";)";
 
   private static final ByteArrayOutputStream oStream = new ByteArrayOutputStream();
@@ -483,10 +488,35 @@
             "uid: user.3",
             "givenName: User 3",
             "sn: 3",
-            "mail: user.3@test",
-            "description: user.3 description",
             "cn: User 3",
-            "l: Austin",
+             "l: Austin",
+            "userPassword: password",
+            "ds-privilege-name: proxied-auth",
+            "",
+            "dn: uid=user.4,ou=People,o=test",
+            "objectClass: top",
+            "objectClass: person",
+            "objectClass: organizationalPerson",
+            "objectClass: inetOrgPerson",
+            "uid: user.4",
+            "givenName: User 4",
+            "sn: 4",
+            "cn: User 4",
+             "l: ft worth",
+            "userPassword: password",
+            "",
+            "dn: uid=user.5,ou=People,o=test",
+            "objectClass: top",
+            "objectClass: person",
+            "objectClass: organizationalPerson",
+            "objectClass: inetOrgPerson",
+            "uid: user.5",
+            "givenName: User 5",
+            "sn: 5",
+            "mail: user.5@test",
+            "description: user.5 description",
+            "cn: User 5",
+            "l: waco",
             "userPassword: password",
             "ds-privilege-name: proxied-auth");
   }
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/authorization/dseecompat/AciTests.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/authorization/dseecompat/AciTests.java
index 6c2f536..4be19c9 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/authorization/dseecompat/AciTests.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/authorization/dseecompat/AciTests.java
@@ -1069,26 +1069,38 @@
                   "allow(write)", BIND_RULE_USERDN_SELF);
 
   private static final String GLOBAL_SCHEMA_ACI =
-          buildGlobalAciValue("name", "User-Visible Schema Operational Attributes",
+          buildGlobalAciValue("name",
+                  "User-Visible Schema Operational Attributes",
                   "target", "ldap:///cn=schema", "targetscope", "base",
                   "targetattr",
-                  "attributeTypes||dITContentRules||dITStructureRules||ldapSyntaxes||matchingRules||matchingRuleUse||nameForms||objectClasses",
+                  "attributeTypes||dITContentRules||dITStructureRules||" +
+                  "ldapSyntaxes||matchingRules||matchingRuleUse||nameForms||" +
+                  "objectClasses",
                   "allow(read, search, compare)", BIND_RULE_USERDN_ANYONE);
 
   private static final String GLOBAL_DSE_ACI = buildGlobalAciValue(
           "name","User-Visible Root DSE Operational Attributes",
           "target", "ldap:///", "targetscope", "base",
           "targetattr",
-          "namingContexts||supportedAuthPasswordSchemes||supportedControl||supportedExtension||supportedFeatures||supportedSASLMechanisms||vendorName||vendorVersion",
+          "namingContexts||supportedAuthPasswordSchemes||supportedControl||" +
+          "supportedExtension||supportedFeatures||supportedSASLMechanisms||" +
+          "vendorName||vendorVersion",
           "allow(read, search, compare)",BIND_RULE_USERDN_ANYONE);
 
   private static final String GLOBAL_USER_OP_ATTRS_ACI = buildGlobalAciValue(
           "name", "User-Visible Operational Attributes", "targetattr",
-          "createTimestamp||creatorsName||modifiersName||modifyTimestamp||entryDN||entryUUID||subschemaSubentry",
+          "createTimestamp||creatorsName||modifiersName||modifyTimestamp||" +
+          "entryDN||entryUUID||subschemaSubentry",
           "allow(read, search, compare)", BIND_RULE_USERDN_ANYONE);
 
   private static final String GLOBAL_CONTROL_ACI = buildGlobalAciValue(
-          "name", "Control", "targetcontrol", "*",
+          "name", "Anonymous control access", "targetcontrol",
+          "*",
+          "allow(read)", BIND_RULE_USERDN_ANYONE);
+
+  private static final String GLOBAL_EXT_OP_ACI = buildGlobalAciValue(
+          "name", "Anonymous extend op access", "extop",
+          "*",
           "allow(read)", BIND_RULE_USERDN_ANYONE);
 
   private static final String GLOBAL_DEFAULT_ACIS =
@@ -1096,7 +1108,7 @@
                                         GLOBAL_ANONYMOUS_READ_ACI,
                                         GLOBAL_SELF_WRITE_ACI, GLOBAL_SCHEMA_ACI,
                                         GLOBAL_DSE_ACI, GLOBAL_USER_OP_ATTRS_ACI,
-                                        GLOBAL_CONTROL_ACI);
+                                        GLOBAL_CONTROL_ACI, GLOBAL_EXT_OP_ACI);
 
  //ACI used to test LDAP compare.
  private static final
@@ -2065,7 +2077,7 @@
     String aciField = aciFields[i];
     String aciValue = aciFields[i+1];
 
-    if (aciField.startsWith("targ")) {
+    if (aciField.startsWith("targ") || aciField.equals("extop")) {
       if (!aciField.endsWith("=")) {  // We allow = or more importantly != to be included with the target
         aciField += "=";
       }
@@ -2092,7 +2104,8 @@
     String permission = aciFields[i];
     String bindRule = aciFields[i+1];
 
-    if (!permission.startsWith("targ") && !permission.equals("name")) {
+    if (!permission.startsWith("targ") && !permission.equals("extop") &&
+        !permission.equals("name")) {
       aci.append(EOL + " " + permission + " " + bindRule + ";");
     }
   }
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/authorization/dseecompat/AlternateRootDN.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/authorization/dseecompat/AlternateRootDN.java
index b6e40c1..dbf633d 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/authorization/dseecompat/AlternateRootDN.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/authorization/dseecompat/AlternateRootDN.java
@@ -33,6 +33,7 @@
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeMethod;
 import org.testng.Assert;
 import org.opends.server.TestCaseUtils;
 import static org.opends.server.config.ConfigConstants.*;
@@ -78,11 +79,18 @@
   @AfterClass
   public void tearDown() throws Exception {
     String aciLdif=makeAddLDIF(ATTR_AUTHZ_GLOBAL_ACI, ACCESS_HANDLER_DN,
-            G_READ_ACI, G_SELF_MOD, G_SCHEMA, G_DSE, G_USER_OPS, G_CONTROL);
+            G_READ_ACI, G_SELF_MOD, G_SCHEMA, G_DSE, G_USER_OPS, G_CONTROL,
+            E_EXTEND_OP);
     LDIFModify(aciLdif, DIR_MGR_DN, PWD);
   }
 
 
+  @BeforeMethod
+  public void clearBackend() throws Exception {
+    deleteAttrFromEntry(user1, "aci");
+    deleteAttrFromEntry(ACCESS_HANDLER_DN, ATTR_AUTHZ_GLOBAL_ACI);
+  }
+
   /**
    * This test uses an ACI allowing access to the userPassword attribute, based
    * on one of the alternate bind DNs of a root entry. The root entry does not
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/authorization/dseecompat/ExtOpTestCase.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/authorization/dseecompat/ExtOpTestCase.java
new file mode 100644
index 0000000..3843231
--- /dev/null
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/authorization/dseecompat/ExtOpTestCase.java
@@ -0,0 +1,240 @@
+/*
+ * 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
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * 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
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  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
+ *
+ *
+ *      Portions Copyright 2007 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.authorization.dseecompat;
+
+import org.testng.annotations.*;
+import org.testng.annotations.Test;
+import org.opends.server.TestCaseUtils;
+import org.opends.server.protocols.ldap.LDAPResultCode;
+import static org.opends.server.util.ServerConstants.*;
+import static org.opends.server.config.ConfigConstants.*;
+
+/**
+ * Unit test to test the extop ACI keyword.
+ */
+
+public class ExtOpTestCase extends AciTestCase {
+
+  private static final String superUser="uid=superuser,ou=admins,o=test";
+  private static final String level5User="uid=user.5,ou=People,o=test";
+  private static final String level4User="uid=user.4,ou=People,o=test";
+  private static final String level3User="uid=user.3,ou=People,o=test";
+  private static final String level2User="uid=user.2,ou=People,o=test";
+  private static final String level1User="uid=user.1,ou=People,o=test";
+  private static final String newPWD="newPWD";
+  private static final String peopleBase="ou=People,o=test";
+  private static final String adminBase="ou=Admins,o=test";
+
+  //Allow either reportauthzID or passwordpolicy controls. Used in the
+  //bind tests.
+  private static final
+  String pwdControls =
+          "(targetcontrol=\"" + OID_AUTHZID_REQUEST + "||" +
+          OID_PASSWORD_POLICY_CONTROL + "\")" +
+          "(version 3.0; acl \"control\";" +
+          "allow(read) userdn=\"ldap:///" + "anyone" + "\";)";
+
+
+  //Allow only password modify extended op.
+  private static final
+  String extOp =
+          "(extop=\"" + OID_PASSWORD_MODIFY_REQUEST + "\")" +
+          "(version 3.0; acl \"extended op\";" +
+          "allow(read) userdn=\"ldap:///" + "anyone" + "\";)";
+
+
+  //Allow all extended ops based on extop = *.
+  private static final
+  String extOpWC =
+          "(extop=\"" + "*" + "\")" +
+          "(version 3.0; acl \"extended op WC\";" +
+          "allow(read) userdn=\"ldap:///" + "anyone" + "\";)";
+
+
+  //Dis-allow all extended ops based on extop != *"
+  private static final
+  String extOpNotWC =
+          "(extop!=\"" + "*" + "\")" +
+          "(version 3.0; acl \"extended op no wc\";" +
+          "allow(read) userdn=\"ldap:///" + "anyone" + "\";)";
+
+
+ //Allow all attributes to be modified - so the password can be changed.
+  private static final
+  String ALLOW_ALL = "(targetattr=\"*\")" +
+          "(version 3.0;acl \"all access\";" +
+          "allow (all) " +
+          "userdn=\"ldap:///self\";)";
+
+ //Allow pwd modify to people branch.
+  private static final
+  String extOpPeople = "(extop=\"" +
+          OID_PASSWORD_MODIFY_REQUEST + "\")" +
+          "(target=\"ldap:///" + peopleBase + "\")" +
+          "(version 3.0; acl \"extended op\";" +
+          "allow(read) userdn=\"ldap:///" + "anyone" + "\";)";
+
+  //Dis-allow pwd modify to admin branch.
+  private static final
+  String extOpAdmin =
+          "(extop!=\"" + OID_PASSWORD_MODIFY_REQUEST + "\")" +
+          "(target=\"ldap:///" + adminBase + "\")" +
+          "(version 3.0; acl \"extended op\";" +
+          "allow(read) userdn=\"ldap:///" + "anyone" + "\";)";
+
+  //Test for side effect -- targetattr rule gives access to denied extended
+  //op.
+  private static final
+  String complicated =
+          "(extop = \"1.2.3.4\")" +
+          "(targetattr != \"userpassword\")" +
+          "(version 3.0; acl \"extended op\";" +
+          "allow(all) userdn=\"ldap:///" + "anyone" + "\";)";
+
+  @BeforeClass
+  public void setupClass() throws Exception {
+    TestCaseUtils.startServer();
+    deleteAttrFromEntry(ACCESS_HANDLER_DN, ATTR_AUTHZ_GLOBAL_ACI);
+    addEntries();
+  }
+
+   @AfterClass
+  public void tearDown() throws Exception {
+       String aciLdif=makeAddLDIF(ATTR_AUTHZ_GLOBAL_ACI, ACCESS_HANDLER_DN,
+               G_READ_ACI, G_SELF_MOD, G_SCHEMA, G_DSE, G_USER_OPS, G_CONTROL,
+               E_EXTEND_OP);
+       LDIFModify(aciLdif, DIR_MGR_DN, PWD);
+   }
+
+  @BeforeMethod
+  public void clearBackend() throws Exception {
+    deleteAttrFromEntry(peopleBase, "aci");
+    deleteAttrFromEntry(adminBase, "aci");
+    deleteAttrFromEntry(ACCESS_HANDLER_DN, ATTR_AUTHZ_GLOBAL_ACI);
+  }
+
+  /**
+   * Test access to extended op using wildcard.
+   *
+   * @throws Exception If an unexpected result is returned.
+   */
+  @Test()
+  public void testExtendOpPwdWC() throws Exception {
+   String pwdLdifs =
+        makeAddLDIF("aci", peopleBase, pwdControls, extOpWC, ALLOW_ALL);
+    LDIFModify(pwdLdifs, DIR_MGR_DN, PWD);
+    String pwdLdifs1 =
+        makeAddLDIF("aci", adminBase, pwdControls, ALLOW_ALL);
+    LDIFModify(pwdLdifs1, DIR_MGR_DN, PWD);
+    //Pass the people branch has access to all extended op using wild-card.
+    pwdModify(level1User, PWD, newPWD, null, null, 0);
+    //Fail the admin branch has no access to the extended op.
+    pwdModify(superUser, PWD, newPWD, null, null,
+              LDAPResultCode.INSUFFICIENT_ACCESS_RIGHTS);
+    deleteAttrFromEntry(peopleBase, "aci");
+    deleteAttrFromEntry(adminBase, "aci");
+  }
+
+  /**
+    * Test denied access to extended operation based on a extop rule
+    * deny all using a wild-card.
+    *
+    * @throws Exception If an unexpected result is returned.
+    */
+    @Test()
+   public void testExtendOpPwdNotWC() throws Exception {
+    String pwdLdifs =
+         makeAddLDIF("aci", peopleBase, pwdControls, extOpNotWC, ALLOW_ALL);
+     LDIFModify(pwdLdifs, DIR_MGR_DN, PWD);
+     pwdModify(level5User, PWD, newPWD, null, null,
+             LDAPResultCode.INSUFFICIENT_ACCESS_RIGHTS);
+     deleteAttrFromEntry(peopleBase, "aci");
+   }
+
+
+  /**
+   * Test access to extended op using one ACI to allow access to the
+   * extended op and another ACI to allow the pwd change..
+   *
+   * @throws Exception If an unexpected result is returned.
+   */
+  @Test()
+  public void testExtendOpPwd() throws Exception {
+   String pwdLdifs =
+        makeAddLDIF("aci", peopleBase, pwdControls, extOp, ALLOW_ALL);
+    LDIFModify(pwdLdifs, DIR_MGR_DN, PWD);
+    pwdModify(level3User, PWD, newPWD, null, null, 0);
+    deleteAttrFromEntry(peopleBase, "aci");
+  }
+
+   /**
+   * Test access to disallowed extended op based on a targetattr rule allowing
+   * access.
+   *
+   * @throws Exception If an unexpected result is returned.
+   */
+
+  @Test()
+  public void testTargetattrSideEffect() throws Exception {
+   String pwdLdifs =
+        makeAddLDIF("aci", peopleBase, complicated);
+    LDIFModify(pwdLdifs, DIR_MGR_DN, PWD);
+    //Fail because pwd not an allowed extended operation.
+    pwdModify(level4User, PWD, newPWD, null, null,
+             LDAPResultCode.INSUFFICIENT_ACCESS_RIGHTS);
+    deleteAttrFromEntry(peopleBase, "aci");
+  }
+
+  /**
+   * Test access to pwd changes using global ACIs with target statements giving
+   * access to different parts of the DIT.
+   *
+   * @throws Exception If an unexpected result is returned.
+   */
+  public void testGlobalTargets() throws Exception {
+    String globalControlAcis=
+            makeAddLDIF(ATTR_AUTHZ_GLOBAL_ACI, ACCESS_HANDLER_DN,
+                    extOpAdmin, extOpPeople);
+    LDIFModify(globalControlAcis, DIR_MGR_DN, PWD);
+    String pwdLdifs =
+         makeAddLDIF("aci", peopleBase, pwdControls, ALLOW_ALL);
+    LDIFModify(pwdLdifs, DIR_MGR_DN, PWD);
+    String pwdLdifs1 =
+        makeAddLDIF("aci", adminBase, pwdControls, ALLOW_ALL);
+    LDIFModify(pwdLdifs1, DIR_MGR_DN, PWD);
+    //Succeed because ACI gives access to people branch.
+    pwdModify(level2User, PWD, newPWD, null, null, 0);
+    //Fail because ACI doesn't give access to admin branch.
+    pwdModify(superUser, PWD, newPWD, null, null,
+                LDAPResultCode.INSUFFICIENT_ACCESS_RIGHTS);
+    deleteAttrFromEntry(peopleBase, "aci");
+    deleteAttrFromEntry(adminBase, "aci");
+    deleteAttrFromEntry(ACCESS_HANDLER_DN, ATTR_AUTHZ_GLOBAL_ACI);
+  }
+
+}
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/authorization/dseecompat/GetEffectiveRightsTestCase.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/authorization/dseecompat/GetEffectiveRightsTestCase.java
index 73cd2dd..c7fa970 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/authorization/dseecompat/GetEffectiveRightsTestCase.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/authorization/dseecompat/GetEffectiveRightsTestCase.java
@@ -171,7 +171,8 @@
   @AfterClass
   public void tearDown() throws Exception {
        String aciLdif=makeAddLDIF(ATTR_AUTHZ_GLOBAL_ACI, ACCESS_HANDLER_DN,
-               G_READ_ACI, G_SELF_MOD, G_SCHEMA, G_DSE, G_USER_OPS, G_CONTROL);
+               G_READ_ACI, G_SELF_MOD, G_SCHEMA, G_DSE, G_USER_OPS, G_CONTROL,
+               E_EXTEND_OP);
        LDIFModify(aciLdif, DIR_MGR_DN, PWD);
    }
 
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/authorization/dseecompat/IPTestCase.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/authorization/dseecompat/IPTestCase.java
index e61aa96..235c65c 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/authorization/dseecompat/IPTestCase.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/authorization/dseecompat/IPTestCase.java
@@ -28,7 +28,6 @@
 
 package org.opends.server.authorization.dseecompat;
 
-import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 import org.testng.annotations.DataProvider;
 import java.net.InetAddress;
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/authorization/dseecompat/TargetAttrTestCase.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/authorization/dseecompat/TargetAttrTestCase.java
index e8969e4..73c6a75 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/authorization/dseecompat/TargetAttrTestCase.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/authorization/dseecompat/TargetAttrTestCase.java
@@ -30,6 +30,7 @@
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeMethod;
 import org.testng.Assert;
 import org.opends.server.TestCaseUtils;
 import static org.opends.server.config.ConfigConstants.*;
@@ -129,11 +130,11 @@
   @AfterClass
   public void tearDown() throws Exception {
      String aciLdif=makeAddLDIF(ATTR_AUTHZ_GLOBAL_ACI, ACCESS_HANDLER_DN,
-          G_READ_ACI, G_SELF_MOD, G_SCHEMA, G_DSE, G_USER_OPS, G_CONTROL);
+          G_READ_ACI, G_SELF_MOD, G_SCHEMA, G_DSE, G_USER_OPS, G_CONTROL,
+             E_EXTEND_OP);
      LDIFModify(aciLdif, DIR_MGR_DN, PWD);
   }
 
-
   /**
    * Test targetattr behavior using userattr bind rule.
    *
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/authorization/dseecompat/TargetControlTestCase.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/authorization/dseecompat/TargetControlTestCase.java
index 81e47c4..2ff7888 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/authorization/dseecompat/TargetControlTestCase.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/authorization/dseecompat/TargetControlTestCase.java
@@ -30,8 +30,7 @@
 
 import org.testng.annotations.DataProvider;
 import org.testng.annotations.Test;
-import org.testng.annotations.BeforeClass;
-import org.testng.annotations.AfterClass;
+import org.testng.annotations.*;
 import org.opends.server.TestCaseUtils;
 import org.opends.server.protocols.ldap.LDAPResultCode;
 import static org.opends.server.util.ServerConstants.*;
@@ -44,6 +43,7 @@
 
   private static final String superUser="uid=superuser,ou=admins,o=test";
   private static final String level3User="uid=user.3,ou=People,o=test";
+  private static final String level4User="uid=user.4,ou=People,o=test";
   private static final String newPWD="newPWD";
 
 
@@ -55,8 +55,8 @@
 
   private static final String peopleBase="ou=People,o=test";
   private static final String adminBase="ou=Admins,o=test";
-  private static final String newPeopleDN="uid=user.4," + peopleBase;
-  private static final String newAdminDN="uid=user.4," + adminBase;
+  private static final String newPeopleDN="uid=user.6," + peopleBase;
+  private static final String newAdminDN="uid=user.6," + adminBase;
 
 
   @BeforeClass
@@ -69,10 +69,19 @@
   @AfterClass
   public void tearDown() throws Exception {
        String aciLdif=makeAddLDIF(ATTR_AUTHZ_GLOBAL_ACI, ACCESS_HANDLER_DN,
-               G_READ_ACI, G_SELF_MOD, G_SCHEMA, G_DSE, G_USER_OPS, G_CONTROL);
+               G_READ_ACI, G_SELF_MOD, G_SCHEMA, G_DSE, G_USER_OPS, G_CONTROL,
+               E_EXTEND_OP);
        LDIFModify(aciLdif, DIR_MGR_DN, PWD);
    }
 
+
+  @BeforeMethod
+  public void clearBackend() throws Exception {
+    deleteAttrFromEntry(peopleBase, "aci");
+    deleteAttrFromEntry(base, "aci");
+    deleteAttrFromEntry(ACCESS_HANDLER_DN, ATTR_AUTHZ_GLOBAL_ACI);
+  }
+
   private static final String[] newEntry = new String[] {
     "objectClass: top",
     "objectClass: person",
@@ -166,6 +175,21 @@
           "(version 3.0; acl \"control\";" +
           "allow(read) userdn=\"ldap:///" + "anyone" + "\";)";
 
+ //Allow all to extended op.
+  private static final
+  String extOpAll =
+          "(extop=\"" + "*" + "\")" +
+          "(version 3.0; acl \"control\";" +
+          "allow(read) userdn=\"ldap:///" + "anyone" + "\";)";
+
+  //Only allow access to the password policy control. Used to test if the
+  //targetattr rule will give access erroneously.
+  private static final
+  String complicated =
+          "(targetcontrol=\"" + OID_PASSWORD_POLICY_CONTROL + "\")" +
+          "(targetattr != \"userpassword\")" +
+          "(version 3.0; acl \"control\";" +
+          "allow(all) userdn=\"ldap:///" + "anyone" + "\";)";
 
   /**
    * Test valid targetcontrol statements.
@@ -202,14 +226,34 @@
   }
 
   /**
+   * Test access to disallowed control based on a targetattr rule allowing
+   * access.
+   *
+   * @throws Exception If an unexpected result is returned.
+   */
+
+  @Test()
+  public void testTargetattrSideEffect() throws Exception {
+   String pwdLdifs =
+        makeAddLDIF("aci", peopleBase, complicated);
+    LDIFModify(pwdLdifs, DIR_MGR_DN, PWD);
+    String noOpCtrlStr=OID_LDAP_NOOP_OPENLDAP_ASSIGNED + ":true";
+    //This should fail beacause this ACI only allows acces to the
+    //password policy control.
+    pwdModify(level4User, PWD, newPWD, noOpCtrlStr, null,
+            LDAPResultCode.INSUFFICIENT_ACCESS_RIGHTS);
+    deleteAttrFromEntry(peopleBase, "aci");
+  }
+
+  /**
    * Test access to extended op controls (no-op and userPasswordPolicy).
    *
    * @throws Exception If an unexpected result is returned.
    */
-  @Test()
+   @Test()
   public void testExtendOpControls() throws Exception {
    String pwdLdifs =
-        makeAddLDIF("aci", peopleBase, extOpControls, ALLOW_ALL);
+        makeAddLDIF("aci", peopleBase, extOpControls, extOpAll, ALLOW_ALL);
     LDIFModify(pwdLdifs, DIR_MGR_DN, PWD);
     String noOpCtrlStr=OID_LDAP_NOOP_OPENLDAP_ASSIGNED + ":true";
     //This pwd change should return no-op since the no-op control is
@@ -228,7 +272,7 @@
    *
    * @throws Exception If an unexpected result is returned.
    */
-  @Test()
+   @Test()
   public void testBindControl() throws Exception {
     String pwdLdifs =
             makeAddLDIF("aci", peopleBase, pwdControls, ALLOW_ALL);
@@ -292,7 +336,6 @@
     deleteAttrFromEntry(ACCESS_HANDLER_DN, ATTR_AUTHZ_GLOBAL_ACI);
   }
 
-
   /**
    * Test wildcard access. First test "targetcontrol != *"
    * expression. Should all be access denied. Remove that ACI and add

--
Gitblit v1.10.0