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 }