From 550e66541ba792fe0e8c24fac50a480f97c114e3 Mon Sep 17 00:00:00 2001
From: dugan <dugan@localhost>
Date: Wed, 21 Feb 2007 16:31:49 +0000
Subject: [PATCH] Initial commit of the dseecompat ACI code
---
opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/EnumTargetKeyword.java | 108
opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/DayOfWeek.java | 96
opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/TargetAttr.java | 187 +
opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciProvider.java | 85
opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/IpBitsNetworkCriteria.java | 200 +
opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/IpMaskNetworkCriteria.java | 140
opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciBody.java | 255 +
opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/EnumBindRuleKeyword.java | 118
opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/EnumBindRuleType.java | 104
opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/UserDN.java | 385 ++
opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/UserDNTypeURL.java | 71
opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciLDAPOperationContainer.java | 120
opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/GroupDN.java | 151 +
opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/Permission.java | 117
opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciTargetMatchContext.java | 134
opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciException.java | 107
opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/ParentInheritance.java | 172 +
opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/PermBindRulePair.java | 93
opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/UserAttr.java | 388 ++
opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/EnumTargetOperator.java | 81
opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciContainer.java | 426 ++
opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/EnumAuthMethod.java | 116
opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/EnumUserDNType.java | 84
opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/EnumBooleanTypes.java | 90
opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciEvalContext.java | 143
opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/DNS.java | 160 +
opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/EnumRight.java | 173 +
opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AuthMethod.java | 112
opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciList.java | 251 +
opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciListenerManager.java | 210 +
opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/EnumDayOfWeek.java | 154 +
opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/Aci.java | 207 +
opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/TimeOfDay.java | 121
opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciHandler.java | 759 +++++
opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/BindRule.java | 538 +++
opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/TargAttrFilters.java | 55
opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciMessages.java | 827 +++++
opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/TargetFilter.java | 103
opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciTargets.java | 472 +++
opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/KeywordBindRule.java | 44
opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/Target.java | 198 +
opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/EnumEvalResult.java | 111
opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/EnumAccessType.java | 88
opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/IpCriteria.java | 291 +
opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/RoleDN.java | 157 +
45 files changed, 9,002 insertions(+), 0 deletions(-)
diff --git a/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/Aci.java b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/Aci.java
new file mode 100644
index 0000000..9edddc5
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/Aci.java
@@ -0,0 +1,207 @@
+/*
+ * 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 org.opends.server.types.ByteString;
+import org.opends.server.types.DN;
+import static org.opends.server.messages.MessageHandler.*;
+import static org.opends.server.authorization.dseecompat.AciMessages.*;
+import java.util.regex.Pattern;
+/**
+ * The Aci class represents ACI strings.
+ */
+public class Aci {
+ /*
+ * The body of the ACI is the version, name and permission-bind rule
+ * pairs.
+ */
+ private AciBody body;
+ /*
+ * The ACI targets.
+ */
+ private AciTargets targets=null;
+ /**
+ * The ACIs are on a linked list hashed by the ACI entry DN.
+ * Next points to the next Aci object in the list.
+ */
+ /*
+ * TODO Remove this linked list an replace with an array
+ * of ACIs.
+ */
+ Aci next = null;
+ /**
+ * Version that we support.
+ */
+ public static final String supportedVersion="3.0";
+ private String aciString;
+ /*
+ * The DN of the entry containing this ACI.
+ */
+ private DN dn;
+ /*
+ * This regular expression is used to do a quick syntax check
+ * when an ACI is being decoded.
+ */
+ private static final String aciRegex =
+ "^\\s*" + AciTargets.targetsRegex + "\\s*"+
+ AciBody.bodyRegx + "\\s*$";
+
+ /**
+ * Construct a new Aci from the provided arguments.
+ * @param input The string representation of the ACI.
+ * @param dn The DN of entry containing the ACI.
+ * @param body The body of the ACI.
+ * @param targets The targets of the ACI.
+ */
+ private Aci(String input, DN dn, AciBody body, AciTargets targets) {
+ this.aciString = input;
+ this.dn=dn;
+ this.body=body;
+ this.targets=targets;
+ }
+
+ /**
+ * Decode an ACI byte string.
+ * @param byteString The ByteString containing the ACI string.
+ * @param dn DN of the ACI entry.
+ * @return Returns a decoded ACI representing the string argument.
+ * @throws AciException If the parsing of the ACI string fails.
+ */
+
+ //MPD remove ConfigException after fixing David's problem
+ public static Aci decode (ByteString byteString, DN dn)
+ throws AciException {
+ String input=byteString.stringValue();
+ //Perform an quick pattern check against the string to catch any
+ //obvious syntax errors.
+ if (!Pattern.matches(aciRegex, input)) {
+ int msgID = MSGID_ACI_SYNTAX_GENERAL_PARSE_FAILED;
+ String message = getMessage(msgID, input);
+ throw new AciException(msgID, message);
+ }
+ //Decode the body first.
+ AciBody body=AciBody.decode(input);
+ //Create a substring from the start of the string to start of
+ //the body. That should be the target.
+ String targetStr = input.substring(0, body.getMatcherStartPos());
+ //Decode that target string using the substring.
+ AciTargets targets=AciTargets.decode(targetStr, dn);
+ return new Aci(input, dn, body, targets);
+ }
+
+ /**
+ * Return the string representation of the ACI. This was the string that
+ * was used to create the Aci class.
+ * @return A string representation of the ACI.
+ */
+ public String toString() {
+ return aciString;
+ }
+
+ /**
+ * Returns the targets of the ACI.
+ * @return Any AciTargets of the ACI. There may be no targets
+ * so this might be null.
+ */
+ public AciTargets getTargets() {
+ return targets;
+ }
+
+ /**
+ * Return the DN of the entry containing the ACI.
+ * @return The DN of the entry containing the ACI.
+ */
+ public DN getDN() {
+ return dn;
+ }
+
+ /**
+ * Test if the given ACI is applicable using the target match information
+ * provided. The ACI target can have four keywords at this time:
+ *
+ * 1. target - checked in isTargetApplicable.
+ * 2. targetscope - checked in isTargetApplicable.
+ * 3. targetfilter - checked in isTargetFilterApplicable.
+ * 4. targetattr - checked in isTargetAttrApplicable.
+ *
+ * One and two are checked for match first. If they return true, then
+ * three is checked. Lastly four is checked.
+ *
+ * @param aci The ACI to test.
+ * @param matchCtx The target matching context containing all the info
+ * needed to match ACI targets.
+ * @return True if this ACI targets are applicable or match.
+ */
+ public static boolean
+ isApplicable(Aci aci, AciTargetMatchContext matchCtx) {
+ return AciTargets.isTargetApplicable(aci, matchCtx) &&
+ AciTargets.isTargetFilterApplicable(aci, matchCtx) &&
+ AciTargets.isTargetAttrApplicable(aci, matchCtx);
+ }
+
+ /**
+ * Check if the body of the ACI matches the rights specified.
+ * @param rights Bit mask representing the rights to match.
+ * @return True if the body's rights match one of the rights specified.
+ */
+ public boolean hasRights(int rights) {
+ return body.hasRights(rights);
+ }
+
+ /**
+ * Re-direct has access type to the body's hasAccessType method.
+ * @param accessType The access type to match.
+ * @return True if the body's hasAccessType determines a permission
+ * contains this access type (allow or deny are valid types).
+ */
+ public boolean hasAccessType(EnumAccessType accessType) {
+ return body.hasAccessType(accessType);
+ }
+
+ /**
+ * Evaluate this ACI using the evaluation context provided. Re-direct
+ * that calls the body's evaluate method.
+ * @param evalCtx The evaluation context to evaluate with.
+ * @return EnumEvalResult that contains the evaluation result of this
+ * aci evaluation.
+ */
+ private EnumEvalResult evaluate(AciEvalContext evalCtx) {
+ return body.evaluate(evalCtx);
+ }
+
+ /**
+ * Static class used to evaluate an ACI and evaluation context.
+ * @param evalCtx The context to evaluate with.
+ * @param aci The ACI to evaluate.
+ * @return EnumEvalResult that contains the evaluation result of the aci
+ * evaluation.
+ */
+ public static EnumEvalResult evaluate(AciEvalContext evalCtx, Aci aci) {
+ return aci.evaluate(evalCtx);
+ }
+}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciBody.java b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciBody.java
new file mode 100644
index 0000000..80e63cc
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciBody.java
@@ -0,0 +1,255 @@
+/*
+ * 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<PermBindRulePair> 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<PermBindRulePair> 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<PermBindRulePair> permBindRulePairs=
+ new ArrayList<PermBindRulePair>();
+ 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) {
+ List<PermBindRulePair>pairs=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) {
+ List<PermBindRulePair>pairs=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<PermBindRulePair> 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;
+ List<PermBindRulePair>pairs=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;
+ }
+}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciContainer.java b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciContainer.java
new file mode 100644
index 0000000..e24e9a0
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciContainer.java
@@ -0,0 +1,426 @@
+/*
+ * 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 org.opends.server.types.*;
+import org.opends.server.api.ClientConnection;
+import org.opends.server.api.Group;
+import org.opends.server.core.AddOperation;
+import org.opends.server.core.Operation;
+import org.opends.server.extensions.TLSConnectionSecurityProvider;
+import org.opends.server.util.ServerConstants;
+import java.net.InetAddress;
+import java.util.LinkedList;
+
+/**
+ * The AciContainer class contains all of the needed information to perform
+ * both target match and evaluate an ACI. Target matching is the process
+ * of testing if an ACI is applicable to an operation, and evaluation is
+ * the actual access evaluation of the ACI.
+ */
+public abstract class AciContainer
+implements AciTargetMatchContext, AciEvalContext {
+
+ /*
+ * The allow and deny lists.
+ */
+ private LinkedList<Aci> denyList, allowList;
+
+ /*
+ * The attribute type in the resource entry currently being evaluated.
+ */
+ private AttributeType attributeType;
+
+ /*
+ * The attribute type value in the resource entry currently being
+ * evaluated.
+ */
+ private AttributeValue attributeValue;
+
+ /*
+ * True if this is the first attribute type in the resource entry being
+ * evaluated.
+ */
+ private boolean isFirst = false;
+
+ /*
+ * True if an entry test rule was seen during target matching of an ACI
+ * entry. A entry test rule is an ACI with targetattrs target keyword.
+ */
+ private boolean isEntryTestRule = false;
+
+ /*
+ * True if the evaluation of an ACI is from the deny list.
+ */
+ private boolean isDenyEval;
+
+ /*
+ * True if the evaluation is a result of an LDAP add operation.
+ */
+ private boolean isAddOp=false;
+
+ /*
+ * The rights to use in the evaluation of the LDAP operation.
+ */
+ private int rights;
+
+ /*
+ * The entry being evaluated (resource entry).
+ */
+ private Entry resourceEntry;
+
+ /*
+ * The client connection information.
+ */
+ private ClientConnection clientConnection;
+
+ /*
+ * The operation being evaluated.
+ */
+ private Operation operation;
+
+ /**
+ * This constructor is used by all currently supported LDAP operations.
+ *
+ * @param operation The Operation object being evaluated and target
+ * matching.
+ * @param rights The rights array to use in evaluation and target matching.
+ * @param entry The current entry being evaluated and target matched.
+ */
+ protected AciContainer(Operation operation, int rights, Entry entry) {
+ this.resourceEntry=entry;
+ this.operation=operation;
+ this.clientConnection=operation.getClientConnection();
+ if(operation instanceof AddOperation)
+ this.isAddOp=true;
+ this.rights = rights;
+ }
+
+ /**
+ * The list of deny ACIs. These are all of the applicable
+ * ACIs that have a deny permission. Note that an ACI can
+ * be on both allow and deny list if it has multiple
+ * permission-bind rule pairs.
+ *
+ * @param denys The list of deny ACIs.
+ */
+ public void setDenyList(LinkedList<Aci> denys) {
+ denyList=denys;
+ }
+
+ /**
+ * The list of allow ACIs. These are all of the applicable
+ * ACIs that have an allow permission.
+ *
+ * @param allows The list of allow ACIs.
+ */
+ public void setAllowList(LinkedList<Aci> allows) {
+ allowList=allows;
+ }
+
+ /**
+ * Return the current attribute type being evaluated.
+ * @return Attribute type being evaluated.
+ */
+ public AttributeType getCurrentAttributeType() {
+ return attributeType;
+ }
+
+ /**
+ * Return the current attribute type value being evaluated.
+ * @return Attribute type value being evaluated.
+ */
+ public AttributeValue getCurrentAttributeValue() {
+ return attributeValue;
+ }
+
+ /**
+ * Set the attribute type to be evaluated.
+ * @param type The attribute type to evaluate.
+ */
+ public void setCurrentAttributeType(AttributeType type) {
+ attributeType=type;
+ }
+
+ /**
+ * Set the attribute type value to be evaluated.
+ * @param value The attribute type value to evaluate.
+ */
+ public void setCurrentAttributeValue(AttributeValue value) {
+ attributeValue=value;
+ }
+
+ /**
+ * Check is this the first attribute being evaluated in an entry.
+ * @return True if it is the first attribute.
+ */
+ public boolean isFirstAttribute() {
+ return isFirst;
+ }
+
+ /**
+ * Set if this is the first attribute in the entry.
+ * @param val True if this is the first attribute being evaluated in the
+ * entry.
+ */
+ public void setIsFirstAttribute(boolean val) {
+ isFirst=val;
+ }
+
+ /**
+ * Check if an entry test rule was seen during target evaluation.
+ * @return True if an entry test rule was seen.
+ */
+ public boolean hasEntryTestRule() {
+ return isEntryTestRule;
+ }
+
+ /**
+ * Used to set if an entry test rule was seen during target evaluation.
+ * @param val Set to true if an entry test rule was seen.
+ */
+ public void setEntryTestRule(boolean val) {
+ isEntryTestRule=val;
+ }
+
+ /**
+ * Get the entry being evaluated (known as the resource entry).
+ * @return The entry being evaluated.
+ */
+ public Entry getResourceEntry() {
+ return resourceEntry;
+ }
+
+ /**
+ * Get the entry that corresponds to the client DN.
+ * @return The client entry.
+ */
+ public Entry getClientEntry() {
+ return clientConnection.getAuthenticationInfo().getAuthorizationEntry();
+ }
+
+ /**
+ * Get the deny list of ACIs.
+ * @return The deny ACI list.
+ */
+ public LinkedList<Aci> getDenyList() {
+ return denyList;
+ }
+
+ /**
+ * Get the allow list of ACIs.
+ * @return The allow ACI list.
+ */
+ public LinkedList<Aci> getAllowList() {
+ return allowList;
+ }
+
+ /**
+ * Check is this is a deny ACI evaluation.
+ * @return True if the evaluation is using an ACI from
+ * deny list.
+ */
+ public boolean isDenyEval() {
+ return isDenyEval;
+ }
+
+ /**
+ * Check is this operation bound anonymously.
+ * @return True if the authentication is anonymous.
+ */
+ public boolean isAnonymousUser() {
+ return !clientConnection.getAuthenticationInfo().isAuthenticated();
+ }
+
+ /**
+ * Set the deny evaluation flag.
+ * @param val True if this evaluation is a deny ACI.
+ */
+ public void setDenyEval(boolean val) {
+ isDenyEval = val;
+ }
+
+ /**
+ * Returns the client authorization DN known as the client DN.
+ * @return The client's authorization DN.
+ */
+ public DN getClientDN() {
+ return clientConnection.getAuthenticationInfo().getAuthorizationDN();
+ }
+
+ /**
+ * Get the DN of the entry being evaluated.
+ * @return The DN of the entry.
+ */
+ public DN getResourceDN() {
+ return resourceEntry.getDN();
+ }
+
+ /**
+ * Checks if the container's rights has the specified rights.
+ * @param rights The rights to check for.
+ * @return True if the container's rights has the specified rights.
+ */
+ public boolean hasRights(int rights) {
+ return (this.rights & rights) != 0;
+ }
+
+ /**
+ * Return the rights set for this container's LDAP operation.
+ * @return The rights set for the container's LDAP operation.
+ */
+ public int getRights() {
+ return this.rights;
+ }
+
+ /**
+ * Sets the rights for this container to the specified rights.
+ * @param rights The rights to set the container's rights to.
+ */
+ public void setRights(int rights) {
+ this.rights=rights;
+ }
+ /**
+ * Gets the hostname of the remote client.
+ * @return Cannonical hostname of remote client.
+ */
+ public String getHostName() {
+ return clientConnection.getRemoteAddress().getCanonicalHostName();
+ }
+
+ /**
+ * Gets the remote client's address information.
+ * @return Remote client's address.
+ */
+ public InetAddress getRemoteAddress() {
+ return clientConnection.getRemoteAddress();
+ }
+
+ /**
+ * Return true if this is an add operation.
+ * @return True if this is an add operation.
+ */
+ public boolean isAddOperation() {
+ return isAddOp;
+ }
+
+ /**
+ * Tries to determine the authentication information from the connection
+ * class. The checks are for simple and SASL, anything else is not a
+ * match. If the bind rule needs the SSL client flag then that needs
+ * to be set. This code is used by the authmethod bind rule keyword.
+ * @param wantSSL True if the bind rule needs the ssl client auth check.
+ * @return Return an enumeration containing the authentication method
+ * for this connection.
+ */
+ public EnumAuthMethod getAuthenticationMethod(boolean wantSSL) {
+ EnumAuthMethod method=EnumAuthMethod.AUTHMETHOD_NOMATCH;
+ AuthenticationInfo authInfo=clientConnection.getAuthenticationInfo();
+ if(authInfo.isAuthenticated()) {
+ if(authInfo.hasAuthenticationType(AuthenticationType.SIMPLE))
+ method=EnumAuthMethod.AUTHMETHOD_SIMPLE;
+ else if(authInfo.hasAuthenticationType(AuthenticationType.SASL))
+ method=getSaslAuthenticationMethod(authInfo, wantSSL);
+ else
+ method=EnumAuthMethod.AUTHMETHOD_NOMATCH;
+ }
+ return method;
+ }
+
+ /*
+ * TODO This method needs to be tested.
+ * TODO Investigate multi-factor authentication.
+ * Second, OpenDS is devised so that it could be possible to use
+ * multi-factor or step-up authentication, in which the same client
+ * has provided multiple forms of credentials, but this method
+ * expects only a single authentication type.
+ */
+ /**
+ * This method attempts to figure out what the SASL method was/is or
+ * what the client auth is.
+ * @param authInfo The authentication information to use.
+ * @param wantSSL The bin drule wants the SSL client auth status.
+ * @return An enumeration containing the SASL bind information.
+ */
+ private EnumAuthMethod
+ getSaslAuthenticationMethod(AuthenticationInfo authInfo, boolean wantSSL) {
+ EnumAuthMethod method=EnumAuthMethod.AUTHMETHOD_NOMATCH;
+ if(authInfo.hasAuthenticationType(AuthenticationType.SASL)) {
+ if(authInfo.hasSASLMechanism(ServerConstants.
+ SASL_MECHANISM_DIGEST_MD5))
+ method=EnumAuthMethod.AUTHMETHOD_SASL_MD5;
+ else if(authInfo.hasSASLMechanism(ServerConstants.
+ SASL_MECHANISM_GSSAPI))
+ method=EnumAuthMethod.AUTHMETHOD_SASL_GSSAPI;
+ else if(authInfo.hasSASLMechanism(ServerConstants.
+ SASL_MECHANISM_EXTERNAL)) {
+ /*
+ * The bind rule wants ssl client auth information. Need the
+ * security provider to see if the clientAuthPolicy is
+ * required. If it is optional, we really can't determine if
+ * the client auth.
+ */
+ if(wantSSL) {
+ String mechName=
+ clientConnection.getConnectionSecurityProvider().
+ getSecurityMechanismName();
+ if(mechName.equalsIgnoreCase("TLS")) {
+ TLSConnectionSecurityProvider tlsProv=
+ (TLSConnectionSecurityProvider)clientConnection.
+ getConnectionSecurityProvider();
+ SSLClientAuthPolicy clientAuthPolicy=
+ tlsProv.getSSLClientAuthPolicy();
+ if(clientAuthPolicy == SSLClientAuthPolicy.REQUIRED)
+ method=EnumAuthMethod.AUTHMETHOD_SSL;
+ } else
+ method=EnumAuthMethod.AUTHMETHOD_NOMATCH;
+ } else {
+ method=EnumAuthMethod.AUTHMETHOD_SASL_EXTERNAL;
+ }
+ } else
+ method=EnumAuthMethod.AUTHMETHOD_NOMATCH;
+ }
+ return method;
+ }
+
+ /**
+ * Convienance method that checks if the the clientDN is a member of the
+ * specified group.
+ * @param group The group to check membership in.
+ * @return True if the clientDN is a member of the specified group.
+ */
+ public boolean isMemberOf(Group group) {
+ boolean ret;
+ try {
+ ret=clientConnection.isMemberOf(group, operation);
+ } catch (DirectoryException ex) {
+ ret=false;
+ }
+ return ret;
+ }
+}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciEvalContext.java b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciEvalContext.java
new file mode 100644
index 0000000..0e2bd70
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciEvalContext.java
@@ -0,0 +1,143 @@
+/*
+ * 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 org.opends.server.types.DN;
+import org.opends.server.types.Entry;
+import org.opends.server.api.Group;
+
+import java.net.InetAddress;
+import java.util.LinkedList;
+
+/**
+ * Interface that provides a view of the AciContainer that is
+ * used by the ACI evaluation code to evaluate an ACI.
+ */
+public interface AciEvalContext
+{
+ /**
+ * Get client DN. The client DN is the authorization DN.
+ * @return The client DN.
+ */
+ public DN getClientDN();
+
+ /**
+ * Get the client entry. The client entry is the entry that corresponds
+ * to the client DN.
+ * @return The client entry corresponding to the client DN.
+ */
+ public Entry getClientEntry();
+
+ /**
+ * Get the resource DN. The resource DN is the DN of the entry being
+ * evaluated.
+ * @return The resource DN.
+ */
+ public DN getResourceDN();
+
+ /**
+ * Get the list of deny ACIs.
+ * @return The deny ACI list.
+ */
+ public LinkedList<Aci> getDenyList();
+
+ /**
+ * Get the list allow ACIs.
+ * @return The allow ACI list.
+ */
+ public LinkedList<Aci> getAllowList();
+
+ /**
+ * Set when the deny list is being evaluated.
+ * @param v True if deny's are being evaluated.
+ */
+ public void setDenyEval(boolean v);
+
+ /**
+ * Returns true if the deny list is being evaluated.
+ * @return True if the deny list is being evaluated.
+ */
+ public boolean isDenyEval();
+
+ /**
+ * Check if the remote client is bound anonymously.
+ * @return True if client is bound anonymously.
+ */
+ public boolean isAnonymousUser();
+
+ /**
+ * Return the rights set for this container's LDAP operation.
+ * @return The rights set for the container's LDAP operation.
+ */
+ public int getRights();
+
+ /**
+ * Return the entry being evaluated
+ * .
+ * @return The evaluation entry.
+ */
+ public Entry getResourceEntry();
+
+ /**
+ * Get the hostname of the bound connection.
+ * @return The hostname of the connection.
+ */
+ public String getHostName();
+
+ /**
+ * Get the authentication method.
+ * @param wantSSL The authmethod bind rule needs the SSL client auth
+ * status.
+ * @return An Enumeration of the auth method bound as.
+ */
+ public EnumAuthMethod getAuthenticationMethod(boolean wantSSL);
+
+ /**
+ * Get the address of the bound connection.
+ * @return The address of the bound connection.
+ */
+ public InetAddress getRemoteAddress();
+
+ /**
+ * Return true if this is an add operation, needed by the userattr
+ * USERDN parent inheritance level 0 processing.
+ * @return True if this is an add operation.
+ */
+ public boolean isAddOperation();
+
+ /**
+ * Return true if the operation associated with this evaluation
+ * context is a member of the specified group. Calls the
+ * ClientConnection.isMemberOf() method, which checks authorization
+ * DN membership in the specified group.
+ * @param group The group to check membership in.
+ * @return True if the authorization DN of the operation is a
+ * member of the specified group.
+ */
+ public boolean isMemberOf(Group group);
+}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciException.java b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciException.java
new file mode 100644
index 0000000..4556ca7
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciException.java
@@ -0,0 +1,107 @@
+/*
+ * 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.loggers.Debug.debugConstructor;
+import static org.opends.server.loggers.Debug.debugEnter;
+
+/**
+ * The AciException class defines an exception that may be thrown
+ * either during ACI syntax verification of an "aci" attribute type value
+ * or during evaluation of an LDAP operation using a set of applicable
+ * ACIs.
+ */
+public class AciException extends Exception {
+
+ /**
+ * The fully-qualified name of this class for debugging purposes.
+ */
+ private static final String CLASS_NAME =
+ "org.opends.server.authorization.dseecompat.AciException";
+
+ /**
+ * The serial version identifier required to satisfy the compiler because this
+ * class extends <CODE>java.lang.Exception</CODE>, which implements the
+ * <CODE>java.io.Serializable</CODE> interface. This value was generated
+ * using the <CODE>serialver</CODE> command-line utility included with the
+ * Java SDK.
+ */
+ private static final long serialVersionUID = -2763328522960628853L;
+
+ // The unique message ID for the associated message.
+ private int messageID;
+
+ /**
+ * Constructs a new exception with <code>null</code> as its detail message.
+ * The cause is not initialized. Used to break out of a recursive bind rule
+ * decode and not print duplicate messages.
+ */
+ public AciException() {
+ super();
+ }
+
+ /**
+ * Creates a new ACI exception with the provided message.
+ *
+ * @param messageID The unique message ID for the provided message.
+ * @param message The message to use for this ACI exception.
+ */
+ public AciException(int messageID, String message) {
+ super(message);
+ assert debugConstructor(CLASS_NAME, String.valueOf(messageID),
+ String.valueOf(message));
+ this.messageID = messageID;
+ }
+
+ /**
+ * Creates a new ACI exception with the provided message and root
+ * cause.
+ *
+ * @param messageID The unique identifier for the associated message.
+ * @param message The message that explains the problem that occurred.
+ * @param cause The exception that was caught to trigger this
+ * exception.
+ */
+ public AciException(int messageID, String message, Throwable cause) {
+ super(message, cause);
+
+ assert debugConstructor(CLASS_NAME, String.valueOf(message),
+ String.valueOf(cause));
+
+ this.messageID = messageID;
+ }
+
+ /**
+ * Retrieves the message ID for this exception.
+ *
+ * @return The message ID for this exception.
+ */
+ public int getMessageID() {
+ assert debugEnter(CLASS_NAME, "getMessageID");
+ return messageID;
+ }
+}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciHandler.java b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciHandler.java
new file mode 100644
index 0000000..b69761b
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciHandler.java
@@ -0,0 +1,759 @@
+/*
+ * 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 org.opends.server.api.AccessControlHandler;
+import static org.opends.server.authorization.dseecompat.AciMessages.*;
+import org.opends.server.core.*;
+import static org.opends.server.loggers.Debug.debugEnter;
+import static org.opends.server.loggers.Error.logError;
+import static org.opends.server.messages.MessageHandler.getMessage;
+import org.opends.server.types.*;
+import static org.opends.server.util.StaticUtils.toLowerCase;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * The AciHandler class performs the main processing for the
+ * dseecompat package.
+ */
+public class AciHandler extends AccessControlHandler
+{
+
+ private static final String CLASS_NAME =
+ "org.opends.server.authorization.dseecompat.AciHandler";
+
+ /**
+ * ACI_ADD is used to set the container rights for a LDAP add operation.
+ */
+ public static final int ACI_ADD = 0x0001;
+
+ /**
+ * ACI_DELETE is used to set the container rights for a LDAP
+ * delete operation.
+ */
+ public static final int ACI_DELETE = 0x0002;
+
+ /**
+ * ACI_READ is used to set the container rights for a LDAP
+ * search operation.
+ */
+ public static final int ACI_READ = 0x0004;
+
+ /**
+ * ACI_WRITE is used to set the container rights for a LDAP
+ * modify operation.
+ */
+ public static final int ACI_WRITE = 0x0008;
+
+ /**
+ * ACI_COMPARE is used to set the container rights for a LDAP
+ * compare operation.
+ */
+ public static final int ACI_COMPARE = 0x0010;
+
+ /**
+ * ACI_SEARCH is used to set the container rights a LDAP search operation.
+ */
+ public static final int ACI_SEARCH = 0x0020;
+
+ /**
+ * ACI_SELF is used for the SELFWRITE right. Currently not implemented.
+ */
+ public static final int ACI_SELF = 0x0040;
+
+ /**
+ * ACI_ALL is used to as a mask for all of the above. These
+ * six below are not masked by the ACI_ALL.
+ */
+ public static final int ACI_ALL = 0x007F;
+
+ /**
+ * ACI_PROXY is used for the PROXY right. Currently not implemented.
+ */
+ public static final int ACI_PROXY = 0x0080;
+
+ /**
+ * ACI_IMPORT is used to set the container rights for a LDAP
+ * modify dn operation. Currently not implemented.
+ */
+ public static final int ACI_IMPORT = 0x0100;
+
+ /**
+ * ACI_EXPORT is used to set the container rights for a LDAP
+ * modify dn operation. Currently not implemented.
+ */
+ public static final int ACI_EXPORT = 0x0200;
+
+ /**
+ * ACI_WRITE_ADD and ACI_WRITE_DELETE are used by the LDAP modify
+ * operation. They currently don't have much value; but will be needed
+ * once the targetattrfilters target and modify dn are implemented.
+ */
+ public static final int ACI_WRITE_ADD = 0x800;
+ /**
+ * See above.
+ */
+ public static final int ACI_WRITE_DELETE = 0x400;
+
+ /**
+ * ACI_NULL is used to set the container rights to all zeros. Used
+ * by LDAP modify.
+ */
+ public static final int ACI_NULL = 0x0000;
+
+ /*
+ * The list that holds that ACIs keyed by the DN of the entry
+ * holding the ACI.
+ */
+ private AciList aciList;
+
+ /**
+ * Attribute type corresponding to "aci" attribute.
+ */
+ public static AttributeType aciType;
+
+ /**
+ * Constructor that registers the message catalog, creates the ACI list
+ * class that manages the ACI list. Instantiates and registers the change
+ * notification listener that is used to manage the ACI list on
+ * modifications and the backend initialization listener that is used to
+ * register/de-register aci attribute types in backends when backends
+ * are initialized/finalized.
+ */
+ public AciHandler() {
+ AciMessages.registerMessages();
+ aciList = new AciList();
+ AciListenerManager aciListenerMgr =
+ new AciListenerManager(aciList);
+ DirectoryServer.registerChangeNotificationListener(aciListenerMgr);
+ DirectoryServer.registerBackendInitializationListener(aciListenerMgr);
+ if((aciType = DirectoryServer.getAttributeType("aci")) == null)
+ aciType = DirectoryServer.getDefaultAttributeType("aci");
+ }
+
+ /*
+ * TODO
+ * The internal search performed by the searchAcis method will require
+ * a presence index on the aci attribute for any database of any significant
+ * size. We should probably consider making this index present by default,
+ * because if they aren't using the DSEE-compatible implementation then
+ * they probably won't have any instances of the aci attribute.
+ */
+ /**
+ * Checks to see if a LDAP modification is allowed access.
+ *
+ * @param container The structure containing the LDAP modifications
+ * @param operation The operation to check modify privileges on.
+ * operation to check and the evaluation context to apply the check against.
+ * @param skipAccessCheck True if access checking should be skipped.
+ * @return True if access is allowed.
+ */
+ private boolean aciCheckMods(AciLDAPOperationContainer container,
+ Operation operation,
+ boolean skipAccessCheck) {
+ Entry resourceEntry=container.getResourceEntry();
+ DN dn=resourceEntry.getDN();
+ List<Modification> modifications=container.getModifications();
+ for(Modification m : modifications) {
+ Attribute modAttr=m.getAttribute();
+ AttributeType modType=modAttr.getAttributeType();
+ switch(m.getModificationType()) {
+ /*
+ * TODO Increment modification type needs to be handled.
+ */
+ case DELETE:
+ case REPLACE:
+ {
+ /*
+ Check if we have rights to delete all values of
+ an attribute type in the resource entry.
+ */
+ if(resourceEntry.hasAttribute(modType)) {
+ container.setCurrentAttributeType(modType);
+ List<Attribute> attrList =
+ resourceEntry.getAttribute(modType,null);
+ for (Attribute a : attrList) {
+ for (AttributeValue v : a.getValues()) {
+ container.setCurrentAttributeValue(v);
+ container.setRights(ACI_WRITE_DELETE);
+ if(!skipAccessCheck &&
+ !accessAllowed(container))
+ return false;
+ }
+ }
+ }
+ }
+ }
+ if(modAttr.hasValue()) {
+ boolean checkPrivileges=true;
+ for(AttributeValue v : modAttr.getValues()) {
+ container.setCurrentAttributeType(modType);
+ container.setCurrentAttributeValue(v);
+ if((m.getModificationType() == ModificationType.ADD) ||
+ (m.getModificationType() == ModificationType.REPLACE)) {
+ container.setRights(ACI_WRITE_ADD);
+ if(!skipAccessCheck && !accessAllowed(container))
+ return false;
+ } else if(m.getModificationType()
+ == ModificationType.DELETE) {
+ container.setRights(ACI_WRITE_DELETE);
+ if(!skipAccessCheck && !accessAllowed(container))
+ return false;
+ } else
+ return false;
+ /*
+ Check if the modification type has an "aci" attribute type.
+ If so, check the syntax of that attribute value. Fail the
+ the operation if the syntax check fails.
+ */
+ if(modType.equals(aciType)) {
+ try {
+ /*
+ * Check that the operation has modify privileges if
+ * it contains an "aci" attribute type. Flip the
+ * boolean to false so this check isn't made again
+ * if there are several ACI values being added.
+ */
+ if(checkPrivileges) {
+ if (!operation.getClientConnection().
+ hasPrivilege(Privilege.MODIFY_ACL, operation)) {
+ int msgID =
+ MSGID_ACI_MODIFY_FAILED_PRIVILEGE;
+ String message = getMessage(msgID,
+ String.valueOf(container.getResourceDN()),
+ String.valueOf(container.getClientDN()));
+ logError(ErrorLogCategory.ACCESS_CONTROL,
+ ErrorLogSeverity.SEVERE_WARNING,
+ message, msgID);
+ return false;
+ }
+ checkPrivileges=false;
+ }
+ Aci.decode(v.getValue(),dn);
+ } catch (AciException ex) {
+ int msgID = MSGID_ACI_MODIFY_FAILED_DECODE;
+ String message = getMessage(msgID,
+ String.valueOf(dn),
+ ex.getMessage());
+ logError(ErrorLogCategory.ACCESS_CONTROL,
+ ErrorLogSeverity.SEVERE_WARNING,
+ message, msgID);
+ return false;
+ }
+ }
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Performs the test of the deny and allow access lists using the
+ * provided evaluation context. The deny list is checked first.
+ *
+ * @param evalCtx The evaluation context to use.
+ * @return True if access is allowed.
+ */
+ private boolean testApplicableLists(AciEvalContext evalCtx) {
+ EnumEvalResult res=EnumEvalResult.FALSE;
+ //First check deny lists
+ LinkedList<Aci>denys=evalCtx.getDenyList();
+ evalCtx.setDenyEval(true);
+ for(Aci denyAci : denys) {
+ res=Aci.evaluate(evalCtx, denyAci);
+ //Failure could be returned if a system limit is hit or
+ //search fails
+ if((res.equals(EnumEvalResult.FAIL) ||
+ (res.equals(EnumEvalResult.TRUE)))) {
+ return false;
+ }
+ }
+ //Now check the allows -- flip the deny flag to false first.
+ evalCtx.setDenyEval(false);
+ LinkedList<Aci>allows=evalCtx.getAllowList();
+ for(Aci allowAci : allows) {
+ res=Aci.evaluate(evalCtx, allowAci);
+ if(res.equals(EnumEvalResult.TRUE)) {
+ break;
+ }
+ }
+ return res.getBoolVal();
+ }
+
+ /**
+ * Creates the allow and deny ACI lists based on the provided target
+ * match context. These lists are stored in the evaluation context.
+ * @param candidates List of all possible ACI candidates.
+ * @param targetMatchCtx Target matching context to use for testing each
+ * ACI.
+ */
+ private void createApplicableList(LinkedList<Aci> candidates,
+ AciTargetMatchContext targetMatchCtx)
+ {
+ LinkedList<Aci>denys=new LinkedList<Aci>();
+ LinkedList<Aci>allows=new LinkedList<Aci>();
+ for(Aci aci : candidates) {
+ if(Aci.isApplicable(aci, targetMatchCtx)) {
+ if (aci.hasAccessType(EnumAccessType.DENY)) {
+ denys.add(aci);
+ }
+ if(aci.hasAccessType(EnumAccessType.ALLOW)) {
+ allows.add(aci);
+ }
+ }
+ }
+ targetMatchCtx.setAllowList(allows);
+ targetMatchCtx.setDenyList(denys);
+ }
+
+
+ /**
+ * Check to see if the client entry has BYPASS_ACL privileges
+ * for this operation.
+ * @param operation The operation to check privileges on.
+ * @return True if access checking can be skipped because
+ * the operation client connection has BYPASS_ACL privileges.
+ */
+ boolean skipAccessCheck(Operation operation) {
+ return operation.getClientConnection().
+ hasPrivilege(Privilege.BYPASS_ACL, operation);
+ }
+
+ /**
+ * Check access using the specified container. This container will have all
+ * of the information to gather applicable ACIs and perform evaluation on
+ * them.
+ *
+ * @param container An ACI operation container which has all of the
+ * information needed to check access.
+ *
+ * @return True if access is allowed.
+ */
+ private boolean accessAllowed(AciContainer container)
+ {
+ DN dn = container.getResourceEntry().getDN();
+ //For ACI_WRITE_ADD and ACI_WRITE_DELETE set the ACI_WRITE
+ //right.
+ if(container.hasRights(ACI_WRITE_ADD) ||
+ container.hasRights(ACI_WRITE_DELETE))
+ container.setRights(container.getRights() | ACI_WRITE);
+ /*
+ * First get all allowed candidate ACIs.
+ */
+ LinkedList<Aci>candidates = aciList.getCandidateAcis(dn);
+ /*
+ * Create an applicable list of ACIs by target matching each
+ * candidate ACI against the container's target match view.
+ */
+ createApplicableList(candidates,container);
+ /*
+ * Lastly, evaluate the applicable list.
+ */
+ return(testApplicableLists(container));
+ }
+
+ /**
+ * Performs an access check against all of the attributes of an entry.
+ * The attributes that fail access are removed from the entry. This method
+ * performs the processing needed for the filterEntry method processing.
+ *
+ * @param container The search or compare container which has all of the
+ * information needed to filter the attributes for this entry.
+ * @return The entry to send back to the client, minus any attribute
+ * types that failed access check.
+ */
+ private SearchResultEntry
+ accessAllowedAttrs(AciLDAPOperationContainer container) {
+ Entry e=container.getResourceEntry();
+ List<AttributeType> typeList=getAllAttrs(e);
+ for(AttributeType attrType : typeList) {
+ container.setCurrentAttributeType(attrType);
+ if(!accessAllowed(container)) {
+ e.removeAttribute(attrType);
+ }
+ }
+ return container.getSearchResultEntry();
+ }
+
+ /**
+ * Gathers all of the attribute types in an entry along with the
+ * "objectclass" attribute type in a List. The "objectclass" attribute is
+ * added to the list first so it is evaluated first.
+ *
+ * @param e Entry to gather the attributes for.
+ * @return List containing the attribute types.
+ */
+ private List<AttributeType> getAllAttrs(Entry e) {
+ Map<AttributeType,List<Attribute>> attrMap = e.getUserAttributes();
+ List<AttributeType> typeList=new LinkedList<AttributeType>();
+ Attribute attr=e.getObjectClassAttribute();
+ /*
+ * When a search is not all attributes returned, the "objectclass"
+ * attribute type is missing from the entry.
+ */
+ if(attr != null) {
+ AttributeType ocType=attr.getAttributeType();
+ typeList.add(ocType);
+ }
+ typeList.addAll(attrMap.keySet());
+ return typeList;
+ }
+
+ /*
+ * TODO Evaluate performance of this method.
+ * TODO Evaluate security concerns of this method. Logic from this method
+ * taken almost directly from DS6 implementation.
+ *
+ * I find the work done in the accessAllowedEntry method, particularly
+ * with regard to the entry test evaluation, to be very confusing and
+ * potentially pretty inefficient. I'm also concerned that the "return
+ * "true" inside the for loop could potentially allow access when it
+ * should be denied.
+ */
+ /**
+ * Check if access is allowed on an entry. Access is checked by iterating
+ * through each attribute of an entry, starting with the "objectclass"
+ * attribute type.
+ *
+ * If access is allowed on the entry based on one of it's attribute types,
+ * then a possible second access check is performed. This second check is
+ * only performed if an entry test ACI was found during the earlier
+ * successful access check. An entry test ACI has no "targetattrs" keyword,
+ * so allowing access based on an attribute type only would be incorrect.
+ *
+ * @param container ACI search container containing all of the information
+ * needed to check access.
+ *
+ * @return True if access is allowed.
+ */
+ private boolean accessAllowedEntry(AciLDAPOperationContainer container) {
+ boolean ret=false;
+ //set flag that specifies this is the first attribute evaluated
+ //in the entry
+ container.setIsFirstAttribute(true);
+ List<AttributeType> typeList=getAllAttrs(container.getResourceEntry());
+ for(AttributeType attrType : typeList) {
+ container.setCurrentAttributeType(attrType);
+ /*
+ * Check if access is allowed. If true, then check to see if an
+ * entry test rule was found (no targetattrs) during target match
+ * evaluation. If such a rule was found, set the current attribute
+ * type to "null" and check access again so that rule is applied.
+ */
+ if(accessAllowed(container)) {
+ if(container.hasEntryTestRule()) {
+ container.setCurrentAttributeType(null);
+ if(!accessAllowed(container)) {
+ /*
+ * If we failed because of a deny permission-bind rule,
+ * we need to stop and return false.
+ */
+ if(container.isDenyEval()) {
+ return false;
+ }
+ /*
+ * If we failed because there was no explicit
+ * allow rule, then we grant implicit access to the
+ * entry.
+ */
+ }
+ }
+ return true;
+ }
+ }
+ return ret;
+ }
+
+ /**
+ * Evaluate an entry to be added to see if it has any "aci"
+ * attribute type. If it does, examines each "aci" attribute type
+ * value for syntax errors. All of the "aci" attribute type values
+ * must pass syntax check for the add operation to proceed. Any
+ * entry with an "aci" attribute type must have "modify-acl"
+ * privileges.
+ *
+ * @param entry The entry to be examined.
+ * @param operation The operation to to check privileges on.
+ * @param clientDN The authorization DN.
+ * @return True if the entry has no ACI attributes or if all of the "aci"
+ * attributes values pass ACI syntax checking.
+ */
+ private boolean
+ verifySyntax(Entry entry, Operation operation, DN clientDN) {
+ if(entry.hasOperationalAttribute(aciType)) {
+ /*
+ * Check that the operation has "modify-acl" privileges since the
+ * entry to be added has an "aci" attribute type.
+ */
+ if (!operation.getClientConnection().
+ hasPrivilege(Privilege.MODIFY_ACL, operation)) {
+ int msgID = MSGID_ACI_ADD_FAILED_PRIVILEGE;
+ String message = getMessage(msgID,
+ String.valueOf(entry.getDN()),
+ String.valueOf(clientDN));
+ logError(ErrorLogCategory.ACCESS_CONTROL,
+ ErrorLogSeverity.SEVERE_WARNING,
+ message, msgID);
+ return false;
+ }
+ List<Attribute> attributeList =
+ entry.getOperationalAttribute(aciType, null);
+ for (Attribute attribute : attributeList)
+ {
+ for (AttributeValue value : attribute.getValues())
+ {
+ try {
+ DN dn=entry.getDN();
+ Aci.decode(value.getValue(),dn);
+ } catch (AciException ex) {
+ int msgID = MSGID_ACI_ADD_FAILED_DECODE;
+ String message = getMessage(msgID,
+ String.valueOf(entry.getDN()),
+ ex.getMessage());
+ logError(ErrorLogCategory.ACCESS_CONTROL,
+ ErrorLogSeverity.SEVERE_WARNING,
+ message, msgID);
+ return false;
+ }
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Check access using the accessAllowed method. The
+ * LDAP add, compare, modify and delete operations use this function.
+ * The other supported LDAP operations have more specialized checks.
+ * @param operationContainer The container containing the information
+ * needed to evaluate this operation.
+ * @param operation The operation being evaluated.
+ * @return True if this operation is allowed access.
+ */
+ private boolean isAllowed(AciLDAPOperationContainer operationContainer,
+ Operation operation) {
+ return skipAccessCheck(operation) || accessAllowed(operationContainer);
+ }
+
+ /**
+ * Check access on add operations.
+ *
+ * @param operation The add operation to check access on.
+ * @return True if access is allowed.
+ */
+ public boolean isAllowed(AddOperation operation) {
+ assert debugEnter(CLASS_NAME, "isAllowed");
+ AciLDAPOperationContainer operationContainer =
+ new AciLDAPOperationContainer(operation, ACI_ADD);
+ boolean ret=isAllowed(operationContainer,operation);
+ //LDAP add needs a verify ACI syntax step in case any
+ //"aci" attribute types are being added.
+ if(ret)
+ ret=verifySyntax(operation.getEntryToAdd(), operation,
+ operationContainer.getClientDN());
+ return ret;
+ }
+
+ /**
+ * Check access on compare operations. Note that the attribute
+ * type is unavailable at this time, so this method partially
+ * parses the raw attribute string to get the base attribute
+ * type. Options are ignored.
+ *
+ * @param operation The compare operation to check access on.
+ * @return True if access is allowed.
+ */
+ public boolean isAllowed(CompareOperation operation) {
+ assert debugEnter(CLASS_NAME, "isAllowed");
+
+ AciLDAPOperationContainer operationContainer =
+ new AciLDAPOperationContainer(operation, ACI_COMPARE);
+ String baseName;
+ String rawAttributeType=operation.getRawAttributeType();
+ int semicolonPosition=rawAttributeType.indexOf(';');
+ if (semicolonPosition > 0)
+ baseName =
+ toLowerCase(rawAttributeType.substring(0, semicolonPosition));
+ else
+ baseName = toLowerCase(rawAttributeType);
+ AttributeType attributeType;
+ if((attributeType =
+ DirectoryServer.getAttributeType(baseName)) == null)
+ attributeType = DirectoryServer.getDefaultAttributeType(baseName);
+ AttributeValue attributeValue =
+ new AttributeValue(attributeType, operation.getAssertionValue());
+ operationContainer.setCurrentAttributeType(attributeType);
+ operationContainer.setCurrentAttributeValue(attributeValue);
+ return isAllowed(operationContainer, operation);
+ }
+
+ /**
+ * Check access on delete operations.
+ *
+ * @param operation The delete operation to check access on.
+ * @return True if access is allowed.
+ */
+ public boolean isAllowed(DeleteOperation operation) {
+ assert debugEnter(CLASS_NAME, "isAllowed");
+ AciLDAPOperationContainer operationContainer=
+ new AciLDAPOperationContainer(operation, ACI_DELETE);
+ return isAllowed(operationContainer, operation);
+ }
+
+ /**
+ * Check access on modify operations.
+ *
+ * @param operation The modify operation to check access on.
+ * @return True if access is allowed.
+ */
+
+ public boolean isAllowed(ModifyOperation operation) {
+ assert debugEnter(CLASS_NAME, "isAllowed");
+ AciLDAPOperationContainer operationContainer=
+ new AciLDAPOperationContainer(operation, ACI_NULL);
+ return aciCheckMods(operationContainer, operation,
+ skipAccessCheck(operation));
+ }
+
+
+ /*
+ * TODO Add access testing of the filter against the entry. This was
+ * brought up in the first code review.
+ *
+ * The static block that creates the arrays of EnumRight objects needs to
+ * be documented to explain what they are. Also, I still disagree with
+ * the interpretation that the READ right is all that is necessary to
+ * perform either search or compare operations. That definitely goes
+ * against the documentation, which states that READ applies only to
+ * the search operation, and that users must have both SEARCH and READ
+ * in order to access the results.
+ */
+ /**
+ * Checks access on a search operation.
+ * @param operation The search operation class containing information to
+ * check the access on.
+ * @param entry The entry to evaluate access.
+ * @return True if access is allowed.
+ */
+ public boolean
+ maySend(SearchOperation operation, SearchResultEntry entry) {
+ assert debugEnter(CLASS_NAME, "maySend");
+ AciLDAPOperationContainer operationContainer =
+ new AciLDAPOperationContainer(operation,
+ (ACI_READ | ACI_SEARCH), entry);
+ return skipAccessCheck(operation) ||
+ accessAllowedEntry(operationContainer);
+ }
+
+ /*
+ * TODO Rename this method. Needs to be changed in SearchOperation.
+ *
+ * I find the name of the filterEntry method to be misleading because
+ * it works on a search operation but has nothing to do with the search
+ * filter. Something like "removeDisallowedAttributes" would be clearer.
+ */
+ /**
+ * Checks access on each attribute in an entry. It removes those attributes
+ * that fail access check.
+ *
+ * @param operation The search operation class containing information to
+ * check access on.
+ * @param entry The entry containing the attributes.
+ * @return The entry to return minus filtered attributes.
+ */
+ public SearchResultEntry filterEntry(SearchOperation operation,
+ SearchResultEntry entry) {
+
+ assert debugEnter(CLASS_NAME, "filterEntry");
+ AciLDAPOperationContainer operationContainer =
+ new AciLDAPOperationContainer(operation,
+ (ACI_READ | ACI_SEARCH), entry);
+ SearchResultEntry returnEntry;
+ if(!skipAccessCheck(operation)) {
+ returnEntry=accessAllowedAttrs(operationContainer);
+ } else
+ returnEntry=entry;
+ return returnEntry;
+ }
+
+ //Planned to be implemented methods
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean maySend(SearchOperation operation,
+ SearchResultReference reference) {
+ assert debugEnter(CLASS_NAME, "maySend");
+ //TODO: Planned to be implemented.
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isAllowed(ModifyDNOperation modifyDNOperation) {
+ assert debugEnter(CLASS_NAME, "isAllowed");
+ // TODO: Planned to be implemented.
+ return true;
+ }
+
+ //Not planned to be implemented methods.
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isAllowed(BindOperation bindOperation) {
+ assert debugEnter(CLASS_NAME, "isAllowed");
+ //Not planned to be implemented.
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isAllowed(ExtendedOperation extendedOperation) {
+ assert debugEnter(CLASS_NAME, "isAllowed");
+ //Not planned to be implemented.
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isAllowed(SearchOperation searchOperation) {
+ assert debugEnter(CLASS_NAME, "isAllowed");
+ //Not planned to be implemented.
+ return true;
+ }
+}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciLDAPOperationContainer.java b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciLDAPOperationContainer.java
new file mode 100644
index 0000000..58d43f4
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciLDAPOperationContainer.java
@@ -0,0 +1,120 @@
+/*
+ * 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 java.util.List;
+
+import org.opends.server.core.AddOperation;
+import org.opends.server.core.CompareOperation;
+import org.opends.server.core.DeleteOperation;
+import org.opends.server.core.ModifyOperation;
+import org.opends.server.core.SearchOperation;
+import org.opends.server.types.Modification;
+import org.opends.server.types.SearchResultEntry;
+
+/**
+ * The AciLDAPOperationContainer is an AciContainer
+ * extended class that wraps each LDAP operation being
+ * evaluated or tested for target matched of an ACI.
+ */
+public class AciLDAPOperationContainer extends AciContainer {
+
+ /**
+ * The entry to be returned if this is a LDAP search.
+ */
+ private SearchResultEntry searchEntry;
+
+ /**
+ * The list of modifications if this operation is a LDAP
+ * modify.
+ */
+ private List<Modification> modifications;
+ /**
+ * Constructor interface for the compare operation.
+ * @param operation The compare operation to evaluate.
+ * @param rights The rights of a compare operation.
+ */
+ public AciLDAPOperationContainer(CompareOperation operation, int rights) {
+ super(operation, rights, operation.getEntryToCompare());
+ }
+
+ /**
+ * Constructor interface for the add operation.
+ * @param operation The add operation to evaluate.
+ * @param rights The rights of an add operation.
+ */
+ public AciLDAPOperationContainer(AddOperation operation, int rights) {
+ super(operation, rights, operation.getEntryToAdd());
+ }
+
+ /**
+ * Constructor interface for the delete operation.
+ * @param operation The add operation to evaluate.
+ * @param rights The rights of a delete operation.
+ */
+ public AciLDAPOperationContainer(DeleteOperation operation, int rights) {
+ super(operation, rights, operation.getEntryToDelete());
+ }
+
+ /**
+ * Constructor interface for the modify operation.
+ * @param rights The rights of modify operation.
+ * @param operation The add operation to evaluate.
+ */
+ public AciLDAPOperationContainer(ModifyOperation operation, int rights) {
+ super(operation, rights, operation.getCurrentEntry());
+ this.modifications=operation.getModifications();
+ }
+
+ /**
+ * Constructor interface for the LDAP search operation.
+ * @param operation The search operation.
+ * @param rights The rights of a search operation.
+ * @param entry The entry to be evaluated for this search.
+ */
+ public AciLDAPOperationContainer(SearchOperation operation, int rights,
+ SearchResultEntry entry) {
+ super(operation, rights, entry);
+ this.searchEntry = entry;
+ }
+
+ /**
+ * Retrieve the search result entry of the search operation.
+ * @return The search result entry.
+ */
+ public SearchResultEntry getSearchResultEntry() {
+ return this.searchEntry;
+ }
+
+ /** Retrieve the list of modifications if this is a LDAP modify.
+ * @return The list of LDAP modifications to made on the resource entry.
+ */
+ public List<Modification> getModifications() {
+ return modifications;
+ }
+}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciList.java b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciList.java
new file mode 100644
index 0000000..3a85b35
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciList.java
@@ -0,0 +1,251 @@
+/*
+ * 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.loggers.Error.logError;
+import static org.opends.server.messages.MessageHandler.getMessage;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+import org.opends.server.types.Attribute;
+import org.opends.server.types.AttributeValue;
+import org.opends.server.types.DN;
+import org.opends.server.types.Entry;
+import org.opends.server.types.ErrorLogCategory;
+import org.opends.server.types.ErrorLogSeverity;
+import org.opends.server.api.Backend;
+
+/**
+ * The AciList class performs caching of the ACI attribute values
+ * using the entry DN as the key.
+ */
+public class AciList {
+ /*
+ * TODO Change linked list implementation as suggested below.
+ * I would strongly recommend that you change aciList to be
+ * LinkedHashMap<DN,List<Aci>> or LinkedHashMap<DN,Aci[]> rather than
+ * LinkedHashMap<String,Aci>. It looks like there are some costly
+ * string->DN and even string->DN->string conversions. Further, the very
+ * hackish way that the linked-list is currently maintained is very
+ * ugly and potentially error-prone.
+ */
+ private LinkedHashMap<DN, Aci> aciList =
+ new LinkedHashMap<DN, Aci>();
+ /*
+ * TODO Evaluate making this class lock-free.
+ * I would definitely try to make this a lock-free class if at all
+ * possible. Read locks aren't free to acquire, since they still require
+ * an exclusive lock at some point. If possible, you should use a
+ * copy-on-write structure so that you only incur penalties for changing
+ * the ACI list (which should be a rare event) and there is no need for
+ * any kind of locking at all for read operations.
+ */
+ private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
+ private final Lock aciReadLock = rwl.readLock();
+ private final Lock aciWriteLock = rwl.writeLock();
+
+ /*
+ * TODO Add support global ACIs in config.ldif.
+ *
+ */
+ /**
+ * Using the base DN, return a list of ACIs that are candidates for
+ * evaluation by walking up from the base DN towards the root of the
+ * DIT gathering ACIs on parents.
+ *
+ * @param baseDN The DN to check.
+ * @return A list of candidate ACIs that might be applicable.
+ */
+ public LinkedList<Aci> getCandidateAcis(DN baseDN) {
+ LinkedList<Aci> candidates = new LinkedList<Aci>();
+ if(baseDN == null)
+ return candidates;
+ try {
+ aciReadLock.lock();
+ while(baseDN != null) {
+ Aci aci = aciList.get(baseDN);
+ if (aci != null)
+ {
+ while (aci != null)
+ {
+ candidates.add(aci);
+ aci = aci.next;
+ }
+ }
+ if(baseDN.isNullDN())
+ break;
+ DN parentDN=baseDN.getParent();
+ if(parentDN == null)
+ baseDN=DN.nullDN();
+ else
+ baseDN=parentDN;
+ }
+ } finally {
+ aciReadLock.unlock();
+ }
+ return candidates;
+ }
+
+
+ /**
+ * Add all of an entries ACI attribute values to the ACI list. This
+ * method locks/unlocks the list.
+ * @param entry The entry containing the "aci" attribute values.\
+ * @return The number of valid ACI attribute values added to the ACI list.
+ */
+ public int addAci(Entry entry) {
+ int validAcis=0;
+ DN dn=entry.getDN();
+ List<Attribute> attributeList =
+ entry.getOperationalAttribute(AciHandler.aciType);
+ try {
+ aciWriteLock.lock();
+ validAcis=addAciAttributeListNoLock(dn, attributeList);
+ } finally {
+ aciWriteLock.unlock();
+ }
+ return validAcis;
+ }
+
+ /**
+ * Add "aci" attribute type values to the ACI list. There is a chance
+ * that an ACI will throw an exception if it has an invalid syntax.
+ * If that happens a message will be logged and the ACI skipped.
+ * @param dn The DN to use a the key in the ACI list.
+ * @param attributeList List of attributes contain the "aci" attribute
+ * values.
+ * @return The number of valid "aci" attribute types added to the ACI list.
+ */
+ private int addAciAttributeListNoLock(DN dn,
+ List<Attribute> attributeList) {
+ int validAcis=0;
+ for (Attribute attribute : attributeList) {
+ for (AttributeValue value : attribute.getValues()) {
+ try {
+ Aci aci= Aci.decode(value.getValue(),dn);
+ addAci(dn, aci);
+ validAcis++;
+ } catch (AciException ex) {
+ /* An illegal ACI might have been loaded
+ * during import and is failing at ACI handler
+ * initialization time. Log a message and continue
+ * processing. ACIs added via LDAP add have their
+ * syntax checked before adding and should never
+ * hit this code.
+ */
+ int msgID = MSGID_ACI_ADD_LIST_FAILED_DECODE;
+ String message = getMessage(msgID,
+ ex.getMessage());
+ logError(ErrorLogCategory.ACCESS_CONTROL,
+ ErrorLogSeverity.SEVERE_WARNING,
+ message, msgID);
+ }
+ }
+ }
+ return validAcis;
+ }
+
+ /**
+ * Remove all of the ACIs related to the old entry and then add all of the
+ * ACIs related to the new entry. This method locks/unlocks the list.
+ * @param oldEntry The old entry maybe containing old "aci" attribute
+ * values.
+ * @param newEntry The new entry maybe containing new "aci" attribute
+ * values.
+ */
+ public void modAciOldNewEntry(Entry oldEntry, Entry newEntry) {
+ if((oldEntry.hasOperationalAttribute(AciHandler.aciType)) ||
+ (newEntry.hasOperationalAttribute(AciHandler.aciType))) {
+ try {
+ aciWriteLock.lock();
+ aciList.remove(oldEntry.getDN());
+ List<Attribute> attributeList =
+ newEntry.getOperationalAttribute(AciHandler.aciType, null);
+ addAciAttributeListNoLock(newEntry.getDN(),attributeList);
+ } finally {
+ aciWriteLock.unlock();
+ }
+ }
+ }
+
+ /**
+ * Add an ACI using the DN as a key. If the DN already
+ * has ACI(s) on the list, then the new ACI is added to the
+ * end of the linked list.
+ * @param dn The DN to use as the key.
+ * @param aci The ACI to add to the list.
+ */
+ public void addAci(DN dn, Aci aci) {
+ if(aciList.containsKey(dn)) {
+ Aci tmpAci = aciList.get(dn);
+ while(tmpAci.next != null)
+ tmpAci=tmpAci.next;
+ tmpAci.next=aci;
+ } else
+ aciList.put(dn, aci);
+ }
+
+ /**
+ * Remove ACIs related to an entry.
+ * @param entry The entry to be removed.
+ * @return True if the ACI set was deleted.
+ */
+ public boolean removeAci(Entry entry) {
+ boolean deleted = false;
+ try {
+ aciWriteLock.lock();
+ if (aciList.remove(entry.getDN()) != null)
+ deleted = true;
+ } finally {
+ aciWriteLock.unlock();
+ }
+ return deleted;
+ }
+
+ /**
+ * Remove all ACIs related to a backend.
+ * @param backend The backend to check if each DN is handled by that
+ * backend.
+ */
+ public void removeAci (Backend backend) {
+ try {
+ aciWriteLock.lock();
+ Set<DN> keys=aciList.keySet();
+ for(DN dn : keys) {
+ if (backend.handlesEntry(dn))
+ aciList.remove(dn);
+ }
+ } finally {
+ aciWriteLock.unlock();
+ }
+ }
+}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciListenerManager.java b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciListenerManager.java
new file mode 100644
index 0000000..0d82365
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciListenerManager.java
@@ -0,0 +1,210 @@
+/*
+ * 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 org.opends.server.api.ChangeNotificationListener;
+import org.opends.server.api.BackendInitializationListener;
+import org.opends.server.api.Backend;
+import org.opends.server.types.*;
+import org.opends.server.types.operation.PostResponseAddOperation;
+import org.opends.server.types.operation.PostResponseDeleteOperation;
+import org.opends.server.types.operation.PostResponseModifyOperation;
+import org.opends.server.types.operation.PostResponseModifyDNOperation;
+import org.opends.server.protocols.internal.InternalClientConnection;
+import org.opends.server.protocols.internal.InternalSearchOperation;
+import static org.opends.server.loggers.Debug.debugException;
+import static org.opends.server.loggers.Error.logError;
+import static org.opends.server.authorization.dseecompat.AciMessages.*;
+import static org.opends.server.messages.MessageHandler.getMessage;
+
+import java.util.LinkedHashSet;
+
+/**
+ * The AciListenerManager updates an ACI list after each
+ * modification operation. Also, updates ACI list when backends are initialized
+ * and finalized.
+ */
+public class AciListenerManager
+ implements ChangeNotificationListener, BackendInitializationListener {
+
+ /**
+ * The fully-qualified name of this class for debugging purposes.
+ */
+ private static final String CLASS_NAME =
+ "org.opends.server.core.AciListenerManager";
+
+ private AciList aciList;
+ /*
+ * Search filter used in context search for "aci" attribute types.
+ */
+ private static SearchFilter aciFilter;
+ /**
+ * The aci attribute type is operational so we need to specify it to be
+ * returned.
+ */
+ private static LinkedHashSet<String> attrs = new LinkedHashSet<String>();
+
+ static {
+ /*
+ * Set up the filter used to search private and public contexts.
+ */
+ try {
+ aciFilter=SearchFilter.createFilterFromString("(aci=*)");
+ } catch (DirectoryException ex) {
+ //TODO should never happen, error message?
+ }
+ attrs.add("aci");
+ }
+ /**
+ * Save the list created by the AciHandler routine.
+ * @param aciList The list object created and loaded by the handler.
+ */
+ public AciListenerManager(AciList aciList) {
+ this.aciList=aciList;
+ }
+
+ /**
+ * A delete operation succeeded. Remove any ACIs associated with the
+ * entry deleted.
+ * @param deleteOperation The delete operation.
+ * @param entry The entry being deleted.
+ */
+ public void handleDeleteOperation(PostResponseDeleteOperation
+ deleteOperation, Entry entry) {
+ if(entry.hasOperationalAttribute(AciHandler.aciType)) {
+ aciList.removeAci(entry);
+ }
+ }
+
+ /**
+ * An Add operation succeeded. Add any ACIs associated with the
+ * entry being added.
+ * @param addOperation The add operation.
+ * @param entry The entry being added.
+ */
+ public void handleAddOperation(PostResponseAddOperation addOperation,
+ Entry entry) {
+ if(entry.hasOperationalAttribute(AciHandler.aciType))
+ {
+ aciList.addAci(entry);
+ }
+ }
+
+ /**
+ * A modify operation succeeded. Adjust the ACIs by removing
+ * ACIs based on the oldEntry and then adding ACIs based on the new
+ * entry.
+ * @param modOperation the modify operation.
+ * @param oldEntry The old entry to examine.
+ * @param newEntry The new entry to examine.
+ */
+ public void handleModifyOperation(PostResponseModifyOperation modOperation,
+ Entry oldEntry, Entry newEntry)
+ {
+ aciList.modAciOldNewEntry(oldEntry, newEntry);
+ }
+
+ /**
+ * Not implemented.
+ * @param modifyDNOperation The LDAP modify DN operation.
+ * @param oldEntry The old entry.
+ * @param newEntry The new entry.
+ */
+ public void handleModifyDNOperation(
+ PostResponseModifyDNOperation modifyDNOperation,
+ Entry oldEntry, Entry newEntry)
+ {
+ /*
+ * TODO Not yet implemented.
+ */
+ }
+
+ /**
+ * {@inheritDoc} In this case, the server will search the backend to find
+ * all aci attribute type values that it may contain and add them to the
+ * ACI list.
+ */
+ public void performBackendInitializationProcessing(Backend backend) {
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ for (DN baseDN : backend.getBaseDNs()) {
+ try {
+ if (! backend.entryExists(baseDN)) {
+ continue;
+ }
+ } catch (Exception e) {
+ assert debugException(CLASS_NAME,
+ "performBackendInitializationProcessing", e);
+ //TODO log message
+ continue;
+ }
+ InternalSearchOperation internalSearch =
+ new InternalSearchOperation(conn,
+ InternalClientConnection.nextOperationID(),
+ InternalClientConnection.nextMessageID(),
+ null, baseDN, SearchScope.WHOLE_SUBTREE,
+ DereferencePolicy.NEVER_DEREF_ALIASES,
+ 0, 0, false, aciFilter, attrs, null);
+ try
+ {
+ backend.search(internalSearch);
+ } catch (Exception e) {
+ assert debugException(CLASS_NAME,
+ "performBackendInitializationProcessing", e);
+ //TODO log message
+ continue;
+ }
+ if(internalSearch.getSearchEntries().isEmpty()) {
+ int msgID = MSGID_ACI_ADD_LIST_NO_ACIS;
+ String message = getMessage(msgID, String.valueOf(baseDN));
+ logError(ErrorLogCategory.ACCESS_CONTROL,
+ ErrorLogSeverity.NOTICE, message, msgID);
+ } else {
+ int validAcis=0;
+ for (SearchResultEntry entry :
+ internalSearch.getSearchEntries()) {
+ validAcis += aciList.addAci(entry);
+ }
+ int msgID = MSGID_ACI_ADD_LIST_ACIS;
+ String message = getMessage(msgID, Integer.toString(validAcis),
+ String.valueOf(baseDN));
+ logError(ErrorLogCategory.ACCESS_CONTROL,
+ ErrorLogSeverity.NOTICE,
+ message, msgID);
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc} In this case, the server will remove all aci attribute
+ * type values associated with entries in the provided backend.
+ */
+ public void performBackendFinalizationProcessing(Backend backend) {
+ aciList.removeAci(backend);
+ }
+}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciMessages.java b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciMessages.java
new file mode 100644
index 0000000..1dd5539
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciMessages.java
@@ -0,0 +1,827 @@
+/*
+ * 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.messages.MessageHandler.*;
+
+/**
+ * The AciMessages class defines the set of message IDs and default format
+ * strings for messages associated with the dseecompat access control
+ * implementation.
+ */
+public class AciMessages {
+
+ /**
+ * The message ID for the message that will be used if an "aci" attribute
+ * type value cannot be parsed because it failed the non-specific regular
+ * expression check during the initial ACI decode process. This takes one
+ * argument, which is the string representation of the "aci" attribute
+ * type value.
+ */
+ public static final int MSGID_ACI_SYNTAX_GENERAL_PARSE_FAILED =
+ CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_SEVERE_WARNING | 1;
+
+ /**
+ * The message ID for the message that will be used if an "aci" attribute
+ * type value failed parsing because of an invalid version string. This
+ * takes one argument, which is the version string parsed from the
+ * "aci" attribute type value.
+ */
+ public static final int MSGID_ACI_SYNTAX_INVAILD_VERSION =
+ CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_SEVERE_WARNING | 2;
+
+ /**
+ * The message ID for the message that will be used if an "aci" attribute
+ * type value failed parsing because of an invalid access type string. This
+ * takes one argument, which is the access type string parsed from the
+ * "aci" attribute type value.
+ */
+ public static final int MSGID_ACI_SYNTAX_INVALID_ACCESS_TYPE_VERSION =
+ CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_SEVERE_WARNING | 3;
+
+ /**
+ * The message ID for the message that will be used if an "aci" attribute
+ * type value failed parsing because of an invalid rights string. This
+ * takes one argument, which is the rights string parsed from the
+ * "aci" attribute type value.
+ */
+ public static final int MSGID_ACI_SYNTAX_INVALID_RIGHTS_SYNTAX =
+ CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_SEVERE_WARNING | 4;
+
+ /**
+ * The message ID for the message that will be used if an "aci" attribute
+ * type value failed parsing because of an invalid rights keyword. This
+ * takes one argument, which is the rights keyword string parsed from the
+ * rights string.
+ */
+ public static final int MSGID_ACI_SYNTAX_INVALID_RIGHTS_KEYWORD =
+ CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_SEVERE_WARNING | 5;
+
+ /**
+ * The message ID for the message that will be used if an ACI bind rule
+ * value failed parsing because it starts with an open parenthesis,
+ * but does not contain a matching close parenthesis. This takes one
+ * argument, which is the bind rule string.
+ */
+ public static final int MSGID_ACI_SYNTAX_BIND_RULE_MISSING_CLOSE_PAREN =
+ CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_MILD_ERROR | 6;
+
+ /**
+ * The message ID for the message that will be used if an ACI bind rule
+ * value failed parsing because it is an invalid bind rule syntax. This
+ * takes one argument, which is the bind rule string.
+ */
+ public static final int MSGID_ACI_SYNTAX_INVALID_BIND_RULE_SYNTAX =
+ CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_MILD_ERROR | 7;
+
+ /**
+ * The message ID for the message that will be used if an "aci" attribute
+ * type value failed parsing because of an invalid bind rule keyword. This
+ * takes one argument, which is the bind rule keyword string parsed from
+ * the bind rule string.
+ */
+ public static final int MSGID_ACI_SYNTAX_INVALID_BIND_RULE_KEYWORD =
+ CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_SEVERE_WARNING | 8;
+
+ /**
+ * The message ID for the message that will be used if an "aci" attribute
+ * type value failed parsing because of an invalid bind rule operator. This
+ * takes one argument, which is the bind rule operator string parsed
+ * from the bind rule string.
+ */
+ public static final int MSGID_ACI_SYNTAX_INVALID_BIND_RULE_OPERATOR =
+ CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_SEVERE_WARNING | 9;
+
+ /**
+ * The message ID for the message that will be used if an "aci" attribute
+ * type value failed parsing because of a missing bind rule expression
+ * string. This takes one argument, which is the bind rule string.
+ */
+ public static final int MSGID_ACI_SYNTAX_MISSING_BIND_RULE_EXPRESSION =
+ CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_SEVERE_WARNING | 10;
+
+ /**
+ * The message ID for the message that will be used if an "aci" attribute
+ * type value failed parsing because of an invalid bind rule boolean
+ * operator. This takes one argument, which is the bind rule boolean
+ * operator string parsed from the bind rule string.
+ */
+ public static
+ final int MSGID_ACI_SYNTAX_INVALID_BIND_RULE_BOOLEAN_OPERATOR =
+ CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_SEVERE_WARNING | 11;
+
+
+ /**
+ * The message ID for the message that will be used if an "aci" attribute
+ * type value failed parsing because of an invalid bind rule keyword,
+ * keyword operation combination. This takes two arguments, which are the
+ * bind rule keyword string and the bind rule keyword operator parsed from
+ * the bind rule string.
+ */
+ public static
+ final int MSGID_ACI_SYNTAX_INVALID_BIND_RULE_KEYWORD_OPERATOR_COMBO =
+ CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_SEVERE_WARNING | 12;
+
+ /**
+ * The message ID for the message that will be used if an "aci" attribute
+ * type value parse failed because a bind rule userdn LDAP URL failed
+ * to decode. This takes one argument the message from the LDAP
+ * URL decode DirectoryException.
+ */
+ public static final int MSGID_ACI_SYNTAX_INVALID_USERDN_URL =
+ CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_SEVERE_WARNING | 13;
+
+ /**
+ * The message ID for the message that will be used if an "aci" attribute
+ * type value parse failed because a bind rule roledn expression failed
+ * to parse. This takes one argument, which is the roledn expression
+ * string parsed from the bind rule string.
+ */
+ public static final int MSGID_ACI_SYNTAX_INVALID_ROLEDN_EXPRESSION =
+ CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_SEVERE_WARNING | 14;
+
+ /**
+ * The message ID for the message that will be used if an "aci" attribute
+ * type value parse failed because a bind rule roledn LDAP URL failed
+ * to decode. This takes one argument the message from the LDAP
+ * URL decode DirectoryException.
+ */
+ public static final int MSGID_ACI_SYNTAX_INVALID_ROLEDN_URL =
+ CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_SEVERE_WARNING | 15;
+
+ /**
+ * The message ID for the message that will be used if an "aci" attribute
+ * type value parse failed because a bind rule groupdn expression failed
+ * to parse. This takes one argument, which is the groupdn expression
+ * string parsed from the bind rule string.
+ */
+ public static final int MSGID_ACI_SYNTAX_INVALID_GROUPDN_EXPRESSION =
+ CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_SEVERE_WARNING | 16;
+
+ /**
+ * The message ID for the message that will be used if an "aci" attribute
+ * type value parse failed because a bind rule groupdn LDAP URL failed
+ * to decode. This takes one argument the message from the LDAP
+ * URL decode DirectoryException.
+ */
+ public static final int MSGID_ACI_SYNTAX_INVALID_GROUPDN_URL =
+ CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_SEVERE_WARNING | 17;
+
+ /**
+ * The message ID for the message that will be used if an "aci" attribute
+ * type value parse failed because a bind rule ip keyword expression
+ * network mask value did not match the expression network address value.
+ * For example, the ACI has a IPV6 network mask; but the internet
+ * address part is IPV4. This takes two arguments, which are the
+ * bind rule ip netmask string and the bind rule ip inet address
+ * parsed from the bind rule string.
+ */
+ public static final int MSGID_ACI_SYNTAX_ADDRESS_FAMILY_MISMATCH =
+ CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_SEVERE_WARNING | 18;
+
+ /**
+ * The message ID for the message that will be used if an "aci" attribute
+ * type value parse failed because a bind rule ip keyword expression
+ * failed to parse because the number of bits specified to match the
+ * network was not valid for the inet address specified. This takes
+ * two arguments, which an string specifying the address type
+ * (inet6address, inet4address) and an error message.
+ */
+ public static final int MSGID_ACI_SYNTAX_INVALID_NETWORK_BIT_MATCH =
+ CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_SEVERE_WARNING | 19;
+
+ /**
+ * The message ID for the message that will be used if an "aci" attribute
+ * type value parse failed because a bind rule ip expression failed
+ * to decode. This takes one argument, the message from the
+ * thrown exception.
+ */
+ public static final int MSGID_ACI_SYNTAX_INVALID_IP_CRITERIA_DECODE =
+ CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_SEVERE_WARNING | 20;
+
+ /**
+ * The message ID for the message that will be used if an "aci" attribute
+ * type value parse failed because a bind rule ip expression failed
+ * to parse. This takes one argument, which is the ip expression
+ * string parsed from the bind rule string.
+ */
+ public static final int MSGID_ACI_SYNTAX_INVALID_IP_EXPRESSION =
+ CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_SEVERE_WARNING | 21;
+
+ /**
+ * The message ID for the message that will be used if an "aci" attribute
+ * type value parse failed because a bind rule dns expression failed
+ * to parse. This takes one argument, which is the dns expression
+ * string parsed from the bind rule string.
+ */
+ public static final int MSGID_ACI_SYNTAX_INVALID_DNS_EXPRESSION =
+ CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_SEVERE_WARNING | 22;
+
+ /**
+ * The message ID for the message that will be used if an "aci" attribute
+ * type value parse failed because a bind rule dns expression failed
+ * to parse because a wild-card was not in the leftmost position.
+ * This takes one argument, which is the dns expression string parsed
+ * from the bind rule string.
+ */
+ public static final int MSGID_ACI_SYNTAX_INVALID_DNS_WILDCARD =
+ CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_SEVERE_WARNING | 23;
+
+ /**
+ * The message ID for the message that will be used if an "aci" attribute
+ * type value parse failed because a bind rule dayofweek expression failed
+ * to parse. This takes one argument, which is the dayofweek expression
+ * string parsed from the bind rule string.
+ */
+ public static final int MSGID_ACI_SYNTAX_INVALID_DAYOFWEEK =
+ CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_SEVERE_WARNING |24;
+
+ /**
+ * The message ID for the message that will be used if an "aci" attribute
+ * type value parse failed because a bind rule timeofday expression failed
+ * to parse. This takes one argument, which is the timeofday expression
+ * string parsed from the bind rule string.
+ */
+
+ public static final int MSGID_ACI_SYNTAX_INVALID_TIMEOFDAY =
+ CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_SEVERE_WARNING |25;
+
+ /**
+ * The message ID for the message that will be used if an "aci" attribute
+ * type value parse failed because a bind rule timeofday expression failed
+ * to parse because the timeofday was not in a valid range. This takes one
+ * argument, which is the timeofday expression string parsed from the
+ * bind rule string.
+ */
+
+ public static final int MSGID_ACI_SYNTAX_INVALID_TIMEOFDAY_RANGE =
+ CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_SEVERE_WARNING |26;
+
+ /**
+ * The message ID for the message that will be used if an "aci" attribute
+ * type value parse failed because a bind rule authmethod expression failed
+ * to parse. This takes one argument, which is the authmethod expression
+ * string parsed from the bind rule string.
+ */
+
+ public static final int MSGID_ACI_SYNTAX_INVALID_AUTHMETHOD_EXPRESSION =
+ CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_SEVERE_WARNING |27;
+
+ /**
+ * The message ID for the message that will be used if an "aci" attribute
+ * type value parse failed because a bind rule userattr expression failed
+ * to decode. This takes one argument, the message from the
+ * thrown exception.
+ */
+ public static final int MSGID_ACI_SYNTAX_INVALID_USERATTR_EXPRESSION =
+ CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_SEVERE_WARNING | 28;
+
+ /**
+ * The message ID for the message that will be used if an "aci" attribute
+ * type value parse failed because a bind rule userattr expression value
+ * is not supported. This takes one argument, which is the userattr
+ * expression string parsed from the bind rule string.
+ */
+ public static final int MSGID_ACI_SYNTAX_INVALID_USERATTR_KEYWORD =
+ CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_SEVERE_WARNING | 29;
+
+ /**
+ * The message ID for the message that will be used if an "aci" attribute
+ * type value parse failed because a bind rule userattr expression
+ * inheritance pattern did not parse. This takes one argument, which
+ * is the userattr expression inheritance pattern string parsed
+ * from the bind rule string.
+ */
+ public static
+ final int MSGID_ACI_SYNTAX_INVALID_USERATTR_INHERITANCE_PATTERN =
+ CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_SEVERE_WARNING | 30;
+
+ /**
+ * The message ID for the message that will be used if an "aci" attribute
+ * type value parse failed because a bind rule userattr expression
+ * inheritance level exceeded the max value. This takes two arguments,
+ * which are the userattr expression inheritance pattern string parsed
+ * from the bind rule string and the max leval value.
+ */
+ public static
+ final int MSGID_ACI_SYNTAX_MAX_USERATTR_INHERITANCE_LEVEL_EXCEEDED =
+ CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_SEVERE_WARNING | 31;
+
+ /**
+ * The message ID for the message that will be used if an "aci" attribute
+ * type value parse failed because a bind rule userattr expression
+ * inheritance level was non-numeric. This takes one argument,
+ * which is the userattr expression inheritance level pattern string
+ * parsed from the bind rule string.
+ */
+ public static
+ final int MSGID_ACI_SYNTAX_INVALID_INHERITANCE_VALUE =
+ CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_SEVERE_WARNING | 32;
+
+ /**
+ * The message ID for the message that will be used if an "aci" attribute
+ * type value parse failed because a target rule had an invalid syntax.
+ * This takes one argument, which is the target rule string
+ * parsed from the "aci" attribute type value string.
+ */
+ public static final int MSGID_ACI_SYNTAX_INVALID_TARGET_SYNTAX =
+ CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_SEVERE_WARNING | 33;
+
+ /**
+ * The message ID for the message that will be used if an "aci" attribute
+ * type value failed parsing because of an invalid target keyword. This
+ * takes one argument, which is the target keyword string parsed
+ * from the "aci" attribute type value string.
+ */
+ public static final int MSGID_ACI_SYNTAX_INVALID_TARGET_KEYWORD =
+ CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_SEVERE_WARNING | 34;
+
+ /**
+ * The message ID for the message that will be used if an "aci" attribute
+ * type value failed parsing because of an invalid target keyword operator.
+ * This takes one argument, which is the target keyword operator string
+ * parsed from the "aci" attribute type value string.
+ */
+ public static final int MSGID_ACI_SYNTAX_INVALID_TARGET_OPERATOR =
+ CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_SEVERE_WARNING | 35;
+
+ /**
+ * The message ID for the message that will be used if an "aci" attribute
+ * type value failed parsing because of a target keyword is not supported
+ * at this time. This takes one argument, which is the unsupported target
+ * keyword string parsed from the "aci" attribute type value string.
+ */
+ public static final int MSGID_ACI_SYNTAX_TARGET_KEYWORD_NOT_SUPPORTED =
+ CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_SEVERE_WARNING | 36;
+
+ /**
+ * The message ID for the message that will be used if an "aci" attribute
+ * type value failed parsing because of a target keyword was seen multiple
+ * times in the value. This takes two arguments, which are the target
+ * keyword string parsed from the "aci" attribute type value string and
+ * the "aci" attribute type value string.
+ */
+ public static
+ final int MSGID_ACI_SYNTAX_INVALID_TARGET_DUPLICATE_KEYWORDS =
+ CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_SEVERE_WARNING | 37;
+
+ /**
+ * The message ID for the message that will be used if an "aci" attribute
+ * type value failed parsing because of an invalid targetscope keyword
+ * operator. This takes one argument, which is the targetscope keyword
+ * operator string parsed from the "aci" attribute type value string.
+ */
+ public static final int MSGID_ACI_SYNTAX_INVALID_TARGETSCOPE_OPERATOR =
+ CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_SEVERE_WARNING | 38;
+
+ /**
+ * The message ID for the message that will be used if an "aci" attribute
+ * type value failed parsing because of an invalid targetscope expression.
+ * This takes one argument, which is the targetscope expression
+ * string parsed from the "aci" attribute type value string.
+ */
+ public static final int MSGID_ACI_SYNTAX_INVALID_TARGETSCOPE_EXPRESSION =
+ CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_SEVERE_WARNING | 39;
+
+ /**
+ * The message ID for the message that will be used if an "aci" attribute
+ * type value failed parsing because of an invalid target keyword
+ * expression. This takes one argument, which is the target keyword
+ * expression string parsed from the "aci" attribute type value string.
+ */
+ public static final int MSGID_ACI_SYNTAX_INVALID_TARGETKEYWORD_EXPRESSION =
+ CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_SEVERE_WARNING | 40;
+
+ /**
+ * The message ID for the message that will be used if an "aci" attribute
+ * type value failed parsing because of a target keyword DN is not a
+ * descendant of the ACI entry DN. This takes two arguments, which are
+ * the target keyword DN string parsed from the "aci" attribute type value
+ * string and the DN of the "aci" attribute type entry.
+ */
+ public static
+ final int MSGID_ACI_SYNTAX_TARGET_DN_NOT_DESCENDENTOF =
+ CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_SEVERE_WARNING | 41;
+
+ /**
+ * The message ID for the message that will be used if an "aci" attribute
+ * type value failed parsing because of a targetattr keyword expression
+ * is invalid. This takes one argument, which is the targetattr
+ * keyword expression string parsed from the "aci" attribute type value
+ * string.
+ */
+ public static
+ final int MSGID_ACI_SYNTAX_INVALID_TARGETATTRKEYWORD_EXPRESSION =
+ CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_SEVERE_WARNING | 42;
+
+ /**
+ * The message ID for the message that will be used if an "aci" attribute
+ * type value failed parsing because of a targetfilter keyword expression
+ * string is invalid. This takes one argument, which is the targetfilter
+ * keyword expression string parsed from the "aci" attribute type value
+ * string.
+ */
+ public static
+ final int MSGID_ACI_SYNTAX_INVALID_TARGETFILTERKEYWORD_EXPRESSION =
+ CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_SEVERE_WARNING | 43;
+
+ /**
+ * The message ID for the ACI message that will be generated when a client
+ * attempts to add an entry with the "aci" attribute type
+ * and they do not have the required "modify-acl"privilege. This takes two
+ * arguments, which are the string representation of the entry DN of the
+ * entry being added, and the string representation of the
+ * authorization DN.
+ */
+ public static final int MSGID_ACI_ADD_FAILED_PRIVILEGE =
+ CATEGORY_MASK_ACCESS_CONTROL | 44;
+
+
+ /**
+ * The message ID for the ACI message that will be generated when a client
+ * attempts to perform a modification on an "aci" attribute type
+ * and they do not have the required "modify-acl"privilege. This takes two
+ * arguments, which are the string representation of the entry DN of the
+ * entry being modified, and the string representation of the
+ * authorization DN.
+ */
+ public static final int MSGID_ACI_MODIFY_FAILED_PRIVILEGE =
+ CATEGORY_MASK_ACCESS_CONTROL | 45;
+
+ /**
+ * The message ID for the ACI message that will be generated when a client
+ * attempts to add an entry with the "aci" attribute type
+ * and the ACI decode failed because of an syntax error. This takes one
+ * argument, which is the message string thrown by the AciException.
+ */
+ public static final int MSGID_ACI_ADD_FAILED_DECODE =
+ CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_SEVERE_WARNING | 46;
+
+ /**
+ * The message ID for the ACI message that will be generated when a client
+ * attempts to perform a modification on an "aci" attribute type
+ * and the ACI decode failed because of a syntax error. This takes one
+ * argument, which is the message string thrown by the AciException.
+ */
+ public static final int MSGID_ACI_MODIFY_FAILED_DECODE =
+ CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_SEVERE_WARNING | 47;
+
+ /**
+ * The message ID for the ACI message that will be generated when
+ * an ACI decode failed because of an syntax error. This message is usually
+ * generated by an invalid ACI that was added during import which
+ * fails the decode at server startup. This takes one
+ * argument, which is the message string thrown by the AciException.
+ */
+ public static final int MSGID_ACI_ADD_LIST_FAILED_DECODE =
+ CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_SEVERE_WARNING | 48;
+
+ /**
+ * The message ID for the ACI message that will be generated the server
+ * searches an directory context for "aci" attribute types and finds none.
+ * This takes one argument, which is the DN of the directory context.
+ */
+ public static final int MSGID_ACI_ADD_LIST_NO_ACIS =
+ CATEGORY_MASK_ACCESS_CONTROL | 49;
+
+ /**
+ * The message ID for the ACI message that will be generated the server
+ * searches an directory context for "aci" attribute types and finds some.
+ * This takes two arguments, which are the DN of the directory context,
+ * the number of valid ACIs decoded.
+ */
+ public static final int MSGID_ACI_ADD_LIST_ACIS =
+ CATEGORY_MASK_ACCESS_CONTROL | 50;
+
+ /**
+ * The message ID for the message that will be used if an "aci" attribute
+ * type value parse failed because a bind rule userattr roledn expression
+ * inheritance pattern did not parse. This takes one argument, which
+ * is the userattr expression inheritance pattern string parsed
+ * from the bind rule string.
+ */
+ public static
+ final int MSGID_ACI_SYNTAX_INVALID_USERATTR_ROLEDN_INHERITANCE_PATTERN =
+ CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_SEVERE_WARNING | 51;
+ /**
+ * Associates a set of generic messages with the message IDs defined in
+ * this class.
+ */
+ public static void registerMessages() {
+
+ registerMessage(MSGID_ACI_SYNTAX_GENERAL_PARSE_FAILED,
+ "The provided string \"%s\" could not be parsed as a valid " +
+ "Access Control Instruction (ACI) because it failed "+
+ "general ACI syntax evaluation.");
+
+ registerMessage(MSGID_ACI_SYNTAX_INVAILD_VERSION,
+ "The provided Access Control Instruction (ACI) version " +
+ "value \"%s\" is invalid, only the version 3.0 is " +
+ "supported.");
+
+ registerMessage(MSGID_ACI_SYNTAX_INVALID_ACCESS_TYPE_VERSION,
+ "The provided Access Control Instruction access " +
+ "type value \"%s\" is invalid. A valid access type " +
+ "value is either allow or deny.");
+
+ registerMessage(MSGID_ACI_SYNTAX_INVALID_RIGHTS_SYNTAX,
+ "The provided Access Control Instruction (ACI) rights " +
+ "values \"%s\" are invalid. The rights must be a " +
+ "list of 1 to 6 comma-separated keywords enclosed in " +
+ "parentheses.");
+
+ registerMessage(MSGID_ACI_SYNTAX_INVALID_RIGHTS_KEYWORD,
+ "The provided Access Control Instruction (ACI) rights " +
+ "keyword values \"%s\" are invalid. The valid rights " +
+ "keyword values are one or more of the following: read, " +
+ "write, add, delete, search, compare or the single value" +
+ "all.");
+
+ registerMessage(MSGID_ACI_SYNTAX_BIND_RULE_MISSING_CLOSE_PAREN,
+ "The provided Access Control Instruction (ACI) bind " +
+ "rule value \"%s\" is invalid because it is missing a " +
+ "close parenthesis that corresponded to the initial open " +
+ "parenthesis.");
+
+ registerMessage(MSGID_ACI_SYNTAX_INVALID_BIND_RULE_SYNTAX,
+ "The provided Access Control Instruction (ACI) bind rule " +
+ "value \"%s\" is invalid. A valid bind rule value must " +
+ "be in the following form: " +
+ "keyword operator \"expression\".");
+
+ registerMessage(MSGID_ACI_SYNTAX_INVALID_BIND_RULE_KEYWORD,
+ "The provided Access Control Instruction (ACI) bind rule " +
+ "keyword value \"%s\" is invalid. A valid keyword value is" +
+ " one of the following: userdn, groupdn, roledn, userattr," +
+ "ip, dns, dayofweek, timeofday or authmethod.");
+
+ registerMessage(MSGID_ACI_SYNTAX_INVALID_BIND_RULE_OPERATOR ,
+ "The provided Access Control Instruction (ACI) bind rule " +
+ "operator value \"%s\" is invalid. A valid bind rule " +
+ "operator value is either '=' or \"!=\".");
+
+ registerMessage(MSGID_ACI_SYNTAX_MISSING_BIND_RULE_EXPRESSION ,
+ "The provided Access Control Instruction (ACI) bind rule " +
+ "expression value corresponding to the keyword value " +
+ "\"%s\" is missing an expression. A valid bind rule value " +
+ "must be in the following form:" +
+ " keyword operator \"expression\".");
+
+ registerMessage(MSGID_ACI_SYNTAX_INVALID_BIND_RULE_BOOLEAN_OPERATOR ,
+ "The provided Access Control Instruction (ACI) bind rule " +
+ "boolean operator value \"%s\" is invalid. A valid bind" +
+ "rule boolean operator value is either \"OR\" or \"AND\".");
+
+ registerMessage(
+ MSGID_ACI_SYNTAX_INVALID_BIND_RULE_KEYWORD_OPERATOR_COMBO,
+ "The provided Access Control Instruction (ACI) bind rule " +
+ "keyword string \"%s\" is invalid for the bind rule " +
+ "operator string \"%s\".");
+
+ registerMessage(MSGID_ACI_SYNTAX_INVALID_USERDN_URL,
+ "The provided Access Control Instruction (ACI) bind rule " +
+ "userdn expression failed to URL decode for " +
+ "the following reason: %s");
+
+ registerMessage(MSGID_ACI_SYNTAX_INVALID_ROLEDN_EXPRESSION,
+ "The provided Access Control Instruction (ACI) bind rule " +
+ "roledn expression value \"%s\" is invalid. A valid roledn " +
+ "keyword expression value requires one or more LDAP URLs " +
+ "in the following format: " +
+ "ldap:///dn [|| ldap:///dn] ... [|| ldap:///dn].");
+
+ registerMessage(MSGID_ACI_SYNTAX_INVALID_ROLEDN_URL,
+ "The provided Access Control Instruction (ACI) bind rule " +
+ "roledn expression failed to URL decode for " +
+ "the following reason: %s");
+
+ registerMessage(MSGID_ACI_SYNTAX_INVALID_GROUPDN_EXPRESSION,
+ "The provided Access Control Instruction (ACI) bind rule " +
+ "groupdn expression value \"%s\" is invalid. A valid groupdn " +
+ "keyword expression value requires one or more LDAP URLs in the" +
+ " following format: " +
+ "ldap:///groupdn [|| ldap:///groupdn] ... [|| ldap:///groupdn].");
+
+ registerMessage(MSGID_ACI_SYNTAX_INVALID_GROUPDN_URL,
+ "The provided Access Control Instruction (ACI) bind rule " +
+ "groupdn expression value failed to URL decode for " +
+ "the following reason: %s");
+
+ registerMessage(MSGID_ACI_SYNTAX_ADDRESS_FAMILY_MISMATCH,
+ "The network mask value \"%s\" is not valid for " +
+ "the ip expression network address \"%s\".");
+
+ registerMessage(MSGID_ACI_SYNTAX_INVALID_NETWORK_BIT_MATCH,
+ "The bit mask for address type value \"%s\" is not valid." +
+ "%s.");
+
+ registerMessage(MSGID_ACI_SYNTAX_INVALID_IP_CRITERIA_DECODE,
+ "The provided Access Control Instruction (ACI) bind rule " +
+ "ip expression value failed to decode for " +
+ "the following reason: %s");
+
+ registerMessage(MSGID_ACI_SYNTAX_INVALID_IP_EXPRESSION,
+ "The provided Access Control Instruction (ACI) bind rule " +
+ "ip expression value \"%s\" is invalid. A valid ip " +
+ "keyword expression value requires one or more" +
+ "comma-separated elements of an IP address list expression.");
+
+ registerMessage(MSGID_ACI_SYNTAX_INVALID_DNS_EXPRESSION,
+ "The provided Access Control Instruction (ACI) bind rule " +
+ "dns expression value \"%s\" is invalid. A valid dns " +
+ "keyword expression value requires a valid fully qualified"+
+ " DNS domain name.");
+
+
+ registerMessage(MSGID_ACI_SYNTAX_INVALID_DNS_WILDCARD,
+ "The provided Access Control Instruction (ACI) bind rule " +
+ "dns expression value \"%s\" is invalid, because a wild-card" +
+ " pattern was found in the wrong position. A valid dns " +
+ "keyword wild-card expression value requires the '*' " +
+ "character only be in the leftmost position of the " +
+ "domain name.");
+
+ registerMessage(MSGID_ACI_SYNTAX_INVALID_DAYOFWEEK,
+ "The provided Access Control Instruction (ACI) bind rule " +
+ "dayofweek expression value \"%s\" is invalid, because of " +
+ "an invalid day of week value. A valid dayofweek value " +
+ "is one of the following English three-letter abbreviations" +
+ "for the days of the week: sun, mon, tue, wed, thu, " +
+ "fri, or sat.");
+
+ registerMessage(MSGID_ACI_SYNTAX_INVALID_TIMEOFDAY,
+ "The provided Access Control Instruction (ACI) bind rule " +
+ "timeofday expression value \"%s\" is invalid. A valid " +
+ "timeofday value is expressed as four digits representing " +
+ "hours and minutes in the 24-hour clock (0 to 2359).");
+
+ registerMessage(MSGID_ACI_SYNTAX_INVALID_TIMEOFDAY_RANGE,
+ "The provided Access Control Instruction (ACI) bind rule " +
+ "timeofday expression value \"%s\" is not in the valid" +
+ " range. A valid timeofday value is expressed as four" +
+ " digits representing hours and minutes in the 24-hour" +
+ " clock (0 to 2359).");
+
+ registerMessage(MSGID_ACI_SYNTAX_INVALID_AUTHMETHOD_EXPRESSION,
+ "The provided Access Control Instruction (ACI) bind rule " +
+ "authmethod expression value \"%s\" is invalid. A valid " +
+ "authmethod value is one of the following: none, simple," +
+ "SSL, sasl EXTERNAL, sasl DIGEST-MD5, or sasl GSSAPI.");
+
+ registerMessage(MSGID_ACI_SYNTAX_INVALID_USERATTR_EXPRESSION,
+ "The provided Access Control Instruction (ACI) bind rule " +
+ "userattr expression value \"%s\" is invalid.");
+
+ registerMessage(MSGID_ACI_SYNTAX_INVALID_USERATTR_KEYWORD,
+ "The provided Access Control Instruction (ACI) bind rule " +
+ "userattr expression value \"%s\" is not supported.");
+
+ registerMessage(MSGID_ACI_SYNTAX_INVALID_USERATTR_INHERITANCE_PATTERN,
+ "The provided Access Control Instruction (ACI) bind rule " +
+ "userattr expression inheritance pattern value \"%s\" is " +
+ "invalid. A valid inheritance pattern value must have" +
+ "the following format:" +
+ " parent[inheritance_level].attribute#bindType.");
+
+ registerMessage(
+ MSGID_ACI_SYNTAX_MAX_USERATTR_INHERITANCE_LEVEL_EXCEEDED,
+ "The provided Access Control Instruction (ACI) bind rule " +
+ "userattr expression inheritance pattern value \"%s\" is " +
+ "invalid. The inheritance level value cannot exceed the" +
+ "max level limit of %s.");
+
+ registerMessage(
+ MSGID_ACI_SYNTAX_INVALID_INHERITANCE_VALUE,
+ "The provided Access Control Instruction (ACI) bind rule " +
+ "userattr expression inheritance pattern value \"%s\" is" +
+ " invalid because it is non-numeric.");
+
+ registerMessage(MSGID_ACI_SYNTAX_INVALID_TARGET_SYNTAX,
+ "The provided Access Control Instruction (ACI) target rule" +
+ "value \"%s\" is invalid. A valid target rule value must" +
+ "be in the following form: " +
+ "keyword operator \"expression\".");
+
+ registerMessage(MSGID_ACI_SYNTAX_INVALID_TARGET_KEYWORD,
+ "The provided Access Control Instruction (ACI) target " +
+ "keyword value \"%s\" is invalid. A valid target keyword" +
+ " value is one of the following: target, targetscope, " +
+ "targetfilter, targetattr or targetattrfilters.");
+
+ registerMessage(MSGID_ACI_SYNTAX_INVALID_TARGET_OPERATOR,
+ "The provided Access Control Instruction (ACI) target " +
+ "keyword operator value \"%s\" is invalid. A valid target" +
+ "keyword operator value is either '=' or \"!=\".");
+
+ registerMessage(MSGID_ACI_SYNTAX_TARGET_KEYWORD_NOT_SUPPORTED,
+ "The provided Access Control Instruction (ACI) " +
+ "target keyword value \"%s\" is not supported at this time.");
+
+ registerMessage(MSGID_ACI_SYNTAX_INVALID_TARGET_DUPLICATE_KEYWORDS,
+ "The provided Access Control Instruction (ACI) " +
+ "target keyword value \"%s\" was seen multiple times in" +
+ " the ACI \"%s\".");
+
+ registerMessage(MSGID_ACI_SYNTAX_INVALID_TARGETSCOPE_OPERATOR,
+ "The provided Access Control Instruction (ACI) targetscope" +
+ " keyword operator value \"%s\" is invalid. The only valid" +
+ "targetscope operator value is '='.");
+
+ registerMessage(MSGID_ACI_SYNTAX_INVALID_TARGETSCOPE_EXPRESSION,
+ "The provided Access Control Instruction (ACI) targetscope" +
+ " expression operator value \"%s\" is invalid. A valid" +
+ " targetscope expression value is one of the following: one," +
+ " onelevel or subtree.");
+
+ registerMessage(MSGID_ACI_SYNTAX_INVALID_TARGETKEYWORD_EXPRESSION,
+ "The provided Access Control Instruction (ACI)" +
+ " target expression value \"%s\" is invalid. A valid target" +
+ " keyword expression value requires a LDAP URL in the" +
+ " following format: ldap:///distinguished_name.");
+
+ registerMessage(MSGID_ACI_SYNTAX_TARGET_DN_NOT_DESCENDENTOF,
+ "The provided Access Control Instruction (ACI) " +
+ "target expression DN value \"%s\" is invalid. The target " +
+ "expression DN value must be a descendant of the ACI entry" +
+ " DN \"%s\", if no wild-card is specified in the target" +
+ "expression DN.");
+
+ registerMessage(MSGID_ACI_SYNTAX_INVALID_TARGETATTRKEYWORD_EXPRESSION,
+ "The provided Access Control Instruction (ACI) " +
+ "targetattr expression value \"%s\" is invalid. A valid " +
+ "targetattr keyword expression value requires one or more " +
+ "attribute type values in the following format: " +
+ "attribute1 [|| attribute1] ... [|| attributen].");
+
+ registerMessage(
+ MSGID_ACI_SYNTAX_INVALID_TARGETFILTERKEYWORD_EXPRESSION,
+ "The provided Access Control Instruction (ACI)" +
+ " targetfilter expression value \"%s\" is invalid because it" +
+ " is not a valid LDAP filter.");
+
+ registerMessage(MSGID_ACI_ADD_FAILED_PRIVILEGE,
+ "An attempt to add the entry \"%s\" containing" +
+ " an aci attribute type failed, because the authorization DN" +
+ " \"%s\" lacked modify-acl privileges.");
+
+ registerMessage(MSGID_ACI_MODIFY_FAILED_PRIVILEGE,
+ "An attempt to modify an aci "+
+ "attribute type in the entry \"%s\" failed, because the" +
+ "authorization DN \"%s\" lacked modify-acl privileges.");
+
+ registerMessage(MSGID_ACI_ADD_FAILED_DECODE,
+ "An attempt to add the entry \"%s\" containing" +
+ " an aci attribute type failed because of the following" +
+ " reason: %s");
+
+ registerMessage(MSGID_ACI_MODIFY_FAILED_DECODE,
+ "An attempt to modify an aci "+
+ "attribute type in the entry \"%s\" failed "+
+ "because of the following reason: %s");
+
+ registerMessage(MSGID_ACI_ADD_LIST_FAILED_DECODE,
+ "An attempt to decode an Access Control Instruction (ACI)" +
+ " failed because of the following reason: %s");
+
+ registerMessage(MSGID_ACI_ADD_LIST_NO_ACIS,
+ "No Access Control Instruction (ACI) attribute types were" +
+ " found in context \"%s\".");
+
+ registerMessage(MSGID_ACI_ADD_LIST_ACIS,
+ "Added %s Access Control Instruction (ACI) attribute types" +
+ " found in context \"%s\" to the access" +
+ "control evaluation engine.");
+
+ registerMessage(
+ MSGID_ACI_SYNTAX_INVALID_USERATTR_ROLEDN_INHERITANCE_PATTERN,
+ "The provided Access Control Instruction (ACI) bind rule " +
+ "userattr expression inheritance pattern value " +
+ "\"%s\" is invalid for the roledn keyword because it starts " +
+ "with the string \"parent[\".");
+ }
+}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciProvider.java b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciProvider.java
new file mode 100644
index 0000000..05a71e1
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciProvider.java
@@ -0,0 +1,85 @@
+/*
+ * 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 org.opends.server.api.AccessControlHandler;
+import org.opends.server.api.AccessControlProvider;
+import org.opends.server.config.ConfigEntry;
+import org.opends.server.config.ConfigException;
+import org.opends.server.types.InitializationException;
+import static org.opends.server.loggers.Debug.debugConstructor;
+
+/**
+ * This class is the provider class for the dseecompt ACI.
+ */
+public class AciProvider implements AccessControlProvider {
+
+ private static final String CLASS_NAME =
+ "org.opends.server.authorization.dseecompat.AciProvider";
+
+ private static AciHandler instance = null;
+
+ /**
+ * Create an aci provider. This doesn't do much.
+ */
+ public AciProvider() {
+ super();
+ assert debugConstructor(CLASS_NAME);
+ }
+
+
+ /**
+ * Creates the AciHandler class and calls its initialization method.
+ * @param configEntry The entry containing the configuration Access Control
+ * entry.
+ * @throws ConfigException If the initialization fails.
+ * @throws InitializationException If the initialization fails.
+ */
+ public void initializeAccessControlHandler(ConfigEntry configEntry)
+ throws ConfigException, InitializationException {
+ getInstance();
+ }
+
+ /**
+ * Returns a new AciHandler instance. There can be only one active.
+ * @return A new AciHandler instance.
+ */
+ public AccessControlHandler getInstance() {
+ if (instance == null) {
+ instance = new AciHandler();
+ }
+ return instance;
+ }
+
+ /**
+ * Not used at this time.
+ */
+ public void finalizeAccessControlHandler() {
+
+ }
+}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciTargetMatchContext.java b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciTargetMatchContext.java
new file mode 100644
index 0000000..2276a50
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciTargetMatchContext.java
@@ -0,0 +1,134 @@
+/*
+ * 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 org.opends.server.types.AttributeType;
+import org.opends.server.types.AttributeValue;
+import org.opends.server.types.Entry;
+import java.util.LinkedList;
+
+/**
+ * The AciTargetMatchContext interface provides a
+ * view of an AciContainer that exposes information to be
+ * used by the Aci.isApplicable() method to determine if
+ * an ACI is applicable (targets matched) to the LDAP operation,
+ * operation rights and entry and attributes having access
+ * checked on.
+ */
+public interface AciTargetMatchContext {
+ /**
+ * Set the deny ACI list.
+ * @param denyList The deny ACI list.
+ */
+ public void setDenyList(LinkedList<Aci> denyList);
+
+ /**
+ * Set the allow ACI list.
+ * @param allowList The list of allow ACIs.
+ */
+ public void setAllowList(LinkedList<Aci> allowList);
+
+ /**
+ * Get the entry being evaluated. This is known as the
+ * resource entry.
+ * @return The entry being evaluated.
+ */
+ public Entry getResourceEntry();
+
+ /**
+ * Get the current attribute type being evaluated.
+ * @return The attribute type being evaluated.
+ */
+ public AttributeType getCurrentAttributeType();
+
+ /**
+ * The current attribute type value being evaluated.
+ * @return The current attribute type value being evaluated.
+ */
+ public AttributeValue getCurrentAttributeValue();
+
+ /**
+ * True if the first attribute of the resource entry is being evaluated.
+ * @return True if this is the first attribute.
+ */
+ public boolean isFirstAttribute();
+
+ /**
+ * Set to true if the first attribute of the resource entry is
+ * being evaluated.
+ * @param isFirst True if this is the first attribute of the
+ * resource entry being evaluated.
+ */
+ public void setIsFirstAttribute(boolean isFirst);
+
+ /**
+ * Set the attribute type to be evaluated.
+ * @param type The attribute type to set to.
+ */
+ public void setCurrentAttributeType(AttributeType type);
+
+ /**
+ * Set the attribute value to be evaluated.
+ * @param v The current attribute value to set to.
+ */
+ public void setCurrentAttributeValue(AttributeValue v);
+
+ /**
+ * True if the target matching code found an entry test rule. An
+ * entry test rule is an ACI without a targetattr target rule.
+ * @param val True if an entry test rule was found.
+ */
+ public void setEntryTestRule(boolean val);
+
+ /**
+ * True if an entry test rule was found.
+ * @return True if an entry test rule was found.
+ */
+ public boolean hasEntryTestRule();
+
+ /**
+ * Return the rights for this container's LDAP operation.
+ * @return The rights for the container's LDAP operation.
+ */
+ public int getRights();
+
+ /**
+ * Checks if the container's rights has the specified rights.
+ * @param rights The rights to check for.
+ * @return True if the container's rights has the specified rights.
+ */
+ public boolean hasRights(int rights);
+
+ /**
+ * Set the rights of the container to the specified rights.
+ * @param rights The rights to set the container's rights to.
+ */
+ public void setRights(int rights);
+}
+
+
diff --git a/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciTargets.java b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciTargets.java
new file mode 100644
index 0000000..8352955
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciTargets.java
@@ -0,0 +1,472 @@
+/*
+ * 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 org.opends.server.types.AttributeType;
+import org.opends.server.types.DN;
+import org.opends.server.types.SearchScope;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * This class represents target part of an ACI's syntax. This is the part
+ * of an ACI before the ACI body and specifies the entry, attributes, or set
+ * of entries and attributes which the ACI controls access.
+ *
+ * The four supported ACI target keywords currently
+ * supported are: target, targetattr, targetscope and targetfilter.
+ * Missing is support for targetattrfilters.
+ */
+public class AciTargets {
+ /*
+ * ACI syntax has a target keyword.
+ */
+ private Target target = null ;
+ /*
+ * ACI syntax has a targetscope keyword.
+ */
+ private SearchScope targetScope = SearchScope.WHOLE_SUBTREE;
+ /*
+ * ACI syntax has a targetattr keyword.
+ */
+ private TargetAttr targetAttr = null ;
+ /*
+ * ACI syntax has a targetfilter keyword.
+ */
+ private TargetFilter targetFilter=null;
+
+ private TargAttrFilters targAttrFilters=null;
+ /*
+ * These are used in the regular expression parsing.
+ */
+ private static final int targetElementCount = 3;
+ private static final int targetKeywordPos = 1;
+ private static final int targetOperatorPos = 2;
+ private static final int targetExpressionPos = 3;
+ /*
+ * TODO Make the regular expression strings below easier to
+ * understand.
+ *
+ * The same note earlier about making regex values easier to
+ * understand applies to this class as well.
+ */
+ private static final String targetRegex =
+ "\\(\\s*(\\w+)\\s*(!?=)\\s*\"([^\"]+)\"\\s*\\)\\s*";
+ /**
+ * Regular expression used in target matching.
+ */
+ public static final String targetsRegex = "(" + targetRegex + ")*";
+
+ /*
+ * Rights that are skipped for certain target evaluations.
+ * The test is use the skipRights array is:
+ *
+ * Either the ACI has a targetattr's rule and the current
+ * attribute type is null or the current attribute type has
+ * a type specified and the targetattr's rule is null.
+ *
+ * The actual check against the skipRights array is:
+ *
+ * 1. Is the ACI's rights in this array? For example,
+ * allow(all) or deny(add)
+ *
+ * AND
+ *
+ * 2. Is the rights from the LDAP operation in this array? For
+ * example, an LDAP add would have rights of add and all.
+ *
+ * If both are true, than the target match test returns true
+ * for this ACI.
+ */
+
+ private static final int skipRights =
+ (AciHandler.ACI_ADD | AciHandler.ACI_DELETE | AciHandler.ACI_PROXY);
+
+ /**
+ * Creates an ACI target from the specified arguments. All of these
+ * may be null -- the ACI has no targets an will use defaults.
+ * @param targetEntry The ACI target keyword if any.
+ * @param targetAttr The ACI targetattr keyword if any.
+ * @param targetFilter The ACI targetfilter keyword if any.
+ * @param targetScope The ACI targetscope keyword if any.
+ */
+ private AciTargets(Target targetEntry, TargetAttr targetAttr,
+ TargetFilter targetFilter,
+ SearchScope targetScope) {
+ this.target=targetEntry;
+ this.targetAttr=targetAttr;
+ this.targetScope=targetScope;
+ this.targetFilter=targetFilter;
+ }
+
+ /**
+ * Return class representing the ACI target keyword. May be
+ * null. The default is the use the DN of the entry containing
+ * the ACI and check if the resource entry is a descendant of that.
+ * @return The ACI target class.
+ */
+ public Target getTarget() {
+ return target;
+ }
+
+ /**
+ * Return class representing the ACI targetattr keyword. May be null.
+ * The default is to not match any attribute types in an entry.
+ * @return The ACI targetattr class.
+ */
+ public TargetAttr getTargetAttr() {
+ return targetAttr;
+ }
+
+ /**
+ * Return the ACI targetscope keyword. Default is WHOLE_SUBTREE.
+ * @return The ACI targetscope information.
+ */
+ public SearchScope getTargetScope() {
+ return targetScope;
+ }
+
+ /**
+ * Return class representing the ACI targetfilter keyword. May be null.
+ * @return The targetscope information.
+ */
+ public TargetFilter getTargetFilter() {
+ return targetFilter;
+ }
+
+ /**
+ * Decode an ACI's target part of the syntax from the string provided.
+ * @param input String representing an ACI target part of syntax.
+ * @param dn The DN of the entry containing the ACI.
+ * @return An AciTargets class representing the decoded ACI target string.
+ * @throws AciException If the provided string contains errors.
+ */
+ public static AciTargets decode(String input, DN dn)
+ throws AciException {
+ Target target=null;
+ TargetAttr targetAttr=null;
+ TargetFilter targetFilter=null;
+ TargAttrFilters targAttrFilters=null;
+ SearchScope targetScope=SearchScope.WHOLE_SUBTREE;
+ Pattern targetPattern = Pattern.compile(targetRegex);
+ Matcher targetMatcher = targetPattern.matcher(input);
+ while (targetMatcher.find())
+ {
+ if (targetMatcher.groupCount() != targetElementCount) {
+ int msgID = MSGID_ACI_SYNTAX_INVALID_TARGET_SYNTAX;
+ String message = getMessage(msgID, input);
+ throw new AciException(msgID, message);
+ }
+ String keyword = targetMatcher.group(targetKeywordPos);
+ EnumTargetKeyword targetKeyword =
+ EnumTargetKeyword.createKeyword(keyword);
+ if (targetKeyword == null) {
+ int msgID = MSGID_ACI_SYNTAX_INVALID_TARGET_KEYWORD;
+ String message = getMessage(msgID, keyword );
+ throw new AciException(msgID, message);
+ }
+ String operator =
+ targetMatcher.group(targetOperatorPos);
+ EnumTargetOperator targetOperator =
+ EnumTargetOperator.createOperator(operator);
+ if (targetOperator == null) {
+ int msgID = MSGID_ACI_SYNTAX_INVALID_TARGET_OPERATOR;
+ String message = getMessage(msgID, operator);
+ throw new AciException(msgID, message);
+ }
+ String expression = targetMatcher.group(targetExpressionPos);
+ switch(targetKeyword)
+ {
+ case KEYWORD_TARGET:
+ {
+ if (target == null){
+ target = Target.decode(targetOperator, expression, dn);
+ }
+ else
+ {
+ int msgID =
+ MSGID_ACI_SYNTAX_INVALID_TARGET_DUPLICATE_KEYWORDS;
+ String message =
+ getMessage(msgID, "target", input);
+ throw new AciException(msgID, message);
+ }
+ break;
+ }
+ case KEYWORD_TARGETATTR:
+ {
+ if (targetAttr == null){
+ targetAttr = TargetAttr.decode(targetOperator,
+ expression);
+ }
+ else {
+ int msgID =
+ MSGID_ACI_SYNTAX_INVALID_TARGET_DUPLICATE_KEYWORDS;
+ String message =
+ getMessage(msgID, "targetattr", input);
+ throw new AciException(msgID, message);
+ }
+ break;
+ }
+ case KEYWORD_TARGETSCOPE:
+ {
+ // Check the operator for the targetscope is EQUALITY
+ if (targetOperator == EnumTargetOperator.NOT_EQUALITY) {
+ int msgID = MSGID_ACI_SYNTAX_INVALID_TARGETSCOPE_OPERATOR;
+ String message = getMessage(msgID, operator);
+ throw new AciException(msgID, message);
+ }
+ targetScope=createScope(expression);
+ break;
+ }
+ case KEYWORD_TARGETFILTER:
+ {
+ if (targetFilter == null){
+ targetFilter = TargetFilter.decode(targetOperator,
+ expression);
+ }
+ else {
+ int msgID =
+ MSGID_ACI_SYNTAX_INVALID_TARGET_DUPLICATE_KEYWORDS;
+ String message =
+ getMessage(msgID, "targetfilter", input);
+ throw new AciException(msgID, message);
+ }
+ break;
+ }
+ case KEYWORD_TARGATTRFILTERS:
+ {
+ if (targAttrFilters == null){
+ targAttrFilters = TargAttrFilters.decode(targetOperator,
+ expression);
+ }
+ else {
+ int msgID =
+ MSGID_ACI_SYNTAX_INVALID_TARGET_DUPLICATE_KEYWORDS;
+ String message =
+ getMessage(msgID, "targattrfilters", input);
+ throw new AciException(msgID, message);
+ }
+ break;
+ }
+ }
+ }
+ return new AciTargets(target, targetAttr, targetFilter, targetScope);
+ }
+
+ /*
+ * TODO Add support for the SearchScope.SUBORDINATE_SUBTREE scope.
+ */
+ /**
+ * Evaluates a provided scope string and returns an appropriate
+ * SearchScope enumeration.
+ * @param expression The expression string.
+ * @return An search scope enumeration matching the string.
+ * @throws AciException If the expression is an invalid targetscope
+ * string.
+ */
+ private static SearchScope createScope(String expression)
+ throws AciException {
+ if(expression.equalsIgnoreCase("base"))
+ return SearchScope.BASE_OBJECT;
+ else if(expression.equalsIgnoreCase("onelevel"))
+ return SearchScope.SINGLE_LEVEL;
+ else if(expression.equalsIgnoreCase("subtree"))
+ return SearchScope.WHOLE_SUBTREE;
+ else {
+ int msgID =
+ MSGID_ACI_SYNTAX_INVALID_TARGETSCOPE_EXPRESSION;
+ String message = getMessage(msgID, expression);
+ throw new AciException(msgID, message);
+ }
+ }
+
+ /**
+ * Checks an ACI's targetfilter information against an target match
+ * context.
+ * @param aci The ACI to try an match the targetfilter of.
+ * @param matchCtx The target match context containing information needed
+ * to perform a target match.
+ * @return True if the targetfilter matched the target context.
+ */
+ public static boolean isTargetFilterApplicable(Aci aci,
+ AciTargetMatchContext matchCtx) {
+ boolean ret=true;
+ TargetFilter targetFilter=aci.getTargets().getTargetFilter();
+ if(targetFilter != null)
+ ret=targetFilter.isApplicable(matchCtx);
+ return ret;
+ }
+
+ /*
+ * TODO Evaluate making this method more efficient.
+ * The isTargetAttrApplicable method looks a lot less efficient than it
+ * could be with regard to the logic that it employs and the repeated use
+ * of method calls over local variables.
+ */
+ /**
+ * Checks an provided ACI's targetattr information against a target match
+ * context.
+ * @param aci The ACI to evaluate.
+ * @param targetMatchCtx The target match context to check the ACI against.
+ * @return True if the targetattr matched the target context.
+ */
+ public static boolean isTargetAttrApplicable(Aci aci,
+ AciTargetMatchContext targetMatchCtx) {
+ boolean ret=true;
+ AciTargets targets=aci.getTargets();
+ AttributeType a=targetMatchCtx.getCurrentAttributeType();
+ int rights=targetMatchCtx.getRights();
+ boolean isFirstAttr=targetMatchCtx.isFirstAttribute();
+ if((a != null) && (targets.getTargetAttr() != null)) {
+ ret=TargetAttr.isApplicable(a, targets.getTargetAttr());
+ } else if((a != null) || (targets.getTargetAttr() != null)) {
+ if((aci.hasRights(skipRights)) && (skipRightsHasRights(rights))) {
+ ret=true;
+ } else if ((targets.getTargetAttr() != null) &&
+ (a == null) && (aci.hasRights(AciHandler.ACI_WRITE))) {
+ ret = true;
+ } else {
+ ret = false;
+ }
+ }
+ if((isFirstAttr) && (aci.getTargets().getTargetAttr() == null))
+ targetMatchCtx.setEntryTestRule(true);
+ return ret;
+ }
+
+ /**
+ * Try and match a one or more of the specified rights in the skiprights
+ * mask.
+ * @param rights The rights to check for.
+ * @return True if the one or more of the specified rights are in the
+ * skiprights rights mask.
+ */
+ public static boolean skipRightsHasRights(int rights) {
+ return ((skipRights & rights) == rights);
+ }
+
+ /*
+ * TODO Track DS 6.1 changes to ONELEVEL scope.
+ *
+ * The isTargetApplicable method appears to handle the ONELEVEL scope
+ * incorrectly. The standard definition of onelevel only includes
+ * the immediate children of a given entry -- it does not include that
+ * entry itself. It is a bug for the server to behave in any other way.
+ * Unfortunately, it does appear that the implementation you currently
+ * have matches the implementation in DS6. Nevertheless, I don't think
+ * that it is acceptable use this standard term in a nonstandard way and
+ * therefore we must change it to the standards-compliant interpretation
+ * which does not include the parent.
+ *
+ * TODO Investigate supporting alternative representations of the scope.
+ *
+ * Should we also consider supporting alternate representations of the
+ * scope values (in particular, allow "one" in addition to "onelevel"
+ * and "sub" in addition to "subtree") to match the very common
+ * abbreviations in widespread use for those terms?
+ */
+ /**
+ * Checks an provided ACI's target information against an target match
+ * context.
+ * @param aci The ACI to match the target against.
+ * @param matchCtx The target match context to check the ACI against.
+ * @return True if the target matched the context.
+ */
+ public static boolean isTargetApplicable(Aci aci,
+ AciTargetMatchContext matchCtx) {
+ boolean ret=true;
+ DN entryDN=matchCtx.getResourceEntry().getDN();
+ DN targetDN=aci.getDN();
+ AciTargets targets=aci.getTargets();
+
+ /*
+ * Scoping of the ACI uses either the DN of the entry
+ * containing the ACI (aci.getDN above), or if the ACI item
+ * contains a simple target DN and a equality operator that
+ * target DN is used.
+ */
+ if((targets.getTarget() != null) &&
+ (!targets.getTarget().isPattern())) {
+ EnumTargetOperator op=targets.getTarget().getOperator();
+ if(op != EnumTargetOperator.NOT_EQUALITY)
+ targetDN=targets.getTarget().getDN();
+ }
+ switch(targets.getTargetScope()) {
+ case BASE_OBJECT:
+ if(!targetDN.equals(entryDN))
+ return false;
+ break;
+ case SINGLE_LEVEL:
+ if((!targetDN.equals(entryDN)) &&
+ (!entryDN.getParent().equals(targetDN)))
+ return false;
+ break;
+ case WHOLE_SUBTREE:
+ if(!entryDN.isDescendantOf(targetDN))
+ return false;
+ break;
+ /*
+ * TODO Add support for the SearchScope.SUBORDINATE_SUBTREE scope.
+ *
+ * The isTargetApplicable method doesn't account for the subordinate
+ * subtree search scope.
+ */
+ default:
+ return false;
+ }
+ /*
+ * The entry is in scope. For inequality checks, scope was tested
+ * against the entry containing the ACI. If operator is inequality,
+ * check that it doesn't match the target DN.
+ */
+ if((targets.getTarget() != null) &&
+ (!targets.getTarget().isPattern())) {
+ EnumTargetOperator op=targets.getTarget().getOperator();
+ if(op == EnumTargetOperator.NOT_EQUALITY) {
+ DN tmpDN=targets.getTarget().getDN();
+ if(entryDN.isDescendantOf(tmpDN))
+ return false;
+ }
+ }
+ /*
+ * There is a pattern, need to match the substring filter
+ * created when the ACI was decoded. If inequality flip the
+ * result.
+ */
+ if((targets.getTarget() != null) &&
+ (targets.getTarget().isPattern())) {
+ ret=targets.getTarget().matchesPattern(entryDN);
+ EnumTargetOperator op=targets.getTarget().getOperator();
+ if(ret && op == EnumTargetOperator.NOT_EQUALITY)
+ ret=!ret;
+ }
+ return ret;
+ }
+}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AuthMethod.java b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AuthMethod.java
new file mode 100644
index 0000000..2027bed
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AuthMethod.java
@@ -0,0 +1,112 @@
+/*
+ * 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;
+
+/**
+ * The AuthMethod class represents an authmethod bind rule keyword expression.
+ */
+public class AuthMethod implements KeywordBindRule {
+ private EnumAuthMethod authMethod=null;
+ private EnumBindRuleType type=null;
+
+ /**
+ * Create a class representing an authmethod bind rule keyword from the
+ * provided method and bind rule type.
+ * @param method An enumeration representing the method of the expression.
+ * @param type An enumeration representing the type of the expression.
+ */
+ private AuthMethod(EnumAuthMethod method, EnumBindRuleType type) {
+ this.authMethod=method;
+ this.type=type;
+ }
+
+ /**
+ * Decode a string representing a authmethod bind rule.
+ * @param expr The string representing the bind rule.
+ * @param type An enumeration representing the bind rule type.
+ * @return An keyword bind rule class that can be used to evaluate the
+ * bind rule.
+ * @throws AciException If the expression string is invalid.
+ */
+ public static KeywordBindRule decode(String expr, EnumBindRuleType type)
+ throws AciException {
+ EnumAuthMethod method=EnumAuthMethod.createAuthmethod(expr);
+ if (method == null)
+ {
+ int msgID = MSGID_ACI_SYNTAX_INVALID_AUTHMETHOD_EXPRESSION;
+ String message = getMessage(msgID, expr);
+ throw new AciException(msgID, message);
+ }
+ return new AuthMethod(method, type);
+ }
+
+ /*
+ * TODO Evaluate if AUTHMETHOD_NONE processing is correct. This was fixed
+ * prior to Neil's review. Verify in a unit test.
+ *
+ * I'm not sure that the evaluate() method handles AUTHMETHOD_NONE
+ * correctly. My understanding is that it should only match in cases
+ * in which no authentication has been performed, but you have it
+ * always matching.
+ */
+ /**
+ * Evaluate authmethod bind rule using the provided evaluation context.
+ * @param evalCtx An evaluation context to use.
+ * @return An enumeration evaluation result.
+ */
+ public EnumEvalResult evaluate(AciEvalContext evalCtx) {
+ EnumEvalResult matched=EnumEvalResult.FALSE;
+ if(authMethod==EnumAuthMethod.AUTHMETHOD_NONE) {
+ matched=EnumEvalResult.TRUE;
+ } else if(authMethod==EnumAuthMethod.AUTHMETHOD_SIMPLE) {
+ if(evalCtx.getAuthenticationMethod(false)
+ == EnumAuthMethod.AUTHMETHOD_SIMPLE){
+ matched=EnumEvalResult.TRUE;
+ }
+ } else if(authMethod == EnumAuthMethod.AUTHMETHOD_SSL) {
+ /*
+ * TODO Verfiy that SSL authemethod is correctly handled in a
+ * unit test.
+ * I'm not sure that the evaluate() method correctly handles
+ * SASL EXTERNAL in all cases. My understanding is that in
+ * DS 5/6, an authmethod of SSL is the same as an authmethod of
+ * SASL EXTERNAL. If that's true, then you don't properly handle
+ * that condition.
+ */
+ if(authMethod == evalCtx.getAuthenticationMethod(true))
+ matched=EnumEvalResult.TRUE;
+ } else {
+ if(authMethod ==evalCtx.getAuthenticationMethod(false))
+ matched=EnumEvalResult.TRUE;
+ }
+ return matched.getRet(type, false);
+ }
+}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/BindRule.java b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/BindRule.java
new file mode 100644
index 0000000..d9e7888
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/BindRule.java
@@ -0,0 +1,538 @@
+/*
+ * 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.regex.Pattern;
+import java.util.regex.Matcher;
+import java.util.HashMap;
+
+/**
+ * This class represents a single bind rule of an ACI permission-bind rule
+ * pair.
+ */
+public class BindRule {
+ /*
+ * This hash table holds the keyword bind rule mapping.
+ */
+ private HashMap<String, KeywordBindRule> keywordRuleMap =
+ new HashMap<String, KeywordBindRule>();
+
+ //True is a boolean "not" was seen.
+ private boolean negate=false;
+
+ //Complex bind rules have left and right values.
+ private BindRule left = null;
+ private BindRule right = null;
+
+ //Enumeration of the boolean type of the complex bind rule ("and" or "or").
+ private EnumBooleanTypes booleanType = null;
+
+ //The keyword of a simple bind rule.
+ private EnumBindRuleKeyword keyword = null;
+
+ //Regular expression stuff that needs to be made clearer.
+ private static final int keywordPos = 1;
+ private static final int opPos = 2;
+ private static final int expressionPos = 3;
+ private static final String keywordRegex = "^(\\w+)";
+ private static final String opRegex = "([!=<>]+)";
+ private static final String expressionRegex = "\"([^\"]+)\"\\s*";
+ private static final String bindruleRegex =
+ keywordRegex + "\\s*" + opRegex + "\\s*" + expressionRegex;
+ private static final int remainingOperandPos = 1;
+ private static final int remainingBindrulePos = 2;
+ private static final String remainingBindruleRegex =
+ "^\\s*(\\w+)\\s*(.*)$";
+
+ /**
+ * Constructor that takes an keyword enumeration and corresponding
+ * simple bind rule. The keyword string is the key for the keyword rule in
+ * the keywordRuleMap. This is a simple bind rule representation:
+
+ * keyword op rule
+ *
+ * An example of a simple bind rule is:
+ *
+ * userdn = "ldap:///anyone"
+ *
+ * @param keyword The keyword enumeration.
+ * @param rule The rule corresponding to this keyword.
+ */
+ private BindRule(EnumBindRuleKeyword keyword, KeywordBindRule rule) {
+ this.keyword=keyword;
+ this.keywordRuleMap.put(keyword.toString(), rule);
+ }
+
+
+ /*
+ * TODO Verify that this handles the NOT boolean properly by
+ * creating a unit test.
+ *
+ * I'm a bit confused by the constructor which takes left and right
+ * arguments. Is it always supposed to have exactly two elements?
+ * Is it supposed to keep nesting bind rules in a chain until all of
+ * them have been processed? The documentation for this method needs
+ * to be a lot clearer. Also, it doesn't look like it handles the NOT
+ * type properly.
+ */
+ /**
+ * Constructor that represents a complex bind rule. The left and right
+ * bind rules are saved along with the boolean type operator. A complex
+ * bind rule looks like:
+ *
+ * bindrule booleantype bindrule
+ *
+ * Each side of the complex bind rule can be complex bind rule(s)
+ * itself. An example of a complex bind rule would be:
+ *
+ * (dns="*.example.com" and (userdn="ldap:///anyone" or
+ * (userdn="ldap:///cn=foo,dc=example,dc=com and ip=129.34.56.66)))
+ *
+ * This constructor should always have two elements. The processing
+ * of a complex bind rule is dependent on the boolean operator type.
+ * See the evalComplex method for more information.
+ *
+ *
+ * @param left The bind rule left of the boolean.
+ * @param right The right bind rule.
+ * @param booleanType The boolean type enumeration ("and" or "or").
+ */
+ private BindRule(BindRule left, BindRule right,
+ EnumBooleanTypes booleanType) {
+ this.booleanType = booleanType;
+ this.left = left;
+ this.right = right;
+ }
+
+ /*
+ * TODO Verify this method handles escaped parentheses by writing
+ * a unit test.
+ *
+ * It doesn't look like the decode() method handles the possibility of
+ * escaped parentheses in a bind rule.
+ */
+ /**
+ * Decode an ACI bind rule string representation.
+ * @param input The string representation of the bind rule.
+ * @return A BindRule class representing the bind rule.
+ * @throws AciException If the string is an invalid bind rule.
+ */
+ public static BindRule decode (String input)
+ throws AciException {
+ if ((input == null) || (input.length() == 0))
+ {
+ return null;
+ }
+ String bindruleStr = input.trim();
+ char firstChar = bindruleStr.charAt(0);
+ char[] bindruleArray = bindruleStr.toCharArray();
+
+ if (firstChar == '(')
+ {
+ BindRule bindrule_1 = null;
+ int currentPos;
+ int numOpen = 0;
+ int numClose = 0;
+
+ // Find the associated closed parenthesis
+ for (currentPos = 0; currentPos < bindruleArray.length; currentPos++)
+ {
+ if (bindruleArray[currentPos] == '(')
+ {
+ numOpen++;
+ }
+ else if (bindruleArray[currentPos] == ')')
+ {
+ numClose++;
+ }
+ if (numClose == numOpen)
+ {
+ //We found the associated closed parenthesis
+ //the parenthesis are removed
+ String bindruleStr1 = bindruleStr.substring(1, currentPos);
+ bindrule_1 = BindRule.decode(bindruleStr1);
+ break;
+ }
+ }
+ /*
+ * Check that the number of open parenthesis is the same as
+ * the number of closed parenthesis.
+ * Raise an exception otherwise.
+ */
+ if (numOpen > numClose) {
+ int msgID = MSGID_ACI_SYNTAX_BIND_RULE_MISSING_CLOSE_PAREN;
+ String message = getMessage(msgID, input);
+ throw new AciException(msgID, message);
+ }
+ /*
+ * If there are remaining chars => there MUST be an
+ * operand (AND / OR)
+ * otherwise there is a syntax error
+ */
+ if (currentPos < (bindruleArray.length - 1))
+ {
+ String remainingBindruleStr =
+ bindruleStr.substring(currentPos + 1);
+ return createBindRule(bindrule_1, remainingBindruleStr);
+ }
+ else
+ {
+ return bindrule_1;
+ }
+ }
+ else
+ {
+ StringBuilder b=new StringBuilder(bindruleStr);
+ /*
+ * TODO Verify by unit test that this negation
+ * is correct. This code handles a simple bind rule negation such
+ * as:
+ *
+ * not userdn="ldap:///anyone"
+ */
+ boolean negate=determineNegation(b);
+ bindruleStr=b.toString();
+ Pattern bindrulePattern = Pattern.compile(bindruleRegex);
+ Matcher bindruleMatcher = bindrulePattern.matcher(bindruleStr);
+ int bindruleEndIndex;
+ if (bindruleMatcher.find())
+ {
+ bindruleEndIndex = bindruleMatcher.end();
+ BindRule bindrule_1 = parseAndCreateBindrule(bindruleMatcher);
+ bindrule_1.setNegate(negate);
+ if (bindruleEndIndex < bindruleStr.length())
+ {
+ String remainingBindruleStr =
+ bindruleStr.substring(bindruleEndIndex);
+ return createBindRule(bindrule_1, remainingBindruleStr);
+ }
+ else {
+ return bindrule_1;
+ }
+ }
+ else {
+ int msgID = MSGID_ACI_SYNTAX_INVALID_BIND_RULE_SYNTAX;
+ String message = getMessage(msgID, input);
+ throw new AciException(msgID, message);
+ }
+ }
+ }
+
+
+ /**
+ * Parses a simple bind rule using the regular expression matcher.
+ * @param bindruleMatcher A regular expression matcher holding
+ * the engine to use in the creation of a simple bind rule.
+ * @return A BindRule determined by the matcher.
+ * @throws AciException If the bind rule matcher found errors.
+ */
+ private static BindRule parseAndCreateBindrule(Matcher bindruleMatcher)
+ throws AciException {
+ String keywordStr = bindruleMatcher.group(keywordPos);
+ String operatorStr = bindruleMatcher.group(opPos);
+ String expression = bindruleMatcher.group(expressionPos);
+ EnumBindRuleKeyword keyword;
+ EnumBindRuleType operator;
+
+ // Get the Keyword
+ keyword = EnumBindRuleKeyword.createBindRuleKeyword(keywordStr);
+ if (keyword == null)
+ {
+ int msgID = MSGID_ACI_SYNTAX_INVALID_BIND_RULE_KEYWORD;
+ String message = getMessage(msgID, keywordStr);
+ throw new AciException(msgID, message);
+ }
+
+ // Get the operator
+ operator = EnumBindRuleType.createBindruleOperand(operatorStr);
+ if (operator == null) {
+ int msgID = MSGID_ACI_SYNTAX_INVALID_BIND_RULE_OPERATOR;
+ String message = getMessage(msgID, operatorStr);
+ throw new AciException(msgID, message);
+ }
+
+ //expression can't be null
+ if (expression == null) {
+ int msgID = MSGID_ACI_SYNTAX_MISSING_BIND_RULE_EXPRESSION;
+ String message = getMessage(msgID, operatorStr);
+ throw new AciException(msgID, message);
+ }
+ validateOperation(keyword, operator);
+ KeywordBindRule rule = decode(expression, keyword, operator);
+ return new BindRule(keyword, rule);
+ }
+
+ /**
+ * Create a complex bind rule from a substring
+ * parsed from the ACI string.
+ * @param bindrule The left hand part of a complex bind rule
+ * parsed previously.
+ * @param remainingBindruleStr The string used to determine the right
+ * hand part.
+ * @return A BindRule representing a complex bind rule.
+ * @throws AciException If the string contains an invalid
+ * right hand bind rule string.
+ */
+ private static BindRule createBindRule(BindRule bindrule,
+ String remainingBindruleStr) throws AciException {
+ Pattern remainingBindrulePattern =
+ Pattern.compile(remainingBindruleRegex);
+ Matcher remainingBindruleMatcher =
+ remainingBindrulePattern.matcher(remainingBindruleStr);
+ if (remainingBindruleMatcher.find()) {
+ String remainingOperand =
+ remainingBindruleMatcher.group(remainingOperandPos);
+ String remainingBindrule =
+ remainingBindruleMatcher.group(remainingBindrulePos);
+ EnumBooleanTypes operand =
+ EnumBooleanTypes.createBindruleOperand(remainingOperand);
+ if ((operand == null)
+ || ((operand != EnumBooleanTypes.AND_BOOLEAN_TYPE) &&
+ (operand != EnumBooleanTypes.OR_BOOLEAN_TYPE))) {
+ int msgID =
+ MSGID_ACI_SYNTAX_INVALID_BIND_RULE_BOOLEAN_OPERATOR;
+ String message = getMessage(msgID, remainingOperand);
+ throw new AciException(msgID, message);
+ }
+ StringBuilder ruleExpr=new StringBuilder(remainingBindrule);
+ /* TODO write a unit test to verify.
+ * This is a check for something like:
+ * bindrule and not (bindrule)
+ * or something ill-advised like:
+ * and not not not (bindrule).
+ */
+ boolean negate=determineNegation(ruleExpr);
+ remainingBindrule=ruleExpr.toString();
+ BindRule bindrule_2 =
+ BindRule.decode(remainingBindrule);
+ bindrule_2.setNegate(negate);
+ return new BindRule(bindrule, bindrule_2, operand);
+ } else {
+ int msgID = MSGID_ACI_SYNTAX_INVALID_BIND_RULE_SYNTAX;
+ String message = getMessage(msgID, remainingBindruleStr);
+ throw new AciException(msgID, message);
+ }
+ }
+
+ /**
+ * Tries to strip an "not" boolean modifier from the string and
+ * determine at the same time if the value should be flipped.
+ * For example:
+ *
+ * not not not bindrule
+ *
+ * is true.
+ *
+ * @param ruleExpr The bindrule expression to evaluate. This
+ * string will be changed if needed.
+ * @return True if the boolean needs to be negated.
+ */
+ private static boolean determineNegation(StringBuilder ruleExpr) {
+ boolean negate=false;
+ String ruleStr=ruleExpr.toString();
+ while(ruleStr.regionMatches(true, 0, "not ", 0, 4)) {
+ negate = !negate;
+ ruleStr = ruleStr.substring(4);
+ }
+ ruleExpr.replace(0, ruleExpr.length(), ruleStr);
+ return negate;
+ }
+
+ /**
+ * Set the negation parameter as determined by the function above.
+ * @param v The value to assign negate to.
+ */
+ private void setNegate(boolean v) {
+ negate=v;
+ }
+
+ /*
+ * TODO This method needs to handle the userattr keyword. Also verify
+ * that the rest of the keywords are handled correctly.
+ * TODO Investigate moving this method into EnumBindRuleKeyword class.
+ *
+ * Does validateOperation need a default case? Why is USERATTR not in this
+ * list? Why is TIMEOFDAY not in this list when DAYOFWEEK is in the list?
+ * Would it be more appropriate to put this logic in the
+ * EnumBindRuleKeyword class so we can be sure it's always handled properly
+ * for all keywords?
+ */
+ /**
+ * Checks the keyword operator enumeration to make sure it is valid.
+ * This method doesn't handle all cases.
+ * @param keyword The keyword enumeration to evaluate.
+ * @param op The operation enumeration to evaluate.
+ * @throws AciException If the operation is not valid for the keyword.
+ */
+ private static void validateOperation(EnumBindRuleKeyword keyword,
+ EnumBindRuleType op)
+ throws AciException {
+ switch (keyword) {
+ case USERDN:
+ case ROLEDN:
+ case GROUPDN:
+ case IP:
+ case DNS:
+ case AUTHMETHOD:
+ case DAYOFWEEK:
+ if ((op != EnumBindRuleType.EQUAL_BINDRULE_TYPE)
+ && (op != EnumBindRuleType.NOT_EQUAL_BINDRULE_TYPE)) {
+ int msgID =
+ MSGID_ACI_SYNTAX_INVALID_BIND_RULE_KEYWORD_OPERATOR_COMBO;
+ String message = getMessage(msgID,
+ keyword.toString(),
+ op.toString());
+ throw new AciException(msgID, message);
+ }
+ }
+ }
+
+ /*
+ * TODO Investigate moving into the EnumBindRuleKeyword class.
+ *
+ * Should we move the logic in the
+ * decode(String,EnumBindRuleKeyword,EnumBindRuleType) method into the
+ * EnumBindRuleKeyword class so we can be sure that it's always
+ * handled properly for all keywords?
+ */
+ /**
+ * Creates a keyword bind rule suitable for saving in the keyword
+ * rule map table. Each individual keyword class will do further
+ * parsing and validation of the expression string. This processing
+ * is part of the simple bind rule creation.
+ * @param expr The expression string to further parse.
+ * @param keyword The keyword to create.
+ * @param op The operation part of the bind rule.
+ * @return A keyword bind rule class that can be stored in the
+ * map table.
+ * @throws AciException If the expr string contains a invalid
+ * bind rule.
+ */
+ private static KeywordBindRule decode(String expr,
+ EnumBindRuleKeyword keyword,
+ EnumBindRuleType op)
+ throws AciException {
+ KeywordBindRule rule;
+ switch (keyword) {
+ case USERDN:
+ {
+ rule = UserDN.decode(expr, op);
+ break;
+ }
+ case ROLEDN:
+ {
+ rule = RoleDN.decode(expr, op);
+ break;
+ }
+ case GROUPDN:
+ {
+ rule = GroupDN.decode(expr, op);
+ break;
+ }
+ case IP:
+ {
+ rule = IpCriteria.decode(expr, op);
+ break;
+ }
+ case DNS:
+ {
+ rule = DNS.decode(expr, op);
+ break;
+ }
+ case DAYOFWEEK:
+ {
+ rule = DayOfWeek.decode(expr, op);
+ break;
+ }
+ case TIMEOFDAY:
+ {
+ rule=TimeOfDay.decode(expr, op);
+ break;
+ }
+ case AUTHMETHOD:
+ {
+ rule = AuthMethod.decode(expr, op);
+ break;
+ }
+ case USERATTR:
+ {
+ rule = UserAttr.decode(expr, op);
+ break;
+ }
+ default: {
+ int msgID = MSGID_ACI_SYNTAX_INVALID_BIND_RULE_KEYWORD;
+ String message = getMessage(msgID, keyword.toString());
+ throw new AciException(msgID, message);
+ }
+ }
+ return rule;
+ }
+
+ /**
+ * Evaluate the results of a complex bind rule. If the boolean
+ * is an AND type then left and right must be TRUE, else
+ * it must be an OR result and one of the bind rules must be
+ * TRUE.
+ * @param left The left bind rule result to evaluate.
+ * @param right The right bind result to evaluate.
+ * @return The result of the complex evaluation.
+ */
+ private EnumEvalResult evalComplex(EnumEvalResult left,
+ EnumEvalResult right) {
+ EnumEvalResult ret=EnumEvalResult.FALSE;
+ if(booleanType == EnumBooleanTypes.AND_BOOLEAN_TYPE) {
+ if((left == EnumEvalResult.TRUE) && (right == EnumEvalResult.TRUE))
+ ret=EnumEvalResult.TRUE;
+ } else if((left == EnumEvalResult.TRUE) ||
+ (right == EnumEvalResult.TRUE))
+ ret=EnumEvalResult.TRUE;
+ return ret;
+ }
+
+ /**
+ * Evaluate an bind rule against an evaluation context. If it is a simple
+ * bind rule (no boolean type) then grab the keyword rule from the map
+ * table and call the corresponding evaluate function. If it is a
+ * complex rule call the routine above "evalComplex()".
+ * @param evalCtx The evaluation context to pass to the keyword
+ * evaluation function.
+ * @return An result enumeration containing the result of the evaluation.
+ */
+ public EnumEvalResult evaluate(AciEvalContext evalCtx) {
+ EnumEvalResult ret;
+ //Simple bind rules have a null booleanType enumeration.
+ if(this.booleanType == null) {
+ KeywordBindRule rule=keywordRuleMap.get(keyword.toString());
+ ret = rule.evaluate(evalCtx);
+ } else
+ ret=evalComplex(left.evaluate(evalCtx),right.evaluate(evalCtx));
+ return EnumEvalResult.negateIfNeeded(ret, negate);
+ }
+}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/DNS.java b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/DNS.java
new file mode 100644
index 0000000..ea403b4
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/DNS.java
@@ -0,0 +1,160 @@
+/*
+ * 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.LinkedList;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * This class implements the dns bind rule keyword.
+ */
+public class DNS implements KeywordBindRule {
+
+ LinkedList<String> patterns=null;
+ private EnumBindRuleType type=null;
+
+ /**
+ * Create a class representing a dns bind rule keyword.
+ * @param patterns List of dns patterns to match against.
+ * @param type An enumeration representing the bind rule type.
+ */
+ private DNS(LinkedList<String> patterns, EnumBindRuleType type) {
+ this.patterns=patterns;
+ this.type=type;
+ }
+
+ /**
+ * Decode an string representing a dns bind rule.
+ * @param expr A string representation of the bind rule.
+ * @param type An enumeration representing the bind rule type.
+ * @return A keyword bind rule class that can be used to evaluate
+ * this bind rule.
+ * @throws AciException If the expression string is invalid.
+ */
+ public static DNS decode(String expr, EnumBindRuleType type)
+ throws AciException
+ {
+ String valueRegex = "([a-zA-Z0-9\\.\\-\\*]+)";
+ String valuesRegex = valueRegex + "\\s*(,\\s*" + valueRegex + ")*";
+ if (!Pattern.matches(valuesRegex, expr)) {
+ int msgID = MSGID_ACI_SYNTAX_INVALID_DNS_EXPRESSION;
+ String message = getMessage(msgID, expr);
+ throw new AciException(msgID, message);
+ }
+ LinkedList<String>dns=new LinkedList<String>();
+ int valuePos = 1;
+ Pattern valuePattern = Pattern.compile(valueRegex);
+ Matcher valueMatcher = valuePattern.matcher(expr);
+ while (valueMatcher.find()) {
+ String hn=valueMatcher.group(valuePos);
+ String[] hnArray=hn.split("\\.", -1);
+ for(int i=1, n=hnArray.length; i < n; i++) {
+ if(hnArray[i].equals("*")) {
+ int msgID = MSGID_ACI_SYNTAX_INVALID_DNS_WILDCARD;
+ String message = getMessage(msgID, expr);
+ throw new AciException(msgID, message);
+ }
+ }
+ dns.add(hn);
+ }
+ return new DNS(dns, type);
+ }
+
+ /**
+ * Performs evaluation of dns keyword bind rule using the provided
+ * evaluation context.
+ * @param evalCtx An evaluation context to use in the evaluation.
+ * @return An enumeration evaluation result.
+ */
+ public EnumEvalResult evaluate(AciEvalContext evalCtx) {
+ EnumEvalResult matched=EnumEvalResult.FALSE;
+ String[] remoteHost = evalCtx.getHostName().split("\\.", -1);
+ for(String p : patterns) {
+ String[] pat = p.split("\\.", -1);
+ if(evalHostName(remoteHost, pat)) {
+ matched=EnumEvalResult.TRUE;
+ break;
+ }
+ }
+ return matched.getRet(type, false);
+ }
+
+ /*
+ * TODO Verify that a DNS pattern of "*" is valid by writing a unit
+ * test. Probably isn't.
+ *
+ * TODO Evaluate if extending the wild-card matching to multiple name
+ * components should be supported. Currently wild-cards are only permitted
+ * in the leftmost field and the rest of the domain name components must
+ * match.
+ *
+ * TODO Evaluate extending wild-card matching to non-complete name matching.
+ *
+ * Is it acceptable to have a DNS address of just "*"
+ * (which presumably will match any system)?
+ *
+ * Is it acceptable for a wildcard to match multiple name components? For
+ * example, is "*.example.com" supposed to be considered a match for
+ * "host.east.example.com"? Similarly, would a pattern like
+ * "www.*.example.com" match "www.newyork.east.example.com"? It doesn't
+ * appear that the current implementation matches either of them.
+ *
+ * Is it acceptable for a wildcard to appear as anything other than a
+ * complete name component? For example, if I have three web servers
+ * "www1.example.com","www2.example.com", and "www3.example.com", then
+ * can I use "www*.example.com"? It doesn't appear that the current
+ * implementation allows that. Further, would "www*.example.com" match
+ * cases like "www.example.com" or "www1.east.example.com"?
+ */
+ /**
+ * Checks an array containing the remote client's hostname against
+ * patterns specified in the bind rule expression. Wild-cards are
+ * only permitted in the leftmost field and the rest of the domain
+ * name array components must match.
+ * @param remoteHostName Array containing components of the remote clients
+ * hostname (split on ".").
+ * @param pat An array containing the pattern specified in
+ * the bind rule expression. The first array slot may be a wild-card "*".
+ * @return True if the remote hostname matches the pattern.
+ */
+ private boolean evalHostName(String[] remoteHostName, String[] pat) {
+ if(remoteHostName.length != pat.length)
+ return false;
+ for(int i=0;i<remoteHostName.length;i++)
+ {
+ if(!pat[i].equals("*")) {
+ if(!pat[i].equalsIgnoreCase(remoteHostName[i]))
+ return false;
+ }
+ }
+ return true;
+ }
+}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/DayOfWeek.java b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/DayOfWeek.java
new file mode 100644
index 0000000..c5c732c
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/DayOfWeek.java
@@ -0,0 +1,96 @@
+/*
+ * 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.Calendar;
+import java.util.GregorianCalendar;
+import java.util.LinkedList;
+
+/**
+ * This class implements the dayofweek bind rule keyword.
+ */
+public class DayOfWeek implements KeywordBindRule {
+
+ LinkedList<EnumDayOfWeek> days=null;
+ private EnumBindRuleType type=null;
+
+ /**
+ * Create a class representing a dayofweek bind rule keyword.
+ * @param days A list of day of the week enumerations.
+ * @param type An enumeration representing the bind rule type.
+ */
+ private DayOfWeek(LinkedList<EnumDayOfWeek> days, EnumBindRuleType type) {
+ this.days=days;
+ this.type=type;
+ }
+
+ /**
+ * Decode an string representing a dayofweek bind rule.
+ * @param expr A string representation of the bind rule.
+ * @param type An enumeration representing the bind rule type.
+ * @return A keyword bind rule class that can be used to evaluate
+ * this bind rule.
+ * @throws AciException If the expression string is invalid.
+ */
+ public static KeywordBindRule decode(String expr, EnumBindRuleType type)
+ throws AciException
+ {
+ LinkedList<EnumDayOfWeek>days=new LinkedList<EnumDayOfWeek>();
+ String[] dayArray=expr.split(",", -1);
+ for(int i=0, m=dayArray.length; i < m; i++)
+ {
+ EnumDayOfWeek day=EnumDayOfWeek.createDayOfWeek(dayArray[i]);
+ if (day == null)
+ {
+ int msgID = MSGID_ACI_SYNTAX_INVALID_DAYOFWEEK;
+ String message = getMessage(msgID, expr);
+ throw new AciException(msgID, message);
+ }
+ days.add(day);
+ }
+ return new DayOfWeek(days, type);
+ }
+
+ /**
+ * Performs evaluation of a dayofweek bind rule using the provided
+ * evaluation context.
+ * @param evalCtx An evaluation context to use in the evaluation.
+ * @return An enumeration evaluation result.
+ */
+ public EnumEvalResult evaluate(AciEvalContext evalCtx) {
+ EnumEvalResult matched=EnumEvalResult.FALSE;
+ GregorianCalendar calendar = new GregorianCalendar();
+ EnumDayOfWeek dayofweek
+ = EnumDayOfWeek.getDayOfWeek(calendar.get(Calendar.DAY_OF_WEEK));
+ if(days.contains(dayofweek))
+ matched=EnumEvalResult.TRUE;
+ return matched.getRet(type, false);
+ }
+}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/EnumAccessType.java b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/EnumAccessType.java
new file mode 100644
index 0000000..f486bb1
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/EnumAccessType.java
@@ -0,0 +1,88 @@
+/*
+ * 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;
+
+/**
+ * This class provides an enumeration of the two access
+ * types (allow, deny).
+ */
+public enum EnumAccessType {
+ /**
+ * Allow access type.
+ */
+ ALLOW ("allow"),
+ /**
+ * Deny access type.
+ */
+ DENY ("deny");
+
+ private final String accessType;
+
+ /**
+ * Constructor that sets the accessType string.
+ * @param accessType The access type string to set.
+ */
+ EnumAccessType (String accessType){
+ this.accessType = accessType ;
+ }
+
+ /**
+ * Checks if the access type is equal to the string
+ * representation passed in.
+ * @param type The string representation of the access type.
+ * @return True if the access types are equal.
+ */
+ public boolean isAccessType(String type){
+ return type.equalsIgnoreCase(accessType);
+ }
+
+ /*
+ * TODO Make this method and all other Enum decode methods more efficient.
+ *
+ * Using the Enum.values() method is documented to be potentially slow.
+ * If we ever expect to use the decode() method in a performance-critical
+ * manner, then we should make it more efficient. The same thing applies
+ * to all of the other enumeration types defined in the package.
+ */
+ /**
+ * Decodes an access type enumeration from a string passed into the method.
+ * @param type The string representation of the access type.
+ * @return Return an EnumAccessType matching the string representation,
+ * or null if the string is not valid.
+ */
+ public static EnumAccessType decode(String type){
+ if (type != null){
+ for (EnumAccessType t : EnumAccessType.values()) {
+ if (t.isAccessType(type)){
+ return t;
+ }
+ }
+ }
+ return null;
+ }
+}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/EnumAuthMethod.java b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/EnumAuthMethod.java
new file mode 100644
index 0000000..740667e
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/EnumAuthMethod.java
@@ -0,0 +1,116 @@
+/*
+ * 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;
+/*
+ * TODO Evaluate moving this to a non-enumeration class that can add
+ * SASL mechanisms dynamically.
+ *
+ * Given our previous discussion about needing to support any kind of SASL
+ * mechanism that may be registered with the server, perhaps an enum isn't
+ * the right way to handle this because we don't know ahead of time what
+ * auth methods might be available (certainly not at compile time, but
+ * potentially not even at runtime since I can add support for a new SASL
+ * mechanism on the fly without restarting the server).
+ */
+/**
+ * This class provides an enumeration of the allowed authmethod types.
+ */
+public enum EnumAuthMethod {
+ /**
+ * The enumeration type when the bind rule has specified authentication of
+ * none.
+ */
+ AUTHMETHOD_NONE ("none"),
+ /**
+ * The enumeration type when the bind rule has specified authentication of
+ * simple.
+ */
+ AUTHMETHOD_SIMPLE ("simple"),
+ /**
+ * The enumeration type when the bind rule has specified authentication of
+ * ssl client auth.
+ */
+ AUTHMETHOD_SSL ("ssl"),
+ /**
+ * The enumeration type when the bind rule has specified authentication of
+ * sasl DIGEST-MD5.
+ */
+ AUTHMETHOD_SASL_MD5 ("sasl DIGEST-MD5"),
+ /**
+ * The enumeration type when the bind rule has specified authentication of
+ * sasl EXTERNAL.
+ */
+ AUTHMETHOD_SASL_EXTERNAL ("sasl EXTERNAL"),
+ /**
+ * The enumeration type when the bind rule has specified authentication of
+ * sasl GSSAPI.
+ */
+ AUTHMETHOD_SASL_GSSAPI ("sasl GSSAPI"),
+ /**
+ * Special internal enumeration for when there is no match.
+ */
+ AUTHMETHOD_NOMATCH ("nomatch");
+
+ /**
+ * The name of the authmethod.
+ */
+ public String authmethod = null;
+
+ /**
+ * Creates a new enumeration type for this authmethod.
+ * @param authmethod The authemethod name.
+ */
+ EnumAuthMethod (String authmethod){
+ this.authmethod = authmethod;
+ }
+
+ /**
+ * Checks if a authmethod name is equal to this enumeration.
+ * @param myauthmethod The name to test for.
+ * @return True if the names match.
+ */
+ public boolean isAuthMethod(String myauthmethod){
+ return myauthmethod.equalsIgnoreCase(this.authmethod);
+ }
+
+ /**
+ * Creates an authmethod enumeration from the name passed in.
+ * @param myauthmethod The name to create.
+ * @return An authmethod enumeration if the name was found or null if not.
+ */
+ public static EnumAuthMethod createAuthmethod(String myauthmethod){
+ if (myauthmethod != null){
+ for (EnumAuthMethod t : EnumAuthMethod.values()){
+ if (t.isAuthMethod(myauthmethod)){
+ return t;
+ }
+ }
+ }
+ return null;
+ }
+}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/EnumBindRuleKeyword.java b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/EnumBindRuleKeyword.java
new file mode 100644
index 0000000..84fa1f3
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/EnumBindRuleKeyword.java
@@ -0,0 +1,118 @@
+/*
+ * 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;
+
+/**
+ * This class provides an enumeration of the allowed bind rule
+ * keyword types.
+ */
+public enum EnumBindRuleKeyword {
+ /**
+ * The enumeration type when the bind rule has specified keyword of
+ * userdn.
+ */
+ USERDN ("userdn"),
+ /**
+ * The enumeration type when the bind rule has specified keyword of
+ * groupdn.
+ */
+ GROUPDN ("groupdn"),
+ /**
+ * The enumeration type when the bind rule has specified keyword of
+ * roledn.
+ */
+ ROLEDN ("roledn"),
+ /**
+ * The enumeration type when the bind rule has specified keyword of
+ * ip.
+ */
+ IP ("ip"),
+ /**
+ * The enumeration type when the bind rule has specified keyword of
+ * dns.
+ */
+ DNS ("dns"),
+ /**
+ * The enumeration type when the bind rule has specified keyword of
+ * dayofweek.
+ */
+ DAYOFWEEK ("dayofweek"),
+ /**
+ * The enumeration type when the bind rule has specified keyword of
+ * timeofday.
+ */
+ TIMEOFDAY ("timeofday"),
+ /**
+ * The enumeration type when the bind rule has specified keyword of
+ * userattr.
+ */
+ USERATTR ("userattr"),
+ /**
+ * The enumeration type when the bind rule has specified keyword of
+ * authmethod.
+ */
+ AUTHMETHOD ("authmethod");
+ /**
+ * The keyword name.
+ */
+ public final String keyword;
+
+ /**
+ * Creates a new enumeration type for the specified keyword.
+ * @param keyword The keyword name.
+ */
+ EnumBindRuleKeyword(String keyword){
+ this.keyword = keyword;
+ }
+
+ /**
+ * Checks to see if the keyword string is equal to the enumeration.
+ * @param keywordStr The keyword name to check equality for.
+ * @return True if the keyword is equal to the specified name.
+ */
+ public boolean isBindRuleKeyword(String keywordStr){
+ return keywordStr.equalsIgnoreCase(this.keyword);
+ }
+
+ /**
+ * Create a new enumeration type for the specified keyword name.
+ * @param keywordStr The name of the enumeration to create.
+ * @return A new enumeration type for the name or null if the name is
+ * not valid.
+ */
+ public static EnumBindRuleKeyword createBindRuleKeyword(String keywordStr){
+ if (keywordStr != null){
+ for (EnumBindRuleKeyword t : EnumBindRuleKeyword.values()){
+ if (t.isBindRuleKeyword(keywordStr)){
+ return t;
+ }
+ }
+ }
+ return null;
+ }
+}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/EnumBindRuleType.java b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/EnumBindRuleType.java
new file mode 100644
index 0000000..ba2421e
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/EnumBindRuleType.java
@@ -0,0 +1,104 @@
+/*
+ * 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;
+
+/**
+ * This class provides an enumeration of the allowed bind rule types.
+ */
+public enum EnumBindRuleType {
+ /**
+ * The enumeration type when the bind rule has specified type of
+ * "=".
+ */
+ EQUAL_BINDRULE_TYPE ("="),
+ /**
+ * The enumeration type when the bind rule has specified type of
+ * "!=".
+ */
+ NOT_EQUAL_BINDRULE_TYPE ("!="),
+ /**
+ * The enumeration type when the bind rule has specified type of
+ * "<".
+ */
+ LESS_BINDRULE_TYPE ("<"),
+ /**
+ * The enumeration type when the bind rule has specified type of
+ * "<=".
+ */
+ LESS_OR_EQUAL_BINDRULE_TYPE ("<="),
+ /**
+ * The enumeration type when the bind rule has specified type of
+ * >".
+ */
+ GREATER_BINDRULE_TYPE (">"),
+ /**
+ * The enumeration type when the bind rule has specified type of
+ * ">=".
+ */
+ GREATER_OR_EQUAL_BINDRULE_TYPE (">=");
+
+ /**
+ * The bind rule type name.
+ */
+ private final String type;
+
+ /**
+ * Creates a new enumeration type for the specified bind rule type.
+ * @param type The bind rule type name.
+ */
+ EnumBindRuleType(String type){
+ this.type = type;
+ }
+
+ /**
+ * Checks to see if the type string is equal to the enumeration type
+ * name.
+ * @param type The type name to check equality for.
+ * @return True if the keyword is equal to the specified name.
+ */
+ public boolean isBindRuleType(String type){
+ return type.equals(this.type);
+ }
+
+ /**
+ * Create a new enumeration type for the specified type name.
+ * @param type The name of the enumeration to create.
+ * @return A new enumeration type for the name or null if the name is
+ * not valid.
+ */
+ public static EnumBindRuleType createBindruleOperand(String type){
+ if (type != null){
+ for (EnumBindRuleType t : EnumBindRuleType.values()){
+ if (t.isBindRuleType(type)){
+ return t;
+ }
+ }
+ }
+ return null;
+ }
+}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/EnumBooleanTypes.java b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/EnumBooleanTypes.java
new file mode 100644
index 0000000..c2fa963
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/EnumBooleanTypes.java
@@ -0,0 +1,90 @@
+/*
+ * 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;
+
+/**
+ * This class provides an enumeration of the allowed bind rule booelan types.
+ */
+public enum EnumBooleanTypes {
+ /**
+ * The enumeration type when the bind rule has specified boolean type of
+ * "AND".
+ */
+ AND_BOOLEAN_TYPE ("and"),
+ /**
+ * The enumeration type when the bind rule has specified boolean type of
+ * "OR".
+ */
+ OR_BOOLEAN_TYPE ("or"),
+ /**
+ * The enumeration type when the bind rule has specified boolean type of
+ * "NOT".
+ */
+ NOT_BOOLEAN_TYPE ("not");
+
+ /**
+ * The bind rule boolean type name.
+ */
+ private final String booleanType;
+
+ /**
+ * Creates a new enumeration type for the specified bind rule boolean type.
+ * @param booleanType The boolean type name.
+ */
+ EnumBooleanTypes(String booleanType){
+ this.booleanType = booleanType;
+ }
+
+ /**
+ * Checks to see if the boolean type string is equal to the enumeration type
+ * name.
+ * @param booleanType The type name to check equality for.
+ * @return True if the keyword is equal to the specified name.
+ */
+ public boolean isBindRuleBooleanOperand(String booleanType){
+ return booleanType.equalsIgnoreCase(this.booleanType);
+ }
+
+ /**
+ * Create a new enumeration type for the specified boolean type name.
+ * @param booleanType The name of the enumeration to create.
+ * @return A new enumeration type for the name or null if the name is
+ * not valid.
+ */
+ public static
+ EnumBooleanTypes createBindruleOperand(String booleanType) {
+ if (booleanType != null){
+ for (EnumBooleanTypes t : EnumBooleanTypes.values()) {
+ if (t.isBindRuleBooleanOperand(booleanType)) {
+ return t;
+ }
+ }
+ }
+ return null;
+ }
+}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/EnumDayOfWeek.java b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/EnumDayOfWeek.java
new file mode 100644
index 0000000..1d183b0
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/EnumDayOfWeek.java
@@ -0,0 +1,154 @@
+/*
+ * 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 java.util.Calendar;
+
+/**
+ * This class provides an enumeration of the allowed dayofweek types.
+ */
+public enum EnumDayOfWeek {
+ /**
+ * The enumeration type when the bind rule has specified dayofweek type of
+ * "mon".
+ */
+ DAY_MONDAY ("mon"),
+ /**
+ * The enumeration type when the bind rule has specified dayofweek type of
+ * "tue" .
+ */
+ DAY_TUESDAY ("tue"),
+ /**
+ * The enumeration type when the bind rule has specified dayofweek type of
+ * "wed".
+ */
+ DAY_WEDNESDAY ("wed"),
+ /**
+ * The enumeration type when the bind rule has specified dayofweek type of
+ * "thu".
+ */
+ DAY_THURSDAY ("thu"),
+ /**
+ * The enumeration type when the bind rule has specified dayofweek type of
+ * "fri".
+ */
+ DAY_FRIDAY ("fri"),
+ /**
+ * The enumeration type when the bind rule has specified dayofweek type of
+ * "sat".
+ */
+ DAY_SATURDAY ("sat"),
+ /**
+ * The enumeration type when the bind rule has specified dayofweek type of
+ * "sun".
+ */
+ DAY_SUNDAY ("sun");
+
+ /**
+ * The bind rule dayofweek type name.
+ */
+ private String day = null;
+
+ /**
+ * Creates a new enumeration type for the specified bind rule dayofweek
+ * type.
+ * @param day The day name.
+ */
+ EnumDayOfWeek (String day){
+ this.day = day;
+ }
+
+ /**
+ * Creates a new enumeration type for the specified bind rule dayofweek
+ * type.
+ * @param day The boolean type name.
+ * @return True if the keyword is equal to the specified name.
+ */
+ public boolean isDayOfWeek(String day){
+ return day.equalsIgnoreCase(this.day);
+ }
+
+ /**
+ * Create a new enumeration type for the specified dayofweek type name.
+ * @param day The name of the enumeration to create.
+ * @return A new enumeration type for the name or null if the name is
+ * not valid.
+ */
+ public static EnumDayOfWeek createDayOfWeek(String day)
+ {
+ if (day != null){
+ for (EnumDayOfWeek t : EnumDayOfWeek.values()){
+ if (t.isDayOfWeek(day)){
+ return t;
+ }
+ }
+ }
+ return null;
+ }
+
+ /*
+ * TODO Evaluate supporting alternative forms for days of the week.
+ *
+ * Should we support alternate forms for the names of the days of the
+ * week in the isDayOfWeek() or createdayOfWeek() method? In particular,
+ * should we handle the case in which the user provided the full name
+ * (e.g., "monday" instead of "mon")?
+ */
+ /**
+ * Return a enumeration relating to a Calendar day of week field.
+ * @param day The day of week index to get.
+ * @return An enumeration corresponding to the wanted day of the week or
+ * null if the day index is invalid.
+ */
+ public static EnumDayOfWeek getDayOfWeek(int day)
+ {
+ switch(day){
+ case Calendar.SUNDAY:
+ return DAY_SUNDAY;
+
+ case Calendar.MONDAY:
+ return DAY_MONDAY;
+
+ case Calendar.TUESDAY:
+ return DAY_TUESDAY;
+
+ case Calendar.WEDNESDAY:
+ return DAY_WEDNESDAY;
+
+ case Calendar.THURSDAY:
+ return DAY_THURSDAY;
+
+ case Calendar.FRIDAY:
+ return DAY_FRIDAY;
+
+ case Calendar.SATURDAY:
+ return DAY_SATURDAY;
+ }
+ return null;
+ }
+}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/EnumEvalResult.java b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/EnumEvalResult.java
new file mode 100644
index 0000000..a3a9d22
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/EnumEvalResult.java
@@ -0,0 +1,111 @@
+/*
+ * 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;
+
+/**
+ * This class provides an enumeration of evaluation results returned by
+ * the bind rule evaluation methods.
+ */
+public enum EnumEvalResult {
+ /**
+ * This enumeration is returned when the result of the evaluation is TRUE.
+ */
+ TRUE(0),
+ /**
+ * This enumeration is returned when the result of the evaluation is FALSE.
+ */
+ FALSE(1),
+ /**
+ * This enumeration is returned when the result of the evaluation is FAIL.
+ * This should only be returned when a system failure occurred.
+ */
+ FAIL(2),
+ /**
+ * This is an internal enumeration used during evaluation of bind rule when
+ * internal processing of the evaluation is undefined. It is never returned
+ * back as a result of the evaluation.
+ */
+ ERR(3);
+
+ /**
+ * Create a new enumeration type for the specified result value.
+ * @param v The value of the result.
+ */
+ EnumEvalResult(int v) {
+ }
+
+ /**
+ * The method tries to determine if the result was undefined, and if so
+ * it returns an FAIL enumeration. If the result was not undefined (the
+ * common case for all of the bind rule evaluations), then the bind rule
+ * type is examined to see if the result needs to be flipped (type equals
+ * NOT_EQUAL_BINDRULE_TYPE).
+ * @param type The bind rule type enumeration of the bind rule.
+ * @param undefined A flag that signals the the result was undefined.
+ * @return An enumeration containing the correct result after processing
+ * the undefined field and the bind rule type enumeration.
+ */
+ public EnumEvalResult getRet(EnumBindRuleType type, boolean undefined) {
+ EnumEvalResult ret=this;
+ if(this.equals(EnumEvalResult.TRUE) || !undefined) {
+ if(type.equals(EnumBindRuleType.NOT_EQUAL_BINDRULE_TYPE))
+ if(this.equals(EnumEvalResult.TRUE))
+ ret=EnumEvalResult.FALSE;
+ else
+ ret=EnumEvalResult.TRUE;
+ } else
+ ret=EnumEvalResult.FAIL;
+ return ret;
+ }
+
+ /**
+ * This method is used to possibly negate the result of a simple bind rule
+ * evaluation. If the boolean is true than the result is negated.
+ * @param v The enumeration result of the simple bind rule evaluation.
+ * @param n If true the result should be negated (TRUE->FALSE, FALSE->TRUE).
+ * @return A possibly negated enumeration result.
+ */
+ public static EnumEvalResult negateIfNeeded(EnumEvalResult v, boolean n) {
+ if(n) {
+ if(v.equals(EnumEvalResult.TRUE))
+ v=EnumEvalResult.FALSE;
+ else
+ v=EnumEvalResult.TRUE;
+ }
+ return v;
+ }
+
+ /**
+ * Helper method that converts this enumeration to a boolean. Usually the
+ * FAIL enumeration has been handled before this is called.
+ * @return True if the enumeration is TRUE, else false.
+ */
+ public boolean getBoolVal() {
+ return this == EnumEvalResult.TRUE;
+ }
+}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/EnumRight.java b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/EnumRight.java
new file mode 100644
index 0000000..335124e
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/EnumRight.java
@@ -0,0 +1,173 @@
+/*
+ * 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;
+
+/**
+ * This class provides an enumeration of the allowed rights.
+ */
+public enum EnumRight {
+
+ /**
+ * This enumeration is returned when the result of the right is "read".
+ */
+ READ ("read"),
+ /**
+ * This enumeration is returned when the result of the right is "write".
+ */
+ WRITE ("write"),
+ /**
+ * This enumeration is returned when the result of the right is "add".
+ */
+ ADD ("add"),
+ /**
+ * This enumeration is returned when the result of the right is "delete".
+ */
+ DELETE ("delete"),
+ /**
+ * This enumeration is returned when the result of the right is "search".
+ */
+ SEARCH ("search"),
+ /**
+ * This enumeration is returned when the result of the right is "compare".
+ */
+ COMPARE ("compare"),
+ /**
+ * This enumeration is returned when the result of the right is
+ * "selfwrite".
+ */
+ SELFWRITE ("selfwrite"),
+ /**
+ * This enumeration is returned when the result of the right is "proxy".
+ */
+ PROXY ("proxy"),
+ /**
+ * This enumeration is returned when the result of the right is "import".
+ */
+ IMPORT ("import"),
+ /**
+ * This enumeration is returned when the result of the right is "export".
+ */
+ EXPORT ("export"),
+ /**
+ * This enumeration is returned when the result of the right is "all".
+ */
+ ALL ("all"),
+ /**
+ * This enumeration is used internally by the modify operation
+ * processing and is not part of the ACI syntax.
+ */
+ DELWRITE ("delwrite"),
+ /**
+ * This enumerations is used internally by the modify operation
+ * processing and is not part of the ACI syntax.
+ */
+ ADDWRITE ("addwrite");
+
+ /**
+ * The name of the right.
+ */
+ private final String right;
+
+ /**
+ * Creates an enumeration of the right name.
+ * @param right The name of the right.
+ */
+ EnumRight (String right) {
+ this.right = right ;
+ }
+
+ /**
+ * Checks if the enumeration is equal to the right name.
+ * @param right The name of the right to check.
+ * @return True if the right is equal to the enumeration's.
+ */
+ public boolean isRight(String right){
+ return right.equalsIgnoreCase(this.right);
+ }
+
+ /**
+ * Creates an enumeration of the right name.
+ * @param right The name of the right.
+ * @return An enumeration of the right or null if the name is invalid.
+ */
+ public static EnumRight decode(String right){
+ if (right != null){
+ for (EnumRight t : EnumRight.values()){
+ if (t.isRight(right)){
+ return t;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns bit mask associated with the specified right.
+ * @param right The right enumeration to return the mask for.
+ * @return The bit mask associated with the right.
+ */
+ public static int getMask(EnumRight right) {
+ int mask=AciHandler.ACI_NULL;
+ switch(right) {
+ case READ:
+ mask=AciHandler.ACI_READ;
+ break;
+ case WRITE:
+ mask=AciHandler.ACI_WRITE;
+ break;
+ case ADD:
+ mask=AciHandler.ACI_ADD;
+ break;
+ case DELETE:
+ mask=AciHandler.ACI_DELETE;
+ break;
+ case SEARCH:
+ mask=AciHandler.ACI_SEARCH;
+ break;
+ case COMPARE:
+ mask=AciHandler.ACI_COMPARE;
+ break;
+ case ALL:
+ mask=AciHandler.ACI_ALL;
+ break;
+ case EXPORT:
+ mask=AciHandler.ACI_EXPORT;
+ break;
+ case IMPORT:
+ mask=AciHandler.ACI_IMPORT;
+ break;
+ case PROXY:
+ mask=AciHandler.ACI_PROXY;
+ break;
+ case SELFWRITE:
+ mask=AciHandler.ACI_SELF;
+ break;
+ }
+ return mask;
+ }
+}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/EnumTargetKeyword.java b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/EnumTargetKeyword.java
new file mode 100644
index 0000000..0368d5d
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/EnumTargetKeyword.java
@@ -0,0 +1,108 @@
+/*
+ * 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;
+
+/**
+ * This class provides an enumeration of the valid ACI target keywords.
+ */
+public enum EnumTargetKeyword {
+ /**
+ * This enumeration is returned when the target keyword is
+ * "target".
+ */
+ KEYWORD_TARGET ("target"),
+ /**
+ * This enumeration is returned when the target keyword is
+ * "targetattr".
+ */
+ KEYWORD_TARGETATTR ("targetattr"),
+ /**
+ * This enumeration is returned when the target keyword is
+ * "targetscope".
+ */
+ KEYWORD_TARGETSCOPE ("targetscope"),
+ /**
+ * This enumeration is returned when the target keyword is
+ * "targetfilter".
+ */
+ KEYWORD_TARGETFILTER ("targetfilter"),
+ /**
+ * This enumeration is returned when the target keyword is
+ * "targattrfilters".
+ */
+ KEYWORD_TARGATTRFILTERS ("targattrfilters");
+ /*
+ * TODO Add support for the targattrfilters keyword.
+ */
+ /**
+ * The target keyword name.
+ */
+ private final String keyword;
+
+ /**
+ * Create a target keyword enumeration of the specified name.
+ * @param keyword The keyword name.
+ */
+ EnumTargetKeyword(String keyword){
+ this.keyword = keyword;
+ }
+
+ /**
+ * Checks if the keyword name is equal to the enumeration name.
+ * @param keyword The keyword name to check.
+ * @return True if the keyword name is equal to the enumeration.
+ */
+ public boolean isKeyword(String keyword){
+ return keyword.equalsIgnoreCase(this.keyword);
+ }
+
+ /**
+ * Create an enumeration of the provided keyword name.
+ * @param keyword The keyword name to create.
+ * @return An enumeration of the specified keyword name or null
+ * if the keyword name is invalid.
+ */
+ public static EnumTargetKeyword createKeyword(String keyword){
+ if (keyword != null){
+ for (EnumTargetKeyword t : EnumTargetKeyword.values()){
+ if (t.isKeyword(keyword)){
+ return t;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Return the enumeration keyword name.
+ * @return The keyword name.
+ */
+ public String getKeyword() {
+ return keyword;
+ }
+}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/EnumTargetOperator.java b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/EnumTargetOperator.java
new file mode 100644
index 0000000..195208c
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/EnumTargetOperator.java
@@ -0,0 +1,81 @@
+/*
+ * 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;
+
+/**
+ * This class provides an enumeration of the valid ACI target operators.
+ */
+public enum EnumTargetOperator {
+ /**
+ * This enumeration is returned when the target operator is "=".
+ */
+ EQUALITY ("="),
+ /**
+ * This enumeration is returned when the target operator is "!=".
+ */
+ NOT_EQUALITY ("!=");
+
+ /**
+ * The target operator name.
+ */
+ private final String operator;
+
+ /**
+ * Create an enumeration of the provided operator name.
+ * @param operator The operator name to create.
+ */
+ EnumTargetOperator(String operator){
+ this.operator = operator;
+ }
+
+ /**
+ * Checks if the provided operator name is equal to the enumeration.
+ * @param op The operator name to check for.
+ * @return True if the operator name is equal to the enumeration.
+ */
+ public boolean isOperator(String op){
+ return op.equalsIgnoreCase(operator);
+ }
+
+ /**
+ * Creates an enumeration of the specified operator type name.
+ * @param op The operator type name to create.
+ * @return Return an enumeration of the operator type name or null if the
+ * name is invalid.
+ */
+ public static EnumTargetOperator createOperator(String op){
+ if (op != null){
+ for (EnumTargetOperator t : EnumTargetOperator.values()){
+ if (t.isOperator(op)){
+ return t;
+ }
+ }
+ }
+ return null;
+ }
+}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/EnumUserDNType.java b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/EnumUserDNType.java
new file mode 100644
index 0000000..240c969
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/EnumUserDNType.java
@@ -0,0 +1,84 @@
+/*
+ * 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;
+
+
+/**
+ * Enumeration that represents the type an "userdn" keyword DN can have.
+ * The issues is the syntax allows invalid URLs such as "ldap:///anyone"
+ * and "ldap:///self". The strategy is to use this class to hold
+ * the type and another class UserDNTypeURL to hold both this type and URL.
+ *
+ * If the URL is an invalid URL, then a dummy URL is saved.
+ * For types such as URL, DN and DNPATTERN, the actual URL is saved and can
+ * be retrieved by the UserDN.evaluate() method when needed. The dummy URL is
+ * ignored in the UserDN.evaluate() method for types such as: ALL, PARENT,
+ * SELF and ANYONE.
+ */
+public enum EnumUserDNType {
+ /**
+ * The enumeration type when the "userdn" URL contains only a DN (no
+ * filter or scope) and that DN has no pattern.
+ */
+ DN(0),
+ /**
+ * The enumeration type when the "userdn" URL contains only a DN (no
+ * filter or scope) and that DN has a substring pattern.
+ */
+ DNPATTERN(1),
+ /**
+ * The enumeration type when the "userdn" URL has the value of:
+ * "ldap:///all".
+ */
+ ALL(2),
+ /**
+ * The enumeration type when the "userdn" URL has the value of:
+ * "ldap:///parent".
+ */
+ PARENT(3),
+ /**
+ * The enumeration type when the "userdn" URL has the value of:
+ * "ldap:///self".
+ */
+ SELF(4),
+ /**
+ * The enumeration type when the "userdn" URL has the value of:
+ * "ldap:///anyone".
+ */
+ ANYONE(5),
+ /**
+ * The enumeration type when the "userdn" URL is contains a DN (suffix),
+ * a scope and a filter.
+ */
+ URL(6);
+
+ /**
+ * Constructor taking an integer value.
+ * @param v Integer value.
+ */
+ EnumUserDNType(int v) {}
+}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/GroupDN.java b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/GroupDN.java
new file mode 100644
index 0000000..2145e77
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/GroupDN.java
@@ -0,0 +1,151 @@
+/*
+ * 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 org.opends.server.types.*;
+import org.opends.server.api.Group;
+import org.opends.server.core.GroupManager;
+import org.opends.server.core.DirectoryServer;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.LinkedHashSet;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+
+/**
+ * This class implements the groupdn bind rule keyword.
+ */
+public class GroupDN implements KeywordBindRule {
+
+ LinkedList<DN> groupDNs=null;
+ private EnumBindRuleType type=null;
+ private static GroupManager groupManager =
+ DirectoryServer.getGroupManager();
+
+ /**
+ * Create a class representing a groupdn bind rule keyword.
+ * @param type An enumeration representing the bind rule type.
+ * @param groupDNs A list of the dns representing groups.
+ */
+ private GroupDN(EnumBindRuleType type, LinkedList<DN> groupDNs ) {
+ this.groupDNs=groupDNs;
+ this.type=type;
+ }
+
+ /**
+ * Decode an string expression representing a groupdn bind rule.
+ * @param expr A string representation of the bind rule.
+ * @param type An enumeration of the type of the bind rule.
+ * @return A keyword bind rule class that can be used to evaluate
+ * this bind rule.
+ * @throws AciException If the expression string is invalid.
+ */
+ public static KeywordBindRule decode(String expr, EnumBindRuleType type)
+ throws AciException {
+ String ldapURLRegex = "\\s*(ldap:///[^\\|]+)";
+ String ldapURLSRegex =
+ ldapURLRegex + "\\s*(\\|\\|\\s*" + ldapURLRegex + ")*";
+ if (!Pattern.matches(ldapURLSRegex, expr)) {
+ int msgID = MSGID_ACI_SYNTAX_INVALID_GROUPDN_EXPRESSION;
+ String message = getMessage(msgID, expr);
+ throw new AciException(msgID, message);
+ }
+ LinkedList<DN>groupDNs=new LinkedList<DN>();
+ int ldapURLPos = 1;
+ Pattern ldapURLPattern = Pattern.compile(ldapURLRegex);
+ Matcher ldapURLMatcher = ldapURLPattern.matcher(expr);
+ while (ldapURLMatcher.find()) {
+ try {
+ String value = ldapURLMatcher.group(ldapURLPos).trim();
+ DN dn=LDAPURL.decode(value, true).getBaseDN();
+ groupDNs.add(dn);
+ } catch (DirectoryException ex) {
+ int msgID = MSGID_ACI_SYNTAX_INVALID_GROUPDN_URL;
+ String message = getMessage(msgID, ex.getErrorMessage());
+ throw new AciException(msgID, message);
+ }
+ }
+ return new GroupDN(type, groupDNs);
+ }
+
+ /**
+ * Performs the evaluation of a groupdn bind rule based on the
+ * evaluation context passed to it. The evaluation stops when there
+ * are no more group DNs to evaluate, or if a group DN evaluates to true
+ * if it contains the client DN.
+ * @param evalCtx An evaluation context to use in the evaluation.
+ * @return Enumeration evaluation result.
+ */
+ public EnumEvalResult evaluate(AciEvalContext evalCtx) {
+ EnumEvalResult matched = EnumEvalResult.FALSE;
+ Iterator<DN> it=groupDNs.iterator();
+ for(; it.hasNext() && matched != EnumEvalResult.TRUE;) {
+ DN groupDN=it.next();
+ Group group = groupManager.getGroupInstance(groupDN);
+ if((group != null) && (evalCtx.isMemberOf(group)))
+ matched = EnumEvalResult.TRUE;
+ }
+ return matched.getRet(type, false);
+ }
+
+ /**
+ * Performs an evaluation of a group that was specified in an attribute
+ * type value of the specified entry and attribute type. Each
+ * value of the attribute type is assumed to be a group DN and evaluation
+ * stops when there are no more values or if the group DN evaluates to
+ * true if it contains the client DN.
+ * @param e The entry to use in the evaluation.
+ * @param evalCtx The evaluation context to use in the evaluation.
+ * @param attributeType The attribute type of the entry to use to get the
+ * values for the groupd DNs.
+ * @return Enumeration evaluation result.
+ */
+ public static EnumEvalResult evaluate (Entry e, AciEvalContext evalCtx,
+ AttributeType attributeType) {
+ EnumEvalResult matched= EnumEvalResult.FALSE;
+ List<Attribute> attrs = e.getAttribute(attributeType);
+ LinkedHashSet<AttributeValue> vals = attrs.get(0).getValues();
+ for(AttributeValue v : vals) {
+ try {
+ DN groupDN=DN.decode(v.getStringValue());
+ Group group = groupManager.getGroupInstance(groupDN);
+ if((group != null) && (evalCtx.isMemberOf(group))) {
+ matched=EnumEvalResult.TRUE;
+ break;
+ }
+ } catch (DirectoryException ex) {
+ break;
+ }
+ }
+ return matched;
+ }
+}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/IpBitsNetworkCriteria.java b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/IpBitsNetworkCriteria.java
new file mode 100644
index 0000000..f1c76e1
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/IpBitsNetworkCriteria.java
@@ -0,0 +1,200 @@
+/*
+ * 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.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+/**
+ * This class builds a network string to an internal representation.
+ */
+public class IpBitsNetworkCriteria {
+
+ byte[] _address; // address in byte format
+ int _bits; // number of bits for matching
+ byte[] _bitsArray; // bits in network order
+ InetAddress _inetAddress;
+
+ /**
+ * Creates a new IpBitsNeworkCriteria instance.
+ *
+ * @param theInputAddress IP address associated the rule. For IPV4
+ * addresses, the following
+ * textual formats are supported
+ * a.b.c.d
+ * a.b.c
+ * a.b
+ * a
+ * For IPv6 addresses, the following textual
+ * format are supported:
+ * x:x:x:x:x:x:x:x, where x are the hexadecimal
+ * values of the 8
+ * 16-bits pieces of the address
+ * Use of :: to compress the leading
+ * and/or trailing zeros e.g.
+ * x::x:x:x:x:x:x
+ *
+ * @param theBits Number of bits of the network address
+ * necessary for matching.
+ * Max is 32 for IPv4 addresses and 128
+ * for IPv6 addresses \
+ *
+ * @throws UnknownHostException Thrown if the inetaddress cannot be gotten
+ * from the input address string.
+ * @throws AciException Thrown if the bit count is not in the correct
+ * ranges.
+ */
+
+ public IpBitsNetworkCriteria(String theInputAddress, int theBits)
+ throws UnknownHostException, AciException
+ {
+ boolean ipv4 = true;
+ _inetAddress = InetAddress.getByName(theInputAddress);
+
+ if (_inetAddress instanceof Inet6Address)
+ {
+ if (theBits < 0 || theBits > 128) {
+ int msgID = MSGID_ACI_SYNTAX_INVALID_NETWORK_BIT_MATCH;
+ String message = getMessage(msgID, "IPV6",
+ "Bits must be in [0..128] range.");
+ throw new AciException(msgID, message);
+ }
+ ipv4=false;
+ }
+ else
+ {
+ // Assume IPv4
+ if (theBits < 0 || theBits > 32) {
+ int msgID = MSGID_ACI_SYNTAX_INVALID_NETWORK_BIT_MATCH;
+ String message = getMessage(msgID, "IPV4",
+ "Bits must be in [0..32] range.");
+ throw new AciException(msgID, message);
+ }
+ }
+
+ _bits = theBits;
+
+ // Convert the bits into a mask in network byte order
+ if (ipv4)
+ {
+ _bitsArray = new byte[4];
+ // in java int is exactly 4 bytes
+ int rawBits;
+ if (theBits==0)
+ rawBits=0;
+ else
+ rawBits=~0;
+ rawBits = rawBits << (32 - theBits);
+ // Use network order for the comparison
+ _bitsArray[0] = (byte) ((rawBits >> 24) & 0xFF );
+ _bitsArray[1] = (byte) ((rawBits >> 16) & 0xFF );
+ _bitsArray[2] = (byte) ((rawBits >> 8) & 0xFF );
+ _bitsArray[3] = (byte) ((rawBits) & 0xFF );
+ }
+ else
+ {
+ _bitsArray = new byte[16];
+ int index=0;
+ if (theBits > 64)
+ {
+ _bitsArray[0] = (byte) 0xFF;
+ _bitsArray[1] = (byte) 0xFF;
+ _bitsArray[2] = (byte) 0xFF;
+ _bitsArray[3] = (byte) 0xFF;
+ _bitsArray[4] = (byte) 0xFF;
+ _bitsArray[5] = (byte) 0xFF;
+ _bitsArray[6] = (byte) 0xFF;
+ _bitsArray[7] = (byte) 0xFF;
+ theBits-=64;
+ index=8;
+ }
+ long rawBits = ~0;
+ rawBits = rawBits << (64 - theBits);
+
+ if (_bits !=0)
+ {
+ _bitsArray[index++] = (byte) ((rawBits >> 56) & 0xFF );
+ _bitsArray[index++] = (byte) ((rawBits >> 48) & 0xFF );
+ _bitsArray[index++] = (byte) ((rawBits >> 40) & 0xFF );
+ _bitsArray[index++] = (byte) ((rawBits >> 32) & 0xFF );
+ _bitsArray[index++] = (byte) ((rawBits >> 24) & 0xFF );
+ _bitsArray[index++] = (byte) ((rawBits >> 16) & 0xFF );
+ _bitsArray[index++] = (byte) ((rawBits >> 8) & 0xFF );
+ _bitsArray[index] = (byte) ((rawBits ) & 0xFF );
+ }
+ }
+
+ _address = _inetAddress.getAddress();
+ }
+
+ /**
+ * Compare an IP address with the network rule.
+ *
+ * @param theSourceAddress IP source address of the client contacting
+ * the proxy server.
+ * @return <CODE>true</CODE> if client matches the network rule or
+ * <CODE>false</CODE> if they may not.
+ */
+
+ public boolean match (InetAddress theSourceAddress)
+ {
+
+ byte[] addr = theSourceAddress.getAddress();
+
+ if ((addr.length * 8) < _bits) {
+ // Client IP too small. Won't match.
+ return false;
+ }
+
+ for (int i=0; i<addr.length; i++)
+ {
+ if ((addr[i] & _bitsArray[i]) != (_address[i] & _bitsArray[i])) {
+ return false;
+ }
+ }
+
+ return true;
+
+ }
+
+ /**
+ * String representation of this criteria.
+ *
+ * @return a String representation of the IpMaskNetworkCriteria
+ */
+
+ public String toString()
+ {
+ return "Address:" + _inetAddress.getHostAddress() +
+ "/" + Integer.toString(_bits);
+ }
+
+}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/IpCriteria.java b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/IpCriteria.java
new file mode 100644
index 0000000..6e05740
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/IpCriteria.java
@@ -0,0 +1,291 @@
+/*
+ * 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.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.HashSet;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * This class represents a ip bind rule keyword.
+ */
+public class IpCriteria implements KeywordBindRule
+{
+ private EnumBindRuleType type=null;
+
+ // private token to express that any address is accepted
+ private final static String ANY_ADDRESSES = "ALL";
+ private boolean matchAnyAddress = false;
+
+ private IpBitsNetworkCriteria[] ipBitsCriteria = null;
+ private IpMaskNetworkCriteria[] ipMaskCriteria = null;
+
+ /*
+ * TODO Verifiy IpCriteria constructor adheres to DS 5.2 ip keyword
+ * syntax.
+ *
+ * Based on the contents of the constructor, it doesn't appear that the
+ * IpCriteria class uses the same set of allowed values as DS 5.2 does
+ * with the "ip" keyword (as documented at
+ * http://docs.sun.com/source/817-7613/aci.html#wp20242). Was that the
+ * intention? It looks like it doesn't allow asterisks as wild-ccards or
+ * plus signs to specify netmasks (although it appears that it expects a
+ * slash to be used if you want a netmask). While I don't mind allowing
+ * alternate formats (e.g., CIDR-style addresses), we can't drop support
+ * for the existing ones if we're trying to maintain compatibility.
+ */
+ /**
+ * Constructor that creates an IpCriteria from an array of values and
+ * an enumeration bind rule type.
+ * @param values An array of address values.
+ * @param type An enumeration of the bind rule type.
+ * @throws UnknownHostException If the host address cannot be resolved to
+ * a hostname.
+ * @throws AciException If a part of the address is invalid.
+ * @throws IndexOutOfBoundsException If an index is incremented past an
+ * array bounds when copying or evaluating an address.
+ */
+ public IpCriteria(String[] values, EnumBindRuleType type)
+ throws UnknownHostException, IndexOutOfBoundsException, AciException {
+ IpBitsNetworkCriteria[] ipBitsCriteria_2 = null;
+ IpMaskNetworkCriteria[] ipMaskCriteria_2 = null;
+ try
+ {
+ for (String value : values)
+ {
+ if (value.equalsIgnoreCase(ANY_ADDRESSES))
+ {
+ matchAnyAddress = true;
+ continue;
+ }
+ // determine what format it is to instantiate
+ // the right criteria object
+ int slash = value.indexOf("/");
+ if (slash == -1)
+ {
+ // simple raw IP address
+ IpBitsNetworkCriteria newInstance;
+ if (InetAddress.getByName(value)
+ instanceof Inet6Address)
+ {
+ newInstance = new IpBitsNetworkCriteria(value, 128);
+ } else
+ {
+ newInstance = new IpBitsNetworkCriteria(value, 32);
+ }
+ if (ipBitsCriteria_2 == null)
+ {
+ ipBitsCriteria_2 = new IpBitsNetworkCriteria[1];
+ } else
+ {
+ IpBitsNetworkCriteria[] newIpBitsCriteria =
+ new IpBitsNetworkCriteria[ipBitsCriteria_2.length + 1];
+ System.arraycopy(ipBitsCriteria_2, 0,
+ newIpBitsCriteria, 0, ipBitsCriteria_2.length);
+ ipBitsCriteria_2 = newIpBitsCriteria;
+ }
+ ipBitsCriteria_2[ipBitsCriteria_2.length - 1] = newInstance;
+ } else
+ {
+ // Extract data following the / and figure out whether it
+ // is a bit number or a mask
+ try
+ {
+ int bits =
+ Integer.parseInt(value.substring(slash + 1));
+ // Well, no exception, so this is a bit
+ // Let's instantiate the corresponding criterion
+ if (ipBitsCriteria_2 == null)
+ {
+ ipBitsCriteria_2 = new IpBitsNetworkCriteria[1];
+ } else
+ {
+ IpBitsNetworkCriteria[] newIpBitsCriteria =
+ new IpBitsNetworkCriteria[ipBitsCriteria_2.length + 1];
+ System.arraycopy(ipBitsCriteria_2, 0,
+ newIpBitsCriteria, 0, ipBitsCriteria_2.length);
+ ipBitsCriteria_2 = newIpBitsCriteria;
+ }
+ ipBitsCriteria_2[ipBitsCriteria_2.length - 1] =
+ new IpBitsNetworkCriteria(value.
+ substring(0, slash), bits);
+ }
+ catch (IndexOutOfBoundsException e1)
+ {
+ throw e1;
+ }
+ catch (Exception e2)
+ {
+ // Looks like this is a network mask.
+ if (ipMaskCriteria_2 == null)
+ {
+ ipMaskCriteria_2 = new IpMaskNetworkCriteria[1];
+ } else
+ {
+ IpMaskNetworkCriteria[] newIpMaskCriteria =
+ new IpMaskNetworkCriteria[ipMaskCriteria_2.length + 1];
+ System.arraycopy(ipMaskCriteria_2, 0,
+ newIpMaskCriteria, 0, ipMaskCriteria_2.length);
+ ipMaskCriteria_2 = newIpMaskCriteria;
+ }
+ try
+ {
+ ipMaskCriteria_2[ipMaskCriteria_2.length - 1] =
+ new IpMaskNetworkCriteria(value.
+ substring(0, slash), value.substring(slash + 1));
+ }
+ catch (IndexOutOfBoundsException e3)
+ {
+ throw e3;
+ }
+ }
+ }
+ }
+ }
+ catch (UnknownHostException ue)
+ {
+ throw ue;
+ }
+ ipBitsCriteria = ipBitsCriteria_2;
+ ipMaskCriteria = ipMaskCriteria_2;
+ this.type=type;
+ }
+
+ /**
+ * Return the ipBitsNetworkCriteria of this IpCriteria.
+ * @return Returns the ipBitsNetworkCriteria.
+ */
+ public IpBitsNetworkCriteria[] getIpBitsNetworkCriteria() {
+ return ipBitsCriteria;
+ }
+
+ /**
+ * Return the ipMaskNetworkCriteria of this IpCriteria.
+ * @return Returns the ipMaskNetworkCriteria.
+ */
+ public IpMaskNetworkCriteria[] getIpMaskNetworkCriteria() {
+ return ipMaskCriteria;
+ }
+
+ /**
+ * Compare an IP address with the network rule.
+ *
+ * @param theSourceAddress IP source address of the client.
+ * @return <CODE>true</CODE> if client matches the network rule or
+ * <CODE>false</CODE> if they may not.
+ */
+ public boolean match (InetAddress theSourceAddress)
+ {
+ if (matchAnyAddress){
+ return true;
+ }
+ if (ipMaskCriteria != null)
+ {
+ for (IpMaskNetworkCriteria anIpMaskCriteria : ipMaskCriteria)
+ {
+ if (anIpMaskCriteria.match(theSourceAddress))
+ {
+ return true;
+ }
+ }
+ }
+
+ if (ipBitsCriteria != null)
+ {
+ for (IpBitsNetworkCriteria anIpBitsCriteria : ipBitsCriteria)
+ {
+ if (anIpBitsCriteria.match(theSourceAddress))
+ {
+ return true;
+ }
+ }
+ }
+
+ return (ipBitsCriteria == null) && (ipMaskCriteria == null);
+ }
+
+ /**
+ * Decode an expression string representing a ip keyword bind rule
+ * expression.
+ * @param expr A string representing the expression.
+ * @param type An enumeration representing the bind rule type.
+ * @return An keyword bind rule that can be used to evaluate the
+ * expression.
+ * @throws AciException If the expression string is invalid.
+ */
+ public static KeywordBindRule decode(String expr, EnumBindRuleType type)
+ throws AciException {
+ String valueRegex = "([^,\\s]+)";
+ String valuesRegex = valueRegex + "\\s*(,\\s*" + valueRegex + ")*";
+ if (!Pattern.matches(valuesRegex, expr)) {
+ int msgID = MSGID_ACI_SYNTAX_INVALID_IP_EXPRESSION;
+ String message = getMessage(msgID, expr);
+ throw new AciException(msgID, message);
+ }
+
+ int valuePos = 1;
+ Pattern valuePattern = Pattern.compile(valueRegex);
+ Matcher valueMatcher = valuePattern.matcher(expr);
+ HashSet<String> values = new HashSet<String>();
+ while (valueMatcher.find()) {
+ String value = valueMatcher.group(valuePos);
+ values.add(value);
+ }
+ IpCriteria ipCriteria;
+ String[] strValues = null;
+ if (!values.isEmpty()) {
+ strValues = values.toArray(new String[values.size()]);
+ }
+ try {
+ ipCriteria = new IpCriteria(strValues, type);
+ } catch (Exception e) {
+ int msgID = MSGID_ACI_SYNTAX_INVALID_IP_CRITERIA_DECODE;
+ String message = getMessage(msgID, e.getMessage());
+ throw new AciException(msgID, message);
+ }
+ return ipCriteria;
+ }
+
+ /**
+ * Evaluate the evaluation context against this ip criteria.
+ * @param evalCtx An evaluation context to use.
+ * @return An enumeration evaluation result.
+ */
+ public EnumEvalResult evaluate(AciEvalContext evalCtx) {
+ EnumEvalResult matched=EnumEvalResult.FALSE;
+ if(match(evalCtx.getRemoteAddress()))
+ matched=EnumEvalResult.TRUE;
+ return matched.getRet(type, false);
+ }
+}
+
diff --git a/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/IpMaskNetworkCriteria.java b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/IpMaskNetworkCriteria.java
new file mode 100644
index 0000000..38e15a7
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/IpMaskNetworkCriteria.java
@@ -0,0 +1,140 @@
+/*
+ * 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.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+/**
+ * This class creates a network mask criteria from the address and mask
+ * string passed to it.
+ */
+class IpMaskNetworkCriteria
+{
+
+ byte[] _address; // address in byte format
+ byte[] _mask; // mask in byte format
+ InetAddress _inetAddress;
+ InetAddress _inetMask;
+ boolean _ipv4; // true if ipv4 address
+
+
+ /**
+ * Creates a new IpMaskNeworkCriteria instance.
+ *
+ * @param theInputAddress IP address associated the rule. For IPV4
+ * addresses, the following
+ * textual formats are supported
+ * a.b.c.d
+ * a.b.c
+ * a.b
+ * a
+ * For IPv6 addresses, the following textual
+ * format are supported:
+ * x:x:x:x:x:x:x:x, where x are the hexadecimal
+ * values of the 8 16-bits pieces of the address
+ * Use of :: to compress the leading and/or
+ * trailing zeros e.g.x::x:x:x:x:x:x
+ *
+ * @param theInputMask Bits of the network address necessary
+ * for matching.
+ * Same format as the IP address above.
+ *
+ * @throws UnknownHostException Thrown if the hostname of the input address
+ * cannot be resolved.
+ * @throws AciException If the address family has a mismatch.
+ */
+
+ public IpMaskNetworkCriteria(String theInputAddress, String theInputMask)
+ throws UnknownHostException, AciException {
+ _inetAddress = InetAddress.getByName(theInputAddress);
+ _inetMask = InetAddress.getByName(theInputMask);
+ _address = _inetAddress.getAddress();
+ _mask = _inetMask.getAddress();
+
+ if (_inetAddress instanceof Inet4Address)
+ _ipv4=true;
+
+ if (_ipv4 && !(_inetMask instanceof Inet4Address) ||
+ (!_ipv4 && !(_inetMask instanceof Inet6Address))) {
+ int msgID = MSGID_ACI_SYNTAX_ADDRESS_FAMILY_MISMATCH;
+ String message = getMessage(msgID, theInputMask, theInputAddress);
+ throw new AciException(msgID, message);
+ }
+ }
+
+ /**
+ * Compare an IP address with the network criteria.
+ *
+ * @param theSourceAddress IP source address of the client.
+ * @return <CODE>true</CODE> if client matches the network rule or
+ * <CODE>false</CODE> if they may not.
+ */
+
+ public boolean match (InetAddress theSourceAddress)
+ {
+ // First address family must match
+ if (_ipv4)
+ {
+ if (!(theSourceAddress instanceof Inet4Address))
+ return false;
+ }
+ else
+ {
+ if (!(theSourceAddress instanceof Inet6Address))
+ return false;
+ }
+
+ byte[] addr = theSourceAddress.getAddress();
+ for (int i=0; i<addr.length; i++) {
+ if ((addr[i] & _mask[i]) != (_address[i] & _mask[i])) {
+ return false;
+ }
+ }
+ return true;
+
+ }
+
+ /**
+ * String representation of this rule.
+ *
+ * @return a String representation of the IpMaskNetworkRule.
+ */
+
+ public String toString()
+ {
+ return "Address:" + _inetAddress.getHostAddress() +
+ " Mask:" + _inetMask.getHostAddress();
+ }
+}
+
+
diff --git a/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/KeywordBindRule.java b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/KeywordBindRule.java
new file mode 100644
index 0000000..c9205f8
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/KeywordBindRule.java
@@ -0,0 +1,44 @@
+/*
+ * 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;
+
+/**
+ * This interface represents a keyword bind rule class
+ * that can evaluate an evaluation context. It defines a single
+ * function that each of the keyword functions implement (ip, dns,
+ * roledn, groupdn, ...)
+ */
+public interface KeywordBindRule
+{
+ /**
+ * Evaluate a bind rule using the passed in context.
+ * @param evalCtx An evaluation context to use in the evaluation.
+ * @return An enumeration evaluation result.
+ */
+ public EnumEvalResult evaluate(AciEvalContext evalCtx);
+}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/ParentInheritance.java b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/ParentInheritance.java
new file mode 100644
index 0000000..abb6ed9
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/ParentInheritance.java
@@ -0,0 +1,172 @@
+/*
+ * 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.StringTokenizer;
+import org.opends.server.core.DirectoryServer;
+import org.opends.server.types.AttributeType;
+
+/**
+ * This class is used by USERDN and GROUPDN userattr types
+ * to determine what parent inheritance checks to make.
+ */
+public class ParentInheritance {
+ /*
+ * The maximum number of parent inheritance levels supported.
+ *
+ */
+ private static final int MAX_LEVELS=10;
+ private String parentPat="parent[";
+ private int[] levels=new int[MAX_LEVELS];
+ private int numLevels;
+ private AttributeType attributeType;
+
+
+ /**
+ * Construct a class from the inheritance pattern. The skipParsing boolean
+ * specifies that parent parsing should be skipped and sets up the class:
+ * with numLevels=1, level[0]=0 and an attribute type from the
+ * specified pattern.
+ *
+ * @param pattern The string pattern containing the inheritance
+ * information.
+ * @param skipParse Specify if the parent inheritance parsing should be
+ * skipped or not.
+ * @throws AciException If the pattern is invalid.
+ */
+ ParentInheritance (String pattern, boolean skipParse) throws AciException {
+ if (skipParse) {
+ //The "parent[" pattern is invalid for ROLEDN user attr keyword.
+ if(pattern.startsWith(parentPat)) {
+ int msgID =
+ MSGID_ACI_SYNTAX_INVALID_USERATTR_ROLEDN_INHERITANCE_PATTERN;
+ String message = getMessage(msgID, pattern);
+ throw new AciException(msgID, message);
+ } else {
+ pattern=pattern.trim();
+ if((this.attributeType =
+ DirectoryServer.getAttributeType(pattern)) == null)
+ this.attributeType =
+ DirectoryServer.getDefaultAttributeType(pattern);
+ numLevels=1;
+ levels[0]=0;
+ }
+ } else
+ parse(pattern);
+ }
+
+ /**
+ * Performs all parsing of the specified pattern string.
+ * @param pattern The string pattern containing the inheritance
+ * information.
+ * @throws AciException If the pattern is invalid.
+ */
+ private void parse (String pattern) throws AciException {
+ pattern=pattern.trim();
+ /**
+ * Check if we have a "parent[" string.
+ */
+ if(pattern.startsWith(parentPat)) {
+ numLevels=0;
+ levels[0]=0;
+ String p=pattern.substring(parentPat.length());
+ /**
+ * Format needs to be parent[XX].attribute -- everything after the
+ * '.' is the attribute type.
+ */
+ String[] toks=p.split("\\.");
+ if(toks.length != 2) {
+ int msgID =
+ MSGID_ACI_SYNTAX_INVALID_USERATTR_INHERITANCE_PATTERN;
+ String message = getMessage(msgID, pattern);
+ throw new AciException(msgID, message);
+ }
+ if((this.attributeType =
+ DirectoryServer.getAttributeType(toks[1])) == null)
+ this.attributeType =
+ DirectoryServer.getDefaultAttributeType(toks[1]);
+ StringTokenizer tok=new StringTokenizer(toks[0],"],",false);
+ while(tok.hasMoreTokens()) {
+ String v=tok.nextToken();
+ /**
+ * Everything between the brackets must be an integer or it's
+ * an error.
+ */
+ try {
+ if(numLevels < MAX_LEVELS) {
+ levels[numLevels++]=Integer.decode(v);
+ } else {
+ int msgID =
+ MSGID_ACI_SYNTAX_MAX_USERATTR_INHERITANCE_LEVEL_EXCEEDED;
+ String message = getMessage(msgID, pattern,
+ Integer.toString(MAX_LEVELS));
+ throw new AciException(msgID, message);
+ }
+ } catch (NumberFormatException ex) {
+ int msgID = MSGID_ACI_SYNTAX_INVALID_INHERITANCE_VALUE;
+ String message = getMessage(msgID, pattern);
+ throw new AciException(msgID, message);
+ }
+ }
+ } else {
+ if((this.attributeType =
+ DirectoryServer.getAttributeType(pattern)) == null)
+ this.attributeType =
+ DirectoryServer.getDefaultAttributeType(pattern);
+ numLevels=1;
+ levels[0]=0;
+ }
+ }
+
+ /**
+ * Returns the number of levels counted.
+ * @return The number of levels.
+ */
+ public int getNumLevels() {
+ return numLevels;
+ }
+
+ /**
+ * Returns an array of levels, where levels are integers.
+ * @return Return an array of levels.
+ */
+ public int[] getLevels() {
+ return levels;
+ }
+
+ /**
+ * Return the attribute type.
+ * @return The attribute type.
+ */
+ public AttributeType getAttributeType() {
+ return attributeType;
+ }
+}
+
diff --git a/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/PermBindRulePair.java b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/PermBindRulePair.java
new file mode 100644
index 0000000..41474a6
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/PermBindRulePair.java
@@ -0,0 +1,93 @@
+/*
+ * 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;
+
+/**
+ * A class representing a permission-bind rule pair. There can be multiple
+ * of these in an ACI.
+ */
+public class PermBindRulePair {
+
+ private BindRule bindRule;
+ private Permission perm=null;
+
+ /**
+ * This constructor calls the permission and bind rule decodes
+ * with the appropriate strings.
+ * @param perm A string representing the permissions.
+ * @param rights A string representing the rights.
+ * @param bindRule A string representing the bind rule.
+ * @throws AciException If any of the strings fail to decode.
+ */
+ private PermBindRulePair(String perm, String rights, String bindRule)
+ throws AciException {
+ this.perm=Permission.decode(perm, rights);
+ this.bindRule=BindRule.decode(bindRule);
+ }
+
+ /**
+ * Decodes a permission bind rule pair.
+ * @param perm A string representing the permissions.
+ * @param rights A string representing the rights.
+ * @param bRule A string representing the bind rule.
+ * @return An permission bind rule pair class representing this pair.
+ * @throws AciException If any of the strings fail to decode.
+ */
+ public static PermBindRulePair decode(String perm, String rights,
+ String bRule) throws AciException {
+ return new PermBindRulePair(perm, rights, bRule);
+ }
+
+ /**
+ * Gets the bind rule part of this pair.
+ * @return The bind rule part of this pair.
+ */
+ public BindRule getBindRule () {
+ return bindRule;
+ }
+
+ /**
+ * Checks the permission to see if it has this access type.
+ * @param accessType An enumeration of the desired access type.
+ * @return True if the access type equals the permission access type.
+ */
+ public boolean hasAccessType(EnumAccessType accessType) {
+ return perm.hasAccessType(accessType);
+ }
+
+ /**
+ * Try and match one or more of the specified rights against a rights set
+ * of the permission class.
+ * @param right The rights to match.
+ * @return True if one or more of the specified rights match a right in
+ * the rights set of the permission class.
+ */
+ public boolean hasRights(int right) {
+ return perm.hasRights(right);
+ }
+}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/Permission.java b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/Permission.java
new file mode 100644
index 0000000..5f340ba
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/Permission.java
@@ -0,0 +1,117 @@
+/*
+ * 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.messages.MessageHandler.getMessage;
+import static org.opends.server.authorization.dseecompat.AciMessages.*;
+import java.util.regex.Pattern;
+
+/**
+ * A class representing the permissions of an bind rule. The permissions
+ * of an ACI look like deny(search, write).
+ */
+public class Permission {
+ //the access type (allow,deny)
+ private EnumAccessType accessType = null;
+ private int rights;
+ private static final String separatorToken = ",";
+ private static final String rightsRegex =
+ "\\s*(\\w+)\\s*(,\\s*(\\w+)\\s*)*";
+
+ /**
+ * Constructor creating a class representing a permission part of an bind
+ * rule.
+ * @param accessType A string representing access type.
+ * @param rights A string representing the rights.
+ * @throws AciException If the access type string or rights string
+ * is invalid.
+ */
+ private Permission(String accessType, String rights)
+ throws AciException {
+ if ((this.accessType =
+ EnumAccessType.decode(accessType)) == null){
+ int msgID = MSGID_ACI_SYNTAX_INVALID_ACCESS_TYPE_VERSION;
+ String message = getMessage(msgID, accessType);
+ throw new AciException(msgID, message);
+ }
+ if (!Pattern.matches(rightsRegex, rights)){
+ int msgID = MSGID_ACI_SYNTAX_INVALID_RIGHTS_SYNTAX;
+ String message = getMessage(msgID, rights);
+ throw new AciException(msgID, message);
+ }
+ else {
+ Pattern separatorPattern = Pattern.compile(separatorToken);
+ String[] rightsStr =
+ separatorPattern.split(rights.replaceAll("\\s", ""));
+ for (String r : rightsStr) {
+ EnumRight right = EnumRight.decode(r);
+ if (right != null)
+ this.rights|= EnumRight.getMask(right);
+ else {
+ int msgID = MSGID_ACI_SYNTAX_INVALID_RIGHTS_KEYWORD;
+ String message = getMessage(msgID, rights);
+ throw new AciException(msgID, message);
+ }
+ }
+ }
+ }
+
+ /**
+ * Decode an string representation of bind rule permission into a Permission
+ * class.
+ * @param accessType A string representing the access type.
+ * @param rights A string representing the rights.
+ * @return A Permission class representing the permissions of the bind
+ * rule.
+ * @throws AciException If the accesstype or rights strings are invalid.
+ */
+ public static
+ Permission decode (String accessType, String rights)
+ throws AciException {
+ return new Permission(accessType, rights);
+ }
+
+ /**
+ * Checks if a given access type enumeration is equal to this classes
+ * access type.
+ * @param accessType An enumeration representing an access type.
+ * @return True if the access types are equal.
+ */
+ public boolean hasAccessType(EnumAccessType accessType) {
+ return this.accessType == accessType;
+ }
+
+ /**
+ * Checks if the permission's rights has the specified rights.
+ * @param rights The rights to check for.
+ * @return True if the permission's rights has the specified rights.
+ */
+ public boolean hasRights(int rights) {
+ return (this.rights & rights) != 0;
+ }
+}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/RoleDN.java b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/RoleDN.java
new file mode 100644
index 0000000..a32f27e
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/RoleDN.java
@@ -0,0 +1,157 @@
+/*
+ * 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 org.opends.server.types.*;
+import org.opends.server.api.Group;
+import org.opends.server.core.GroupManager;
+import org.opends.server.core.DirectoryServer;
+
+import java.util.LinkedList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.LinkedHashSet;
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
+
+/**
+ * A class representing a roledn bind rule keyword. This class is almost
+ * an exact copy of groupDN, except for variable names and error messages.
+ */
+public class RoleDN implements KeywordBindRule {
+
+ LinkedList<DN> roleDNs=null;
+ private EnumBindRuleType type=null;
+ private static GroupManager groupManager =
+ DirectoryServer.getGroupManager();
+
+ /**
+ * Constructor creating a class representing a roledn keyword of a bind
+ * rule.
+ * @param type An enumeration of the type of the bind rule.
+ * @param roleDNs A list of the role dns parsed from the expression string.
+ */
+ private RoleDN(EnumBindRuleType type, LinkedList<DN> roleDNs ) {
+ this.roleDNs=roleDNs;
+ this.type=type;
+ }
+
+ /**
+ * Decodes an expression string representing an roledn bind rule.
+ * @param expr A string representation of the bind rule.
+ * @param type An enumeration of the type of the bind rule.
+ * @return A keyword bind rule class that can be used to evaluate
+ * this bind rule.
+ * @throws AciException If the expression is invalid.
+ */
+ public static KeywordBindRule decode(String expr, EnumBindRuleType type)
+ throws AciException {
+ String ldapURLRegex = "\\s*(ldap:///[^\\|]+)";
+ String ldapURLSRegex =
+ ldapURLRegex + "\\s*(\\|\\|\\s*" + ldapURLRegex + ")*";
+ if (!Pattern.matches(ldapURLSRegex, expr)) {
+ int msgID = MSGID_ACI_SYNTAX_INVALID_ROLEDN_EXPRESSION;
+ String message = getMessage(msgID, expr);
+ throw new AciException(msgID, message);
+ }
+ LinkedList<DN>roleDNs=new LinkedList<DN>();
+ int ldapURLPos = 1;
+ Pattern ldapURLPattern = Pattern.compile(ldapURLRegex);
+ Matcher ldapURLMatcher = ldapURLPattern.matcher(expr);
+ while (ldapURLMatcher.find()) {
+ String val = ldapURLMatcher.group(ldapURLPos);
+ val = val.trim();
+ DN dn;
+ try {
+ dn=DN.decode(val);
+ } catch (DirectoryException ex) {
+ int msgID = MSGID_ACI_SYNTAX_INVALID_ROLEDN_URL;
+ String message = getMessage(msgID, ex.getErrorMessage());
+ throw new AciException(msgID, message);
+ }
+ roleDNs.add(dn);
+ }
+ return new RoleDN(type, roleDNs);
+ }
+
+
+ /**
+ * Performs the evaluation of a roledn bind rule based on the
+ * evaluation context passed to it. The method uses an exact copy
+ * evaluation method as the groupDN.evaluate(). The evaluation stops when
+ * there are no more group DNs to evaluate, or if a group DN evaluates to
+ * true if it contains the authorization DN.
+ * @param evalCtx An evaluation context to use in the evaluation.
+ * @return Enumeration evaluation result.
+ */
+ public EnumEvalResult evaluate(AciEvalContext evalCtx) {
+ EnumEvalResult matched = EnumEvalResult.FALSE;
+ Iterator<DN> it=roleDNs.iterator();
+ for(; it.hasNext() && matched != EnumEvalResult.TRUE;) {
+ DN groupDN=it.next();
+ Group group = groupManager.getGroupInstance(groupDN);
+ if(evalCtx.isMemberOf(group))
+ matched = EnumEvalResult.TRUE;
+ }
+ return matched.getRet(type, false);
+ }
+
+ /**
+ * Performs an evaluation of a group that was specified in an attribute
+ * type value of the specified entry and attribute type. Each
+ * value of the attribute type is assumed to be a group DN and evaluation
+ * stops when there are no more values or if the group DN evaluates to
+ * true if it contains the client DN.
+ * @param e The entry to use in the evaluation.
+ * @param evalCtx The evaluation context to use in the evaluation.
+ * @param attributeType The attribute type of the entry to use to get the
+ * values for the groupd DNs.
+ * @return Enumeration evaluation result.
+ */
+ public static EnumEvalResult evaluate (Entry e, AciEvalContext evalCtx,
+ AttributeType attributeType) {
+ EnumEvalResult matched= EnumEvalResult.FALSE;
+ List<Attribute> attrs = e.getAttribute(attributeType);
+ LinkedHashSet<AttributeValue> vals = attrs.get(0).getValues();
+ for(AttributeValue v : vals) {
+ try {
+ DN groupDN=DN.decode(v.getStringValue());
+ Group group = groupManager.getGroupInstance(groupDN);
+ if((group != null) && (evalCtx.isMemberOf(group))) {
+ matched=EnumEvalResult.TRUE;
+ break;
+ }
+ } catch (DirectoryException ex) {
+ break;
+ }
+ }
+ return matched;
+ }
+}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/TargAttrFilters.java b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/TargAttrFilters.java
new file mode 100644
index 0000000..2efc454
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/TargAttrFilters.java
@@ -0,0 +1,55 @@
+/*
+ * 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;
+
+/**
+ * Placeholder. This class is partially complete. The TargAttrFilters class
+ * will represent an targAttrFitlers rule.
+ */
+public class TargAttrFilters {
+
+ /**
+ * Represents an targAttrFilters rule.
+ */
+ public TargAttrFilters() {
+
+ }
+
+ /**
+ * Decode an string representing a targattrfilters keyword.
+ * @param operator The operator of the rule.
+ * @param expression The string parsed from the ACI representing the
+ * targattrfilters rule.
+ * @return An object representing an targattrfilters rule.
+ * @throws AciException if the expression string cannot be parsed.
+ */
+ public static TargAttrFilters decode(EnumTargetOperator operator,
+ String expression) throws AciException {
+ return new TargAttrFilters();
+ }
+}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/Target.java b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/Target.java
new file mode 100644
index 0000000..d3d755b
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/Target.java
@@ -0,0 +1,198 @@
+/*
+ * 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.LinkedHashSet;
+import java.util.regex.Pattern;
+import org.opends.server.core.DirectoryServer;
+import org.opends.server.types.Attribute;
+import org.opends.server.types.AttributeType;
+import org.opends.server.types.AttributeValue;
+import org.opends.server.types.DN;
+import org.opends.server.types.DirectoryException;
+import org.opends.server.types.Entry;
+import org.opends.server.types.LDAPURL;
+import org.opends.server.types.SearchFilter;
+
+/**
+ * A class representing an ACI target keyword.
+ */
+public class Target
+{
+ private EnumTargetOperator operator = EnumTargetOperator.EQUALITY;
+ private LDAPURL targetURL = null;
+ private DN urlDN=null;
+ private boolean isPattern=false;
+ private SearchFilter filter=null;
+ private AttributeType targetType;
+
+
+ /*
+ * TODO Save aciDN parameter and use it in matchesPattern re-write.
+ *
+ * Should the aciDN argument provided to the constructor be stored so that
+ * it can be used in the matchesPattern() method? The DN should only be
+ * considered a potential match if it is at or below the entry containing
+ * the ACI.
+ *
+ * TODO Evaluate re-writing pattern (substring) determination code. The
+ * current code is similar to current DS6 implementation.
+ *
+ * I'm confused by the part of the constructor that generates a search
+ * filter. First, there is no substring matching rule defined for the
+ * DN syntax in the official standard, so technically trying to perform
+ * substring matching against DNs is illegal. Although we do try to use
+ * the caseIgnoreSubstringsMatch rule, it is extremely unreliable for DNs
+ * because it's just not possible to do substring matching correctly in all
+ * cases for them. Also, the logic in place there will only generate a
+ * filter if the DN contains a wildcard, and if it starts with a wildcard
+ * (which is handled by the targetDN.startsWith("*") clause), then you'll
+ * end up with something like "(target=**dc=example,dc=com)", which isn't
+ * legal.
+ */
+ /**
+ * This constructor parses the target string.
+ * @param operator An enumeration of the operation of this target.
+ * @param target A string representation of the target.
+ * @param aciDN The dn of the ACI entry used for a descendant check.
+ * @throws AciException If the target string is invalid.
+ */
+ private Target(EnumTargetOperator operator, String target, DN aciDN)
+ throws AciException {
+ this.operator = operator;
+ try {
+ String ldapURLRegex = "\\s*(ldap:///[^\\|]+)";
+ if (!Pattern.matches(ldapURLRegex, target)) {
+ int msgID = MSGID_ACI_SYNTAX_INVALID_TARGETKEYWORD_EXPRESSION;
+ String message = getMessage(msgID, target);
+ throw new AciException(msgID, message);
+ }
+ targetURL = LDAPURL.decode(target, false);
+ urlDN=targetURL.getBaseDN();
+ String targetDN=urlDN.toNormalizedString();
+ if((targetDN.startsWith("*")) ||
+ (targetDN.indexOf("*") != -1)) {
+ this.isPattern=true;
+ String pattern="target=*"+targetDN;
+ filter=SearchFilter.createFilterFromString(pattern);
+ targetType = DirectoryServer.getAttributeType("target");
+ if (targetType == null)
+ targetType =
+ DirectoryServer.getDefaultAttributeType("target");
+ } else {
+ if(!urlDN.isDescendantOf(aciDN)) {
+ int msgID = MSGID_ACI_SYNTAX_TARGET_DN_NOT_DESCENDENTOF;
+ String message = getMessage(msgID,
+ urlDN.toNormalizedString(),
+ aciDN.toNormalizedString());
+ throw new AciException(msgID, message);
+ }
+ }
+ }
+ catch (DirectoryException e){
+ int msgID = MSGID_ACI_SYNTAX_INVALID_TARGETKEYWORD_EXPRESSION;
+ String message = getMessage(msgID, target);
+ throw new AciException(msgID, message);
+ }
+ }
+
+ /**
+ * Decode an expression string representing a target keyword expression.
+ * @param operator An enumeration of the operation of this target.
+ * @param expr A string representation of the target.
+ * @param aciDN The DN of the ACI entry used for a descendant check.
+ * @return A Target class representing this target.
+ * @throws AciException If the expression string is invalid.
+ */
+ public static Target decode(EnumTargetOperator operator,
+ String expr, DN aciDN)
+ throws AciException {
+ return new Target(operator, expr, aciDN);
+ }
+
+ /**
+ * Returns the operator of this expression.
+ * @return An enumeration of the operation value.
+ */
+ public EnumTargetOperator getOperator() {
+ return operator;
+ }
+
+ /**
+ * Returns the URL DN of the expression.
+ * @return A DN of the URL.
+ */
+ public DN getDN() {
+ return urlDN;
+ }
+
+ /**
+ * Returns boolean if a pattern was seen during parsing.
+ * @return True if the DN is a wild-card.
+ */
+ public boolean isPattern() {
+ return isPattern;
+ }
+
+ /*
+ * TODO Evaluate re-writing this method.
+ *
+ * The matchesPattern() method really needs to be rewritten. It's using a
+ * very inefficient and very error-prone method to make the determination.
+ * If you're really going to attempt pattern matching on a DN, then I'd
+ * suggest trying a regular expression against the normalized DN rather
+ * than a filter.
+ */
+ /**
+ * This method tries to match a pattern against a DN. It builds an entry
+ * with a target attribute containing the pattern and then matches against
+ * it.
+ * @param dn The DN to try an match.
+ * @return True if the pattern matches.
+ */
+ public boolean matchesPattern(DN dn) {
+ boolean ret;
+ String targetDN=dn.toNormalizedString();
+ LinkedHashSet<AttributeValue> values =
+ new LinkedHashSet<AttributeValue>();
+ values.add(new AttributeValue(targetType, targetDN));
+ Attribute attr = new Attribute(targetType, "target", values);
+ Entry e = new Entry(DN.nullDN(), null, null, null);
+ e.addAttribute(attr,new ArrayList<AttributeValue>());
+ try {
+ ret=filter.matchesEntry(e);
+ } catch (DirectoryException ex) {
+ //TODO information message?
+ return false;
+ }
+ return ret;
+ }
+}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/TargetAttr.java b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/TargetAttr.java
new file mode 100644
index 0000000..6cb7db8
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/TargetAttr.java
@@ -0,0 +1,187 @@
+/*
+ * 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.HashSet;
+import java.util.regex.Pattern;
+import org.opends.server.core.DirectoryServer;
+import org.opends.server.types.AttributeType;
+
+/**
+ * A class representing an ACI's targetattr keyword.
+ */
+public class TargetAttr {
+ private EnumTargetOperator operator = EnumTargetOperator.EQUALITY;
+ private boolean allAttributes = false ;
+ /*
+ * HashSet of the attribute types parsed by the constructor.
+ */
+ private HashSet<AttributeType> attributes = new HashSet<AttributeType>();
+ //private String[] attributes = new String[0];
+ private static final String allAttrsRegex = "\\s*\\*\\s*";
+ private static final String noAttrsRegex = "\\s*";
+ private static final String separatorToken = "\\|\\|";
+ private static final String attrListRegex =
+ "\\s*(\\w+)\\s*(" + separatorToken + "\\s*(\\w+)\\s*)*";
+
+ /**
+ * Constructor creating a class representing a targetattr keyword of an ACI.
+ * @param operator The operation enumeration of the targetattr
+ * expression (=, !=).
+ * @param attrString A string representing the attributes specified in
+ * the targetattr expression (ie, dn || cn).
+ * @throws AciException If the attrs string is invalid.
+ */
+ private TargetAttr(EnumTargetOperator operator, String attrString)
+ throws AciException {
+ this.operator = operator;
+ if (attrString != null) {
+ if (Pattern.matches(allAttrsRegex, attrString) ){
+ allAttributes = true ;
+ } else {
+ if (Pattern.matches(noAttrsRegex, attrString)){
+ allAttributes = false;
+ } else {
+ if (Pattern.matches(attrListRegex, attrString)) {
+ // Remove the spaces in the attr string and
+ // split the list.
+ Pattern separatorPattern =
+ Pattern.compile(separatorToken);
+ attrString=attrString.replaceAll("\\s", "");
+ String[] attributeArray=
+ separatorPattern.split(attrString);
+ //Add each element of array to attributes HashSet
+ //after converting it to AttributeType.
+ arrayToAttributeTypes(attributeArray);
+ } else {
+ int msgID =
+ MSGID_ACI_SYNTAX_INVALID_TARGETATTRKEYWORD_EXPRESSION;
+ String message = getMessage(msgID, operator);
+ throw new AciException(msgID, message);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Converts each element of an array of attribute type strings
+ * to attribute types and adds them to the attributes HashSet.
+ * @param attributeArray The array of attribute type strings.
+ */
+ private void arrayToAttributeTypes(String[] attributeArray) {
+ for (int i=0, n=attributeArray.length; i < n; i++) {
+ String attribute=attributeArray[i].toLowerCase();
+ AttributeType attributeType;
+ if((attributeType =
+ DirectoryServer.getAttributeType(attribute)) == null)
+ attributeType =
+ DirectoryServer.getDefaultAttributeType(attribute);
+ attributes.add(attributeType);
+ }
+ }
+ /**
+ * Returns the operator enumeration of the targetattr expression.
+ * @return The operator enumeration.
+ */
+ public EnumTargetOperator getOperator() {
+ return operator;
+ }
+
+ /**
+ * This flag is set if the parsing code saw:
+ * targetattr="*" or targetattr != "*".
+ * @return True if all attributes was seen.
+ */
+ public boolean isAllAttributes() {
+ return allAttributes;
+ }
+
+ /**
+ * Return array holding each attribute type to be evaluated
+ * in the expression.
+ * @return Array holding each attribute types.
+ */
+ public HashSet<AttributeType> getAttributes() {
+ return attributes;
+ }
+
+ /**
+ * Decodes an targetattr expression string into a targetattr class suitable
+ * for evaluation.
+ * @param operator The operator enumeration of the expression.
+ * @param expr The expression string to be decoded.
+ * @return A TargetAttr suitable to evaluate this ACI's targetattrs.
+ * @throws AciException If the expression string is invalid.
+ */
+ public static TargetAttr decode(EnumTargetOperator operator, String expr)
+ throws AciException {
+ return new TargetAttr(operator, expr);
+ }
+
+ /**
+ * Perform two checks to see if a specified attribute type is applicable.
+ * First, check the targetAttr's isAllAttributes() boolean. The
+ * isAllAttributes boolean is set true when the string:
+ *
+ * targetattrs="*"
+ *
+ * is seen when an ACI is parsed. If the isAllAttributes boolean is
+ * true, the second check is skipped and the TargetAttr's operator is
+ * checked to see if the method should return false (NOT_EQUALITY)
+ * instead of true.
+ *
+ * If the isAllAttributes boolean is false, then the TargeAttr's
+ * attribute type HashSet is searched to see if it contains the
+ * specified attribute type. That result could be negated depending
+ * on if the TargetAttr's operator is NOT_EQUALITY.
+ *
+ * @param a The attribute type to evaluate.
+ * @param targetAttr The ACI's TargetAttr class to evaluate against.
+ * @return The boolean result of the above tests and application
+ * TargetAttr's operator value applied to the test result.
+ */
+ public static boolean isApplicable(AttributeType a,
+ TargetAttr targetAttr) {
+ boolean ret;
+ if(targetAttr.isAllAttributes()) {
+ ret =
+ !targetAttr.getOperator().equals(EnumTargetOperator.NOT_EQUALITY);
+ } else {
+ ret=false;
+ HashSet<AttributeType> attributes=targetAttr.getAttributes();
+ if(attributes.contains(a))
+ ret=true;
+ if(targetAttr.getOperator().equals(EnumTargetOperator.NOT_EQUALITY))
+ ret = !ret;
+ }
+ return ret;
+ }
+}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/TargetFilter.java b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/TargetFilter.java
new file mode 100644
index 0000000..14cf8a4
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/TargetFilter.java
@@ -0,0 +1,103 @@
+/*
+ * 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 org.opends.server.types.DirectoryException;
+import org.opends.server.types.Entry;
+import org.opends.server.types.SearchFilter;
+
+/**
+ * This class represents a targetfilter keyword of an aci.
+ *
+ */
+public class TargetFilter {
+ private EnumTargetOperator op = EnumTargetOperator.EQUALITY;
+ private SearchFilter filter;
+
+ /**
+ * Class representing a targetfilter keyword.
+ * @param op The operation of the targetfilter expression (=, !=)
+ * @param filter The filter itself.
+ */
+ private TargetFilter(EnumTargetOperator op, SearchFilter filter) {
+ this.op=op;
+ this.filter=filter;
+ }
+
+ /**
+ * Decode a aci's targetfilter string.
+ * @param op The operation enumeration of the expression.
+ * @param expr A string representing the target filter.
+ * @return A TargetFilter class suitable for using in a match.
+ * @throws AciException If the expression string is invalid.
+ */
+ public static TargetFilter decode(EnumTargetOperator op, String expr)
+ throws AciException {
+ SearchFilter filter;
+ try {
+ filter = SearchFilter.createFilterFromString(expr);
+ } catch (DirectoryException ex) {
+ int msgID =
+ MSGID_ACI_SYNTAX_INVALID_TARGETFILTERKEYWORD_EXPRESSION;
+ String message = getMessage(msgID, expr);
+ throw new AciException(msgID, message);
+ }
+ return new TargetFilter(op, filter);
+ }
+
+ /**
+ * Checks if a targetfilter matches an evaluation context.
+ * @param matchCtx The evaluation context to use in the matching.
+ * @return True if the target filter matched the context.
+ */
+ public boolean isApplicable(AciTargetMatchContext matchCtx) {
+ boolean ret;
+ ret=matchesFilter(matchCtx.getResourceEntry());
+ if(op.equals(EnumTargetOperator.NOT_EQUALITY))
+ ret = !ret;
+ return ret;
+ }
+
+ /**
+ * Checks the filter against an entry taken from the match context.
+ * @param e The entry from the evaluation context above.
+ * @return True if the filter matches the entry.
+ */
+ private boolean matchesFilter(Entry e) {
+ boolean ret;
+ try {
+ ret=filter.matchesEntry(e);
+ } catch (DirectoryException ex) {
+ //TODO information message?
+ return false;
+ }
+ return ret;
+ }
+}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/TimeOfDay.java b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/TimeOfDay.java
new file mode 100644
index 0000000..f96f484
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/TimeOfDay.java
@@ -0,0 +1,121 @@
+/*
+ * 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 org.opends.server.util.TimeThread;
+import java.util.regex.Pattern;
+
+/**
+ * This class represents the timeofday keyword in a bind rule.
+ */
+public class TimeOfDay implements KeywordBindRule {
+ private static final String timeofdayRegex = "[0-2]\\d[0-5]\\d";
+ private EnumBindRuleType type=null;
+ private int timeRef;
+
+ /**
+ * Constructor to create a timeofday keyword class.
+ * @param timeVal The time value to check for (0-2359).
+ * @param type An enumeration of the type of the expression.
+ */
+ private TimeOfDay(int timeVal, EnumBindRuleType type) {
+ this.timeRef=timeVal;
+ this.type=type;
+ }
+
+ /**
+ * Decodes a string representation of a timeofday bind rule expression.
+ * @param expr A string representation of the expression.
+ * @param type An enumeration of the type of the expression.
+ * @return A TimeOfDay class representing the expression.
+ * @throws AciException If the expression is invalid.
+ */
+ public static TimeOfDay decode(String expr, EnumBindRuleType type)
+ throws AciException {
+ if (!Pattern.matches(timeofdayRegex, expr))
+ {
+ int msgID = MSGID_ACI_SYNTAX_INVALID_TIMEOFDAY;
+ String message = getMessage(msgID,expr);
+ throw new AciException(msgID, message);
+ }
+ int valueAsInt = Integer.parseInt(expr);
+ if ((valueAsInt < 0) || (valueAsInt > 2359))
+ {
+ int msgID = MSGID_ACI_SYNTAX_INVALID_TIMEOFDAY_RANGE;
+ String message = getMessage(msgID,expr);
+ throw new AciException(msgID, message);
+ }
+
+ return new TimeOfDay(valueAsInt, type);
+ }
+
+ /**
+ * Evaluates the timeofday bind rule using the evaluation context
+ * passed into the method.
+ * @param evalCtx The evaluation context to use for the evaluation.
+ * @return An enumeration result representing the result of the
+ * evaluation.
+ */
+ public EnumEvalResult evaluate(AciEvalContext evalCtx) {
+ EnumEvalResult matched=EnumEvalResult.FALSE;
+
+ int currentTime=TimeThread.getHourAndMinute();
+ //check the type
+ switch (type) {
+ case LESS_OR_EQUAL_BINDRULE_TYPE:
+ if (currentTime <= timeRef)
+ {
+ matched=EnumEvalResult.TRUE;
+ }
+ break;
+
+ case LESS_BINDRULE_TYPE:
+ if (currentTime < timeRef)
+ {
+ matched=EnumEvalResult.TRUE;
+ }
+ break;
+
+ case GREATER_OR_EQUAL_BINDRULE_TYPE:
+ if (currentTime >= timeRef)
+ {
+ matched=EnumEvalResult.TRUE;
+ }
+ break;
+
+ case GREATER_BINDRULE_TYPE:
+ if (currentTime > timeRef)
+ {
+ matched=EnumEvalResult.TRUE;
+ }
+ }
+ return matched.getRet(type, false);
+ }
+}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/UserAttr.java b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/UserAttr.java
new file mode 100644
index 0000000..7d409e7
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/UserAttr.java
@@ -0,0 +1,388 @@
+/*
+ * 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.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+import org.opends.server.core.DirectoryServer;
+import org.opends.server.protocols.internal.InternalClientConnection;
+import org.opends.server.protocols.internal.InternalSearchOperation;
+import org.opends.server.types.*;
+/*
+ * TODO Evaluate making this class more efficient.
+ *
+ * This class isn't as efficient as it could be. For example, the evalVAL()
+ * method should be able to use cached versions of the attribute type and
+ * filter. The evalURL() and evalDN() methods should also be able to use a
+ * cached version of the attribute type.
+ */
+/**
+ * This class implements the userattr bind rule keyword.
+ */
+public class UserAttr implements KeywordBindRule {
+ /**
+ * This enumeration is the various types the userattr can have after
+ * the "#" token.
+ */
+ enum UserAttrType {
+ USERDN, GROUPDN, ROLEDN, URL, VALUE
+ }
+ private static String f="objectclass=*";
+ private String attrStr=null;
+ private String attrVal=null;
+ private UserAttrType userAttrType=null;
+ private EnumBindRuleType type=null;
+ private ParentInheritance parentInheritance=null;
+
+ /**
+ * Create an non-USERDN/GROUPDN instance of the userattr keyword class.
+ * @param attrStr The attribute name in string form. Kept in string form
+ * until processing.
+ * @param attrVal The attribute value in string form -- used in the USERDN
+ * evaluation for the parent hierarchy expression.
+ * @param userAttrType The userattr type of the rule
+ * "USERDN, GROUPDN, ...".
+ * @param type The bind rule type "=, !=".
+ */
+ private UserAttr(String attrStr, String attrVal, UserAttrType userAttrType,
+ EnumBindRuleType type) {
+ this.attrStr=attrStr;
+ this.attrVal=attrVal;
+ this.userAttrType=userAttrType;
+ this.type=type;
+ }
+
+ /**
+ * Create an USERDN or GROUPDN instance of the userattr keyword class.
+ * @param userAttrType The userattr type of the rule (USERDN or GROUPDN)
+ * only.
+ * @param type The bind rule type "=, !=".
+ * @param parentInheritance The parent inheritance class to use for parent
+ * inheritance checks if any.
+ */
+ private UserAttr(UserAttrType userAttrType, EnumBindRuleType type,
+ ParentInheritance parentInheritance) {
+ this.userAttrType=userAttrType;
+ this.type=type;
+ this.parentInheritance=parentInheritance;
+ }
+ /**
+ * Decode an string containing the userattr bind rule expression.
+ * @param expression The expression string.
+ * @param type The bind rule type.
+ * @return A class suitable for evaluating a userattr bind rule.
+ * @throws AciException If the string contains an invalid expression.
+ */
+ public static KeywordBindRule decode(String expression,
+ EnumBindRuleType type)
+ throws AciException {
+ String[] vals=expression.split("#");
+ if(vals.length != 2) {
+ int msgID = MSGID_ACI_SYNTAX_INVALID_USERATTR_EXPRESSION;
+ String message = getMessage(msgID, expression);
+ throw new AciException(msgID, message);
+ }
+ UserAttrType userAttrType=getType(vals[1]);
+ switch (userAttrType) {
+ case GROUPDN:
+ case USERDN: {
+ ParentInheritance parentInheritance =
+ new ParentInheritance(vals[0], false);
+ return new UserAttr (userAttrType, type, parentInheritance);
+ }
+ case ROLEDN: {
+ //Even though parent inheritance is invalid for the ROLEDN
+ //keyword, we are going to up a simple parent inheritance
+ //class so that most of the evaluate methods in this class
+ //can be re-used. The true boolean means to skip parsing,
+ //except for a quick validation parse.
+ ParentInheritance parentInheritance =
+ new ParentInheritance(vals[0], true);
+ return new UserAttr(userAttrType, type, parentInheritance);
+ }
+ }
+ return new UserAttr(vals[0], vals[1], userAttrType, type);
+ }
+
+ /**
+ * Evaluate the expression using an evaluation context.
+ * @param evalCtx The evaluation context to use in the evaluation of the
+ * userattr expression.
+ * @return An enumeration containing the result of the evaluation.
+ */
+ public EnumEvalResult evaluate(AciEvalContext evalCtx) {
+ EnumEvalResult matched;
+ boolean undefined=false;
+
+ switch(userAttrType) {
+ case ROLEDN:
+ case GROUPDN:
+ case USERDN: {
+ matched=evalDNKeywords(evalCtx);
+ break;
+ }
+ case URL: {
+ matched=evalURL(evalCtx);
+ break;
+ }
+ default:
+ matched=evalVAL(evalCtx);
+ }
+ if(matched == EnumEvalResult.ERR)
+ undefined=true;
+ return matched.getRet(type, undefined);
+ }
+
+ /** Evaluate a VALUE userattr type. Look in client entry for an
+ * attribute value and in the resource entry for the same
+ * value. If both entries have the same value than return true.
+ * @param evalCtx The evaluation context to use.
+ * @return An enumeration containing the result of the
+ * evaluation.
+ */
+ private EnumEvalResult evalVAL(AciEvalContext evalCtx) {
+ EnumEvalResult matched= EnumEvalResult.FALSE;
+ boolean undefined=false;
+ AttributeType attrType;
+ if((attrType = DirectoryServer.getAttributeType(attrStr)) == null)
+ attrType = DirectoryServer.getDefaultAttributeType(attrStr);
+ try {
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ InternalSearchOperation op =
+ conn.processSearch(evalCtx.getClientDN(),
+ SearchScope.BASE_OBJECT,
+ DereferencePolicy.NEVER_DEREF_ALIASES, 0, 0, false,
+ SearchFilter.createFilterFromString(f), null);
+ LinkedList<SearchResultEntry> result = op.getSearchEntries();
+ if (!result.isEmpty()) {
+ AttributeValue val=new AttributeValue(attrType, attrVal);
+ SearchResultEntry resultEntry = result.getFirst();
+ if(resultEntry.hasValue(attrType, null, val)) {
+ Entry e=evalCtx.getResourceEntry();
+ if(e.hasValue(attrType, null, val))
+ matched=EnumEvalResult.TRUE;
+ }
+ }
+ } catch (DirectoryException ex) {
+ undefined = true;
+ matched = EnumEvalResult.ERR;
+ }
+ return matched.getRet(type, undefined);
+ }
+
+ /**
+ * Parses the substring after the '#' character to determine the userattr
+ * type.
+ * @param expr The string with the substring.
+ * @return An enumeration containing the type.
+ * @throws AciException If the substring contains an invalid type (roledn
+ * or groupdn).
+ */
+ private static UserAttrType getType(String expr) throws AciException {
+ UserAttrType userAttrType;
+ if(expr.equalsIgnoreCase("userdn"))
+ userAttrType=UserAttrType.USERDN;
+ else if(expr.equalsIgnoreCase("groupdn")) {
+ userAttrType=UserAttrType.GROUPDN;
+ /*
+ int msgID = MSGID_ACI_SYNTAX_INVALID_USERATTR_KEYWORD;
+ String message = getMessage(msgID, "The groupdn userattr" +
+ "keyword is not supported.");
+ throw new AciException(msgID, message);
+ */
+ } else if(expr.equalsIgnoreCase("roledn")) {
+ userAttrType=UserAttrType.ROLEDN;
+ /*
+ int msgID = MSGID_ACI_SYNTAX_INVALID_USERATTR_KEYWORD;
+ String message = getMessage(msgID, "The roledn userattr" +
+ "keyword is not supported.");
+ throw new AciException(msgID, message);
+ */
+ } else if(expr.equalsIgnoreCase("ldapurl"))
+ userAttrType=UserAttrType.URL;
+ else
+ userAttrType=UserAttrType.VALUE;
+ return userAttrType;
+ }
+
+ /**
+ * Evaluate an URL userattr type. Look into the resource entry for the
+ * specified attribute and values. Assume it is an URL. Decode it an try
+ * and match it against the client entry attribute.
+ * @param evalCtx The evaluation context to evaluate with.
+ * @return An enumeration containing a result of the URL evaluation.
+ */
+ private EnumEvalResult evalURL(AciEvalContext evalCtx) {
+ EnumEvalResult matched= EnumEvalResult.FALSE;
+ boolean undefined=false;
+ AttributeType attrType;
+ if((attrType = DirectoryServer.getAttributeType(attrStr)) == null)
+ attrType = DirectoryServer.getDefaultAttributeType(attrStr);
+ List<Attribute> attrs=evalCtx.getResourceEntry().getAttribute(attrType);
+ if(!attrs.isEmpty()) {
+ for(Attribute a : attrs) {
+ LinkedHashSet<AttributeValue> vals=a.getValues();
+ for(AttributeValue v : vals) {
+ String urlStr=v.getStringValue();
+ LDAPURL url;
+ try {
+ url=LDAPURL.decode(urlStr, true);
+ } catch (DirectoryException e) {
+ break;
+ }
+ matched=UserDN.evalURL(evalCtx, url);
+ if(matched != EnumEvalResult.FALSE)
+ break;
+ }
+ if(matched == EnumEvalResult.TRUE)
+ break;
+ if(matched == EnumEvalResult.ERR) {
+ undefined=true;
+ break;
+ }
+ }
+ }
+ return matched.getRet(type, undefined);
+ }
+
+ /**
+ * Evaluate the DN type userattr keywords. These are roledn, userdn and
+ * groupdn. The processing is the same for all three, although roledn is
+ * a slightly different. For the roledn userattr keyword, a very simple
+ * parent inheritance class was created. The rest of the processing is the
+ * same for all three keywords.
+ *
+ * @param evalCtx The evaluation context to evaluate with.
+ * @return An enumeration containing a result of the USERDN evaluation.
+ */
+ private EnumEvalResult evalDNKeywords(AciEvalContext evalCtx) {
+ EnumEvalResult matched= EnumEvalResult.FALSE;
+ boolean undefined=false, stop=false;
+ int numLevels=parentInheritance.getNumLevels();
+ int[] levels=parentInheritance.getLevels();
+ AttributeType attrType=parentInheritance.getAttributeType();
+ for(int i=0;((i < numLevels) && !stop); i++ ) {
+ //The ROLEDN keyword will always enter this statement. The others
+ //might. For the add operation, the resource itself (level 0)
+ //must never be allowed to give access.
+ if(levels[i] == 0) {
+ if(evalCtx.isAddOperation()) {
+ undefined=true;
+ } else if (evalCtx.getResourceEntry().hasAttribute(attrType)) {
+ matched =
+ evalEntryAttr(evalCtx.getResourceEntry(),
+ evalCtx,attrType);
+ if(matched.equals(EnumEvalResult.TRUE))
+ stop=true;
+ }
+ } else {
+ try {
+ DN pDN=
+ getDNParentLevel(levels[i], evalCtx.getResourceDN());
+ if(pDN == null)
+ continue;
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ InternalSearchOperation op = conn.processSearch(pDN,
+ SearchScope.BASE_OBJECT,
+ DereferencePolicy.NEVER_DEREF_ALIASES, 0, 0, false,
+ SearchFilter.createFilterFromString(f), null);
+ LinkedList<SearchResultEntry> result =
+ op.getSearchEntries();
+ if (!result.isEmpty()) {
+ Entry e = result.getFirst();
+ if (e.hasAttribute(attrType)) {
+ matched = evalEntryAttr(e, evalCtx, attrType);
+ if(matched.equals(EnumEvalResult.TRUE))
+ stop=true;
+ }
+ }
+ } catch (DirectoryException ex) {
+ undefined=true;
+ stop=true;
+ matched=EnumEvalResult.ERR;
+ }
+ }
+ }
+ return matched.getRet(type, undefined);
+ }
+
+ /**
+ * This method returns a parent DN based on the level. Not very
+ * sophisticated but it works.
+ * @param l The level.
+ * @param dn The DN to get the parent of.
+ * @return Parent DN based on the level or null if the level is greater
+ * than the rdn count.
+ */
+ private DN getDNParentLevel(int l, DN dn) {
+ int rdns=dn.getNumComponents();
+ if(l > rdns)
+ return null;
+ DN theDN=dn;
+ for(int i=0; i < l;i++) {
+ theDN=theDN.getParent();
+ }
+ return theDN;
+ }
+
+
+ /**
+ * This method evaluates the user attribute type and calls the correct
+ * evalaution method. The three user attribute types that can be selected
+ * are ROLEDN, USERDN or GROUPDN.
+ * @param e The entry to use in the evaluation.
+ * @param evalCtx The evaluation context to use in the evaluation.
+ * @param attributeType The attribute type to use in the evaluation.
+ * @return The result of the evaluation routine.
+ */
+ private EnumEvalResult evalEntryAttr(Entry e, AciEvalContext evalCtx,
+ AttributeType attributeType) {
+ EnumEvalResult result=EnumEvalResult.FALSE;
+ switch (userAttrType) {
+ case USERDN: {
+ result=UserDN.evaluate(e, evalCtx.getClientDN(),
+ attributeType);
+ break;
+ }
+ case ROLEDN:
+ result=RoleDN.evaluate(e, evalCtx, attributeType);
+ break;
+ case GROUPDN: {
+ result=GroupDN.evaluate(e, evalCtx, attributeType);
+ break;
+ }
+ }
+ return result;
+ }
+
+}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/UserDN.java b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/UserDN.java
new file mode 100644
index 0000000..49da707
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/UserDN.java
@@ -0,0 +1,385 @@
+/*
+ * 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.*;
+import org.opends.server.types.*;
+import org.opends.server.core.DirectoryServer;
+
+/**
+ * This class represents the userdn keyword in a bind rule.
+ */
+public class UserDN implements KeywordBindRule {
+ /*
+ * A dummy URL for invalid URLs such as: all, parent, anyone, self.
+ */
+ private static String urlStr="ldap:///";
+
+ /**
+ * This list holds a list of objects representing a EnumUserDNType
+ * URL mapping.
+ */
+ List<UserDNTypeURL> urlList=null;
+ private EnumBindRuleType type=null;
+ private AttributeType userDNAttrType;
+ /**
+ * Constructor that creates the userdn class. It also sets up an attribute
+ * type ("userdn") needed for wild-card matching.
+ * @param type The type of operation.
+ * @param urlList A list of enumerations containing the URL type and URL
+ * object that can be retrieved at evaluation time.
+ */
+ private UserDN(EnumBindRuleType type, List<UserDNTypeURL> urlList) {
+ this.type=type;
+ this.urlList=urlList;
+ userDNAttrType = DirectoryServer.getAttributeType("userdn");
+ if (userDNAttrType == null)
+ userDNAttrType = DirectoryServer.getDefaultAttributeType("userdn");
+ }
+
+ /**
+ * Decodes an expression string representing a userdn bind rule.
+ * @param expression The string representation of the userdn bind rule
+ * expression.
+ * @param type An enumeration of the type of the bind rule.
+ * @return A KeywordBindRule class that represents the bind rule.
+ * @throws AciException If the expression failed to LDAP URL decode.
+ */
+ public static KeywordBindRule decode(String expression,
+ EnumBindRuleType type) throws AciException {
+
+ String[] vals=expression.split("[|][|]");
+ List<UserDNTypeURL> urlList = new LinkedList<UserDNTypeURL>();
+ for(int i=0, m=vals.length; i < m; i++)
+ {
+ StringBuilder value = new StringBuilder(vals[i].trim());
+ /*
+ * TODO Evaluate using a wild-card in the dn portion of LDAP url.
+ * The current implementation (DS6) does not treat a "*"
+ * as a wild-card.
+ *
+ * Is it allowed to have a full LDAP URL (i.e., including a base,
+ * scope, and filter) in which the base DN contains asterisks to
+ * make it a wildcard? If so, then I don't think that the current
+ * implementation handles that correctly. It will probably fail
+ * when attempting to create the LDAP URL because the base DN isn't a
+ * valid DN.
+ */
+ EnumUserDNType userDNType = UserDN.getType(value);
+ LDAPURL url;
+ try {
+ url=LDAPURL.decode(value.toString(), true);
+ } catch (DirectoryException de) {
+ int msgID = MSGID_ACI_SYNTAX_INVALID_USERDN_URL;
+ String message = getMessage(msgID, de.getErrorMessage());
+ throw new AciException(msgID, message);
+ }
+ UserDNTypeURL dnTypeURL=new UserDNTypeURL(userDNType, url);
+ urlList.add(dnTypeURL);
+ }
+ return new UserDN(type, urlList);
+ }
+
+ /**
+ * This method determines the type of the DN (suffix in URL terms)
+ * part of a URL, by examining the full URL itself for known strings
+ * such as (corresponding type shown in parenthesis)
+ *
+ * "ldap:///anyone" (EnumUserDNType.ANYONE)
+ * "ldap:///parent" (EnumUserDNType.PARENT)
+ * "ldap:///all" (EnumUserDNType.ALL)
+ * "ldap:///self" (EnumUserDNType.SELF)
+ *
+ * If one of the four above are found, the URL is replaced with a dummy
+ * pattern "ldap:///". This is done because the above four are invalid
+ * URLs; but the syntax is valid for an userdn keyword expression. The
+ * dummy URLs are never used.
+ *
+ * If none of the above are found, it determine if the URL DN is a
+ * substring pattern, such as:
+ *
+ * "ldap:///uid=*, dc=example, dc=com" (EnumUserDNType.PATTERN)
+ *
+ * If none of the above are determined, it checks if the URL
+ * is a complete URL with scope and filter defined:
+ *
+ * "ldap:///uid=test,dc=example,dc=com??sub?(cn=j*)" (EnumUserDNType.URL)
+ *
+ * If none of these those types can be identified, it defaults to
+ * EnumUserDNType.DN.
+ *
+ * @param bldr A string representation of the URL that can be modified.
+ * @return The user DN type of the URL.
+ */
+ private static EnumUserDNType getType(StringBuilder bldr) {
+ EnumUserDNType type;
+ String str=bldr.toString();
+
+ if(str.indexOf("?") != -1) {
+ type = EnumUserDNType.URL;
+ } else if(str.equalsIgnoreCase("ldap:///self")) {
+ type = EnumUserDNType.SELF;
+ bldr.replace(0, bldr.length(), urlStr);
+ } else if(str.equalsIgnoreCase("ldap:///anyone")) {
+ type = EnumUserDNType.ANYONE;
+ bldr.replace(0, bldr.length(), urlStr);
+ } else if(str.equalsIgnoreCase("ldap:///parent")) {
+ type = EnumUserDNType.PARENT;
+ bldr.replace(0, bldr.length(), urlStr);
+ } else if(str.equalsIgnoreCase("ldap:///all")) {
+ type = EnumUserDNType.ALL;
+ bldr.replace(0, bldr.length(), urlStr);
+ } else if(str.indexOf("*") != -1) {
+ type = EnumUserDNType.DNPATTERN;
+ } else {
+ type = EnumUserDNType.DN;
+ }
+ return type;
+ }
+
+ /**
+ * Performs the evaluation of a userdn bind rule based on the
+ * evaluation context passed to it. The evaluation stops when there
+ * are no more UserDNTypeURLs to evaluate or if an UserDNTypeURL
+ * evaluates to true.
+ * @param evalCtx The evaluation context to evaluate with.
+ * @return An evaluation result enumeration containing the result
+ * of the evaluation.
+ */
+ public EnumEvalResult evaluate(AciEvalContext evalCtx) {
+ EnumEvalResult matched = EnumEvalResult.FALSE;
+ boolean undefined=false;
+
+ boolean isAnonUser=evalCtx.isAnonymousUser();
+ Iterator<UserDNTypeURL> it=urlList.iterator();
+ for(; it.hasNext() && matched != EnumEvalResult.TRUE &&
+ matched != EnumEvalResult.ERR;) {
+ UserDNTypeURL dnTypeURL=it.next();
+ //Handle anonymous checks here
+ if(isAnonUser) {
+ if(dnTypeURL.getUserDNType() == EnumUserDNType.ANYONE)
+ matched = EnumEvalResult.TRUE;
+ } else
+ matched=evalNonAnonymous(evalCtx, dnTypeURL);
+ }
+ return matched.getRet(type, undefined);
+ }
+
+ /**
+ * Performs an evaluation of a single UserDNTypeURL of a userdn bind
+ * rule using the evaluation context provided. This method is called
+ * for the non-anonymous user case.
+ * @param evalCtx The evaluation context to evaluate with.
+ * @param dnTypeURL The URL dn type mapping to evaluate.
+ * @return An evaluation result enumeration containing the result
+ * of the evaluation.
+ */
+ private EnumEvalResult evalNonAnonymous(AciEvalContext evalCtx,
+ UserDNTypeURL dnTypeURL) {
+ DN clientDN=evalCtx.getClientDN();
+ DN resDN=evalCtx.getResourceDN();
+ EnumEvalResult matched = EnumEvalResult.FALSE;
+ EnumUserDNType type=dnTypeURL.getUserDNType();
+ LDAPURL url=dnTypeURL.getURL();
+ switch (type) {
+ case URL:
+ {
+ matched = evalURL(evalCtx, url);
+ break;
+ }
+ case ANYONE:
+ {
+ matched = EnumEvalResult.TRUE;
+ break;
+ }
+ case SELF:
+ {
+ if (clientDN.equals(resDN)) matched = EnumEvalResult.TRUE;
+ break;
+ }
+ case PARENT:
+ {
+ DN parentDN = resDN.getParent();
+ if ((parentDN != null) &&
+ (parentDN.equals(clientDN)))
+ matched = EnumEvalResult.TRUE;
+ break;
+ }
+ case ALL:
+ {
+ matched = EnumEvalResult.TRUE;
+ break;
+ }
+ case DNPATTERN:
+ {
+ matched = evalDNPattern(evalCtx, url);
+ break;
+ }
+ case DN:
+ {
+ try
+ {
+ DN dn = url.getBaseDN();
+ if (clientDN.equals(dn))
+ matched = EnumEvalResult.TRUE;
+ } catch (DirectoryException ex) {
+ //TODO add message
+ }
+ }
+ }
+ return matched;
+ }
+
+ /*
+ * TODO Evaluate making this more efficient.
+ *
+ * The evalDNPattern() method looks like it suffers from the
+ * same problem as the matchesPattern() method in the Target
+ * class. Creating a dummy entry and attempting to do substring
+ * matching on a DN is a pretty expensive and error-prone approach.
+ * Using a regular expression would likely be much more efficient and
+ * should be simpler.
+ */
+ /**
+ * This method evaluates a DN pattern userdn expression. It creates a
+ * dummy entry and a substring filter and applies the filter to the
+ * entry.
+ * @param evalCtx The evaluation context to use.
+ * @param url The LDAP URL containing the pattern.
+ * @return An enumeration evaluation result.
+ */
+ private EnumEvalResult evalDNPattern(AciEvalContext evalCtx, LDAPURL url) {
+ boolean rc;
+ EnumEvalResult ret=EnumEvalResult.TRUE;
+ String urlDN;
+ SearchFilter filter;
+ try {
+ urlDN=url.getBaseDN().toNormalizedString();
+ String pattern="userdn="+urlDN;
+ filter=SearchFilter.createFilterFromString(pattern);
+ } catch (DirectoryException ex) {
+ return EnumEvalResult.FALSE;
+ }
+ LinkedHashSet<AttributeValue> vals =
+ new LinkedHashSet<AttributeValue>();
+ String userDNStr=evalCtx.getClientDN().toNormalizedString();
+ vals.add(new AttributeValue(userDNAttrType, userDNStr));
+ Attribute attr = new Attribute(userDNAttrType, "userdn", vals);
+ Entry e = new Entry(DN.nullDN(), null, null, null);
+ e.addAttribute(attr,new ArrayList<AttributeValue>());
+ try {
+ rc=filter.matchesEntry(e);
+ } catch (DirectoryException ex) {
+ return EnumEvalResult.FALSE;
+ }
+ if(!rc)
+ ret=EnumEvalResult.FALSE;
+ return ret;
+ }
+
+
+ /**
+ * This method evaluates an URL userdn expression. Something like:
+ * ldap:///suffix??sub?(filter). It also searches for the client DN
+ * entry and saves it in the evaluation context for repeat evaluations
+ * that might come later in processing.
+ *
+ * @param evalCtx The evaluation context to use.
+ * @param url URL containing the URL to use in the evaluation.
+ * @return An enumeration of the evaluation result.
+ */
+ public static EnumEvalResult evalURL(AciEvalContext evalCtx, LDAPURL url) {
+ EnumEvalResult ret=EnumEvalResult.FALSE;
+ DN urlDN;
+ SearchFilter filter;
+ try {
+ urlDN=url.getBaseDN();
+ filter=url.getFilter();
+ } catch (DirectoryException ex) {
+ return EnumEvalResult.FALSE;
+ }
+ SearchScope scope=url.getScope();
+ if(scope == SearchScope.WHOLE_SUBTREE) {
+ if(!evalCtx.getClientDN().isDescendantOf(urlDN))
+ return EnumEvalResult.FALSE;
+ } else if(scope == SearchScope.SINGLE_LEVEL) {
+ DN parent=evalCtx.getClientDN().getParent();
+ if((parent != null) && !parent.equals(urlDN))
+ return EnumEvalResult.FALSE;
+ } else {
+ if(!evalCtx.getClientDN().equals(urlDN))
+ return EnumEvalResult.FALSE;
+ }
+ try {
+ if(filter.matchesEntry(evalCtx.getClientEntry()))
+ ret=EnumEvalResult.TRUE;
+ } catch (DirectoryException ex) {
+ return EnumEvalResult.FALSE;
+ }
+ return ret;
+ }
+
+ /*
+ * TODO Evaluate making this method more efficient.
+ *
+ * The evalDNEntryAttr method isn't as efficient as it could be. It would
+ * probably be faster to to convert the clientDN to an AttributeValue and
+ * see if the entry has that value than to decode each value as a DN and
+ * see if it matches the clientDN.
+ */
+ /**
+ * This method searches an entry for an attribute value that is
+ * treated as a DN. That DN is then compared against the client
+ * DN.
+ * @param e The entry to get the attribute type from.
+ * @param clientDN The client authorization DN to check for.
+ * @param attrType The attribute type from the bind rule.
+ * @return An enumeration with the result.
+ */
+ public static EnumEvalResult evaluate(Entry e, DN clientDN,
+ AttributeType attrType) {
+ EnumEvalResult matched= EnumEvalResult.FALSE;
+ List<Attribute> attrs = e.getAttribute(attrType);
+ LinkedHashSet<AttributeValue> vals = attrs.get(0).getValues();
+ for(AttributeValue v : vals) {
+ try {
+ DN dn=DN.decode(v.getStringValue());
+ if(dn.equals(clientDN)) {
+ matched=EnumEvalResult.TRUE;
+ break;
+ }
+ } catch (DirectoryException ex) {
+ break;
+ }
+ }
+ return matched;
+ }
+}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/UserDNTypeURL.java b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/UserDNTypeURL.java
new file mode 100644
index 0000000..11ea4c7
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/UserDNTypeURL.java
@@ -0,0 +1,71 @@
+/*
+ * 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 org.opends.server.types.LDAPURL;
+
+/**
+ * The UserDNTypeURL class contains the EnumUserDNType and the URL value,
+ * of a "userdn" URL decoded by the UserDN.decode() method.
+ */
+public class UserDNTypeURL {
+ /**
+ * The DN type of the URL.
+ */
+ private EnumUserDNType dnType;
+ /*
+ * The URL value. Maybe a dummy value for types such as ANYONE or SELF.
+ */
+ private LDAPURL url;
+
+ /**
+ * Create a class representing the "userdn" URL decoded by the
+ * UserDN.decode() method.
+ * @param dnType The type of the URL determined by examining the DN
+ * or suffix.
+ * @param url The URL itself from the ACI "userdn" string expression.
+ */
+ UserDNTypeURL(EnumUserDNType dnType, LDAPURL url) {
+ this.url=url;
+ this.dnType=dnType;
+ }
+
+ /**
+ * Returns the DN type.
+ * @return The DN type of the URL.
+ */
+ public EnumUserDNType getUserDNType() {
+ return this.dnType;
+ }
+
+ /** Returns the URL.
+ * @return The URL decoded by the UserDN.decode() method.
+ */
+ public LDAPURL getURL() {
+ return this.url;
+ }
+}
--
Gitblit v1.10.0