001    /*
002     * CDDL HEADER START
003     *
004     * The contents of this file are subject to the terms of the
005     * Common Development and Distribution License, Version 1.0 only
006     * (the "License").  You may not use this file except in compliance
007     * with the License.
008     *
009     * You can obtain a copy of the license at
010     * trunk/opends/resource/legal-notices/OpenDS.LICENSE
011     * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
012     * See the License for the specific language governing permissions
013     * and limitations under the License.
014     *
015     * When distributing Covered Code, include this CDDL HEADER in each
016     * file and include the License file at
017     * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
018     * add the following below this CDDL HEADER, with the fields enclosed
019     * by brackets "[]" replaced with your own identifying information:
020     *      Portions Copyright [yyyy] [name of copyright owner]
021     *
022     * CDDL HEADER END
023     *
024     *
025     *      Copyright 2008 Sun Microsystems, Inc.
026     */
027    
028    package org.opends.server.authorization.dseecompat;
029    import org.opends.messages.Message;
030    
031    import static org.opends.messages.AccessControlMessages.*;
032    import static org.opends.server.authorization.dseecompat.Aci.*;
033    import java.util.HashSet;
034    import java.util.regex.Pattern;
035    import org.opends.server.core.DirectoryServer;
036    import org.opends.server.types.AttributeType;
037    
038    /**
039     * A class representing an ACI's targetattr keyword.
040     */
041    public class TargetAttr {
042        /*
043         * Enumeration representing the targetattr operator.
044         */
045        private EnumTargetOperator operator = EnumTargetOperator.EQUALITY;
046    
047        /*
048         * Flags that is set if all user attributes pattern seen "*".
049         */
050        private boolean allUserAttributes = false ;
051    
052        /*
053         * Flags that is set if all operational attributes pattern seen "+".
054         */
055        private boolean allOpAttributes = false ;
056    
057        /*
058         * HashSet of the attribute types parsed by the constructor.
059         */
060        private HashSet<AttributeType> attributes = new HashSet<AttributeType>();
061    
062        /**
063         * HashSet of the operational attribute types parsed by the constructor.
064         */
065        private HashSet<AttributeType> opAttributes = new HashSet<AttributeType>();
066    
067        /*
068         * Regular expression that matches one or more ATTR_NAME's separated by
069         * the "||" token.
070         */
071        private static final String attrListRegex  =  ZERO_OR_MORE_WHITESPACE +
072               ATTR_NAME + ZERO_OR_MORE_WHITESPACE + "(" +
073                LOGICAL_OR + ZERO_OR_MORE_WHITESPACE + ATTR_NAME +
074                ZERO_OR_MORE_WHITESPACE + ")*";
075    
076        /**
077         * Constructor creating a class representing a targetattr keyword of an ACI.
078         * @param operator The operation enumeration of the targetattr
079         * expression (=, !=).
080         * @param attrString A string representing the attributes specified in
081         * the targetattr expression (ie, dn || +).
082         * @throws AciException If the attrs string is invalid.
083         */
084        private TargetAttr(EnumTargetOperator operator, String attrString)
085        throws AciException {
086            this.operator = operator;
087            if (attrString != null) {
088                if (Pattern.matches(ALL_USER_ATTRS_WILD_CARD, attrString) )
089                    allUserAttributes = true ;
090                else  if (Pattern.matches(ALL_OP_ATTRS_WILD_CARD, attrString) )
091                    allOpAttributes = true ;
092                else {
093                    if (Pattern.matches(ZERO_OR_MORE_WHITESPACE, attrString)){
094                        allUserAttributes = false;
095                        allOpAttributes=false;
096                    } else {
097                        if (Pattern.matches(attrListRegex, attrString)) {
098                            // Remove the spaces in the attr string and
099                            // split the list.
100                            Pattern separatorPattern =
101                                Pattern.compile(LOGICAL_OR);
102                            attrString=
103                             attrString.replaceAll(ZERO_OR_MORE_WHITESPACE, "");
104                            String[] attributeArray=
105                                 separatorPattern.split(attrString);
106                            //Add each element of array to appropriate HashSet
107                            //after conversion to AttributeType.
108                            arrayToAttributeTypes(attributeArray, attrString);
109                        } else {
110                          Message message =
111                              WARN_ACI_SYNTAX_INVALID_TARGETATTRKEYWORD_EXPRESSION.
112                                get(attrString);
113                          throw new AciException(message);
114                        }
115                    }
116                }
117            }
118        }
119    
120    
121        /**
122         * Converts each element of an array of attribute strings
123         * to attribute types and adds them to either the user attributes HashSet or
124         * the operational attributes HashSet. Also, scan for the shorthand tokens
125         * "*" for all user attributes and "+" for all operational attributes.
126         *
127         * @param attributeArray The array of attribute type strings.
128         * @param attrStr String used in error message if an Aci Exception
129         *                is thrown.
130         * @throws AciException If the one of the attribute checks fails.
131         */
132        private void arrayToAttributeTypes(String[] attributeArray, String attrStr)
133                throws AciException {
134            for (int i=0, n=attributeArray.length; i < n; i++) {
135                String attribute=attributeArray[i].toLowerCase();
136                if(attribute.equals("*")) {
137                    if(!allUserAttributes)
138                        allUserAttributes=true;
139                    else {
140                        Message message =
141                            WARN_ACI_TARGETATTR_INVALID_ATTR_TOKEN.get(attrStr);
142                        throw new AciException(message);
143                    }
144                } else if(attribute.equals("+")) {
145                    if(!allOpAttributes)
146                        allOpAttributes=true;
147                    else {
148                        Message message =
149                            WARN_ACI_TARGETATTR_INVALID_ATTR_TOKEN.get(attrStr);
150                        throw new AciException(message);
151                    }
152                } else {
153                    AttributeType attributeType;
154                    if((attributeType =
155                            DirectoryServer.getAttributeType(attribute)) == null)
156                        attributeType =
157                                DirectoryServer.getDefaultAttributeType(attribute);
158                    if(attributeType.isOperational())
159                        opAttributes.add(attributeType);
160                    else
161                        attributes.add(attributeType);
162                }
163            }
164        }
165    
166        /**
167         * Returns the operator enumeration of the targetattr expression.
168         * @return The operator enumeration.
169         */
170        public EnumTargetOperator getOperator() {
171            return operator;
172        }
173    
174        /**
175         * This flag is set if the parsing code saw:
176         * targetattr="*" or targetattr != "*".
177         * @return True if all attributes was seen.
178         */
179        public boolean isAllUserAttributes() {
180            return allUserAttributes;
181        }
182    
183    
184        /**
185         * This flag is set if the parsing code saw:
186         * targetattr="+" or targetattr != "+".
187         * @return True if all attributes was seen.
188         */
189        public boolean isAllOpAttributes() {
190            return allOpAttributes;
191        }
192    
193        /**
194         * Return array holding each attribute type to be evaluated
195         * in the expression.
196         * @return Array holding each attribute types.
197         */
198        public HashSet<AttributeType> getAttributes() {
199            return attributes;
200        }
201    
202      /**
203       * Return array holding  operational attribute types to be evaluated
204       * in the expression.
205       * @return  Array holding attribute types.
206       */
207      public HashSet<AttributeType> getOpAttributes() {
208            return opAttributes;
209        }
210    
211        /**
212         * Decodes an targetattr expression string into a targetattr class suitable
213         * for evaluation.
214         * @param operator The operator enumeration of the expression.
215         * @param expr The expression string to be decoded.
216         * @return A TargetAttr suitable to evaluate this ACI's targetattrs.
217         * @throws AciException If the expression string is invalid.
218         */
219        public static TargetAttr decode(EnumTargetOperator operator, String expr)
220                throws AciException  {
221            return new TargetAttr(operator, expr);
222        }
223    
224        /**
225         * Performs test to see if the specified is applicable to the specified
226         * TargetAttr. First a check if the TargetAttr parsing code saw an
227         * expression like:
228         *
229         *  (targetattrs="+ || *), (targetattrs != "* || +)
230         *
231         * where both shorthand tokens where parsed. IF so then the attribute type
232         * matches automatically (or not matches if NOT_EQUALITY).
233         *
234         * If there isn't a match, then the method evalAttrType is called to further
235         * evaluate the attribute type and targetAttr combination.
236         *
237         *
238         * @param a The attribute type to evaluate.
239         * @param targetAttr The ACI's TargetAttr class to evaluate against.
240         * @return The boolean result of the above tests and application
241         * TargetAttr's operator value applied to the test result.
242         */
243    
244        public static boolean isApplicable(AttributeType a,
245                                           TargetAttr targetAttr) {
246            boolean ret;
247            if(targetAttr.isAllUserAttributes() && targetAttr.isAllOpAttributes()) {
248                ret =
249                  !targetAttr.getOperator().equals(EnumTargetOperator.NOT_EQUALITY);
250            } else
251                ret=evalAttrType(a, targetAttr);
252    
253            return ret;
254        }
255    
256        /**
257         * First check is to see if the attribute type is operational. If so then
258         * a match is true if the allOpAttributes boolean is true or if the
259         * attribute type is found in the operational attributes HashSet.
260         * Both results can be negated if the expression operator is NOT_EQUALITY).
261         *
262         * Second check is similar to above, except the user attributes boolean
263         * and HashSet is examined.
264         *
265         *
266         * @param a The attribute type to evaluate.
267         * @param targetAttr The targetAttr to apply to the attribute type.
268         * @return True if the attribute type is applicable to the targetAttr.
269         */
270          private static
271          boolean evalAttrType(AttributeType a, TargetAttr targetAttr) {
272            boolean ret=false;
273            if(a.isOperational()) {
274              if(targetAttr.isAllOpAttributes() ||
275                      targetAttr.opAttributes.contains(a))
276                ret=true;
277              if(targetAttr.isAllOpAttributes() ||
278                 !targetAttr.opAttributes.isEmpty()) {
279                if(targetAttr.getOperator().
280                        equals(EnumTargetOperator.NOT_EQUALITY))
281                  ret=!ret;
282              }
283            } else {
284              if(targetAttr.isAllUserAttributes() ||
285                      targetAttr.attributes.contains(a))
286                ret=true;
287              if(targetAttr.isAllUserAttributes() ||
288                      !targetAttr.attributes.isEmpty()) {
289                if(targetAttr.getOperator().
290                        equals(EnumTargetOperator.NOT_EQUALITY))
291                  ret=!ret;
292              }
293            }
294          return ret;
295          }
296      }