From a6ffa6f49ae5d4c3311eb5ffdd74dc3a8d6c4f40 Mon Sep 17 00:00:00 2001
From: dugan <dugan@localhost>
Date: Thu, 14 Jun 2007 19:12:52 +0000
Subject: [PATCH] Add support for '+' all attributes description in targetattrs expression, with missing files from previous commit. See issue 1779 for more info.

---
 opends/src/server/org/opends/server/authorization/dseecompat/Aci.java                                        |   52 +++
 opends/src/server/org/opends/server/authorization/dseecompat/AciTargets.java                                 |   59 +++-
 opends/src/server/org/opends/server/authorization/dseecompat/AciContainer.java                               |   89 +++++--
 opends/src/server/org/opends/server/authorization/dseecompat/AciHandler.java                                 |   41 ++-
 opends/tests/unit-tests-testng/src/server/org/opends/server/authorization/dseecompat/TargetAttrTestCase.java |  162 +++++++++++++
 opends/src/server/org/opends/server/authorization/dseecompat/TargetAttr.java                                 |  203 +++++++++-------
 opends/src/server/org/opends/server/authorization/dseecompat/AciTargetMatchContext.java                      |   48 +++
 opends/src/server/org/opends/server/messages/AciMessages.java                                                |   35 --
 8 files changed, 498 insertions(+), 191 deletions(-)

diff --git a/opends/src/server/org/opends/server/authorization/dseecompat/Aci.java b/opends/src/server/org/opends/server/authorization/dseecompat/Aci.java
index 6f5b75b..46ef3db 100644
--- a/opends/src/server/org/opends/server/authorization/dseecompat/Aci.java
+++ b/opends/src/server/org/opends/server/authorization/dseecompat/Aci.java
@@ -110,11 +110,12 @@
     /**
      * 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.
+     * digit characters, hyphens, semi-colons and underscores. It also allows
+     * the special shorthand characters "*" for all user attributes and "+" for
+     * all operational attributes.
      */
     public  static final String ATTR_NAME =
-              "((?i)[a-z\\d]{1}[[a-z]\\d-_.;]*(?-i))";
+              "((?i)[a-z\\d]{1}[[a-z]\\d-_.;]*(?-i)|\\*{1}|\\+{1})";
 
     /**
       * Regular expression matching a LDAP URL.
@@ -150,8 +151,16 @@
     /**
      * Regular expression the matches "*".
      */
-    public static final String ALL_ATTRS_WILD_CARD = ZERO_OR_MORE_WHITESPACE +
-                                           "\\*" + ZERO_OR_MORE_WHITESPACE;
+    public static final String ALL_USER_ATTRS_WILD_CARD =
+            ZERO_OR_MORE_WHITESPACE +
+                    "\\*" + ZERO_OR_MORE_WHITESPACE;
+
+    /**
+     * Regular expression the matches "+".
+     */
+    public static final String ALL_OP_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.
@@ -265,15 +274,38 @@
      * evaluation if the flag is ACI_ATTR_STAR_MATCHED (all attributes match)
      * and the attribute type is not operational.
      */
-    public static final int ACI_ATTR_STAR_MATCHED = 0x0008;
+    public static final int ACI_USER_ATTR_STAR_MATCHED = 0x0008;
 
     /**
-     * ACI_FOUND_ATTR_RULE is the flag set when the evaluation reason of a
+     * ACI_FOUND_USER_ATTR_RULE is the flag set when the evaluation reason of a
      * AciHandler.maysend ACI_READ access evaluation was the result of an
-     * ACI targetattr specific attribute expression
-     * (targetattr="some attribute type") target match.
+     * ACI targetattr specific user attribute expression
+     * (targetattr="some user attribute type") target match.
      */
-    public static final int ACI_FOUND_ATTR_RULE = 0x0010;
+    public static final int ACI_FOUND_USER_ATTR_RULE = 0x0010;
+
+    /**
+     * ACI_OP_ATTR_PLUS_MATCHED is the flag set when the evaluation reason of a
+     * AciHandler.maysend ACI_READ access evaluation was the result of an
+     * ACI targetattr all operational attributes expression (targetattr="+")
+     * target match. For this flag to be set, there must be only one
+     * ACI matching.
+     *
+     * This flag and ACI_FOUND_OP_ATTR_RULE are used in the
+     * AciHandler.filterEntry.accessAllowedAttrs method to skip access
+     * evaluation if the flag is ACI_OP_ATTR_PLUS_MATCHED (all operational
+     * attributes match) and the attribute type is operational.
+     */
+
+    public static final int ACI_OP_ATTR_PLUS_MATCHED = 0x0004;
+
+    /**
+     * ACI_FOUND_OP_ATTR_RULE is the flag set when the evaluation reason of a
+     * AciHandler.maysend ACI_READ access evaluation was the result of an
+     * ACI targetattr specific operational attribute expression
+     * (targetattr="some operational attribute type") target match.
+     */
+    public static final int ACI_FOUND_OP_ATTR_RULE = 0x0020;
 
     /**
      * ACI_NULL is used to set the container rights to all zeros. Used
diff --git a/opends/src/server/org/opends/server/authorization/dseecompat/AciContainer.java b/opends/src/server/org/opends/server/authorization/dseecompat/AciContainer.java
index ff7da54..c2126da 100644
--- a/opends/src/server/org/opends/server/authorization/dseecompat/AciContainer.java
+++ b/opends/src/server/org/opends/server/authorization/dseecompat/AciContainer.java
@@ -280,12 +280,20 @@
             this.authzid=getEffectiveRightsControl.getAuthzDN();
           this.specificAttrs=getEffectiveRightsControl.getAttributes();
         }
-        //If the ACI evaluated because of an Targetattr="*", then the
+        //If an ACI evaluated because of an Targetattr="*", then the
         //AciHandler.maySend method signaled this via adding this attachment
         //string.
-        String allAttrs=(String)operation.getAttachment(ALL_ATTRS_MATCHED);
-        if(allAttrs != null)
-          evalAllAttributes = ACI_ATTR_STAR_MATCHED;
+        String allUserAttrs=
+                  (String)operation.getAttachment(ALL_USER_ATTRS_MATCHED);
+        if(allUserAttrs != null)
+          evalAllAttributes |= ACI_USER_ATTR_STAR_MATCHED;
+        //If an ACI evaluated because of an Targetattr="+", then the
+        //AciHandler.maySend method signaled this via adding this attachment
+        //string.
+        String allOpAttrs=(String)operation.getAttachment(ALL_OP_ATTRS_MATCHED);
+        if(allOpAttrs != null)
+          evalAllAttributes |= ACI_OP_ATTR_PLUS_MATCHED;
+
         //The AciHandler.maySend method also adds the full attribute version of
         //the resource entry in this attachment.
         fullEntry=(Entry)operation.getAttachment(ALL_ATTRS_RESOURCE_ENTRY);
@@ -823,43 +831,74 @@
   /**
    * {@inheritDoc}
    */
-  public  void setACIEvalAttributesRule(int v) {
+  public  void setEvalUserAttributes(int v) {
     if(operation instanceof SearchOperation && (rights == ACI_READ)) {
-      if(v == ACI_FOUND_ATTR_RULE) {
-        evalAllAttributes |= ACI_FOUND_ATTR_RULE;
-        evalAllAttributes &= ~ACI_ATTR_STAR_MATCHED;
+      if(v == ACI_FOUND_USER_ATTR_RULE) {
+        evalAllAttributes |= ACI_FOUND_USER_ATTR_RULE;
+        evalAllAttributes &= ~ACI_USER_ATTR_STAR_MATCHED;
       } else
-        evalAllAttributes |= Aci.ACI_ATTR_STAR_MATCHED;
+        evalAllAttributes |= ACI_USER_ATTR_STAR_MATCHED;
+    }
+  }
+
+     /**
+   * {@inheritDoc}
+   */
+  public  void setEvalOpAttributes(int v) {
+    if(operation instanceof SearchOperation && (rights == ACI_READ)) {
+      if(v == ACI_FOUND_OP_ATTR_RULE) {
+        evalAllAttributes |= ACI_FOUND_OP_ATTR_RULE;
+        evalAllAttributes &= ~ACI_OP_ATTR_PLUS_MATCHED;
+      } else
+        evalAllAttributes |= ACI_OP_ATTR_PLUS_MATCHED;
     }
   }
 
   /**
    * {@inheritDoc}
    */
-  public boolean hasACIEvalAttributes() {
-    return (evalAllAttributes & ACI_FOUND_ATTR_RULE) == ACI_FOUND_ATTR_RULE;
-  }
-
-
-  /**
-   * Return true if the evaluating ACI either contained a targetattr all
-   * attributes rule matched only.
-   *
-   * @return  True if the above condition was seen.
-   **/
-  public boolean hasACIAllAttributes() {
-    return (evalAllAttributes & ACI_ATTR_STAR_MATCHED) == ACI_ATTR_STAR_MATCHED;
+  public boolean hasEvalUserAttributes() {
+    return (evalAllAttributes & ACI_FOUND_USER_ATTR_RULE) ==
+            ACI_FOUND_USER_ATTR_RULE;
   }
 
   /**
    * {@inheritDoc}
    */
-  public void clearACIEvalAttributesRule(int v) {
+  public boolean hasEvalOpAttributes() {
+    return (evalAllAttributes & ACI_FOUND_OP_ATTR_RULE) ==
+            ACI_FOUND_OP_ATTR_RULE;
+  }
+
+  /**
+   * Return true if the evaluating ACI contained a targetattr all
+   * user attributes rule match.
+   *
+   * @return  True if the above condition was seen.
+   **/
+  public boolean hasAllUserAttributes() {
+    return (evalAllAttributes & ACI_USER_ATTR_STAR_MATCHED) ==
+            ACI_USER_ATTR_STAR_MATCHED;
+  }
+
+  /**
+   * Return true if the evaluating ACI contained a targetattr all
+   * operational attributes rule match.
+   *
+   * @return  True if the above condition was seen.
+   **/
+    public boolean hasAllOpAttributes() {
+    return (evalAllAttributes & ACI_OP_ATTR_PLUS_MATCHED) ==
+            ACI_OP_ATTR_PLUS_MATCHED;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  public void clearEvalAttributes(int v) {
     if(v == 0)
       evalAllAttributes=0;
     else
       evalAllAttributes &= ~v;
   }
-
-
 }
diff --git a/opends/src/server/org/opends/server/authorization/dseecompat/AciHandler.java b/opends/src/server/org/opends/server/authorization/dseecompat/AciHandler.java
index 134262c..e934089 100644
--- a/opends/src/server/org/opends/server/authorization/dseecompat/AciHandler.java
+++ b/opends/src/server/org/opends/server/authorization/dseecompat/AciHandler.java
@@ -98,10 +98,16 @@
     public static String ALL_ATTRS_RESOURCE_ENTRY = "allAttrsResourceEntry";
 
     /**
-     * String used to indicate that the evaluating ACI had a all attributes
+     * String used to indicate that the evaluating ACI had a all user attributes
      * targetattr match (targetattr="*").
      */
-     public static String ALL_ATTRS_MATCHED = "allAttrsMatched";
+     public static String ALL_USER_ATTRS_MATCHED = "allUserAttrsMatched";
+
+    /**
+     * String used to indicate that the evaluating ACI had a all operational
+     * attributes targetattr match (targetattr="+").
+     */
+     public static String ALL_OP_ATTRS_MATCHED = "allOpAttrsMatched";
 
     /**
      * This constructor instantiates the ACI handler class that performs the
@@ -603,17 +609,18 @@
      */
     private SearchResultEntry
     accessAllowedAttrs(AciLDAPOperationContainer container) {
-      Entry e=container.getResourceEntry();
-      List<AttributeType> typeList=getAllAttrs(e);
-      for(AttributeType attrType : typeList) {
-        if(container.hasACIAllAttributes() && !attrType.isOperational())
-          continue;
-        container.setCurrentAttributeType(attrType);
-        if(!accessAllowed(container)) {
-          e.removeAttribute(attrType);
+        Entry e=container.getResourceEntry();
+        List<AttributeType> typeList=getAllAttrs(e);
+        for(AttributeType attrType : typeList) {
+            if(container.hasAllUserAttributes() && !attrType.isOperational())
+                continue;
+            if(container.hasAllOpAttributes() && attrType.isOperational())
+                continue;
+            container.setCurrentAttributeType(attrType);
+            if(!accessAllowed(container))
+                e.removeAttribute(attrType);
         }
-      }
-      return container.getSearchResultEntry();
+        return container.getSearchResultEntry();
     }
 
     /**
@@ -916,12 +923,16 @@
             ret=false;
           }
           if (ret) {
-              operationContainer.clearACIEvalAttributesRule(ACI_NULL);
+              operationContainer.clearEvalAttributes(ACI_NULL);
               operationContainer.setRights(ACI_READ);
               ret=accessAllowedEntry(operationContainer);
             if(ret) {
-              if(!operationContainer.hasACIEvalAttributes())
-                operation.setAttachment(ALL_ATTRS_MATCHED, ALL_ATTRS_MATCHED);
+              if(!operationContainer.hasEvalUserAttributes())
+                operation.setAttachment(ALL_USER_ATTRS_MATCHED,
+                        ALL_USER_ATTRS_MATCHED);
+              if(!operationContainer.hasEvalOpAttributes())
+                operation.setAttachment(ALL_OP_ATTRS_MATCHED,
+                        ALL_OP_ATTRS_MATCHED);
             }
           }
       }
diff --git a/opends/src/server/org/opends/server/authorization/dseecompat/AciTargetMatchContext.java b/opends/src/server/org/opends/server/authorization/dseecompat/AciTargetMatchContext.java
index d6fb958..33f4db1 100644
--- a/opends/src/server/org/opends/server/authorization/dseecompat/AciTargetMatchContext.java
+++ b/opends/src/server/org/opends/server/authorization/dseecompat/AciTargetMatchContext.java
@@ -168,38 +168,64 @@
 
   /**
    * This method toggles a mask that indicates that access checking of
-   * individual non-operational attributes may or may not be skipped depending
-   * on if there is a single ACI containing a targetattr all attributes  rule
-   * (targetattr="*").
+   * individual user attributes may or may not be skipped depending
+   * on if there is a single ACI containing a targetattr all user
+   * attributes rule (targetattr="*").
    *
-   * The only case where individual non-operational attribute access checking
+   * The only case where individual user attribute access checking
    * can be skipped, is when a single ACI matched using a targetattr
-   * all attributes rule.
+   * all user attributes rule and the attribute type being check is not
+   * operational.
    *
    * @param v  The mask to this value.
    */
-  void setACIEvalAttributesRule(int v);
+  void setEvalUserAttributes(int v);
+
+  /**
+   * This method toggles a mask that indicates that access checking of
+   * individual operational attributes may or may not be skipped depending
+   * on if there is a single ACI containing a targetattr all operational
+   * attributes rule (targetattr="+").
+   *
+   * The only case where individual operational attribute access checking
+   * can be skipped, is when a single ACI matched using a targetattr
+   * all operational attributes rule and the attribute type being check is
+   * operational.
+   *
+   * @param v  The mask to this value.
+   */
+  void setEvalOpAttributes(int v);
 
   /**
    * Return true if the evaluating ACI either contained an explicitly defined
-   * attribute type in a targeattr target rule or both a targetattr all
-   * attributes rule matched and a explictly defined targetattr target rule
+   * user attribute type in a targeattr target rule or both a targetattr all
+   * user attributes rule matched and a explictly defined targetattr target rule
    * matched.
    *
    * @return  True if the above condition was seen.
    */
-    boolean hasACIEvalAttributes();
+    boolean hasEvalUserAttributes();
+
+  /**
+   * Return true if the evaluating ACI either contained an explicitly defined
+   * operational attribute type in a targeattr target rule or both a targetattr
+   * all operational attributes rule matched and a explictly defined targetattr
+   * target rule matched.
+   *
+   * @return  True if the above condition was seen.
+   */
+    boolean hasEvalOpAttributes();
 
 
   /**
    * Used to clear the mask used to detect if access checking needs to be
-   * performed on individual non-operational attributes types. The specified
+   * performed on individual attributes types. The specified
    * value is cleared from the mask or if the value equals 0 the mask is
    * completely cleared.
    *
    * @param v  The flag to clear or 0 to set the mask to 0.
    */
-    public void clearACIEvalAttributesRule(int v);
+    public void clearEvalAttributes(int v);
 }
 
 
diff --git a/opends/src/server/org/opends/server/authorization/dseecompat/AciTargets.java b/opends/src/server/org/opends/server/authorization/dseecompat/AciTargets.java
index dc13132..41e58a0 100644
--- a/opends/src/server/org/opends/server/authorization/dseecompat/AciTargets.java
+++ b/opends/src/server/org/opends/server/authorization/dseecompat/AciTargets.java
@@ -412,21 +412,8 @@
             int rights=targetMatchCtx.getRights();
             boolean isFirstAttr=targetMatchCtx.isFirstAttribute();
             if((a != null) && (targets.getTargetAttr() != null))  {
-             ret=TargetAttr.isApplicable(a,targets.getTargetAttr());
-             targetMatchCtx.clearACIEvalAttributesRule(ACI_ATTR_STAR_MATCHED);
-             /*
-               If a explicitly defined targetattr's match rule has not
-               been seen (~ACI_FOUND_ATTR_RULE) and the current attribute type
-               is applicable because of a targetattr all attributes rule match,
-               set a flag to indicate this situation (ACI_ATTR_STAR_MATCHED).
-               Else the attributes is applicable because it is operational or
-               not a targetattr's all attribute match.
-              */
-             if(ret && targets.getTargetAttr().isAllAttributes() &&
-                !targetMatchCtx.hasACIEvalAttributes())
-               targetMatchCtx.setACIEvalAttributesRule(ACI_ATTR_STAR_MATCHED);
-              else
-                targetMatchCtx.setACIEvalAttributesRule(ACI_FOUND_ATTR_RULE);
+              ret=TargetAttr.isApplicable(a,targets.getTargetAttr());
+              setEvalAttributes(targetMatchCtx,targets,ret);
             } else if((a != null) || (targets.getTargetAttr() != null)) {
                 if((aci.hasRights(skipRights)) &&
                                                 (skipRightsHasRights(rights)))
@@ -563,4 +550,46 @@
         }
         return ret;
     }
+
+
+    /**
+     * The method is used to try and determine if a targetAttr expression that
+     * is applicable has a '*' (or '+' operational attributes) token or if it
+     * was applicable because of a specific attribute type declared in the
+     * targetattrs expression (i.e., targetattrs=cn).
+     *
+     *
+     * @param ctx  The ctx to check against.
+     * @param targets The targets part of the ACI.
+     * @param ret  The is true if the ACI has already been evaluated to be
+     *             applicable.
+     */
+    private static
+    void setEvalAttributes(AciTargetMatchContext ctx, AciTargets targets,
+                           boolean ret) {
+        ctx.clearEvalAttributes(ACI_USER_ATTR_STAR_MATCHED);
+        ctx.clearEvalAttributes(ACI_OP_ATTR_PLUS_MATCHED);
+        /*
+         If an applicable targetattr's match rule has not
+         been seen (~ACI_FOUND_OP_ATTR_RULE or ~ACI_FOUND_USER_ATTR_RULE) and
+         the current attribute type is applicable because of a targetattr all
+         user (or operational) attributes rule match,
+         set a flag to indicate this situation (ACI_USER_ATTR_STAR_MATCHED or
+         ACI_OP_ATTR_PLUS_MATCHED). This check also catches the following case
+         where the match was by a specific attribute type (either user or
+         operational) and the other attribute type has an all attribute token.
+         For example, the expression is: (targetattrs="cn || +) and the current
+         attribute type is cn.
+        */
+        if(ret && targets.getTargetAttr().isAllUserAttributes() &&
+                !ctx.hasEvalUserAttributes())
+          ctx.setEvalUserAttributes(ACI_USER_ATTR_STAR_MATCHED);
+        else
+          ctx.setEvalUserAttributes(ACI_FOUND_USER_ATTR_RULE);
+        if(ret && targets.getTargetAttr().isAllOpAttributes() &&
+                !ctx.hasEvalOpAttributes())
+          ctx.setEvalOpAttributes(ACI_OP_ATTR_PLUS_MATCHED);
+        else
+          ctx.setEvalOpAttributes(ACI_FOUND_OP_ATTR_RULE);
+    }
 }
diff --git a/opends/src/server/org/opends/server/authorization/dseecompat/TargetAttr.java b/opends/src/server/org/opends/server/authorization/dseecompat/TargetAttr.java
index 9b2afff..c28633e 100644
--- a/opends/src/server/org/opends/server/authorization/dseecompat/TargetAttr.java
+++ b/opends/src/server/org/opends/server/authorization/dseecompat/TargetAttr.java
@@ -45,9 +45,14 @@
     private EnumTargetOperator operator = EnumTargetOperator.EQUALITY;
 
     /*
-     * Flags that is set if all attributes pattern seen "*".
+     * Flags that is set if all user attributes pattern seen "*".
      */
-    private boolean allAttributes = false ;
+    private boolean allUserAttributes = false ;
+
+    /*
+     * Flags that is set if all operational attributes pattern seen "+".
+     */
+    private boolean allOpAttributes = false ;
 
     /*
      * HashSet of the attribute types parsed by the constructor.
@@ -73,18 +78,21 @@
      * @param operator The operation enumeration of the targetattr
      * expression (=, !=).
      * @param attrString A string representing the attributes specified in
-     * the targetattr expression (ie, dn || cn).
+     * the targetattr expression (ie, dn || +).
      * @throws AciException If the attrs string is invalid.
      */
     private TargetAttr(EnumTargetOperator operator, String attrString)
     throws AciException {
         this.operator = operator;
         if (attrString != null) {
-            if (Pattern.matches(ALL_ATTRS_WILD_CARD, attrString) ){
-                allAttributes = true ;
-            } else {
+            if (Pattern.matches(ALL_USER_ATTRS_WILD_CARD, attrString) )
+                allUserAttributes = true ;
+            else  if (Pattern.matches(ALL_OP_ATTRS_WILD_CARD, attrString) )
+                allOpAttributes = true ;
+            else {
                 if (Pattern.matches(ZERO_OR_MORE_WHITESPACE, attrString)){
-                    allAttributes = false;
+                    allUserAttributes = false;
+                    allOpAttributes=false;
                 } else {
                     if (Pattern.matches(attrListRegex, attrString)) {
                         // Remove the spaces in the attr string and
@@ -95,25 +103,9 @@
                          attrString.replaceAll(ZERO_OR_MORE_WHITESPACE, "");
                         String[] attributeArray=
                              separatorPattern.split(attrString);
-                        //Add each element of array to attributes HashSet
-                        //after converting it to AttributeType.
-                        arrayToAttributeTypes(attributeArray);
-                       //Must be either all operational attrs or all user attrs,
-                       //but not both.
-                        if(!opAttributes.isEmpty() && !attributes.isEmpty()) {
-                            int msgID =
-                             MSGID_ACI_TARGETATTR_INVALID_OP_USER_ATTR;
-                            String message = getMessage(msgID, attrString);
-                            throw new AciException(msgID, message);
-                        }
-                        //Inequality not allowed with operational attrs.
-                        if(!opAttributes.isEmpty() &&
-                            operator.equals(EnumTargetOperator.NOT_EQUALITY)) {
-                            int msgID =
-                               MSGID_ACI_TARGATTR_INVALID_OP_ATTR_INEQUALITY;
-                            String message = getMessage(msgID, attrString);
-                            throw new AciException(msgID, message);
-                        }
+                        //Add each element of array to appropriate HashSet
+                        //after conversion to AttributeType.
+                        arrayToAttributeTypes(attributeArray, attrString);
                     } else {
                       int msgID =
                          MSGID_ACI_SYNTAX_INVALID_TARGETATTRKEYWORD_EXPRESSION;
@@ -126,23 +118,47 @@
     }
 
     /**
-     * Converts each element of an array of attribute type strings
-     * to attribute types and adds them to either the attributes HashSet or
-     * the operational attributes HashSet if they are operational.
+     * Converts each element of an array of attribute strings
+     * to attribute types and adds them to either the user attributes HashSet or
+     * the operational attributes HashSet. Also, scan for the shorthand tokens
+     * "*" for all user attributes and "+" for all operational attributes.
+     *
      * @param attributeArray The array of attribute type strings.
+     * @param attrStr String used in error message if an Aci Exception
+     *                is thrown.
+     * @throws AciException If the one of the attribute checks fails.
      */
-    private void arrayToAttributeTypes(String[] attributeArray) {
+    private void arrayToAttributeTypes(String[] attributeArray, String attrStr)
+            throws AciException {
         for (int i=0, n=attributeArray.length; i < n; i++) {
             String attribute=attributeArray[i].toLowerCase();
-            AttributeType attributeType;
-            if((attributeType =
-                    DirectoryServer.getAttributeType(attribute)) == null)
-                attributeType =
-                        DirectoryServer.getDefaultAttributeType(attribute);
-          if(attributeType.isOperational())
-           opAttributes.add(attributeType);
-          else
-            attributes.add(attributeType);
+            if(attribute.equals("*")) {
+                if(!allUserAttributes)
+                    allUserAttributes=true;
+                else {
+                    int msgID = MSGID_ACI_TARGETATTR_INVALID_ATTR_TOKEN;
+                    String message = getMessage(msgID, attrStr);
+                    throw new AciException(msgID, message);
+                }
+            } else if(attribute.equals("+")) {
+                if(!allOpAttributes)
+                    allOpAttributes=true;
+                else {
+                    int msgID = MSGID_ACI_TARGETATTR_INVALID_ATTR_TOKEN;
+                    String message = getMessage(msgID, attrStr);
+                    throw new AciException(msgID, message);
+                }
+            } else {
+                AttributeType attributeType;
+                if((attributeType =
+                        DirectoryServer.getAttributeType(attribute)) == null)
+                    attributeType =
+                            DirectoryServer.getDefaultAttributeType(attribute);
+                if(attributeType.isOperational())
+                    opAttributes.add(attributeType);
+                else
+                    attributes.add(attributeType);
+            }
         }
     }
 
@@ -159,8 +175,18 @@
      * targetattr="*" or targetattr != "*".
      * @return True if all attributes was seen.
      */
-    public boolean isAllAttributes() {
-        return allAttributes;
+    public boolean isAllUserAttributes() {
+        return allUserAttributes;
+    }
+
+
+    /**
+     * This flag is set if the parsing code saw:
+     * targetattr="+" or targetattr != "+".
+     * @return True if all attributes was seen.
+     */
+    public boolean isAllOpAttributes() {
+        return allOpAttributes;
     }
 
     /**
@@ -195,67 +221,64 @@
     }
 
     /**
-     * Perform two checks to see if a specified attribute type is applicable.
-     * First, check the targetAttr's isAllAttributes() boolean. The
-     * isAllAttributes boolean is set true when the string:
+     * Performs test to see if the specified is applicable to the specified
+     * TargetAttr. First a check if the TargetAttr parsing code saw an
+     * expression like:
      *
-     *       targetattrs="*"
+     *  (targetattrs="+ || *), (targetattrs != "* || +)
      *
-     * is  seen when an ACI is parsed.  This boolean only applies to
-     * non-operational attribute types. If the attribute type being evaluated
-     * and the isAllAttributes is true, then the evaluation will return false
-     * because operational attributes must be explicity defined.
+     * where both shorthand tokens where parsed. IF so then the attribute type
+     * matches automatically (or not matches if NOT_EQUALITY).
      *
-     * If the isAllAttributes boolean is true (and the attribute is
-     * non-operational), the second check is skipped and the TargetAttr's
-     * operator is checked to see if the method should return false
-     * (NOT_EQUALITY) instead of true.
+     * If there isn't a match, then the method evalAttrType is called to further
+     * evaluate the attribute type and targetAttr combination.
      *
-     * If the isAllAttributes boolean is false, then the attribute type is
-     * checked to see if it is operational. If it is, then the operational
-     * HashSet is searched to see if it contains the operational attribute
-     * type. If it is found then true is returned, else false is returned
-     * if it isn't found. The NOT_EQUALITY operator is invalid for operational
-     * attribute types and is not checked.
-     *
-     * If the attribute is not operational,  then the TargeAttr's user
-     * attribute type HashSet is searched to see if it contains the
-     * specified attribute type. That result could be negated depending
-     * on if the TargetAttr's operator is NOT_EQUALITY.
      *
      * @param a The attribute type to evaluate.
      * @param targetAttr The ACI's TargetAttr class to evaluate against.
      * @return The boolean result of the above tests and application
      * TargetAttr's operator value applied to the test result.
      */
+
     public static boolean isApplicable(AttributeType a,
                                        TargetAttr targetAttr) {
-      boolean ret;
-      if(targetAttr.isAllAttributes()) {
-        //If it is an operational attribute, then access is denied for all
-        //attributes wild-card. Operational attributes must be
-        // explicitly defined and cannot be negated.
-        if(a.isOperational()) {
-          ret=false;
+        boolean ret;
+        if(targetAttr.isAllUserAttributes() && targetAttr.isAllOpAttributes()) {
+            ret =
+              !targetAttr.getOperator().equals(EnumTargetOperator.NOT_EQUALITY);
         } else
-          ret =
-             !targetAttr.getOperator().equals(EnumTargetOperator.NOT_EQUALITY);
-      }  else {
-        ret=false;
-          HashSet<AttributeType> attributes=targetAttr.getAttributes();
-          HashSet<AttributeType> opAttributes=targetAttr.getOpAttributes();
-           //Check if the attribute is operational, if so check the
-           //operation HashSet.
-           if(a.isOperational()) {
-             if(opAttributes.contains(a))
-               ret=true;
-         } else {
-            if(attributes.contains(a))
-              ret=true;
-            if(targetAttr.getOperator().equals(EnumTargetOperator.NOT_EQUALITY))
-              ret = !ret;
-          }
-       }
-      return ret;
+            ret=evalAttrType(a, targetAttr);
+
+        return ret;
+    }
+
+    /**
+     * First check is to see if the attribute type is operational. If so then
+     * a match is true if the allOpAttributes boolean is true or if the
+     * attribute type is found in the operational attributes HashSet.
+     *
+     * Second check is similar to above, except the user attributes boolean
+     * and HashSet is examined. Both results can be negated if the expression
+     * operator is NOT_EQUALITT).
+     *
+     * @param a The attribute type to evaluate.
+     * @param targetAttr The targetAttr to apply to the attribute type.
+     * @return True if the attribute type is applicable to the targetAttr.
+     */
+    private static
+    boolean evalAttrType(AttributeType a, TargetAttr targetAttr) {
+        boolean ret=false;
+        if(a.isOperational()) {
+            if(targetAttr.isAllOpAttributes() ||
+                    targetAttr.opAttributes.contains(a))
+                ret=true;
+        } else {
+            if(targetAttr.isAllUserAttributes() ||
+                    targetAttr.attributes.contains(a))
+                ret=true;
+        }
+        if(targetAttr.getOperator().equals(EnumTargetOperator.NOT_EQUALITY))
+            ret = !ret;
+        return ret;
     }
 }
diff --git a/opends/src/server/org/opends/server/messages/AciMessages.java b/opends/src/server/org/opends/server/messages/AciMessages.java
index 722a2e0..811b377 100644
--- a/opends/src/server/org/opends/server/messages/AciMessages.java
+++ b/opends/src/server/org/opends/server/messages/AciMessages.java
@@ -740,22 +740,15 @@
   public static final int MSGID_ACI_NOT_VALID_DN =
        CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_SEVERE_WARNING | 73;
 
-    /**
-   * The message ID for the message that will be used if a targetattr
-   * keyword expression contains both operational and user attribute
-   * types. This takes one argument, which is the targetattr expression string.
-   */
-  public static final int MSGID_ACI_TARGETATTR_INVALID_OP_USER_ATTR =
-       CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_SEVERE_WARNING | 74;
-
   /**
    * The message ID for the message that will be used if a targetattr
-   * keyword expression performs both an inequality operation using
-   * operational attribute types. This takes one argument, which is the
-   * targetattr expression string.
+   * keyword expression contains an error when a each token in an targetattr
+   * expression was parsed. This takes one argument, which is the expression
+   * string that caused the error.
    */
-  public static final int MSGID_ACI_TARGATTR_INVALID_OP_ATTR_INEQUALITY =
-       CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_SEVERE_WARNING | 75;
+  public static final int MSGID_ACI_TARGETATTR_INVALID_ATTR_TOKEN =
+          CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_SEVERE_WARNING | 74;
+
 
   /**
    * The message ID for the message that will be used if a roledn
@@ -763,7 +756,7 @@
    * This takes one argument, which is the roledn expression string.
    */
   public static final int MSGID_ACI_SYNTAX_ROLEDN_NOT_SUPPORTED =
-       CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_SEVERE_WARNING | 76;
+       CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_SEVERE_WARNING | 75;
 
    /**
    * The message ID for the message that will be used if there are ACI decode
@@ -772,7 +765,7 @@
    * exception.
    */
   public static final int MSGID_ACI_SERVER_DECODE_FAILED =
-       CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_SEVERE_WARNING | 77;
+       CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_SEVERE_WARNING | 76;
 
 
    /**
@@ -781,7 +774,7 @@
    * causing the server is being put in lockdown mode. The takes no arguments.
    */
   public static final int MSGID_ACI_ENTER_LOCKDOWN_MODE =
-       CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_SEVERE_WARNING | 78;
+       CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_SEVERE_WARNING | 77;
 
 
     /**
@@ -1206,17 +1199,11 @@
           "Selfwrite check skipped because an attribute \"%s\" with a " +
           "distinguished name syntax was not a valid DN");
 
-      registerMessage(MSGID_ACI_TARGETATTR_INVALID_OP_USER_ATTR,
+      registerMessage(MSGID_ACI_TARGETATTR_INVALID_ATTR_TOKEN,
               "The provided Access Control Instruction (ACI) " +
               "targetattr expression value \"%s\" is invalid because" +
-              " the expression contains both operational attribute types" +
-              " and user attribute types");
+              " the expression contains invalid or duplicate tokens");
 
-      registerMessage(MSGID_ACI_TARGATTR_INVALID_OP_ATTR_INEQUALITY,
-              "The provided Access Control Instruction (ACI) " +
-              "targetattr expression value \"%s\" is invalid because" +
-              " the expression performs an inequality operation using " +
-              "operational attribute types");
 
       registerMessage(MSGID_ACI_SYNTAX_ROLEDN_NOT_SUPPORTED,
               "The provided Access Control Instruction (ACI) expression " +
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/authorization/dseecompat/TargetAttrTestCase.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/authorization/dseecompat/TargetAttrTestCase.java
index fe96c12..cfc978b 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/authorization/dseecompat/TargetAttrTestCase.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/authorization/dseecompat/TargetAttrTestCase.java
@@ -44,9 +44,46 @@
   private static final String user3="uid=user.3,ou=People,o=test";
   public  static final String aciFilter = "(aci=*)";
 
+
+  private static final
+  String starAciAttrs = "(targetattr=\"* || aci\")" +
+          "(version 3.0;acl \"read/search all user, aci op\";" +
+          "allow (search, read) " +
+          "userattr=\"l#Austin\";)";
+
+  private static final
+  String ocOpAttrs = "(targetattr=\"objectclass || +\")" +
+          "(version 3.0;acl \"read/search all op, oc user\";" +
+          "allow (search, read) " +
+          "userattr=\"l#Austin\";)";
+
+  private static final
+  String OpSrchAttrs = "(targetattr=\"sn || uid || +\")" +
+          "(version 3.0;acl \"read/search all op, sn uid user\";" +
+          "allow (search, read) " +
+          "userattr=\"l#Austin\";)";
+
+  private static final
+  String allAttrs = "(targetattr=\"* || +\")" +
+          "(version 3.0;acl \"read/search all user and all op lattr\";" +
+          "allow (search, read) " +
+          "userattr=\"l#Austin\";)";
+
+  private static final
+  String allOpAttrAci1 = "(targetattr=\"+\")" +
+          "(version 3.0;acl \"read/search all op attr\";" +
+          "allow (search, read) " +
+          "userattr!=\"l#New York\";)";
+
+  private static final
+  String notAllOpAttrAci1 = "(targetattr!=\"+\")" +
+          "(version 3.0;acl \"read/search not all op attr\";" +
+          "allow (search, read) " +
+          "userattr!=\"l#New York\";)";
+
   private static final
   String userAttrAci = "(targetattr=\"*\")" +
-          "(version 3.0;acl \"read/search userattr\";" +
+          "(version 3.0;acl \"read/search all userattr\";" +
           "allow (search, read) " +
           "userattr=\"l#Austin\";)";
 
@@ -164,6 +201,129 @@
     deleteAttrFromEntry(user1, "aci");
   }
 
+  /**
+   * Test targetattr shorthand behavior, all attrs both user and operational.
+   * See comments.
+   *
+   * @throws Exception  If a test result is unexpected.
+   */
+  @Test()
+  public void testTargetAttrAllAttr() throws Exception {
+    //Add aci with: (targetattr = "+ || *")
+    String aciLdif=makeAddAciLdif("aci", user1, allAttrs);
+    modEntries(aciLdif, DIR_MGR_DN, PWD);
+    String userResults =
+            LDAPSearchParams(user3, PWD, null, null, null,
+                    user1, filter, opAttrList);
+    Assert.assertFalse(userResults.equals(""));
+    HashMap<String, String> attrMap=getAttrMap(userResults);
+    //All should be returned.
+    Assert.assertTrue(attrMap.containsKey("aci"));
+    Assert.assertTrue(attrMap.containsKey("sn"));
+    Assert.assertTrue(attrMap.containsKey("uid"));
+    deleteAttrFromEntry(user1, "aci");
+  }
+
+
+  /**
+   * Test targetattr shorthand behavior, userattr and plus sign (all op attrs).
+   * See comments.
+   *
+   * @throws Exception If a test result is unexpected.
+   */
+  @Test()
+  public void testTargetAttrOpPlusAttr() throws Exception {
+    //Add aci with: (targetattr = "objectclass|| +")
+    String aciLdif=makeAddAciLdif("aci", user1, ocOpAttrs);
+    modEntries(aciLdif, DIR_MGR_DN, PWD);
+    String userResults =
+            LDAPSearchParams(user3, PWD, null, null, null,
+                    user1, filter, opAttrList);
+    Assert.assertFalse(userResults.equals(""));
+    HashMap<String, String> attrMap=getAttrMap(userResults);
+    //Only aci should be returned.
+    Assert.assertTrue(attrMap.containsKey("aci"));
+    Assert.assertFalse(attrMap.containsKey("sn"));
+    Assert.assertFalse(attrMap.containsKey("uid"));
+    deleteAttrFromEntry(user1, "aci");
+  }
+
+
+  /**
+   * Test targetattr shorthand behavior, star (all user attr) or aci attr.
+   * See comments.
+   *
+   * @throws Exception  If a test result is unexpected.
+   */
+  @Test()
+  public void testTargetAttrUserStarAttr() throws Exception {
+    //Add aci with: (targetattr = "*|| aci")
+    String aciLdif=makeAddAciLdif("aci", user1, starAciAttrs);
+    modEntries(aciLdif, DIR_MGR_DN, PWD);
+    String userResults =
+            LDAPSearchParams(user3, PWD, null, null, null,
+                    user1, filter, opAttrList);
+    Assert.assertFalse(userResults.equals(""));
+    HashMap<String, String> attrMap=getAttrMap(userResults);
+    //All should be returned.
+    Assert.assertTrue(attrMap.containsKey("aci"));
+    Assert.assertTrue(attrMap.containsKey("sn"));
+    Assert.assertTrue(attrMap.containsKey("uid"));
+    deleteAttrFromEntry(user1, "aci");
+  }
+
+  /**
+   * Test targetattr shorthand behavior using '+' in expression and an
+   * operational attribute in the filter. The second test is two ACIs one
+   * with targetattr='+' and the other with targetattr='*'.
+   *
+   * @throws Exception If test result is unexpected.
+   */
+  @Test()
+  public void testTargetAttrSrchShorthand() throws Exception {
+    //Aci: (targetattrs="sn || uid || +) and search with an
+    //operational attr (aci).
+    String aciLdif=makeAddAciLdif("aci", user1, OpSrchAttrs);
+    modEntries(aciLdif, DIR_MGR_DN, PWD);
+    String userResults =
+            LDAPSearchParams(user3, PWD, null, null, null,
+                    user1, aciFilter, opAttrList);
+    Assert.assertFalse(userResults.equals(""));
+    HashMap<String, String> attrMap=getAttrMap(userResults);
+    //All should be returned.
+    Assert.assertTrue(attrMap.containsKey("aci"));
+    Assert.assertTrue(attrMap.containsKey("sn"));
+    Assert.assertTrue(attrMap.containsKey("uid"));
+    deleteAttrFromEntry(user1, "aci");
+    //Add two ACIs, one with '+' and the other with '*'.
+    String aciLdif1=makeAddAciLdif("aci", user1, allOpAttrAci1, userAttrAci);
+    modEntries(aciLdif1, DIR_MGR_DN, PWD);
+    String userResults1 =
+            LDAPSearchParams(user3, PWD, null, null, null,
+                    user1, aciFilter, opAttrList);
+    Assert.assertFalse(userResults1.equals(""));
+    HashMap<String, String> attrMap1=getAttrMap(userResults1);
+    //All should be returned.
+    Assert.assertTrue(attrMap1.containsKey("aci"));
+    Assert.assertTrue(attrMap1.containsKey("sn"));
+    Assert.assertTrue(attrMap1.containsKey("uid"));
+    deleteAttrFromEntry(user1, "aci");
+        //Add two ACIs, one with '+' and the other with '*'.
+    String aciLdif2=makeAddAciLdif("aci", user1, notAllOpAttrAci1, userAttrAci);
+    modEntries(aciLdif2, DIR_MGR_DN, PWD);
+    String userResults2 =
+            LDAPSearchParams(user3, PWD, null, null, null,
+                    user1, filter, opAttrList);
+    Assert.assertFalse(userResults2.equals(""));
+    HashMap<String, String> attrMap2=getAttrMap(userResults2);
+    //Only non-operation should be returned.
+    Assert.assertFalse(attrMap2.containsKey("aci"));
+    Assert.assertTrue(attrMap2.containsKey("sn"));
+    Assert.assertTrue(attrMap2.containsKey("uid"));
+    deleteAttrFromEntry(user1, "aci");
+  }
+
+
   private void
   checkAttributeVal(HashMap<String, String> attrMap, String attr,
                       String val) throws Exception {

--
Gitblit v1.10.0