/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at * trunk/opends/resource/legal-notices/OpenDS.LICENSE * or https://OpenDS.dev.java.net/OpenDS.LICENSE. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, * add the following below this CDDL HEADER, with the fields enclosed * by brackets "[]" replaced with your own identifying * information: * Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * * Portions Copyright 2007 Sun Microsystems, Inc. */ package org.opends.server.authorization.dseecompat; import static org.opends.server.authorization.dseecompat.AciMessages.*; import static org.opends.server.messages.MessageHandler.getMessage; import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * This class represents the body of an ACI. The body of the ACI is the * version, name, and permission-bind rule pairs. */ public class AciBody { private static final int VERSION = 1; private static final int NAME = 2; private static final int PERM = 1; private static final int RIGHTS = 2; private static final int BINDRULE = 3; private int startPos=0; /* * The name of the ACI, currently not used but parsed. */ private String name = null; /* * The version of the ACi, current not used but parsed and checked * for 3.0. */ private String version = null; /* This structure represents a permission-bind rule pairs. There can be several of these. */ private List permBindRulePairs; /* * TODO Define constants for these regular expressions to make them more * readable. * The regular expressions would probably be a lot easier * to understand if you defined a number of constants for the * individual components and then concatenated them. For example, * "\\s*" could be defined in a constant named ZERO_OR_MORE_SPACES. * This would also help make it easier to understand which parentheses * were part of the regex and which were part of the ACI syntax. */ private static final String permissionRegex = "(\\w+)\\s*\\(([^()]+)\\)"; private static final String bindRuleRegex = "(.+?\"[)]*)\\s*;"; private static final String actionRegex = "\\s*" + permissionRegex + "\\s*" + bindRuleRegex; private static final String versionRegex = "(\\d\\.\\d)"; private static final String versionToken = "(?i)version"; private static final String aclToken = "(?i)acl"; /** * Regular expression used to parse the body of an ACI. */ public static final String bodyRegx = "\\(\\s*" + versionToken + "\\s*" + versionRegex + "\\s*;\\s*" + aclToken + "\\s*\"(.*)\"\\s*;\\s*" + actionRegex + "\\s*\\)"; /** * Regular expression used to parse the header of the ACI body. The * header is version and acl name. */ public static final String header = "\\(\\s*" + versionToken + "\\s*" + versionRegex + "\\s*;\\s*" + aclToken + "\\s*\"(.*?)\"\\s*;"; /** * Construct an ACI body from the specified version, name and * permission-bind rule pairs. * * @param verision The version of the ACI. * @param name The name of the ACI. * @param startPos The start position in the string of the ACI body. * @param permBindRulePairs The set of fully parsed permission-bind rule * pairs pertaining to this ACI. */ private AciBody(String verision, String name, int startPos, List permBindRulePairs) { this.version=verision; this.name=name; this.startPos=startPos; this.permBindRulePairs=permBindRulePairs; } /** * Decode an ACI string representing the ACI body. * * @param input String representation of the ACI body. * @return An AciBody class representing the decoded ACI body string. * @throws AciException If the provided string contains errors. */ public static AciBody decode(String input) throws AciException { String version=null, name=null; int startPos=0; List permBindRulePairs= new ArrayList(); Pattern bodyPattern = Pattern.compile(header); Matcher bodyMatcher = bodyPattern.matcher(input); if(bodyMatcher.find()) { startPos=bodyMatcher.start(); version = bodyMatcher.group(VERSION); if (!version.equalsIgnoreCase(Aci.supportedVersion)) { int msgID = MSGID_ACI_SYNTAX_INVAILD_VERSION; String message = getMessage(msgID, version); throw new AciException(msgID, message); } name = bodyMatcher.group(NAME); } Pattern bodyPattern1 = Pattern.compile(actionRegex); Matcher bodyMatcher1 = bodyPattern1.matcher(input); /* * The may be many permission-bind rule pairs. */ while(bodyMatcher1.find()) { String perm=bodyMatcher1.group(PERM); String rights=bodyMatcher1.group(RIGHTS); String bRule=bodyMatcher1.group(BINDRULE); PermBindRulePair pair = PermBindRulePair.decode(perm, rights, bRule); permBindRulePairs.add(pair); } return new AciBody(version, name, startPos, permBindRulePairs); } /** * Checks all of the permissions in this body for a specific access type. * Need to walk down each permission-bind rule pair and call it's * hasAccessType method. * * @param accessType The access type enumeration to search for. * @return True if the access type is found in a permission of * a permission bind rule pair. */ public boolean hasAccessType(EnumAccessType accessType) { Listpairs=getPermBindRulePairs(); for(PermBindRulePair p : pairs) { if(p.hasAccessType(accessType)) return true; } return false; } /** * Search through each permission bind rule associated with this body and * try and match a single right of the specified rights. * * @param rights The rights that are used in the match. * @return True if a one or more right of the specified rights matches * a body's permission rights. */ public boolean hasRights(int rights) { Listpairs=getPermBindRulePairs(); for(PermBindRulePair p : pairs) { if(p.hasRights(rights)) return true; } return false; } /** * Retrieve the permission-bind rule pairs of this ACI body. * * @return The permission-bind rule pairs. */ private List getPermBindRulePairs() { return permBindRulePairs; } /** * Get the start position in the ACI string of the ACI body. * * @return Index into the ACI string of the ACI body. */ public int getMatcherStartPos() { return startPos; } //TODO Evaluate adding support for the "absolute" deny access // type precedence operator. /** * Performs an evaluation of the permission-bind rule pairs * using the evaluation context. The method walks down * each PermBindRulePair object and: * * 1. Skips a pair if the evaluation context rights don't * apply to that ACI. For example, an LDAP search would skip * an ACI pair that allows writes. * * 2. The pair's bind rule is evaluated using the evaluation context. * 3. The result of the evaluation is itself evaluated. See comments * below in the code. * * @param evalCtx The evaluation context to evaluate against. * @return An enumeration result of the evaluation. */ public EnumEvalResult evaluate(AciEvalContext evalCtx) { EnumEvalResult res=EnumEvalResult.FALSE; Listpairs=getPermBindRulePairs(); for(PermBindRulePair p : pairs) { if(!p.hasRights(evalCtx.getRights())) continue; res=p.getBindRule().evaluate(evalCtx); // The evaluation result could be FAIL. Stop processing and return //FAIL. Maybe an internal search failed. if((res != EnumEvalResult.TRUE) && (res != EnumEvalResult.FALSE)) { res=EnumEvalResult.FAIL; break; //If the access type is DENY and the pair evaluated to TRUE, //then stop processing and return TRUE. A deny pair //succeeded. } else if((p.hasAccessType(EnumAccessType.DENY)) && (res == EnumEvalResult.TRUE)) { res=EnumEvalResult.TRUE; break; //An allow access type evaluated TRUE, stop processing //and return TRUE. } else if((p.hasAccessType(EnumAccessType.ALLOW) && (res == EnumEvalResult.TRUE))) { res=EnumEvalResult.TRUE; break; } } return res; } }