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

dugan
07.56.2007 14c5f3996a46c1281cb133de439f25492c97530a
These changes are mostly related to restructuring the regular
expression patterns to make them more readable by defining constants.

The other changes included are:

- support the "targattrfilters" target keyword
- make the "search" right behave correctly
- fix a bug in the way we evaluate multiple permission bind rule pairs
- the targetattr wild-card "*" will now not return operational
attributes; operational attributes now need to be explicitly defined
in the targetattr rule to grant access
1 files added
42 files modified
2231 ■■■■ changed files
opends/src/server/org/opends/server/authorization/dseecompat/Aci.java 187 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/authorization/dseecompat/AciBody.java 168 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/authorization/dseecompat/AciContainer.java 37 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/authorization/dseecompat/AciException.java 4 ●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/authorization/dseecompat/AciHandler.java 153 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/authorization/dseecompat/AciLDAPOperationContainer.java 5 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/authorization/dseecompat/AciList.java 3 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/authorization/dseecompat/AciListenerManager.java 11 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/authorization/dseecompat/AciMessages.java 170 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/authorization/dseecompat/AciProvider.java 6 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/authorization/dseecompat/AciTargetMatchContext.java 14 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/authorization/dseecompat/AciTargets.java 179 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/authorization/dseecompat/AuthMethod.java 8 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/authorization/dseecompat/BindRule.java 69 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/authorization/dseecompat/DNS.java 24 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/authorization/dseecompat/DayOfWeek.java 7 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/authorization/dseecompat/EnumAccessType.java 4 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/authorization/dseecompat/EnumAuthMethod.java 5 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/authorization/dseecompat/EnumBindRuleKeyword.java 6 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/authorization/dseecompat/EnumBindRuleType.java 3 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/authorization/dseecompat/EnumBooleanTypes.java 3 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/authorization/dseecompat/EnumDayOfWeek.java 3 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/authorization/dseecompat/EnumEvalResult.java 1 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/authorization/dseecompat/EnumRight.java 27 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/authorization/dseecompat/EnumTargetKeyword.java 5 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/authorization/dseecompat/EnumTargetOperator.java 3 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/authorization/dseecompat/EnumUserDNType.java 1 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/authorization/dseecompat/GroupDN.java 28 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/authorization/dseecompat/IpCriteria.java 13 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/authorization/dseecompat/ParentInheritance.java 60 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/authorization/dseecompat/PermBindRulePair.java 7 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/authorization/dseecompat/Permission.java 25 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/authorization/dseecompat/RoleDN.java 31 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/authorization/dseecompat/TargAttrFilterList.java 221 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/authorization/dseecompat/TargAttrFilters.java 354 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/authorization/dseecompat/Target.java 41 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/authorization/dseecompat/TargetAttr.java 50 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/authorization/dseecompat/TargetFilter.java 10 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/authorization/dseecompat/TimeOfDay.java 12 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/authorization/dseecompat/UserAttr.java 111 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/authorization/dseecompat/UserDN.java 13 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/authorization/dseecompat/UserDNTypeURL.java 4 ●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/authorization/dseecompat/AciTests.java 145 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/authorization/dseecompat/Aci.java
@@ -37,6 +37,7 @@
 * The Aci class represents ACI strings.
 */
public class Aci  {
    /*
     * The body of the ACI is the version, name and permission-bind rule
     * pairs.
@@ -53,6 +54,9 @@
     */
    public static final String supportedVersion="3.0";
    /*
     * String representation of the ACI used.
     */
    private String aciString;
    /*
@@ -60,13 +64,191 @@
     */
    private DN dn;
    /**
     * Regular expression matching a word group.
     */
    public static final String WORD_GROUP="(\\w+)";
    /**
     * Regular expression matching a word group at the start of a
     * pattern.
     */
    public static final String WORD_GROUP_START_PATTERN = "^" + WORD_GROUP;
    /**
     * Regular expression matching a white space.
     */
    public static final String ZERO_OR_MORE_WHITESPACE="\\s*";
    /**
     * Regular expression matching a white space at the start of a pattern.
     */
    public static final String ZERO_OR_MORE_WHITESPACE_START_PATTERN =
                                             "^" + ZERO_OR_MORE_WHITESPACE ;
    /**
     * Regular expression matching a white space at the end of a pattern.
     */
    public static final String ZERO_OR_MORE_WHITESPACE_END_PATTERN =
                                             ZERO_OR_MORE_WHITESPACE  + "$";
    /**
     * Regular expression matching a ACL statement separator.
     */
    public static final String ACI_STATEMENT_SEPARATOR =
                ZERO_OR_MORE_WHITESPACE + ";" + ZERO_OR_MORE_WHITESPACE;
    /*
     * This regular expression is used to do a quick syntax check
     * when an ACI is being decoded.
     */
    private static final String aciRegex =
            "^\\s*" + AciTargets.targetsRegex + "\\s*"+
             AciBody.bodyRegx + "\\s*$";
           ZERO_OR_MORE_WHITESPACE_START_PATTERN + AciTargets.targetsRegex +
           ZERO_OR_MORE_WHITESPACE + AciBody.bodyRegx +
           ZERO_OR_MORE_WHITESPACE_END_PATTERN;
    /**
     * Regular expression that graciously matches an attribute type name. Must
     * begin with an ASCII letter or digit, and contain only ASCII letters,
     * digit characters, hyphens, semi-colons and underscores.
     * They are case insensitive.
     */
    public  static final String ATTR_NAME =
              "((?i)[a-z\\d]{1}[[a-z]\\d-_.;]*(?-i))";
    /**
      * Regular expression matching a LDAP URL.
      */
     public  static final String LDAP_URL = ZERO_OR_MORE_WHITESPACE  +
                                                 "(ldap:///[^\\|]+)";
    /**
     * Regular expression used to match token that joins expressions (||).
     */
    public static final String LOGICAL_OR = "\\|\\|";
    /**
     * Regular expression used to match an open parenthesis.
     */
    public static final String OPEN_PAREN = "\\(";
    /**
     * Regular expression used to match a closed parenthesis.
     */
    public static final String CLOSED_PAREN = "\\)";
    /**
     * Regular expression used to match a single equal sign.
     */
    public static final String EQUAL_SIGN = "={1}";
    /**
     * Regular expression the matches "*".
     */
    public static final String ALL_ATTRS_WILD_CARD = ZERO_OR_MORE_WHITESPACE +
                                           "\\*" + ZERO_OR_MORE_WHITESPACE;
    /**
     * ACI_ADD is used to set the container rights for a LDAP add operation.
     */
    public static final int ACI_ADD = 0x0001;
    /**
     * ACI_DELETE is used to set the container rights for a LDAP
     * delete operation.
     */
    public static final int ACI_DELETE = 0x0002;
    /**
     * ACI_READ is used to set the container rights for a LDAP
     * search operation.
     */
    public static final int ACI_READ = 0x0004;
    /**
     * ACI_WRITE is used to set the container rights for a LDAP
     * modify operation.
     */
    public static final int ACI_WRITE = 0x0008;
    /**
     * ACI_COMPARE is used to set the container rights for a LDAP
     * compare operation.
     */
    public static final int ACI_COMPARE = 0x0010;
    /**
     * ACI_SEARCH is used to set the container rights a LDAP search operation.
     */
    public static final int ACI_SEARCH = 0x0020;
    /**
     * ACI_SELF is used for the SELFWRITE right. Currently not implemented.
     */
    public static final int ACI_SELF = 0x0040;
    /**
     * ACI_ALL is used to as a mask for all of the above. These
     * six below are not masked by the ACI_ALL.
     */
    public static final int ACI_ALL = 0x007F;
    /**
     * ACI_PROXY is used for the PROXY right. Currently not used.
     */
    public static final int ACI_PROXY = 0x0080;
    /**
     * ACI_IMPORT is used to set the container rights for a LDAP
     * modify dn operation. Currently not used.
     */
    public static final int ACI_IMPORT = 0x0100;
    /**
     * ACI_EXPORT is used to set the container rights for a LDAP
     * modify dn operation. Currently not used.
     */
    public static final int ACI_EXPORT = 0x0200;
    /**
     * ACI_WRITE_ADD is used by the LDAP modify operation.
     */
    public static final int ACI_WRITE_ADD = 0x800;
    /**
     * ACI_WRITE_DELETE is used by the LDAP modify operation.
     */
    public static final int ACI_WRITE_DELETE = 0x400;
    /**
     * TARGATTRFILTER_ADD is used to specify that a
     * targattrfilters ADD operation was seen in the ACI. For example,
     * given an ACI with:
     *
     * (targattrfilters="add=mail:(mail=*@example.com)")
     *
     * The TARGATTRFILTERS_ADD flag would be set during ACI parsing in the
     * TargAttrFilters class.
     */
    public static final int TARGATTRFILTERS_ADD = 0x1000;
    /**
     * TARGATTRFILTER_DELETE is used to specify that a
     * targattrfilters DELETE operation was seen in the ACI. For example,
     * given an ACI with:
     *
     * (targattrfilters="del=mail:(mail=*@example.com)")
     *
     * The TARGATTRFILTERS_DELETE flag would be set during ACI parsing in the
     * TargAttrFilters class.
     */
    public static final int TARGATTRFILTERS_DELETE = 0x2000;
    /**
     * ACI_NULL is used to set the container rights to all zeros. Used
     * by LDAP modify.
     */
    public static final int ACI_NULL = 0x0000;
    /**
     * Construct a new Aci from the provided arguments.
@@ -158,6 +340,7 @@
    isApplicable(Aci aci, AciTargetMatchContext matchCtx) {
        return AciTargets.isTargetApplicable(aci, matchCtx) &&
                AciTargets.isTargetFilterApplicable(aci, matchCtx) &&
                AciTargets.isTargAttrFiltersApplicable(aci, matchCtx) &&
                AciTargets.isTargetAttrApplicable(aci, matchCtx);
    }
opends/src/server/org/opends/server/authorization/dseecompat/AciBody.java
@@ -28,6 +28,7 @@
package org.opends.server.authorization.dseecompat;
import static org.opends.server.authorization.dseecompat.AciMessages.*;
import static org.opends.server.authorization.dseecompat.Aci.*;
import static org.opends.server.messages.MessageHandler.getMessage;
import java.util.ArrayList;
import java.util.List;
@@ -40,57 +41,120 @@
 */
public class AciBody {
    /*
     * Regular expression group position for the version string.
     */
    private static final int VERSION = 1;
    /*
     * Regular expression group position for the namr string.
     */
    private static final int NAME = 2;
    /*
     * Regular expression group position for the permission string.
     */
    private static final int PERM = 1;
    /*
     * Regular expression group position for the rights string.
     */
    private static final int RIGHTS = 2;
    /*
     * Regular expression group position for the bindrule string.
     */
    private static final int BINDRULE = 3;
    /*
     * Index into the ACI string where the ACI body starts.
     */
    private int startPos=0;
    /*
     * The name of the ACI, currently not used but parsed.
     */
    * The name of the ACI, currently not used but parsed.
    */
    private String name = null;
    /*
     * The version of the ACi, current not used but parsed and checked
     * for 3.0.
     */
    * The version of the ACi, current not used but parsed and checked
    * for 3.0.
    */
    private String version = null;
    /*
     This structure represents a permission-bind rule pairs. There can be
     several of these.
    */
    private List<PermBindRulePair> permBindRulePairs;
    /*
     * TODO Define constants for these regular expressions to make them more
     * readable.
     * The regular expressions would probably be a lot easier
     * to understand if you defined a number of constants for the
     * individual components and then concatenated them.  For example,
     * "\\s*" could be defined in a constant named ZERO_OR_MORE_SPACES.
     * This would also help make it easier to understand which parentheses
     * were part of the regex and which were part of the ACI syntax.
     * Regular expression used to match the access type group (allow, deny) and
     * the rights group "(read, write, ...)". The last pattern looks for a group
     * surrounded by parenthesis. The group must contain at least one
     * non-paren character.
     */
    private static final String permissionRegex = "(\\w+)\\s*\\(([^()]+)\\)";
    private static final String bindRuleRegex = "(.+?\"[)]*)\\s*;";
    private static final
    String permissionRegex =
               WORD_GROUP + ZERO_OR_MORE_WHITESPACE + "\\(([^()]+)\\)";
    /*
     * Regular expression that matches a bind rule group at a coarse level. It
     * matches any character one or more times, a single quotation and
     * an optional right parenthesis.
     */
    private static final String bindRuleRegex =
            "(.+?\"[)]*)" + ACI_STATEMENT_SEPARATOR;
    /*
     * Regular expression used to match the actions of the ACI. The actions
     * are permissions and matching bind rules.
     */
    private static final String actionRegex =
            "\\s*" + permissionRegex + "\\s*" + bindRuleRegex;
            ZERO_OR_MORE_WHITESPACE + permissionRegex +
            ZERO_OR_MORE_WHITESPACE + bindRuleRegex;
    /*
     * Regular expression used to match the version value (digit.digit).
     */
    private static final String versionRegex = "(\\d\\.\\d)";
    private static final String versionToken = "(?i)version";
    private static final String aclToken = "(?i)acl";
    /*
     * Regular expression used to match the version token. Case insensitive.
     */
    private static final String versionToken = "(?i)version(?-i)";
    /*
     * Regular expression used to match the acl token. Case insensitive.
     */
    private static final String aclToken = "(?i)acl(?-i)";
    /**
     * Regular expression used to parse the body of an ACI.
     * Regular expression used to match the body of an ACI. This pattern is
     * a general verification check.
     */
    public static final String bodyRegx =
        "\\(\\s*" + versionToken + "\\s*"
        + versionRegex + "\\s*;\\s*" + aclToken + "\\s*\"(.*)\"\\s*;\\s*"
        + actionRegex + "\\s*\\)";
    /**
     * Regular expression used to parse the header of the ACI body. The
        "\\(" + ZERO_OR_MORE_WHITESPACE + versionToken +
        ZERO_OR_MORE_WHITESPACE + versionRegex +
        ACI_STATEMENT_SEPARATOR + aclToken + ZERO_OR_MORE_WHITESPACE +
        "\"(.*)\"" + ACI_STATEMENT_SEPARATOR + actionRegex +
        ZERO_OR_MORE_WHITESPACE  + "\\)";
        public static final String bodyRegx1 =
        "\\("+ Aci.ZERO_OR_MORE_WHITESPACE + versionToken +
        Aci.ZERO_OR_MORE_WHITESPACE + versionRegex +
        Aci.ACI_STATEMENT_SEPARATOR + aclToken + Aci.ZERO_OR_MORE_WHITESPACE +
        "\"(.*)\"" + Aci.ACI_STATEMENT_SEPARATOR + actionRegex +
        Aci.ZERO_OR_MORE_WHITESPACE  + "\\)";
    /*
     * Regular expression used to match the header of the ACI body. The
     * header is version and acl name.
     */
    public static final String header =
        "\\(\\s*" + versionToken + "\\s*"
        + versionRegex + "\\s*;\\s*" + aclToken + "\\s*\"(.*?)\"\\s*;";
    private static final String header =
       OPEN_PAREN + ZERO_OR_MORE_WHITESPACE + versionToken +
       ZERO_OR_MORE_WHITESPACE +
       versionRegex + ACI_STATEMENT_SEPARATOR + aclToken +
       ZERO_OR_MORE_WHITESPACE +  "\"(.*?)\"" + ACI_STATEMENT_SEPARATOR;
    /**
     * Construct an ACI body from the specified version, name and
@@ -128,7 +192,7 @@
        if(bodyMatcher.find()) {
            startPos=bodyMatcher.start();
            version  = bodyMatcher.group(VERSION);
            if (!version.equalsIgnoreCase(Aci.supportedVersion)) {
            if (!version.equalsIgnoreCase(supportedVersion)) {
                int msgID = MSGID_ACI_SYNTAX_INVAILD_VERSION;
                String message = getMessage(msgID, version);
                throw new AciException(msgID, message);
@@ -203,9 +267,6 @@
        return startPos;
    }
    //TODO Evaluate adding support for the "absolute" deny access
    //     type precedence operator.
    /**
     * Performs an evaluation of the permission-bind rule pairs
     * using the evaluation context. The method walks down
@@ -226,29 +287,32 @@
        EnumEvalResult res=EnumEvalResult.FALSE;
        List<PermBindRulePair>pairs=getPermBindRulePairs();
        for(PermBindRulePair p : pairs) {
            if(evalCtx.isDenyEval() &&
                    (p.hasAccessType(EnumAccessType.ALLOW)))
                continue;
            if(!p.hasRights(evalCtx.getRights()))
                continue;
           res=p.getBindRule().evaluate(evalCtx);
           // The evaluation result could be FAIL. Stop processing and return
           //FAIL. Maybe an internal search failed.
           if((res != EnumEvalResult.TRUE) &&
              (res != EnumEvalResult.FALSE)) {
               res=EnumEvalResult.FAIL;
               break;
           //If the access type is DENY and the pair evaluated to TRUE,
           //then stop processing and return TRUE. A deny pair
           //succeeded.
           } else if((p.hasAccessType(EnumAccessType.DENY)) &&
                     (res == EnumEvalResult.TRUE)) {
               res=EnumEvalResult.TRUE;
               break;
           //An allow access type evaluated TRUE, stop processing
           //and return TRUE.
           } else if((p.hasAccessType(EnumAccessType.ALLOW) &&
                     (res == EnumEvalResult.TRUE))) {
               res=EnumEvalResult.TRUE;
               break;
           }
            res=p.getBindRule().evaluate(evalCtx);
            // The evaluation result could be FAIL. Stop processing and return
            //FAIL. Maybe an internal search failed.
            if((res != EnumEvalResult.TRUE) &&
                    (res != EnumEvalResult.FALSE)) {
                res=EnumEvalResult.FAIL;
                break;
                //If the access type is DENY and the pair evaluated to TRUE,
                //then stop processing and return TRUE. A deny pair
                //succeeded.
            } else if((p.hasAccessType(EnumAccessType.DENY)) &&
                    (res == EnumEvalResult.TRUE)) {
                res=EnumEvalResult.TRUE;
                break;
                //An allow access type evaluated TRUE, stop processing
                //and return TRUE.
            } else if((p.hasAccessType(EnumAccessType.ALLOW) &&
                    (res == EnumEvalResult.TRUE))) {
                res=EnumEvalResult.TRUE;
                break;
            }
        }
        return res;
    }
opends/src/server/org/opends/server/authorization/dseecompat/AciContainer.java
@@ -104,6 +104,11 @@
     */
    private Operation operation;
    /*
     * True if a targattrfilters match was found.
     */
    private boolean targAttrFiltersMatch=false;
    /**
     * This constructor is used by all currently supported LDAP operations.
     *
@@ -305,6 +310,7 @@
    public void setRights(int rights) {
         this.rights=rights;
    }
    /**
     * Gets the hostname of the remote client.
     * @return  Cannonical hostname of remote client.
@@ -322,7 +328,7 @@
    }
    /**
     * Return true if this is an add operation.
     * Return true if the current operation is a LDAP add operation.
     * @return True if this is an add operation.
     */
    public boolean isAddOperation() {
@@ -330,13 +336,32 @@
    }
    /**
     * Tries to determine the authentication information from the connection
     * class. The checks are for simple and SASL, anything else is not a
     * match. If the bind rule needs the SSL client flag then that needs
     * to be set. This code is used by the authmethod bind rule keyword.
     * Set to true  if the ACI had a targattrfilter rule that matched.
     * @param v  The value to use.
     */
    public void setTargAttrFiltersMatch(boolean v) {
        this.targAttrFiltersMatch=v;
    }
    /**
     * Return the value of the targAttrFiltersMatch variable. This is set to
     * true if the ACI had a targattrfilter rule that matched.
     * @return  True if the ACI had a targattrfilter rule that matched.
     */
    public boolean getTargAttrFiltersMatch() {
        return targAttrFiltersMatch;
    }
    /**
     * Try to determine the authentication information from the current
     * client connection. The checks are for simple and SASL, anything else
     * is not a match. If the bind rule requires any SSL client authentication
     * information then the "wantSSL" flag should be set. This code is used by
     * the authmethod bind rule keyword.
     *
     * @param wantSSL True if the bind rule needs the ssl client auth check.
     * @return  Return an enumeration containing the authentication method
     * for this connection.
     * type for this client connection.
     */
    public EnumAuthMethod getAuthenticationMethod(boolean wantSSL) {
        EnumAuthMethod method=EnumAuthMethod.AUTHMETHOD_NOMATCH;
opends/src/server/org/opends/server/authorization/dseecompat/AciException.java
@@ -45,7 +45,9 @@
   */
  private static final long serialVersionUID = -2763328522960628853L;
    // The unique message ID for the associated message.
    /*
     * The unique message ID for the associated message.
     */
    private int messageID;
    /**
opends/src/server/org/opends/server/authorization/dseecompat/AciHandler.java
@@ -29,20 +29,11 @@
import org.opends.server.api.AccessControlHandler;
import static org.opends.server.authorization.dseecompat.AciMessages.*;
import static org.opends.server.authorization.dseecompat.Aci.*;
import org.opends.server.core.*;
import static org.opends.server.loggers.Error.logError;
import static org.opends.server.messages.MessageHandler.getMessage;
import org.opends.server.types.Attribute;
import org.opends.server.types.AttributeType;
import org.opends.server.types.AttributeValue;
import org.opends.server.types.DN;
import org.opends.server.types.Entry;
import org.opends.server.types.ErrorLogCategory;
import org.opends.server.types.ErrorLogSeverity;
import org.opends.server.types.Modification;
import org.opends.server.types.Privilege;
import org.opends.server.types.SearchResultEntry;
import org.opends.server.types.SearchResultReference;
import org.opends.server.types.*;
import static org.opends.server.util.StaticUtils.toLowerCase;
import java.util.LinkedList;
import java.util.List;
@@ -54,87 +45,6 @@
 */
public class AciHandler extends AccessControlHandler
{
    /**
     * ACI_ADD is used to set the container rights for a LDAP add operation.
     */
    public static final int ACI_ADD = 0x0001;
    /**
     * ACI_DELETE is used to set the container rights for a LDAP
     * delete operation.
     */
    public static final int ACI_DELETE = 0x0002;
    /**
     * ACI_READ is used to set the container rights for a LDAP
     * search operation.
     */
    public static final int ACI_READ = 0x0004;
    /**
     * ACI_WRITE is used to set the container rights for a LDAP
     * modify operation.
     */
    public static final int ACI_WRITE = 0x0008;
    /**
     * ACI_COMPARE is used to set the container rights for a LDAP
     * compare operation.
     */
    public static final int ACI_COMPARE = 0x0010;
    /**
     * ACI_SEARCH is used to set the container rights a LDAP search operation.
     */
    public static final int ACI_SEARCH = 0x0020;
    /**
     * ACI_SELF is used for the SELFWRITE right. Currently not implemented.
     */
    public static final int ACI_SELF = 0x0040;
    /**
     * ACI_ALL is used to as a mask for all of the above. These
     * six below are not masked by the ACI_ALL.
     */
    public static final int ACI_ALL = 0x007F;
    /**
     * ACI_PROXY is used for the PROXY right. Currently not implemented.
     */
    public static final int ACI_PROXY = 0x0080;
    /**
     * ACI_IMPORT is used to set the container rights for a LDAP
     * modify dn operation. Currently not implemented.
     */
    public static final int ACI_IMPORT = 0x0100;
    /**
     * ACI_EXPORT is used to set the container rights for a LDAP
     * modify dn operation. Currently not implemented.
     */
    public static final int ACI_EXPORT = 0x0200;
    /**
     * ACI_WRITE_ADD and ACI_WRITE_DELETE are used by the LDAP modify
     * operation. They currently don't have much value; but will be needed
     * once the targetattrfilters target and modify dn are implemented.
     */
    public static final int ACI_WRITE_ADD = 0x800;
    /**
     * See above.
     */
    public static final int ACI_WRITE_DELETE = 0x400;
    /**
     * ACI_NULL is used to set the container rights to all zeros. Used
     * by LDAP modify.
     */
    public static final int ACI_NULL = 0x0000;
    /*
     * The list that holds that ACIs keyed by the DN of the entry
      * holding the ACI.
@@ -511,6 +421,39 @@
    }
    /**
     * Test the attribute types of the search filter for access. This method
     * supports the search right.
     *
     * @param container  The container used in the access evaluation.
     * @param filter The filter to check access on.
     * @return  True if all attribute types in the filter have access.
     */
    private boolean
    testFilter(AciLDAPOperationContainer container, SearchFilter filter) {
        boolean ret=true;
        switch (filter.getFilterType()) {
            case AND:
            case OR: {
                for (SearchFilter f : filter.getFilterComponents())
                    if(!testFilter(container, f))
                        return false ;
                break;
            }
            case NOT:  {
                SearchFilter f = filter.getNotComponent();
                ret=!testFilter(container, f);
                break;
            }
            default: {
                AttributeType attrType=filter.getAttributeType();
                container.setCurrentAttributeType(attrType);
                ret=accessAllowed(container);
            }
        }
        return ret;
    }
    /**
     * Evaluate an entry to be added to see if it has any "aci"
     * attribute type. If it does, examines each "aci" attribute type
     * value for syntax errors. All of the "aci" attribute type values
@@ -657,19 +600,6 @@
                          skipAccessCheck(operation));
  }
  /*
   * TODO Add access testing of the filter against the entry. This was
   * brought up in the first code review.
   *
   *  The static block that creates the arrays of EnumRight objects needs to
   *  be documented to explain what they are.  Also, I still disagree with
   *  the  interpretation that the READ right is all that is necessary to
   *  perform either search or compare operations.  That definitely goes
   *  against the documentation, which states that READ applies only to
   *  the search operation, and that users must have both SEARCH and READ
   *  in order to access the results.
   */
  /**
   * Checks access on a search operation.
   * @param operation The search operation class containing information to
@@ -681,9 +611,16 @@
  maySend(SearchOperation operation, SearchResultEntry entry) {
      AciLDAPOperationContainer operationContainer =
              new AciLDAPOperationContainer(operation,
                                           (ACI_READ | ACI_SEARCH), entry);
      return skipAccessCheck(operation) ||
              accessAllowedEntry(operationContainer);
                      (ACI_SEARCH), entry);
      boolean ret;
      if(!(ret=skipAccessCheck(operation))) {
          ret=testFilter(operationContainer, operation.getFilter());
          if (ret) {
              operationContainer.setRights(ACI_READ);
              ret=accessAllowedEntry(operationContainer);
          }
      }
      return ret;
  }
  /*
opends/src/server/org/opends/server/authorization/dseecompat/AciLDAPOperationContainer.java
@@ -44,16 +44,17 @@
 */
public class AciLDAPOperationContainer extends AciContainer  {
    /**
    /*
     * The entry to be returned if this is a LDAP search.
     */
    private SearchResultEntry searchEntry;
    /**
    /*
     * The list of modifications if this operation is a LDAP
     * modify.
     */
    private List<Modification>  modifications;
    /**
     * Constructor interface for the compare operation.
     * @param operation The compare operation to evaluate.
opends/src/server/org/opends/server/authorization/dseecompat/AciList.java
@@ -49,7 +49,8 @@
 * using the entry DN as the key.
 */
public class AciList {
  /**
  /*
   * A map containing all the ACIs.
   * We use the copy-on-write technique to avoid locking when reading.
   */
opends/src/server/org/opends/server/authorization/dseecompat/AciListenerManager.java
@@ -54,13 +54,17 @@
public class AciListenerManager
        implements ChangeNotificationListener, BackendInitializationListener {
    /*
     * The AciList caches the ACIs.
     */
    private AciList aciList;
       /*
    /*
     * Search filter used in context search for "aci" attribute types.
     */
    private static SearchFilter aciFilter;
    /**
    /*
     * The aci attribute type is operational so we need to specify it to be
     * returned.
     */
@@ -77,6 +81,7 @@
        }
        attrs.add("aci");
    }
    /**
     * Save the list created by the AciHandler routine.
     * @param aciList The list object created and loaded by the handler.
opends/src/server/org/opends/server/authorization/dseecompat/AciMessages.java
@@ -364,10 +364,10 @@
    /**
     * The message ID for the message that will be used if an "aci" attribute
     * type value failed parsing because of an invalid target keyword operator.
     * This takes one argument, which is the target keyword operator string
     * parsed from the "aci" attribute type value string.
     * This takes two arguments, which  are the target keyword operator string
     * parsed from the "aci" attribute type value string and the keyword string.
     */
    public static final int MSGID_ACI_SYNTAX_INVALID_TARGET_OPERATOR =
    public static final int MSGID_ACI_SYNTAX_INVALID_TARGET_NOT_OPERATOR =
        CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_SEVERE_WARNING | 35;
    /**
@@ -392,11 +392,11 @@
    /**
     * The message ID for the message that will be used if an "aci" attribute
     * type value failed parsing because of an invalid targetscope keyword
     * operator. This takes one argument, which is the targetscope keyword
     * type value failed parsing because of an invalid target keyword
     * operator. This takes one argument, which is the target keyword
     * operator string parsed from the "aci" attribute type value string.
     */
    public static final int MSGID_ACI_SYNTAX_INVALID_TARGETSCOPE_OPERATOR =
    public static final int MSGID_ACI_SYNTAX_INVALID_TARGETS_OPERATOR =
        CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_SEVERE_WARNING | 38;
    /**
@@ -528,6 +528,88 @@
    public static
    final int MSGID_ACI_SYNTAX_INVALID_USERATTR_ROLEDN_INHERITANCE_PATTERN =
        CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_SEVERE_WARNING | 51;
   /**
     * The message ID for the message that will be used if an "aci" attribute
     * type value parse failed because a targattrfilters keyword expression
     * did not parse because the operation was invalid.  This takes two
     * arguments, which are the targattrfilters expression parsed from the ACI
     * and a message further clarifying the error.
     */
    public static
    final int MSGID_ACI_SYNTAX_INVALID_TARGATTRFILTERS_OPERATION =
        CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_SEVERE_WARNING | 52;
    /**
     * The message ID for the message that will be used if an "aci" attribute
     * type value parse failed because a targattrfiltesr keyword expression
     * did not parse.  This takes one argument, which is the targattrfilters
     * expression parsed from the ACI.
     */
    public static
    final int MSGID_ACI_SYNTAX_INVALID_TARGATTRFILTERS_EXPRESSION =
        CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_SEVERE_WARNING | 53;
      /**
     * The message ID for the message that will be used if an "aci" attribute
     * type value parse failed because the operation tokens targattrfilters
     * in the expresssion are the same.  This takes one argument, which is
     * the targattrfilters expression parsed from the ACI.
     */
    public static
    final int MSGID_ACI_SYNTAX_INVALID_TARGATTRFILTERS_OPS_MATCH =
        CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_SEVERE_WARNING | 54;
    /**
    * The message ID for the message that will be used if an "aci" attribute
    * type value parse failed because the there are two many targattrfilters
    * filter list statements in the ACI.  This takes one argument, which is
    * the targattrfilters expression parsed from the ACI.
    */
   public static
   final int MSGID_ACI_SYNTAX_INVALID_TARGATTRFILTERS_MAX_FILTER_LISTS =
       CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_SEVERE_WARNING | 55;
  /**
    * The message ID for the message that will be used if an "aci" attribute
    * type value parse failed because the targattrfilters expression statement
    * is in the wrong format.  This takes one argument, which is
    * the targattrfilters expression parsed from the ACI.
    */
   public static
   final int MSGID_ACI_SYNTAX_INVALID_TARGATTRFILTERS_FILTER_LIST_FORMAT =
       CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_SEVERE_WARNING | 56;
    /**
      * The message ID for the message that will be used if an "aci" attribute
      * type value parse failed because one or more targattrfilters filter
      * statements are invalid.  This takes two arguments, which are
      * the targattrfilters expression parsed from the ACI and an error
      * message from the createFilterFromString method.
      */
     public static
     final int MSGID_ACI_SYNTAX_INVALID_TARGATTRFILTERS_FILTER_LISTS_FILTER =
         CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_SEVERE_WARNING | 57;
     /**
      * The message ID for the message that will be used if an "aci" attribute
      * type value parse failed because one or more targattrfilters filter
      * statements contain invalid attribute type names.  This takes one
      * argument, which is the targattrfilters expression parsed from the ACI.
      */
     public static final
        int MSGID_ACI_SYNTAX_INVALID_TARGATTRFILTERS_FILTER_LISTS_ATTR_FILTER =
         CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_SEVERE_WARNING | 58;
     /**
      * The message ID for the message that will be used if an "aci" attribute
      * type name is invalid.  This takes one
      * argument, which is the attribute type name parsed from the ACI.
      */
     public static final
        int MSGID_ACI_SYNTAX_INVALID_ATTRIBUTE_TYPE_NAME =
         CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_SEVERE_WARNING | 59;
    /**
     * Associates a set of generic messages with the message IDs defined in
     * this class.
@@ -559,7 +641,7 @@
                "The provided Access Control Instruction (ACI) rights " +
                "keyword values \"%s\" are invalid. The valid rights " +
                "keyword values are one or more of the following: read, " +
                "write, add, delete, search, compare or the single value" +
                "write, add, delete, search, compare or the single value " +
                "all.");
        registerMessage(MSGID_ACI_SYNTAX_BIND_RULE_MISSING_CLOSE_PAREN,
@@ -733,7 +815,7 @@
                " value is one of the following: target, targetscope, " +
                "targetfilter, targetattr or targetattrfilters.");
        registerMessage(MSGID_ACI_SYNTAX_INVALID_TARGET_OPERATOR,
        registerMessage(MSGID_ACI_SYNTAX_INVALID_TARGETS_OPERATOR,
                "The provided Access Control Instruction (ACI) target " +
                "keyword operator value  \"%s\" is invalid. A valid target" +
                "keyword operator value is either '=' or \"!=\".");
@@ -747,10 +829,10 @@
                "target keyword value \"%s\" was seen multiple times in" +
                " the ACI \"%s\".");
        registerMessage(MSGID_ACI_SYNTAX_INVALID_TARGETSCOPE_OPERATOR,
                "The provided Access Control Instruction (ACI) targetscope" +
                " keyword operator value \"%s\" is invalid. The only valid" +
                "targetscope operator value is '='.");
        registerMessage(MSGID_ACI_SYNTAX_INVALID_TARGET_NOT_OPERATOR,
                "The provided Access Control Instruction (ACI) target" +
                " operator value \"%s\" is invalid. The only valid" +
                "target operator value for the \"%s\" keyword is '='.");
        registerMessage(MSGID_ACI_SYNTAX_INVALID_TARGETSCOPE_EXPRESSION,
                "The provided Access Control Instruction (ACI) targetscope" +
@@ -775,7 +857,7 @@
                "The provided Access Control Instruction (ACI) " +
                "targetattr expression value \"%s\" is invalid. A valid " +
                "targetattr keyword expression value requires one or more " +
                "attribute type values in the following format: " +
                "valid attribute type names in the following format: " +
                "attribute1 [|| attribute1] ... [|| attributen].");
        registerMessage(
@@ -823,5 +905,67 @@
                "userattr expression inheritance pattern value " +
                "\"%s\" is invalid for the roledn keyword because it starts " +
                        "with the string \"parent[\".");
        registerMessage(MSGID_ACI_SYNTAX_INVALID_TARGATTRFILTERS_OPERATION,
                "The provided Access Control Instruction (ACI) " +
                "targattrfilter expression value " +
                "%s is invalid because %s.");
        registerMessage(MSGID_ACI_SYNTAX_INVALID_TARGATTRFILTERS_EXPRESSION,
                "The provided Access Control Instruction (ACI) " +
                "targattrfilter expression value " +
                "%s is invalid because it is not in the correct format." +
                "A valid targattrsfilters expression value must be in " +
                "the following format: "+
               "\"add=attr1: F1 && attr2: F2 ... && attrn: Fn" +
                ",del= attr1: F1 && attr2: F2 ... && attrn: Fn\"");
        registerMessage(MSGID_ACI_SYNTAX_INVALID_TARGATTRFILTERS_OPS_MATCH,
                "The provided Access Control Instruction (ACI) " +
                   "targattrfilter expression value " +
                   "%s is invalid because the both operation tokens " +
                   "match in the two filter lists.");
        registerMessage(
                MSGID_ACI_SYNTAX_INVALID_TARGATTRFILTERS_MAX_FILTER_LISTS,
                "The provided Access Control Instruction (ACI) " +
                     "targattrfilters expression value " +
                     "%s is invalid because there are more than two" +
                      "filter list statements.");
        registerMessage(
                MSGID_ACI_SYNTAX_INVALID_TARGATTRFILTERS_FILTER_LIST_FORMAT,
                "The provided Access Control Instruction (ACI) " +
                "targattrfilters expression value " +
                "%s is invalid because the provided filter list string " +
                "is in the wrong format. A valid targattrfilters filter " +
                "list must be in the following format: " +
                "add=attr1: F1 && attr2: F2 ... && attrn: Fn .");
        registerMessage(
             MSGID_ACI_SYNTAX_INVALID_TARGATTRFILTERS_FILTER_LISTS_FILTER,
             "The provided Access Control Instruction (ACI) " +
             "targattrfilters expression value " +
             "%s is invalid because the one or more of the specified" +
             "filters are invalid for the following reason: " +
             "%s.");
        registerMessage(
             MSGID_ACI_SYNTAX_INVALID_TARGATTRFILTERS_FILTER_LISTS_ATTR_FILTER,
             "The provided Access Control Instruction (ACI) " +
             "targattrfilters expression value " +
             "%s is invalid because the one or more of the specified" +
             "filters are invalid because of non-matching attribute" +
             "type names in the filter.");
        registerMessage(
             MSGID_ACI_SYNTAX_INVALID_ATTRIBUTE_TYPE_NAME,
             "The provided Access Control Instruction (ACI) " +
             "attribute name value " +
             "%s is invalid. A valid attribute type name must begin " +
             "with an ASCII letter and must contain only ASCII letters," +
              "digits or the \"-\" character.");
    }
}
opends/src/server/org/opends/server/authorization/dseecompat/AciProvider.java
@@ -38,11 +38,13 @@
 */
public class AciProvider  implements AccessControlProvider  {
    /*
     * The AciHandler does all the work in this package.
     */
    private static AciHandler instance = null;
    /**
     * Create an aci provider. This doesn't do much.
     * Create an aci provider.
     */
    public AciProvider() {
        super();
opends/src/server/org/opends/server/authorization/dseecompat/AciTargetMatchContext.java
@@ -41,6 +41,7 @@
 * checked on.
 */
public interface AciTargetMatchContext {
    /**
     * Set the deny ACI list.
     * @param denyList The deny ACI list.
@@ -129,6 +130,19 @@
     * @param rights The rights to set the container's rights to.
     */
    public void setRights(int rights);
    /**
     * Set to true  if the ACI had a targattrfilter rule that matched.
     * @param v  The value to use.
     */
    public void setTargAttrFiltersMatch(boolean v);
    /**
     * Return the value of the targAttrFiltersMatch variable. This is set to
     * true if the ACI had a targattrfilter rule that matched.
     * @return  True if the ACI had a targattrfilter rule that matched.
     */
    public boolean getTargAttrFiltersMatch();
}
opends/src/server/org/opends/server/authorization/dseecompat/AciTargets.java
@@ -28,6 +28,7 @@
package org.opends.server.authorization.dseecompat;
import static org.opends.server.authorization.dseecompat.AciMessages.*;
import static org.opends.server.authorization.dseecompat.Aci.*;
import static org.opends.server.messages.MessageHandler.getMessage;
import org.opends.server.types.AttributeType;
import org.opends.server.types.DN;
@@ -40,47 +41,75 @@
 * of an ACI before the ACI body and specifies the entry, attributes, or set
 * of entries and attributes which the ACI controls access.
 *
 * The four supported  ACI target keywords currently
 * supported are: target, targetattr, targetscope and targetfilter.
 * Missing is support for targetattrfilters.
 * The five supported  ACI target keywords are: target, targetattr,
 * targetscope, targetfilter and targattrfilters.
 */
public class AciTargets {
    /*
     * ACI syntax has a target keyword.
     */
    private Target target = null ;
    /*
     * ACI syntax has a targetscope keyword.
     */
    private SearchScope targetScope = SearchScope.WHOLE_SUBTREE;
    /*
     * ACI syntax has a targetattr keyword.
     */
    private TargetAttr targetAttr = null ;
    /*
     * ACI syntax has a targetfilter keyword.
     */
    private TargetFilter targetFilter=null;
    private TargAttrFilters targAttrFilters=null;
    /*
     * These are used in the regular expression parsing.
     * ACI syntax has a targattrtfilters keyword.
     */
    private TargAttrFilters targAttrFilters=null;
    /*
     * The number of regular expression group positions in a valid ACI target
     * expression.
     */
    private static final int targetElementCount = 3;
    private static final int targetKeywordPos       = 1;
    private static final int targetOperatorPos      = 2;
    private static final int targetExpressionPos    = 3;
    /*
     * TODO Make the regular expression strings below easier to
     * understand.
     *
     * The same note earlier about making regex values easier to
     * understand applies to this class as well.
     *  Regular expression group position of a target keyword.
     */
    private static final int targetKeywordPos       = 1;
    /*
     *  Regular expression group position of a target operator enumeration.
     */
    private static final int targetOperatorPos      = 2;
    /*
     *  Regular expression group position of a target expression statement.
     */
    private static final int targetExpressionPos    = 3;
    /*
     * Regular expression used to match a single target rule.
     */
    private static final String targetRegex =
            "\\(\\s*(\\w+)\\s*(!?=)\\s*\"([^\"]+)\"\\s*\\)\\s*";
           OPEN_PAREN +  ZERO_OR_MORE_WHITESPACE  +  WORD_GROUP +
           ZERO_OR_MORE_WHITESPACE + "(!?=)" + ZERO_OR_MORE_WHITESPACE +
           "\"([^\"]+)\"" + ZERO_OR_MORE_WHITESPACE + CLOSED_PAREN +
           ZERO_OR_MORE_WHITESPACE;
    private static final String targetRegex1 =
           "\\(" +  Aci.ZERO_OR_MORE_WHITESPACE  +  Aci.WORD_GROUP +
           Aci.ZERO_OR_MORE_WHITESPACE + "(!?=)" + Aci.ZERO_OR_MORE_WHITESPACE +
           "\"([^\"]+)\"" + Aci.ZERO_OR_MORE_WHITESPACE + "\\)" +
           Aci.ZERO_OR_MORE_WHITESPACE;
    /**
    * Regular expression used in target matching.
    * Regular expression used to match one or more target rules. The patern is
    * part of a general ACI verification.
    */
    public static final String targetsRegex = "(" + targetRegex + ")*";
@@ -106,8 +135,7 @@
     *  for this ACI.
     */
    private static final int skipRights =
            (AciHandler.ACI_ADD | AciHandler.ACI_DELETE | AciHandler.ACI_PROXY);
    private static final int skipRights = (ACI_ADD | ACI_DELETE | ACI_PROXY);
    /**
     * Creates an ACI target from the specified arguments. All of these
@@ -116,14 +144,17 @@
     * @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.
     */
    private AciTargets(Target targetEntry, TargetAttr targetAttr,
                       TargetFilter targetFilter,
                       SearchScope targetScope) {
                       SearchScope targetScope,
                       TargAttrFilters targAttrFilters) {
       this.target=targetEntry;
       this.targetAttr=targetAttr;
       this.targetScope=targetScope;
       this.targetFilter=targetFilter;
       this.targAttrFilters=targAttrFilters;
    }
    /**
@@ -162,6 +193,14 @@
    }
    /**
     * Return the class representing the ACI targattrfilters keyword. May be
     * null.
     * @return The targattrfilters information.
     */
    public TargAttrFilters getTargAttrFilters() {
        return targAttrFilters;
    }
    /**
     * Decode an ACI's target part of the syntax from the string provided.
     * @param input String representing an ACI target part of syntax.
     * @param dn The DN of the entry containing the ACI.
@@ -197,7 +236,7 @@
            EnumTargetOperator targetOperator =
                EnumTargetOperator.createOperator(operator);
            if (targetOperator == null) {
                int msgID = MSGID_ACI_SYNTAX_INVALID_TARGET_OPERATOR;
                int msgID = MSGID_ACI_SYNTAX_INVALID_TARGETS_OPERATOR;
                String message = getMessage(msgID, operator);
                throw new AciException(msgID, message);
            }
@@ -238,7 +277,7 @@
            {
                // Check the operator for the targetscope is EQUALITY
                if (targetOperator == EnumTargetOperator.NOT_EQUALITY) {
                    int msgID = MSGID_ACI_SYNTAX_INVALID_TARGETSCOPE_OPERATOR;
                    int msgID = MSGID_ACI_SYNTAX_INVALID_TARGET_NOT_OPERATOR;
                    String message = getMessage(msgID, operator);
                    throw new AciException(msgID, message);
                }
@@ -253,31 +292,39 @@
                }
                else {
                    int msgID =
                        MSGID_ACI_SYNTAX_INVALID_TARGET_DUPLICATE_KEYWORDS;
                            MSGID_ACI_SYNTAX_INVALID_TARGET_DUPLICATE_KEYWORDS;
                    String message =
                        getMessage(msgID, "targetfilter", input);
                            getMessage(msgID, "targetfilter", input);
                    throw new AciException(msgID, message);
                }
                break;
            }
                case KEYWORD_TARGATTRFILTERS:
                {
                    if (targAttrFilters == null){
                        targAttrFilters = TargAttrFilters.decode(targetOperator,
                                expression);
                    }
                    else {
            case KEYWORD_TARGATTRFILTERS:
            {
                if (targAttrFilters == null){
                    // Check the operator for the targattrfilters is EQUALITY
                    if (targetOperator == EnumTargetOperator.NOT_EQUALITY) {
                        int msgID =
                             MSGID_ACI_SYNTAX_INVALID_TARGET_DUPLICATE_KEYWORDS;
                        String message =
                                getMessage(msgID, "targattrfilters", input);
                                MSGID_ACI_SYNTAX_INVALID_TARGET_NOT_OPERATOR;
                        String message = getMessage(msgID, operator);
                        throw new AciException(msgID, message);
                    }
                    break;
                    targAttrFilters = TargAttrFilters.decode(targetOperator,
                            expression);
                }
                else {
                    int msgID =
                            MSGID_ACI_SYNTAX_INVALID_TARGET_DUPLICATE_KEYWORDS;
                    String message =
                            getMessage(msgID, "targattrfilters", input);
                    throw new AciException(msgID, message);
                }
                break;
            }
            }
        }
        return new AciTargets(target, targetAttr, targetFilter, targetScope);
        return new AciTargets(target, targetAttr, targetFilter,
                              targetScope, targAttrFilters);
    }
    /*
@@ -308,7 +355,7 @@
    }
    /**
     * Checks an ACI's targetfilter information against an target match
     * Checks an ACI's targetfilter 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
@@ -324,6 +371,32 @@
        return ret;
    }
    /**
     * Check an ACI's targattrfilters 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.
     */
    public static boolean isTargAttrFiltersApplicable(Aci aci,
                                               AciTargetMatchContext matchCtx) {
        boolean ret=true;
        TargAttrFilters targAttrFilters=aci.getTargets().getTargAttrFilters();
        if(targAttrFilters != null) {
            if((matchCtx.hasRights(ACI_ADD) &&
                targAttrFilters.hasMask(TARGATTRFILTERS_ADD)) ||
              (matchCtx.hasRights(ACI_DELETE) &&
               targAttrFilters.hasMask(TARGATTRFILTERS_DELETE)))
                ret=targAttrFilters.isApplicableAddDel(matchCtx);
            else if((matchCtx.hasRights(ACI_WRITE_ADD) &&
                     targAttrFilters.hasMask(TARGATTRFILTERS_ADD)) ||
                    (matchCtx.hasRights(ACI_WRITE_DELETE) &&
                    targAttrFilters.hasMask(TARGATTRFILTERS_DELETE)))
                ret=targAttrFilters.isApplicableMod(matchCtx);
        }
        return ret;
    }
    /*
     * TODO Evaluate making this method more efficient.
     * The isTargetAttrApplicable method looks a lot less efficient than it
@@ -338,26 +411,28 @@
     * @return True if the targetattr matched the target context.
     */
    public static boolean isTargetAttrApplicable(Aci aci,
                                AciTargetMatchContext targetMatchCtx) {
                                         AciTargetMatchContext targetMatchCtx) {
        boolean ret=true;
        AciTargets targets=aci.getTargets();
        AttributeType a=targetMatchCtx.getCurrentAttributeType();
        int rights=targetMatchCtx.getRights();
        boolean isFirstAttr=targetMatchCtx.isFirstAttribute();
        if((a != null) && (targets.getTargetAttr() != null)) {
            ret=TargetAttr.isApplicable(a, targets.getTargetAttr());
        } else if((a != null) || (targets.getTargetAttr() != null)) {
            if((aci.hasRights(skipRights)) && (skipRightsHasRights(rights))) {
                ret=true;
            } else if ((targets.getTargetAttr() != null) &&
                    (a == null) && (aci.hasRights(AciHandler.ACI_WRITE))) {
                ret = true;
            } else {
                ret = false;
        if(!targetMatchCtx.getTargAttrFiltersMatch()) {
            AciTargets targets=aci.getTargets();
            AttributeType a=targetMatchCtx.getCurrentAttributeType();
            int rights=targetMatchCtx.getRights();
            boolean isFirstAttr=targetMatchCtx.isFirstAttribute();
            if((a != null) && (targets.getTargetAttr() != null))
                ret=TargetAttr.isApplicable(a, targets.getTargetAttr());
            else if((a != null) || (targets.getTargetAttr() != null)) {
                if((aci.hasRights(skipRights)) &&
                                                (skipRightsHasRights(rights)))
                    ret=true;
                else if ((targets.getTargetAttr() != null) &&
                        (a == null) && (aci.hasRights(ACI_WRITE)))
                    ret = true;
                else
                    ret = false;
            }
            if((isFirstAttr) && (aci.getTargets().getTargetAttr() == null))
                targetMatchCtx.setEntryTestRule(true);
        }
        if((isFirstAttr) && (aci.getTargets().getTargetAttr() == null))
            targetMatchCtx.setEntryTestRule(true);
        return ret;
    }
opends/src/server/org/opends/server/authorization/dseecompat/AuthMethod.java
@@ -34,7 +34,15 @@
 * The AuthMethod class represents an authmethod bind rule keyword expression.
 */
public class AuthMethod implements KeywordBindRule {
    /*
     * Enumeration representing the authentication method.
     */
    private EnumAuthMethod authMethod=null;
    /*
     * Enumeration representing the bind rule operation type.
     */
    private EnumBindRuleType type=null;
    /**
opends/src/server/org/opends/server/authorization/dseecompat/BindRule.java
@@ -28,6 +28,7 @@
package org.opends.server.authorization.dseecompat;
import static org.opends.server.authorization.dseecompat.AciMessages.*;
import static org.opends.server.authorization.dseecompat.Aci.*;
import static org.opends.server.messages.MessageHandler.getMessage;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
@@ -38,38 +39,84 @@
 * pair.
 */
public class BindRule {
    /*
     * This hash table holds the keyword bind rule mapping.
     */
    private HashMap<String, KeywordBindRule> keywordRuleMap =
                                    new HashMap<String, KeywordBindRule>();
    //True is a boolean "not" was seen.
    /*
     * True is a boolean "not" was seen.
     */
    private boolean negate=false;
    //Complex bind rules have left and right values.
    /*
     * Complex bind rules have left and right values.
     */
    private BindRule left = null;
    private BindRule right = null;
    //Enumeration of the boolean type of the complex bind rule ("and" or "or").
    /*
     * Enumeration of the boolean type of the complex bind rule ("and" or "or").
     */
    private EnumBooleanTypes booleanType = null;
    //The keyword of a simple bind rule.
    /*
     * The keyword of a simple bind rule.
     */
    private EnumBindRuleKeyword keyword = null;
    //Regular expression stuff that needs to be made clearer.
    /*
     * Regular expression group position of a bind rule keyword.
     */
    private static final int keywordPos = 1;
    /*
     * Regular expression group position of a bind rule operation.
     */
    private static final int opPos = 2;
    /*
     * Regular expression group position of a bind rule expression.
     */
    private static final int expressionPos = 3;
    private static final String keywordRegex = "^(\\w+)";
    private static final String opRegex = "([!=<>]+)";
    private static final String expressionRegex = "\"([^\"]+)\"\\s*";
    private static final String bindruleRegex =
        keywordRegex + "\\s*" + opRegex + "\\s*" + expressionRegex;
    /*
     * Regular expression group position of the remainder part of an operand.
     */
    private static final int remainingOperandPos = 1;
    /*
     * Regular expression group position of the remainder of the bind rule.
     */
    private static final int remainingBindrulePos = 2;
    /*
     * Regular expression for valid bind rule operator group.
     */
    private static final String opRegGroup = "([!=<>]+)";
    /*
     * Regular expression for the expression part of a partially parsed
     * bind rule.
     */
    private static final String expressionRegex =
                                  "\"([^\"]+)\"" + ZERO_OR_MORE_WHITESPACE;
    /*
     * Regular expression for a single bind rule.
     */
    private static final String bindruleRegex =
        WORD_GROUP_START_PATTERN + ZERO_OR_MORE_WHITESPACE +
        opRegGroup + ZERO_OR_MORE_WHITESPACE + expressionRegex;
    /*
     * Regular expression of the remainder part of a partially parsed bind rule.
     */
    private static final String remainingBindruleRegex =
        "^\\s*(\\w+)\\s*(.*)$";
        ZERO_OR_MORE_WHITESPACE_START_PATTERN + WORD_GROUP +
        ZERO_OR_MORE_WHITESPACE + "(.*)$";
    /**
     * Constructor that takes an keyword enumeration and corresponding
opends/src/server/org/opends/server/authorization/dseecompat/DNS.java
@@ -28,6 +28,7 @@
package org.opends.server.authorization.dseecompat;
import static org.opends.server.authorization.dseecompat.AciMessages.*;
import static org.opends.server.authorization.dseecompat.Aci.*;
import static org.opends.server.messages.MessageHandler.getMessage;
import java.util.LinkedList;
import java.util.regex.Matcher;
@@ -38,9 +39,28 @@
 */
public class DNS implements KeywordBindRule {
    /*
     * List of patterns to match against.
     */
    LinkedList<String> patterns=null;
    /*
     * The enumeration representing the bind rule type of the DNS rule.
     */
    private EnumBindRuleType type=null;
    /*
     *  Regular expression group used to match a dns rule.
     */
    private static final String valueRegex = "([a-zA-Z0-9\\.\\-\\*]+)";
    /*
     * Regular expression group used to match one or more DNS values.
     */
    private static final String valuesRegExGroup =
            valueRegex + ZERO_OR_MORE_WHITESPACE +
            "(," +  ZERO_OR_MORE_WHITESPACE  +  valueRegex  +  ")*";
    /**
     * Create a class representing a dns bind rule keyword.
     * @param patterns List of dns patterns to match against.
@@ -62,9 +82,7 @@
    public static DNS decode(String expr,  EnumBindRuleType type)
    throws AciException
    {
        String valueRegex = "([a-zA-Z0-9\\.\\-\\*]+)";
        String valuesRegex = valueRegex + "\\s*(,\\s*" + valueRegex + ")*";
        if (!Pattern.matches(valuesRegex, expr)) {
        if (!Pattern.matches(valuesRegExGroup, expr)) {
            int msgID = MSGID_ACI_SYNTAX_INVALID_DNS_EXPRESSION;
            String message = getMessage(msgID, expr);
            throw new AciException(msgID, message);
opends/src/server/org/opends/server/authorization/dseecompat/DayOfWeek.java
@@ -38,7 +38,14 @@
 */
public class DayOfWeek  implements KeywordBindRule {
    /*
     * List containing the enumeration of the day of the week.
     */
    LinkedList<EnumDayOfWeek> days=null;
    /*
     * Enumeration representing the bind rule operation type.
     */
    private EnumBindRuleType type=null;
    /**
opends/src/server/org/opends/server/authorization/dseecompat/EnumAccessType.java
@@ -32,6 +32,7 @@
 * types (allow, deny).
 */
public enum EnumAccessType {
    /**
     * Allow access type.
     */
@@ -41,6 +42,9 @@
     */
    DENY    ("deny");
    /*
     * The access type string.
     */
    private final String accessType;
    /**
opends/src/server/org/opends/server/authorization/dseecompat/EnumAuthMethod.java
@@ -41,6 +41,7 @@
 * This class provides an enumeration of the allowed authmethod types.
 */
public enum EnumAuthMethod {
    /**
     * The enumeration type when the bind rule has specified authentication of
     * none.
@@ -76,10 +77,10 @@
     */
    AUTHMETHOD_NOMATCH       ("nomatch");
    /**
    /*
     * The name of the authmethod.
     */
    public String authmethod = null;
    private String authmethod = null;
    /**
     * Creates a new enumeration type for this authmethod.
opends/src/server/org/opends/server/authorization/dseecompat/EnumBindRuleKeyword.java
@@ -32,6 +32,7 @@
 * keyword types.
 */
public enum EnumBindRuleKeyword {
    /**
     * The enumeration type when the bind rule has specified keyword of
     * userdn.
@@ -77,10 +78,11 @@
     * authmethod.
     */
    AUTHMETHOD ("authmethod");
    /**
    /*
     * The keyword name.
     */
    public final String keyword;
    private final String keyword;
    /**
     * Creates a new enumeration type for the specified keyword.
opends/src/server/org/opends/server/authorization/dseecompat/EnumBindRuleType.java
@@ -31,6 +31,7 @@
 * This class provides an enumeration of the allowed bind rule types.
 */
public enum EnumBindRuleType {
    /**
     * The enumeration type when the bind rule has specified type of
     * "=".
@@ -62,7 +63,7 @@
     */
    GREATER_OR_EQUAL_BINDRULE_TYPE  (">=");
    /**
    /*
     * The bind rule type name.
     */
    private final String type;
opends/src/server/org/opends/server/authorization/dseecompat/EnumBooleanTypes.java
@@ -31,6 +31,7 @@
 * This class provides an enumeration of the allowed bind rule booelan types.
 */
public enum EnumBooleanTypes {
    /**
     * The enumeration type when the bind rule has specified boolean type of
     * "AND".
@@ -47,7 +48,7 @@
     */
    NOT_BOOLEAN_TYPE                ("not");
    /**
    /*
    * The bind rule boolean type name.
     */
    private final String booleanType;
opends/src/server/org/opends/server/authorization/dseecompat/EnumDayOfWeek.java
@@ -33,6 +33,7 @@
 * This class provides an enumeration of the allowed dayofweek types.
 */
public enum EnumDayOfWeek {
    /**
     * The enumeration type when the bind rule has specified dayofweek type of
     * "mon".
@@ -69,7 +70,7 @@
     */
    DAY_SUNDAY      ("sun");
    /**
    /*
    * The bind rule dayofweek type name.
     */
    private String day = null;
opends/src/server/org/opends/server/authorization/dseecompat/EnumEvalResult.java
@@ -32,6 +32,7 @@
 * the bind rule evaluation methods.
 */
public enum EnumEvalResult {
    /**
     * This enumeration is returned when the result of the evaluation is TRUE.
     */
opends/src/server/org/opends/server/authorization/dseecompat/EnumRight.java
@@ -26,6 +26,7 @@
 */
package org.opends.server.authorization.dseecompat;
import static org.opends.server.authorization.dseecompat.Aci.*;
/**
 * This class provides an enumeration of the allowed rights.
@@ -88,7 +89,7 @@
     */
    ADDWRITE    ("addwrite");
    /**
    /*
     * The name of the right.
     */
    private final String right;
@@ -132,40 +133,40 @@
     * @return The bit mask associated with the right.
     */
    public static int getMask(EnumRight right) {
        int mask=AciHandler.ACI_NULL;
        int mask=ACI_NULL;
        switch(right) {
            case READ:
                mask=AciHandler.ACI_READ;
                mask=ACI_READ;
                break;
            case WRITE:
                mask=AciHandler.ACI_WRITE;
                mask=ACI_WRITE;
                break;
            case ADD:
                mask=AciHandler.ACI_ADD;
                mask=ACI_ADD;
                break;
            case DELETE:
                mask=AciHandler.ACI_DELETE;
                mask=ACI_DELETE;
                break;
            case SEARCH:
                mask=AciHandler.ACI_SEARCH;
                mask=ACI_SEARCH;
                break;
            case COMPARE:
                mask=AciHandler.ACI_COMPARE;
                mask=ACI_COMPARE;
                break;
            case ALL:
                mask=AciHandler.ACI_ALL;
                mask=ACI_ALL;
                break;
            case  EXPORT:
                mask=AciHandler.ACI_EXPORT;
                mask=ACI_EXPORT;
                break;
            case IMPORT:
                mask=AciHandler.ACI_IMPORT;
                mask=ACI_IMPORT;
                break;
            case PROXY:
                mask=AciHandler.ACI_PROXY;
                mask=ACI_PROXY;
                break;
            case SELFWRITE:
                mask=AciHandler.ACI_SELF;
                mask=ACI_SELF;
                break;
        }
        return mask;
opends/src/server/org/opends/server/authorization/dseecompat/EnumTargetKeyword.java
@@ -31,6 +31,7 @@
 *  This class provides an enumeration of the valid ACI target keywords.
 */
public enum EnumTargetKeyword {
    /**
     * This enumeration is returned when the target keyword is
     * "target".
@@ -56,10 +57,8 @@
     * "targattrfilters".
     */
    KEYWORD_TARGATTRFILTERS ("targattrfilters");
    /*
     * TODO Add support for the targattrfilters keyword.
     */
    /**
     * The target keyword name.
     */
    private final String keyword;
opends/src/server/org/opends/server/authorization/dseecompat/EnumTargetOperator.java
@@ -31,6 +31,7 @@
 *  This class provides an enumeration of the valid ACI target operators.
 */
public enum EnumTargetOperator {
    /**
    * This enumeration is returned when the target operator is  "=".
     */
@@ -40,7 +41,7 @@
     */
    NOT_EQUALITY    ("!=");
    /**
    /*
     * The target operator name.
     */
    private final String operator;
opends/src/server/org/opends/server/authorization/dseecompat/EnumUserDNType.java
@@ -40,6 +40,7 @@
 * SELF and ANYONE.
 */
public enum EnumUserDNType {
        /**
         * The enumeration type when the "userdn" URL contains only a DN (no
         * filter or scope) and that DN has no pattern.
opends/src/server/org/opends/server/authorization/dseecompat/GroupDN.java
@@ -28,6 +28,7 @@
package org.opends.server.authorization.dseecompat;
import static org.opends.server.authorization.dseecompat.AciMessages.*;
import static org.opends.server.authorization.dseecompat.Aci.*;
import static org.opends.server.messages.MessageHandler.getMessage;
import org.opends.server.types.*;
import org.opends.server.api.Group;
@@ -46,10 +47,28 @@
 */
public class GroupDN implements KeywordBindRule {
    /*
     * List of group DNs.
     */
    LinkedList<DN> groupDNs=null;
    /*
     * Enumeration representing the groupdn operator type.
     */
    private EnumBindRuleType type=null;
    /*
     * Group manager needed for group API.
     */
    private static GroupManager groupManager =
            DirectoryServer.getGroupManager();
                                            DirectoryServer.getGroupManager();
    /**
     * Regular expression matching one or more LDAP URLs separated by
     * "||".
     */
    public static final String LDAP_URLS = LDAP_URL +
            ZERO_OR_MORE_WHITESPACE + "(" + LOGICAL_OR +
            ZERO_OR_MORE_WHITESPACE + LDAP_URL + ")*";
    /**
     * Create a class representing a groupdn bind rule keyword.
@@ -71,17 +90,14 @@
     */
    public static KeywordBindRule decode(String expr, EnumBindRuleType type)
    throws AciException  {
        String ldapURLRegex = "\\s*(ldap:///[^\\|]+)";
        String ldapURLSRegex =
            ldapURLRegex + "\\s*(\\|\\|\\s*" + ldapURLRegex + ")*";
        if (!Pattern.matches(ldapURLSRegex, expr)) {
        if (!Pattern.matches(LDAP_URLS, expr)) {
            int msgID = MSGID_ACI_SYNTAX_INVALID_GROUPDN_EXPRESSION;
            String message = getMessage(msgID, expr);
            throw new AciException(msgID, message);
        }
        LinkedList<DN>groupDNs=new LinkedList<DN>();
        int ldapURLPos = 1;
        Pattern ldapURLPattern = Pattern.compile(ldapURLRegex);
        Pattern ldapURLPattern = Pattern.compile(LDAP_URL);
        Matcher ldapURLMatcher = ldapURLPattern.matcher(expr);
        while (ldapURLMatcher.find()) {
            try {
opends/src/server/org/opends/server/authorization/dseecompat/IpCriteria.java
@@ -28,6 +28,7 @@
package org.opends.server.authorization.dseecompat;
import static org.opends.server.authorization.dseecompat.AciMessages.*;
import static org.opends.server.authorization.dseecompat.Aci.*;
import static org.opends.server.messages.MessageHandler.getMessage;
import java.net.Inet6Address;
import java.net.InetAddress;
@@ -39,8 +40,7 @@
/**
 * This class represents a ip bind rule keyword.
 */
public class IpCriteria implements KeywordBindRule
{
public class IpCriteria implements KeywordBindRule {
    private EnumBindRuleType type=null;
    // private token to express that any address is accepted
@@ -50,6 +50,13 @@
    private IpBitsNetworkCriteria[] ipBitsCriteria = null;
    private IpMaskNetworkCriteria[] ipMaskCriteria = null;
    private static final String valueRegex =
                                   "([^," + ZERO_OR_MORE_WHITESPACE + "]+)";
    private static final String valuesRegex =
            valueRegex + ZERO_OR_MORE_WHITESPACE + "(," +
            ZERO_OR_MORE_WHITESPACE + valueRegex + ")*";
    /*
     * TODO Verifiy IpCriteria constructor adheres to DS 5.2 ip keyword
     * syntax.
@@ -245,8 +252,6 @@
     */
    public static KeywordBindRule decode(String expr, EnumBindRuleType type)
    throws AciException  {
        String valueRegex = "([^,\\s]+)";
        String valuesRegex = valueRegex + "\\s*(,\\s*" + valueRegex + ")*";
        if (!Pattern.matches(valuesRegex, expr)) {
            int msgID = MSGID_ACI_SYNTAX_INVALID_IP_EXPRESSION;
            String message = getMessage(msgID, expr);
opends/src/server/org/opends/server/authorization/dseecompat/ParentInheritance.java
@@ -28,8 +28,12 @@
package org.opends.server.authorization.dseecompat;
import static org.opends.server.authorization.dseecompat.AciMessages.*;
import static org.opends.server.authorization.dseecompat.Aci.*;
import static org.opends.server.messages.MessageHandler.getMessage;
import java.util.StringTokenizer;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
import org.opends.server.core.DirectoryServer;
import org.opends.server.types.AttributeType;
@@ -38,14 +42,31 @@
 * to determine what parent inheritance checks to make.
 */
public class ParentInheritance {
    /*
     * The maximum number of parent inheritance levels supported.
     *
     */
    private static final int MAX_LEVELS=10;
    /*
     * Pattern to match for parent inheritance.
     */
    private String parentPat="parent[";
    /*
     * Array used to hold the level information. Each slot corresponds to a
     * level parsed from the rule.
     */
    private int[] levels=new int[MAX_LEVELS];
    /*
     * The number of levels parsed.
     */
    private int numLevels;
    /*
     * The attribute type parsed from the rule.
     */
    private AttributeType attributeType;
@@ -66,21 +87,29 @@
            //The "parent[" pattern is invalid for ROLEDN user attr keyword.
            if(pattern.startsWith(parentPat)) {
                int msgID =
                  MSGID_ACI_SYNTAX_INVALID_USERATTR_ROLEDN_INHERITANCE_PATTERN;
                   MSGID_ACI_SYNTAX_INVALID_USERATTR_ROLEDN_INHERITANCE_PATTERN;
                String message = getMessage(msgID, pattern);
                throw new AciException(msgID, message);
            }  else {
                pattern=pattern.trim();
                if((this.attributeType =
                        DirectoryServer.getAttributeType(pattern)) == null)
                    this.attributeType =
                            DirectoryServer.getDefaultAttributeType(pattern);
                numLevels=1;
                levels[0]=0;
                Pattern pattern1=Pattern.compile(ATTR_NAME);
                Matcher matcher=pattern1.matcher(pattern);
               //Check if valid attribute type name.
               if(!matcher.find() || matcher.groupCount() != 1) {
                int msgID =
                        MSGID_ACI_SYNTAX_INVALID_ATTRIBUTE_TYPE_NAME;
                String message = getMessage(msgID, pattern);
                throw new AciException(msgID, message);
               }
               if((this.attributeType =
                    DirectoryServer.getAttributeType(pattern)) == null)
                this.attributeType =
                        DirectoryServer.getDefaultAttributeType(pattern);
               numLevels=1;
              levels[0]=0;
            }
        } else
            parse(pattern);
    }
    } else parse(pattern);
}
    /**
     * Performs all parsing of the specified pattern string.
@@ -108,6 +137,15 @@
                String message = getMessage(msgID, pattern);
                throw new AciException(msgID, message);
            }
            Pattern pattern1=Pattern.compile(ATTR_NAME);
            Matcher matcher=pattern1.matcher(toks[1]);
            //Check if valid attribute type name.
            if(!matcher.find() || matcher.groupCount() != 1) {
                int msgID =
                    MSGID_ACI_SYNTAX_INVALID_ATTRIBUTE_TYPE_NAME;
                String message = getMessage(msgID, toks[1]);
                throw new AciException(msgID, message);
            }
            if((this.attributeType =
                DirectoryServer.getAttributeType(toks[1])) == null)
                this.attributeType =
opends/src/server/org/opends/server/authorization/dseecompat/PermBindRulePair.java
@@ -33,7 +33,14 @@
 */
public class PermBindRulePair {
    /*
     * The Bind Rule part.
     */
    private BindRule bindRule;
    /*
     * The permission part.
     */
    private Permission perm=null;
    /**
opends/src/server/org/opends/server/authorization/dseecompat/Permission.java
@@ -29,6 +29,7 @@
import static org.opends.server.messages.MessageHandler.getMessage;
import static org.opends.server.authorization.dseecompat.AciMessages.*;
import static org.opends.server.authorization.dseecompat.Aci.*;
import java.util.regex.Pattern;
/**
@@ -36,12 +37,30 @@
 * of an ACI look like deny(search, write).
 */
public class Permission {
    //the access type (allow,deny)
    /*
     *  The access type (allow,deny) corresponding to the ACI permission value.
     */
    private EnumAccessType accessType = null;
    /*
     * The rights (search, add, delete, ...) corresponding to the ACI rights
     * value.
     */
    private int rights;
    /*
     * Regular expression token representing the separator.
     */
    private static final String separatorToken = ",";
    private static final String rightsRegex =
        "\\s*(\\w+)\\s*(,\\s*(\\w+)\\s*)*";
    /*
     * Regular expression used to match the ACI rights string.
     */
    private static final String rightsRegex = ZERO_OR_MORE_WHITESPACE +
            WORD_GROUP + ZERO_OR_MORE_WHITESPACE +
            "(," + ZERO_OR_MORE_WHITESPACE + WORD_GROUP +
            ZERO_OR_MORE_WHITESPACE +  ")*";
    /**
     * Constructor creating a class representing a permission part of an bind
opends/src/server/org/opends/server/authorization/dseecompat/RoleDN.java
@@ -28,6 +28,7 @@
package org.opends.server.authorization.dseecompat;
import static org.opends.server.authorization.dseecompat.AciMessages.*;
import static org.opends.server.authorization.dseecompat.Aci.*;
import static org.opends.server.messages.MessageHandler.getMessage;
import org.opends.server.types.*;
import org.opends.server.api.Group;
@@ -47,10 +48,21 @@
 */
public class RoleDN  implements KeywordBindRule {
    /*
     * List of DNs parsed from the ACI bind rule.
     */
    LinkedList<DN> roleDNs=null;
    /*
     * The bind rule type of the RoleDN statement.
     */
    private EnumBindRuleType type=null;
    /*
     * Group manager needed by the class.
     */
    private static GroupManager groupManager =
            DirectoryServer.getGroupManager();
                           DirectoryServer.getGroupManager();
    /**
     * Constructor creating a class representing a roledn keyword of a bind
@@ -72,31 +84,26 @@
     * @throws AciException If the expression is invalid.
     */
    public static KeywordBindRule decode(String expr, EnumBindRuleType type)
    throws AciException {
        String ldapURLRegex = "\\s*(ldap:///[^\\|]+)";
        String ldapURLSRegex =
            ldapURLRegex + "\\s*(\\|\\|\\s*" + ldapURLRegex + ")*";
        if (!Pattern.matches(ldapURLSRegex, expr)) {
            throws AciException {
        if (!Pattern.matches(GroupDN.LDAP_URLS, expr)) {
            int msgID = MSGID_ACI_SYNTAX_INVALID_ROLEDN_EXPRESSION;
            String message = getMessage(msgID, expr);
            throw new AciException(msgID, message);
        }
        LinkedList<DN>roleDNs=new LinkedList<DN>();
        int ldapURLPos = 1;
        Pattern ldapURLPattern = Pattern.compile(ldapURLRegex);
        Pattern ldapURLPattern = Pattern.compile(LDAP_URL);
        Matcher ldapURLMatcher = ldapURLPattern.matcher(expr);
        while (ldapURLMatcher.find()) {
            String val = ldapURLMatcher.group(ldapURLPos);
            val = val.trim();
            DN dn;
            String value = ldapURLMatcher.group(ldapURLPos).trim();
            try {
                dn=DN.decode(val);
                DN dn=LDAPURL.decode(value, true).getBaseDN();
                roleDNs.add(dn);
            } catch (DirectoryException ex) {
                int msgID = MSGID_ACI_SYNTAX_INVALID_ROLEDN_URL;
                String message = getMessage(msgID, ex.getErrorMessage());
                throw new AciException(msgID, message);
            }
            roleDNs.add(dn);
        }
        return new RoleDN(type, roleDNs);
    }
opends/src/server/org/opends/server/authorization/dseecompat/TargAttrFilterList.java
New file
@@ -0,0 +1,221 @@
/*
 * 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.opends.server.types.AttributeType;
import org.opends.server.types.SearchFilter;
import org.opends.server.types.DirectoryException;
import org.opends.server.core.DirectoryServer;
import static org.opends.server.authorization.dseecompat.AciMessages.*;
import static org.opends.server.authorization.dseecompat.Aci.*;
import static org.opends.server.messages.MessageHandler.getMessage;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
import java.util.LinkedHashMap;
/**
 * The TargAttrFilterList class represents an targattrfilters list. A
 * targattrfilters list looks like:
 *
 *   "Op=attr1:F1 [(&& attr2:F2)*]
 */
public class TargAttrFilterList {
    /*
     * The mask coresponding to the operation of this list (add or del).
     */
    private int mask=0;
    /*
     * ListHashMap keyed by the attribute type and mapping to the corresponding
     * search filter. LinkedHashMap is used so everything is in order.
     */
    private LinkedHashMap<AttributeType, SearchFilter> attrFilterList;
    /*
     * Regular expression group count.
     */
    private static int expectedGroupCount=2;
    /*
     * Regular expression attribute group position.
     */
    private static int attributePos=1;
    /*
     * Regular expression filter group position.
     */
    private static int filterPos=2;
    /*
     * Regular expression used to match a filter list including the strange
     * "and" token used to join the multiple attribute type filter pairs.
     */
    private static final String filterListSeperator =
              ZERO_OR_MORE_WHITESPACE  + "&&" + ZERO_OR_MORE_WHITESPACE;
    /*
     * Regular expression used to match an attribute filter pair.
     */
    private static final String attributeFilter=
            ATTR_NAME + ZERO_OR_MORE_WHITESPACE + ":{1}" +
            ZERO_OR_MORE_WHITESPACE + "(\\({1}.*\\){1})";
    /**
     * Construct a class representing an targattrfilters filter list.
     * @param mask The mask representing the operation.
     * @param attrFilterList The list map containing the attribute type
     * filter mappings.
     */
    public TargAttrFilterList(int mask,
                    LinkedHashMap<AttributeType, SearchFilter> attrFilterList) {
        this.mask=mask;
        this.attrFilterList=attrFilterList;
    }
    /**
     * Decode an TargAttrFilterList from the specified expression string.
     * @param mask  The mask representing the operation.
     * @param expression The expression string to decode.
     * @return A TargAttrFilterList class representing the targattrfilters
     * filter list.
     * @throws AciException If the expression string contains errors.
     */
    public static TargAttrFilterList decode(int mask, String expression)
            throws AciException {
        LinkedHashMap<AttributeType, SearchFilter> attrFilterList =
                new LinkedHashMap<AttributeType, SearchFilter>();
        String[] subExpressions=expression.split(filterListSeperator, -1);
        //Iterate over each sub-expression, parse and add them to the list
        //if there are no errors.
        for(String subs : subExpressions) {
            Pattern pattern=Pattern.compile(attributeFilter);
            Matcher matcher=pattern.matcher(subs);
            //Match the attribute:filter pair part of the expression
            if(!matcher.find() || matcher.groupCount() != expectedGroupCount) {
                int msgID =
                    MSGID_ACI_SYNTAX_INVALID_TARGATTRFILTERS_FILTER_LIST_FORMAT;
                String message = getMessage(msgID, expression);
                throw new AciException(msgID, message);
            }
            String attributeName=matcher.group(attributePos).toLowerCase();
            //Strip off any options, so it will match the filter option
            //handling.
            int semicolon = attributeName.indexOf(';');
            if (semicolon != -1)
                attributeName=attributeName.substring(0, semicolon);
            String filterString=matcher.group(filterPos);
            AttributeType attributeType;
            if((attributeType =
                    DirectoryServer.getAttributeType(attributeName)) == null)
                attributeType =
                        DirectoryServer.getDefaultAttributeType(attributeName);
            SearchFilter filter;
            //Check if it is a valid filter and add it to the list map if ok.
            try {
               filter = SearchFilter.createFilterFromString(filterString);
               attrFilterList.put(attributeType, filter);
            } catch (DirectoryException ex) {
                String er=ex.getErrorMessage();
                int msgID =
                   MSGID_ACI_SYNTAX_INVALID_TARGATTRFILTERS_FILTER_LISTS_FILTER;
                String message = getMessage(msgID, filterString, er);
                throw new AciException(msgID, message);
            }
            //Verify the filter components. This check assures that each
            //attribute type in the filter matches the provided attribute
            //type.
            verifyFilterComponents(filter, attributeType);
        }
        return new TargAttrFilterList(mask, attrFilterList);
    }
    /**
     * Verify the filter component attribute types by assuring that each
     * attribute type in the filter matches the specified attribute type.
     * @param filter  The filter to verify.
     * @param type The attribute type to use in the verification.
     * @throws AciException  If the filter contains an attribute type not
     * specified.
     */
    private static void  verifyFilterComponents(SearchFilter filter,
                                                AttributeType type)
            throws AciException {
        switch (filter.getFilterType()) {
            case AND:
            case OR: {
                for (SearchFilter f : filter.getFilterComponents()) {
                    verifyFilterComponents(f, type);
                }
                break;
            }
            case NOT:  {
                SearchFilter f = filter.getNotComponent();
                verifyFilterComponents(f, type);
                break;
            }
            default: {
                AttributeType attrType=filter.getAttributeType();
                if(!attrType.equals(type)) {
                    int msgID =
              MSGID_ACI_SYNTAX_INVALID_TARGATTRFILTERS_FILTER_LISTS_ATTR_FILTER;
                    String message = getMessage(msgID, filter.toString());
                    throw new AciException(msgID, message);
                }
            }
        }
    }
    /**
     * Return the mask of this TargAttrFilterList.
     * @return  The mask value.
     */
    public int getMask() {
        return this.mask;
    }
    /**
     * Check if the mask value of this TargAttrFilterList class contains the
     * specified mask value.
     * @param mask The mask to check for.
     * @return  True if the mask matches the specified value.
     */
    public boolean hasMask(int mask) {
        return (this.mask & mask) != 0;
    }
    /**
     * Return the list map holding the attribute type to filter mappings.
     * @return  The list map.
     */
    public
    LinkedHashMap<AttributeType, SearchFilter> getAttributeTypeFilterList() {
        return  attrFilterList;
    }
}
opends/src/server/org/opends/server/authorization/dseecompat/TargAttrFilters.java
@@ -27,29 +27,355 @@
package org.opends.server.authorization.dseecompat;
import static org.opends.server.authorization.dseecompat.AciMessages.*;
import static org.opends.server.authorization.dseecompat.Aci.*;
import static org.opends.server.messages.MessageHandler.getMessage;
import org.opends.server.types.*;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
import java.util.*;
/**
 * Placeholder. This class is partially complete. The TargAttrFilters class
 * will represent an targAttrFitlers rule.
 * The TargAttrFilters class represents a targattrfilters rule of an ACI.
 */
public class TargAttrFilters {
    /**
     * Represents an targAttrFilters rule.
    /*
     * A valid targattrfilters rule may have two TargFilterlist parts -- the
     * first one is required.
     */
    public TargAttrFilters() {
    TargAttrFilterList firstFilterList=null;
    TargAttrFilterList secondFilterList=null;
    /*
     * Regular expression group position for the first operation value.
     */
    private static final int firstOpPos = 1;
    /*
     * Regular expression group position for the rest of an partially parsed
     * rule.
     */
    private static final int restOfExpressionPos=2;
    /*
     * Regular expression used to match the operation group (either add or del).
     */
    private static final String ADD_OR_DEL_KEYWORD_GROUP = "(add|del)";
    /**
     * Regular expression used to match the second operation of the filter list.
     * If the first was "add" this must be "del", if the first was "del" this
     * must be "add".
     */
    public static final String secondOp =
            "[,]{1}" + ZERO_OR_MORE_WHITESPACE + "del|add" +
            ZERO_OR_MORE_WHITESPACE + EQUAL_SIGN + ZERO_OR_MORE_WHITESPACE;
    /*
     * Regular expression used to match the first targFilterList, it must exist
     * or an exception is thrown.
     */
    private static final String firstOp = "^" + ADD_OR_DEL_KEYWORD_GROUP +
            ZERO_OR_MORE_WHITESPACE + EQUAL_SIGN + ZERO_OR_MORE_WHITESPACE;
    /*
     * Regular expression used to group the remainder of a partially parsed
     * rule.  Any character one or more times.
     */
    private static String restOfExpression = "(.+)";
    /*
     * Regular expression used to match the first operation keyword and the
     * rest of the expression.
     */
    private static String keywordFullPattern = firstOp + restOfExpression;
    /*
     * The enumeration representing the operation.
     */
    EnumTargetOperator op;
    /*
     * A mask used to denote if the rule has add, del or both operations in the
     * composite TargFilterList parts.
     */
    private int operationMask;
    /**
     * Represents an targatterfilters keyword rule.
     * @param op The enumeration representing the operation type.
     *
     * @param firstFilterList  The first filter list class parsed from the rule.
     * This one is required.
     *
     * @param secondFilterList The second filter list class parsed from the
     * rule. This one is optional.
     */
    public TargAttrFilters(EnumTargetOperator op,
                           TargAttrFilterList firstFilterList,
                           TargAttrFilterList secondFilterList ) {
        this.op=op;
        this.firstFilterList=firstFilterList;
        operationMask=firstFilterList.getMask();
        if(secondFilterList != null) {
            //Add the second filter list mask to the mask.
            operationMask |= secondFilterList.getMask();
            this.secondFilterList=secondFilterList;
        }
    }
    /**
     * Decode an string representing a targattrfilters keyword.
     * @param operator The operator of the rule.
     * @param expression The string parsed from the ACI representing the
     * targattrfilters rule.
     * @return  An object representing an targattrfilters rule.
     * @throws  AciException if the expression string cannot be parsed.
     * Decode an targattrfilter rule.
     * @param type The enumeration representing the type of this rule. Defaults
     * to equality for this target.
     *
     * @param expression The string expression to be decoded.
     * @return  A TargAttrFilters class representing the decode expression.
     * @throws AciException If the expression string contains errors and
     * cannot be decoded.
     */
    public static TargAttrFilters decode(EnumTargetOperator operator,
                                  String expression) throws AciException {
        return new TargAttrFilters();
    public static TargAttrFilters decode(EnumTargetOperator type,
                                        String expression) throws AciException {
        Pattern fullPattern=Pattern.compile(keywordFullPattern);
        Matcher matcher = fullPattern.matcher(expression);
        //First match for overall correctness and to get the first operation.
        if(!matcher.find()) {
            int msgID = MSGID_ACI_SYNTAX_INVALID_TARGATTRFILTERS_EXPRESSION;
            String message = getMessage(msgID, expression);
            throw new AciException(msgID, message);
        }
        String firstOp=matcher.group(firstOpPos);
        String subExpression=matcher.group(restOfExpressionPos);
        //This pattern is built dynamically and is used to see if the operations
        //in the two filter list parts (if the second exists) are equal. See
        //comment below.
        String opPattern=
                "[,]{1}" + ZERO_OR_MORE_WHITESPACE  +
                firstOp + ZERO_OR_MORE_WHITESPACE + EQUAL_SIGN +
                ZERO_OR_MORE_WHITESPACE;
        String[] temp=subExpression.split(opPattern);
        /**
         * Check that the initial list operation is not equal to the second.
         * For example:  Matcher find
         *
         *  "add:cn:(cn=foo), add:cn:(cn=bar)"
         *
         * This is invalid.
         */
        if(temp.length > 1) {
            int msgID =
                    MSGID_ACI_SYNTAX_INVALID_TARGATTRFILTERS_OPS_MATCH;
            String message = getMessage(msgID, expression);
            throw new AciException(msgID, message);
        }
        /**
         * Check that there are not too many filter lists. There can only
         * be either one or two.
         */
        String[] filterLists=
                subExpression.split(secondOp, -1);
        if(filterLists.length > 2) {
            int msgID =
                    MSGID_ACI_SYNTAX_INVALID_TARGATTRFILTERS_MAX_FILTER_LISTS;
            String message = getMessage(msgID, expression);
            throw new AciException(msgID, message);
        }
        TargAttrFilterList firstFilterList =
                TargAttrFilterList.decode(getMask(firstOp), filterLists[0]);
        TargAttrFilterList secondFilterList=null;
        //Handle the second filter list if there is one.
        if(filterLists.length == 2) {
            String temp2= filterLists[1].substring(1,filterLists[1].length());
            //Assume the first op is an "add" so this has to be a "del".
            String secondOp="del";
            //If the first op is a "del", the second has to be an "add".
            if(getMask(firstOp) == TARGATTRFILTERS_DELETE)
                secondOp="add";
            secondFilterList =
                    TargAttrFilterList.decode(getMask(secondOp), temp2);
        }
        return new TargAttrFilters(type, firstFilterList, secondFilterList);
    }
    /**
     * Return the mask corrsponding to the specified string.
     * @param op The op string.
     * @return   The mask corresponding to the operation string.
     */
    private static  int getMask(String op) {
        if(op.equals("add"))
            return TARGATTRFILTERS_ADD;
        else
            return TARGATTRFILTERS_DELETE;
    }
    /**
     * Gets the TargFilterList  corresponding to the mask value.
     * @param matchCtx The target match context containing the rights to
     * match against.
     * @return  A TargAttrFilterList matching both the rights of the target
     * match context and the mask of the TargFilterAttrList. May return null.
     */
    public TargAttrFilterList
    getTargAttrFilterList(AciTargetMatchContext matchCtx) {
        TargAttrFilterList filterList=null;
        int mask=ACI_NULL;
        //Set up the wanted mask by evaluating both the target match
        //context's rights and the mask.
        if(matchCtx.hasRights(ACI_ADD) &&
                hasMask(TARGATTRFILTERS_ADD))
            mask=TARGATTRFILTERS_ADD;
        else if(matchCtx.hasRights(ACI_DELETE) &&
                hasMask(TARGATTRFILTERS_DELETE))
            mask=TARGATTRFILTERS_DELETE;
        //Check the first list first, it always has to be there. If it doesn't
        //match then check the second if it exists.
        if(firstFilterList.hasMask(mask))
            filterList=firstFilterList;
        else if((secondFilterList != null) &&
                secondFilterList.hasMask(mask))
            filterList=secondFilterList;
        return filterList;
    }
    /**
     * Check if this TargAttrFilters object is applicable to the target
     * specified match context. This check is only used for the LDAP modify
     * operation.
     * @param matchCtx The target match context containing the information
     * needed to match.
     * @return True if this TargAttrFitlers object is applicable to this
     * target match context.
     */
    public boolean isApplicableMod(AciTargetMatchContext matchCtx) {
        //Get the targFitlerList corresponding to this context's rights.
        TargAttrFilterList attrFilterList=getTargAttrFilterList(matchCtx);
        //If the list is empty return true and go on to the targattr check
        //in AciTargets.isApplicable().
        if(attrFilterList == null)
            return true;
        LinkedHashMap<AttributeType, SearchFilter> filterList  =
                attrFilterList.getAttributeTypeFilterList();
        boolean attrMatched=true;
        AttributeType attrType=matchCtx.getCurrentAttributeType();
        //If the filter list contains the current attribute type; check
        //the attribute types value(s) against the corresponding filter.
        // If the filter list does not contain the attribute type skip the
        // attribute type.
        if((attrType != null) && (filterList.containsKey(attrType))) {
            AttributeValue value=matchCtx.getCurrentAttributeValue();
            SearchFilter filter = filterList.get(attrType);
            attrMatched=matchFilterAttributeValue(attrType, value, filter);
            //This flag causes any targattr checks to be bypassed in AciTargets.
            if(attrMatched)
                matchCtx.setTargAttrFiltersMatch(true);
            if(op.equals(EnumTargetOperator.NOT_EQUALITY))
                attrMatched = !attrMatched;
        }
        return attrMatched;
    }
    /**
     * Check if this TargAttrFilters object is applicable to the specified
     * target match context. This check is only used for either LDAP add or
     * delete operations.
     * @param matchCtx The target match context containing the information
     * needed to match.
     * @return True if this TargAttrFilters object is applicable to this
     * target match context.
     */
    public boolean isApplicableAddDel(AciTargetMatchContext matchCtx) {
        TargAttrFilterList attrFilterList=getTargAttrFilterList(matchCtx);
        //List didn't match current operation return true.
        if(attrFilterList == null)
            return true;
        LinkedHashMap<AttributeType, SearchFilter> filterList  =
                attrFilterList.getAttributeTypeFilterList();
        Iterator<AttributeType> iterator=filterList.keySet().iterator();
        boolean attrMatched=true;
        //Get the resource entry.
        Entry resEntry=matchCtx.getResourceEntry();
        //Iterate through each attribute type in the filter list checking
        //the resource entry to see if it has that attribute type. If not
        //go to the next attribute type. If it is found, then check the entries
        //attribute type values against the filter.
        for(;iterator.hasNext() && attrMatched;) {
            AttributeType attrType=iterator.next();
            SearchFilter f=filterList.get(attrType);
            //Found a match in the entry, iterate over each attribute
            //type in the entry and check its values agaist the filter.
            if(resEntry.hasAttribute(attrType)) {
                ListIterator<Attribute> attrIterator=
                        resEntry.getAttribute(attrType).listIterator();
                for(;attrIterator.hasNext() && attrMatched;) {
                    Attribute a=attrIterator.next();
                    attrMatched=matchFilterAttributeValues(a, attrType, f);
                }
            }
        }
        if(op.equals(EnumTargetOperator.NOT_EQUALITY))
            attrMatched = !attrMatched;
        return attrMatched;
    }
    /**
     * Iterate over each attribute type attribute and compare the values
     * against the provided filter.
     * @param a The attribute from the resource entry.
     * @param attrType The attribute type currently working on.
     * @param filter  The filter to evaluate the values against.
     * @return  True if all of the values matched the filter.
     */
    private boolean matchFilterAttributeValues(Attribute a,
                                               AttributeType attrType,
                                               SearchFilter filter) {
        boolean filterMatch=true;
        Iterator<AttributeValue> valIterator = a.getValues().iterator();
        //Iterate through each value and apply the filter against it.
        for(; valIterator.hasNext() && filterMatch;) {
            AttributeValue value=valIterator.next();
            filterMatch=matchFilterAttributeValue(attrType, value, filter);
        }
        return filterMatch;
    }
    /**
     * Matches an specified attribute value against a specified filter. A dummy
     * entry is created with only a single attribute containing the value  The
     * filter is applied against that entry.
     *
     * @param attrType The attribute type currently being evaluated.
     * @param value  The value to match the filter against.
     * @param filter  The filter to match.
     * @return  True if the value matches the filter.
     */
    private boolean matchFilterAttributeValue(AttributeType attrType,
                                              AttributeValue value,
                                              SearchFilter filter) {
        boolean filterMatch;
        LinkedHashSet<AttributeValue> values =
                new LinkedHashSet<AttributeValue>();
        values.add(new AttributeValue(attrType, value.getValue()));
        Attribute attr =
                new Attribute(attrType, attrType.toString(), values);
        Entry e = new Entry(DN.nullDN(), null, null, null);
        e.addAttribute(attr, new ArrayList<AttributeValue>());
        try {
            filterMatch=filter.matchesEntry(e);
        } catch(DirectoryException ex) {
            filterMatch=false;
        }
        return filterMatch;
    }
    /**
     * Return true if the TargAttrFilters mask contains the specified mask.
     * @param mask  The mask to check for.
     * @return  True if the mask matches.
     */
    public boolean hasMask(int mask) {
        return (this.operationMask & mask) != 0;
    }
}
opends/src/server/org/opends/server/authorization/dseecompat/Target.java
@@ -28,6 +28,7 @@
package org.opends.server.authorization.dseecompat;
import static org.opends.server.authorization.dseecompat.AciMessages.*;
import static org.opends.server.authorization.dseecompat.Aci.*;
import static org.opends.server.messages.MessageHandler.getMessage;
import java.util.ArrayList;
import java.util.LinkedHashSet;
@@ -47,13 +48,37 @@
 */
public class Target
{
    /*
     * Enumeration representing the target operator.
     */
    private EnumTargetOperator operator = EnumTargetOperator.EQUALITY;
    private LDAPURL targetURL = null;
    private DN urlDN=null;
    private boolean isPattern=false;
    private SearchFilter filter=null;
    private AttributeType targetType;
    /*
     * The target URL.
     */
    private LDAPURL targetURL = null;
    /*
     * The DN of the above URL.
     */
    private DN urlDN=null;
    /*
     * True of a wild-card pattern was seen.
     */
    private boolean isPattern=false;
    /*
     * Filter used to apply to an dummy entry when a wild-card pattern is
     * used.
     */
    private SearchFilter filter=null;
    /*
     * Attribute type that is used to create the pattern attribute.
     *  See matchesPattern method.
     */
    private AttributeType targetType;
      /*
     * TODO Save aciDN parameter and use it in matchesPattern re-write.
@@ -89,8 +114,7 @@
            throws AciException {
        this.operator = operator;
        try {
          String ldapURLRegex = "\\s*(ldap:///[^\\|]+)";
          if (!Pattern.matches(ldapURLRegex, target)) {
          if (!Pattern.matches(LDAP_URL, target)) {
              int msgID = MSGID_ACI_SYNTAX_INVALID_TARGETKEYWORD_EXPRESSION;
              String message = getMessage(msgID, target);
              throw new AciException(msgID, message);
@@ -163,7 +187,8 @@
    }
    /*
     * TODO Evaluate re-writing this method.
     * TODO Evaluate re-writing this method. Evaluate if we want to dis-allow
     * wild-card matching in the suffix part of the dn.
     *
     * The matchesPattern() method really needs to be rewritten.  It's using a
     * very inefficient and very error-prone method to make the determination.
opends/src/server/org/opends/server/authorization/dseecompat/TargetAttr.java
@@ -28,6 +28,7 @@
package org.opends.server.authorization.dseecompat;
import static org.opends.server.authorization.dseecompat.AciMessages.*;
import static org.opends.server.authorization.dseecompat.Aci.*;
import static org.opends.server.messages.MessageHandler.getMessage;
import java.util.HashSet;
import java.util.regex.Pattern;
@@ -38,18 +39,29 @@
 * A class representing an ACI's targetattr keyword.
 */
public class TargetAttr {
    /*
     * Enumeration representing the targetattr operator.
     */
    private EnumTargetOperator operator = EnumTargetOperator.EQUALITY;
    /*
     * Flags that is set if all attributes pattern seen "*".
     */
    private boolean allAttributes = false ;
    /*
     * HashSet of the attribute types parsed by the constructor.
     */
    private HashSet<AttributeType> attributes = new HashSet<AttributeType>();
    //private String[] attributes = new String[0];
    private static final String allAttrsRegex  = "\\s*\\*\\s*";
    private static final String noAttrsRegex   = "\\s*";
    private static final String separatorToken = "\\|\\|";
    private static final String attrListRegex  =
        "\\s*(\\w+)\\s*(" + separatorToken + "\\s*(\\w+)\\s*)*";
    /*
     * Regular expression that matches one or more ATTR_NAME's separated by
     * the "||" token.
     */
    private static final String attrListRegex  =  ZERO_OR_MORE_WHITESPACE +
           ATTR_NAME + ZERO_OR_MORE_WHITESPACE + "(" +
            LOGICAL_OR + ZERO_OR_MORE_WHITESPACE + ATTR_NAME +
            ZERO_OR_MORE_WHITESPACE + ")*";
    /**
     * Constructor creating a class representing a targetattr keyword of an ACI.
@@ -63,18 +75,19 @@
    throws AciException {
        this.operator = operator;
        if (attrString != null) {
            if (Pattern.matches(allAttrsRegex, attrString) ){
            if (Pattern.matches(ALL_ATTRS_WILD_CARD, attrString) ){
                allAttributes = true ;
            } else {
                if (Pattern.matches(noAttrsRegex, attrString)){
                if (Pattern.matches(ZERO_OR_MORE_WHITESPACE, attrString)){
                    allAttributes = false;
                } else {
                    if (Pattern.matches(attrListRegex, attrString)) {
                        // Remove the spaces in the attr string and
                        // split the list.
                        Pattern separatorPattern =
                            Pattern.compile(separatorToken);
                        attrString=attrString.replaceAll("\\s", "");
                            Pattern.compile(LOGICAL_OR);
                        attrString=
                         attrString.replaceAll(ZERO_OR_MORE_WHITESPACE, "");
                        String[] attributeArray=
                             separatorPattern.split(attrString);
                        //Add each element of array to attributes HashSet
@@ -83,7 +96,7 @@
                    } else {
                      int msgID =
                         MSGID_ACI_SYNTAX_INVALID_TARGETATTRKEYWORD_EXPRESSION;
                      String message = getMessage(msgID, operator);
                      String message = getMessage(msgID, attrString);
                      throw new AciException(msgID, message);
                    }
                }
@@ -101,12 +114,13 @@
            String attribute=attributeArray[i].toLowerCase();
            AttributeType attributeType;
            if((attributeType =
                DirectoryServer.getAttributeType(attribute)) == null)
                    DirectoryServer.getAttributeType(attribute)) == null)
                attributeType =
                    DirectoryServer.getDefaultAttributeType(attribute);
                        DirectoryServer.getDefaultAttributeType(attribute);
            attributes.add(attributeType);
        }
    }
    /**
     * Returns the operator enumeration of the targetattr expression.
     * @return The operator enumeration.
@@ -172,8 +186,14 @@
                          TargetAttr targetAttr) {
      boolean ret;
      if(targetAttr.isAllAttributes()) {
          ret =
             !targetAttr.getOperator().equals(EnumTargetOperator.NOT_EQUALITY);
          //If it is an operational attribute, then access is denied for all
          //attributes wild-card. Operational attributes must be
          // explicitly defined.
          if(a.isOperational()) {
              ret=false;
          } else
              ret =
              !targetAttr.getOperator().equals(EnumTargetOperator.NOT_EQUALITY);
      }  else {
          ret=false;
          HashSet<AttributeType> attributes=targetAttr.getAttributes();
opends/src/server/org/opends/server/authorization/dseecompat/TargetFilter.java
@@ -38,10 +38,18 @@
 *
 */
public class TargetFilter {
    /*
     * Enumeration representing the targetfilter operation.
     */
    private EnumTargetOperator op = EnumTargetOperator.EQUALITY;
    /*
     * Filter parsed from the ACI used to match the resource entry.
     */
    private SearchFilter filter;
    /**
    /*
     * Class representing a targetfilter keyword.
     * @param op The operation of the targetfilter expression (=, !=)
     * @param filter The filter itself.
opends/src/server/org/opends/server/authorization/dseecompat/TimeOfDay.java
@@ -36,8 +36,20 @@
 * This class represents the timeofday keyword in a bind rule.
 */
public class TimeOfDay implements KeywordBindRule {
    /*
     * Regular expression matching a valid timeofday rule value (0-2359).
     */
    private static final String timeofdayRegex = "[0-2]\\d[0-5]\\d";
    /*
     *  Enumeration representing the bind rule operation type.
     */
    private EnumBindRuleType type=null;
    /*
     * Holds the time value parsed from the ACI.
     */
    private int timeRef;
    /**
opends/src/server/org/opends/server/authorization/dseecompat/UserAttr.java
@@ -48,20 +48,58 @@
 * This class implements the  userattr bind rule keyword.
 */
public class UserAttr implements KeywordBindRule {
    /**
     * This enumeration is the various types the userattr can have after
     * the "#" token.
     */
    enum UserAttrType {
    private enum UserAttrType {
        USERDN, GROUPDN, ROLEDN, URL, VALUE
    }
    private  static String f="objectclass=*";
    /*
     * Filter used in  internal search.
     */
    private static SearchFilter filter;
    /*
     * Used to create an attribute type that can compare the value below in
     * an entry returned from an internal search.
     */
    private  String attrStr=null;
    /*
     * Used to compare a attribute value returned from a search against this
     * value which might have been defined in the ACI userattr rule.
     */
    private  String attrVal=null;
    /*
     * Contains the type of the userattr, one of the above enumerations.
     */
    private UserAttrType userAttrType=null;
    /*
     * An enumeration representing the bind rule type.
     */
    private EnumBindRuleType type=null;
    /*
     * The class used to hold the parent inheritance information.
     */
    private ParentInheritance parentInheritance=null;
    static {
        /*
         * Set up the filter used to search private and public contexts.
         */
        try {
            filter=SearchFilter.createFilterFromString("(objectclass=*)");
        } catch (DirectoryException ex) {
            //TODO should never happen, error message?
        }
    }
    /**
     * Create an non-USERDN/GROUPDN instance of the userattr keyword class.
     * @param attrStr The attribute name in string form. Kept in string form
@@ -174,27 +212,22 @@
        AttributeType attrType;
        if((attrType = DirectoryServer.getAttributeType(attrStr)) == null)
            attrType = DirectoryServer.getDefaultAttributeType(attrStr);
        try {
            InternalClientConnection conn =
        InternalClientConnection conn =
                InternalClientConnection.getRootConnection();
            InternalSearchOperation op =
                    conn.processSearch(evalCtx.getClientDN(),
                    SearchScope.BASE_OBJECT,
                    DereferencePolicy.NEVER_DEREF_ALIASES, 0, 0, false,
                    SearchFilter.createFilterFromString(f), null);
            LinkedList<SearchResultEntry> result = op.getSearchEntries();
            if (!result.isEmpty()) {
                AttributeValue val=new AttributeValue(attrType, attrVal);
                SearchResultEntry resultEntry = result.getFirst();
                if(resultEntry.hasValue(attrType, null, val)) {
                    Entry e=evalCtx.getResourceEntry();
                    if(e.hasValue(attrType, null, val))
                        matched=EnumEvalResult.TRUE;
                }
        InternalSearchOperation op =
                conn.processSearch(evalCtx.getClientDN(),
                        SearchScope.BASE_OBJECT,
                        DereferencePolicy.NEVER_DEREF_ALIASES, 0, 0, false,
                        filter, null);
        LinkedList<SearchResultEntry> result = op.getSearchEntries();
        if (!result.isEmpty()) {
            AttributeValue val=new AttributeValue(attrType, attrVal);
            SearchResultEntry resultEntry = result.getFirst();
            if(resultEntry.hasValue(attrType, null, val)) {
                Entry e=evalCtx.getResourceEntry();
                if(e.hasValue(attrType, null, val))
                    matched=EnumEvalResult.TRUE;
            }
        } catch (DirectoryException ex) {
            undefined = true;
            matched = EnumEvalResult.ERR;
        }
        return matched.getRet(type, undefined);
    }
@@ -305,31 +338,25 @@
                        stop=true;
                }
            } else {
                try {
                    DN pDN=
                DN pDN=
                        getDNParentLevel(levels[i], evalCtx.getResourceDN());
                    if(pDN == null)
                        continue;
                    InternalClientConnection conn =
                if(pDN == null)
                    continue;
                InternalClientConnection conn =
                        InternalClientConnection.getRootConnection();
                    InternalSearchOperation op = conn.processSearch(pDN,
                            SearchScope.BASE_OBJECT,
                            DereferencePolicy.NEVER_DEREF_ALIASES, 0, 0, false,
                            SearchFilter.createFilterFromString(f), null);
                    LinkedList<SearchResultEntry> result =
                InternalSearchOperation op = conn.processSearch(pDN,
                        SearchScope.BASE_OBJECT,
                        DereferencePolicy.NEVER_DEREF_ALIASES, 0, 0, false,
                        filter, null);
                LinkedList<SearchResultEntry> result =
                        op.getSearchEntries();
                    if (!result.isEmpty()) {
                        Entry e = result.getFirst();
                        if (e.hasAttribute(attrType)) {
                            matched = evalEntryAttr(e, evalCtx, attrType);
                           if(matched.equals(EnumEvalResult.TRUE))
                                stop=true;
                        }
                if (!result.isEmpty()) {
                    Entry e = result.getFirst();
                    if (e.hasAttribute(attrType)) {
                        matched = evalEntryAttr(e, evalCtx, attrType);
                        if(matched.equals(EnumEvalResult.TRUE))
                            stop=true;
                    }
                } catch (DirectoryException ex) {
                    undefined=true;
                    stop=true;
                    matched=EnumEvalResult.ERR;
                }
            }
        }
opends/src/server/org/opends/server/authorization/dseecompat/UserDN.java
@@ -43,13 +43,22 @@
     */
    private static String urlStr="ldap:///";
    /**
    /*
     * This list holds a list of objects representing a EnumUserDNType
     * URL mapping.
     */
    List<UserDNTypeURL> urlList=null;
    private List<UserDNTypeURL> urlList=null;
    /*
     * Enumeration of the userdn operation type.
     */
    private EnumBindRuleType type=null;
    /*
     * Used to evaluate a userdn that has a pattern  (wild-card).
     */
    private AttributeType userDNAttrType;
    /**
     * Constructor that creates the userdn class. It also sets up an attribute
     * type ("userdn") needed  for wild-card matching.
opends/src/server/org/opends/server/authorization/dseecompat/UserDNTypeURL.java
@@ -33,10 +33,12 @@
 * of a "userdn" URL decoded by the UserDN.decode() method.
 */
public class UserDNTypeURL {
    /**
    /*
     * The DN type of the URL.
     */
    private EnumUserDNType dnType;
    /*
     * The URL value. Maybe a dummy value for types such as ANYONE or SELF.
     */
opends/tests/unit-tests-testng/src/server/org/opends/server/authorization/dseecompat/AciTests.java
@@ -227,8 +227,24 @@
  private static final String BIND_RULE_GROUPDN_1 = "groupdn=\"ldap:///cn=SomeGroup,dc=example,dc=com\"";
  private static final String BIND_RULE_GROUPDN_2 = "groupdn=\"ldap:///cn=SomeGroup,dc=example,dc=com || ldap:///cn=SomeOtherGroup,dc=example,dc=com\"";
  private static final String BIND_RULE_GROUPDN_3 = "groupdn=\"ldap:///cn=SomeGroup,dc=example,dc=com || ldap:///cn=SomeOtherGroup,dc=example,dc=com || ldap:///cn=SomeThirdGroup,dc=example,dc=com\"";
  private static final String BIND_RULE_ROLEDN_1 = "roledn=\"ldap:///cn=SomeGroup,dc=example,dc=com\"";
  private static final String BIND_RULE_ROLEDN_2 =  "roledn=\"ldap:///cn=SomeGroup,dc=example,dc=com || ldap:///cn=SomeOtherGroup,dc=example,dc=com\"";
  private static final String BIND_RULE_ROLEDN_3 =  "roledn=\"ldap:///cn=SomeGroup,dc=example,dc=com || ldap:///cn=SomeOtherGroup,dc=example,dc=com || ldap:///cn=SomeThirdGroup,dc=example,dc=com\"";
  private static final String BIND_RULE_USERDN_FILTER = "userdn=\"ldap:///dc=example,dc=com??one?(|(ou=eng)(ou=acct))\"";
  //bind rule user attr ACIs
  private static final String BIND_RULE_USERATTR_USERDN = "userattr=\"manager#USERDN\"";
  private static final String BIND_RULE_USERATTR_USERDN_1 = "userattr=\"ldap:///dc=example,dc=com?owner#USERDN\"";
  private static final String BIND_RULE_USERATTR_URL = "userattr=\"cn#LDAPURL\"";
  private static final String BIND_RULE_USERATTR_GROUPDN = "userattr=\"manager#GROUPDN\"";
  private static final String BIND_RULE_USERATTR_GROUPDN_1 = "userattr=\"ldap:///dc=example,dc=com?owner#GROUPDN\"";
  private static final String BIND_RULE_USERATTR_ROLEDN = "userattr=\"manager#ROLEDN\"";
  private static final String BIND_RULE_USERATTR_ROLEDN_1 = "userattr=\"ldap:///dc=example,dc=com?owner#ROLEDN\"";
  private static final String BIND_RULE_USERATTR_USERDN_INHERITANCE = "userattr=\"parent[0,1,2].cn#USERDN\"";
  private static final String BIND_RULE_USERATTR_GROUPDN_INHERITANCE = "userattr=\"parent[0,1,2].cn#GROUPDN\"";
  private static final String BIND_RULE_USERATTR_VALUE = "userattr=\"manager#a manager\"";
  private static final String BIND_RULE_INVALID_DAY = "dayofweek=\"sumday\"";
  private static final String BIND_RULE_ONLY_AT_NOON = "timeofday=\"1200\"";
@@ -237,6 +253,21 @@
  private static final String BIND_RULE_NOON_AND_AFTER = "timeofday>=\"1200\"";
  private static final String BIND_RULE_BEFORE_NOON = "timeofday<\"1200\"";
  private static final String BIND_RULE_NOON_AND_BEFORE = "timeofday<=\"1200\"";
  //targattrfilters
  private static final String TARG_ATTR_FILTERS =  "add=cn:(!(cn=superAdmin))";
  private static final String TARG_ATTR_FILTERS_1 =  "add=cn:(!(cn=superAdmin)) && telephoneNumber:(telephoneNumber=123*)";
  private static final String TARG_ATTR_FILTERS_2 =  "add=cn:(!(cn=superAdmin)), del=sn:(!(sn=nonSuperAdmin))";
  private static final String TARG_ATTR_FILTERS_4 =  "del=cn:(&(cn=foo)(cn=f*)) && sn:(sn=joe*)";
  private static final String TARG_ATTR_FILTERS_5 = TARG_ATTR_FILTERS_1 + "," + TARG_ATTR_FILTERS_4 ;
  //targattrfilters invalids
  private static final String TARG_ATTR_FILTERS_INVALID_FILTER =  "del=cn:(&(cnfoo)(cn=f*)) && sn:(snjoe*)";
  private static final String TARG_ATTR_FILTERS_BAD_OP =  "delete=cn:(&(cn=foo)(cn=f*)) && sn:(sn=joe*)";
  private static final String TARG_ATTR_FILTERS_BAD_OP_MATCH = TARG_ATTR_FILTERS_1 + "," + TARG_ATTR_FILTERS_1 ;
  private static final String TARG_ATTR_FILTERS_BAD_FILTER_ATTR =  "del=cn:(&(cn=foo)(cn=f*)) && sn:(cn=joe*)";
  private static final String TARG_ATTR_FILTERS_BAD_FORMAT =  "delete=cn;(&(cn=foo)(cn=f*)) && sn:(sn=joe*)";
  private static final String TARG_ATTR_FILTERS_TOO_MANY_LISTS = TARG_ATTR_FILTERS_1 + "," + TARG_ATTR_FILTERS_4 + "," + TARG_ATTR_FILTERS_1;
  private static final String TARG_ATTR_FILTERS_BAD_TOK =  "delete=cn:(&(cn=foo)(cn=f*)) && sn:(sn=joe*) || pager:(pager=123-*)";
  private static final String TARG_ATTR_FILTERS_ATTR_TYPE_NAME =  "del=cn:(&(cn=foo)(cn=f*)) && 1sn_;:(1sn_;=joe*)";
  private static final String SELF_MODIFY_ACI = "aci: (targetattr=\"*\")(version 3.0; acl \"self modify\";allow(all) userdn=\"userdn=\"ldap:///self\";)";
@@ -250,7 +281,7 @@
          buildAciValue("name", "allow all to anyone", "targetattr", "*", "allow(all)", BIND_RULE_USERDN_ANYONE);
  private static final String ALLOW_SEARCH_TO_ADMIN =
          buildAciValue("name", "allow search to admin", "targetattr", "*", "allow(search)", BIND_RULE_USERDN_ADMIN);
          buildAciValue("name", "allow search to admin", "targetattr", "*", "allow(search, read)", BIND_RULE_USERDN_ADMIN);
  private static final String DENY_ALL_TO_ALL =
          buildAciValue("name", "deny all", "targetattr", "*", "deny(all)", BIND_RULE_USERDN_ALL);
@@ -262,7 +293,7 @@
          buildAciValue("name", "deny search", "targetattr", "*", "deny(search)", BIND_RULE_USERDN_ALL);
  private static final String ALLOW_SEARCH_TO_ALL =
          buildAciValue("name", "allow search", "targetattr", "*", "allow(search)", BIND_RULE_USERDN_ALL);
          buildAciValue("name", "allow search", "targetattr", "*", "allow(search, read)", BIND_RULE_USERDN_ALL);
  private static final String ALLOW_READ_TO_ALL =
          buildAciValue("name", "allow read", "targetattr", "*", "allow(read)", BIND_RULE_USERDN_ALL);
@@ -289,7 +320,7 @@
          buildAciValue("name", "allow all to non ou person", "targetattr", "*", "targetfilter!=", "(|(objectclass=organizationalunit)(objectclass=person))", "allow(all)", BIND_RULE_USERDN_ALL);
  private static final String ALLOW_WRITE_DELETE_SEARCH_TO_ALL =
          buildAciValue("name", "allow write, delete, and search,", "targetattr", "*", "allow(write, delete, search)", BIND_RULE_USERDN_ALL);
          buildAciValue("name", "allow write, delete, and search,", "targetattr", "*", "allow(write, delete, search, read)", BIND_RULE_USERDN_ALL);
  private static final String DENY_WRITE_DELETE_READ_TO_ALL =
          buildAciValue("name", "deny write delete read to all", "targetattr", "*", "deny(write, delete, read)", BIND_RULE_USERDN_ALL);
@@ -307,7 +338,7 @@
          buildAciValue("name", "deny read to users with 'admin' in their cn", "targetattr", "*", "deny(read)", BIND_RULE_USERDN_ALL_CN_ADMINS);
  private static final String ALLOW_SEARCH_TO_CN_ADMINS =
          buildAciValue("name", "allow search to users with 'admin' in their cn", "targetattr", "*", "allow(search)", BIND_RULE_USERDN_ALL_CN_ADMINS);
          buildAciValue("name", "allow search to users with 'admin' in their cn", "targetattr", "*", "allow(search, read)", BIND_RULE_USERDN_ALL_CN_ADMINS);
  private static final String DENY_READ_TO_TOP_LEVEL_CN_ADMINS =
          buildAciValue("name", "deny read to users with 'admin' in their cn", "targetattr", "*", "deny(read)", BIND_RULE_USERDN_TOP_LEVEL_CN_ADMINS);
@@ -386,19 +417,19 @@
          buildAciValue("name", "allow not admin", "targetattr", "*", "allow(all)", BIND_RULE_USERDN_NOT_ADMIN);
  private static final String ALLOW_SEARCH_TO_LOCALHOST =
          buildAciValue("name", "allow search to localhost", "targetattr", "*", "allow(search)", BIND_RULE_IP_LOCALHOST);
          buildAciValue("name", "allow search to localhost", "targetattr", "*", "allow(search, read)", BIND_RULE_IP_LOCALHOST);
  private static final String ALLOW_SEARCH_REALATTRS_TO_LOCALHOST =
          buildAciValue("name", "allow search to localhost", "targetattr!=", "bogusAttr", "allow(search)", BIND_RULE_IP_LOCALHOST);
          buildAciValue("name", "allow search to localhost", "targetattr!=", "bogusAttr", "allow(search, read)", BIND_RULE_IP_LOCALHOST);
  private static final String ALLOW_SEARCH_OUR_ATTRS_TO_ADMIN =
          buildAciValue("name", "allow search to our attributes to admin", "targetattr", "objectclass||ou||cn||sn||givenname", "target", LDAP_URL_OU_INNER, "allow(search)", BIND_RULE_USERDN_ADMIN);
          buildAciValue("name", "allow search to our attributes to admin", "targetattr", "objectclass||ou||cn||sn||givenname", "target", LDAP_URL_OU_INNER, "allow(search, read)", BIND_RULE_USERDN_ADMIN);
  private static final String ALLOW_SEARCH_TARGET_INNER_TO_LOCALHOST =
          buildAciValue("name", "allow search inner to localhost", "targetattr", "*", "target", LDAP_URL_OU_INNER, "allow(search)", BIND_RULE_IP_LOCALHOST);
          buildAciValue("name", "allow search inner to localhost", "targetattr", "*", "target", LDAP_URL_OU_INNER, "allow(search, read)", BIND_RULE_IP_LOCALHOST);
  private static final String ALLOW_SEARCH_OU_AND_PERSON_TO_SIMPLE =
          buildAciValue("name", "allow search ou and person to localhost", "targetattr", "*", "targetfilter", "(|(objectclass=organizationalunit)(objectclass=person))", "allow(search)", BIND_RULE_AUTHMETHOD_SIMPLE);
          buildAciValue("name", "allow search ou and person to localhost", "targetattr", "*", "targetfilter", "(|(objectclass=organizationalunit)(objectclass=person))", "allow(search, read)", BIND_RULE_AUTHMETHOD_SIMPLE);
// -----------------------------------------------------------------------------
@@ -461,6 +492,13 @@
    buildAciValue("name", "w/ 1 targetattr", "targetattr", "cn", "allow (write)", BIND_RULE_USERDN_SELF),
    buildAciValue("name", "w/ 2 targetattr", "targetattr", "cn || sn", "allow (write)", BIND_RULE_USERDN_SELF),
    buildAciValue("name", "w/ 3 targetattr", "targetattr", "cn || sn || uid", "allow (write)", BIND_RULE_USERDN_SELF),
    //These are four are OpenDS specific attr names
    buildAciValue("name", "opends targetattr", "targetattr", "1-digitinfirst", "allow (write)", BIND_RULE_USERDN_SELF),
    buildAciValue("name", "opendstargetattr", "targetattr", "this_has_underscores", "allow (write)", BIND_RULE_USERDN_SELF),
    buildAciValue("name", "locality targetattr", "targetattr", "locality;lang-fr-ca", "allow (write)", BIND_RULE_USERDN_SELF),
    buildAciValue("name", "oid targetattr", "targetattr", " 2.16.840.1.113730.3.3.2.18.1.4", "allow (write)", BIND_RULE_USERDN_SELF),
    buildAciValue("name", "complicated targetattr", "targetattr", "1ocal_ity;lang-fr-ca", "allow (write)", BIND_RULE_USERDN_SELF),
    buildAciValue("name", "w/ * targetattr", "targetattr", "*", "allow (write)", BIND_RULE_USERDN_SELF),
    buildAciValue("name", "w/ non-existing attr", "targetattr", "notanattr", "allow (write)", BIND_RULE_USERDN_SELF),       // DS 5.2p4 accepts this so we should too.
    buildAciValue("name", "w/ non-existing attr", "targetattr", "cn || notanattr", "allow (write)", BIND_RULE_USERDN_SELF), // DS 5.2p4 accepts this so we should too.
@@ -481,20 +519,16 @@
    buildAciValue("name", "w/ 1 !targetattr", "targetattr!=", "cn", "allow (write)", BIND_RULE_USERDN_SELF),
    buildAciValue("name", "w/ 2 !targetattr", "targetattr!=", "cn || sn", "allow (write)", BIND_RULE_USERDN_SELF),
    buildAciValue("name", "w/ targetfilter", "targetfilter!=", "(sn=admin)", "allow (write)", BIND_RULE_USERDN_SELF),
// </PASSES>
// <FAILS>
//  These aren't supported yet.  We should open an issue.
//    buildAciValue("name", "w/ targetattrfilters", "targetattrfilters", "add=cn:(!(cn=superAdmin))", "allow (write)", BIND_RULE_USERDN_SELF),
//    buildAciValue("name", "w/ targetattrfilters", "targetattrfilters", "add=cn:(!(cn=superAdmin)) && telephoneNumber:(telephoneNumber=123*)", "allow (write)", BIND_RULE_USERDN_SELF),
// </FAILS>
// <PASSES>
    buildAciValue("name", "w/ targattrfilters", "targattrfilters=",  TARG_ATTR_FILTERS, "allow (write)", BIND_RULE_USERDN_SELF),
    buildAciValue("name", "w/ targattrfilters", "targattrfilters=",  TARG_ATTR_FILTERS_1 , "allow (write)", BIND_RULE_USERDN_SELF),
    buildAciValue("name", "w/ targattrfilters", "targattrfilters=",  TARG_ATTR_FILTERS_2 , "allow (write)", BIND_RULE_USERDN_SELF),
    buildAciValue("name", "w/ targattrfilters", "targattrfilters=",  TARG_ATTR_FILTERS_5 , "allow (write)", BIND_RULE_USERDN_SELF),
    buildAciValue("name", "bad_ATTR_TYPE_NAME", "targattrfilters",TARG_ATTR_FILTERS_ATTR_TYPE_NAME, "allow (write)", BIND_RULE_USERDN_SELF),
    buildAciValue("name", "read", "targetattr", "*", "allow (read)", BIND_RULE_USERDN_SELF),
    buildAciValue("name", "write", "targetattr", "*", "allow (write)", BIND_RULE_USERDN_SELF),
    buildAciValue("name", "add", "targetattr", "*", "allow (add)", BIND_RULE_USERDN_SELF),
    buildAciValue("name", "delete", "targetattr", "*", "allow (delete)", BIND_RULE_USERDN_SELF),
    buildAciValue("name", "search", "targetattr", "*", "allow (search)", BIND_RULE_USERDN_SELF),
    buildAciValue("name", "search", "targetattr", "*", "allow (search, read)", BIND_RULE_USERDN_SELF),
    buildAciValue("name", "compare", "targetattr", "*", "allow (compare)", BIND_RULE_USERDN_SELF),
    buildAciValue("name", "selfwrite", "targetattr", "*", "allow (selfwrite)", BIND_RULE_USERDN_SELF),
    buildAciValue("name", "all", "targetattr", "*", "allow (all)", BIND_RULE_USERDN_SELF),
@@ -518,16 +552,22 @@
    buildAciValue("name", "read anyone", "targetattr", "*", "allow (read)", BIND_RULE_USERDN_ANYONE),
    buildAciValue("name", "read filter", "targetattr", "*", "allow (read)", BIND_RULE_USERDN_FILTER),
    buildAciValue("name", "read parent", "targetattr", "*", "allow (read)", BIND_RULE_USERDN_PARENT),
// <FAIL>
//  These aren't supported yet.  We should open an issue.
//    buildAciValue("name", "read group dn 1", "targetattr", "*", "allow (read)", BIND_RULE_GROUPDN_1),
//    buildAciValue("name", "read group dn 2", "targetattr", "*", "allow (read)", BIND_RULE_GROUPDN_2),
//    buildAciValue("name", "read group dn 3", "targetattr", "*", "allow (read)", BIND_RULE_GROUPDN_3),
// </FAIL>
    buildAciValue("name", "userattr", "targetattr", "*", "allow (read)", "userattr=\"manager#USERDN\""),
    buildAciValue("name", "userattr", "targetattr", "*", "allow (read)", "userattr=\"ldap:///dc=example,dc=com?owner#USERDN\""),
    buildAciValue("name", "userattr", "targetattr", "*", "allow (read)", "userattr=\"cn#LDAPURL\""),
    buildAciValue("name", "read group dn 1", "targetattr", "*", "allow (read)", BIND_RULE_GROUPDN_1),
    buildAciValue("name", "read group dn 2", "targetattr", "*", "allow (read)", BIND_RULE_GROUPDN_2),
    buildAciValue("name", "read group dn 3", "targetattr", "*", "allow (read)", BIND_RULE_GROUPDN_3),
    buildAciValue("name", "read group dn 1", "targetattr", "*", "allow (read)", BIND_RULE_ROLEDN_1),
    buildAciValue("name", "read group dn 2", "targetattr", "*", "allow (read)", BIND_RULE_ROLEDN_2),
    buildAciValue("name", "read group dn 3", "targetattr", "*", "allow (read)", BIND_RULE_ROLEDN_3),
    buildAciValue("name", "userattr", "targetattr", "*", "allow (read)", BIND_RULE_USERATTR_USERDN),
    buildAciValue("name", "userattr", "targetattr", "*", "allow (read)", BIND_RULE_USERATTR_USERDN_1),
    buildAciValue("name", "userattr", "targetattr", "*", "allow (read)", BIND_RULE_USERATTR_URL),
    buildAciValue("name", "userattr", "targetattr", "*", "allow (read)", BIND_RULE_USERATTR_GROUPDN),
    buildAciValue("name", "userattr", "targetattr", "*", "allow (read)", BIND_RULE_USERATTR_GROUPDN_1),
    buildAciValue("name", "userattr", "targetattr", "*", "allow (read)", BIND_RULE_USERATTR_ROLEDN),
    buildAciValue("name", "userattr", "targetattr", "*", "allow (read)", BIND_RULE_USERATTR_ROLEDN_1),
    buildAciValue("name", "userattr", "targetattr", "*", "allow (read)", BIND_RULE_USERATTR_USERDN_INHERITANCE),
    buildAciValue("name", "userattr", "targetattr", "*", "allow (read)", BIND_RULE_USERATTR_GROUPDN_INHERITANCE),
    buildAciValue("name", "userattr", "targetattr", "*", "allow (read)", BIND_RULE_USERATTR_VALUE),
    // BUG!  These work with DS 5.2p4, but not with OpenDS.
// <FAIL>
//    DENY_ALL_TO_LOCALHOST_SUBNET,
@@ -559,12 +599,6 @@
     DENY_ALL_TO_ADMIN_AND_LOCALHOST_OR_SSL,
     ALLOW_ALL_NOT_ADMIN
// <FAIL>
//  These aren't supported yet.  We should open an issue.
//    buildAciValue("name", "userattr 1", "targetattr", "*", "allow (read)", "userattr=\"owner#GROUPDN\""),
//    buildAciValue("name", "userattr 1", "targetattr", "*", "allow (read)", "userattr=\"ldap:///dc=example,dc=com?owner#GROUPDN\""),
// </FAIL>
// </PASSES>
    // TODO: bind rules for 'ip', 'dns', 'dayofweek', 'timeofday', 'authmethod'
    // TODO: combinations of these things, including multiple bind rules.
@@ -575,20 +609,29 @@
    // Test each feature in isolation.
// <PASSES>
    "aci: ",
    buildAciValue("allow (write)", BIND_RULE_USERDN_SELF),  // No name
    buildAciValue("name", "invalid", "target", "ldap:///", "allow (write)", BIND_RULE_USERDN_SELF),
    buildAciValue("name", "invalid", "target", "ldap:///not a DN", "allow (write)", BIND_RULE_USERDN_SELF),
    buildAciValue("name", "invalid", "target", "ldap:///cn=", "allow (write)", BIND_RULE_USERDN_SELF),
    buildAciValue("name", "invalid", "targetattr", "", "allow (write)", BIND_RULE_USERDN_SELF),
    buildAciValue("name", "invalid", "targetattr", "not an attr", "allow (write)", BIND_RULE_USERDN_SELF),
    buildAciValue("name", "invalid", "targetattr", "cn ||", "allow (write)", BIND_RULE_USERDN_SELF),
    buildAciValue("name", "invalid", "targetattr", "not/an/attr", "allow (write)", BIND_RULE_USERDN_SELF),
    buildAciValue("name", "invalid", "targetattr", "cn", "allow (write)", BIND_RULE_INVALID_DAY),
          buildAciValue("allow (write)", BIND_RULE_USERDN_SELF),  // No name
          buildAciValue("name", "invalid", "target", "ldap:///", "allow (write)", BIND_RULE_USERDN_SELF),
          buildAciValue("name", "invalid", "target", "ldap:///not a DN", "allow (write)", BIND_RULE_USERDN_SELF),
          buildAciValue("name", "invalid", "target", "ldap:///cn=", "allow (write)", BIND_RULE_USERDN_SELF),
          buildAciValue("name", "invalid", "targetattr", "", "allow (write)", BIND_RULE_USERDN_SELF),
          buildAciValue("name", "invalid", "targetattr", "not an attr", "allow (write)", BIND_RULE_USERDN_SELF),
          buildAciValue("name", "invalid", "targetattr", "cn ||", "allow (write)", BIND_RULE_USERDN_SELF),
          buildAciValue("name", "invalid", "targetattr", "not/an/attr", "allow (write)", BIND_RULE_USERDN_SELF),
          buildAciValue("name", "invalid", "targetattr", "cn", "allow (write)", BIND_RULE_INVALID_DAY),
          buildAciValue("name", "bad_filters", "targetattrfilters",TARG_ATTR_FILTERS_INVALID_FILTER, "allow (write)", BIND_RULE_USERDN_SELF),
          buildAciValue("name", "bad_op", "targetattrfilters",TARG_ATTR_FILTERS_BAD_OP, "allow (write)", BIND_RULE_USERDN_SELF),
          buildAciValue("name", "bad_op_match", "targetattrfilters",TARG_ATTR_FILTERS_BAD_OP_MATCH, "allow (write)", BIND_RULE_USERDN_SELF),
          buildAciValue("name", "bad_filter_attr", "targetattrfilters",TARG_ATTR_FILTERS_BAD_FILTER_ATTR, "allow (write)", BIND_RULE_USERDN_SELF),
          buildAciValue("name", "bad_format", "targetattrfilters",TARG_ATTR_FILTERS_BAD_FORMAT, "allow (write)", BIND_RULE_USERDN_SELF),
          buildAciValue("name", "too_many_lists", "targetattrfilters",TARG_ATTR_FILTERS_TOO_MANY_LISTS, "allow (write)", BIND_RULE_USERDN_SELF),
          buildAciValue("name", "bad_tok", "targetattrfilters",TARG_ATTR_FILTERS_BAD_TOK, "allow (write)", BIND_RULE_USERDN_SELF),
// <FAIL>
// Attributes can't have '_' right?, but DS 5.2p4 accepts this, so should we?
//    buildAciValue("name", "invalid", "targetattr", "not_an_attr", "allow (write)", BIND_RULE_USERDN_SELF),
// </FAIL>
         buildAciValue("name", "bad targetScope", "targetScope", "sub_tree", "allow (write)", BIND_RULE_USERDN_SELF),
         buildAciValue("name", "bad right", "targetattr", "*", "allow (read, write, add, delete, search, compare, selfwrite, all, foo)", BIND_RULE_USERDN_SELF),
         buildAciValue("name", "bad access type", "targetattr", "*", "allows (read, write, add, delete, search, compare, selfwrite, all)", BIND_RULE_USERDN_SELF),
         //no name
         buildAciValue("targetattr", "*", "allows (read, write, add, delete, search, compare, selfwrite, all)", BIND_RULE_USERDN_SELF),
// </PASSES>
  };
@@ -1392,7 +1435,7 @@
      // Ignoring whitespace the diff should be empty.
      Assert.assertTrue(diffFromExpected.replaceAll("\\s", "").length() == 0);
    } catch (Throwable e) {
      System.err.println(
        System.err.println(
              "Started with dit:\n" +
              params._initialDitLdif +
              ((params._aciLdif.length() == 0) ?
@@ -1429,7 +1472,7 @@
      String aciField = aciFields[i];
      String aciValue = aciFields[i+1];
      if (aciField.startsWith("target")) {
      if (aciField.startsWith("targ")) {
        if (!aciField.endsWith("=")) {  // We allow = or more importantly != to be included with the target
          aciField += "=";
        }
@@ -1456,7 +1499,7 @@
      String permission = aciFields[i];
      String bindRule = aciFields[i+1];
      if (!permission.startsWith("target") && !permission.equals("name")) {
      if (!permission.startsWith("targ") && !permission.equals("name")) {
        aci.append(EOL + " " + permission + " " + bindRule + ";");
      }
    }