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

dugan
26.12.2007 3f02855a9202d146ec2e87a7568180caec938235
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:

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";)

which allows anonymous access to the following extended operations:

- StartTLS 1.3.6.1.4.1.1466.20037
- password policy state 1.3.6.1.4.1.26027.1.6.1
- password modify 1.3.6.1.4.1.4203.1.11.1
- Who Am I 1.3.6.1.4.1.4203.1.11.3

A wildcard can also be specified:

aci: (extop="*")(version 3.0; acl "Anonymous extended operation access"; allow(read) userdn="ldap:///anyone";)

Issue #443.
2 files added
18 files modified
814 ■■■■ changed files
opendj-sdk/opends/resource/config/config.ldif 1 ●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/Aci.java 64 ●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciContainer.java 26 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciHandler.java 33 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciLDAPOperationContainer.java 30 ●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciTargetMatchContext.java 21 ●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciTargets.java 116 ●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/EnumTargetKeyword.java 7 ●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/ExtOp.java 104 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/TargetControl.java 2 ●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/messages/AciMessages.java 17 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/tests/unit-tests-testng/resource/config-changes.ldif 6 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/authorization/dseecompat/AciTestCase.java 38 ●●●● patch | view | raw | blame | history
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/authorization/dseecompat/AciTests.java 29 ●●●● patch | view | raw | blame | history
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/authorization/dseecompat/AlternateRootDN.java 10 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/authorization/dseecompat/ExtOpTestCase.java 240 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/authorization/dseecompat/GetEffectiveRightsTestCase.java 3 ●●●● patch | view | raw | blame | history
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/authorization/dseecompat/IPTestCase.java 1 ●●●● patch | view | raw | blame | history
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/authorization/dseecompat/TargetAttrTestCase.java 5 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/authorization/dseecompat/TargetControlTestCase.java 61 ●●●● patch | view | raw | blame | history
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";)
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);
      }
    }
    /**
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}
     */
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;
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());
    }
    /**
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.
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.
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.
opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/ExtOp.java
New file
@@ -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;
  }
}
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 '*').
   */
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]");
    }
}
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
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");
  }
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 + ";");
    }
  }
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
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/authorization/dseecompat/ExtOpTestCase.java
New file
@@ -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);
  }
}
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);
   }
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;
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.
   *
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