From 898c594c58fc6f019e732c46d14fda4cb8be05b5 Mon Sep 17 00:00:00 2001
From: matthew_swift <matthew_swift@localhost>
Date: Thu, 23 Apr 2009 18:44:44 +0000
Subject: [PATCH] Fix formatting.
---
opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciHandler.java | 2564 +++++++++++++++++++++++++++++++++-------------------------
1 files changed, 1,455 insertions(+), 1,109 deletions(-)
diff --git a/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciHandler.java b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciHandler.java
index ee94d81..8e8d4c7 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciHandler.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciHandler.java
@@ -24,60 +24,101 @@
*
* Copyright 2008-2009 Sun Microsystems, Inc.
*/
-
package org.opends.server.authorization.dseecompat;
-import org.opends.messages.Message;
-import static org.opends.server.authorization.dseecompat.Aci.*;
-import static org.opends.server.config.ConfigConstants.ATTR_AUTHZ_GLOBAL_ACI;
-import static org.opends.server.loggers.ErrorLogger.logError;
-import static org.opends.server.loggers.debug.DebugLogger.debugEnabled;
-import static org.opends.server.loggers.debug.DebugLogger.getTracer;
+
+
import static org.opends.messages.AccessControlMessages.*;
-import static org.opends.server.schema.SchemaConstants.SYNTAX_DN_OID;
+import static org.opends.server.authorization.dseecompat.Aci.*;
+import static org.opends.server.config.ConfigConstants.*;
+import static org.opends.server.loggers.ErrorLogger.*;
+import static org.opends.server.loggers.debug.DebugLogger.*;
+import static org.opends.server.schema.SchemaConstants.*;
import static org.opends.server.util.ServerConstants.*;
-import static org.opends.server.util.StaticUtils.toLowerCase;
-import java.util.*;
+import static org.opends.server.util.StaticUtils.*;
+
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.SortedSet;
import java.util.concurrent.locks.Lock;
+
+import org.opends.messages.Message;
import org.opends.server.admin.std.server.DseeCompatAccessControlHandlerCfg;
import org.opends.server.api.AccessControlHandler;
import org.opends.server.api.ClientConnection;
+import org.opends.server.backends.jeb.EntryContainer;
import org.opends.server.config.ConfigException;
-import org.opends.server.core.*;
+import org.opends.server.controls.GetEffectiveRightsRequestControl;
+import org.opends.server.core.DirectoryServer;
+import org.opends.server.core.ExtendedOperation;
+import org.opends.server.core.SearchOperation;
import org.opends.server.loggers.debug.DebugTracer;
import org.opends.server.protocols.internal.InternalClientConnection;
import org.opends.server.protocols.internal.InternalSearchOperation;
import org.opends.server.protocols.ldap.LDAPControl;
-import org.opends.server.types.*;
+import org.opends.server.types.Attribute;
+import org.opends.server.types.AttributeBuilder;
+import org.opends.server.types.AttributeType;
+import org.opends.server.types.AttributeValue;
+import org.opends.server.types.AttributeValues;
+import org.opends.server.types.AuthenticationInfo;
+import org.opends.server.types.Control;
+import org.opends.server.types.DN;
+import org.opends.server.types.DebugLogLevel;
+import org.opends.server.types.DereferencePolicy;
+import org.opends.server.types.DirectoryException;
+import org.opends.server.types.Entry;
+import org.opends.server.types.InitializationException;
+import org.opends.server.types.LockManager;
+import org.opends.server.types.Modification;
+import org.opends.server.types.ModificationType;
+import org.opends.server.types.Operation;
+import org.opends.server.types.Privilege;
+import org.opends.server.types.RDN;
+import org.opends.server.types.SearchFilter;
+import org.opends.server.types.SearchResultEntry;
+import org.opends.server.types.SearchResultReference;
+import org.opends.server.types.SearchScope;
import org.opends.server.workflowelement.localbackend.*;
-import org.opends.server.controls.GetEffectiveRightsRequestControl;
-import org.opends.server.backends.jeb.EntryContainer;
+
/**
- * The AciHandler class performs the main processing for the dseecompat package.
+ * The AciHandler class performs the main processing for the dseecompat
+ * package.
*/
-public class AciHandler
- extends AccessControlHandler<DseeCompatAccessControlHandlerCfg>
+public class AciHandler extends
+ AccessControlHandler<DseeCompatAccessControlHandlerCfg>
{
/**
- * The tracer object for the debug logger.
+ * 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.
*/
- private static final DebugTracer TRACER = getTracer();
-
+ public static final String ALL_ATTRS_RESOURCE_ENTRY =
+ "allAttrsResourceEntry";
/**
- * The list that holds that ACIs keyed by the DN of the entry
- * holding the ACI.
+ * String used to indicate that the evaluating ACI had a all
+ * operational attributes targetattr match (targetattr="+").
*/
- private AciList aciList;
+ public static final String ALL_OP_ATTRS_MATCHED = "allOpAttrsMatched";
/**
- * The listener that handles ACI changes caused by LDAP operations, ACI
- * decode failure alert logging and backend initialization ACI list
- * adjustment.
+ * String used to indicate that the evaluating ACI had a all user
+ * attributes targetattr match (targetattr="*").
*/
- private AciListenerManager aciListenerMgr;
+ public static final String ALL_USER_ATTRS_MATCHED =
+ "allUserAttrsMatched";
+
+ /**
+ * String used to save the original authorization entry in an
+ * operation attachment if a proxied authorization control was seen.
+ */
+ public static final String ORIG_AUTH_ENTRY = "origAuthorizationEntry";
/**
* Attribute type corresponding to "aci" attribute.
@@ -85,122 +126,156 @@
static AttributeType aciType;
/**
- * Attribute type corresponding to global "ds-cfg-global-aci" attribute.
+ * Attribute type corresponding to global "ds-cfg-global-aci"
+ * attribute.
*/
static AttributeType globalAciType;
/**
* Attribute type corresponding to "debugsearchindex" attribute.
*/
- static AttributeType debugSearchIndex;
+ private static AttributeType debugSearchIndex;
+
+ /*
+ * DN corresponding to "debugsearchindex" attribute type.
+ */
+ private static DN debugSearchIndexDN;
/**
- * Attribute type corresponding to the "ref" attribute type. Used in the
- * search reference access check.
+ * Attribute type corresponding to the "ref" attribute type. Used in
+ * the search reference access check.
*/
- static AttributeType refAttrType;
-
- /*
- * DN corresponding to "debugsearchindex" attribute type.
- */
- static DN debugSearchIndexDN;
-
+ private static AttributeType refAttrType;
/**
- * String used to save the original authorization entry in an operation
- * attachment if a proxied authorization control was seen.
+ * The tracer object for the debug logger.
*/
- public static final String ORIG_AUTH_ENTRY="origAuthorizationEntry";
+ private static final DebugTracer TRACER = getTracer();
- /**
- * 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 final String ALL_ATTRS_RESOURCE_ENTRY = "allAttrsResourceEntry";
+ static
+ {
+ initStatics();
+ }
- /**
- * String used to indicate that the evaluating ACI had a all user attributes
- * targetattr match (targetattr="*").
- */
- public static final String ALL_USER_ATTRS_MATCHED = "allUserAttrsMatched";
- /**
- * String used to indicate that the evaluating ACI had a all operational
- * attributes targetattr match (targetattr="+").
- */
- public static final String ALL_OP_ATTRS_MATCHED = "allOpAttrsMatched";
- static {
- initStatics();
- }
-
- // We initialize these for each new AciHandler so that we can clear out
- // the stale references that can occur during an in-core restart.
+ // We initialize these for each new AciHandler so that we can clear
+ // out the stale references that can occur during an in-core restart.
private static void initStatics()
{
- if((aciType = DirectoryServer.getAttributeType("aci")) == null)
+ if ((aciType = DirectoryServer.getAttributeType("aci")) == null)
{
aciType = DirectoryServer.getDefaultAttributeType("aci");
}
- if((globalAciType =
- DirectoryServer.getAttributeType(ATTR_AUTHZ_GLOBAL_ACI)) == null)
+ if ((globalAciType =
+ DirectoryServer.getAttributeType(ATTR_AUTHZ_GLOBAL_ACI)) == null)
{
globalAciType =
- DirectoryServer.getDefaultAttributeType(ATTR_AUTHZ_GLOBAL_ACI);
+ DirectoryServer
+ .getDefaultAttributeType(ATTR_AUTHZ_GLOBAL_ACI);
}
- if((debugSearchIndex =
- DirectoryServer.
- getAttributeType(EntryContainer.ATTR_DEBUG_SEARCH_INDEX)) == null)
- {
- debugSearchIndex =
- DirectoryServer.
- getDefaultAttributeType(EntryContainer.ATTR_DEBUG_SEARCH_INDEX);
- }
+ if ((debugSearchIndex =
+ DirectoryServer
+ .getAttributeType(EntryContainer.ATTR_DEBUG_SEARCH_INDEX)) == null)
+ {
+ debugSearchIndex =
+ DirectoryServer
+ .getDefaultAttributeType(EntryContainer.ATTR_DEBUG_SEARCH_INDEX);
+ }
- if((refAttrType =
- DirectoryServer.
- getAttributeType(ATTR_REFERRAL_URL)) == null) {
- refAttrType =
- DirectoryServer.
- getDefaultAttributeType(ATTR_REFERRAL_URL);
- }
- try {
- debugSearchIndexDN=DN.decode("cn=debugsearch");
- } catch (DirectoryException ex) {
- //Should never happen.
- }
+ if ((refAttrType =
+ DirectoryServer.getAttributeType(ATTR_REFERRAL_URL)) == null)
+ {
+ refAttrType =
+ DirectoryServer.getDefaultAttributeType(ATTR_REFERRAL_URL);
+ }
+ try
+ {
+ debugSearchIndexDN = DN.decode("cn=debugsearch");
+ }
+ catch (DirectoryException ex)
+ {
+ // Should never happen.
+ }
}
+
+
+ /**
+ * The list that holds that ACIs keyed by the DN of the entry holding
+ * the ACI.
+ */
+ private AciList aciList;
+
+ /**
+ * The listener that handles ACI changes caused by LDAP operations,
+ * ACI decode failure alert logging and backend initialization ACI
+ * list adjustment.
+ */
+ private AciListenerManager aciListenerMgr;
+
+
+
/**
* Creates a new DSEE-compatible access control handler.
*/
public AciHandler()
{
- // No implementation required. All initialization should be done in the
- // intializeAccessControlHandler method.
+ // No implementation required. All initialization should be done in
+ // the intializeAccessControlHandler method.
}
- /**
- * {@inheritDoc}
+ /*
+ * TODO Rename this method. Needs to be changed in SearchOperation. I
+ * find the name of the filterEntry method to be misleading because it
+ * works on a search operation but has nothing to do with the search
+ * filter. Something like "removeDisallowedAttributes" would be
+ * clearer.
*/
- @Override()
- public void initializeAccessControlHandler(
- DseeCompatAccessControlHandlerCfg configuration)
- throws ConfigException, InitializationException
+
+ /**
+ * Checks access on each attribute in an entry. It removes those
+ * attributes that fail access check.
+ *
+ * @param operation
+ * The search operation class containing information to check
+ * access on.
+ * @param entry
+ * The entry containing the attributes.
+ * @return The entry to return minus filtered attributes.
+ */
+ @Override
+ public SearchResultEntry filterEntry(SearchOperation operation,
+ SearchResultEntry entry)
{
- initStatics();
- DN configurationDN=configuration.dn();
- aciList = new AciList(configurationDN);
- aciListenerMgr = new AciListenerManager(aciList, configurationDN);
- processGlobalAcis(configuration);
- processConfigAcis();
- DirectoryServer.registerSupportedControl(OID_GET_EFFECTIVE_RIGHTS);
+ AciLDAPOperationContainer operationContainer =
+ new AciLDAPOperationContainer(operation, (ACI_READ), entry);
+ // Proxy access check has already been done for this entry in the
+ // maySend method, set the seen flag to true to bypass any proxy
+ // check.
+ operationContainer.setSeenEntry(true);
+ SearchResultEntry returnEntry;
+ 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;
}
@@ -213,1086 +288,1357 @@
{
aciListenerMgr.finalizeListenerManager();
AciEffectiveRights.finalizeOnShutdown();
- DirectoryServer.deregisterSupportedControl(OID_GET_EFFECTIVE_RIGHTS);
+ DirectoryServer
+ .deregisterSupportedControl(OID_GET_EFFECTIVE_RIGHTS);
}
- /**
- * Process all global ACI attribute types found in the configuration
- * entry and adds them to that ACI list cache. It also logs messages about
- * the number of ACI attribute types added to the cache. This method is
- * called once at startup. It also will put the server into lockdown
- * mode if needed.
- *
- * @param configuration The config handler containing the ACI
- * configuration information.
- * @throws InitializationException If there is an error reading
- * the global ACIs from the configuration entry.
- */
- private void processGlobalAcis(
- DseeCompatAccessControlHandlerCfg configuration)
- throws InitializationException {
- SortedSet<Aci> globalAcis = configuration.getGlobalACI();
- try {
- if (globalAcis != null) {
- aciList.addAci(DN.nullDN(),globalAcis);
- Message message = INFO_ACI_ADD_LIST_GLOBAL_ACIS.get(
- Integer.toString(globalAcis.size()));
- logError(message);
- }
- } catch (Exception e) {
- if (debugEnabled())
- TRACER.debugCaught(DebugLogLevel.ERROR, e);
- Message message = INFO_ACI_HANDLER_FAIL_PROCESS_GLOBAL_ACI.
- get(String.valueOf(configuration.dn()));
- throw new InitializationException(message, e);
- }
- }
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public void initializeAccessControlHandler(
+ DseeCompatAccessControlHandlerCfg configuration)
+ throws ConfigException, InitializationException
+ {
+ initStatics();
+ DN configurationDN = configuration.dn();
+ aciList = new AciList(configurationDN);
+ aciListenerMgr = new AciListenerManager(aciList, configurationDN);
+ processGlobalAcis(configuration);
+ processConfigAcis();
+ DirectoryServer.registerSupportedControl(OID_GET_EFFECTIVE_RIGHTS);
+ }
- /**
- * Process all ACIs under the "cn=config" naming context and adds them to
- * the ACI list cache. It also logs messages about the number of ACIs added
- * to the cache. This method is called once at startup. It will put the
- * server in lockdown mode if needed.
- *
- * @throws InitializationException If there is an error searching for
- * the ACIs in the naming context.
- */
- private void processConfigAcis() throws InitializationException {
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isAllowed(DN entryDN, Operation op, Control control)
+ {
+ boolean ret;
+ if (!(ret = skipAccessCheck(op)))
+ {
+ Entry e = new Entry(entryDN, null, null, null);
+ AciLDAPOperationContainer operationContainer =
+ new AciLDAPOperationContainer(op, e, control,
+ (ACI_READ | ACI_CONTROL));
+ ret = accessAllowed(operationContainer);
+ }
+ if (control.getOID().equals(OID_PROXIED_AUTH_V2)
+ || control.getOID().equals(OID_PROXIED_AUTH_V1))
+ {
+ op.setAttachment(ORIG_AUTH_ENTRY, op.getAuthorizationEntry());
+ }
+ else if (control.getOID().equals(OID_GET_EFFECTIVE_RIGHTS))
+ {
try
{
- DN configDN=DN.decode("cn=config");
- LinkedHashSet<String> attrs = new LinkedHashSet<String>(1);
- attrs.add("aci");
- LinkedList<Message>failedACIMsgs=new LinkedList<Message>();
- InternalClientConnection conn =
- InternalClientConnection.getRootConnection();
- InternalSearchOperation op = conn.processSearch(configDN,
- SearchScope.WHOLE_SUBTREE,
- DereferencePolicy.NEVER_DEREF_ALIASES, 0, 0, false,
- SearchFilter.createFilterFromString("aci=*"), attrs);
- if(!op.getSearchEntries().isEmpty()) {
- int validAcis =
- aciList.addAci(op.getSearchEntries(), failedACIMsgs);
- if(!failedACIMsgs.isEmpty())
- aciListenerMgr.logMsgsSetLockDownMode(failedACIMsgs);
- Message message = INFO_ACI_ADD_LIST_ACIS.get(
- Integer.toString(validAcis), String.valueOf(configDN));
- logError(message);
- }
- } catch (DirectoryException e) {
- Message message = INFO_ACI_HANDLER_FAIL_PROCESS_ACI.get();
- throw new InitializationException(message, e);
- }
- }
-
-
- /**
- * Checks to see if a LDAP modification is allowed access.
- *
- * @param container The structure containing the LDAP modifications
- * @param operation The operation to check modify privileges on.
- * operation to check and the evaluation context to apply the check against.
- * @param skipAccessCheck True if access checking should be skipped.
- * @return True if access is allowed.
- */
- private boolean aciCheckMods(AciLDAPOperationContainer container,
- LocalBackendModifyOperation operation,
- boolean skipAccessCheck) {
- Entry resourceEntry=container.getResourceEntry();
- DN dn=resourceEntry.getDN();
- List<Modification> modifications=container.getModifications();
- for(Modification m : modifications) {
- Attribute modAttr=m.getAttribute();
- AttributeType modAttrType=modAttr.getAttributeType();
-
- if(modAttrType.equals(aciType)) {
- /*
- * Check that the operation has modify privileges if
- * it contains an "aci" attribute type.
- */
- if (!operation.getClientConnection().
- hasPrivilege(Privilege.MODIFY_ACL, operation)) {
- Message message = INFO_ACI_MODIFY_FAILED_PRIVILEGE.
- get(String.valueOf(container.getResourceDN()),
- String.valueOf(container.getClientDN()));
- logError(message);
- return false;
- }
- }
- //This access check handles the case where all attributes of this
- //type are being replaced or deleted. If only a subset is being
- //deleted than this access check is skipped.
- ModificationType modType=m.getModificationType();
- if((modType == ModificationType.DELETE &&
- modAttr.isEmpty()) ||
- (modType == ModificationType.REPLACE ||
- modType == ModificationType.INCREMENT)) {
- /*
- * Check if we have rights to delete all values of
- * an attribute type in the resource entry.
- */
- if(resourceEntry.hasAttribute(modAttrType)) {
- container.setCurrentAttributeType(modAttrType);
- List<Attribute> attrList =
- resourceEntry.getAttribute(modAttrType,modAttr.getOptions());
- if(attrList != null) {
- for (Attribute a : attrList) {
- for (AttributeValue v : a) {
- container.setCurrentAttributeValue(v);
- container.setRights(ACI_WRITE_DELETE);
- if(!skipAccessCheck &&
- !accessAllowed(container))
- return false;
- }
- }
- }
- }
- }
-
- if(!modAttr.isEmpty()) {
- for(AttributeValue v : modAttr) {
- container.setCurrentAttributeType(modAttrType);
- switch (m.getModificationType())
- {
- case ADD:
- case REPLACE:
- container.setCurrentAttributeValue(v);
- container.setRights(ACI_WRITE_ADD);
- if(!skipAccessCheck && !accessAllowed(container))
- return false;
- break;
- case DELETE:
- container.setCurrentAttributeValue(v);
- container.setRights(ACI_WRITE_DELETE);
- if(!skipAccessCheck && !accessAllowed(container))
- return false;
- break;
- case INCREMENT:
- Entry modifiedEntry = operation.getModifiedEntry();
- List<Attribute> modifiedAttrs =
- modifiedEntry.getAttribute(modAttrType,
- modAttr.getOptions());
- if (modifiedAttrs != null)
- {
- for (Attribute attr : modifiedAttrs)
- {
- for (AttributeValue val : attr)
- {
- container.setCurrentAttributeValue(val);
- container.setRights(ACI_WRITE_ADD);
- if(!skipAccessCheck && !accessAllowed(container))
- return false;
- }
- }
- }
- break;
- }
- /*
- Check if the modification type has an "aci" attribute type.
- If so, check the syntax of that attribute value. Fail the
- the operation if the syntax check fails.
- */
- if(modAttrType.equals(aciType) ||
- modAttrType.equals(globalAciType)) {
- try {
- //A global ACI needs a NULL DN, not the DN of the
- //modification.
- if(modAttrType.equals(globalAciType))
- dn=DN.nullDN();
- Aci.decode(v.getValue(),dn);
- } catch (AciException ex) {
- Message message = WARN_ACI_MODIFY_FAILED_DECODE.get(
- String.valueOf(dn), ex.getMessage());
- logError(message);
- return false;
- }
- }
- }
- }
- }
- return true;
- }
-
- /**
- * Performs the test of the deny and allow access lists using the
- * provided evaluation context. The deny list is checked first.
- *
- * @param evalCtx The evaluation context to use.
- * @return True if access is allowed.
- */
- private boolean testApplicableLists(AciEvalContext evalCtx) {
- EnumEvalResult res;
- 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)) {
- 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);
- for(Aci allowAci : allows) {
- 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;
- }
- }
- }
- //Nothing matched fall through.
- evalCtx.setEvalReason(EnumEvalReason.NO_MATCHED_ALLOWS_ACIS);
- evalCtx.setDecidingAci(null);
- return false;
- }
-
- /**
- * Creates the allow and deny ACI lists based on the provided target
- * match context. These lists are stored in the evaluation context.
- * @param candidates List of all possible ACI candidates.
- * @param targetMatchCtx Target matching context to use for testing each
- * ACI.
- */
- private void createApplicableList(LinkedList<Aci> candidates,
- AciTargetMatchContext targetMatchCtx)
- {
- LinkedList<Aci>denys=new LinkedList<Aci>();
- LinkedList<Aci>allows=new LinkedList<Aci>();
- for(Aci aci : candidates) {
- if(Aci.isApplicable(aci, targetMatchCtx)) {
- if (aci.hasAccessType(EnumAccessType.DENY)) {
- denys.add(aci);
- }
- if(aci.hasAccessType(EnumAccessType.ALLOW)) {
- allows.add(aci);
- }
- }
- if(targetMatchCtx.getTargAttrFiltersMatch())
- targetMatchCtx.setTargAttrFiltersMatch(false);
- }
- targetMatchCtx.setAllowList(allows);
- targetMatchCtx.setDenyList(denys);
- }
-
-
- /**
- * Check to see if the client entry has BYPASS_ACL privileges
- * for this operation.
- * @param operation The operation to check privileges on.
- * @return True if access checking can be skipped because
- * the operation client connection has BYPASS_ACL privileges.
- */
- private boolean skipAccessCheck(Operation operation) {
- return operation.getClientConnection().
- hasPrivilege(Privilege.BYPASS_ACL, operation);
- }
-
- /**
- * Check to see if the specified entry has the specified privilege.
- *
- * @param e The entry to check privileges on.
- * @return {@code true} if the entry has the
- * specified privilege, or {@code false} if not.
- */
- private boolean skipAccessCheck(Entry e) {
- return ClientConnection.hasPrivilege(e, Privilege.BYPASS_ACL);
- }
-
- /**
- * Check access using the specified container. This container will have all
- * of the information to gather applicable ACIs and perform evaluation on
- * them.
- *
- * @param container An ACI operation container which has all of the
- * information needed to check access.
- *
- * @return True if access is allowed.
- */
- boolean accessAllowed(AciContainer container)
- {
- DN dn = container.getResourceEntry().getDN();
- //For ACI_WRITE_ADD and ACI_WRITE_DELETE set the ACI_WRITE
- //right.
- if(container.hasRights(ACI_WRITE_ADD) ||
- container.hasRights(ACI_WRITE_DELETE))
- container.setRights(container.getRights() | ACI_WRITE);
- //Check if the ACI_SELF right needs to be set (selfwrite right).
- //Only done if the right is ACI_WRITE, an attribute value is set and
- //that attribute value is a DN.
- if((container.getCurrentAttributeValue() != null) &&
- (container.hasRights(ACI_WRITE)) &&
- (isAttributeDN(container.getCurrentAttributeType()))) {
- String DNString=null;
- try {
- DNString =
- container.getCurrentAttributeValue().getValue().toString();
- DN tmpDN = DN.decode(DNString);
- //Have a valid DN, compare to clientDN to see if the ACI_SELF
- //right should be set.
- if(tmpDN.equals(container.getClientDN())) {
- container.setRights(container.getRights() | ACI_SELF);
- }
- } catch (DirectoryException ex) {
- //Log a message and keep going.
- Message message = WARN_ACI_NOT_VALID_DN.get(DNString);
- logError(message);
- }
- }
-
- //Check proxy authorization only if the entry has not already been
- //processed (working on a new entry). If working on a new entry, then
- //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.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);
- //Switch to the original authorization entry, not the proxied one.
- container.useOrigAuthorizationEntry(true);
- if(!accessAllowed(container))
- return false;
- //Access is ok, put the original rights back.
- container.setRights(currentRights);
- //Put the proxied authorization entry back to the current
- //authorization entry.
- container.useOrigAuthorizationEntry(false);
- }
- //Set the seen flag so proxy processing is not performed for this
- //entry again.
- container.setSeenEntry(true);
- }
-
- /*
- * First get all allowed candidate ACIs.
- */
- LinkedList<Aci>candidates = aciList.getCandidateAcis(dn);
- /*
- * Create an applicable list of ACIs by target matching each
- * candidate ACI against the container's target match view.
- */
- createApplicableList(candidates,container);
- /*
- * Evaluate the applicable list.
- */
- boolean ret=testApplicableLists(container);
- //Build summary string if doing geteffectiverights eval.
- if(container.isGetEffectiveRightsEval())
- AciEffectiveRights.createSummary(container, ret, "main");
- return ret;
- }
-
- /**
- * Check if the specified attribute type is a DN by checking if its syntax
- * OID is equal to the DN syntax OID.
- * @param attribute The attribute type to check.
- * @return True if the attribute type syntax OID is equal to a DN syntax
- * OID.
- */
- private boolean isAttributeDN(AttributeType attribute) {
- return (attribute.getSyntaxOID().equals(SYNTAX_DN_OID));
- }
-
- /**
- * Performs an access check against all of the attributes of an entry.
- * The attributes that fail access are removed from the entry. This method
- * performs the processing needed for the filterEntry method processing.
- *
- * @param container The search or compare container which has all of the
- * information needed to filter the attributes for this entry.
- * @return The entry to send back to the client, minus any attribute
- * types that failed access check.
- */
- private SearchResultEntry
- accessAllowedAttrs(AciLDAPOperationContainer container) {
- Entry e=container.getResourceEntry();
- List<AttributeType> typeList=getAllAttrs(e);
- for(AttributeType attrType : typeList) {
- if(container.hasAllUserAttributes() && !attrType.isOperational())
- continue;
- if(container.hasAllOpAttributes() && attrType.isOperational())
- continue;
- container.setCurrentAttributeType(attrType);
- if(!accessAllowed(container))
- e.removeAttribute(attrType);
- }
- return container.getSearchResultEntry();
- }
-
- /**
- * Gathers all of the attribute types in an entry along with the
- * "objectclass" attribute type in a List. The "objectclass" attribute is
- * added to the list first so it is evaluated first.
- *
- * @param e Entry to gather the attributes for.
- * @return List containing the attribute types.
- */
- private List<AttributeType> getAllAttrs(Entry e) {
- Map<AttributeType,List<Attribute>> attrMap = e.getUserAttributes();
- Map<AttributeType,List<Attribute>> opAttrMap =
- e.getOperationalAttributes();
- List<AttributeType> typeList=new LinkedList<AttributeType>();
- Attribute attr=e.getObjectClassAttribute();
- /*
- * When a search is not all attributes returned, the "objectclass"
- * attribute type is missing from the entry.
- */
- if(attr != null) {
- AttributeType ocType=attr.getAttributeType();
- typeList.add(ocType);
- }
- typeList.addAll(attrMap.keySet());
- typeList.addAll(opAttrMap.keySet());
- return typeList;
- }
-
- /*
- * TODO Evaluate performance of this method.
- * TODO Evaluate security concerns of this method. Logic from this method
- * taken almost directly from DS6 implementation.
- *
- * I find the work done in the accessAllowedEntry method, particularly
- * with regard to the entry test evaluation, to be very confusing and
- * potentially pretty inefficient. I'm also concerned that the "return
- * "true" inside the for loop could potentially allow access when it
- * should be denied.
- */
- /**
- * Check if access is allowed on an entry. Access is checked by iterating
- * through each attribute of an entry, starting with the "objectclass"
- * attribute type.
- *
- * If access is allowed on the entry based on one of it's attribute types,
- * then a possible second access check is performed. This second check is
- * only performed if an entry test ACI was found during the earlier
- * successful access check. An entry test ACI has no "targetattrs" keyword,
- * so allowing access based on an attribute type only would be incorrect.
- *
- * @param container ACI search container containing all of the information
- * needed to check access.
- *
- * @return True if access is allowed.
- */
- boolean accessAllowedEntry(AciLDAPOperationContainer container) {
- boolean ret=false;
- //set flag that specifies this is the first attribute evaluated
- //in the entry
- container.setIsFirstAttribute(true);
- List<AttributeType> typeList=getAllAttrs(container.getResourceEntry());
- for(AttributeType attrType : typeList) {
- container.setCurrentAttributeType(attrType);
- /*
- * Check if access is allowed. If true, then check to see if an
- * entry test rule was found (no targetattrs) during target match
- * evaluation. If such a rule was found, set the current attribute
- * type to "null" and check access again so that rule is applied.
- */
- if(accessAllowed(container)) {
- if(container.hasEntryTestRule()) {
- container.setCurrentAttributeType(null);
- if(!accessAllowed(container)) {
- /*
- * If we failed because of a deny permission-bind rule,
- * we need to stop and return false.
- */
- if(container.isDenyEval()) {
- return false;
- }
- /*
- * If we failed because there was no explicit
- * allow rule, then we grant implicit access to the
- * entry.
- */
- }
- }
- return true;
- }
- }
- return ret;
- }
-
- /**
- * Test the attribute types of the search filter for access. This method
- * supports the search right.
- *
- * @param container The container used in the access evaluation.
- * @param filter The filter to check access on.
- * @return True if all attribute types in the filter have access.
- * @throws DirectoryException If there is a problem matching the entry
- * using the provided filter.
- */
- private boolean
- testFilter(AciLDAPOperationContainer container, SearchFilter filter)
- throws DirectoryException {
- boolean ret=true;
- //If the resource entry has a dn equal to "cn=debugsearch" and it
- //contains the special attribute type "debugsearchindex", then the
- //resource entry is a pseudo entry created for debug purposes. Return
- //true if that is the case.
- if(debugSearchIndexDN.equals(container.getResourceDN()) &&
- container.getResourceEntry().hasAttribute(debugSearchIndex))
- return true;
- switch (filter.getFilterType()) {
- case AND:
- case OR: {
- for (SearchFilter f : filter.getFilterComponents())
- if(!testFilter(container, f))
- return false ;
- break;
- }
- case NOT: {
- ret=false;
- SearchFilter f = filter.getNotComponent();
- if(f.matchesEntry(container.getResourceEntry()))
- ret=true;
- if(ret)
- ret=testFilter(container, f);
- ret=!ret;
- break;
- }
- default: {
- AttributeType attrType=filter.getAttributeType();
- container.setCurrentAttributeType(attrType);
- ret=accessAllowed(container);
- }
- }
- return ret;
- }
-
- /**
- * Check access using the accessAllowed method. The
- * LDAP add, compare, modify and delete operations use this function.
- * The other supported LDAP operations have more specialized checks.
- * @param operationContainer The container containing the information
- * needed to evaluate this operation.
- * @param operation The operation being evaluated.
- * @return True if this operation is allowed access.
- */
- private boolean isAllowed(AciLDAPOperationContainer operationContainer,
- Operation operation) {
- return skipAccessCheck(operation) || accessAllowed(operationContainer);
- }
-
- /**
- * Evaluate an entry to be added to see if it has any "aci"
- * attribute type. If it does, examines each "aci" attribute type
- * value for syntax errors. All of the "aci" attribute type values
- * must pass syntax check for the add operation to proceed. Any
- * entry with an "aci" attribute type must have "modify-acl"
- * privileges.
- *
- * @param entry The entry to be examined.
- * @param operation The operation to to check privileges on.
- * @param clientDN The authorization DN.
- * @return True if the entry has no ACI attributes or if all of the "aci"
- * attributes values pass ACI syntax checking.
- */
- private boolean
- verifySyntax(Entry entry, Operation operation, DN clientDN) {
- if(entry.hasOperationalAttribute(aciType)) {
- /*
- * Check that the operation has "modify-acl" privileges since the
- * entry to be added has an "aci" attribute type.
- */
- if (!operation.getClientConnection().
- hasPrivilege(Privilege.MODIFY_ACL, operation)) {
- Message message = INFO_ACI_ADD_FAILED_PRIVILEGE.get(
- String.valueOf(entry.getDN()), String.valueOf(clientDN));
- logError(message);
- return false;
- }
- List<Attribute> attributeList =
- entry.getOperationalAttribute(aciType, null);
- for (Attribute attribute : attributeList)
+ GetEffectiveRightsRequestControl getEffectiveRightsControl;
+ if (control instanceof LDAPControl)
{
- for (AttributeValue value : attribute)
- {
- try {
- DN dn=entry.getDN();
- Aci.decode(value.getValue(),dn);
- } catch (AciException ex) {
- Message message = WARN_ACI_ADD_FAILED_DECODE.get(
- String.valueOf(entry.getDN()), ex.getMessage());
- logError(message);
- return false;
- }
- }
+ getEffectiveRightsControl =
+ GetEffectiveRightsRequestControl.DECODER.decode(control
+ .isCritical(), ((LDAPControl) control).getValue());
}
- }
- return true;
- }
-
- /**
- * Check access on add operations.
- *
- * @param operation The add operation to check access on.
- * @return True if access is allowed.
- */
- public boolean isAllowed(LocalBackendAddOperation operation) {
- AciLDAPOperationContainer operationContainer =
- new AciLDAPOperationContainer(operation, ACI_ADD);
- boolean ret=isAllowed(operationContainer,operation);
-
- //LDAP add needs a verify ACI syntax step in case any
- //"aci" attribute types are being added.
- if(ret)
- ret=verifySyntax(operation.getEntryToAdd(), operation,
- operationContainer.getClientDN());
- return ret;
- }
-
- /**
- * Check access on compare operations. Note that the attribute
- * type is unavailable at this time, so this method partially
- * parses the raw attribute string to get the base attribute
- * type. Options are ignored.
- *
- * @param operation The compare operation to check access on.
- * @return True if access is allowed.
- */
- public boolean isAllowed(LocalBackendCompareOperation operation) {
- AciLDAPOperationContainer operationContainer =
- new AciLDAPOperationContainer(operation, ACI_COMPARE);
- String baseName;
- String rawAttributeType=operation.getRawAttributeType();
- int semicolonPosition=rawAttributeType.indexOf(';');
- if (semicolonPosition > 0)
- baseName =
- toLowerCase(rawAttributeType.substring(0, semicolonPosition));
- else
- baseName = toLowerCase(rawAttributeType);
- AttributeType attributeType;
- if((attributeType =
- DirectoryServer.getAttributeType(baseName)) == null)
- attributeType = DirectoryServer.getDefaultAttributeType(baseName);
- AttributeValue attributeValue =
- AttributeValues.create(attributeType,
- operation.getAssertionValue());
- operationContainer.setCurrentAttributeType(attributeType);
- operationContainer.setCurrentAttributeValue(attributeValue);
- return isAllowed(operationContainer, operation);
- }
-
- /**
- * Check access on delete operations.
- *
- * @param operation The delete operation to check access on.
- * @return True if access is allowed.
- */
- public boolean isAllowed(LocalBackendDeleteOperation operation) {
- AciLDAPOperationContainer operationContainer=
- new AciLDAPOperationContainer(operation, ACI_DELETE);
- return isAllowed(operationContainer, operation);
- }
-
- /**
- * Check access on modify operations.
- *
- * @param operation The modify operation to check access on.
- * @return True if access is allowed.
- */
-
- public boolean isAllowed(LocalBackendModifyOperation operation) {
- AciLDAPOperationContainer operationContainer=
- new AciLDAPOperationContainer(operation, ACI_NULL);
- return aciCheckMods(operationContainer, operation,
- skipAccessCheck(operation));
- }
-
- /**
- * Checks access on a search operation.
- * @param operation The search operation class containing information to
- * check the access on.
- * @param entry The entry to evaluate access.
- * @return True if access is allowed.
- */
- public boolean
- maySend(SearchOperation operation, SearchResultEntry entry) {
- AciLDAPOperationContainer operationContainer =
- new AciLDAPOperationContainer(operation,
- (ACI_SEARCH), entry);
- boolean ret;
- if(!(ret=skipAccessCheck(operation))) {
- try {
- ret=testFilter(operationContainer, operation.getFilter());
- } catch (DirectoryException ex) {
- ret=false;
- }
- if (ret) {
- operationContainer.clearEvalAttributes(ACI_NULL);
- operationContainer.setRights(ACI_READ);
- ret=accessAllowedEntry(operationContainer);
- if(ret) {
- if(!operationContainer.hasEvalUserAttributes())
- operation.setAttachment(ALL_USER_ATTRS_MATCHED,
- ALL_USER_ATTRS_MATCHED);
- if(!operationContainer.hasEvalOpAttributes())
- operation.setAttachment(ALL_OP_ATTRS_MATCHED,
- ALL_OP_ATTRS_MATCHED);
- }
- }
- }
- //Save a copy of the full resource entry for possible
- //userattr bind rule or geteffectiveright's evaluations in the filterEnty
- //method.
- operation.setAttachment(ALL_ATTRS_RESOURCE_ENTRY, entry );
- return ret;
- }
-
- /*
- * TODO Rename this method. Needs to be changed in SearchOperation.
- *
- * I find the name of the filterEntry method to be misleading because
- * it works on a search operation but has nothing to do with the search
- * filter. Something like "removeDisallowedAttributes" would be clearer.
- */
- /**
- * Checks access on each attribute in an entry. It removes those attributes
- * that fail access check.
- *
- * @param operation The search operation class containing information to
- * check access on.
- * @param entry The entry containing the attributes.
- * @return The entry to return minus filtered attributes.
- */
- public SearchResultEntry filterEntry(SearchOperation operation,
- SearchResultEntry entry) {
- AciLDAPOperationContainer operationContainer =
- new AciLDAPOperationContainer(operation,
- (ACI_READ), entry);
- //Proxy access check has already been done for this entry in the maySend
- //method, set the seen flag to true to bypass any proxy check.
- operationContainer.setSeenEntry(true);
- SearchResultEntry returnEntry;
- 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;
- }
-
- /**
- * Perform all needed RDN checks for the modifyDN operation. The old RDN is
- * not equal to the new RDN. The access checks are:
- *
- * - Verify WRITE access to the original entry.
- * - Verfiy WRITE_ADD access on each RDN component of the new RDN. The
- * WRITE_ADD access is used because this access could be restricted by
- * the targattrfilters keyword.
- * - If the deleteOLDRDN flag is set, verify WRITE_DELETE access on the
- * old RDN. The WRITE_DELETE access is used because this access could be
- * restricted by the targattrfilters keyword.
- *
- * @param operation The ModifyDN operation class containing information to
- * check access on.
- * @param oldRDN The old RDN component.
- * @param newRDN The new RDN component.
- * @return True if access is allowed.
- */
- private boolean aciCheckRDNs(LocalBackendModifyDNOperation operation,
- RDN oldRDN,
- RDN newRDN) {
- boolean ret;
-
- AciLDAPOperationContainer operationContainer =
- new AciLDAPOperationContainer(operation, (ACI_WRITE),
- operation.getOriginalEntry());
- ret=accessAllowed(operationContainer);
- if(ret)
- ret=checkRDN(ACI_WRITE_ADD, newRDN, operationContainer);
- if(ret && operation.deleteOldRDN()) {
- ret =
- checkRDN(ACI_WRITE_DELETE, oldRDN, operationContainer);
- }
- return ret;
- }
-
-
- /**
- * Check access on each attribute-value pair component of the specified RDN.
- * There may be more than one attribute-value pair if the RDN is multi-valued.
- *
- * @param right The access right to check for.
- * @param rdn The RDN to examine the attribute-value pairs of.
- * @param container The container containing the information needed to
- * evaluate the specified RDN.
- * @return True if access is allowed for all attribute-value pairs.
- */
- private boolean checkRDN(int right, RDN rdn, AciContainer container) {
- boolean ret=false;
- int numAVAs = rdn.getNumValues();
- container.setRights(right);
- for (int i = 0; i < numAVAs; i++){
- AttributeType type=rdn.getAttributeType(i);
- AttributeValue value=rdn.getAttributeValue(i);
- container.setCurrentAttributeType(type);
- container.setCurrentAttributeValue(value);
- if(!(ret=accessAllowed(container)))
- break;
+ else
+ {
+ getEffectiveRightsControl =
+ (GetEffectiveRightsRequestControl) control;
}
- return ret;
- }
-
- /**
- * Check access on the new superior entry if it exists. If the entry does not
- * exist or the DN cannot be locked then false is returned.
- *
- * @param superiorDN The DN of the new superior entry.
- * @param op The modifyDN operation to check access on.
- * @return True if access is granted to the new superior entry.
- * @throws DirectoryException If a problem occurs while trying to
- * retrieve the new superior entry.
- */
- private boolean aciCheckSuperiorEntry(DN superiorDN,
- LocalBackendModifyDNOperation op)
- throws DirectoryException {
- boolean ret=false;
- Lock entryLock = null;
- for (int i=0; i < 3; i++) {
- entryLock = LockManager.lockRead(superiorDN);
- if (entryLock != null)
- break;
- }
- if (entryLock == null) {
- Message message = WARN_ACI_HANDLER_CANNOT_LOCK_NEW_SUPERIOR_USER.get(
- String.valueOf(superiorDN));
- logError(message);
- return false;
- }
- try {
- Entry superiorEntry=DirectoryServer.getEntry(superiorDN);
- if(superiorEntry!= null) {
- AciLDAPOperationContainer operationContainer =
- new AciLDAPOperationContainer(op, (ACI_IMPORT),
- superiorEntry);
- ret=accessAllowed(operationContainer);
+ op.setAttachment(OID_GET_EFFECTIVE_RIGHTS,
+ getEffectiveRightsControl);
}
- } finally {
- LockManager.unlock(superiorDN, entryLock);
+ catch (DirectoryException de)
+ {
+ Message message =
+ WARN_ACI_SYNTAX_DECODE_EFFECTIVERIGHTS_FAIL.get(de
+ .getMessage());
+ logError(message);
+ ret = false;
+ }
}
return ret;
}
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isAllowed(ExtendedOperation operation)
+ {
+ boolean ret;
+ if (!(ret = skipAccessCheck(operation)))
+ {
+ Entry e =
+ new Entry(operation.getAuthorizationDN(), null, null, null);
+ AciLDAPOperationContainer operationContainer =
+ new AciLDAPOperationContainer(operation, e,
+ (ACI_READ | ACI_EXT_OP));
+ ret = accessAllowed(operationContainer);
+ }
+ return ret;
+ }
+
+
+
+ /**
+ * Check access on add operations.
+ *
+ * @param operation
+ * The add operation to check access on.
+ * @return True if access is allowed.
+ */
+ @Override
+ public boolean isAllowed(LocalBackendAddOperation operation)
+ {
+ AciLDAPOperationContainer operationContainer =
+ new AciLDAPOperationContainer(operation, ACI_ADD);
+ boolean ret = isAllowed(operationContainer, operation);
+
+ // LDAP add needs a verify ACI syntax step in case any
+ // "aci" attribute types are being added.
+ if (ret)
+ {
+ ret =
+ verifySyntax(operation.getEntryToAdd(), operation,
+ operationContainer.getClientDN());
+ }
+ return ret;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isAllowed(LocalBackendBindOperation bindOperation)
+ {
+ // Not planned to be implemented.
+ return true;
+ }
+
+
+
+ /**
+ * Check access on compare operations. Note that the attribute type is
+ * unavailable at this time, so this method partially parses the raw
+ * attribute string to get the base attribute type. Options are
+ * ignored.
+ *
+ * @param operation
+ * The compare operation to check access on.
+ * @return True if access is allowed.
+ */
+ @Override
+ public boolean isAllowed(LocalBackendCompareOperation operation)
+ {
+ AciLDAPOperationContainer operationContainer =
+ new AciLDAPOperationContainer(operation, ACI_COMPARE);
+ String baseName;
+ String rawAttributeType = operation.getRawAttributeType();
+ int semicolonPosition = rawAttributeType.indexOf(';');
+ if (semicolonPosition > 0)
+ {
+ baseName =
+ toLowerCase(rawAttributeType.substring(0, semicolonPosition));
+ }
+ else
+ {
+ baseName = toLowerCase(rawAttributeType);
+ }
+ AttributeType attributeType;
+ if ((attributeType = DirectoryServer.getAttributeType(baseName)) == null)
+ {
+ attributeType = DirectoryServer.getDefaultAttributeType(baseName);
+ }
+ AttributeValue attributeValue =
+ AttributeValues.create(attributeType, operation
+ .getAssertionValue());
+ operationContainer.setCurrentAttributeType(attributeType);
+ operationContainer.setCurrentAttributeValue(attributeValue);
+ return isAllowed(operationContainer, operation);
+ }
+
+
+
+ /**
+ * Check access on delete operations.
+ *
+ * @param operation
+ * The delete operation to check access on.
+ * @return True if access is allowed.
+ */
+ @Override
+ public boolean isAllowed(LocalBackendDeleteOperation operation)
+ {
+ AciLDAPOperationContainer operationContainer =
+ new AciLDAPOperationContainer(operation, ACI_DELETE);
+ return isAllowed(operationContainer, operation);
+ }
+
+
+
/**
* Checks access on a modifyDN operation.
*
- * @param operation The modifyDN operation to check access on.
+ * @param operation
+ * The modifyDN operation to check access on.
* @return True if access is allowed.
+ */
+ @Override
+ public boolean isAllowed(LocalBackendModifyDNOperation operation)
+ {
+ boolean ret = true;
+ DN newSuperiorDN;
+ RDN oldRDN = operation.getOriginalEntry().getDN().getRDN();
+ RDN newRDN = operation.getNewRDN();
+ if (!skipAccessCheck(operation))
+ {
+ // If this is a modifyDN move to a new superior, then check if the
+ // superior DN has import accesss.
+ if ((newSuperiorDN = operation.getNewSuperior()) != null)
+ {
+ try
+ {
+ ret = aciCheckSuperiorEntry(newSuperiorDN, operation);
+ }
+ catch (DirectoryException ex)
+ {
+ ret = false;
+ }
+ }
+ boolean rdnEquals = oldRDN.equals(newRDN);
+ // Perform the RDN access checks only if the RDNs are not equal.
+ if (ret && !rdnEquals)
+ {
+ ret = aciCheckRDNs(operation, oldRDN, newRDN);
+ }
+
+ // If this is a modifyDN move to a new superior, then check if the
+ // original entry DN has export access.
+ if (ret && (newSuperiorDN != null))
+ {
+ AciLDAPOperationContainer operationContainer =
+ new AciLDAPOperationContainer(operation, (ACI_EXPORT),
+ operation.getOriginalEntry());
+ // The RDNs are not equal, skip the proxy check since it was
+ // already performed in the aciCheckRDNs call above.
+ if (!rdnEquals)
+ {
+ operationContainer.setSeenEntry(true);
+ }
+ ret = accessAllowed(operationContainer);
+ }
+ }
+ return ret;
+ }
+
+
+
+ /**
+ * Check access on modify operations.
*
+ * @param operation
+ * The modify operation to check access on.
+ * @return True if access is allowed.
*/
- public boolean isAllowed(LocalBackendModifyDNOperation operation) {
- boolean ret=true;
- DN newSuperiorDN;
- RDN oldRDN=operation.getOriginalEntry().getDN().getRDN();
- RDN newRDN=operation.getNewRDN();
- if(!skipAccessCheck(operation)) {
- //If this is a modifyDN move to a new superior, then check if the
- //superior DN has import accesss.
- if((newSuperiorDN=operation.getNewSuperior()) != null) {
- try {
- ret=aciCheckSuperiorEntry(newSuperiorDN, operation);
- } catch (DirectoryException ex) {
- ret=false;
- }
- }
- boolean rdnEquals=oldRDN.equals(newRDN);
- //Perform the RDN access checks only if the RDNs are not equal.
- if(ret && !rdnEquals)
- ret=aciCheckRDNs(operation, oldRDN, newRDN);
- //If this is a modifyDN move to a new superior, then check if the
- //original entry DN has export access.
- if(ret && (newSuperiorDN != null)) {
- AciLDAPOperationContainer operationContainer =
- new AciLDAPOperationContainer(operation, (ACI_EXPORT),
- operation.getOriginalEntry());
- //The RDNs are not equal, skip the proxy check since it was
- //already performed in the aciCheckRDNs call above.
- if(!rdnEquals)
- operationContainer.setSeenEntry(true);
- ret=accessAllowed(operationContainer);
- }
- }
- return ret;
+ @Override
+ public boolean isAllowed(LocalBackendModifyOperation operation)
+ {
+ AciLDAPOperationContainer operationContainer =
+ new AciLDAPOperationContainer(operation, ACI_NULL);
+ return aciCheckMods(operationContainer, operation,
+ skipAccessCheck(operation));
}
+
/**
* {@inheritDoc}
*/
@Override
- public boolean isAllowed(DN entryDN, Operation op, Control control) {
- boolean ret;
- if(!(ret=skipAccessCheck(op))) {
- Entry e = new Entry(entryDN, null, null, null);
- AciLDAPOperationContainer operationContainer =
- new AciLDAPOperationContainer(op, e, control,
- (ACI_READ | ACI_CONTROL));
- ret=accessAllowed(operationContainer);
- }
- if(control.getOID().equals(OID_PROXIED_AUTH_V2) ||
- control.getOID().equals(OID_PROXIED_AUTH_V1))
- op.setAttachment(ORIG_AUTH_ENTRY, op.getAuthorizationEntry());
- else if(control.getOID().equals(OID_GET_EFFECTIVE_RIGHTS)) {
- try {
- GetEffectiveRightsRequestControl getEffectiveRightsControl;
- if(control instanceof LDAPControl)
- {
- getEffectiveRightsControl = GetEffectiveRightsRequestControl.DECODER
- .decode(control.isCritical(), ((LDAPControl) control).getValue());
- }
- else
- {
- getEffectiveRightsControl = (GetEffectiveRightsRequestControl)control;
- }
- op.setAttachment(OID_GET_EFFECTIVE_RIGHTS, getEffectiveRightsControl);
- } catch (DirectoryException de) {
- Message message =
- WARN_ACI_SYNTAX_DECODE_EFFECTIVERIGHTS_FAIL.get(de.getMessage());
- logError(message);
- ret=false;
- }
- }
- return ret;
+ public boolean isAllowed(LocalBackendSearchOperation searchOperation)
+ {
+ // Not planned to be implemented.
+ return true;
}
+
/**
* {@inheritDoc}
*/
@Override
- public boolean isAllowed(ExtendedOperation operation) {
+ public boolean mayProxy(Entry proxyUser, Entry proxiedUser,
+ Operation op)
+ {
boolean ret;
- if(!(ret=skipAccessCheck(operation))) {
- Entry e = new Entry(operation.getAuthorizationDN(), null, null, null);
+ if (!(ret = skipAccessCheck(proxyUser)))
+ {
+ AuthenticationInfo authInfo =
+ new AuthenticationInfo(proxyUser, DirectoryServer
+ .isRootDN(proxyUser.getDN()));
AciLDAPOperationContainer operationContainer =
- new AciLDAPOperationContainer(operation, e, (ACI_READ | ACI_EXT_OP));
- ret=accessAllowed(operationContainer);
+ new AciLDAPOperationContainer(op, proxiedUser, authInfo,
+ ACI_PROXY);
+ ret = accessAllowedEntry(operationContainer);
}
return ret;
}
+
/**
* {@inheritDoc}
*/
@Override
public boolean maySend(DN dn, SearchOperation operation,
- SearchResultReference reference) {
+ SearchResultReference reference)
+ {
boolean ret;
- if(!(ret=skipAccessCheck(operation))) {
+ if (!(ret = skipAccessCheck(operation)))
+ {
Entry e = new Entry(dn, null, null, null);
AttributeBuilder builder =
- new AttributeBuilder(refAttrType, ATTR_REFERRAL_URL);
+ new AttributeBuilder(refAttrType, ATTR_REFERRAL_URL);
List<String> URLStrings = reference.getReferralURLs();
// Load the values, a bind rule might want to evaluate them.
- for (String URLString : URLStrings) {
+ for (String URLString : URLStrings)
+ {
builder.add(AttributeValues.create(refAttrType, URLString));
}
- e.addAttribute(builder.toAttribute(),null);
- SearchResultEntry se=new SearchResultEntry(e);
+ e.addAttribute(builder.toAttribute(), null);
+ SearchResultEntry se = new SearchResultEntry(e);
AciLDAPOperationContainer operationContainer =
- new AciLDAPOperationContainer(operation,
- (ACI_READ), se);
+ new AciLDAPOperationContainer(operation, (ACI_READ), se);
operationContainer.setCurrentAttributeType(refAttrType);
- ret=accessAllowed(operationContainer);
+ ret = accessAllowed(operationContainer);
}
return ret;
}
+
/**
- * {@inheritDoc}
+ * Checks access on a search operation.
+ *
+ * @param operation
+ * The search operation class containing information to check
+ * the access on.
+ * @param entry
+ * The entry to evaluate access.
+ * @return True if access is allowed.
*/
@Override
- public boolean
- mayProxy(Entry proxyUser, Entry proxiedUser, Operation op) {
- boolean ret;
- if(!(ret=skipAccessCheck(proxyUser))) {
- AuthenticationInfo authInfo =
- new AuthenticationInfo(proxyUser,
- DirectoryServer.isRootDN(proxyUser.getDN()));
- AciLDAPOperationContainer operationContainer =
- new AciLDAPOperationContainer(op, proxiedUser,
- authInfo, ACI_PROXY);
- ret=accessAllowedEntry(operationContainer);
+ public boolean maySend(SearchOperation operation,
+ SearchResultEntry entry)
+ {
+ AciLDAPOperationContainer operationContainer =
+ new AciLDAPOperationContainer(operation, (ACI_SEARCH), entry);
+ boolean ret;
+ if (!(ret = skipAccessCheck(operation)))
+ {
+ try
+ {
+ ret = testFilter(operationContainer, operation.getFilter());
}
- return ret;
+ catch (DirectoryException ex)
+ {
+ ret = false;
+ }
+ if (ret)
+ {
+ operationContainer.clearEvalAttributes(ACI_NULL);
+ operationContainer.setRights(ACI_READ);
+ ret = accessAllowedEntry(operationContainer);
+ if (ret)
+ {
+ if (!operationContainer.hasEvalUserAttributes())
+ {
+ operation.setAttachment(ALL_USER_ATTRS_MATCHED,
+ ALL_USER_ATTRS_MATCHED);
+ }
+ if (!operationContainer.hasEvalOpAttributes())
+ {
+ operation.setAttachment(ALL_OP_ATTRS_MATCHED,
+ ALL_OP_ATTRS_MATCHED);
+ }
+ }
+ }
+ }
+ // Save a copy of the full resource entry for possible
+ // userattr bind rule or geteffectiveright's evaluations in the
+ // filterEnty method.
+ operation.setAttachment(ALL_ATTRS_RESOURCE_ENTRY, entry);
+ return ret;
}
- /**
- * {@inheritDoc}
- */
- @Override
- public boolean isAllowed(LocalBackendBindOperation bindOperation) {
- //Not planned to be implemented.
- return true;
- }
/**
- * {@inheritDoc}
+ * Check access using the specified container. This container will
+ * have all of the information to gather applicable ACIs and perform
+ * evaluation on them.
+ *
+ * @param container
+ * An ACI operation container which has all of the
+ * information needed to check access.
+ * @return True if access is allowed.
*/
- @Override
- public boolean isAllowed(LocalBackendSearchOperation searchOperation) {
- //Not planned to be implemented.
+ boolean accessAllowed(AciContainer container)
+ {
+ DN dn = container.getResourceEntry().getDN();
+ // For ACI_WRITE_ADD and ACI_WRITE_DELETE set the ACI_WRITE
+ // right.
+ if (container.hasRights(ACI_WRITE_ADD)
+ || container.hasRights(ACI_WRITE_DELETE))
+ {
+ container.setRights(container.getRights() | ACI_WRITE);
+ }
+ // Check if the ACI_SELF right needs to be set (selfwrite right).
+ // Only done if the right is ACI_WRITE, an attribute value is set
+ // and that attribute value is a DN.
+ if ((container.getCurrentAttributeValue() != null)
+ && (container.hasRights(ACI_WRITE))
+ && (isAttributeDN(container.getCurrentAttributeType())))
+ {
+ String DNString = null;
+ try
+ {
+ DNString =
+ container.getCurrentAttributeValue().getValue().toString();
+ DN tmpDN = DN.decode(DNString);
+ // Have a valid DN, compare to clientDN to see if the ACI_SELF
+ // right should be set.
+ if (tmpDN.equals(container.getClientDN()))
+ {
+ container.setRights(container.getRights() | ACI_SELF);
+ }
+ }
+ catch (DirectoryException ex)
+ {
+ // Log a message and keep going.
+ Message message = WARN_ACI_NOT_VALID_DN.get(DNString);
+ logError(message);
+ }
+ }
+
+ // Check proxy authorization only if the entry has not already been
+ // processed (working on a new entry). If working on a new entry,
+ // then 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.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);
+ // Switch to the original authorization entry, not the proxied
+ // one.
+ container.useOrigAuthorizationEntry(true);
+ if (!accessAllowed(container))
+ {
+ return false;
+ }
+ // Access is ok, put the original rights back.
+ container.setRights(currentRights);
+ // Put the proxied authorization entry back to the current
+ // authorization entry.
+ container.useOrigAuthorizationEntry(false);
+ }
+ // Set the seen flag so proxy processing is not performed for this
+ // entry again.
+ container.setSeenEntry(true);
+ }
+
+ /*
+ * First get all allowed candidate ACIs.
+ */
+ LinkedList<Aci> candidates = aciList.getCandidateAcis(dn);
+ /*
+ * Create an applicable list of ACIs by target matching each
+ * candidate ACI against the container's target match view.
+ */
+ createApplicableList(candidates, container);
+ /*
+ * Evaluate the applicable list.
+ */
+ boolean ret = testApplicableLists(container);
+ // Build summary string if doing geteffectiverights eval.
+ if (container.isGetEffectiveRightsEval())
+ {
+ AciEffectiveRights.createSummary(container, ret, "main");
+ }
+ return ret;
+ }
+
+
+
+ /*
+ * TODO Evaluate performance of this method. TODO Evaluate security
+ * concerns of this method. Logic from this method taken almost
+ * directly from DS6 implementation. I find the work done in the
+ * accessAllowedEntry method, particularly with regard to the entry
+ * test evaluation, to be very confusing and potentially pretty
+ * inefficient. I'm also concerned that the "return "true" inside the
+ * for loop could potentially allow access when it should be denied.
+ */
+
+ /**
+ * Check if access is allowed on an entry. Access is checked by
+ * iterating through each attribute of an entry, starting with the
+ * "objectclass" attribute type. If access is allowed on the entry
+ * based on one of it's attribute types, then a possible second access
+ * check is performed. This second check is only performed if an entry
+ * test ACI was found during the earlier successful access check. An
+ * entry test ACI has no "targetattrs" keyword, so allowing access
+ * based on an attribute type only would be incorrect.
+ *
+ * @param container
+ * ACI search container containing all of the information
+ * needed to check access.
+ * @return True if access is allowed.
+ */
+ boolean accessAllowedEntry(AciLDAPOperationContainer container)
+ {
+ boolean ret = false;
+ // set flag that specifies this is the first attribute evaluated
+ // in the entry
+ container.setIsFirstAttribute(true);
+ List<AttributeType> typeList =
+ getAllAttrs(container.getResourceEntry());
+ for (AttributeType attrType : typeList)
+ {
+ container.setCurrentAttributeType(attrType);
+ /*
+ * Check if access is allowed. If true, then check to see if an
+ * entry test rule was found (no targetattrs) during target match
+ * evaluation. If such a rule was found, set the current attribute
+ * type to "null" and check access again so that rule is applied.
+ */
+ if (accessAllowed(container))
+ {
+ if (container.hasEntryTestRule())
+ {
+ container.setCurrentAttributeType(null);
+ if (!accessAllowed(container))
+ {
+ /*
+ * If we failed because of a deny permission-bind rule, we
+ * need to stop and return false.
+ */
+ if (container.isDenyEval())
+ {
+ return false;
+ }
+ /*
+ * If we failed because there was no explicit allow rule,
+ * then we grant implicit access to the entry.
+ */
+ }
+ }
+ return true;
+ }
+ }
+ return ret;
+ }
+
+
+
+ /**
+ * Performs an access check against all of the attributes of an entry.
+ * The attributes that fail access are removed from the entry. This
+ * method performs the processing needed for the filterEntry method
+ * processing.
+ *
+ * @param container
+ * The search or compare container which has all of the
+ * information needed to filter the attributes for this
+ * entry.
+ * @return The entry to send back to the client, minus any attribute
+ * types that failed access check.
+ */
+ private SearchResultEntry accessAllowedAttrs(
+ AciLDAPOperationContainer container)
+ {
+ Entry e = container.getResourceEntry();
+ List<AttributeType> typeList = getAllAttrs(e);
+ for (AttributeType attrType : typeList)
+ {
+ if (container.hasAllUserAttributes() && !attrType.isOperational())
+ {
+ continue;
+ }
+ if (container.hasAllOpAttributes() && attrType.isOperational())
+ {
+ continue;
+ }
+ container.setCurrentAttributeType(attrType);
+ if (!accessAllowed(container))
+ {
+ e.removeAttribute(attrType);
+ }
+ }
+ return container.getSearchResultEntry();
+ }
+
+
+
+ /**
+ * Checks to see if a LDAP modification is allowed access.
+ *
+ * @param container
+ * The structure containing the LDAP modifications
+ * @param operation
+ * The operation to check modify privileges on. operation to
+ * check and the evaluation context to apply the check
+ * against.
+ * @param skipAccessCheck
+ * True if access checking should be skipped.
+ * @return True if access is allowed.
+ */
+ private boolean aciCheckMods(AciLDAPOperationContainer container,
+ LocalBackendModifyOperation operation, boolean skipAccessCheck)
+ {
+ Entry resourceEntry = container.getResourceEntry();
+ DN dn = resourceEntry.getDN();
+ List<Modification> modifications = container.getModifications();
+ for (Modification m : modifications)
+ {
+ Attribute modAttr = m.getAttribute();
+ AttributeType modAttrType = modAttr.getAttributeType();
+
+ if (modAttrType.equals(aciType))
+ {
+ /*
+ * Check that the operation has modify privileges if it contains
+ * an "aci" attribute type.
+ */
+ if (!operation.getClientConnection().hasPrivilege(
+ Privilege.MODIFY_ACL, operation))
+ {
+ Message message =
+ INFO_ACI_MODIFY_FAILED_PRIVILEGE.get(String
+ .valueOf(container.getResourceDN()), String
+ .valueOf(container.getClientDN()));
+ logError(message);
+ return false;
+ }
+ }
+ // This access check handles the case where all attributes of this
+ // type are being replaced or deleted. If only a subset is being
+ // deleted than this access check is skipped.
+ ModificationType modType = m.getModificationType();
+ if (((modType == ModificationType.DELETE) && modAttr.isEmpty())
+ || ((modType == ModificationType.REPLACE)
+ || (modType == ModificationType.INCREMENT)))
+ {
+ /*
+ * Check if we have rights to delete all values of an attribute
+ * type in the resource entry.
+ */
+ if (resourceEntry.hasAttribute(modAttrType))
+ {
+ container.setCurrentAttributeType(modAttrType);
+ List<Attribute> attrList =
+ resourceEntry.getAttribute(modAttrType, modAttr
+ .getOptions());
+ if (attrList != null)
+ {
+ for (Attribute a : attrList)
+ {
+ for (AttributeValue v : a)
+ {
+ container.setCurrentAttributeValue(v);
+ container.setRights(ACI_WRITE_DELETE);
+ if (!skipAccessCheck && !accessAllowed(container))
+ {
+ return false;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (!modAttr.isEmpty())
+ {
+ for (AttributeValue v : modAttr)
+ {
+ container.setCurrentAttributeType(modAttrType);
+ switch (m.getModificationType())
+ {
+ case ADD:
+ case REPLACE:
+ container.setCurrentAttributeValue(v);
+ container.setRights(ACI_WRITE_ADD);
+ if (!skipAccessCheck && !accessAllowed(container))
+ {
+ return false;
+ }
+ break;
+ case DELETE:
+ container.setCurrentAttributeValue(v);
+ container.setRights(ACI_WRITE_DELETE);
+ if (!skipAccessCheck && !accessAllowed(container))
+ {
+ return false;
+ }
+ break;
+ case INCREMENT:
+ Entry modifiedEntry = operation.getModifiedEntry();
+ List<Attribute> modifiedAttrs =
+ modifiedEntry.getAttribute(modAttrType, modAttr
+ .getOptions());
+ if (modifiedAttrs != null)
+ {
+ for (Attribute attr : modifiedAttrs)
+ {
+ for (AttributeValue val : attr)
+ {
+ container.setCurrentAttributeValue(val);
+ container.setRights(ACI_WRITE_ADD);
+ if (!skipAccessCheck && !accessAllowed(container))
+ {
+ return false;
+ }
+ }
+ }
+ }
+ break;
+ }
+ /*
+ * Check if the modification type has an "aci" attribute type.
+ * If so, check the syntax of that attribute value. Fail the
+ * the operation if the syntax check fails.
+ */
+ if (modAttrType.equals(aciType)
+ || modAttrType.equals(globalAciType))
+ {
+ try
+ {
+ // A global ACI needs a NULL DN, not the DN of the
+ // modification.
+ if (modAttrType.equals(globalAciType))
+ {
+ dn = DN.nullDN();
+ }
+ Aci.decode(v.getValue(), dn);
+ }
+ catch (AciException ex)
+ {
+ Message message =
+ WARN_ACI_MODIFY_FAILED_DECODE.get(String.valueOf(dn),
+ ex.getMessage());
+ logError(message);
+ return false;
+ }
+ }
+ }
+ }
+ }
+ return true;
+ }
+
+
+
+ /**
+ * Perform all needed RDN checks for the modifyDN operation. The old
+ * RDN is not equal to the new RDN. The access checks are: - Verify
+ * WRITE access to the original entry. - Verfiy WRITE_ADD access on
+ * each RDN component of the new RDN. The WRITE_ADD access is used
+ * because this access could be restricted by the targattrfilters
+ * keyword. - If the deleteOLDRDN flag is set, verify WRITE_DELETE
+ * access on the old RDN. The WRITE_DELETE access is used because this
+ * access could be restricted by the targattrfilters keyword.
+ *
+ * @param operation
+ * The ModifyDN operation class containing information to
+ * check access on.
+ * @param oldRDN
+ * The old RDN component.
+ * @param newRDN
+ * The new RDN component.
+ * @return True if access is allowed.
+ */
+ private boolean aciCheckRDNs(LocalBackendModifyDNOperation operation,
+ RDN oldRDN, RDN newRDN)
+ {
+ boolean ret;
+
+ AciLDAPOperationContainer operationContainer =
+ new AciLDAPOperationContainer(operation, (ACI_WRITE), operation
+ .getOriginalEntry());
+ ret = accessAllowed(operationContainer);
+ if (ret)
+ {
+ ret = checkRDN(ACI_WRITE_ADD, newRDN, operationContainer);
+ }
+ if (ret && operation.deleteOldRDN())
+ {
+ ret = checkRDN(ACI_WRITE_DELETE, oldRDN, operationContainer);
+ }
+ return ret;
+ }
+
+
+
+ /**
+ * Check access on the new superior entry if it exists. If the entry
+ * does not exist or the DN cannot be locked then false is returned.
+ *
+ * @param superiorDN
+ * The DN of the new superior entry.
+ * @param op
+ * The modifyDN operation to check access on.
+ * @return True if access is granted to the new superior entry.
+ * @throws DirectoryException
+ * If a problem occurs while trying to retrieve the new
+ * superior entry.
+ */
+ private boolean aciCheckSuperiorEntry(DN superiorDN,
+ LocalBackendModifyDNOperation op) throws DirectoryException
+ {
+ boolean ret = false;
+ Lock entryLock = null;
+ for (int i = 0; i < 3; i++)
+ {
+ entryLock = LockManager.lockRead(superiorDN);
+ if (entryLock != null)
+ {
+ break;
+ }
+ }
+ if (entryLock == null)
+ {
+ Message message =
+ WARN_ACI_HANDLER_CANNOT_LOCK_NEW_SUPERIOR_USER.get(String
+ .valueOf(superiorDN));
+ logError(message);
+ return false;
+ }
+ try
+ {
+ Entry superiorEntry = DirectoryServer.getEntry(superiorDN);
+ if (superiorEntry != null)
+ {
+ AciLDAPOperationContainer operationContainer =
+ new AciLDAPOperationContainer(op, (ACI_IMPORT),
+ superiorEntry);
+ ret = accessAllowed(operationContainer);
+ }
+ }
+ finally
+ {
+ LockManager.unlock(superiorDN, entryLock);
+ }
+ return ret;
+ }
+
+
+
+ /**
+ * Check access on each attribute-value pair component of the
+ * specified RDN. There may be more than one attribute-value pair if
+ * the RDN is multi-valued.
+ *
+ * @param right
+ * The access right to check for.
+ * @param rdn
+ * The RDN to examine the attribute-value pairs of.
+ * @param container
+ * The container containing the information needed to
+ * evaluate the specified RDN.
+ * @return True if access is allowed for all attribute-value pairs.
+ */
+ private boolean checkRDN(int right, RDN rdn, AciContainer container)
+ {
+ boolean ret = false;
+ int numAVAs = rdn.getNumValues();
+ container.setRights(right);
+ for (int i = 0; i < numAVAs; i++)
+ {
+ AttributeType type = rdn.getAttributeType(i);
+ AttributeValue value = rdn.getAttributeValue(i);
+ container.setCurrentAttributeType(type);
+ container.setCurrentAttributeValue(value);
+ if (!(ret = accessAllowed(container)))
+ {
+ break;
+ }
+ }
+ return ret;
+ }
+
+
+
+ /**
+ * Creates the allow and deny ACI lists based on the provided target
+ * match context. These lists are stored in the evaluation context.
+ *
+ * @param candidates
+ * List of all possible ACI candidates.
+ * @param targetMatchCtx
+ * Target matching context to use for testing each ACI.
+ */
+ private void createApplicableList(LinkedList<Aci> candidates,
+ AciTargetMatchContext targetMatchCtx)
+ {
+ LinkedList<Aci> denys = new LinkedList<Aci>();
+ LinkedList<Aci> allows = new LinkedList<Aci>();
+ for (Aci aci : candidates)
+ {
+ if (Aci.isApplicable(aci, targetMatchCtx))
+ {
+ if (aci.hasAccessType(EnumAccessType.DENY))
+ {
+ denys.add(aci);
+ }
+ if (aci.hasAccessType(EnumAccessType.ALLOW))
+ {
+ allows.add(aci);
+ }
+ }
+ if (targetMatchCtx.getTargAttrFiltersMatch())
+ {
+ targetMatchCtx.setTargAttrFiltersMatch(false);
+ }
+ }
+ targetMatchCtx.setAllowList(allows);
+ targetMatchCtx.setDenyList(denys);
+ }
+
+
+
+ /**
+ * Gathers all of the attribute types in an entry along with the
+ * "objectclass" attribute type in a List. The "objectclass" attribute
+ * is added to the list first so it is evaluated first.
+ *
+ * @param e
+ * Entry to gather the attributes for.
+ * @return List containing the attribute types.
+ */
+ private List<AttributeType> getAllAttrs(Entry e)
+ {
+ Map<AttributeType, List<Attribute>> attrMap = e.getUserAttributes();
+ Map<AttributeType, List<Attribute>> opAttrMap =
+ e.getOperationalAttributes();
+ List<AttributeType> typeList = new LinkedList<AttributeType>();
+ Attribute attr = e.getObjectClassAttribute();
+ /*
+ * When a search is not all attributes returned, the "objectclass"
+ * attribute type is missing from the entry.
+ */
+ if (attr != null)
+ {
+ AttributeType ocType = attr.getAttributeType();
+ typeList.add(ocType);
+ }
+ typeList.addAll(attrMap.keySet());
+ typeList.addAll(opAttrMap.keySet());
+ return typeList;
+ }
+
+
+
+ /**
+ * Check access using the accessAllowed method. The LDAP add, compare,
+ * modify and delete operations use this function. The other supported
+ * LDAP operations have more specialized checks.
+ *
+ * @param operationContainer
+ * The container containing the information needed to
+ * evaluate this operation.
+ * @param operation
+ * The operation being evaluated.
+ * @return True if this operation is allowed access.
+ */
+ private boolean isAllowed(
+ AciLDAPOperationContainer operationContainer, Operation operation)
+ {
+ return skipAccessCheck(operation)
+ || accessAllowed(operationContainer);
+ }
+
+
+
+ /**
+ * Check if the specified attribute type is a DN by checking if its
+ * syntax OID is equal to the DN syntax OID.
+ *
+ * @param attribute
+ * The attribute type to check.
+ * @return True if the attribute type syntax OID is equal to a DN
+ * syntax OID.
+ */
+ private boolean isAttributeDN(AttributeType attribute)
+ {
+ return (attribute.getSyntaxOID().equals(SYNTAX_DN_OID));
+ }
+
+
+
+ /**
+ * Process all ACIs under the "cn=config" naming context and adds them
+ * to the ACI list cache. It also logs messages about the number of
+ * ACIs added to the cache. This method is called once at startup. It
+ * will put the server in lockdown mode if needed.
+ *
+ * @throws InitializationException
+ * If there is an error searching for the ACIs in the naming
+ * context.
+ */
+ private void processConfigAcis() throws InitializationException
+ {
+ try
+ {
+ DN configDN = DN.decode("cn=config");
+ LinkedHashSet<String> attrs = new LinkedHashSet<String>(1);
+ attrs.add("aci");
+ LinkedList<Message> failedACIMsgs = new LinkedList<Message>();
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ InternalSearchOperation op =
+ conn.processSearch(configDN, SearchScope.WHOLE_SUBTREE,
+ DereferencePolicy.NEVER_DEREF_ALIASES, 0, 0, false,
+ SearchFilter.createFilterFromString("aci=*"), attrs);
+ if (!op.getSearchEntries().isEmpty())
+ {
+ int validAcis =
+ aciList.addAci(op.getSearchEntries(), failedACIMsgs);
+ if (!failedACIMsgs.isEmpty())
+ {
+ aciListenerMgr.logMsgsSetLockDownMode(failedACIMsgs);
+ }
+ Message message =
+ INFO_ACI_ADD_LIST_ACIS.get(Integer.toString(validAcis),
+ String.valueOf(configDN));
+ logError(message);
+ }
+ }
+ catch (DirectoryException e)
+ {
+ Message message = INFO_ACI_HANDLER_FAIL_PROCESS_ACI.get();
+ throw new InitializationException(message, e);
+ }
+ }
+
+
+
+ /**
+ * Process all global ACI attribute types found in the configuration
+ * entry and adds them to that ACI list cache. It also logs messages
+ * about the number of ACI attribute types added to the cache. This
+ * method is called once at startup. It also will put the server into
+ * lockdown mode if needed.
+ *
+ * @param configuration
+ * The config handler containing the ACI configuration
+ * information.
+ * @throws InitializationException
+ * If there is an error reading the global ACIs from the
+ * configuration entry.
+ */
+ private void processGlobalAcis(
+ DseeCompatAccessControlHandlerCfg configuration)
+ throws InitializationException
+ {
+ SortedSet<Aci> globalAcis = configuration.getGlobalACI();
+ try
+ {
+ if (globalAcis != null)
+ {
+ aciList.addAci(DN.nullDN(), globalAcis);
+ Message message =
+ INFO_ACI_ADD_LIST_GLOBAL_ACIS.get(Integer
+ .toString(globalAcis.size()));
+ logError(message);
+ }
+ }
+ catch (Exception e)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ }
+ Message message =
+ INFO_ACI_HANDLER_FAIL_PROCESS_GLOBAL_ACI.get(String
+ .valueOf(configuration.dn()));
+ throw new InitializationException(message, e);
+ }
+ }
+
+
+
+ /**
+ * Check to see if the specified entry has the specified privilege.
+ *
+ * @param e
+ * The entry to check privileges on.
+ * @return {@code true} if the entry has the specified privilege, or
+ * {@code false} if not.
+ */
+ private boolean skipAccessCheck(Entry e)
+ {
+ return ClientConnection.hasPrivilege(e, Privilege.BYPASS_ACL);
+ }
+
+
+
+ /**
+ * Check to see if the client entry has BYPASS_ACL privileges for this
+ * operation.
+ *
+ * @param operation
+ * The operation to check privileges on.
+ * @return True if access checking can be skipped because the
+ * operation client connection has BYPASS_ACL privileges.
+ */
+ private boolean skipAccessCheck(Operation operation)
+ {
+ return operation.getClientConnection().hasPrivilege(
+ Privilege.BYPASS_ACL, operation);
+ }
+
+
+
+ /**
+ * Performs the test of the deny and allow access lists using the
+ * provided evaluation context. The deny list is checked first.
+ *
+ * @param evalCtx
+ * The evaluation context to use.
+ * @return True if access is allowed.
+ */
+ private boolean testApplicableLists(AciEvalContext evalCtx)
+ {
+ EnumEvalResult res;
+ 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))
+ {
+ 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);
+ for (Aci allowAci : allows)
+ {
+ 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;
+ }
+ }
+ }
+ // Nothing matched fall through.
+ evalCtx.setEvalReason(EnumEvalReason.NO_MATCHED_ALLOWS_ACIS);
+ evalCtx.setDecidingAci(null);
+ return false;
+ }
+
+
+
+ /**
+ * Test the attribute types of the search filter for access. This
+ * method supports the search right.
+ *
+ * @param container
+ * The container used in the access evaluation.
+ * @param filter
+ * The filter to check access on.
+ * @return True if all attribute types in the filter have access.
+ * @throws DirectoryException
+ * If there is a problem matching the entry using the
+ * provided filter.
+ */
+ private boolean testFilter(AciLDAPOperationContainer container,
+ SearchFilter filter) throws DirectoryException
+ {
+ boolean ret = true;
+ // If the resource entry has a dn equal to "cn=debugsearch" and it
+ // contains the special attribute type "debugsearchindex", then the
+ // resource entry is a pseudo entry created for debug purposes.
+ // Return true if that is the case.
+ if (debugSearchIndexDN.equals(container.getResourceDN())
+ && container.getResourceEntry().hasAttribute(debugSearchIndex))
+ {
return true;
+ }
+ switch (filter.getFilterType())
+ {
+ case AND:
+ case OR:
+ {
+ for (SearchFilter f : filter.getFilterComponents())
+ {
+ if (!testFilter(container, f))
+ {
+ return false;
+ }
+ }
+ break;
+ }
+ case NOT:
+ {
+ ret = false;
+ SearchFilter f = filter.getNotComponent();
+ if (f.matchesEntry(container.getResourceEntry()))
+ {
+ ret = true;
+ }
+ if (ret)
+ {
+ ret = testFilter(container, f);
+ }
+ ret = !ret;
+ break;
+ }
+ default:
+ {
+ AttributeType attrType = filter.getAttributeType();
+ container.setCurrentAttributeType(attrType);
+ ret = accessAllowed(container);
+ }
+ }
+ return ret;
+ }
+
+
+
+ /**
+ * Evaluate an entry to be added to see if it has any "aci" attribute
+ * type. If it does, examines each "aci" attribute type value for
+ * syntax errors. All of the "aci" attribute type values must pass
+ * syntax check for the add operation to proceed. Any entry with an
+ * "aci" attribute type must have "modify-acl" privileges.
+ *
+ * @param entry
+ * The entry to be examined.
+ * @param operation
+ * The operation to to check privileges on.
+ * @param clientDN
+ * The authorization DN.
+ * @return True if the entry has no ACI attributes or if all of the
+ * "aci" attributes values pass ACI syntax checking.
+ */
+ private boolean verifySyntax(Entry entry, Operation operation,
+ DN clientDN)
+ {
+ if (entry.hasOperationalAttribute(aciType))
+ {
+ /*
+ * Check that the operation has "modify-acl" privileges since the
+ * entry to be added has an "aci" attribute type.
+ */
+ if (!operation.getClientConnection().hasPrivilege(
+ Privilege.MODIFY_ACL, operation))
+ {
+ Message message =
+ INFO_ACI_ADD_FAILED_PRIVILEGE.get(String.valueOf(entry
+ .getDN()), String.valueOf(clientDN));
+ logError(message);
+ return false;
+ }
+ List<Attribute> attributeList =
+ entry.getOperationalAttribute(aciType, null);
+ for (Attribute attribute : attributeList)
+ {
+ for (AttributeValue value : attribute)
+ {
+ try
+ {
+ DN dn = entry.getDN();
+ Aci.decode(value.getValue(), dn);
+ }
+ catch (AciException ex)
+ {
+ Message message =
+ WARN_ACI_ADD_FAILED_DECODE.get(String.valueOf(entry
+ .getDN()), ex.getMessage());
+ logError(message);
+ return false;
+ }
+ }
+ }
+ }
+ return true;
}
}
-
--
Gitblit v1.10.0