From 2d0aba220afc66dcb50fcd2639df306a25f639ea Mon Sep 17 00:00:00 2001
From: dugan <dugan@localhost>
Date: Wed, 02 May 2007 02:02:04 +0000
Subject: [PATCH] Add ACI support for Get Effective Rights control. Issue #87.
---
opends/src/server/org/opends/server/core/DefaultAccessControlProvider.java | 10
opends/src/server/org/opends/server/authorization/dseecompat/TargAttrFilters.java | 10
opends/src/server/org/opends/server/authorization/dseecompat/EnumEvalReason.java | 74 +
opends/src/server/org/opends/server/controls/GetEffectiveRights.java | 245 ++++
opends/src/server/org/opends/server/core/SearchOperation.java | 44
opends/src/server/org/opends/server/authorization/dseecompat/TargetAttr.java | 92 +
opends/src/server/org/opends/server/messages/AciMessages.java | 41
opends/src/server/org/opends/server/messages/CoreMessages.java | 14
opends/src/server/org/opends/server/util/ServerConstants.java | 6
opends/src/server/org/opends/server/authorization/dseecompat/AciTargets.java | 5
opends/src/server/org/opends/server/authorization/dseecompat/AciEvalContext.java | 158 +++
opends/src/server/org/opends/server/authorization/dseecompat/AciBody.java | 10
opends/src/server/org/opends/server/authorization/dseecompat/AciEffectiveRights.java | 735 ++++++++++++++
opends/src/server/org/opends/server/authorization/dseecompat/Aci.java | 32
opends/tests/unit-tests-testng/src/server/org/opends/server/authorization/dseecompat/GetEffectiveRightsTestCase.java | 667 +++++++++++++
opends/src/server/org/opends/server/authorization/dseecompat/AciContainer.java | 538 ++++++++--
opends/src/server/org/opends/server/tools/ToolConstants.java | 23
opends/src/server/org/opends/server/api/AccessControlHandler.java | 25
opends/src/server/org/opends/server/authorization/dseecompat/AciHandler.java | 125 ++
opends/src/server/org/opends/server/tools/LDAPToolUtils.java | 5
opends/src/server/org/opends/server/tools/LDAPSearch.java | 50
opends/src/server/org/opends/server/authorization/dseecompat/AciTargetMatchContext.java | 22
opends/src/server/org/opends/server/messages/ToolMessages.java | 38
opends/src/server/org/opends/server/messages/ProtocolMessages.java | 44
opends/resource/schema/00-core.ldif | 10
25 files changed, 2,825 insertions(+), 198 deletions(-)
diff --git a/opends/resource/schema/00-core.ldif b/opends/resource/schema/00-core.ldif
index cbe3be8..52d3c4d 100644
--- a/opends/resource/schema/00-core.ldif
+++ b/opends/resource/schema/00-core.ldif
@@ -370,6 +370,16 @@
DESC 'Sun-defined access control information attribute type'
SYNTAX 1.3.6.1.4.1.26027.1.3.4 USAGE directoryOperation
X-ORIGIN 'Sun Java System Directory Server' )
+attributeTypes: ( 1.3.6.1.4.1.42.2.27.9.1.39 NAME 'aclRights'
+ DESC 'Sun-defined access control effective rights attribute type'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
+ SINGLE-VALUE NO-USER-MODIFICATION USAGE directoryOperation
+ X-ORIGIN 'Sun Java System Directory Server' )
+attributeTypes: ( 1.3.6.1.4.1.42.2.27.9.1.40 NAME 'aclRightsInfo'
+ DESC 'Sun-defined access control effective rights information attribute type'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
+ SINGLE-VALUE NO-USER-MODIFICATION USAGE directoryOperation
+ X-ORIGIN 'Sun Java System Directory Server' )
attributeTypes: ( 2.16.840.1.113730.3.1.542 NAME 'nsUniqueId'
DESC 'Sun-defined unique identifier' SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
SINGLE-VALUE NO-USER-MODIFICATION USAGE directoryOperation
diff --git a/opends/src/server/org/opends/server/api/AccessControlHandler.java b/opends/src/server/org/opends/server/api/AccessControlHandler.java
index 71579e5..0639d14 100644
--- a/opends/src/server/org/opends/server/api/AccessControlHandler.java
+++ b/opends/src/server/org/opends/server/api/AccessControlHandler.java
@@ -29,11 +29,7 @@
import org.opends.server.core.*;
-import org.opends.server.types.SearchResultEntry;
-import org.opends.server.types.SearchResultReference;
-import org.opends.server.types.Entry;
-import org.opends.server.types.Operation;
-
+import org.opends.server.types.*;
/**
@@ -251,5 +247,24 @@
*/
public abstract boolean isProxiedAuthAllowed(Operation operation,
Entry newAuthorizationEntry);
+
+ /**
+ * Indicates whether a geteffectiverights control is allowed
+ * based on the current operation and the control contents.
+ *
+ * @param operation
+ * The operation with which the geteffectiverights
+ * control is associated. This is always a
+ * SearchOperation.
+ * @param control
+ * The control class containing the decoded
+ * geteffectiverights control contents.
+ * @return <CODE>true</CODE> if the operation should be allowed
+ * by the access control configuration, or
+ * <CODE>false</CODE> if not.
+ */
+ public abstract
+ boolean isGetEffectiveRightsAllowed(Operation operation,
+ Control control);
}
diff --git a/opends/src/server/org/opends/server/authorization/dseecompat/Aci.java b/opends/src/server/org/opends/server/authorization/dseecompat/Aci.java
index 2d6869a..b56d87b 100644
--- a/opends/src/server/org/opends/server/authorization/dseecompat/Aci.java
+++ b/opends/src/server/org/opends/server/authorization/dseecompat/Aci.java
@@ -151,13 +151,13 @@
/**
* ACI_ADD is used to set the container rights for a LDAP add operation.
*/
- public static final int ACI_ADD = 0x0001;
+ public static final int ACI_ADD = 0x0020;
/**
* ACI_DELETE is used to set the container rights for a LDAP
* delete operation.
*/
- public static final int ACI_DELETE = 0x0002;
+ public static final int ACI_DELETE = 0x0010;
/**
* ACI_READ is used to set the container rights for a LDAP
@@ -175,12 +175,12 @@
* ACI_COMPARE is used to set the container rights for a LDAP
* compare operation.
*/
- public static final int ACI_COMPARE = 0x0010;
+ public static final int ACI_COMPARE = 0x0001;
/**
* ACI_SEARCH is used to set the container rights a LDAP search operation.
*/
- public static final int ACI_SEARCH = 0x0020;
+ public static final int ACI_SEARCH = 0x0002;
/**
* ACI_SELF is used for the SELFWRITE right.
@@ -221,6 +221,11 @@
public static final int ACI_WRITE_DELETE = 0x400;
/**
+ * ACI_SKIP_PROXY_CHECK is used to bypass the proxy access check.
+ */
+ public static final int ACI_SKIP_PROXY_CHECK = 0x4000;
+
+ /**
* TARGATTRFILTER_ADD is used to specify that a
* targattrfilters ADD operation was seen in the ACI. For example,
* given an ACI with:
@@ -271,8 +276,6 @@
* @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();
@@ -338,6 +341,15 @@
*/
public static boolean
isApplicable(Aci aci, AciTargetMatchContext matchCtx) {
+ int ctxRights=matchCtx.getRights();
+ //First check if the ACI and context have similar rights.
+ if(!aci.hasRights(ctxRights)) {
+ //TODO This check might be able to be removed further testing
+ // is needed.
+ if(!(aci.hasRights(ACI_SEARCH| ACI_READ) &&
+ matchCtx.hasRights(ACI_SEARCH | ACI_READ)))
+ return false;
+ }
return AciTargets.isTargetApplicable(aci, matchCtx) &&
AciTargets.isTargetFilterApplicable(aci, matchCtx) &&
AciTargets.isTargAttrFiltersApplicable(aci, matchCtx) &&
@@ -384,4 +396,12 @@
public static EnumEvalResult evaluate(AciEvalContext evalCtx, Aci aci) {
return aci.evaluate(evalCtx);
}
+
+ /**
+ * Returns the name string of this ACI.
+ * @return The name string.
+ */
+ public String getName() {
+ return this.body.getName();
+ }
}
diff --git a/opends/src/server/org/opends/server/authorization/dseecompat/AciBody.java b/opends/src/server/org/opends/server/authorization/dseecompat/AciBody.java
index db2b2b1..2300504 100644
--- a/opends/src/server/org/opends/server/authorization/dseecompat/AciBody.java
+++ b/opends/src/server/org/opends/server/authorization/dseecompat/AciBody.java
@@ -47,7 +47,7 @@
private static final int VERSION = 1;
/*
- * Regular expression group position for the namr string.
+ * Regular expression group position for the name string.
*/
private static final int NAME = 2;
@@ -310,4 +310,12 @@
}
return res;
}
+
+ /**
+ * Returns the name string.
+ * @return The name string.
+ */
+ public String getName() {
+ return this.name;
+ }
}
diff --git a/opends/src/server/org/opends/server/authorization/dseecompat/AciContainer.java b/opends/src/server/org/opends/server/authorization/dseecompat/AciContainer.java
index 2602242..495212b 100644
--- a/opends/src/server/org/opends/server/authorization/dseecompat/AciContainer.java
+++ b/opends/src/server/org/opends/server/authorization/dseecompat/AciContainer.java
@@ -32,11 +32,18 @@
import org.opends.server.api.Group;
import org.opends.server.api.ConnectionSecurityProvider;
import org.opends.server.core.AddOperation;
+import org.opends.server.core.SearchOperation;
import org.opends.server.extensions.TLSConnectionSecurityProvider;
import org.opends.server.types.Operation;
import java.net.InetAddress;
import java.util.LinkedList;
+import java.util.List;
+import java.util.HashMap;
+
+import static org.opends.server.authorization.dseecompat.Aci.*;
import static org.opends.server.authorization.dseecompat.AciHandler.*;
+import org.opends.server.controls.GetEffectiveRights;
+import static org.opends.server.util.ServerConstants.OID_GET_EFFECTIVE_RIGHTS;
/**
* The AciContainer class contains all of the needed information to perform
@@ -94,6 +101,7 @@
* The entry being evaluated (resource entry).
*/
private Entry resourceEntry;
+ private Entry saveResourceEntry;
/*
* The client connection information.
@@ -145,12 +153,86 @@
*/
private boolean seenEntry=false;
- /**
+ /*
+ * True if geteffectiverights evaluation is in progress.
+ */
+ private boolean isGetEffectiveRightsEval=false;
+
+ /*
+ * True if the operation has a geteffectiverights control.
+ */
+ private boolean hasGetEffectiveRightsControl=false;
+
+ /*
+ * The geteffectiverights authzID in DN format.
+ */
+ private DN authzid=null;
+
+ /*
+ * True if the authZid should be used as the client DN, only used in
+ * geteffectiverights evaluation.
+ */
+ private boolean useAuthzid=false;
+
+ /*
+ * The list of specific attributes to get rights for, in addition to
+ * any attributes requested in the search.
+ */
+ private List<AttributeType> specificAttrs=null;
+
+ /*
+ * The entry with all of its attributes available. Used in
+ * geteffectiverights read entry level evaluation.
+ */
+ private Entry fullEntry=null;
+
+ /*
+ * Table of ACIs that have targattrfilter keywords that matched. Used
+ * in geteffectiverights attributeLevel write evaluation.
+ */
+ private HashMap<Aci,Aci> targAttrFilterAcis=new HashMap<Aci, Aci>();
+
+ /*
+ * The name of a ACI that decided an evaluation and contained a
+ * targattrfilter keyword. Used in geteffectiverights attributeLevel
+ * write evaluation.
+ */
+ private String targAttrFiltersAciName=null;
+
+ /*
+ * Value that is used to store the allow/deny result of a deciding ACI
+ * containing a targattrfilter keyword. Used in geteffectiverights
+ * attributeLevel write evaluation.
+ */
+ private int targAttrMatch=0;
+
+ /*
+ * The ACI that decided the last evaluation. Used in geteffectiverights
+ * loginfo processing.
+ */
+ private Aci decidingAci=null;
+
+ /*
+ * The reason the last evaluation decision was made. Used both
+ * in geteffectiverights loginfo processing and attributeLevel write
+ * evaluation.
+ */
+ private EnumEvalReason evalReason=null;
+
+ /*
+ * A summary string holding the last evaluation information in textual
+ * format. Used in geteffectiverights loginfo processing.
+ */
+ private String summaryString=null;
+
+ /**
* 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) {
@@ -167,16 +249,34 @@
if(origAuthorizationEntry != null)
this.proxiedAuthorization=true;
this.authorizationEntry=operation.getAuthorizationEntry();
-
+ //Only need to process the geteffectiverights control once, -- for a
+ //SearchOperation with read right. It is saved in the operation
+ //attachment after that.
+ if(operation instanceof SearchOperation && (rights == ACI_READ)) {
+ GetEffectiveRights getEffectiveRightsControl =
+ (GetEffectiveRights)
+ operation.getAttachment(OID_GET_EFFECTIVE_RIGHTS);
+ if(getEffectiveRightsControl != null) {
+ hasGetEffectiveRightsControl=true;
+ if(getEffectiveRightsControl.getAuthzDN() == null)
+ this.authzid=getClientDN();
+ else
+ this.authzid=getEffectiveRightsControl.getAuthzDN();
+ this.specificAttrs=getEffectiveRightsControl.getAttributes();
+ fullEntry=(Entry)operation.getAttachment(ALL_ATTRS_RESOURCE_ENTRY);
+ }
+ }
//Reference the current authorization entry, so it can be put back
//if an access proxy check was performed.
this.saveAuthorizationEntry=this.authorizationEntry;
+ this.saveResourceEntry=this.resourceEntry;
this.rights = rights;
}
/**
* Returns true if an entry has already been processed by an access proxy
* check.
+ *
* @return True if an entry has already been processed by an access proxy
* check.
*/
@@ -187,6 +287,7 @@
/**
* Set to true if an entry has already been processsed by an access proxy
* check.
+ *
* @param val The value to set the seenEntry boolean to.
*/
public void setSeenEntry(boolean val) {
@@ -194,14 +295,200 @@
}
/**
- * Returns true if proxied authorization is being used.
- * @return True if proxied authorization is being used.
+ * {@inheritDoc}
*/
public boolean isProxiedAuthorization() {
return this.proxiedAuthorization;
}
/**
+ * {@inheritDoc}
+ */
+ public boolean isGetEffectiveRightsEval() {
+ return this.isGetEffectiveRightsEval;
+ }
+
+ /**
+ * The container is going to be used in a geteffectiverights evaluation, set
+ * the flag isGetEffectiveRightsEval to true.
+ */
+ public void setGetEffectiveRightsEval() {
+ this.isGetEffectiveRightsEval=true;
+ }
+
+ /**
+ * Return true if the container is being used in a geteffectiverights
+ * evaluation.
+ *
+ * @return True if the container is being used in a geteffectiverights
+ * evaluation.
+ */
+ public boolean hasGetEffectiveRightsControl() {
+ return this.hasGetEffectiveRightsControl;
+ }
+
+ /**
+ * Use the DN from the geteffectiverights control's authzId as the
+ * client DN, rather than the authorization entry's DN.
+ *
+ * @param v The valued to set the useAuthzid to.
+ */
+ public void useAuthzid(boolean v) {
+ this.useAuthzid=v;
+ }
+
+ /**
+ * Return the list of additional attributes specified in the
+ * geteffectiveritghts control.
+ *
+ * @return The list of attributes to return rights information about in the
+ * entry.
+ */
+ public List<AttributeType> getSpecificAttributes() {
+ return this.specificAttrs;
+ }
+
+ /**
+ * During the geteffectiverights entrylevel read evaluation, an entry with all
+ * of the attributes used in the AciHandler's maysend method evaluation is
+ * needed to perform the evaluation over again. This entry was saved
+ * in the operation's attachment mechanism when the container was created
+ * during the SearchOperation read evaluation.
+ *
+ * This method is used to replace the current resource entry with that saved
+ * entry to perform the entrylevel read evaluation described above and to
+ * switch back to the current resource entry when needed.
+ *
+ * @param val Specifies if the saved entry should be used or not. True if it
+ * should be used, false if the original resource entry should be used.
+ *
+ */
+ public void useFullResourceEntry(boolean val) {
+ if(val)
+ resourceEntry=fullEntry;
+ else
+ resourceEntry=saveResourceEntry;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void addTargAttrFiltersMatchAci(Aci aci) {
+ this.targAttrFilterAcis.put(aci, aci);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean hasTargAttrFiltersMatchAci(Aci aci) {
+ return this.targAttrFilterAcis.containsKey(aci);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isTargAttrFilterMatchAciEmpty() {
+ return this.targAttrFilterAcis.isEmpty();
+ }
+
+ /**
+ * Reset the values used by the geteffectiverights evaluation to
+ * original values. The geteffectiverights evaluation uses the same container
+ * repeatedly for different rights evaluations (read, write, proxy,...) and
+ * this method resets variables that are specific to a single evaluation.
+ */
+ public void resetEffectiveRightsParams() {
+ this.targAttrFilterAcis.clear();
+ this.decidingAci=null;
+ this.evalReason=null;
+ this.targAttrFiltersMatch=false;
+ this.summaryString=null;
+ this.targAttrMatch=0;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void setTargAttrFiltersAciName(String name) {
+ this.targAttrFiltersAciName=name;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getTargAttrFiltersAciName() {
+ return this.targAttrFiltersAciName;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void setTargAttrFiltersMatchOp(int flag) {
+ this.targAttrMatch |= flag;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean hasTargAttrFiltersMatchOp(int flag) {
+ return (this.targAttrMatch & flag) != 0;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void setDecidingAci(Aci aci) {
+ this.decidingAci=aci;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getDecidingAciName() {
+ if(this.decidingAci != null)
+ return this.decidingAci.getName();
+ else return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void setEvalReason(EnumEvalReason reason) {
+ this.evalReason=reason;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public EnumEvalReason getEvalReason() {
+ return this.evalReason;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void setEvalSummary(String summary) {
+ this.summaryString=summary;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getEvalSummary() {
+ return this.summaryString;
+ }
+
+ /**
+ * Returns true if the geteffectiverights control's authZid DN is equal to the
+ * authoritzation entry's DN.
+ *
+ * @return True if the authZid is equal to the authorization entry's DN.
+ */
+ public boolean isAuthzidAuthorizationDN() {
+ return this.authzid.equals(this.authorizationEntry.getDN());
+ }
+
+ /**
* If the specified value is true, then the original authorization entry,
* which is the entry before the switch performed by the proxied
* authorization control processing should be set to the current
@@ -216,228 +503,194 @@
authorizationEntry=saveAuthorizationEntry;
}
- /**
- * 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.
- */
+ /**
+ * {@inheritDoc}
+ */
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.
- */
+ /**
+ * {@inheritDoc}
+ */
public void setAllowList(LinkedList<Aci> allows) {
allowList=allows;
}
- /**
- * Return the current attribute type being evaluated.
- * @return Attribute type being evaluated.
- */
+ /**
+ * {@inheritDoc}
+ */
public AttributeType getCurrentAttributeType() {
return attributeType;
}
- /**
- * Return the current attribute type value being evaluated.
- * @return Attribute type value being evaluated.
- */
+ /**
+ * {@inheritDoc}
+ */
public AttributeValue getCurrentAttributeValue() {
return attributeValue;
}
- /**
- * Set the attribute type to be evaluated.
- * @param type The attribute type to evaluate.
- */
+ /**
+ * {@inheritDoc}
+ */
public void setCurrentAttributeType(AttributeType type) {
attributeType=type;
}
- /**
- * Set the attribute type value to be evaluated.
- * @param value The attribute type value to evaluate.
- */
+ /**
+ * {@inheritDoc}
+ */
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.
- */
+ /**
+ * {@inheritDoc}
+ */
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.
- */
+ /**
+ * {@inheritDoc}
+ */
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.
- */
+ /**
+ * {@inheritDoc}
+ */
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) {
+ /**
+ * {@inheritDoc}
+ */
+ public void setEntryTestRule(boolean val) {
isEntryTestRule=val;
}
- /**
- * Get the entry being evaluated (known as the resource entry).
- * @return The entry being evaluated.
- */
+ /**
+ * {@inheritDoc}
+ */
public Entry getResourceEntry() {
return resourceEntry;
}
- /**
- * Get the entry that corresponds to the client DN.
- * @return The client entry.
- */
+ /**
+ * {@inheritDoc}
+ */
public Entry getClientEntry() {
return this.authorizationEntry;
}
- /**
- * Get the deny list of ACIs.
- * @return The deny ACI list.
- */
+ /**
+ * {@inheritDoc}
+ */
public LinkedList<Aci> getDenyList() {
return denyList;
}
- /**
- * Get the allow list of ACIs.
- * @return The allow ACI list.
- */
+ /**
+ * {@inheritDoc}
+ */
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.
- */
+ /**
+ * {@inheritDoc}
+ */
public boolean isDenyEval() {
return isDenyEval;
}
- /**
- * Check is this operation bound anonymously.
- * @return True if the authentication is anonymous.
- */
+ /**
+ * {@inheritDoc}
+ */
public boolean isAnonymousUser() {
return !clientConnection.getAuthenticationInfo().isAuthenticated();
}
- /**
- * Set the deny evaluation flag.
- * @param val True if this evaluation is a deny ACI.
- */
+ /**
+ * {@inheritDoc}
+ */
public void setDenyEval(boolean val) {
isDenyEval = val;
}
- /**
- * Returns the client authorization DN known as the client DN.
- * @return The client's authorization DN.
- */
+ /**
+ * {@inheritDoc}
+ */
public DN getClientDN() {
- return this.authorizationEntry.getDN();
+ if(this.useAuthzid)
+ return this.authzid;
+ else
+ return this.authorizationEntry.getDN();
}
- /**
- * Get the DN of the entry being evaluated.
- * @return The DN of the entry.
- */
+ /**
+ * {@inheritDoc}
+ */
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.
- */
+ /**
+ * {@inheritDoc}
+ */
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.
- */
+ /**
+ * {@inheritDoc}
+ */
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.
- */
+ /**
+ * {@inheritDoc}
+ */
public void setRights(int rights) {
this.rights=rights;
}
- /**
- * Gets the hostname of the remote client.
- * @return Cannonical hostname of remote client.
- */
+ /**
+ * {@inheritDoc}
+ */
public String getHostName() {
return clientConnection.getRemoteAddress().getCanonicalHostName();
}
- /**
- * Gets the remote client's address information.
- * @return Remote client's address.
- */
+ /**
+ * {@inheritDoc}
+ */
public InetAddress getRemoteAddress() {
return clientConnection.getRemoteAddress();
}
- /**
- * Return true if the current operation is a LDAP add operation.
- * @return True if this is an add operation.
- */
+ /**
+ * {@inheritDoc}
+ */
public boolean isAddOperation() {
return isAddOp;
}
- /**
- * Set to true if the ACI had a targattrfilter rule that matched.
- * @param v The value to use.
- */
+ /**
+ * {@inheritDoc}
+ */
public void setTargAttrFiltersMatch(boolean v) {
this.targAttrFiltersMatch=v;
}
- /**
- * Return the value of the targAttrFiltersMatch variable. This is set to
- * true if the ACI had a targattrfilter rule that matched.
- * @return True if the ACI had a targattrfilter rule that matched.
- */
+ /**
+ * {@inheritDoc}
+ */
public boolean getTargAttrFiltersMatch() {
return targAttrFiltersMatch;
}
@@ -498,12 +751,9 @@
return matched;
}
- /**
- * Convenience 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.
- */
+ /**
+ * {@inheritDoc}
+ */
public boolean isMemberOf(Group group) {
boolean ret;
try {
@@ -513,4 +763,32 @@
}
return ret;
}
+
+ /**
+ * {@inheritDoc}
+ */
+ public String rightToString() {
+ if(hasRights(ACI_SEARCH))
+ return "search";
+ else if(hasRights(ACI_COMPARE))
+ return "compare";
+ else if(hasRights(ACI_READ))
+ return "read";
+ else if(hasRights(ACI_DELETE))
+ return "delete";
+ else if(hasRights(ACI_ADD))
+ return "add";
+ else if(hasRights(ACI_WRITE))
+ return "write";
+ else if(hasRights(ACI_PROXY))
+ return "proxy";
+ else if(hasRights(ACI_IMPORT))
+ return "import";
+ else if(hasRights(ACI_EXPORT))
+ return "export";
+ else if(hasRights(ACI_WRITE) &&
+ hasRights(ACI_SELF))
+ return "selfwrite";
+ return null;
+ }
}
diff --git a/opends/src/server/org/opends/server/authorization/dseecompat/AciEffectiveRights.java b/opends/src/server/org/opends/server/authorization/dseecompat/AciEffectiveRights.java
new file mode 100644
index 0000000..2ef9ad7
--- /dev/null
+++ b/opends/src/server/org/opends/server/authorization/dseecompat/AciEffectiveRights.java
@@ -0,0 +1,735 @@
+/*
+ * 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.core.DirectoryServer;
+import static org.opends.server.authorization.dseecompat.Aci.*;
+import org.opends.server.protocols.asn1.ASN1OctetString;
+
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.LinkedList;
+
+/**
+ * This class implements the dseecompat geteffectiverights evaluation.
+ */
+public class AciEffectiveRights {
+
+ //Value used when a aclRights attribute was seen in the search operation
+ //attribute set.
+ private static final int ACL_RIGHTS = 0x001;
+
+ //Value used when a aclRightsInfo attribute was seen in the search operation
+ //attribute set.
+ private static final int ACL_RIGHTS_INFO = 0x002;
+
+ //Value used when an ACI has a targattrfilters keyword match and the result
+ //of the access check was a deny.
+ private static final int ACL_TARGATTR_DENY_MATCH = 0x004;
+
+ //Value used when an ACI has a targattrfilters keyword match and the result
+ //of the access check was an allow.
+ private static final int ACL_TARGATTR_ALLOW_MATCH = 0x008;
+
+ //String used to build attribute type name when an aclRights result needs to
+ //be added to the return entry.
+ private static final String aclRightsAttrStr = "aclRights";
+
+ //String used to build attribute type name when an AclRightsInfo result needs
+ //to be added to the return entry.
+ private static final String aclRightsInfoAttrStr = "aclRightsInfo";
+
+ //String used to build attribute type name when an entryLevel rights
+ //attribute type name needs to be added to the return entry.
+ private static final String entryLevelStr = "entryLevel";
+
+ //String used to build attribute type name when an attributeLevel rights
+ //attribute type name needs to be added to the return entry.
+ private static final String attributeLevelStr = "attributeLevel";
+
+ //The string that is used as the attribute type name when an
+ //aclRights entryLevel evaluation needs to be added to the return entry.
+ private static final String aclRightsEntryLevelStr=
+ aclRightsAttrStr + ";" + entryLevelStr;
+
+ //The string that is used as the attribute type name when an
+ //aclRights attribute level evaluation needs to be added to the return entry.
+ //This string has the attribute type name used in the evaluation appended to
+ //it to form the final attribute type name.
+ private static final String aclRightsAttributeLevelStr=
+ aclRightsAttrStr + ";" + attributeLevelStr;
+
+ //The string used to build attribute type name when an attribute level
+ //aclRightsInfo attribute needs to be added to the return entry. This string
+ //has the attribute type name used in the evaluation appended to it to form
+ //the final attribute type name.
+ private static final String aclRightsInfoAttrLogsStr =
+ aclRightsInfoAttrStr + ";logs;attributeLevel";
+
+ //The string used to build attribute type name when an entryLevel
+ //aclRightsInfo attribute needs to be added to the return entry.
+ private static final String aclRightsInfoEntryLogsStr =
+ aclRightsInfoAttrStr + ";logs;entryLevel";
+
+ //Attribute type used in access evaluation to see if the geteffectiverights
+ //related to the "aclRights" attribute can be performed.
+ private static AttributeType aclRights = null;
+
+ //Attribute type used in access evaluation to see if the geteffectiverights
+ //related to the "aclRightsInfo" attribute can be performed.
+ private static AttributeType aclRightsInfo = null;
+
+ //String used to fill in the summary status field when access was allowed.
+ private static String ALLOWED="access allowed";
+
+ //String used to fill in the summary status field when access was not allowed.
+ private static String NOT_ALLOWED="access not allowed";
+
+ //Evaluated as anonymous user. Used to fill in summary field.
+ private static String anonymous="anonymous";
+
+ //Format used to build the summary string.
+ private static String summaryFormatStr =
+ "acl_summary(%s): %s(%s) on entry/attr(%s, %s) to (%s)" +
+ " (not proxied) ( reason: %s %s)";
+
+ //Strings below represent access denied or allowed evaluation reasons.
+ //Used to fill in the summary status field.
+ //Access evaluated an allow ACI.
+ private static String EVALUATED_ALLOW="evaluated allow";
+
+ //Access evaluated a deny ACI.
+ private static String EVALUATED_DENY="evaluated deny";
+
+ //Access evaluated deny because there were no allow ACIs.
+ private static String NO_ALLOWS="no acis matched the resource";
+
+ //Access evaluated deny because no allow or deny ACIs evaluated.
+ private static String NO_ALLOWS_MATCHED="no acis matched the subject";
+
+ //Access evaluated allow because the clientDN has bypass-acl privileges.
+ private static String SKIP_ACI="user has bypass-acl privileges";
+
+ //TODO add support for the modify-acl privilige?
+
+ /**
+ * Attempts to add the geteffectiverights asked for in the search to the entry
+ * being returned. The two geteffectiverights attributes that can be requested
+ * are: aclRights and aclRightsInfo. The aclRightsInfo attribute will return
+ * a summary string describing in human readable form, a summary of each
+ * requested evaluation result. Here is a sample aclRightsInfo summary:
+ *
+ * acl_summary(main): access_not_allowed(proxy) on
+ * entry/attr(uid=proxieduser,ou=acis,dc=example,dc=com, NULL) to
+ * (uid=superuser,ou=acis,dc=example,dc=com) (not proxied)
+ * (reason: no acis matched the resource )
+ *
+ * The aclRights attribute will return a simple
+ * string with the following format:
+ *
+ * add:0,delete:0,read:1,write:?,proxy:0
+ *
+ * A 0 represents access denied, 1 access allowed and ? that evaluation
+ * depends on a value of an attribute (targattrfilter keyword present in ACI).
+ *
+ * There are two levels of rights information:
+ *
+ * 1. entryLevel - entry level rights information
+ * 2. attributeLevel - attribute level rights information
+ *
+ * The attribute type names are built up using subtypes:
+ *
+ * aclRights;entryLevel - aclRights entry level presentation
+ * aclRightsInfo;log;entryLevel;{right} - aclRightsInfo entry level
+ * presentation for each type of right (proxy, read, write, add,
+ * delete).
+ * aclRights;attributeLevel;{attributeType name} - aclRights attribute
+ * level presentation for each attribute type requested.
+ * aclRights;attributeLevel;logs;{right};{attributeType name}
+ * - aclRightsInfo attribute level presentation for each attribute
+ * type requested.
+ *
+ * @param handler The ACI handler to use in the evaluation.
+ * @param searchAttributes The attributes requested in the search.
+ * @param container The LDAP operation container to use in the evaluations.
+ * @param e The entry to add the rights attributes to.
+ * @param skipCheck True if ACI evaluation was skipped because bypass-acl
+ * privilege was found.
+ * @return A SearchResultEntry with geteffectiverights information possibly
+ * added to it.
+ */
+ public static SearchResultEntry
+ addRightsToEntry(AciHandler handler, LinkedHashSet<String> searchAttributes,
+ AciLDAPOperationContainer container,SearchResultEntry e,
+ boolean skipCheck) {
+ List<AttributeType> nonRightsAttrs = new LinkedList<AttributeType>();
+ int attrMask=ACI_NULL;
+ if(aclRights == null)
+ aclRights =
+ DirectoryServer.getAttributeType(aclRightsAttrStr.toLowerCase());
+ if(aclRightsInfo == null)
+ aclRightsInfo =
+ DirectoryServer.getAttributeType(aclRightsInfoAttrStr.toLowerCase());
+ //Check if the attributes aclRights and aclRightsInfo were requested and
+ //add attributes less those two attributes to a new list of attribute types.
+ for(String a : searchAttributes) {
+ if(a.equalsIgnoreCase(aclRightsAttrStr))
+ attrMask |= ACL_RIGHTS;
+ else if(a.equalsIgnoreCase(aclRightsInfoAttrStr))
+ attrMask |= ACL_RIGHTS_INFO;
+ else {
+ AttributeType attrType;
+ if((attrType = DirectoryServer.getAttributeType(a)) == null)
+ attrType = DirectoryServer.getDefaultAttributeType(a);
+ nonRightsAttrs.add(attrType);
+ }
+ }
+ //If the special geteffectiverights attributes were not found or
+ //the user does not have both bypass-acl privs and is not allowed to
+ //perform rights evalation -- return the entry unchanged.
+ if(attrMask == ACI_NULL ||
+ (!skipCheck && !rightsAccessAllowed(container,handler,attrMask)))
+ return e;
+ //From here on out, geteffectiverights evaluation is being performed and the
+ //container will be manipulated. First set the flag that geteffectiverights
+ //evaluation's underway and to use the authZid for authorizationDN (they
+ //might be the same).
+ container.setGetEffectiveRightsEval();
+ container.useAuthzid(true);
+ //If no attributes were requested return only entryLevel rights, else
+ //return attributeLevel rights and entryLevel rights. Always try and
+ //return the specific attribute rights if they exist.
+ if(nonRightsAttrs.isEmpty()) {
+ e=addAttributeLevelRights(container,handler,attrMask,e,
+ container.getSpecificAttributes(), skipCheck);
+ e=addEntryLevelRights(container,handler,attrMask,e, skipCheck);
+ } else {
+ e=addAttributeLevelRights(container,handler,attrMask,e,
+ nonRightsAttrs,skipCheck);
+ e=addAttributeLevelRights(container,handler,attrMask,e,
+ container.getSpecificAttributes(), skipCheck);
+ e=addEntryLevelRights(container,handler,attrMask,e,skipCheck);
+ }
+ return e;
+ }
+
+
+ /**
+ * Perform the attributeLevel rights evaluation on a list of specified
+ * attribute types. Each attribute has an access check done for the following
+ * rights: search, read, compare, add, delete, proxy, selfwrite_add,
+ * selfwrite_delete and write.
+ *
+ * The special rights, selfwrite_add and selfwrite_delete, use the authZid as
+ * the attribute value to evaluate against the attribute type being
+ * evaluated. The selfwrite_add performs the access check using the
+ * ACI_WRITE_ADD right and selfwrite_delete uses ACI_WRITE_ADD right.
+ *
+ * The write right is made complicated by the targattrfilters keyword, which
+ * might depend on an unknown value of an attribute type. For this case a
+ * dummy attribute value is used to try and determine if a "?" needs to be
+ * placed in the rights string.
+ *
+ * The special flag ACI_SKIP_PROXY_CHECK is always set, so that proxy
+ * evaluation is bypassed in the Aci Handler's accessAllowed method.
+ *
+ * @param container The LDAP operation container to use in the evaluations.
+ * @param handler The Aci Handler to use in the access evaluations.
+ * @param mask Mask specifing what rights attribute processing to perform
+ * (aclRights or aclRightsInfo or both).
+ * @param retEntry The entry to return.
+ * @param attrList The list of attribute types to iterate over.
+ * @param skipCheck True if ACI evaluation was skipped because bypass-acl
+ * privilege was found.
+ * @return A SearchResultEntry with geteffectiverights attribute level
+ * information added to it.
+ */
+ private static
+ SearchResultEntry addAttributeLevelRights(AciLDAPOperationContainer container,
+ AciHandler handler, int mask,
+ SearchResultEntry retEntry,
+ List<AttributeType> attrList,
+ boolean skipCheck) {
+
+ //The attribute list might be null.
+ if(attrList == null)
+ return retEntry;
+ for(AttributeType a : attrList) {
+ StringBuilder evalInfo=new StringBuilder();
+ container.setCurrentAttributeType(a);
+ container.setCurrentAttributeValue(null);
+ //Perform search check and append results.
+ container.setRights(ACI_SEARCH | ACI_SKIP_PROXY_CHECK);
+ evalInfo.append(rightsString(container, handler, skipCheck, "search"));
+ addAttrLevelRightsInfo(container, mask, a, retEntry, "search");
+ evalInfo.append(',');
+ //Perform read check and append results.
+ container.setRights(ACI_READ | ACI_SKIP_PROXY_CHECK);
+ evalInfo.append(rightsString(container, handler, skipCheck, "read"));
+ addAttrLevelRightsInfo(container, mask, a, retEntry, "read");
+ evalInfo.append(',');
+ //Perform compare and append results.
+ container.setRights(ACI_COMPARE | ACI_SKIP_PROXY_CHECK);
+ evalInfo.append(rightsString(container, handler, skipCheck, "compare"));
+ addAttrLevelRightsInfo(container, mask, a, retEntry, "compare");
+ evalInfo.append(',');
+ //Write right is more complicated. Create a dummy value and set that as
+ //the attribute's value. Call the special writeRightsString method, rather
+ //than rightsString.
+ AttributeValue val=new AttributeValue(a, "dum###Val");
+ container.setCurrentAttributeValue(val);
+ evalInfo.append(attributeLevelWriteRights(container, handler, skipCheck));
+ addAttrLevelRightsInfo(container, mask, a, retEntry, "write");
+ evalInfo.append(',');
+ //Perform both selfwrite_add and selfwrite_delete and append results.
+ ByteString clientDNStr=
+ new ASN1OctetString(container.getClientDN().toString());
+ AttributeValue val1=new AttributeValue(a, clientDNStr);
+ container.setCurrentAttributeValue(val1);
+ container.setRights(ACI_WRITE_ADD | ACI_SKIP_PROXY_CHECK);
+ evalInfo.append(rightsString(container, handler, skipCheck,
+ "selfwrite_add"));
+ addAttrLevelRightsInfo(container, mask, a, retEntry, "selfwrite_add");
+ evalInfo.append(',');
+ container.setRights(ACI_WRITE_DELETE | ACI_SKIP_PROXY_CHECK);
+ evalInfo.append(rightsString(container, handler, skipCheck,
+ "selfwrite_delete"));
+ addAttrLevelRightsInfo(container, mask, a, retEntry, "selfwrite_delete");
+ evalInfo.append(',');
+ container.setCurrentAttributeValue(null);
+ container.setRights(ACI_PROXY | ACI_SKIP_PROXY_CHECK);
+ evalInfo.append(rightsString(container, handler, skipCheck, "proxy"));
+ addAttrLevelRightsInfo(container, mask, a, retEntry, "proxy");
+ //It is possible that only the aclRightsInfo attribute type was requested.
+ //Only add the aclRights information if the aclRights attribute type was
+ //seen.
+ if(hasAttrMask(mask, ACL_RIGHTS)) {
+ String typeStr=aclRightsAttributeLevelStr + ";" +
+ a.getNormalizedPrimaryName();
+ AttributeType attributeType=
+ DirectoryServer.getDefaultAttributeType(typeStr);
+ LinkedHashSet<AttributeValue> vals =
+ new LinkedHashSet<AttributeValue>();
+ vals.add(new AttributeValue(attributeType, evalInfo.toString()));
+ Attribute attr =
+ new Attribute(attributeType, typeStr, vals);
+ //It is possible that the user might have specified the same attributes
+ //in both the search and the specific attribute part of the control.
+ //Only try to add the attribute type if it already hasn't been added.
+ if(!retEntry.hasAttribute(attributeType))
+ retEntry.addAttribute(attr,null);
+ }
+ }
+ container.setCurrentAttributeValue(null);
+ container.setCurrentAttributeType(null);
+ return retEntry;
+ }
+
+ /**
+ * Perform the attributeLevel write rights evaluation. The issue here is that
+ * an ACI could contain a targattrfilters keyword that matches the attribute
+ * being evaluated.
+ *
+ * There is no way of knowing if the filter part of the targattrfilter would
+ * be successful or not. So if the ACI that allowed access, has an
+ * targattrfilter keyword, a "?" is used as the result of the write (depends
+ * on attribute value).
+ *
+ * If the allow ACI doesn't contain a targattrfilters keyword than a
+ * "1" is added. If the ACI denies then a "0" is added. If the skipCheck flag
+ * is true, then a 1 is used for the write access, since the client DN has
+ * bypass privs.
+ *
+ * @param container The LDAP operation container to use in the evaluations.
+ * @param handler The Aci Handler to use in the access evaluations.
+ * @param skipCheck True if ACI evaluation was skipped because bypass-acl
+ * privilege was found.
+ * @return A string representing the rights information.
+ */
+ private static
+ String attributeLevelWriteRights(AciLDAPOperationContainer container,
+ AciHandler handler, boolean skipCheck){
+ boolean addRet=false, delRet=false;
+ StringBuilder resString=new StringBuilder();
+ //If the user has bypass-acl privs and the authzid is equal to the
+ //authorization dn, create a right string with a '1' and a valid
+ //summary. If the user has bypass-acl privs and is querying for
+ //another authzid or they don't have privs -- fall through.
+ if(skipCheck && container.isAuthzidAuthorizationDN()) {
+ resString.append("write").append(":1");
+ container.setEvalReason(EnumEvalReason.SKIP_ACI);
+ container.setDecidingAci(null);
+ createSummary(container, true, "main");
+ } else {
+ //Reset everything.
+ container.resetEffectiveRightsParams();
+ //Reset name.
+ container.setTargAttrFiltersAciName(null);
+ container.setRights(ACI_WRITE_ADD | ACI_SKIP_PROXY_CHECK);
+ if(handler.accessAllowed(container)) {
+ if(container.getTargAttrFiltersAciName() == null)
+ addRet=true;
+ }
+ container.setRights(ACI_WRITE_DELETE | ACI_SKIP_PROXY_CHECK);
+ if(handler.accessAllowed(container)) {
+ if(container.getTargAttrFiltersAciName() == null)
+ delRet=true;
+ }
+ //If both booleans are true, then access was allowed by ACIs that did
+ //not contain targattrfilters.
+ if(addRet && delRet)
+ resString.append("write").append(":1");
+ else {
+ //If there is an ACI name then an ACI with a targattrfilters allowed,
+ //access. A '?' is needed because that evaluation really depends on an
+ //unknown attribute value, not the dummy value. If there is no ACI
+ //then one of the above access checks failed and a '0' is needed.
+ if(container.getTargAttrFiltersAciName() != null) {
+ resString.append("write").append(":?");
+ } else {
+ resString.append("write").append(":0");
+ }
+ }
+ }
+ return resString.toString();
+ }
+
+ /**
+ * Perform entryLevel rights evaluation. The rights string is added to the
+ * entry if the aclRights attribute was seen in the search's requested
+ * attribute set.
+ *
+ * @param container The LDAP operation container to use in the evaluations.
+ * @param handler The Aci Handler to use in the access evaluations.
+ * @param mask Mask specifing what rights attribute processing to perform
+ * (aclRights or aclRightsInfo or both).
+ * @param retEntry The entry to return.
+ * @param skipCheck True if ACI evaluation was skipped because bypass-acl
+ * privilege was found.
+ * @return A SearchResultEntry with geteffectiverights entryLevel rights
+ * information added to it.
+ */
+
+ private static SearchResultEntry
+ addEntryLevelRights(AciLDAPOperationContainer container,
+ AciHandler handler,
+ int mask, SearchResultEntry retEntry,
+ boolean skipCheck) {
+ //Perform access evaluations for rights: add, delete, read, write, proxy.
+ StringBuilder evalInfo=new StringBuilder();
+ container.setCurrentAttributeType(null);
+ container.setRights(ACI_ADD | ACI_SKIP_PROXY_CHECK);
+ evalInfo.append(rightsString(container, handler, skipCheck, "add"));
+ addEntryLevelRightsInfo(container, mask, retEntry, "add");
+ evalInfo.append(',');
+ container.setCurrentAttributeType(null);
+ container.setRights(ACI_DELETE | ACI_SKIP_PROXY_CHECK);
+ evalInfo.append(rightsString(container, handler, skipCheck, "delete"));
+ addEntryLevelRightsInfo(container, mask, retEntry, "delete");
+ evalInfo.append(',');
+ container.setCurrentAttributeType(null);
+ //The read right needs the entry with the full set of attributes. This was
+ //saved in the Aci Handlers maysend method.
+ container.useFullResourceEntry(true);
+ container.setRights(ACI_READ | ACI_SKIP_PROXY_CHECK);
+ evalInfo.append(rightsString(container, handler, skipCheck, "read"));
+ addEntryLevelRightsInfo(container, mask, retEntry, "read");
+ evalInfo.append(',');
+ //Switch back to the entry from the Aci Handler's filterentry method.
+ container.useFullResourceEntry(false);
+ container.setCurrentAttributeType(null);
+ container.setRights(ACI_WRITE| ACI_SKIP_PROXY_CHECK);
+ evalInfo.append(rightsString(container, handler, skipCheck, "write"));
+ addEntryLevelRightsInfo(container, mask, retEntry, "write");
+ evalInfo.append(',');
+ container.setCurrentAttributeType(null);
+ container.setRights(ACI_PROXY| ACI_SKIP_PROXY_CHECK);
+ evalInfo.append(rightsString(container, handler, skipCheck, "proxy"));
+ addEntryLevelRightsInfo(container, mask, retEntry, "proxy");
+ if(hasAttrMask(mask, ACL_RIGHTS)) {
+ AttributeType attributeType=
+ DirectoryServer.getDefaultAttributeType(aclRightsEntryLevelStr);
+ LinkedHashSet<AttributeValue> vals = new LinkedHashSet<AttributeValue>();
+ vals.add(new AttributeValue(attributeType, evalInfo.toString()));
+ Attribute attr =
+ new Attribute(attributeType, aclRightsEntryLevelStr, vals);
+ retEntry.addAttribute(attr,null);
+ }
+ return retEntry;
+ }
+
+ /**
+ * Create the rights for aclRights attributeLevel or entryLevel rights
+ * evaluation. The only right needing special treatment is the read right
+ * with no current attribute type set in the container. For that case the
+ * accessAllowedEntry method is used instead of the accessAllowed method.
+ *
+ * @param container The LDAP operation container to use in the evaluations.
+ * @param handler The Aci Handler to use in the access evaluations.
+ * @param skipCheck True if ACI evaluation was skipped because bypass-acl
+ * privilege was found.
+ * @param rightStr String used representation of the right we are evaluating.
+ * @return A string representing the aclRights for the current right and
+ * attribute type/value combinations.
+ */
+ private static
+ String rightsString(AciLDAPOperationContainer container,
+ AciHandler handler,
+ boolean skipCheck, String rightStr){
+ StringBuilder resString=new StringBuilder();
+ container.resetEffectiveRightsParams();
+ //If the user has bypass-acl privs and the authzid is equal to the
+ //authorization dn, create a right string with a '1' and a valid
+ //summary. If the user has bypass-acl privs and is querying for
+ //another authzid or they don't have privs -- fall through.
+ if(skipCheck && container.isAuthzidAuthorizationDN()) {
+ resString.append(rightStr).append(":1");
+ container.setEvalReason(EnumEvalReason.SKIP_ACI);
+ container.setDecidingAci(null);
+ createSummary(container, true, "main");
+ } else {
+ boolean ret;
+ //Check if read right check, if so do accessAllowedEntry.
+ if(container.hasRights(ACI_READ) &&
+ container.getCurrentAttributeType() == null)
+ ret=handler.accessAllowedEntry(container);
+ else
+ ret=handler.accessAllowed(container);
+ if(ret)
+ resString.append(rightStr).append(":1");
+ else
+ resString.append(rightStr).append(":0");
+ }
+ return resString.toString();
+ }
+
+
+ /**
+ * Check that access is allowed on the aclRights and/or aclRightsInfo
+ * attribute types.
+ *
+ * @param container The LDAP operation container to use in the evaluations.
+ * @param handler The Aci Handler to use in the access evaluations.
+ * @param mask Mask specifing what rights attribute processing to perform
+ * (aclRights or aclRightsInfo or both).
+ * @return True if access to the geteffectiverights attribute types are
+ * allowed.
+ */
+ private static
+ boolean rightsAccessAllowed(AciLDAPOperationContainer container,
+ AciHandler handler, int mask) {
+ boolean retRight=true, retInfo=true;
+ if(hasAttrMask(mask, ACL_RIGHTS)) {
+ container.setCurrentAttributeType(aclRights);
+ container.setRights(ACI_READ | ACI_SKIP_PROXY_CHECK);
+ retRight=handler.accessAllowed(container);
+ }
+ if(hasAttrMask(mask, ACL_RIGHTS_INFO)) {
+ container.setCurrentAttributeType(aclRightsInfo);
+ container.setRights(ACI_READ | ACI_SKIP_PROXY_CHECK);
+ retInfo=handler.accessAllowed(container);
+ }
+ return !(!retRight || !retInfo);
+ }
+
+
+ /**
+ * Add aclRightsInfo attributeLevel information to the entry. This is the
+ * summary string built from the last access check.
+ *
+ * @param container The LDAP operation container to use in the evaluations.
+ * @param mask Mask specifing what rights attribute processing to perform
+ * (aclRights or aclRightsInfo or both).
+ * @param aType The attribute type to use in building the attribute type name.
+ * @param retEntry The entry to add the rights information to.
+ * @param rightStr The string representation of the rights evaluated.
+ */
+ private static
+ void addAttrLevelRightsInfo(AciLDAPOperationContainer container, int mask,
+ AttributeType aType, SearchResultEntry retEntry,
+ String rightStr) {
+
+ //Check if the aclRightsInfo attribute was requested.
+ if(hasAttrMask(mask,ACL_RIGHTS_INFO)) {
+ //Build the attribute type.
+ String typeStr=
+ aclRightsInfoAttrLogsStr + ";" + rightStr + ";" +
+ aType.getPrimaryName();
+ AttributeType attributeType=
+ DirectoryServer.getDefaultAttributeType(typeStr);
+ LinkedHashSet<AttributeValue> vals = new LinkedHashSet<AttributeValue>();
+ vals.add(new AttributeValue(attributeType, container.getEvalSummary()));
+ Attribute attr =
+ new Attribute(attributeType, typeStr, vals);
+ //The attribute type might have already been added, probably not but it
+ //is possible.
+ if(!retEntry.hasAttribute(attributeType))
+ retEntry.addAttribute(attr,null);
+ }
+ }
+
+ /**
+ * Add aclRightsInfo entryLevel rights to the entry to be returned. This is
+ * the summary string built from the last access check.
+ *
+ * @param container The LDAP operation container to use in the evaluations.
+ * @param mask Mask specifing what rights attribute processing to perform
+ * (aclRights or aclRightsInfo or both).
+ * @param retEntry The entry to add the rights information to.
+ * @param rightStr The string representation of the rights evaluated.
+ */
+ private static
+ void addEntryLevelRightsInfo(AciLDAPOperationContainer container, int mask,
+ SearchResultEntry retEntry,
+ String rightStr) {
+
+ //Check if the aclRightsInfo attribute was requested.
+ if(hasAttrMask(mask,ACL_RIGHTS_INFO)) {
+ String typeStr=
+ aclRightsInfoEntryLogsStr + ";" + rightStr;
+ AttributeType attributeType=
+ DirectoryServer.getDefaultAttributeType(typeStr);
+ LinkedHashSet<AttributeValue> vals = new LinkedHashSet<AttributeValue>();
+ vals.add(new AttributeValue(attributeType, container.getEvalSummary()));
+ Attribute attr =
+ new Attribute(attributeType, typeStr, vals);
+ retEntry.addAttribute(attr,null);
+ }
+ }
+
+ /**
+ * Check if the provided mask has a specific rights attr value.
+ *
+ * @param mask The mask with the attribute flags.
+ * @param rightsAttr The rights attr value to check for.
+ * @return True if the mask contains the rights attr value.
+ */
+ private static boolean hasAttrMask(int mask, int rightsAttr) {
+ return (mask & rightsAttr) != 0;
+ }
+
+
+ /**
+ * Create the summary string used in the aclRightsInfo log string.
+ *
+ * @param evalCtx The evaluation context to gather information from.
+ * @param evalRet The value returned from the access evaluation.
+ * @param srcStr String that can be used to specify where the summary call's
+ * origin is.
+ */
+ public static
+ void createSummary(AciEvalContext evalCtx, boolean evalRet, String srcStr) {
+ String accessStatus=NOT_ALLOWED;
+ if(evalRet)
+ accessStatus=ALLOWED;
+ String accessReason="";
+ StringBuilder decideAci=new StringBuilder("");
+ //Try and determine what reason string to use.
+ if(evalCtx.getEvalReason() == EnumEvalReason.EVALUATED_ALLOW_ACI) {
+ accessReason=EVALUATED_ALLOW;
+ decideAci.append(", deciding_aci: ").append(evalCtx.getDecidingAciName());
+ } else if(evalCtx.getEvalReason() == EnumEvalReason.EVALUATED_DENY_ACI) {
+ accessReason=EVALUATED_DENY;
+ decideAci.append(", deciding_aci: ").append(evalCtx.getDecidingAciName());
+ } else if(evalCtx.getEvalReason() == EnumEvalReason.NO_ALLOW_ACIS)
+ accessReason=NO_ALLOWS;
+ else if(evalCtx.getEvalReason() == EnumEvalReason.NO_MATCHED_ALLOWS_ACIS)
+ accessReason=NO_ALLOWS_MATCHED;
+ else if(evalCtx.getEvalReason() == EnumEvalReason.SKIP_ACI)
+ accessReason=SKIP_ACI;
+ //Only manipulate the evaluation context's targattrfilters ACI name
+ //if not a selfwrite evaluation and the context's targattrfilter match
+ //hashtable is not empty.
+ if(!evalCtx.isTargAttrFilterMatchAciEmpty() &&
+ !evalCtx.hasRights(ACI_SELF)) {
+ //If the allow list was empty then access is '0'.
+ if(evalCtx.getAllowList().isEmpty()) {
+ evalCtx.setTargAttrFiltersAciName(null);
+ } else if(evalRet) {
+ //The evaluation returned true, clear the evaluation context's
+ //targattrfilters ACI name only if a deny targattrfilters ACI
+ //was not seen. It could remove the allow.
+ if(!evalCtx.hasTargAttrFiltersMatchOp(ACL_TARGATTR_DENY_MATCH))
+ evalCtx.setTargAttrFiltersAciName(null);
+ } else {
+ //The evaluation returned false. If the reason was an
+ //explicit deny evaluation by a non-targattrfilters ACI, clear
+ //the evaluation context's targattrfilters ACI name since targattrfilter
+ //evaluation is pretty much ignored during geteffectiverights eval.
+ //Else, it was a non-explicit deny, if there is not a targattrfilters
+ //ACI that might have granted access the deny stands, else there is
+ //a targattrfilters ACI that might grant access.
+ if(evalCtx.getEvalReason() == EnumEvalReason.EVALUATED_DENY_ACI)
+ evalCtx.setTargAttrFiltersAciName(null);
+ else if(!evalCtx.hasTargAttrFiltersMatchOp(ACL_TARGATTR_ALLOW_MATCH))
+ evalCtx.setTargAttrFiltersAciName(null);
+ }
+ }
+ //Actually build the string.
+ String user=anonymous;
+ if(!evalCtx.getClientDN().isNullDN())
+ user=evalCtx.getClientDN().toString();
+ String right=evalCtx.rightToString();
+ AttributeType aType=evalCtx.getCurrentAttributeType();
+ String attrStr="NULL";
+ if(aType != null)
+ attrStr=aType.getPrimaryName();
+ if(evalCtx.getTargAttrFiltersAciName() != null)
+ decideAci.append(", access depends on attr value");
+ String summaryStr = String.format(summaryFormatStr, srcStr, accessStatus,
+ right,evalCtx.getResourceDN().toString(),attrStr, user,
+ accessReason, decideAci.toString());
+ evalCtx.setEvalSummary(summaryStr);
+ }
+
+ /**
+ * If the specified ACI is in the targattrfilters hashtable contained in the
+ * evaluation context, set the evaluation context's targattrfilters match
+ * variable to either ACL_TARGATTR_DENY_MATCH or ACL_TARGATTR_ALLOW_MATCH
+ * depending on the value of the variable denyAci.
+ *
+ * @param evalCtx The evaluation context to evaluate and save information to.
+ * @param aci The ACI to match.
+ * @param denyAci True if the evaluation was a allow, false if the
+ * evaluation was an deny or the ACI is not in the table.
+ * @return True if the ACI was found in the hashtable.
+ */
+ public static
+ boolean setTargAttrAci(AciEvalContext evalCtx, Aci aci, boolean denyAci) {
+ boolean ret=false;
+ if(evalCtx.hasTargAttrFiltersMatchAci(aci)) {
+ if(denyAci)
+ evalCtx.setTargAttrFiltersMatchOp(ACL_TARGATTR_DENY_MATCH);
+ else
+ evalCtx.setTargAttrFiltersMatchOp(ACL_TARGATTR_ALLOW_MATCH);
+ ret=true;
+ }
+ return ret;
+ }
+}
diff --git a/opends/src/server/org/opends/server/authorization/dseecompat/AciEvalContext.java b/opends/src/server/org/opends/server/authorization/dseecompat/AciEvalContext.java
index 3d94604..401e79e 100644
--- a/opends/src/server/org/opends/server/authorization/dseecompat/AciEvalContext.java
+++ b/opends/src/server/org/opends/server/authorization/dseecompat/AciEvalContext.java
@@ -29,6 +29,7 @@
import org.opends.server.types.DN;
import org.opends.server.types.Entry;
+import org.opends.server.types.AttributeType;
import org.opends.server.api.Group;
import java.net.InetAddress;
@@ -146,4 +147,161 @@
* member of the specified group.
*/
public boolean isMemberOf(Group group);
+
+ /**
+ * Returns true if the hashtable of ACIs that matched the targattrfilters
+ * keyword evaluation is empty. Used by geteffectiverights evaluation to
+ * determine the access value to put in the "write" rights evaluation field.
+ *
+ * @return True if there were not any ACIs that matched targattrfilters
+ * keyword evaluation.
+ */
+ public boolean isTargAttrFilterMatchAciEmpty();
+
+ /**
+ * The context maintains a hashtable of ACIs that matched the targattrfilters
+ * keyword evaluation. The hasTargAttrFiltersMatchAci method returns true if
+ * the specified ACI is contained in that hashtable. Used by
+ * geteffectiverights evaluation to determine the access value to put in the
+ * "write" rights evaluation field.
+ *
+ * @param aci The ACI that to evaluate if it contains a match during
+ * targattrfilters keyword evaluation.
+ *
+ * @return True if a specified ACI matched targattrfilters evaluation.
+ */
+ public boolean hasTargAttrFiltersMatchAci(Aci aci);
+
+ /**
+ * Return true if an ACI that evaluated to deny or allow has an
+ * targattrfilters keyword. Used by geteffectiverights
+ * evaluation to determine the access value to put in the "write" rights
+ * evaluation field.
+ *
+ * @param flag The integer value specifying either a deny or allow, but not
+ * both.
+ *
+ * @return True if the ACI that evaluated to
+ */
+ public boolean hasTargAttrFiltersMatchOp(int flag);
+
+ /**
+ * Returns true if the evaluation context is being used in a
+ * geteffectiverights evaluation.
+ *
+ * @return True if the evaluation context is being used in a
+ * geteffectiverights evaluation.
+ */
+ public boolean isGetEffectiveRightsEval();
+
+ /**
+ * Set the name of the ACI that last matched a targattrfilters rule. Used
+ * in geteffectiverights targattrfilters "write" rights evaluation.
+ *
+ * @param name The ACI name string matching the targattrfilters rule.
+ */
+ public void setTargAttrFiltersAciName(String name);
+
+ /**
+ * Set a flag that specifies that a ACI that evaluated to either deny or
+ * allow contains a targattrfilters keyword. Used by geteffectiverights
+ * evaluation to determine the access value to put in the "write" rights
+ * evaluation field.
+ *
+ * @param flag Either the integer value representing an allow or a deny,
+ * but not both.
+ */
+ public void setTargAttrFiltersMatchOp(int flag);
+
+ /**
+ * Set the reason the last access evaluation was evaluated the way it
+ * was. Used by geteffectiverights evaluation to eventually build the
+ * summary string.
+ *
+ * @param reason The enumeration representing the reason of the last access
+ * evaluation.
+ */
+ public void setEvalReason(EnumEvalReason reason);
+
+ /**
+ * Return the reason the last access evaluation was evaluated the way it
+ * was. Used by geteffectiverights evaluation to build the summary string.
+ *
+ * @return The enumeration representing the reason of the last access
+ * evaluation.
+ */
+ public EnumEvalReason getEvalReason();
+
+ /**
+ * Set the ACI that decided that last access evaluation. Used by
+ * geteffectiverights evaluation to the build summary string.
+ *
+ * @param aci The ACI that decided the last access evaluation.
+ */
+ public void setDecidingAci(Aci aci);
+
+ /**
+ * Check if an evaluation context contains a set of access rights.
+ *
+ * @param rights The rights mask to check.
+ *
+ * @return True if the evaluation context contains a access right set.
+ */
+ public boolean hasRights(int rights);
+
+ /**
+ * Return the name of the ACI that decided the last access evaluation. Used
+ * by geteffectiverights evaluation to build the summmary string.
+ *
+ * @return The name of the ACI that decided the last access evaluation.
+ */
+ public String getDecidingAciName();
+
+ /**
+ * Return true if a evaluation context is being used in proxied authorization
+ * evaluation.
+ *
+ * @return True if evaluation context is being used in proxied authorization
+ * evaluation.
+ */
+ public boolean isProxiedAuthorization();
+
+ /**
+ * Get the current attribute type being evaluated.
+ *
+ * @return The attribute type currently being evaluated.
+ */
+ public AttributeType getCurrentAttributeType();
+
+ /**
+ * Set the value of the summary string to the specified string.
+ * Used in geteffectiverights evaluation to build summary string.
+ *
+ * @param summary The string to set the summary string to
+ */
+ public void setEvalSummary(String summary);
+
+ /**
+ * Return the access evaluation summary string. Used by the geteffectiverights
+ * evaluation when a aclRightsInfo attribute was specified in a search.
+ *
+ * @return The string describing the access evaluation.
+ */
+ public String getEvalSummary();
+
+ /**
+ * Return a string representation of the current right being evaluated.
+ * Used in geteffectiverights evaluation to build summary string.
+ *
+ * @return String representation of the current right being evaluated.
+ */
+ public String rightToString();
+
+ /**
+ * Return the name of the ACI that last matched a targattrfilters rule. Used
+ * in geteffectiverights evaluation.
+ *
+ * @return The name of the ACI that last matched a targattrfilters rule.
+ */
+ public String getTargAttrFiltersAciName();
}
diff --git a/opends/src/server/org/opends/server/authorization/dseecompat/AciHandler.java b/opends/src/server/org/opends/server/authorization/dseecompat/AciHandler.java
index 2d15fbb..86a5462 100644
--- a/opends/src/server/org/opends/server/authorization/dseecompat/AciHandler.java
+++ b/opends/src/server/org/opends/server/authorization/dseecompat/AciHandler.java
@@ -43,6 +43,7 @@
import org.opends.server.protocols.internal.InternalSearchOperation;
import org.opends.server.protocols.internal.InternalClientConnection;
import static org.opends.server.schema.SchemaConstants.*;
+import static org.opends.server.util.ServerConstants.OID_GET_EFFECTIVE_RIGHTS;
import java.util.*;
import java.util.concurrent.locks.Lock;
@@ -75,6 +76,14 @@
*/
public static String ORIG_AUTH_ENTRY="origAuthorizationEntry";
+ /**
+ * String used to save a resource entry containing all the attributes in
+ * the SearchOperation attachment list. This is only used during
+ * geteffectiverights read right processing when all of an entry'ss
+ * attributes need to examined.
+ */
+ public static String ALL_ATTRS_RESOURCE_ENTRY = "allAttrsResourceEntry";
+
/**
* This constructor instantiates the ACI handler class that performs the
* main processing for the dseecompat ACI package. It does the following
@@ -352,28 +361,71 @@
*/
private boolean testApplicableLists(AciEvalContext evalCtx) {
EnumEvalResult res=EnumEvalResult.FALSE;
- //First check deny lists
+ evalCtx.setEvalReason(EnumEvalReason.NO_REASON);
LinkedList<Aci>denys=evalCtx.getDenyList();
+ LinkedList<Aci>allows=evalCtx.getAllowList();
+ //If allows list is empty and not doing geteffectiverights return
+ //false.
+ if(allows.isEmpty() && !(evalCtx.isGetEffectiveRightsEval() &&
+ !evalCtx.hasRights(ACI_SELF) &&
+ evalCtx.isTargAttrFilterMatchAciEmpty())) {
+ evalCtx.setEvalReason(EnumEvalReason.NO_ALLOW_ACIS);
+ evalCtx.setDecidingAci(null);
+ return false;
+ }
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;
+ if(res.equals(EnumEvalResult.FAIL)) {
+ evalCtx.setEvalReason(EnumEvalReason.EVALUATED_DENY_ACI);
+ evalCtx.setDecidingAci(denyAci);
+ return false;
+ } else if (res.equals(EnumEvalResult.TRUE)) {
+ if(evalCtx.isGetEffectiveRightsEval() &&
+ !evalCtx.hasRights(ACI_SELF) &&
+ !evalCtx.isTargAttrFilterMatchAciEmpty()) {
+ //Iterate to next only if deny ACI contains a targattrfilters
+ //keyword.
+ if(AciEffectiveRights.setTargAttrAci(evalCtx, denyAci, true))
+ continue;
+ evalCtx.setEvalReason(EnumEvalReason.EVALUATED_DENY_ACI);
+ evalCtx.setDecidingAci(denyAci);
+ return false;
+ } else {
+ evalCtx.setEvalReason(EnumEvalReason.EVALUATED_DENY_ACI);
+ evalCtx.setDecidingAci(denyAci);
+ 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;
- }
+ res=Aci.evaluate(evalCtx, allowAci);
+ if(res.equals(EnumEvalResult.TRUE)) {
+ if(evalCtx.isGetEffectiveRightsEval() &&
+ !evalCtx.hasRights(ACI_SELF) &&
+ !evalCtx.isTargAttrFilterMatchAciEmpty()) {
+ //Iterate to next only if deny ACI contains a targattrfilters
+ //keyword.
+ if(AciEffectiveRights.setTargAttrAci(evalCtx, allowAci, false))
+ continue;
+ evalCtx.setEvalReason(EnumEvalReason.EVALUATED_ALLOW_ACI);
+ evalCtx.setDecidingAci(allowAci);
+ return true;
+ } else {
+ evalCtx.setEvalReason(EnumEvalReason.EVALUATED_ALLOW_ACI);
+ evalCtx.setDecidingAci(allowAci);
+ return true;
+ }
+ }
}
- return res.getBoolVal();
+ //Nothing matched fall through.
+ evalCtx.setEvalReason(EnumEvalReason.NO_MATCHED_ALLOWS_ACIS);
+ evalCtx.setDecidingAci(null);
+ return false;
}
/**
@@ -397,6 +449,8 @@
allows.add(aci);
}
}
+ if(targetMatchCtx.getTargAttrFiltersMatch())
+ targetMatchCtx.setTargAttrFiltersMatch(false);
}
targetMatchCtx.setAllowList(allows);
targetMatchCtx.setDenyList(denys);
@@ -425,7 +479,7 @@
*
* @return True if access is allowed.
*/
- private boolean accessAllowed(AciContainer container)
+ boolean accessAllowed(AciContainer container)
{
DN dn = container.getResourceEntry().getDN();
//For ACI_WRITE_ADD and ACI_WRITE_DELETE set the ACI_WRITE
@@ -439,9 +493,9 @@
if((container.getCurrentAttributeValue() != null) &&
(container.hasRights(ACI_WRITE)) &&
(isAttributeDN(container.getCurrentAttributeType()))) {
+ String DNString=null;
try {
- String DNString =
- container.getCurrentAttributeValue().getStringValue();
+ DNString = container.getCurrentAttributeValue().getStringValue();
DN tmpDN = DN.decode(DNString);
//Have a valid DN, compare to clientDN to see if the ACI_SELF
//right should be set.
@@ -449,7 +503,11 @@
container.setRights(container.getRights() | ACI_SELF);
}
} catch (DirectoryException ex) {
- return false;
+ //Log a message and keep going.
+ int msgID = MSGID_ACI_NOT_VALID_DN;
+ String message = getMessage(msgID, DNString);
+ logError(ErrorLogCategory.ACCESS_CONTROL,
+ ErrorLogSeverity.INFORMATIONAL, message, msgID);
}
}
@@ -458,8 +516,9 @@
//only do a proxy check if the right is not set to ACI_PROXY and the
//proxied authorization control has been decoded.
if(!container.hasSeenEntry()) {
- if(!container.hasRights(ACI_PROXY) &&
- container.isProxiedAuthorization()) {
+ if(container.isProxiedAuthorization() &&
+ !container.hasRights(ACI_PROXY) &&
+ !container.hasRights(ACI_SKIP_PROXY_CHECK)) {
int currentRights=container.getRights();
//Save the current rights so they can be put back if on success.
container.setRights(ACI_PROXY);
@@ -488,9 +547,13 @@
*/
createApplicableList(candidates,container);
/*
- * Lastly, evaluate the applicable list.
+ * Evaluate the applicable list.
*/
- return(testApplicableLists(container));
+ boolean ret=testApplicableLists(container);
+ //Build summary string if doing geteffectiverights eval.
+ if(container.isGetEffectiveRightsEval())
+ AciEffectiveRights.createSummary(container, ret, "main");
+ return ret;
}
/**
@@ -578,7 +641,7 @@
*
* @return True if access is allowed.
*/
- private boolean accessAllowedEntry(AciLDAPOperationContainer container) {
+ boolean accessAllowedEntry(AciLDAPOperationContainer container) {
boolean ret=false;
//set flag that specifies this is the first attribute evaluated
//in the entry
@@ -816,6 +879,8 @@
ret=accessAllowedEntry(operationContainer);
}
}
+ if(ret && operation.getAttachment(OID_GET_EFFECTIVE_RIGHTS) != null)
+ operation.setAttachment(ALL_ATTRS_RESOURCE_ENTRY, entry );
return ret;
}
@@ -844,10 +909,17 @@
//method, set the seen flag to true to bypass any proxy check.
operationContainer.setSeenEntry(true);
SearchResultEntry returnEntry;
- if(!skipAccessCheck(operation)) {
+ boolean skipCheck=skipAccessCheck(operation);
+ if(!skipCheck) {
returnEntry=accessAllowedAttrs(operationContainer);
} else
returnEntry=entry;
+ if(operationContainer.hasGetEffectiveRightsControl()) {
+ returnEntry =
+ AciEffectiveRights.addRightsToEntry(this, operation.getAttributes(),
+ operationContainer, returnEntry,
+ skipCheck);
+ }
return returnEntry;
}
@@ -1009,6 +1081,19 @@
return true;
}
+ /**
+ * Called when a geteffectiverights request control was decoded. Currently
+ * used to save the control in the specified operation's attachment list.
+ * Eventually will be used to check access to the actual control.
+ * @param operation The operation to save the attachment to.
+ * @param c The request control to save.
+ * @return True if the control is allowed access.
+ */
+ public boolean isGetEffectiveRightsAllowed(Operation operation, Control c) {
+ operation.setAttachment(OID_GET_EFFECTIVE_RIGHTS, c);
+ return true;
+ }
+
//Not planned to be implemented methods.
/**
diff --git a/opends/src/server/org/opends/server/authorization/dseecompat/AciTargetMatchContext.java b/opends/src/server/org/opends/server/authorization/dseecompat/AciTargetMatchContext.java
index 7fdb608..3ccb74b 100644
--- a/opends/src/server/org/opends/server/authorization/dseecompat/AciTargetMatchContext.java
+++ b/opends/src/server/org/opends/server/authorization/dseecompat/AciTargetMatchContext.java
@@ -143,6 +143,28 @@
* @return True if the ACI had a targattrfilter rule that matched.
*/
public boolean getTargAttrFiltersMatch();
+
+ /**
+ * Add the specified ACI to a list of ACIs that have a targattrfilters rule
+ * that matched. This is used by geteffectiverights to determine the rights
+ * of an attribute that possibly might evaluate to true.
+ * @param aci The ACI to save.
+ */
+ public void addTargAttrFiltersMatchAci(Aci aci);
+
+ /**
+ * Save the name of the last ACI that matched a targattrfilters rule. This
+ * is used by geteffectiverights evaluation.
+ * @param name The ACI's name to save.
+ */
+ void setTargAttrFiltersAciName(String name);
+
+ /**
+ * Returns true of a match context is performing a geteffectiverights
+ * evaluation.
+ * @return True if a match context is evaluating geteffectiverights.
+ */
+ boolean isGetEffectiveRightsEval();
}
diff --git a/opends/src/server/org/opends/server/authorization/dseecompat/AciTargets.java b/opends/src/server/org/opends/server/authorization/dseecompat/AciTargets.java
index 0fd4984..8fd9d7b 100644
--- a/opends/src/server/org/opends/server/authorization/dseecompat/AciTargets.java
+++ b/opends/src/server/org/opends/server/authorization/dseecompat/AciTargets.java
@@ -385,7 +385,7 @@
targAttrFilters.hasMask(TARGATTRFILTERS_ADD)) ||
(matchCtx.hasRights(ACI_WRITE_DELETE) &&
targAttrFilters.hasMask(TARGATTRFILTERS_DELETE)))
- ret=targAttrFilters.isApplicableMod(matchCtx);
+ ret=targAttrFilters.isApplicableMod(matchCtx, aci);
}
return ret;
}
@@ -423,7 +423,8 @@
else
ret = false;
}
- if((isFirstAttr) && (aci.getTargets().getTargetAttr() == null))
+ if((isFirstAttr) && (aci.getTargets().getTargetAttr() == null)
+ && aci.getTargets().getTargAttrFilters() == null)
targetMatchCtx.setEntryTestRule(true);
}
return ret;
diff --git a/opends/src/server/org/opends/server/authorization/dseecompat/EnumEvalReason.java b/opends/src/server/org/opends/server/authorization/dseecompat/EnumEvalReason.java
new file mode 100644
index 0000000..3b9e359
--- /dev/null
+++ b/opends/src/server/org/opends/server/authorization/dseecompat/EnumEvalReason.java
@@ -0,0 +1,74 @@
+/*
+ * 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 reasons why an ACI evaluation
+ * returned a result from the AciHandler's testApplicableLists call. This class
+ * is used by a geteffectiverights aclRightsInfo attribute search to build
+ * a summary string.
+ */
+
+public enum EnumEvalReason {
+
+ /**
+ * There are aren't any allow ACIs.
+ */
+ NO_ALLOW_ACIS(0),
+
+ /**
+ * An deny ACI either evaluated to FAIL or to TRUE.
+ */
+ EVALUATED_DENY_ACI(1),
+
+ /**
+ * An allow evaluated to true.
+ */
+ EVALUATED_ALLOW_ACI(2),
+
+ /**
+ * None of the allow and deny ACIs evaluated to true.
+ */
+ NO_MATCHED_ALLOWS_ACIS(3),
+
+ /**
+ * No specific reason could be determined.
+ */
+ NO_REASON(4),
+
+ /**
+ * The authorization DN has bypass-acl privileges.
+ */
+ SKIP_ACI(5);
+
+ /**
+ * Create a new enumeration type for the specified result value.
+ * @param v The value of the result.
+ */
+ EnumEvalReason(int v) {}
+}
diff --git a/opends/src/server/org/opends/server/authorization/dseecompat/TargAttrFilters.java b/opends/src/server/org/opends/server/authorization/dseecompat/TargAttrFilters.java
index 256c883..fd001d7 100644
--- a/opends/src/server/org/opends/server/authorization/dseecompat/TargAttrFilters.java
+++ b/opends/src/server/org/opends/server/authorization/dseecompat/TargAttrFilters.java
@@ -246,10 +246,12 @@
* operation.
* @param matchCtx The target match context containing the information
* needed to match.
+ * @param aci The ACI currently being evaluted for a target match.
* @return True if this TargAttrFitlers object is applicable to this
* target match context.
*/
- public boolean isApplicableMod(AciTargetMatchContext matchCtx) {
+ public boolean isApplicableMod(AciTargetMatchContext matchCtx,
+ Aci aci) {
//Get the targFitlerList corresponding to this context's rights.
TargAttrFilterList attrFilterList=getTargAttrFilterList(matchCtx);
//If the list is empty return true and go on to the targattr check
@@ -270,6 +272,12 @@
attrMatched=matchFilterAttributeValue(attrType, value, filter);
//This flag causes any targattr checks to be bypassed in AciTargets.
matchCtx.setTargAttrFiltersMatch(true);
+ //Doing a geteffectiverights eval, save the ACI and the name
+ //in the context.
+ if(matchCtx.isGetEffectiveRightsEval()) {
+ matchCtx.setTargAttrFiltersAciName(aci.getName());
+ matchCtx.addTargAttrFiltersMatchAci(aci);
+ }
if(op.equals(EnumTargetOperator.NOT_EQUALITY))
attrMatched = !attrMatched;
}
diff --git a/opends/src/server/org/opends/server/authorization/dseecompat/TargetAttr.java b/opends/src/server/org/opends/server/authorization/dseecompat/TargetAttr.java
index 57bfe49..9b2afff 100644
--- a/opends/src/server/org/opends/server/authorization/dseecompat/TargetAttr.java
+++ b/opends/src/server/org/opends/server/authorization/dseecompat/TargetAttr.java
@@ -54,6 +54,11 @@
*/
private HashSet<AttributeType> attributes = new HashSet<AttributeType>();
+ /**
+ * HashSet of the operational attribute types parsed by the constructor.
+ */
+ private HashSet<AttributeType> opAttributes = new HashSet<AttributeType>();
+
/*
* Regular expression that matches one or more ATTR_NAME's separated by
* the "||" token.
@@ -93,6 +98,22 @@
//Add each element of array to attributes HashSet
//after converting it to AttributeType.
arrayToAttributeTypes(attributeArray);
+ //Must be either all operational attrs or all user attrs,
+ //but not both.
+ if(!opAttributes.isEmpty() && !attributes.isEmpty()) {
+ int msgID =
+ MSGID_ACI_TARGETATTR_INVALID_OP_USER_ATTR;
+ String message = getMessage(msgID, attrString);
+ throw new AciException(msgID, message);
+ }
+ //Inequality not allowed with operational attrs.
+ if(!opAttributes.isEmpty() &&
+ operator.equals(EnumTargetOperator.NOT_EQUALITY)) {
+ int msgID =
+ MSGID_ACI_TARGATTR_INVALID_OP_ATTR_INEQUALITY;
+ String message = getMessage(msgID, attrString);
+ throw new AciException(msgID, message);
+ }
} else {
int msgID =
MSGID_ACI_SYNTAX_INVALID_TARGETATTRKEYWORD_EXPRESSION;
@@ -106,7 +127,8 @@
/**
* Converts each element of an array of attribute type strings
- * to attribute types and adds them to the attributes HashSet.
+ * to attribute types and adds them to either the attributes HashSet or
+ * the operational attributes HashSet if they are operational.
* @param attributeArray The array of attribute type strings.
*/
private void arrayToAttributeTypes(String[] attributeArray) {
@@ -117,6 +139,9 @@
DirectoryServer.getAttributeType(attribute)) == null)
attributeType =
DirectoryServer.getDefaultAttributeType(attribute);
+ if(attributeType.isOperational())
+ opAttributes.add(attributeType);
+ else
attributes.add(attributeType);
}
}
@@ -147,6 +172,15 @@
return attributes;
}
+ /**
+ * Return array holding operational attribute types to be evaluated
+ * in the expression.
+ * @return Array holding attribute types.
+ */
+ public HashSet<AttributeType> getOpAttributes() {
+ return opAttributes;
+ }
+
/**
* Decodes an targetattr expression string into a targetattr class suitable
* for evaluation.
@@ -167,12 +201,24 @@
*
* 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.
+ * is seen when an ACI is parsed. This boolean only applies to
+ * non-operational attribute types. If the attribute type being evaluated
+ * and the isAllAttributes is true, then the evaluation will return false
+ * because operational attributes must be explicity defined.
*
- * If the isAllAttributes boolean is false, then the TargeAttr's
+ * If the isAllAttributes boolean is true (and the attribute is
+ * non-operational), 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 attribute type is
+ * checked to see if it is operational. If it is, then the operational
+ * HashSet is searched to see if it contains the operational attribute
+ * type. If it is found then true is returned, else false is returned
+ * if it isn't found. The NOT_EQUALITY operator is invalid for operational
+ * attribute types and is not checked.
+ *
+ * If the attribute is not operational, then the TargeAttr's user
* 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.
@@ -183,25 +229,33 @@
* TargetAttr's operator value applied to the test result.
*/
public static boolean isApplicable(AttributeType a,
- TargetAttr targetAttr) {
+ TargetAttr targetAttr) {
boolean ret;
if(targetAttr.isAllAttributes()) {
- //If it is an operational attribute, then access is denied for all
- //attributes wild-card. Operational attributes must be
- // explicitly defined.
- if(a.isOperational()) {
- ret=false;
- } else
- ret =
- !targetAttr.getOperator().equals(EnumTargetOperator.NOT_EQUALITY);
- } else {
+ //If it is an operational attribute, then access is denied for all
+ //attributes wild-card. Operational attributes must be
+ // explicitly defined and cannot be negated.
+ if(a.isOperational()) {
ret=false;
+ } else
+ ret =
+ !targetAttr.getOperator().equals(EnumTargetOperator.NOT_EQUALITY);
+ } else {
+ ret=false;
HashSet<AttributeType> attributes=targetAttr.getAttributes();
- if(attributes.contains(a))
+ HashSet<AttributeType> opAttributes=targetAttr.getOpAttributes();
+ //Check if the attribute is operational, if so check the
+ //operation HashSet.
+ if(a.isOperational()) {
+ if(opAttributes.contains(a))
+ ret=true;
+ } else {
+ if(attributes.contains(a))
ret=true;
- if(targetAttr.getOperator().equals(EnumTargetOperator.NOT_EQUALITY))
+ if(targetAttr.getOperator().equals(EnumTargetOperator.NOT_EQUALITY))
ret = !ret;
- }
+ }
+ }
return ret;
}
}
diff --git a/opends/src/server/org/opends/server/controls/GetEffectiveRights.java b/opends/src/server/org/opends/server/controls/GetEffectiveRights.java
new file mode 100644
index 0000000..24f8777
--- /dev/null
+++ b/opends/src/server/org/opends/server/controls/GetEffectiveRights.java
@@ -0,0 +1,245 @@
+/*
+ * 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.controls;
+
+import org.opends.server.types.*;
+import org.opends.server.protocols.asn1.ASN1OctetString;
+import org.opends.server.protocols.asn1.ASN1Element;
+import org.opends.server.protocols.asn1.ASN1Exception;
+import org.opends.server.protocols.ldap.LDAPResultCode;
+import static org.opends.server.util.ServerConstants.OID_GET_EFFECTIVE_RIGHTS;
+import static org.opends.server.util.Validator.ensureNotNull;
+import static org.opends.server.util.StaticUtils.toLowerCase;
+import org.opends.server.core.DirectoryServer;
+import static org.opends.server.messages.ProtocolMessages.*;
+import static org.opends.server.messages.MessageHandler.getMessage;
+import static org.opends.server.loggers.debug.DebugLogger.debugEnabled;
+import static org.opends.server.loggers.debug.DebugLogger.debugCaught;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.LinkedList;
+
+/**
+ * This class partially implements the geteffectiverights control as defined
+ * in draft-ietf-ldapext-acl-model-08.txt. The main differences are:
+ *
+ * - The response control is not supported. Instead the dseecompat
+ * geteffectiverights control implementation creates attributes containing
+ * right information strings and adds those attributes to the
+ * entry being returned. The attribute type names are dynamically created;
+ * see the dseecompat's AciGetEffectiveRights class for details.
+ *
+ * - The dseecompat implementation allows additional attribute types
+ * in the request control for which rights information can be returned.
+ * These are known as the specified attribute types.
+ *
+ * The dseecompat request control value is the following:
+ *
+ * <BR>
+ * <PRE>
+ * GetRightsControl ::= SEQUENCE {
+ * authzId authzId
+ * attributes SEQUENCE OF AttributeType
+ * }
+ *
+ * -- Only the "dn:DN form is supported.
+ *
+ * </PRE>
+ *
+ **/
+public class GetEffectiveRights extends Control {
+
+ //The DN representing the authzId. May be null.
+ private DN authzDN=null;
+
+ //The list of additional attribute types to return rights for. May be null.
+ private List<AttributeType> attrs=null;
+
+ /**
+ * Create a new geteffectiverights control with null authzDN and null
+ * attribute list.
+ */
+ public GetEffectiveRights() {
+ super(OID_GET_EFFECTIVE_RIGHTS, true, null);
+ }
+
+ /**
+ * Create a new geteffectiverights control with the specified raw octet
+ * string, an authzDN and an attribute list.
+ *
+ * @param val The octet string repsentation of the control value.
+ *
+ * @param authzDN The authzDN.
+ *
+ * @param attrs The list of additional attributes to be returned.
+ */
+ public GetEffectiveRights(ASN1OctetString val, DN authzDN,
+ List<AttributeType> attrs) {
+ super(OID_GET_EFFECTIVE_RIGHTS, true, val);
+ this.authzDN=authzDN;
+ this.attrs=attrs;
+ }
+
+ /**
+ * Return the authzDN parsed from the control.
+ *
+ * @return The DN representing the authzId.
+ */
+ public DN getAuthzDN () {
+ return authzDN;
+ }
+
+ /**
+ * Return the requested additional attributes parsed from the control. Known
+ * as the specified attributes.
+ *
+ * @return The list containing any additional attributes to return rights
+ * about.
+ */
+ public List<AttributeType> getAttributes() {
+ return attrs;
+ }
+
+
+ /**
+ * Decodes the provided ASN1 element. Assume that it is a ASN1 sequence
+ * of attributetypes.
+ *
+ * @param attributeElement The ASN1 element to be decoded.
+ *
+ * @return A list of attribute types to process rights of.
+ *
+ * @throws ASN1Exception If the attribute element cannot be decoded as a
+ * sequence.
+ */
+ private static
+ List<AttributeType> decodeAttributeSequence(ASN1Element attributeElement)
+ throws ASN1Exception {
+ List<AttributeType> attributeList = new LinkedList<AttributeType>();
+ //Decode the sequence element and put the individual elements in array.
+ ArrayList<ASN1Element> attrElems =
+ attributeElement.decodeAsSequence().elements();
+ int numAttrElements = attrElems.size();
+ for(int i=0; i < numAttrElements; i++) {
+ //Decode as an octet string.
+ ASN1OctetString tmp=attrElems.get(i).decodeAsOctetString();
+ //Get an attribute type for it and add to the list.
+ AttributeType attributeType;
+ if((attributeType =
+ DirectoryServer.getAttributeType(tmp.toString())) == null)
+ attributeType =
+ DirectoryServer.getDefaultAttributeType(tmp.toString());
+ attributeList.add(attributeType);
+ }
+ return attributeList;
+ }
+
+ /**
+ * Decodes the request control's value octet string into a GetEffectiveRights
+ * class. It assumes that it is an ASN1 sequence as described in class
+ * description.
+ *
+ * @param val The octet string repsentation of the control value.
+ *
+ * @return A decoded GetEffectiveRights class representing the request
+ * control.
+ *
+ * @throws LDAPException If the request control's value contains errors
+ * causing a valid GetEffectiveRights class to not
+ * be created.
+ */
+ private static
+ GetEffectiveRights decodeValueSequence(ASN1OctetString val )
+ throws LDAPException {
+ DN authzDN;
+ List<AttributeType> attrs=null;
+ String authzIDString="";
+ try {
+ ASN1Element sequence = ASN1Element.decode(val.value());
+ ArrayList<ASN1Element> elements =
+ sequence.decodeAsSequence().elements();
+ ASN1OctetString authzID = elements.get(0).decodeAsOctetString();
+ //There is an sequence containing an attribute list, try to decode it.
+ if(elements.size() == 2)
+ attrs=decodeAttributeSequence(elements.get(1));
+ authzIDString = authzID.stringValue();
+ String lowerAuthzIDString = toLowerCase(authzIDString);
+ //Make sure authzId starts with "dn:" and is a valid DN.
+ if (lowerAuthzIDString.startsWith("dn:"))
+ authzDN = DN.decode(authzIDString.substring(3));
+ else {
+ int msgID = MSGID_GETEFFECTIVERIGHTS_INVALID_AUTHZID;
+ String message = getMessage(msgID, authzID);
+ throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, msgID, message);
+ }
+ } catch (ASN1Exception e) {
+ if (debugEnabled()) {
+ debugCaught(DebugLogLevel.ERROR, e);
+ }
+
+ int msgID = MSGID_GETEFFECTIVERIGHTS_DECODE_ERROR;
+ String message = getMessage(msgID, e.getMessage());
+ throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, msgID, message);
+ } catch (DirectoryException de) {
+ if (debugEnabled()) {
+ debugCaught(DebugLogLevel.ERROR, de);
+ }
+
+ int msgID = MSGID_CANNOT_DECODE_GETEFFECTIVERIGHTS_AUTHZID_DN;
+ String message =
+ getMessage(msgID, authzIDString.substring(3), de.getErrorMessage());
+ throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, msgID, message);
+ }
+ return new GetEffectiveRights(val, authzDN, attrs);
+ }
+
+ /**
+ * Decodes the request control's value into a GetEffectiveRights class.
+ *
+ * @param control The control class representing the request control.
+ *
+ * @return A decoded GetEffectiveRights class representing the request
+ * control.
+ *
+ * @throws LDAPException If the request control's value contains errors
+ * causing a valid GetEffectiveRights class to not
+ * be created.
+ */
+ public static
+ GetEffectiveRights decodeControl(Control control) throws LDAPException {
+ ensureNotNull(control);
+ ASN1OctetString value=control.getValue();
+ //If the value is null create a GetEffectiveRights class with null
+ //authzDN and attribute list, else try to decode the value.
+ if(value == null)
+ return new GetEffectiveRights();
+ else
+ return GetEffectiveRights.decodeValueSequence(value);
+ }
+}
diff --git a/opends/src/server/org/opends/server/core/DefaultAccessControlProvider.java b/opends/src/server/org/opends/server/core/DefaultAccessControlProvider.java
index 9dc931d..6800688 100644
--- a/opends/src/server/org/opends/server/core/DefaultAccessControlProvider.java
+++ b/opends/src/server/org/opends/server/core/DefaultAccessControlProvider.java
@@ -34,7 +34,7 @@
import org.opends.server.types.Operation;
import org.opends.server.types.SearchResultEntry;
import org.opends.server.types.SearchResultReference;
-import org.opends.server.types.Entry;
+import org.opends.server.types.*;
/**
* This class implements a default access control provider for the
@@ -209,5 +209,13 @@
public boolean isProxiedAuthAllowed(Operation operation, Entry entry) {
return true;
}
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isGetEffectiveRightsAllowed(Operation operation, Control c) {
+ return true;
+ }
}
}
diff --git a/opends/src/server/org/opends/server/core/SearchOperation.java b/opends/src/server/org/opends/server/core/SearchOperation.java
index 47d6c20..bca3da7 100644
--- a/opends/src/server/org/opends/server/core/SearchOperation.java
+++ b/opends/src/server/org/opends/server/core/SearchOperation.java
@@ -42,12 +42,7 @@
import org.opends.server.api.plugin.PreParsePluginResult;
import org.opends.server.api.plugin.SearchEntryPluginResult;
import org.opends.server.api.plugin.SearchReferencePluginResult;
-import org.opends.server.controls.AccountUsableResponseControl;
-import org.opends.server.controls.LDAPAssertionRequestControl;
-import org.opends.server.controls.MatchedValuesControl;
-import org.opends.server.controls.PersistentSearchControl;
-import org.opends.server.controls.ProxiedAuthV1Control;
-import org.opends.server.controls.ProxiedAuthV2Control;
+import org.opends.server.controls.*;
import org.opends.server.protocols.asn1.ASN1OctetString;
import org.opends.server.protocols.ldap.LDAPFilter;
import org.opends.server.types.Attribute;
@@ -1922,6 +1917,43 @@
else if (oid.equals(OID_VIRTUAL_ATTRS_ONLY))
{
virtualAttributesOnly = true;
+ } else if(oid.equals(OID_GET_EFFECTIVE_RIGHTS)) {
+ GetEffectiveRights effectiveRightsControl;
+ if (c instanceof GetEffectiveRights)
+ {
+ effectiveRightsControl = (GetEffectiveRights) c;
+ }
+ else
+ {
+ try
+ {
+ effectiveRightsControl = GetEffectiveRights.decodeControl(c);
+ }
+ catch (LDAPException le)
+ {
+ if (debugEnabled())
+ {
+ debugCaught(DebugLogLevel.ERROR, le);
+ }
+
+ setResultCode(ResultCode.valueOf(le.getResultCode()));
+ appendErrorMessage(le.getMessage());
+
+ break searchProcessing;
+ }
+ }
+
+ if (!AccessControlConfigManager.getInstance()
+ .getAccessControlHandler().
+ isGetEffectiveRightsAllowed(this, effectiveRightsControl)) {
+ setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS);
+ int msgID =
+ MSGID_SEARCH_EFFECTIVERIGHTS_INSUFFICIENT_ACCESS_RIGHTS;
+ appendErrorMessage(getMessage(msgID, String.valueOf(baseDN)));
+
+ skipPostOperation = true;
+ break searchProcessing;
+ }
}
// NYI -- Add support for additional controls.
diff --git a/opends/src/server/org/opends/server/messages/AciMessages.java b/opends/src/server/org/opends/server/messages/AciMessages.java
index d140858..9935405 100644
--- a/opends/src/server/org/opends/server/messages/AciMessages.java
+++ b/opends/src/server/org/opends/server/messages/AciMessages.java
@@ -731,6 +731,31 @@
public static final int MSGID_ACI_HANDLER_CANNOT_LOCK_NEW_SUPERIOR_USER =
CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_SEVERE_WARNING | 72;
+ /**
+ * The message ID for the message that will be used if a attribute type with
+ * a DN syntax failed to DN decode in the selfwrite access checking. This
+ * takes one argument, which is the invalid DN string.
+ */
+ public static final int MSGID_ACI_NOT_VALID_DN =
+ CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_SEVERE_WARNING | 73;
+
+ /**
+ * The message ID for the message that will be used if a targetattr
+ * keyword expression contains both operational and user attribute
+ * types. This takes one argument, which is the targetattr expression string.
+ */
+ public static final int MSGID_ACI_TARGETATTR_INVALID_OP_USER_ATTR =
+ CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_SEVERE_WARNING | 74;
+
+ /**
+ * The message ID for the message that will be used if a targetattr
+ * keyword expression performs both an inequality operation using
+ * operational attribute types. This takes one argument, which is the
+ * targetattr expression string.
+ */
+ public static final int MSGID_ACI_TARGATTR_INVALID_OP_ATTR_INEQUALITY =
+ CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_SEVERE_WARNING | 75;
+
/**
* Associates a set of generic messages with the message IDs defined in
* this class.
@@ -1148,5 +1173,21 @@
registerMessage(MSGID_ACI_HANDLER_CANNOT_LOCK_NEW_SUPERIOR_USER,
"Unable to obtain a lock on the ModifyDN new superior entry %s");
+
+ registerMessage(MSGID_ACI_NOT_VALID_DN,
+ "Selfwrite check skipped because an attribute \"%s\" with a " +
+ "distinguished name syntax was not a valid DN");
+
+ registerMessage(MSGID_ACI_TARGETATTR_INVALID_OP_USER_ATTR,
+ "The provided Access Control Instruction (ACI) " +
+ "targetattr expression value \"%s\" is invalid because" +
+ " the expression contains both operational attribute types" +
+ " and user attribute types");
+
+ registerMessage(MSGID_ACI_TARGATTR_INVALID_OP_ATTR_INEQUALITY,
+ "The provided Access Control Instruction (ACI) " +
+ "targetattr expression value \"%s\" is invalid because" +
+ " the expression performs an inequality operation using " +
+ "operational attribute types");
}
}
diff --git a/opends/src/server/org/opends/server/messages/CoreMessages.java b/opends/src/server/org/opends/server/messages/CoreMessages.java
index d27a8e9..1f3f357 100644
--- a/opends/src/server/org/opends/server/messages/CoreMessages.java
+++ b/opends/src/server/org/opends/server/messages/CoreMessages.java
@@ -6120,6 +6120,16 @@
CATEGORY_MASK_CORE | SEVERITY_MASK_INFORMATIONAL | 610;
+ /**
+ * The message ID for the message that will be used if a search
+ * operation cannot be processed due to insufficient access rights caused
+ * by an access control check on the geteffectiverights control. This message
+ * takes a single argument, the name of the search operation's base entry.
+ */
+ public static final
+ int MSGID_SEARCH_EFFECTIVERIGHTS_INSUFFICIENT_ACCESS_RIGHTS =
+ CATEGORY_MASK_CORE | SEVERITY_MASK_SEVERE_ERROR | 611;
+
/**
* Associates a set of generic messages with the message IDs defined
@@ -8175,8 +8185,8 @@
"The entry %s cannot be modified due to insufficient access rights");
registerMessage(MSGID_SEARCH_AUTHZ_INSUFFICIENT_ACCESS_RIGHTS,
"The entry %s cannot be searched due to insufficient access rights");
-
-
+ registerMessage(MSGID_SEARCH_EFFECTIVERIGHTS_INSUFFICIENT_ACCESS_RIGHTS,
+ "The entry %s cannot be searched due to insufficient access rights");
registerMessage(MSGID_BIND_OPERATION_INSECURE_SIMPLE_BIND,
"Rejecting a simple bind request for user %s because the " +
"password policy requires secure authentication");
diff --git a/opends/src/server/org/opends/server/messages/ProtocolMessages.java b/opends/src/server/org/opends/server/messages/ProtocolMessages.java
index 156a19c..af4cef1 100644
--- a/opends/src/server/org/opends/server/messages/ProtocolMessages.java
+++ b/opends/src/server/org/opends/server/messages/ProtocolMessages.java
@@ -4578,6 +4578,36 @@
/**
+ * The message ID for the message that will be used if an error occurs parsing
+ * the geteffectiverights authzid because it does not start with the required
+ * string "dn:". This takes one argument, which is the authzid string.
+ */
+ public static final int MSGID_GETEFFECTIVERIGHTS_INVALID_AUTHZID =
+ CATEGORY_MASK_PROTOCOL | SEVERITY_MASK_INFORMATIONAL | 424;
+
+
+
+ /**
+ * The message ID for the message that will be used if an error occurs while
+ * attempting to decode the value of an geteffectiverights request control.
+ * This takes a single argument, which is a message explaining the problem
+ * that occurred.
+ */
+ public static final int MSGID_GETEFFECTIVERIGHTS_DECODE_ERROR =
+ CATEGORY_MASK_PROTOCOL | SEVERITY_MASK_INFORMATIONAL | 425;
+
+
+ /**
+ * The message ID for the message that will be used if an error occurs while
+ * trying to decode the geteffectiverights authzid DN string. This takes two
+ * arguments, which are the authzid string and a message explaining the
+ * problem that was encountered.
+ */
+ public static final int MSGID_CANNOT_DECODE_GETEFFECTIVERIGHTS_AUTHZID_DN =
+ CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 426;
+
+
+ /**
* Associates a set of generic messages with the message IDs defined in this
* class.
*/
@@ -6540,6 +6570,18 @@
registerMessage(MSGID_VLVRES_CONTROL_CANNOT_DECODE_VALUE,
"Unable to process the provided VLV response control " +
"because an error occurred while attempting to decode " +
- "the control value: %s"); }
+ "the control value: %s");
+ registerMessage(MSGID_GETEFFECTIVERIGHTS_INVALID_AUTHZID,
+ "The authorization ID \"%s\" contained in the " +
+ "geteffectiverights control is invalid because it does" +
+ " not start with \"dn:\" to indicate a user DN");
+ registerMessage(MSGID_GETEFFECTIVERIGHTS_DECODE_ERROR,
+ "Cannot decode the provided geteffectiverights " +
+ "request control: %s");
+ registerMessage(MSGID_CANNOT_DECODE_GETEFFECTIVERIGHTS_AUTHZID_DN,
+ "Unable to decode authzid DN string \"%s\" as a valid " +
+ "distinguished name: %s"); }
+
+
}
diff --git a/opends/src/server/org/opends/server/messages/ToolMessages.java b/opends/src/server/org/opends/server/messages/ToolMessages.java
index 9bfe596..e43ace7 100644
--- a/opends/src/server/org/opends/server/messages/ToolMessages.java
+++ b/opends/src/server/org/opends/server/messages/ToolMessages.java
@@ -7067,6 +7067,31 @@
public static final int MSGID_LDIFIMPORT_CANNOT_READ_FILE =
CATEGORY_MASK_TOOLS | SEVERITY_MASK_SEVERE_ERROR | 887;
+/**
+ * The message ID for the message that will be used as the description of the
+ * geteffectiverights control authzid argument. This does not take any
+ * arguments.
+ */
+ public static final int MSGID_DESCRIPTION_EFFECTIVERIGHTS_USER =
+ CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 888;
+
+
+ /**
+ * The message ID for the message that will be used as the description of the
+ * geteffectiverights control specific attribute list argument. This does
+ * not take any arguments.
+ */
+ public static final int MSGID_DESCRIPTION_EFFECTIVERIGHTS_ATTR =
+ CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 889;
+
+
+ /**
+ * The message ID for the message that will be used as the description of the
+ * geteffectiverights authzid when it does not start with the required string
+ * "dn:". This takes a single argument, which is the authzid string.
+ */
+ public static final int MSGID_EFFECTIVERIGHTS_INVALID_AUTHZID =
+ CATEGORY_MASK_TOOLS | SEVERITY_MASK_MILD_ERROR | 890;
/**
@@ -9294,6 +9319,19 @@
registerMessage(MSGID_LDIFIMPORT_CANNOT_READ_FILE,
"The specified LDIF file %s cannot be read");
+
+ registerMessage(MSGID_DESCRIPTION_EFFECTIVERIGHTS_USER,
+ "Use geteffectiverights control with the provided " +
+ "authzid");
+
+ registerMessage(MSGID_DESCRIPTION_EFFECTIVERIGHTS_ATTR,
+ "Specifies geteffectiverights control specific " +
+ "attribute list");
+
+ registerMessage(MSGID_EFFECTIVERIGHTS_INVALID_AUTHZID,
+ "The authorization ID \"%s\" contained in the " +
+ "geteffectiverights control is invalid because it does" +
+ " not start with \"dn:\" to indicate a user DN");
}
}
diff --git a/opends/src/server/org/opends/server/tools/LDAPSearch.java b/opends/src/server/org/opends/server/tools/LDAPSearch.java
index b799511..2c604de 100644
--- a/opends/src/server/org/opends/server/tools/LDAPSearch.java
+++ b/opends/src/server/org/opends/server/tools/LDAPSearch.java
@@ -71,10 +71,7 @@
import org.opends.server.protocols.ldap.SearchResultDoneProtocolOp;
import org.opends.server.protocols.ldap.SearchResultEntryProtocolOp;
import org.opends.server.protocols.ldap.SearchResultReferenceProtocolOp;
-import org.opends.server.types.DN;
-import org.opends.server.types.DebugLogLevel;
-import org.opends.server.types.LDAPException;
-import org.opends.server.types.NullOutputStream;
+import org.opends.server.types.*;
import static org.opends.server.loggers.debug.DebugLogger.*;
import static org.opends.server.messages.MessageHandler.*;
@@ -671,6 +668,8 @@
StringArgument trustStorePath = null;
StringArgument trustStorePassword = null;
StringArgument vlvDescriptor = null;
+ StringArgument effectiveRightsUser = null;
+ StringArgument effectiveRightsAttrs = null;
// Create the command-line argument parser for use with this program.
@@ -872,6 +871,21 @@
"{controloid[:criticality[:value|::b64value|:<fileurl]]}",
null, null, MSGID_DESCRIPTION_CONTROLS);
argParser.addArgument(controlStr);
+ effectiveRightsUser =
+ new StringArgument("effectiveRightsUser",
+ OPTION_SHORT_EFFECTIVERIGHTSUSER,
+ OPTION_LONG_EFFECTIVERIGHTSUSER, false, false, true,
+ "{authzid}", null, null,
+ MSGID_DESCRIPTION_EFFECTIVERIGHTS_USER );
+ argParser.addArgument(effectiveRightsUser);
+
+ effectiveRightsAttrs =
+ new StringArgument("effectiveRightsAttrs",
+ OPTION_SHORT_EFFECTIVERIGHTSATTR,
+ OPTION_LONG_EFFECTIVERIGHTSATTR, false, true, true,
+ "{attribute}", null, null,
+ MSGID_DESCRIPTION_EFFECTIVERIGHTS_ATTR );
+ argParser.addArgument(effectiveRightsAttrs);
version = new IntegerArgument("version", 'V', "version", false, false,
true, "{version}", 3, null,
@@ -1157,6 +1171,34 @@
}
}
+ if(effectiveRightsUser.isPresent()) {
+ String authzID=effectiveRightsUser.getValue();
+ if (!authzID.startsWith("dn:")) {
+ int msgID = MSGID_EFFECTIVERIGHTS_INVALID_AUTHZID;
+ String message = getMessage(msgID, authzID);
+ err.println(wrapText(message, MAX_LINE_WIDTH));
+ err.println(argParser.getUsage());
+ return 1;
+ }
+ ASN1OctetString v=null;
+ ASN1OctetString effectiveRightsUserVal =
+ new ASN1OctetString(authzID);
+ ASN1Sequence sequence=null;
+ ArrayList<ASN1Element> attrElements =
+ new ArrayList<ASN1Element>();
+ for(String a : effectiveRightsAttrs.getValues())
+ attrElements.add(new ASN1OctetString(a));
+ ASN1Sequence attrSeq=new ASN1Sequence(attrElements);
+ ArrayList<ASN1Element> elements = new ArrayList<ASN1Element>(2);
+ elements.add(effectiveRightsUserVal);
+ elements.add(attrSeq);
+ sequence= new ASN1Sequence(elements);
+ LDAPControl effectiveRightsControl =
+ new LDAPControl(OID_GET_EFFECTIVE_RIGHTS, false,
+ new ASN1OctetString(sequence.encode()));
+ searchOptions.getControls().add(effectiveRightsControl);
+ }
+
if (proxyAuthzID.isPresent())
{
ASN1OctetString proxyValue = new ASN1OctetString(proxyAuthzID.getValue());
diff --git a/opends/src/server/org/opends/server/tools/LDAPToolUtils.java b/opends/src/server/org/opends/server/tools/LDAPToolUtils.java
index ca40b69..3a54aa0 100644
--- a/opends/src/server/org/opends/server/tools/LDAPToolUtils.java
+++ b/opends/src/server/org/opends/server/tools/LDAPToolUtils.java
@@ -121,6 +121,11 @@
{
controlOID = OID_VIRTUAL_ATTRS_ONLY;
}
+ else if(lowerOID.equals("effectiverights") ||
+ lowerOID.equals("geteffectiverights"))
+ {
+ controlOID = OID_GET_EFFECTIVE_RIGHTS;
+ }
if (idx < 0)
{
diff --git a/opends/src/server/org/opends/server/tools/ToolConstants.java b/opends/src/server/org/opends/server/tools/ToolConstants.java
index 613f0b3..92ba45a 100644
--- a/opends/src/server/org/opends/server/tools/ToolConstants.java
+++ b/opends/src/server/org/opends/server/tools/ToolConstants.java
@@ -457,5 +457,28 @@
* displayed in usage information.
*/
public static final String OPTION_VALUE_SASLOPTION = "{name=value}";
+
+ /**
+ * The value for the short option geteffectiverights control authzid.
+ */
+ public static final char OPTION_SHORT_EFFECTIVERIGHTSUSER = 'g';
+
+ /**
+ * The value for the long option geteffectiverights control authzid.
+ */
+ public static final String OPTION_LONG_EFFECTIVERIGHTSUSER =
+ "getEffectiveRightsAuthzid";
+
+ /**
+ * The value for the short option geteffectiveights control attributes.
+ */
+ public static final char OPTION_SHORT_EFFECTIVERIGHTSATTR = 'e';
+
+ /**
+ * The value for the long option geteffectiverights control specific
+ * attribute list.
+ */
+ public static final String OPTION_LONG_EFFECTIVERIGHTSATTR =
+ "getEffectiveRightsAttribute";
}
diff --git a/opends/src/server/org/opends/server/util/ServerConstants.java b/opends/src/server/org/opends/server/util/ServerConstants.java
index 79cc242..b8b39f9 100644
--- a/opends/src/server/org/opends/server/util/ServerConstants.java
+++ b/opends/src/server/org/opends/server/util/ServerConstants.java
@@ -1786,6 +1786,12 @@
public static final String OID_PROXIED_AUTH_V2 = "2.16.840.1.113730.3.4.18";
+ /**
+ * The OID for the get effective rights control.
+ */
+ public static final String OID_GET_EFFECTIVE_RIGHTS =
+ "1.3.6.1.4.1.42.2.27.9.5.2";
+
/**
* The OID for the real attributes only control.
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/authorization/dseecompat/GetEffectiveRightsTestCase.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/authorization/dseecompat/GetEffectiveRightsTestCase.java
new file mode 100644
index 0000000..4ae744c
--- /dev/null
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/authorization/dseecompat/GetEffectiveRightsTestCase.java
@@ -0,0 +1,667 @@
+/*
+ * 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.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+import org.testng.annotations.BeforeMethod;
+import static org.opends.server.config.ConfigConstants.*;
+import org.testng.Assert;
+import static org.testng.Assert.assertEquals;
+import org.opends.server.TestCaseUtils;
+import org.opends.server.types.AttributeType;
+import org.opends.server.types.Attribute;
+import org.opends.server.tools.LDAPSearch;
+import org.opends.server.tools.LDAPModify;
+import static org.opends.server.util.ServerConstants.OID_GET_EFFECTIVE_RIGHTS;
+import static org.opends.server.util.ServerConstants.EOL;
+
+import java.io.*;
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.List;
+
+public class GetEffectiveRightsTestCase {
+ private static ByteArrayOutputStream oStream = new ByteArrayOutputStream();
+ private static final String DIR_MGR_DN = "cn=Directory Manager";
+ private static final String PWD = "password";
+ private static final String filter = "(objectclass=*)";
+ private static final String base="uid=user.3,ou=People,o=test";
+ private static final String user1="uid=user.1,ou=People,o=test";
+ private static final String superUser="uid=superuser,ou=admins,o=test";
+ private static final String[] attrList={"pager", "fax"};
+ private static final String[] memberAttrList={"member"};
+ private static final String entryLevel = "aclRights;entryLevel";
+ private static final String attributeLevel = "aclRights;attributeLevel;";
+
+ //Various results for entryLevel searches.
+ private static final
+ String bypassRights = "add:1,delete:1,read:1,write:1,proxy:1";
+
+ private static final
+ String rRights = "add:0,delete:0,read:1,write:0,proxy:0";
+
+ private static final
+ String arRights = "add:1,delete:0,read:1,write:0,proxy:0";
+
+ private static final
+ String adrRights = "add:1,delete:1,read:1,write:0,proxy:0";
+
+ private static final
+ String adrwRights = "add:1,delete:1,read:1,write:1,proxy:0";
+
+ private static final
+ String allRights = "add:1,delete:1,read:1,write:1,proxy:1";
+
+ private static final
+ String ACCESS_HANDLER_DN = "cn=Access Control Handler,cn=config";
+
+ //Results for attributeLevel searches
+ private static final String srwMailAttrRights =
+ "search:1,read:1,compare:0,write:1," +
+ "selfwrite_add:1,selfwrite_delete:1,proxy:0";
+
+ private static final String srDescrptionAttrRights =
+ "search:1,read:1,compare:0,write:0," +
+ "selfwrite_add:0,selfwrite_delete:0,proxy:0";
+
+ private static final String srxFaxAttrRights =
+ "search:1,read:1,compare:0,write:?," +
+ "selfwrite_add:0,selfwrite_delete:0,proxy:0";
+
+ private static final String srPagerAttrRights =
+ "search:1,read:1,compare:0,write:0," +
+ "selfwrite_add:0,selfwrite_delete:0,proxy:0";
+
+ private static final String selfWriteAttrRights =
+ "search:0,read:0,compare:0,write:0," +
+ "selfwrite_add:1,selfwrite_delete:1,proxy:0";
+
+ //ACI needed to search/read aciRights attribute.
+ private static final
+ String aclRightsAci = "(targetattr=\"aclRights\")" +
+ "(version 3.0;acl \"aclRights access\";" +
+ "allow (search, read) " +
+ "userdn=\"ldap:///uid=superuser,ou=admins,o=test\";)";
+
+ //General ACI superuser to search/read.
+
+ private static final
+ String readSearchAci = "(targetattr=\"*\")" +
+ "(version 3.0;acl \"read/search access\";" +
+ "allow (search, read) " +
+ "userdn=\"ldap:///uid=superuser,ou=admins,o=test\";)";
+
+ //General ACI for anonymous test.
+ private static final
+ String readSearchAnonAci = "(targetattr=\"*\")" +
+ "(version 3.0;acl \"anonymous read/search access\";" +
+ "allow (search, read) " +
+ "userdn=\"ldap:///anyone\";)";
+
+ //Test ACIs.
+ private static final
+ String addAci = "(version 3.0;acl \"add access\";" +
+ "allow (add) " +
+ "userdn=\"ldap:///uid=superuser,ou=admins,o=test\";)";
+
+ private static final
+ String delAci = "(version 3.0;acl \"delete access\";" +
+ "allow (delete) " +
+ "userdn=\"ldap:///uid=superuser,ou=admins,o=test\";)";
+
+ private static final
+ String writeAci = "(version 3.0;acl \"write access\";" +
+ "allow (write) " +
+ "userdn=\"ldap:///uid=superuser,ou=admins,o=test\";)";
+
+ private static final
+ String writeMailAci = "(targetattr=\"mail\")" +
+ "(version 3.0;acl \"write mail access\";" +
+ "allow (write) " +
+ "userdn=\"ldap:///uid=superuser,ou=admins,o=test\";)";
+
+ private static final
+ String proxyAci = "(version 3.0;acl \"proxy access\";" +
+ "allow (proxy) " +
+ "userdn=\"ldap:///uid=superuser,ou=admins,o=test\";)";
+
+ private static final
+ String faxTargAttrAci =
+ "(targattrfilters=\"add=fax:(fax=*), del=fax:(fax=*)\")" +
+ "(version 3.0;acl \"allow write fax\";" +
+ "allow (write)" +
+ "userdn=\"ldap:///uid=superuser,ou=admins,o=test\";)";
+
+ private static final
+ String pagerTargAttrAci =
+ "(targattrfilters=\"add=pager:(pager=*), del=pager:(pager=*)\")" +
+ "(version 3.0;acl \"deny write pager\";" +
+ "deny (write)" +
+ "userdn=\"ldap:///uid=superuser,ou=admins,o=test\";)";
+
+ private static final
+ String selfWriteAci = "(targetattr=\"member\")" +
+ "(version 3.0; acl \"selfwrite\"; allow(selfwrite)" + "" +
+ "userdn=\"ldap:///uid=user.1,ou=People,o=test\";)";
+
+ @BeforeClass
+ public void setupClass() throws Exception {
+ TestCaseUtils.startServer();
+ deleteAttrFromEntry(ACCESS_HANDLER_DN, ATTR_AUTHZ_GLOBAL_ACI);
+ addEntries();
+ }
+
+ @BeforeMethod
+ public void removeAcis() throws Exception {
+ deleteAttrFromEntry("ou=People,o=test", "aci");
+ }
+
+ /**
+ * Test entry level using the -g param and anonymous dn as the authzid.
+ * @throws Exception If the search result is empty or a right string
+ * doesn't match the expected value.
+ */
+ @Test()
+ public void testAnonEntryLevelParams() throws Exception {
+ String aciLdif=makeAddAciLdif("aci", "ou=People,o=test", readSearchAnonAci);
+ modEntries(aciLdif, DIR_MGR_DN, PWD);
+ String userResults =
+ LDAPSearchParams(DIR_MGR_DN, PWD, null, "dn:", null,
+ base, filter, "aclRights");
+ Assert.assertFalse(userResults.equals(""));
+ HashMap<String, String> attrMap=getAttrMap(userResults);
+ checkEntryLevel(attrMap, rRights);
+
+ }
+
+ /**
+ * Test entry level using the -g param and superuser dn as the authzid.
+ * @throws Exception If the search result is empty or a right string
+ * doesn't match the expected value.
+ */
+ @Test()
+ public void testSuEntryLevelParams() throws Exception {
+ String aciLdif=makeAddAciLdif("aci", "ou=People,o=test", aclRightsAci);
+ modEntries(aciLdif, DIR_MGR_DN, PWD);
+ aciLdif=makeAddAciLdif("aci", "ou=People,o=test", readSearchAci);
+ modEntries(aciLdif, DIR_MGR_DN, PWD);
+ String userResults =
+ LDAPSearchParams(superUser, PWD, null, "dn: " + superUser, null,
+ base, filter, "aclRights");
+ Assert.assertFalse(userResults.equals(""));
+ HashMap<String, String> attrMap=getAttrMap(userResults);
+ checkEntryLevel(attrMap, rRights);
+ aciLdif=makeAddAciLdif("aci", "ou=People,o=test", addAci);
+ modEntries(aciLdif, DIR_MGR_DN, PWD);
+ userResults =
+ LDAPSearchParams(superUser, PWD, null, "dn: " + superUser, null,
+ base, filter, "aclRights");
+ Assert.assertFalse(userResults.equals(""));
+ attrMap=getAttrMap(userResults);
+ checkEntryLevel(attrMap, arRights);
+ aciLdif=makeAddAciLdif("aci", "ou=People,o=test", delAci);
+ modEntries(aciLdif, DIR_MGR_DN, PWD);
+ userResults =
+ LDAPSearchParams(superUser, PWD, null, "dn: " + superUser, null,
+ base, filter, "aclRights");
+ Assert.assertFalse(userResults.equals(""));
+ attrMap=getAttrMap(userResults);
+ checkEntryLevel(attrMap, adrRights);
+ aciLdif=makeAddAciLdif("aci", "ou=People,o=test", writeAci);
+ modEntries(aciLdif, DIR_MGR_DN, PWD);
+ userResults =
+ LDAPSearchParams(superUser, PWD, null, "dn: " + superUser, null,
+ base, filter, "aclRights");
+ Assert.assertFalse(userResults.equals(""));
+ attrMap=getAttrMap(userResults);
+ checkEntryLevel(attrMap, adrwRights);
+ aciLdif=makeAddAciLdif("aci", "ou=People,o=test", proxyAci);
+ modEntries(aciLdif, DIR_MGR_DN, PWD);
+ userResults =
+ LDAPSearchParams(superUser, PWD, null, "dn: " + superUser, null,
+ base, filter, "aclRights");
+ Assert.assertFalse(userResults.equals(""));
+ attrMap=getAttrMap(userResults);
+ checkEntryLevel(attrMap, allRights);
+ }
+
+ /**
+ * Test entry level using the control OID only (no authzid specified).
+ * Should use the bound user (superuser) as the authzid.
+ * @throws Exception If the search result is empty or a right string
+ * doesn't match the expected value.
+ */
+ @Test()
+ public void testSuEntryLevelCtrl() throws Exception {
+ String aciLdif=makeAddAciLdif("aci", "ou=People,o=test", aclRightsAci);
+ modEntries(aciLdif, DIR_MGR_DN, PWD);
+ aciLdif=makeAddAciLdif("aci", "ou=People,o=test", readSearchAci);
+ modEntries(aciLdif, DIR_MGR_DN, PWD);
+ String userResults =
+ LDAPSearchCtrl(superUser, PWD, null, OID_GET_EFFECTIVE_RIGHTS,
+ base, filter, "aclRights");
+ Assert.assertFalse(userResults.equals(""));
+ HashMap<String, String> attrMap=getAttrMap(userResults);
+ checkEntryLevel(attrMap, rRights);
+ aciLdif=makeAddAciLdif("aci", "ou=People,o=test", addAci);
+ modEntries(aciLdif, DIR_MGR_DN, PWD);
+ userResults =
+ LDAPSearchCtrl(superUser, PWD, null, OID_GET_EFFECTIVE_RIGHTS,
+ base, filter, "aclRights");
+ Assert.assertFalse(userResults.equals(""));
+ attrMap=getAttrMap(userResults);
+ checkEntryLevel(attrMap, arRights);
+ aciLdif=makeAddAciLdif("aci", "ou=People,o=test", delAci);
+ modEntries(aciLdif, DIR_MGR_DN, PWD);
+ userResults =
+ LDAPSearchCtrl(superUser, PWD, null, OID_GET_EFFECTIVE_RIGHTS,
+ base, filter, "aclRights");
+ Assert.assertFalse(userResults.equals(""));
+ attrMap=getAttrMap(userResults);
+ checkEntryLevel(attrMap, adrRights);
+ aciLdif=makeAddAciLdif("aci", "ou=People,o=test", writeAci);
+ modEntries(aciLdif, DIR_MGR_DN, PWD);
+ userResults =
+ LDAPSearchCtrl(superUser, PWD, null, OID_GET_EFFECTIVE_RIGHTS,
+ base, filter, "aclRights");
+ Assert.assertFalse(userResults.equals(""));
+ attrMap=getAttrMap(userResults);
+ checkEntryLevel(attrMap, adrwRights);
+ aciLdif=makeAddAciLdif("aci", "ou=People,o=test", proxyAci);
+ modEntries(aciLdif, DIR_MGR_DN, PWD);
+ userResults =
+ LDAPSearchCtrl(superUser, PWD, null, OID_GET_EFFECTIVE_RIGHTS,
+ base, filter, "aclRights");
+ Assert.assertFalse(userResults.equals(""));
+ attrMap=getAttrMap(userResults);
+ checkEntryLevel(attrMap, allRights);
+ }
+
+ /**
+ * Test entry level using the control OID only -- bound as a bypass user.
+ * Should use the bound user (DIR_MGR) as the authzid.
+ * @throws Exception If the search result is empty or a right string
+ * doesn't match the expected value.
+ */
+ @Test()
+ public void testBypassEntryLevelCtrl() throws Exception {
+ String userResults =
+ LDAPSearchCtrl(DIR_MGR_DN, PWD, null, OID_GET_EFFECTIVE_RIGHTS,
+ base, filter, "aclRights");
+ Assert.assertFalse(userResults.equals(""));
+ HashMap<String, String> attrMap=getAttrMap(userResults);
+ checkEntryLevel(attrMap, bypassRights);
+ }
+
+ /**
+ * Test attribute level using the -g param and superuser dn as the authzid.
+ * The attributes used are mail and description. Mail should show write
+ * access allowed, description should show write access not allowed.
+ * @throws Exception If the search result is empty or a right string
+ * doesn't match the expected value.
+ */
+ @Test()
+ public void testSuAttrLevelParams() throws Exception {
+ String aciLdif=makeAddAciLdif("aci", "ou=People,o=test", aclRightsAci);
+ modEntries(aciLdif, DIR_MGR_DN, PWD);
+ aciLdif=makeAddAciLdif("aci", "ou=People,o=test", readSearchAci);
+ modEntries(aciLdif, DIR_MGR_DN, PWD);
+ aciLdif=makeAddAciLdif("aci", "ou=People,o=test", writeMailAci);
+ modEntries(aciLdif, DIR_MGR_DN, PWD);
+ String userResults =
+ LDAPSearchParams(superUser, PWD, null, "dn: " + superUser, null,
+ base, filter, "aclRights mail description");
+ Assert.assertFalse(userResults.equals(""));
+ HashMap<String, String> attrMap=getAttrMap(userResults);
+ checkAttributeLevel(attrMap, "mail", srwMailAttrRights);
+ checkAttributeLevel(attrMap, "description", srDescrptionAttrRights);
+ }
+
+ /**
+ * Test attribute level using the -g param and superuser dn as the authzid and
+ * the -e option using pager and fax.
+ * The attributes used are mail and description. Mail should show write
+ * access allowed, description should show write access not allowed.
+ *
+ * @throws Exception If the search result is empty or a right string
+ * doesn't match the expected value.
+ */
+@Test()
+public void testSuAttrLevelParams2() throws Exception {
+ String aciLdif=makeAddAciLdif("aci", "ou=People,o=test", aclRightsAci);
+ modEntries(aciLdif, DIR_MGR_DN, PWD);
+ aciLdif=makeAddAciLdif("aci", "ou=People,o=test", readSearchAci);
+ modEntries(aciLdif, DIR_MGR_DN, PWD);
+ aciLdif=makeAddAciLdif("aci", "ou=People,o=test", writeMailAci);
+ modEntries(aciLdif, DIR_MGR_DN, PWD);
+ aciLdif=makeAddAciLdif("aci", "ou=People,o=test", faxTargAttrAci);
+ modEntries(aciLdif, DIR_MGR_DN, PWD);
+ aciLdif=makeAddAciLdif("aci", "ou=People,o=test", pagerTargAttrAci);
+ modEntries(aciLdif, DIR_MGR_DN, PWD);
+ String userResults =
+ LDAPSearchParams(superUser, PWD, null, "dn: " + superUser, attrList,
+ base, filter, "aclRights mail description");
+ Assert.assertFalse(userResults.equals(""));
+ HashMap<String, String> attrMap=getAttrMap(userResults);
+ checkAttributeLevel(attrMap, "mail", srwMailAttrRights);
+ checkAttributeLevel(attrMap, "description", srDescrptionAttrRights);
+ checkAttributeLevel(attrMap, "fax", srxFaxAttrRights);
+ checkAttributeLevel(attrMap, "pager", srPagerAttrRights);
+}
+
+ /**
+ * Test selfwrite attribute level using the -g param and user.1 dn as the
+ * authzid and the -e option member.
+ * The attributes used are mail and description. Mail should show write
+ * access allowed, description should show write access not allowed.
+ *
+ * @throws Exception If the search result is empty or a right string
+ * doesn't match the expected value.
+ */
+@Test()
+public void testSuAttrLevelParams3() throws Exception {
+ String aciLdif=makeAddAciLdif("aci", "ou=People,o=test", aclRightsAci);
+ modEntries(aciLdif, DIR_MGR_DN, PWD);
+ aciLdif=makeAddAciLdif("aci", "ou=People,o=test", readSearchAci);
+ modEntries(aciLdif, DIR_MGR_DN, PWD);
+ aciLdif=makeAddAciLdif("aci", "ou=People,o=test", selfWriteAci);
+ modEntries(aciLdif, DIR_MGR_DN, PWD);
+ String userResults =
+ LDAPSearchParams(superUser, PWD, null, "dn: " + user1, memberAttrList,
+ base, filter, "aclRights");
+ Assert.assertFalse(userResults.equals(""));
+ HashMap<String, String> attrMap=getAttrMap(userResults);
+ checkAttributeLevel(attrMap, "member", selfWriteAttrRights);
+}
+
+ private void
+ checkAttributeLevel(HashMap<String, String> attrMap, String attr,
+ String reqRightsStr) throws Exception {
+ String attrType=attributeLevel + attr;
+ String retRightsStr=attrMap.get(attrType);
+ Assert.assertTrue(retRightsStr.equals(reqRightsStr));
+ }
+
+ private void
+ checkEntryLevel(HashMap<String, String> attrMap, String reqRightsStr)
+ throws Exception {
+ String retRightsStr=attrMap.get(entryLevel);
+ Assert.assertTrue(retRightsStr.equals(reqRightsStr));
+ }
+
+ private HashMap<String, String>
+ getAttrMap(String resultString) throws Exception {
+ StringReader r=new StringReader(resultString);
+ BufferedReader br=new BufferedReader(r);
+ HashMap<String, String> attrMap = new HashMap<String,String>();
+ try {
+ while(true) {
+ String s = br.readLine();
+ if(s == null)
+ break;
+ if(s.startsWith("dn:"))
+ continue;
+ String[] a=s.split(": ");
+ if(a.length != 2)
+ break;
+ attrMap.put(a[0],a[1]);
+ }
+ } catch (IOException e) {
+ Assert.assertEquals(0, 1, e.getMessage());
+ }
+ return attrMap;
+ }
+
+
+ private void addEntries() throws Exception {
+ TestCaseUtils.initializeTestBackend(true);
+ TestCaseUtils.addEntries(
+ "dn: ou=People,o=test",
+ "objectClass: top",
+ "objectClass: organizationalUnit",
+ "ou: People",
+ "",
+ "dn: ou=admins,o=test",
+ "objectClass: top",
+ "objectClass: organizationalUnit",
+ "ou: admins",
+ "",
+ "dn: cn=group,ou=People,o=test",
+ "objectclass: top",
+ "objectclass: groupOfNames",
+ "cn: group",
+ "member: uid=user.1,ou=People,o=test",
+ "",
+ "dn: uid=superuser,ou=admins,o=test",
+ "objectClass: top",
+ "objectClass: person",
+ "objectClass: organizationalPerson",
+ "objectClass: inetOrgPerson",
+ "uid: superuser",
+ "givenName: superuser",
+ "sn: 1",
+ "cn: User 1",
+ "userPassword: password",
+ "ds-privilege-name: proxied-auth",
+ "",
+ "dn: uid=proxyuser,ou=admins,o=test",
+ "objectClass: top",
+ "objectClass: person",
+ "objectClass: organizationalPerson",
+ "objectClass: inetOrgPerson",
+ "uid: proxyuser",
+ "givenName: proxyuser",
+ "sn: 1",
+ "cn: User 1",
+ "userPassword: password",
+ "",
+ "dn: uid=user.1,ou=People,o=test",
+ "objectClass: top",
+ "objectClass: person",
+ "objectClass: organizationalPerson",
+ "objectClass: inetOrgPerson",
+ "uid: user.1",
+ "givenName: User",
+ "sn: 1",
+ "cn: User 1",
+ "userPassword: password",
+ "",
+ "dn: uid=user.2,ou=People,o=test",
+ "objectClass: top",
+ "objectClass: person",
+ "objectClass: organizationalPerson",
+ "objectClass: inetOrgPerson",
+ "uid: user.2",
+ "givenName: User",
+ "sn: 2",
+ "cn: User 2",
+ "userPassword: password",
+ "",
+ "dn: uid=user.3,ou=People,o=test",
+ "objectClass: top",
+ "objectClass: person",
+ "objectClass: organizationalPerson",
+ "objectClass: inetOrgPerson",
+ "uid: user.3",
+ "givenName: User",
+ "sn: 3",
+ "mail: user.3@test",
+ "description: user.3 description",
+ "cn: User 3",
+ "userPassword: password");
+ }
+
+ private String LDAPSearchCtrl(String bindDn, String bindPassword,
+ String proxyDN, String controlStr,
+ String base, String filter, String attr)
+ throws Exception {
+ ArrayList<String> argList=new ArrayList<String>(20);
+ argList.add("-h");
+ argList.add("127.0.0.1");
+ argList.add("-p");
+ argList.add(String.valueOf(TestCaseUtils.getServerLdapPort()));
+ argList.add("-D");
+ argList.add(bindDn);
+ argList.add("-w");
+ argList.add(bindPassword);
+ argList.add("-T");
+ if(proxyDN != null) {
+ argList.add("-Y");
+ argList.add("dn:" + proxyDN);
+ }
+ if(controlStr != null) {
+ argList.add("-J");
+ argList.add(controlStr);
+ }
+ argList.add("-b");
+ argList.add(base);
+ argList.add("-s");
+ argList.add("sub");
+ argList.add(filter);
+ String[] attrs=attr.split("\\s+");
+ for(String a : attrs)
+ argList.add(a);
+ String[] args = new String[argList.size()];
+ oStream.reset();
+ int retVal =
+ LDAPSearch.mainSearch(argList.toArray(args), false, oStream, oStream);
+ Assert.assertEquals(0, retVal, "Returned error: " + oStream.toString());
+ return oStream.toString();
+ }
+
+ private String LDAPSearchParams(String bindDn, String bindPassword,
+ String proxyDN, String authzid, String[] attrList,
+ String base, String filter ,String attr)
+ throws Exception {
+ ArrayList<String> argList=new ArrayList<String>(20);
+ argList.add("-h");
+ argList.add("127.0.0.1");
+ argList.add("-p");
+ argList.add(String.valueOf(TestCaseUtils.getServerLdapPort()));
+ argList.add("-D");
+ argList.add(bindDn);
+ argList.add("-w");
+ argList.add(bindPassword);
+ argList.add("-T");
+ if(proxyDN != null) {
+ argList.add("-Y");
+ argList.add("dn:" + proxyDN);
+ }
+ if(authzid != null) {
+ argList.add("-g");
+ argList.add(authzid);
+ }
+ if(attrList != null) {
+ for(String a : attrList) {
+ argList.add("-e");
+ argList.add(a);
+ }
+ }
+ argList.add("-b");
+ argList.add(base);
+ argList.add("-s");
+ argList.add("sub");
+ argList.add(filter);
+ String[] attrs=attr.split("\\s+");
+ for(String a : attrs)
+ argList.add(a);
+ String[] args = new String[argList.size()];
+ oStream.reset();
+ int retVal =
+ LDAPSearch.mainSearch(argList.toArray(args), false, oStream, oStream);
+ Assert.assertEquals(0, retVal, "Returned error: " + oStream.toString());
+ return oStream.toString();
+ }
+
+
+ private void modEntries(String ldif, String bindDn, String bindPassword)
+ throws Exception {
+ File tempFile = getTemporaryLdifFile();
+ TestCaseUtils.writeFile(tempFile, ldif);
+ ArrayList<String> argList=new ArrayList<String>(20);
+ argList.add("-h");
+ argList.add("127.0.0.1");
+ argList.add("-p");
+ argList.add(String.valueOf(TestCaseUtils.getServerLdapPort()));
+ argList.add("-D");
+ argList.add(bindDn);
+ argList.add("-w");
+ argList.add(bindPassword);
+ argList.add("-f");
+ argList.add(tempFile.getAbsolutePath());
+ String[] args = new String[argList.size()];
+ ldapModify(argList.toArray(args));
+ }
+
+
+ private void ldapModify(String[] args) {
+ oStream.reset();
+ LDAPModify.mainModify(args, false, oStream, oStream);
+ }
+
+ private void deleteAttrFromEntry(String dn, String attr)
+ throws Exception {
+ StringBuilder ldif = new StringBuilder();
+ ldif.append(TestCaseUtils.makeLdif(
+ "dn: " + dn,
+ "changetype: modify",
+ "delete: " + attr));
+ modEntries(ldif.toString(), DIR_MGR_DN, PWD);
+ }
+
+ private static ThreadLocal<Map<String,File>> tempLdifFile =
+ new ThreadLocal<Map<String,File>>();
+
+ private File getTemporaryLdifFile() throws IOException {
+ Map<String,File> tempFilesForThisThread = tempLdifFile.get();
+ if (tempFilesForThisThread == null) {
+ tempFilesForThisThread = new HashMap<String,File>();
+ tempLdifFile.set(tempFilesForThisThread);
+ }
+ File tempFile = tempFilesForThisThread.get("effectiverights-tests");
+ if (tempFile == null) {
+ tempFile = File.createTempFile("effectiverights-tests", ".ldif");
+ tempFile.deleteOnExit();
+ tempFilesForThisThread.put("effectiverights-tests", tempFile);
+ }
+ return tempFile;
+ }
+
+ private static String makeAddAciLdif(String attr, String dn, String... acis) {
+ StringBuilder ldif = new StringBuilder();
+ ldif.append("dn: ").append(dn).append(EOL);
+ ldif.append("changetype: modify").append(EOL);
+ ldif.append("add: ").append(attr).append(EOL);
+ for(String aci : acis)
+ ldif.append(attr).append(":").append(aci).append(EOL);
+ ldif.append(EOL);
+ return ldif.toString();
+ }
+}
--
Gitblit v1.10.0