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.ArrayList;
034    import java.util.List;
035    import java.util.regex.Matcher;
036    import java.util.regex.Pattern;
037    
038    /**
039     * This class represents the body of an ACI. The body of the ACI is the
040     * version, name, and permission-bind rule pairs.
041     */
042    public class AciBody {
043    
044        /*
045         * Regular expression group position for the version string.
046         */
047        private static final int VERSION = 1;
048    
049        /*
050         * Regular expression group position for the name string.
051         */
052        private static final int NAME = 2;
053    
054        /*
055         * Regular expression group position for the permission string.
056         */
057        private static final int PERM = 1;
058    
059        /*
060         * Regular expression group position for the rights string.
061         */
062        private static final int RIGHTS = 2;
063    
064        /*
065         * Regular expression group position for the bindrule string.
066         */
067        private static final int BINDRULE = 3;
068    
069        /*
070         * Index into the ACI string where the ACI body starts.
071         */
072        private int startPos=0;
073    
074        /*
075        * The name of the ACI, currently not used but parsed.
076        */
077        private String name = null;
078    
079        /*
080        * The version of the ACi, current not used but parsed and checked
081        * for 3.0.
082        */
083        private String version = null;
084    
085        /*
086         This structure represents a permission-bind rule pairs. There can be
087         several of these.
088        */
089        private List<PermBindRulePair> permBindRulePairs;
090    
091        /*
092         * Regular expression used to match the access type group (allow, deny) and
093         * the rights group "(read, write, ...)". The last pattern looks for a group
094         * surrounded by parenthesis. The group must contain at least one
095         * non-paren character.
096         */
097        private static final
098        String permissionRegex =
099                   WORD_GROUP + ZERO_OR_MORE_WHITESPACE + "\\(([^()]+)\\)";
100    
101        /*
102         * Regular expression that matches a bind rule group at a coarse level. It
103         * matches any character one or more times, a single quotation and
104         * an optional right parenthesis.
105         */
106        private static final String bindRuleRegex =
107                "(.+?\"[)]*)" + ACI_STATEMENT_SEPARATOR;
108    
109        /*
110         * Regular expression used to match the actions of the ACI. The actions
111         * are permissions and matching bind rules.
112         */
113        private static final String actionRegex =
114                ZERO_OR_MORE_WHITESPACE + permissionRegex +
115                ZERO_OR_MORE_WHITESPACE + bindRuleRegex;
116    
117        /*
118         * Regular expression used to match the version value (digit.digit).
119         */
120        private static final String versionRegex = "(\\d\\.\\d)";
121    
122        /*
123         * Regular expression used to match the version token. Case insensitive.
124         */
125        private static final String versionToken = "(?i)version(?-i)";
126    
127        /*
128         * Regular expression used to match the acl token. Case insensitive.
129         */
130        private static final String aclToken = "(?i)acl(?-i)";
131    
132        /**
133         * Regular expression used to match the body of an ACI. This pattern is
134         * a general verification check.
135         */
136        public static final String bodyRegx =
137            "\\(" + ZERO_OR_MORE_WHITESPACE + versionToken +
138            ZERO_OR_MORE_WHITESPACE + versionRegex +
139            ACI_STATEMENT_SEPARATOR + aclToken + ZERO_OR_MORE_WHITESPACE +
140            "\"(.*)\"" + ACI_STATEMENT_SEPARATOR + actionRegex +
141            ZERO_OR_MORE_WHITESPACE  + "\\)";
142    
143        /*
144         * Regular expression used to match the header of the ACI body. The
145         * header is version and acl name.
146         */
147        private static final String header =
148           OPEN_PAREN + ZERO_OR_MORE_WHITESPACE + versionToken +
149           ZERO_OR_MORE_WHITESPACE +
150           versionRegex + ACI_STATEMENT_SEPARATOR + aclToken +
151           ZERO_OR_MORE_WHITESPACE +  "\"(.*?)\"" + ACI_STATEMENT_SEPARATOR;
152    
153        /**
154         * Construct an ACI body from the specified version, name and
155         * permission-bind rule pairs.
156         *
157         * @param verision The version of the ACI.
158         * @param name The name of the ACI.
159         * @param startPos The start position in the string of the ACI body.
160         * @param permBindRulePairs The set of fully parsed permission-bind rule
161         * pairs pertaining to this ACI.
162         */
163        private AciBody(String verision, String name, int startPos,
164                List<PermBindRulePair> permBindRulePairs) {
165            this.version=verision;
166            this.name=name;
167            this.startPos=startPos;
168            this.permBindRulePairs=permBindRulePairs;
169        }
170    
171        /**
172         * Decode an ACI string representing the ACI body.
173         *
174         * @param input String representation of the ACI body.
175         * @return An AciBody class representing the decoded ACI body string.
176         * @throws AciException If the provided string contains errors.
177         */
178        public static AciBody decode(String input)
179        throws AciException {
180            String version=null, name=null;
181            int startPos=0;
182            List<PermBindRulePair> permBindRulePairs=
183                    new ArrayList<PermBindRulePair>();
184            Pattern bodyPattern = Pattern.compile(header);
185            Matcher bodyMatcher = bodyPattern.matcher(input);
186            if(bodyMatcher.find()) {
187                startPos=bodyMatcher.start();
188                version  = bodyMatcher.group(VERSION);
189                if (!version.equalsIgnoreCase(supportedVersion)) {
190                    Message message = WARN_ACI_SYNTAX_INVAILD_VERSION.get(version);
191                    throw new AciException(message);
192                }
193                name = bodyMatcher.group(NAME);
194            }
195            Pattern bodyPattern1 = Pattern.compile(actionRegex);
196            Matcher bodyMatcher1 = bodyPattern1.matcher(input);
197            /*
198             * The may be many permission-bind rule pairs.
199             */
200            while(bodyMatcher1.find()) {
201             String perm=bodyMatcher1.group(PERM);
202             String rights=bodyMatcher1.group(RIGHTS);
203             String bRule=bodyMatcher1.group(BINDRULE);
204             PermBindRulePair pair = PermBindRulePair.decode(perm, rights, bRule);
205             permBindRulePairs.add(pair);
206            }
207            return new AciBody(version, name, startPos, permBindRulePairs);
208        }
209    
210        /**
211         * Checks all of the permissions in this body for a specific access type.
212         * Need to walk down each permission-bind rule pair and call it's
213         * hasAccessType method.
214         *
215         * @param accessType The access type enumeration to search for.
216         * @return True if the access type is found in a permission of
217         * a permission bind rule pair.
218         */
219        public boolean hasAccessType(EnumAccessType accessType) {
220            List<PermBindRulePair>pairs=getPermBindRulePairs();
221             for(PermBindRulePair p : pairs) {
222                 if(p.hasAccessType(accessType))
223                     return true;
224             }
225             return false;
226        }
227    
228        /**
229         * Search through each permission bind rule associated with this body and
230         * try and match a single right of the specified rights.
231         *
232         * @param rights The rights that are used in the match.
233         * @return True if a one or more right of the specified rights matches
234         * a body's permission rights.
235         */
236        public boolean hasRights(int rights) {
237            List<PermBindRulePair>pairs=getPermBindRulePairs();
238            for(PermBindRulePair p : pairs) {
239                if(p.hasRights(rights))
240                    return true;
241            }
242            return false;
243        }
244    
245        /**
246         * Retrieve the permission-bind rule pairs of this ACI body.
247         *
248         * @return The permission-bind rule pairs.
249         */
250        private List<PermBindRulePair> getPermBindRulePairs() {
251            return permBindRulePairs;
252        }
253    
254        /**
255         * Get the start position in the ACI string of the ACI body.
256         *
257         * @return Index into the ACI string of the ACI body.
258         */
259        public int getMatcherStartPos() {
260            return startPos;
261        }
262    
263        /**
264         * Performs an evaluation of the permission-bind rule pairs
265         * using the evaluation context. The method walks down
266         * each PermBindRulePair object and:
267         *
268         *  1. Skips a pair if the evaluation context rights don't
269         *     apply to that ACI. For example, an LDAP search would skip
270         *     an ACI pair that allows writes.
271         *
272         *  2. The pair's bind rule is evaluated using the evaluation context.
273         *  3. The result of the evaluation is itself evaluated. See comments
274         *     below in the code.
275         *
276         * @param evalCtx The evaluation context to evaluate against.
277         * @return An enumeration result of the evaluation.
278         */
279        public  EnumEvalResult evaluate(AciEvalContext evalCtx) {
280            EnumEvalResult res=EnumEvalResult.FALSE;
281            List<PermBindRulePair>pairs=getPermBindRulePairs();
282            for(PermBindRulePair p : pairs) {
283                if(evalCtx.isDenyEval() &&
284                        (p.hasAccessType(EnumAccessType.ALLOW)))
285                    continue;
286                if(!p.hasRights(getEvalRights(evalCtx)))
287                    continue;
288                res=p.getBindRule().evaluate(evalCtx);
289                // The evaluation result could be FAIL. Stop processing and return
290                //FAIL. Maybe an internal search failed.
291                if((res != EnumEvalResult.TRUE) &&
292                        (res != EnumEvalResult.FALSE)) {
293                    res=EnumEvalResult.FAIL;
294                    break;
295                    //If the access type is DENY and the pair evaluated to TRUE,
296                    //then stop processing and return TRUE. A deny pair
297                    //succeeded.
298                } else if((p.hasAccessType(EnumAccessType.DENY)) &&
299                        (res == EnumEvalResult.TRUE)) {
300                    res=EnumEvalResult.TRUE;
301                    break;
302                    //An allow access type evaluated TRUE, stop processing
303                    //and return TRUE.
304                } else if((p.hasAccessType(EnumAccessType.ALLOW) &&
305                        (res == EnumEvalResult.TRUE))) {
306                    res=EnumEvalResult.TRUE;
307                    break;
308                }
309            }
310            return res;
311        }
312    
313      /**
314       * Returns the name string.
315       * @return The name string.
316       */
317      public String getName() {
318          return this.name;
319        }
320    
321    
322      /**
323       * Mainly used because geteffectiverights adds flags to the rights that aren't
324       * needed in the actual evaluation of the ACI. This routine returns only the
325       * rights needed in the evaluation. The order does matter, ACI_SELF evaluation
326       * needs to be before ACI_WRITE.
327       *
328       * @param evalCtx  The evaluation context to determine the rights of.
329       * @return  The evaluation rights to used in the evaluation.
330       */
331      private int getEvalRights(AciEvalContext evalCtx) {
332        if(evalCtx.hasRights(ACI_WRITE) &&
333                evalCtx.hasRights(ACI_SELF))
334          return ACI_SELF;
335        else  if(evalCtx.hasRights(ACI_COMPARE))
336          return ACI_COMPARE;
337        else if(evalCtx.hasRights(ACI_SEARCH))
338          return ACI_SEARCH;
339        else if(evalCtx.hasRights(ACI_READ))
340          return ACI_READ;
341        else if(evalCtx.hasRights(ACI_DELETE))
342          return ACI_DELETE;
343        else if(evalCtx.hasRights(ACI_ADD))
344          return ACI_ADD;
345        else if(evalCtx.hasRights(ACI_WRITE))
346          return ACI_WRITE;
347        else if(evalCtx.hasRights(ACI_PROXY))
348          return ACI_PROXY;
349        else if(evalCtx.hasRights(ACI_IMPORT))
350          return ACI_IMPORT;
351        else if(evalCtx.hasRights(ACI_EXPORT))
352          return ACI_EXPORT;
353        return ACI_NULL;
354      }
355    
356      /**
357       * Return version string of the ACI.
358       *
359       * @return The ACI version string.
360       */
361      public String getVersion () {
362        return version;
363      }
364    }