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 }