From b1e3b0ccdaa423b68ef6fa2fee67d3e09990985f Mon Sep 17 00:00:00 2001
From: neil_a_wilson <neil_a_wilson@localhost>
Date: Mon, 24 Sep 2007 23:15:46 +0000
Subject: [PATCH] Perform a significant refactoring of the local backend workflow element classes so that they use smaller method sizes, which allows the VM to better optimize the code. Also, move the code for processing each type of operation into the class for the associated operation rather than keeping it all in the LocalBackendWorkflowElement class.
---
opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendModifyOperation.java | 2321 +++++++
opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendSearchOperation.java | 624 ++
opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElement.java | 8625 -----------------------------
opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendBindOperation.java | 1260 ++++
opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendModifyDNOperation.java | 1347 ++++
opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendCompareOperation.java | 562 +
opends/src/server/org/opends/server/workflowelement/WorkflowElement.java | 31
opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendDeleteOperation.java | 807 ++
opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendAddOperation.java | 1539 +++++
9 files changed, 8,375 insertions(+), 8,741 deletions(-)
diff --git a/opends/src/server/org/opends/server/workflowelement/WorkflowElement.java b/opends/src/server/org/opends/server/workflowelement/WorkflowElement.java
index fb1a6cc..ce81dd2 100644
--- a/opends/src/server/org/opends/server/workflowelement/WorkflowElement.java
+++ b/opends/src/server/org/opends/server/workflowelement/WorkflowElement.java
@@ -40,17 +40,14 @@
*/
public abstract class WorkflowElement
{
+ // Indicates whether the workflow element encapsulates a private local
+ // backend.
+ private boolean isPrivate = false;
// The workflow element identifier.
private String workflowElementID = null;
- /**
- * Indicates whether the workflow element encapsulates a private
- * local backend.
- */
- protected boolean isPrivate = false;
-
/**
* Creates a new instance of the workflow element.
@@ -64,14 +61,14 @@
}
+
/**
* Executes the workflow element for an operation.
*
* @param operation the operation to execute
*/
- public abstract void execute(
- Operation operation
- );
+ public abstract void execute(Operation operation);
+
/**
@@ -87,6 +84,21 @@
}
+
+ /**
+ * Specifies whether the workflow element encapsulates a private local
+ * backend.
+ *
+ * @param isPrivate Indicates whether the workflow element encapsulates a
+ * private local backend.
+ */
+ protected void setPrivate(boolean isPrivate)
+ {
+ this.isPrivate = isPrivate;
+ }
+
+
+
/**
* Provides the workflow element identifier.
*
@@ -97,3 +109,4 @@
return workflowElementID;
}
}
+
diff --git a/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendAddOperation.java b/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendAddOperation.java
index 89e40ea..8207729 100644
--- a/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendAddOperation.java
+++ b/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendAddOperation.java
@@ -25,28 +25,43 @@
* Portions Copyright 2007 Sun Microsystems, Inc.
*/
package org.opends.server.workflowelement.localbackend;
-import org.opends.messages.Message;
-import org.opends.messages.MessageBuilder;
-import static org.opends.server.config.ConfigConstants.*;
-import static org.opends.messages.CoreMessages.*;
-import static org.opends.server.util.ServerConstants.*;
+
import java.util.ArrayList;
import java.util.HashSet;
+import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.locks.Lock;
+import org.opends.messages.Message;
+import org.opends.messages.MessageBuilder;
+import org.opends.server.api.AttributeSyntax;
+import org.opends.server.api.Backend;
+import org.opends.server.api.ChangeNotificationListener;
+import org.opends.server.api.ClientConnection;
import org.opends.server.api.PasswordStorageScheme;
import org.opends.server.api.PasswordValidator;
+import org.opends.server.api.SynchronizationProvider;
+import org.opends.server.api.plugin.PostOperationPluginResult;
+import org.opends.server.api.plugin.PreOperationPluginResult;
+import org.opends.server.controls.LDAPAssertionRequestControl;
+import org.opends.server.controls.LDAPPostReadRequestControl;
+import org.opends.server.controls.LDAPPostReadResponseControl;
import org.opends.server.controls.PasswordPolicyErrorType;
import org.opends.server.controls.PasswordPolicyResponseControl;
+import org.opends.server.controls.ProxiedAuthV1Control;
+import org.opends.server.controls.ProxiedAuthV2Control;
+import org.opends.server.core.AccessControlConfigManager;
import org.opends.server.core.AddOperation;
import org.opends.server.core.AddOperationWrapper;
import org.opends.server.core.DirectoryServer;
import org.opends.server.core.PasswordPolicy;
+import org.opends.server.core.PluginConfigManager;
+import org.opends.server.loggers.debug.DebugTracer;
import org.opends.server.protocols.asn1.ASN1OctetString;
import org.opends.server.schema.AuthPasswordSyntax;
import org.opends.server.schema.BooleanSyntax;
@@ -55,31 +70,82 @@
import org.opends.server.types.AttributeType;
import org.opends.server.types.AttributeValue;
import org.opends.server.types.ByteString;
+import org.opends.server.types.CancelledOperationException;
+import org.opends.server.types.CancelResult;
import org.opends.server.types.Control;
+import org.opends.server.types.DebugLogLevel;
import org.opends.server.types.DirectoryException;
+import org.opends.server.types.DN;
import org.opends.server.types.Entry;
+import org.opends.server.types.LDAPException;
+import org.opends.server.types.LockManager;
import org.opends.server.types.ObjectClass;
+import org.opends.server.types.Privilege;
+import org.opends.server.types.RDN;
import org.opends.server.types.ResultCode;
+import org.opends.server.types.SearchFilter;
+import org.opends.server.types.SearchResultEntry;
+import org.opends.server.types.SynchronizationProviderResult;
import org.opends.server.types.operation.PostOperationAddOperation;
import org.opends.server.types.operation.PostResponseAddOperation;
import org.opends.server.types.operation.PreOperationAddOperation;
import org.opends.server.types.operation.PostSynchronizationAddOperation;
import org.opends.server.util.TimeThread;
+import static org.opends.messages.CoreMessages.*;
+import static org.opends.server.loggers.ErrorLogger.*;
+import static org.opends.server.loggers.debug.DebugLogger.*;
+import static org.opends.server.config.ConfigConstants.*;
+import static org.opends.server.util.ServerConstants.*;
+import static org.opends.server.util.StaticUtils.*;
+
+
+
/**
* This class defines an operation used to add an entry in a local backend
* of the Directory Server.
*/
-public class LocalBackendAddOperation extends AddOperationWrapper
- implements PreOperationAddOperation,
- PostOperationAddOperation,
- PostResponseAddOperation,
- PostSynchronizationAddOperation
+public class LocalBackendAddOperation
+ extends AddOperationWrapper
+ implements PreOperationAddOperation, PostOperationAddOperation,
+ PostResponseAddOperation, PostSynchronizationAddOperation
{
+ /**
+ * The tracer object for the debug logger.
+ */
+ private static final DebugTracer TRACER = getTracer();
+
+
+
+ // The backend in which the entry is to be added.
+ private Backend backend;
+
+ // Indicates whether the request includes the LDAP no-op control.
+ private boolean noOp;
+
+ // Indicates whether to skip post-operation plugin processing.
+ private boolean skipPostOperation;
+
+ // The DN of the entry to be added.
+ private DN entryDN;
// The entry being added to the server.
private Entry entry;
+ // The post-read request control included in the request, if applicable.
+ LDAPPostReadRequestControl postReadRequest;
+
+ // The set of object classes for the entry to add.
+ private Map<ObjectClass, String> objectClasses;
+
+ // The set of operational attributes for the entry to add.
+ private Map<AttributeType,List<Attribute>> operationalAttributes;
+
+ // The set of user attributes for the entry to add.
+ private Map<AttributeType,List<Attribute>> userAttributes;
+
+
+
/**
* Creates a new operation that may be used to add a new entry in a
* local backend of the Directory Server.
@@ -89,10 +155,12 @@
public LocalBackendAddOperation(AddOperation add)
{
super(add);
+
LocalBackendWorkflowElement.attachLocalOperation (add, this);
}
+
/**
* Retrieves the entry to be added to the server. Note that this will not be
* available to pre-parse plugins or during the conflict resolution portion of
@@ -106,34 +174,983 @@
return entry;
}
+
+
/**
- * Sets the entry to be added to the server.
+ * Process this add operation against a local backend.
*
- * @param entry - The entry to be added to the server, or <CODE>null</CODE>
- * if it is not yet available.
+ * @param backend The backend in which the add operation should be
+ * processed.
*/
- public final void setEntryToAdd(Entry entry){
- this.entry = entry;
+ void processLocalAdd(Backend backend)
+ {
+ this.backend = backend;
+ ClientConnection clientConnection = getClientConnection();
+
+ // Get the plugin config manager that will be used for invoking plugins.
+ PluginConfigManager pluginConfigManager =
+ DirectoryServer.getPluginConfigManager();
+ skipPostOperation = false;
+
+ // Check for a request to cancel this operation.
+ if (getCancelRequest() != null)
+ {
+ return;
+ }
+
+ // Create a labeled block of code that we can break out of if a problem is
+ // detected.
+addProcessing:
+ {
+ // Process the entry DN and set of attributes to convert them from their
+ // raw forms as provided by the client to the forms required for the rest
+ // of the add processing.
+ entryDN = getEntryDN();
+ if (entryDN == null)
+ {
+ break addProcessing;
+ }
+
+ objectClasses = getObjectClasses();
+ userAttributes = getUserAttributes();
+ operationalAttributes = getOperationalAttributes();
+
+ if ((objectClasses == null ) || (userAttributes == null) ||
+ (operationalAttributes == null))
+ {
+ break addProcessing;
+ }
+
+ // Check for a request to cancel this operation.
+ if (getCancelRequest() != null)
+ {
+ return;
+ }
+
+
+ // Grab a read lock on the parent entry, if there is one. We need to do
+ // this to ensure that the parent is not deleted or renamed while this add
+ // is in progress, and we could also need it to check the entry against
+ // a DIT structure rule.
+ Lock parentLock = null;
+ Lock entryLock = null;
+
+ DN parentDN = entryDN.getParentDNInSuffix();
+ try
+ {
+ parentLock = lockParent(parentDN);
+ }
+ catch (DirectoryException de)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, de);
+ }
+
+ setResponseData(de);
+ break addProcessing;
+ }
+
+ try
+ {
+ // Check for a request to cancel this operation.
+ if (getCancelRequest() != null)
+ {
+ return;
+ }
+
+
+ // Grab a write lock on the target entry. We'll need to do this
+ // eventually anyway, and we want to make sure that the two locks are
+ // always released when exiting this method, no matter what. Since
+ // the entry shouldn't exist yet, locking earlier than necessary
+ // shouldn't cause a problem.
+ for (int i=0; i < 3; i++)
+ {
+ entryLock = LockManager.lockWrite(entryDN);
+ if (entryLock != null)
+ {
+ break;
+ }
+ }
+
+ if (entryLock == null)
+ {
+ setResultCode(DirectoryServer.getServerErrorResultCode());
+ appendErrorMessage(ERR_ADD_CANNOT_LOCK_ENTRY.get(
+ String.valueOf(entryDN)));
+
+ skipPostOperation = true;
+ break addProcessing;
+ }
+
+
+ // Invoke any conflict resolution processing that might be needed by the
+ // synchronization provider.
+ for (SynchronizationProvider provider :
+ DirectoryServer.getSynchronizationProviders())
+ {
+ try
+ {
+ SynchronizationProviderResult result =
+ provider.handleConflictResolution(this);
+ if (! result.continueOperationProcessing())
+ {
+ break addProcessing;
+ }
+ }
+ catch (DirectoryException de)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, de);
+ }
+
+ logError(ERR_ADD_SYNCH_CONFLICT_RESOLUTION_FAILED.get(
+ getConnectionID(), getOperationID(),
+ getExceptionMessage(de)));
+
+ setResponseData(de);
+ break addProcessing;
+ }
+ }
+
+ for (AttributeType at : userAttributes.keySet())
+ {
+ // If the attribute type is marked "NO-USER-MODIFICATION" then fail
+ // unless this is an internal operation or is related to
+ // synchronization in some way.
+ // This must be done before running the password policy code
+ // and any other code that may add attributes marked as
+ // "NO-USER-MODIFICATION"
+ //
+ // Note that doing this checks at this time
+ // of the processing does not make it possible for pre-parse plugins
+ // to add NO-USER-MODIFICATION attributes to the entry.
+ if (at.isNoUserModification())
+ {
+ if (! (isInternalOperation() || isSynchronizationOperation()))
+ {
+ setResultCode(ResultCode.UNWILLING_TO_PERFORM);
+ appendErrorMessage(ERR_ADD_ATTR_IS_NO_USER_MOD.get(
+ String.valueOf(entryDN),
+ at.getNameOrOID()));
+
+ break addProcessing;
+ }
+ }
+ }
+
+ for (AttributeType at : operationalAttributes.keySet())
+ {
+ if (at.isNoUserModification())
+ {
+ if (! (isInternalOperation() || isSynchronizationOperation()))
+ {
+ setResultCode(ResultCode.UNWILLING_TO_PERFORM);
+ appendErrorMessage(ERR_ADD_ATTR_IS_NO_USER_MOD.get(
+ String.valueOf(entryDN),
+ at.getNameOrOID()));
+
+ break addProcessing;
+ }
+ }
+ }
+
+ // Check to see if the entry already exists. We do this before
+ // checking whether the parent exists to ensure a referral entry
+ // above the parent results in a correct referral.
+ try
+ {
+ if (DirectoryServer.entryExists(entryDN))
+ {
+ setResultCode(ResultCode.ENTRY_ALREADY_EXISTS);
+ appendErrorMessage(ERR_ADD_ENTRY_ALREADY_EXISTS.get(
+ String.valueOf(entryDN)));
+ break addProcessing;
+ }
+ }
+ catch (DirectoryException de)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, de);
+ }
+
+ setResponseData(de);
+ break addProcessing;
+ }
+
+
+ // Get the parent entry, if it exists.
+ Entry parentEntry = null;
+ if (parentDN != null)
+ {
+ try
+ {
+ parentEntry = DirectoryServer.getEntry(parentDN);
+
+ if (parentEntry == null)
+ {
+ DN matchedDN = parentDN.getParentDNInSuffix();
+ while (matchedDN != null)
+ {
+ try
+ {
+ if (DirectoryServer.entryExists(matchedDN))
+ {
+ setMatchedDN(matchedDN);
+ break;
+ }
+ }
+ catch (Exception e)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ }
+ break;
+ }
+
+ matchedDN = matchedDN.getParentDNInSuffix();
+ }
+
+
+ // The parent doesn't exist, so this add can't be successful.
+ setResultCode(ResultCode.NO_SUCH_OBJECT);
+ appendErrorMessage(ERR_ADD_NO_PARENT.get(String.valueOf(entryDN),
+ String.valueOf(parentDN)));
+ break addProcessing;
+ }
+ }
+ catch (DirectoryException de)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, de);
+ }
+
+ setResponseData(de);
+ break addProcessing;
+ }
+ }
+
+
+ // Check to make sure that all of the RDN attributes are included as
+ // attribute values. If not, then either add them or report an error.
+ try
+ {
+ addRDNAttributesIfNecessary();
+ }
+ catch (DirectoryException de)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, de);
+ }
+
+ setResponseData(de);
+ break addProcessing;
+ }
+
+
+ // Check to make sure that all objectclasses have their superior classes
+ // listed in the entry. If not, then add them.
+ HashSet<ObjectClass> additionalClasses = null;
+ for (ObjectClass oc : objectClasses.keySet())
+ {
+ ObjectClass superiorClass = oc.getSuperiorClass();
+ if ((superiorClass != null) &&
+ (! objectClasses.containsKey(superiorClass)))
+ {
+ if (additionalClasses == null)
+ {
+ additionalClasses = new HashSet<ObjectClass>();
+ }
+
+ additionalClasses.add(superiorClass);
+ }
+ }
+
+ if (additionalClasses != null)
+ {
+ for (ObjectClass oc : additionalClasses)
+ {
+ addObjectClassChain(oc);
+ }
+ }
+
+
+ // Create an entry object to encapsulate the set of attributes and
+ // objectclasses.
+ entry = new Entry(entryDN, objectClasses, userAttributes,
+ operationalAttributes);
+
+ // Check to see if the entry includes a privilege specification. If so,
+ // then the requester must have the PRIVILEGE_CHANGE privilege.
+ AttributeType privType =
+ DirectoryServer.getAttributeType(OP_ATTR_PRIVILEGE_NAME, true);
+ if (entry.hasAttribute(privType) &&
+ (! clientConnection.hasPrivilege(Privilege.PRIVILEGE_CHANGE, this)))
+ {
+
+ appendErrorMessage(
+ ERR_ADD_CHANGE_PRIVILEGE_INSUFFICIENT_PRIVILEGES.get());
+ setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS);
+ break addProcessing;
+ }
+
+
+ // If it's not a synchronization operation, then check
+ // to see if the entry contains one or more passwords and if they
+ // are valid in accordance with the password policies associated with
+ // the user. Also perform any encoding that might be required by
+ // password storage schemes.
+ if (! isSynchronizationOperation())
+ {
+ try
+ {
+ handlePasswordPolicy();
+ }
+ catch (DirectoryException de)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, de);
+ }
+
+ setResponseData(de);
+ break addProcessing;
+ }
+ }
+
+
+ // If the server is configured to check schema and the
+ // operation is not a synchronization operation,
+ // check to see if the entry is valid according to the server schema,
+ // and also whether its attributes are valid according to their syntax.
+ if ((DirectoryServer.checkSchema()) && (! isSynchronizationOperation()))
+ {
+ try
+ {
+ checkSchema(parentEntry);
+ }
+ catch (DirectoryException de)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, de);
+ }
+
+ setResponseData(de);
+ break addProcessing;
+ }
+ }
+
+
+ // Get the backend in which the add is to be performed.
+ if (backend == null)
+ {
+ setResultCode(ResultCode.NO_SUCH_OBJECT);
+ appendErrorMessage(Message.raw("No backend for entry " +
+ entryDN.toString())); // TODO: i18n
+ break addProcessing;
+ }
+
+
+ // Check to see if there are any controls in the request. If so, then
+ // see if there is any special processing required.
+ try
+ {
+ processControls(parentDN);
+ }
+ catch (DirectoryException de)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, de);
+ }
+
+ setResponseData(de);
+ break addProcessing;
+ }
+
+
+ // Check to see if the client has permission to perform the add.
+
+ // FIXME: for now assume that this will check all permission
+ // pertinent to the operation. This includes proxy authorization
+ // and any other controls specified.
+
+ // FIXME: earlier checks to see if the entry already exists or
+ // if the parent entry does not exist may have already exposed
+ // sensitive information to the client.
+ if (AccessControlConfigManager.getInstance().getAccessControlHandler().
+ isAllowed(this) == false)
+ {
+ setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS);
+ appendErrorMessage(ERR_ADD_AUTHZ_INSUFFICIENT_ACCESS_RIGHTS.get(
+ String.valueOf(entryDN)));
+ skipPostOperation = true;
+ break addProcessing;
+ }
+
+ // Check for a request to cancel this operation.
+ if (getCancelRequest() != null)
+ {
+ return;
+ }
+
+
+ // If the operation is not a synchronization operation,
+ // Invoke the pre-operation add plugins.
+ if (! isSynchronizationOperation())
+ {
+ PreOperationPluginResult preOpResult =
+ pluginConfigManager.invokePreOperationAddPlugins(this);
+ if (preOpResult.connectionTerminated())
+ {
+ // There's no point in continuing with anything. Log the result
+ // and return.
+ setResultCode(ResultCode.CANCELED);
+ appendErrorMessage(ERR_CANCELED_BY_PREOP_DISCONNECT.get());
+
+ return;
+ }
+ else if (preOpResult.sendResponseImmediately())
+ {
+ skipPostOperation = true;
+ break addProcessing;
+ }
+ else if (preOpResult.skipCoreProcessing())
+ {
+ skipPostOperation = false;
+ break addProcessing;
+ }
+ }
+
+
+ // Check for a request to cancel this operation.
+ if (getCancelRequest() != null)
+ {
+ return;
+ }
+
+
+ // If it is not a private backend, then check to see if the server or
+ // backend is operating in read-only mode.
+ if (! backend.isPrivateBackend())
+ {
+ switch (DirectoryServer.getWritabilityMode())
+ {
+ case DISABLED:
+ setResultCode(ResultCode.UNWILLING_TO_PERFORM);
+ appendErrorMessage(ERR_ADD_SERVER_READONLY.get(
+ String.valueOf(entryDN)));
+ break addProcessing;
+
+ case INTERNAL_ONLY:
+ if (! (isInternalOperation() || isSynchronizationOperation()))
+ {
+ setResultCode(ResultCode.UNWILLING_TO_PERFORM);
+ appendErrorMessage(ERR_ADD_SERVER_READONLY.get(
+ String.valueOf(entryDN)));
+ break addProcessing;
+ }
+ break;
+ }
+
+ switch (backend.getWritabilityMode())
+ {
+ case DISABLED:
+ setResultCode(ResultCode.UNWILLING_TO_PERFORM);
+ appendErrorMessage(ERR_ADD_BACKEND_READONLY.get(
+ String.valueOf(entryDN)));
+ break addProcessing;
+
+ case INTERNAL_ONLY:
+ if (! (isInternalOperation() || isSynchronizationOperation()))
+ {
+ setResultCode(ResultCode.UNWILLING_TO_PERFORM);
+ appendErrorMessage(ERR_ADD_BACKEND_READONLY.get(
+ String.valueOf(entryDN)));
+ break addProcessing;
+ }
+ break;
+ }
+ }
+
+
+ try
+ {
+ if (noOp)
+ {
+ appendErrorMessage(INFO_ADD_NOOP.get());
+ setResultCode(ResultCode.NO_OPERATION);
+ }
+ else
+ {
+ for (SynchronizationProvider provider :
+ DirectoryServer.getSynchronizationProviders())
+ {
+ try
+ {
+ SynchronizationProviderResult result =
+ provider.doPreOperation(this);
+ if (! result.continueOperationProcessing())
+ {
+ break addProcessing;
+ }
+ }
+ catch (DirectoryException de)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, de);
+ }
+
+ logError(ERR_ADD_SYNCH_PREOP_FAILED.get(getConnectionID(),
+ getOperationID(), getExceptionMessage(de)));
+ setResponseData(de);
+ break addProcessing;
+ }
+ }
+
+ backend.addEntry(entry, this);
+ }
+
+ if (postReadRequest != null)
+ {
+ addPostReadResponse();
+ }
+
+
+ if (! noOp)
+ {
+ setResultCode(ResultCode.SUCCESS);
+ }
+ }
+ catch (DirectoryException de)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, de);
+ }
+
+ setResponseData(de);
+ break addProcessing;
+ }
+ catch (CancelledOperationException coe)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, coe);
+ }
+
+ CancelResult cancelResult = coe.getCancelResult();
+
+ setCancelResult(cancelResult);
+ setResultCode(cancelResult.getResultCode());
+
+ Message message = coe.getMessageObject();
+ if ((message != null) && (message.length() > 0))
+ {
+ appendErrorMessage(message);
+ }
+
+ break addProcessing;
+ }
+ }
+ finally
+ {
+ if (entryLock != null)
+ {
+ LockManager.unlock(entryDN, entryLock);
+ }
+
+ if (parentLock != null)
+ {
+ LockManager.unlock(parentDN, parentLock);
+ }
+
+
+ for (SynchronizationProvider provider :
+ DirectoryServer.getSynchronizationProviders())
+ {
+ try
+ {
+ provider.doPostOperation(this);
+ }
+ catch (DirectoryException de)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, de);
+ }
+
+ logError(ERR_ADD_SYNCH_POSTOP_FAILED.get(getConnectionID(),
+ getOperationID(), getExceptionMessage(de)));
+ setResponseData(de);
+ break;
+ }
+ }
+ }
+ }
+
+
+ // Indicate that it is now too late to attempt to cancel the operation.
+ setCancelResult(CancelResult.TOO_LATE);
+
+
+ // Invoke the post-operation or post-synchronization add plugins.
+ if (isSynchronizationOperation())
+ {
+ if (getResultCode() == ResultCode.SUCCESS)
+ {
+ pluginConfigManager.invokePostSynchronizationAddPlugins(this);
+ }
+ }
+ else if (! skipPostOperation)
+ {
+ // FIXME -- Should this also be done while holding the locks?
+ PostOperationPluginResult postOpResult =
+ pluginConfigManager.invokePostOperationAddPlugins(this);
+ if (postOpResult.connectionTerminated())
+ {
+ // There's no point in continuing with anything. Log the result and
+ // return.
+ setResultCode(ResultCode.CANCELED);
+ appendErrorMessage(ERR_CANCELED_BY_PREOP_DISCONNECT.get());
+
+ return;
+ }
+ }
+
+
+ // Notify any change notification listeners that might be registered with
+ // the server.
+ if ((getResultCode() == ResultCode.SUCCESS) && (entry != null))
+ {
+ for (ChangeNotificationListener changeListener :
+ DirectoryServer.getChangeNotificationListeners())
+ {
+ try
+ {
+ changeListener.handleAddOperation(this, entry);
+ }
+ catch (Exception e)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ }
+
+ logError(ERR_ADD_ERROR_NOTIFYING_CHANGE_LISTENER.get(
+ getExceptionMessage(e)));
+ }
+ }
+ }
}
+
+
+ /**
+ * Acquire a read lock on the parent of the entry to add.
+ *
+ * @return The acquired read lock.
+ *
+ * @throws DirectoryException If a problem occurs while attempting to
+ * acquire the lock.
+ */
+ private Lock lockParent(DN parentDN)
+ throws DirectoryException
+ {
+ Lock parentLock = null;
+
+ if (parentDN == null)
+ {
+ // Either this entry is a suffix or doesn't belong in the directory.
+ if (DirectoryServer.isNamingContext(entryDN))
+ {
+ // This is fine. This entry is one of the configured suffixes.
+ parentLock = null;
+ }
+ else if (entryDN.isNullDN())
+ {
+ // This is not fine. The root DSE cannot be added.
+ throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
+ ERR_ADD_CANNOT_ADD_ROOT_DSE.get());
+ }
+ else
+ {
+ // The entry doesn't have a parent but isn't a suffix. This is not
+ // allowed.
+ throw new DirectoryException(ResultCode.NO_SUCH_OBJECT,
+ ERR_ADD_ENTRY_NOT_SUFFIX.get(
+ String.valueOf(entryDN)));
+ }
+ }
+ else
+ {
+ for (int i=0; i < 3; i++)
+ {
+ parentLock = LockManager.lockRead(parentDN);
+ if (parentLock != null)
+ {
+ break;
+ }
+ }
+
+ if (parentLock == null)
+ {
+ throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
+ ERR_ADD_CANNOT_LOCK_PARENT.get(
+ String.valueOf(entryDN),
+ String.valueOf(parentDN)));
+ }
+ }
+
+ return parentLock;
+ }
+
+
+
+ /**
+ * Adds any missing RDN attributes to the entry.
+ *
+ * @throws DirectoryException If the entry is missing one or more RDN
+ * attributes and the server is configured to
+ * reject such entries.
+ */
+ private void addRDNAttributesIfNecessary()
+ throws DirectoryException
+ {
+ RDN rdn = entryDN.getRDN();
+ int numAVAs = rdn.getNumValues();
+ for (int i=0; i < numAVAs; i++)
+ {
+ AttributeType t = rdn.getAttributeType(i);
+ AttributeValue v = rdn.getAttributeValue(i);
+ String n = rdn.getAttributeName(i);
+ if (t.isOperational())
+ {
+ List<Attribute> attrList = operationalAttributes.get(t);
+ if (attrList == null)
+ {
+ if (isSynchronizationOperation() ||
+ DirectoryServer.addMissingRDNAttributes())
+ {
+ LinkedHashSet<AttributeValue> valueList =
+ new LinkedHashSet<AttributeValue>(1);
+ valueList.add(v);
+
+ attrList = new ArrayList<Attribute>();
+ attrList.add(new Attribute(t, n, valueList));
+
+ operationalAttributes.put(t, attrList);
+ }
+ else
+ {
+ throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
+ ERR_ADD_MISSING_RDN_ATTRIBUTE.get(
+ String.valueOf(entryDN), n));
+ }
+ }
+ else
+ {
+ boolean found = false;
+ for (Attribute a : attrList)
+ {
+ if (a.hasOptions())
+ {
+ continue;
+ }
+ else
+ {
+ if (! a.hasValue(v))
+ {
+ a.getValues().add(v);
+ }
+
+ found = true;
+ break;
+ }
+ }
+
+ if (! found)
+ {
+ if (isSynchronizationOperation() ||
+ DirectoryServer.addMissingRDNAttributes())
+ {
+ LinkedHashSet<AttributeValue> valueList =
+ new LinkedHashSet<AttributeValue>(1);
+ valueList.add(v);
+ attrList.add(new Attribute(t, n, valueList));
+ }
+ else
+ {
+ throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
+ ERR_ADD_MISSING_RDN_ATTRIBUTE.get(
+ String.valueOf(entryDN), n));
+ }
+ }
+ }
+ }
+ else
+ {
+ List<Attribute> attrList = userAttributes.get(t);
+ if (attrList == null)
+ {
+ if (isSynchronizationOperation() ||
+ DirectoryServer.addMissingRDNAttributes())
+ {
+ LinkedHashSet<AttributeValue> valueList =
+ new LinkedHashSet<AttributeValue>(1);
+ valueList.add(v);
+
+ attrList = new ArrayList<Attribute>();
+ attrList.add(new Attribute(t, n, valueList));
+
+ userAttributes.put(t, attrList);
+ }
+ else
+ {
+ throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
+ ERR_ADD_MISSING_RDN_ATTRIBUTE.get(
+ String.valueOf(entryDN),n));
+ }
+ }
+ else
+ {
+ boolean found = false;
+ for (Attribute a : attrList)
+ {
+ if (a.hasOptions())
+ {
+ continue;
+ }
+ else
+ {
+ if (! a.hasValue(v))
+ {
+ a.getValues().add(v);
+ }
+
+ found = true;
+ break;
+ }
+ }
+
+ if (! found)
+ {
+ if (isSynchronizationOperation() ||
+ DirectoryServer.addMissingRDNAttributes())
+ {
+ LinkedHashSet<AttributeValue> valueList =
+ new LinkedHashSet<AttributeValue>(1);
+ valueList.add(v);
+ attrList.add(new Attribute(t, n, valueList));
+ }
+ else
+ {
+ throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
+ ERR_ADD_MISSING_RDN_ATTRIBUTE.get(
+ String.valueOf(entryDN),n));
+ }
+ }
+ }
+ }
+ }
+ }
+
+
+
+ /**
+ * Adds the provided objectClass to the entry, along with its superior classes
+ * if appropriate.
+ *
+ * @param objectClass The objectclass to add to the entry.
+ */
+ public final void addObjectClassChain(ObjectClass objectClass)
+ {
+ Map<ObjectClass, String> objectClasses = getObjectClasses();
+ if (objectClasses != null){
+ if (! objectClasses.containsKey(objectClass))
+ {
+ objectClasses.put(objectClass, objectClass.getNameOrOID());
+ }
+
+ ObjectClass superiorClass = objectClass.getSuperiorClass();
+ if ((superiorClass != null) &&
+ (! objectClasses.containsKey(superiorClass)))
+ {
+ addObjectClassChain(superiorClass);
+ }
+ }
+ }
+
+
+
/**
* Performs all password policy processing necessary for the provided add
* operation.
*
- * @param passwordPolicy The password policy associated with the entry to be
- * added.
- * @param userEntry The user entry being added.
- *
* @throws DirectoryException If a problem occurs while performing password
* policy processing for the add operation.
*/
- public final void handlePasswordPolicy(PasswordPolicy passwordPolicy,
- Entry userEntry)
+ public final void handlePasswordPolicy()
throws DirectoryException
{
+ // FIXME -- We need to check to see if the password policy subentry
+ // might be specified virtually rather than as a real
+ // attribute.
+ PasswordPolicy passwordPolicy = null;
+ List<Attribute> pwAttrList =
+ entry.getAttribute(OP_ATTR_PWPOLICY_POLICY_DN);
+ if ((pwAttrList != null) && (! pwAttrList.isEmpty()))
+ {
+ Attribute a = pwAttrList.get(0);
+ LinkedHashSet<AttributeValue> valueSet = a.getValues();
+ Iterator<AttributeValue> iterator = valueSet.iterator();
+ if (iterator.hasNext())
+ {
+ DN policyDN;
+ try
+ {
+ policyDN = DN.decode(iterator.next().getValue());
+ }
+ catch (DirectoryException de)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, de);
+ }
+
+ throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
+ ERR_ADD_INVALID_PWPOLICY_DN_SYNTAX.get(
+ String.valueOf(entryDN),
+ de.getMessageObject()));
+ }
+
+ passwordPolicy = DirectoryServer.getPasswordPolicy(policyDN);
+ if (passwordPolicy == null)
+ {
+ throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
+ ERR_ADD_NO_SUCH_PWPOLICY.get(
+ String.valueOf(entryDN),
+ String.valueOf(policyDN)));
+ }
+ }
+ }
+
+ if (passwordPolicy == null)
+ {
+ passwordPolicy = DirectoryServer.getDefaultPasswordPolicy();
+ }
+
// See if a password was specified.
AttributeType passwordAttribute = passwordPolicy.getPasswordAttribute();
- List<Attribute> attrList = userEntry.getAttribute(passwordAttribute);
+ List<Attribute> attrList = entry.getAttribute(passwordAttribute);
if ((attrList == null) || attrList.isEmpty())
{
// The entry doesn't have a password, so no action is required.
@@ -237,7 +1254,7 @@
passwordPolicy.getPasswordValidators().values())
{
if (! validator.passwordIsAcceptable(value, currentPasswords, this,
- userEntry, invalidReason))
+ entry, invalidReason))
{
addPWPolicyControl(
PasswordPolicyErrorType.INSUFFICIENT_PASSWORD_QUALITY);
@@ -296,7 +1313,7 @@
OP_ATTR_PWPOLICY_CHANGED_TIME,
changedTimeValues));
- userEntry.putAttribute(changedTimeType, changedTimeList);
+ entry.putAttribute(changedTimeType, changedTimeList);
// If we should force change on add, then set the appropriate flag.
@@ -319,10 +1336,12 @@
ArrayList<Attribute> resetList = new ArrayList<Attribute>(1);
resetList.add(new Attribute(resetType, OP_ATTR_PWPOLICY_RESET_REQUIRED,
resetValues));
- userEntry.putAttribute(resetType, resetList);
+ entry.putAttribute(resetType, resetList);
}
}
+
+
/**
* Adds a password policy response control if the corresponding request
* control was included.
@@ -341,28 +1360,462 @@
}
}
- /**
- * Adds the provided objectClass to the entry, along with its superior classes
- * if appropriate.
- *
- * @param objectClass The objectclass to add to the entry.
- */
- public final void addObjectClassChain(ObjectClass objectClass)
- {
- Map<ObjectClass, String> objectClasses = getObjectClasses();
- if (objectClasses != null){
- if (! objectClasses.containsKey(objectClass))
- {
- objectClasses.put(objectClass, objectClass.getNameOrOID());
- }
- ObjectClass superiorClass = objectClass.getSuperiorClass();
- if ((superiorClass != null) &&
- (! objectClasses.containsKey(superiorClass)))
+
+ /**
+ * Verifies that the entry to be added conforms to the server schema.
+ *
+ * @param parentEntry The parent of the entry to add.
+ *
+ * @throws DirectoryException If the entry violates the server schema
+ * configuration.
+ */
+ private void checkSchema(Entry parentEntry)
+ throws DirectoryException
+ {
+ MessageBuilder invalidReason = new MessageBuilder();
+ if (! entry.conformsToSchema(parentEntry, true, true, true, invalidReason))
+ {
+ throw new DirectoryException(ResultCode.OBJECTCLASS_VIOLATION,
+ invalidReason.toMessage());
+ }
+ else
+ {
+ switch (DirectoryServer.getSyntaxEnforcementPolicy())
{
- addObjectClassChain(superiorClass);
+ case REJECT:
+ invalidReason = new MessageBuilder();
+ for (List<Attribute> attrList : userAttributes.values())
+ {
+ for (Attribute a : attrList)
+ {
+ AttributeSyntax syntax = a.getAttributeType().getSyntax();
+ if (syntax != null)
+ {
+ for (AttributeValue v : a.getValues())
+ {
+ if (! syntax.valueIsAcceptable(v.getValue(), invalidReason))
+ {
+ Message message = WARN_ADD_OP_INVALID_SYNTAX.get(
+ String.valueOf(entryDN),
+ String.valueOf(v.getStringValue()),
+ String.valueOf(a.getName()),
+ String.valueOf(invalidReason));
+
+ throw new DirectoryException(
+ ResultCode.INVALID_ATTRIBUTE_SYNTAX,
+ message);
+ }
+ }
+ }
+ }
+ }
+
+ for (List<Attribute> attrList :
+ operationalAttributes.values())
+ {
+ for (Attribute a : attrList)
+ {
+ AttributeSyntax syntax = a.getAttributeType().getSyntax();
+ if (syntax != null)
+ {
+ for (AttributeValue v : a.getValues())
+ {
+ if (! syntax.valueIsAcceptable(v.getValue(),
+ invalidReason))
+ {
+ Message message = WARN_ADD_OP_INVALID_SYNTAX.
+ get(String.valueOf(entryDN),
+ String.valueOf(v.getStringValue()),
+ String.valueOf(a.getName()),
+ String.valueOf(invalidReason));
+
+ throw new DirectoryException(
+ ResultCode.INVALID_ATTRIBUTE_SYNTAX,
+ message);
+ }
+ }
+ }
+ }
+ }
+
+ break;
+
+
+ case WARN:
+ invalidReason = new MessageBuilder();
+ for (List<Attribute> attrList : userAttributes.values())
+ {
+ for (Attribute a : attrList)
+ {
+ AttributeSyntax syntax = a.getAttributeType().getSyntax();
+ if (syntax != null)
+ {
+ for (AttributeValue v : a.getValues())
+ {
+ if (! syntax.valueIsAcceptable(v.getValue(),
+ invalidReason))
+ {
+ logError(WARN_ADD_OP_INVALID_SYNTAX.get(
+ String.valueOf(entryDN),
+ String.valueOf(v.getStringValue()),
+ String.valueOf(a.getName()),
+ String.valueOf(invalidReason)));
+ }
+ }
+ }
+ }
+ }
+
+ for (List<Attribute> attrList : operationalAttributes.values())
+ {
+ for (Attribute a : attrList)
+ {
+ AttributeSyntax syntax = a.getAttributeType().getSyntax();
+ if (syntax != null)
+ {
+ for (AttributeValue v : a.getValues())
+ {
+ if (! syntax.valueIsAcceptable(v.getValue(),
+ invalidReason))
+ {
+ logError(WARN_ADD_OP_INVALID_SYNTAX.get(
+ String.valueOf(entryDN),
+ String.valueOf(v.getStringValue()),
+ String.valueOf(a.getName()),
+ String.valueOf(invalidReason)));
+ }
+ }
+ }
+ }
+ }
+
+ break;
+ }
+ }
+
+
+ // See if the entry contains any attributes or object classes marked
+ // OBSOLETE. If so, then reject the entry.
+ for (AttributeType at : userAttributes.keySet())
+ {
+ if (at.isObsolete())
+ {
+ throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
+ WARN_ADD_ATTR_IS_OBSOLETE.get(
+ String.valueOf(entryDN),
+ at.getNameOrOID()));
+ }
+ }
+
+ for (AttributeType at : operationalAttributes.keySet())
+ {
+ if (at.isObsolete())
+ {
+ throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
+ WARN_ADD_ATTR_IS_OBSOLETE.get(
+ String.valueOf(entryDN),
+ at.getNameOrOID()));
+ }
+ }
+
+ for (ObjectClass oc : objectClasses.keySet())
+ {
+ if (oc.isObsolete())
+ {
+ throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
+ WARN_ADD_OC_IS_OBSOLETE.get(
+ String.valueOf(entryDN),
+ oc.getNameOrOID()));
}
}
}
+
+
+ /**
+ * Processes the set of controls contained in the add request.
+ *
+ * @param parentDN The DN of the parent of the entry to add.
+ *
+ * @throws DirectoryException If there is a problem with any of the
+ * request controls.
+ */
+ private void processControls(DN parentDN)
+ throws DirectoryException
+ {
+ List<Control> requestControls = getRequestControls();
+ if ((requestControls != null) && (! requestControls.isEmpty()))
+ {
+ for (int i=0; i < requestControls.size(); i++)
+ {
+ Control c = requestControls.get(i);
+ String oid = c.getOID();
+
+ if (!AccessControlConfigManager.getInstance().
+ getAccessControlHandler().isAllowed(parentDN, this, c))
+ {
+ skipPostOperation = true;
+ throw new DirectoryException(ResultCode.INSUFFICIENT_ACCESS_RIGHTS,
+ ERR_CONTROL_INSUFFICIENT_ACCESS_RIGHTS.get(oid));
+ }
+
+ if (oid.equals(OID_LDAP_ASSERTION))
+ {
+ LDAPAssertionRequestControl assertControl;
+ if (c instanceof LDAPAssertionRequestControl)
+ {
+ assertControl = (LDAPAssertionRequestControl) c;
+ }
+ else
+ {
+ try
+ {
+ assertControl = LDAPAssertionRequestControl.decodeControl(c);
+ requestControls.set(i, assertControl);
+ }
+ catch (LDAPException le)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, le);
+ }
+
+ throw new DirectoryException(
+ ResultCode.valueOf(le.getResultCode()),
+ le.getMessageObject());
+ }
+ }
+
+ try
+ {
+ // FIXME -- We need to determine whether the current user has
+ // permission to make this determination.
+ SearchFilter filter = assertControl.getSearchFilter();
+ if (! filter.matchesEntry(entry))
+ {
+ throw new DirectoryException(ResultCode.ASSERTION_FAILED,
+ ERR_ADD_ASSERTION_FAILED.get(
+ String.valueOf(entryDN)));
+ }
+ }
+ catch (DirectoryException de)
+ {
+ if (de.getResultCode() == ResultCode.ASSERTION_FAILED)
+ {
+ throw de;
+ }
+
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, de);
+ }
+
+ throw new DirectoryException(ResultCode.PROTOCOL_ERROR,
+ ERR_ADD_CANNOT_PROCESS_ASSERTION_FILTER.get(
+ String.valueOf(entryDN),
+ de.getMessageObject()));
+ }
+ }
+ else if (oid.equals(OID_LDAP_NOOP_OPENLDAP_ASSIGNED))
+ {
+ noOp = true;
+ }
+ else if (oid.equals(OID_LDAP_READENTRY_POSTREAD))
+ {
+ if (c instanceof LDAPPostReadRequestControl)
+ {
+ postReadRequest = (LDAPPostReadRequestControl) c;
+ }
+ else
+ {
+ try
+ {
+ postReadRequest = LDAPPostReadRequestControl.decodeControl(c);
+ requestControls.set(i, postReadRequest);
+ }
+ catch (LDAPException le)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, le);
+ }
+
+ throw new DirectoryException(
+ ResultCode.valueOf(le.getResultCode()),
+ le.getMessageObject());
+ }
+ }
+ }
+ else if (oid.equals(OID_PROXIED_AUTH_V1))
+ {
+ // The requester must have the PROXIED_AUTH privilige in order to
+ // be able to use this control.
+ if (! getClientConnection().hasPrivilege(Privilege.PROXIED_AUTH,
+ this))
+ {
+ throw new DirectoryException(ResultCode.AUTHORIZATION_DENIED,
+ ERR_PROXYAUTH_INSUFFICIENT_PRIVILEGES.get());
+ }
+
+
+ ProxiedAuthV1Control proxyControl;
+ if (c instanceof ProxiedAuthV1Control)
+ {
+ proxyControl = (ProxiedAuthV1Control) c;
+ }
+ else
+ {
+ try
+ {
+ proxyControl = ProxiedAuthV1Control.decodeControl(c);
+ }
+ catch (LDAPException le)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, le);
+ }
+
+ throw new DirectoryException(
+ ResultCode.valueOf(le.getResultCode()),
+ le.getMessageObject());
+ }
+ }
+
+
+ Entry authorizationEntry = proxyControl.getAuthorizationEntry();
+ setAuthorizationEntry(authorizationEntry);
+ if (authorizationEntry == null)
+ {
+ setProxiedAuthorizationDN(DN.nullDN());
+ }
+ else
+ {
+ setProxiedAuthorizationDN(authorizationEntry.getDN());
+ }
+ }
+ else if (oid.equals(OID_PROXIED_AUTH_V2))
+ {
+ // The requester must have the PROXIED_AUTH privilige in order to
+ // be able to use this control.
+ if (! getClientConnection().hasPrivilege(Privilege.PROXIED_AUTH,
+ this))
+ {
+ throw new DirectoryException(ResultCode.AUTHORIZATION_DENIED,
+ ERR_PROXYAUTH_INSUFFICIENT_PRIVILEGES.get());
+ }
+
+
+ ProxiedAuthV2Control proxyControl;
+ if (c instanceof ProxiedAuthV2Control)
+ {
+ proxyControl = (ProxiedAuthV2Control) c;
+ }
+ else
+ {
+ try
+ {
+ proxyControl = ProxiedAuthV2Control.decodeControl(c);
+ }
+ catch (LDAPException le)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, le);
+ }
+
+ throw new DirectoryException(
+ ResultCode.valueOf(le.getResultCode()),
+ le.getMessageObject());
+ }
+ }
+
+
+ Entry authorizationEntry = proxyControl.getAuthorizationEntry();
+ setAuthorizationEntry(authorizationEntry);
+ if (authorizationEntry == null)
+ {
+ setProxiedAuthorizationDN(DN.nullDN());
+ }
+ else
+ {
+ setProxiedAuthorizationDN(authorizationEntry.getDN());
+ }
+ }
+ else if (oid.equals(OID_PASSWORD_POLICY_CONTROL))
+ {
+ // We don't need to do anything here because it's already handled
+ // in LocalBackendAddOperation.handlePasswordPolicy().
+ }
+
+ // NYI -- Add support for additional controls.
+ else if (c.isCritical())
+ {
+ if ((backend == null) || (! backend.supportsControl(oid)))
+ {
+ throw new DirectoryException(
+ ResultCode.UNAVAILABLE_CRITICAL_EXTENSION,
+ ERR_ADD_UNSUPPORTED_CRITICAL_CONTROL.get(
+ String.valueOf(entryDN), oid));
+ }
+ }
+ }
+ }
+ }
+
+
+
+ /**
+ * Adds the post-read response control to the response.
+ */
+ private void addPostReadResponse()
+ {
+ Entry addedEntry = entry.duplicate(true);
+
+ if (! postReadRequest.allowsAttribute(
+ DirectoryServer.getObjectClassAttributeType()))
+ {
+ addedEntry.removeAttribute(DirectoryServer.getObjectClassAttributeType());
+ }
+
+ if (! postReadRequest.returnAllUserAttributes())
+ {
+ Iterator<AttributeType> iterator =
+ addedEntry.getUserAttributes().keySet().iterator();
+ while (iterator.hasNext())
+ {
+ AttributeType attrType = iterator.next();
+ if (! postReadRequest.allowsAttribute(attrType))
+ {
+ iterator.remove();
+ }
+ }
+ }
+
+ if (! postReadRequest.returnAllOperationalAttributes())
+ {
+ Iterator<AttributeType> iterator =
+ addedEntry.getOperationalAttributes().keySet().iterator();
+ while (iterator.hasNext())
+ {
+ AttributeType attrType = iterator.next();
+ if (! postReadRequest.allowsAttribute(attrType))
+ {
+ iterator.remove();
+ }
+ }
+ }
+
+ // FIXME -- Check access controls on the entry to see if it should
+ // be returned or if any attributes need to be stripped
+ // out..
+ SearchResultEntry searchEntry = new SearchResultEntry(addedEntry);
+ LDAPPostReadResponseControl responseControl =
+ new LDAPPostReadResponseControl(postReadRequest.getOID(),
+ postReadRequest.isCritical(),
+ searchEntry);
+ addResponseControl(responseControl);
+ }
}
+
diff --git a/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendBindOperation.java b/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendBindOperation.java
index e101a13..4e209a7 100644
--- a/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendBindOperation.java
+++ b/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendBindOperation.java
@@ -26,21 +26,146 @@
*/
package org.opends.server.workflowelement.localbackend;
+
+
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.concurrent.locks.Lock;
+
+import org.opends.messages.Message;
+import org.opends.server.admin.std.meta.PasswordPolicyCfgDefn;
+import org.opends.server.api.Backend;
+import org.opends.server.api.ClientConnection;
+import org.opends.server.api.SASLMechanismHandler;
+import org.opends.server.api.plugin.PostOperationPluginResult;
+import org.opends.server.api.plugin.PreOperationPluginResult;
+import org.opends.server.controls.AuthorizationIdentityResponseControl;
+import org.opends.server.controls.PasswordExpiredControl;
+import org.opends.server.controls.PasswordExpiringControl;
+import org.opends.server.controls.PasswordPolicyErrorType;
+import org.opends.server.controls.PasswordPolicyResponseControl;
+import org.opends.server.controls.PasswordPolicyWarningType;
+import org.opends.server.core.AccessControlConfigManager;
import org.opends.server.core.BindOperation;
import org.opends.server.core.BindOperationWrapper;
+import org.opends.server.core.DirectoryServer;
+import org.opends.server.core.PasswordPolicy;
+import org.opends.server.core.PasswordPolicyState;
+import org.opends.server.core.PluginConfigManager;
+import org.opends.server.loggers.debug.DebugTracer;
+import org.opends.server.types.AccountStatusNotification;
+import org.opends.server.types.AccountStatusNotificationType;
+import org.opends.server.types.Attribute;
+import org.opends.server.types.AttributeType;
+import org.opends.server.types.AttributeValue;
+import org.opends.server.types.AuthenticationInfo;
+import org.opends.server.types.ByteString;
+import org.opends.server.types.Control;
+import org.opends.server.types.DebugLogLevel;
+import org.opends.server.types.DirectoryException;
+import org.opends.server.types.DN;
+import org.opends.server.types.Entry;
+import org.opends.server.types.LockManager;
+import org.opends.server.types.ResultCode;
+import org.opends.server.types.WritabilityMode;
import org.opends.server.types.operation.PostOperationBindOperation;
import org.opends.server.types.operation.PostResponseBindOperation;
import org.opends.server.types.operation.PreOperationBindOperation;
+import static org.opends.messages.CoreMessages.*;
+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.util.ServerConstants.*;
+import static org.opends.server.util.StaticUtils.*;
+
+
+
/**
* This class defines an operation used to bind against the Directory Server,
* with the bound user entry within a local backend.
*/
-public class LocalBackendBindOperation extends BindOperationWrapper
- implements PreOperationBindOperation,
- PostOperationBindOperation,
- PostResponseBindOperation
+public class LocalBackendBindOperation
+ extends BindOperationWrapper
+ implements PreOperationBindOperation, PostOperationBindOperation,
+ PostResponseBindOperation
{
+ /**
+ * The tracer object for the debug logger.
+ */
+ private static final DebugTracer TRACER = getTracer();
+
+
+
+ // The backend in which the bind operation should be processed.
+ private Backend backend;
+
+ // Indicates whether the bind response should include the first warning for an
+ // upcoming password expiration.
+ private boolean isFirstWarning;
+
+ // Indicates whether this bind is using a grace login for the user.
+ private boolean isGraceLogin;
+
+ // Indicates whether the user must change his/her password before doing
+ // anything else.
+ private boolean mustChangePassword;
+
+ // Indicates whether the user requested the password policy control.
+ private boolean pwPolicyControlRequested;
+
+ // Indicates whether the server should return the authorization ID as a
+ // control in the bind response.
+ private boolean returnAuthzID;
+
+ // Indicates whether to skip post-operation plugin processing.
+ private boolean skipPostOperation;
+
+ // The client connection associated with this bind operation.
+ private ClientConnection clientConnection;
+
+ // The bind DN provided by the client.
+ private DN bindDN;
+
+ // The entry of the user that successfully authenticated during processing for
+ // this bind operation.
+ private Entry authenticatedUserEntry;
+
+ // The lookthrough limit that should be enforced for the user.
+ private int lookthroughLimit;
+
+ // The value to use for the password policy warning.
+ private int pwPolicyWarningValue;
+
+ // The size limit that should be enforced for the user.
+ private int sizeLimit;
+
+ // The time limit that should be enforced for the user.
+ private int timeLimit;
+
+ // The idle time limit that should be enforced for the user.
+ private long idleTimeLimit;
+
+ // The password policy that applies to the user.
+ private PasswordPolicy policy;
+
+ // The password policy state for the user.
+ private PasswordPolicyState pwPolicyState;
+
+ // The password policy error type for this bind operation.
+ private PasswordPolicyErrorType pwPolicyErrorType;
+
+ // The password policy warning type for this bind operation.
+ private PasswordPolicyWarningType pwPolicyWarningType;
+
+ // The plugin config manager for the Directory Server.
+ private PluginConfigManager pluginConfigManager;
+
+ // The SASL mechanism used for this bind operation.
+ private String saslMechanism;
+
+
/**
* Creates a new operation that may be used to bind where
@@ -54,4 +179,1131 @@
LocalBackendWorkflowElement.attachLocalOperation (bind, this);
}
+
+
+ /**
+ * Process this bind operation in a local backend.
+ *
+ * @param backend The backend in which the bind operation should be
+ * processed.
+ */
+ void processLocalBind(Backend backend)
+ {
+ this.backend = backend;
+
+ // Initialize a number of variables for use during the bind processing.
+ clientConnection = getClientConnection();
+ returnAuthzID = false;
+ skipPostOperation = false;
+ sizeLimit = DirectoryServer.getSizeLimit();
+ timeLimit = DirectoryServer.getTimeLimit();
+ lookthroughLimit = DirectoryServer.getLookthroughLimit();
+ idleTimeLimit = DirectoryServer.getIdleTimeLimit();
+ bindDN = getBindDN();
+ saslMechanism = getSASLMechanism();
+ pwPolicyState = null;
+ pwPolicyErrorType = null;
+ pwPolicyControlRequested = false;
+ isGraceLogin = false;
+ isFirstWarning = false;
+ mustChangePassword = false;
+ pwPolicyWarningType = null;
+ pwPolicyWarningValue = -1 ;
+ authenticatedUserEntry = null;
+ pluginConfigManager = DirectoryServer.getPluginConfigManager();
+
+
+ // Create a labeled block of code that we can break out of if a problem is
+ // detected.
+bindProcessing:
+ {
+ // Check to see if the client has permission to perform the
+ // bind.
+
+ // FIXME: for now assume that this will check all permission
+ // pertinent to the operation. This includes any controls
+ // specified.
+ if (! AccessControlConfigManager.getInstance().getAccessControlHandler().
+ isAllowed(this))
+ {
+ setResultCode(ResultCode.INVALID_CREDENTIALS);
+ setAuthFailureReason(ERR_BIND_AUTHZ_INSUFFICIENT_ACCESS_RIGHTS.get(
+ String.valueOf(bindDN)));
+ skipPostOperation = true;
+ break bindProcessing;
+ }
+
+ // Check to see if there are any controls in the request. If so, then see
+ // if there is any special processing required.
+ try
+ {
+ handleRequestControls();
+ }
+ catch (DirectoryException de)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, de);
+ }
+
+ setResponseData(de);
+ break bindProcessing;
+ }
+
+
+ // Check to see if this is a simple bind or a SASL bind and process
+ // accordingly.
+ switch (getAuthenticationType())
+ {
+ case SIMPLE:
+ try
+ {
+ if (! processSimpleBind())
+ {
+ return;
+ }
+ }
+ catch (DirectoryException de)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, de);
+ }
+
+ if (de.getResultCode() == ResultCode.INVALID_CREDENTIALS)
+ {
+ setResultCode(ResultCode.INVALID_CREDENTIALS);
+ setAuthFailureReason(de.getMessageObject());
+ }
+ else
+ {
+ setResponseData(de);
+ }
+ break bindProcessing;
+ }
+ break;
+
+
+ case SASL:
+ try
+ {
+ if (! processSASLBind())
+ {
+ return;
+ }
+ }
+ catch (DirectoryException de)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, de);
+ }
+
+ if (de.getResultCode() == ResultCode.INVALID_CREDENTIALS)
+ {
+ setResultCode(ResultCode.INVALID_CREDENTIALS);
+ setAuthFailureReason(de.getMessageObject());
+ }
+ else
+ {
+ setResponseData(de);
+ }
+ break bindProcessing;
+ }
+ break;
+
+
+ default:
+ // Send a protocol error response to the client and disconnect.
+ // NYI
+ return;
+ }
+ }
+
+
+ // Update the user's account with any password policy changes that may be
+ // required.
+ try
+ {
+ if (pwPolicyState != null)
+ {
+ pwPolicyState.updateUserEntry();
+ }
+ }
+ catch (DirectoryException de)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, de);
+ }
+
+ setResponseData(de);
+ }
+
+
+ // Invoke the post-operation bind plugins.
+ if (! skipPostOperation)
+ {
+ PostOperationPluginResult postOpResult =
+ pluginConfigManager.invokePostOperationBindPlugins(this);
+ if (postOpResult.connectionTerminated())
+ {
+ // There's no point in continuing with anything. Log the result
+ // and return.
+ setResultCode(ResultCode.CANCELED);
+ appendErrorMessage(ERR_CANCELED_BY_PREOP_DISCONNECT.get());
+ setProcessingStopTime();
+ return;
+ }
+ }
+
+
+ // Update the authentication information for the user.
+ AuthenticationInfo authInfo = getAuthenticationInfo();
+ if ((getResultCode() == ResultCode.SUCCESS) && (authInfo != null))
+ {
+ authenticatedUserEntry = authInfo.getAuthenticationEntry();
+ clientConnection.setAuthenticationInfo(authInfo);
+ clientConnection.setSizeLimit(sizeLimit);
+ clientConnection.setTimeLimit(timeLimit);
+ clientConnection.setIdleTimeLimit(idleTimeLimit);
+ clientConnection.setLookthroughLimit(lookthroughLimit);
+ clientConnection.setMustChangePassword(mustChangePassword);
+
+ if (returnAuthzID)
+ {
+ addResponseControl(new AuthorizationIdentityResponseControl(
+ authInfo.getAuthorizationDN()));
+ }
+ }
+
+
+ // See if we need to send a password policy control to the client. If so,
+ // then add it to the response.
+ if (getResultCode() == ResultCode.SUCCESS)
+ {
+ if (pwPolicyControlRequested)
+ {
+ PasswordPolicyResponseControl pwpControl =
+ new PasswordPolicyResponseControl(pwPolicyWarningType,
+ pwPolicyWarningValue,
+ pwPolicyErrorType);
+ addResponseControl(pwpControl);
+ }
+ else
+ {
+ if (pwPolicyErrorType == PasswordPolicyErrorType.PASSWORD_EXPIRED)
+ {
+ addResponseControl(new PasswordExpiredControl());
+ }
+ else if (pwPolicyWarningType ==
+ PasswordPolicyWarningType.TIME_BEFORE_EXPIRATION)
+ {
+ addResponseControl(new PasswordExpiringControl(pwPolicyWarningValue));
+ }
+ }
+ }
+ else
+ {
+ if (pwPolicyControlRequested)
+ {
+ PasswordPolicyResponseControl pwpControl =
+ new PasswordPolicyResponseControl(pwPolicyWarningType,
+ pwPolicyWarningValue,
+ pwPolicyErrorType);
+ addResponseControl(pwpControl);
+ }
+ else
+ {
+ if (pwPolicyErrorType == PasswordPolicyErrorType.PASSWORD_EXPIRED)
+ {
+ addResponseControl(new PasswordExpiredControl());
+ }
+ }
+ }
+
+ // Stop the processing timer.
+ setProcessingStopTime();
+ }
+
+
+
+ /**
+ * Handles request control processing for this bind operation.
+ *
+ * @throws DirectoryException If there is a problem with any of the
+ * controls.
+ */
+ private void handleRequestControls()
+ throws DirectoryException
+ {
+ List<Control> requestControls = getRequestControls();
+ if ((requestControls != null) && (! requestControls.isEmpty()))
+ {
+ for (int i=0; i < requestControls.size(); i++)
+ {
+ Control c = requestControls.get(i);
+ String oid = c.getOID();
+
+ if (! AccessControlConfigManager.getInstance().
+ getAccessControlHandler(). isAllowed(bindDN, this, c))
+ {
+ skipPostOperation = true;
+ throw new DirectoryException(ResultCode.INSUFFICIENT_ACCESS_RIGHTS,
+ ERR_CONTROL_INSUFFICIENT_ACCESS_RIGHTS.get(oid));
+ }
+
+ if (oid.equals(OID_AUTHZID_REQUEST))
+ {
+ returnAuthzID = true;
+ }
+ else if (oid.equals(OID_PASSWORD_POLICY_CONTROL))
+ {
+ pwPolicyControlRequested = true;
+ }
+
+ // NYI -- Add support for additional controls.
+
+ else if (c.isCritical())
+ {
+ throw new DirectoryException(
+ ResultCode.UNAVAILABLE_CRITICAL_EXTENSION,
+ ERR_BIND_UNSUPPORTED_CRITICAL_CONTROL.get(oid));
+ }
+ }
+ }
+ }
+
+
+
+ /**
+ * Performs the processing necessary for a simple bind operation.
+ *
+ * @return {@code true} if processing should continue for the operation, or
+ * {@code false} if not.
+ *
+ * @throws DirectoryException If a problem occurs that should cause the bind
+ * operation to fail.
+ */
+ private boolean processSimpleBind()
+ throws DirectoryException
+ {
+ // See if this is an anonymous bind. If so, then determine whether
+ // to allow it.
+ ByteString simplePassword = getSimplePassword();
+ if ((simplePassword == null) || (simplePassword.value().length == 0))
+ {
+ return processAnonymousSimpleBind();
+ }
+
+ // See if the bind DN is actually one of the alternate root DNs
+ // defined in the server. If so, then replace it with the actual DN
+ // for that user.
+ DN actualRootDN = DirectoryServer.getActualRootBindDN(bindDN);
+ if (actualRootDN != null)
+ {
+ bindDN = actualRootDN;
+ }
+
+ // Get the user entry based on the bind DN. If it does not exist,
+ // then fail.
+ Lock userLock = null;
+ for (int i=0; i < 3; i++)
+ {
+ userLock = LockManager.lockRead(bindDN);
+ if (userLock != null)
+ {
+ break;
+ }
+ }
+
+ if (userLock == null)
+ {
+ throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
+ ERR_BIND_OPERATION_CANNOT_LOCK_USER.get(
+ String.valueOf(bindDN)));
+ }
+
+ try
+ {
+ Entry userEntry;
+ try
+ {
+ userEntry = backend.getEntry(bindDN);
+ }
+ catch (DirectoryException de)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, de);
+ }
+
+ userEntry = null;
+ throw new DirectoryException(ResultCode.INVALID_CREDENTIALS,
+ de.getMessageObject());
+ }
+
+ if (userEntry == null)
+ {
+ throw new DirectoryException(ResultCode.INVALID_CREDENTIALS,
+ ERR_BIND_OPERATION_UNKNOWN_USER.get(
+ String.valueOf(bindDN)));
+ }
+ else
+ {
+ setUserEntryDN(userEntry.getDN());
+ }
+
+
+ // Check to see if the user has a password. If not, then fail.
+ // FIXME -- We need to have a way to enable/disable debugging.
+ pwPolicyState = new PasswordPolicyState(userEntry, false, false);
+ policy = pwPolicyState.getPolicy();
+ AttributeType pwType = policy.getPasswordAttribute();
+
+ List<Attribute> pwAttr = userEntry.getAttribute(pwType);
+ if ((pwAttr == null) || (pwAttr.isEmpty()))
+ {
+ throw new DirectoryException(ResultCode.INVALID_CREDENTIALS,
+ ERR_BIND_OPERATION_NO_PASSWORD.get(
+ String.valueOf(bindDN)));
+ }
+
+
+ // Perform a number of password policy state checks for the user.
+ checkPasswordPolicyState(userEntry, null);
+
+
+ // Invoke the pre-operation bind plugins.
+ PreOperationPluginResult preOpResult =
+ pluginConfigManager.invokePreOperationBindPlugins(this);
+ if (preOpResult.connectionTerminated())
+ {
+ // There's no point in continuing with anything. Log the result
+ // and return.
+ setResultCode(ResultCode.CANCELED);
+ appendErrorMessage(ERR_CANCELED_BY_PREOP_DISCONNECT.get());
+ setProcessingStopTime();
+ return false;
+ }
+ else if (preOpResult.sendResponseImmediately() ||
+ preOpResult.skipCoreProcessing())
+ {
+ skipPostOperation = false;
+ return true;
+ }
+
+
+ // Determine whether the provided password matches any of the stored
+ // passwords for the user.
+ if (pwPolicyState.passwordMatches(simplePassword))
+ {
+ setResultCode(ResultCode.SUCCESS);
+
+ boolean isRoot = DirectoryServer.isRootDN(userEntry.getDN());
+ if (DirectoryServer.lockdownMode() && (! isRoot))
+ {
+ throw new DirectoryException(ResultCode.INVALID_CREDENTIALS,
+ ERR_BIND_REJECTED_LOCKDOWN_MODE.get());
+ }
+ setAuthenticationInfo(new AuthenticationInfo(userEntry,
+ simplePassword,
+ isRoot));
+
+
+ // Set resource limits for the authenticated user.
+ setResourceLimits(userEntry);
+
+
+ // Perform any remaining processing for a successful simple
+ // authentication.
+ pwPolicyState.handleDeprecatedStorageSchemes(simplePassword);
+ pwPolicyState.clearFailureLockout();
+
+ if (isFirstWarning)
+ {
+ pwPolicyState.setWarnedTime();
+
+ int numSeconds = pwPolicyState.getSecondsUntilExpiration();
+ Message m = WARN_BIND_PASSWORD_EXPIRING.get(
+ secondsToTimeString(numSeconds));
+
+ pwPolicyState.generateAccountStatusNotification(
+ AccountStatusNotificationType.PASSWORD_EXPIRING, userEntry, m,
+ AccountStatusNotification.createProperties(pwPolicyState,
+ false, numSeconds, null, null));
+ }
+
+ if (isGraceLogin)
+ {
+ pwPolicyState.updateGraceLoginTimes();
+ }
+
+ pwPolicyState.setLastLoginTime();
+ }
+ else
+ {
+ setResultCode(ResultCode.INVALID_CREDENTIALS);
+ setAuthFailureReason(ERR_BIND_OPERATION_WRONG_PASSWORD.get());
+
+ if (policy.getLockoutFailureCount() > 0)
+ {
+ pwPolicyState.updateAuthFailureTimes();
+ if (pwPolicyState.lockedDueToFailures())
+ {
+ AccountStatusNotificationType notificationType;
+ Message m;
+
+ boolean tempLocked;
+ int lockoutDuration = pwPolicyState.getSecondsUntilUnlock();
+ if (lockoutDuration > -1)
+ {
+ notificationType = AccountStatusNotificationType.
+ ACCOUNT_TEMPORARILY_LOCKED;
+ tempLocked = true;
+
+ m = ERR_BIND_ACCOUNT_TEMPORARILY_LOCKED.get(
+ secondsToTimeString(lockoutDuration));
+ }
+ else
+ {
+ notificationType = AccountStatusNotificationType.
+ ACCOUNT_PERMANENTLY_LOCKED;
+ tempLocked = false;
+
+ m = ERR_BIND_ACCOUNT_PERMANENTLY_LOCKED.get();
+ }
+
+ pwPolicyState.generateAccountStatusNotification(
+ notificationType, userEntry, m,
+ AccountStatusNotification.createProperties(pwPolicyState,
+ tempLocked, -1, null, null));
+ }
+ }
+ }
+
+ return true;
+ }
+ finally
+ {
+ // No matter what, make sure to unlock the user's entry.
+ LockManager.unlock(bindDN, userLock);
+ }
+ }
+
+
+
+ /**
+ * Performs the processing necessary for an anonymous simple bind.
+ *
+ * @throws DirectoryException If a problem occurs that should cause the bind
+ * operation to fail.
+ */
+ private boolean processAnonymousSimpleBind()
+ throws DirectoryException
+ {
+ // If the server is in lockdown mode, then fail.
+ if (DirectoryServer.lockdownMode())
+ {
+ throw new DirectoryException(ResultCode.INVALID_CREDENTIALS,
+ ERR_BIND_REJECTED_LOCKDOWN_MODE.get());
+ }
+
+ // If there is a bind DN, then see whether that is acceptable.
+ if (DirectoryServer.bindWithDNRequiresPassword() &&
+ ((bindDN != null) && (! bindDN.isNullDN())))
+ {
+ throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
+ ERR_BIND_DN_BUT_NO_PASSWORD.get());
+ }
+
+
+ // Invoke the pre-operation bind plugins.
+ PreOperationPluginResult preOpResult =
+ pluginConfigManager.invokePreOperationBindPlugins(this);
+ if (preOpResult.connectionTerminated())
+ {
+ // There's no point in continuing with anything. Log the result
+ // and return.
+ setResultCode(ResultCode.CANCELED);
+ appendErrorMessage(ERR_CANCELED_BY_PREOP_DISCONNECT.get());
+ setProcessingStopTime();
+ return false;
+ }
+ else if (preOpResult.sendResponseImmediately() ||
+ preOpResult.skipCoreProcessing())
+ {
+ skipPostOperation = true;
+ return true;
+ }
+
+ setResultCode(ResultCode.SUCCESS);
+ setAuthenticationInfo(new AuthenticationInfo());
+ return true;
+ }
+
+
+
+ /**
+ * Performs the processing necessary for a SASL bind operation.
+ *
+ * @return {@code true} if processing should continue for the operation, or
+ * {@code false} if not.
+ *
+ * @throws DirectoryException If a problem occurs that should cause the bind
+ * operation to fail.
+ */
+ private boolean processSASLBind()
+ throws DirectoryException
+ {
+ // Get the appropriate authentication handler for this request based
+ // on the SASL mechanism. If there is none, then fail.
+ SASLMechanismHandler saslHandler =
+ DirectoryServer.getSASLMechanismHandler(saslMechanism);
+ if (saslHandler == null)
+ {
+ throw new DirectoryException(ResultCode.AUTH_METHOD_NOT_SUPPORTED,
+ ERR_BIND_OPERATION_UNKNOWN_SASL_MECHANISM.get(
+ saslMechanism));
+ }
+
+
+ // Check to see if the client has sufficient permission to perform the bind.
+ // NYI
+
+
+ // Invoke the pre-operation bind plugins.
+ PreOperationPluginResult preOpResult =
+ pluginConfigManager.invokePreOperationBindPlugins(this);
+ if (preOpResult.connectionTerminated())
+ {
+ // There's no point in continuing with anything. Log the result
+ // and return.
+ setResultCode(ResultCode.CANCELED);
+ appendErrorMessage(ERR_CANCELED_BY_PREOP_DISCONNECT.get());
+ setProcessingStopTime();
+ return false;
+ }
+ else if (preOpResult.sendResponseImmediately() ||
+ preOpResult.skipCoreProcessing())
+ {
+ skipPostOperation = false;
+ return true;
+ }
+
+ // Actually process the SASL bind.
+ saslHandler.processSASLBind(this);
+
+
+ // If the server is operating in lockdown mode, then we will need to
+ // ensure that the authentication was successful and performed as a
+ // root user to continue.
+ Entry saslAuthUserEntry = getSASLAuthUserEntry();
+ if (DirectoryServer.lockdownMode())
+ {
+ ResultCode resultCode = getResultCode();
+ if (resultCode != ResultCode.SASL_BIND_IN_PROGRESS)
+ {
+ if ((resultCode != ResultCode.SUCCESS) ||
+ (saslAuthUserEntry == null) ||
+ (! DirectoryServer.isRootDN(saslAuthUserEntry.getDN())))
+ {
+ throw new DirectoryException(ResultCode.INVALID_CREDENTIALS,
+ ERR_BIND_REJECTED_LOCKDOWN_MODE.get());
+ }
+ }
+ }
+
+ // Create the password policy state object.
+ if (saslAuthUserEntry == null)
+ {
+ pwPolicyState = null;
+ }
+ else
+ {
+ // FIXME -- Need to have a way to enable debugging.
+ pwPolicyState = new PasswordPolicyState(saslAuthUserEntry, false,
+ false);
+ policy = pwPolicyState.getPolicy();
+ setUserEntryDN(saslAuthUserEntry.getDN());
+
+
+ // Perform password policy checks that will need to be completed
+ // regardless of whether the authentication was successful.
+ checkPasswordPolicyState(saslAuthUserEntry, saslHandler);
+ }
+
+
+ // Determine whether the authentication was successful and perform
+ // any remaining password policy processing accordingly.
+ ResultCode resultCode = getResultCode();
+ if (resultCode == ResultCode.SUCCESS)
+ {
+ if (pwPolicyState != null)
+ {
+ if (saslHandler.isPasswordBased(saslMechanism) &&
+ pwPolicyState.mustChangePassword())
+ {
+ mustChangePassword = true;
+ }
+
+ if (isFirstWarning)
+ {
+ pwPolicyState.setWarnedTime();
+
+ int numSeconds = pwPolicyState.getSecondsUntilExpiration();
+ Message m = WARN_BIND_PASSWORD_EXPIRING.get(
+ secondsToTimeString(numSeconds));
+
+ pwPolicyState.generateAccountStatusNotification(
+ AccountStatusNotificationType.PASSWORD_EXPIRING,
+ saslAuthUserEntry, m,
+ AccountStatusNotification.createProperties(pwPolicyState,
+ false, numSeconds, null, null));
+ }
+
+ if (isGraceLogin)
+ {
+ pwPolicyState.updateGraceLoginTimes();
+ }
+
+ pwPolicyState.setLastLoginTime();
+
+
+ // Set appropriate resource limits for the user.
+ setResourceLimits(saslAuthUserEntry);
+ }
+ }
+ else if (resultCode == ResultCode.SASL_BIND_IN_PROGRESS)
+ {
+ // FIXME -- Is any special processing needed here?
+ return false;
+ }
+ else
+ {
+ if (pwPolicyState != null)
+ {
+ if (saslHandler.isPasswordBased(saslMechanism))
+ {
+
+ if (pwPolicyState.getPolicy().getLockoutFailureCount() > 0)
+ {
+ pwPolicyState.updateAuthFailureTimes();
+ if (pwPolicyState.lockedDueToFailures())
+ {
+ AccountStatusNotificationType notificationType;
+ boolean tempLocked;
+ Message m;
+
+ int lockoutDuration = pwPolicyState.getSecondsUntilUnlock();
+ if (lockoutDuration > -1)
+ {
+ notificationType = AccountStatusNotificationType.
+ ACCOUNT_TEMPORARILY_LOCKED;
+ tempLocked = true;
+ m = ERR_BIND_ACCOUNT_TEMPORARILY_LOCKED.get(
+ secondsToTimeString(lockoutDuration));
+ }
+ else
+ {
+ notificationType =
+ AccountStatusNotificationType.ACCOUNT_PERMANENTLY_LOCKED;
+ tempLocked = false;
+ m = ERR_BIND_ACCOUNT_PERMANENTLY_LOCKED.get();
+ }
+
+ pwPolicyState.generateAccountStatusNotification(
+ notificationType, saslAuthUserEntry, m,
+ AccountStatusNotification.createProperties(
+ pwPolicyState, tempLocked, -1, null, null));
+ }
+ }
+ }
+ }
+ }
+
+ return true;
+ }
+
+
+
+ /**
+ * Validates a number of password policy state constraints for the user.
+ *
+ * @param userEntry The entry for the user that is authenticating.
+ * @param saslHandler The SASL mechanism handler if this is a SASL bind, or
+ * {@code null} for a simple bind.
+ *
+ * @throws DirectoryException If a problem occurs that should cause the bind
+ * to fail.
+ */
+ private void checkPasswordPolicyState(Entry userEntry,
+ SASLMechanismHandler saslHandler)
+ throws DirectoryException
+ {
+ boolean isSASLBind = (saslHandler != null);
+
+ // If the password policy is configured to track authentication failures or
+ // keep the last login time and the associated backend is disabled, then we
+ // may need to reject the bind immediately.
+ if ((policy.getStateUpdateFailurePolicy() ==
+ PasswordPolicyCfgDefn.StateUpdateFailurePolicy.PROACTIVE) &&
+ ((policy.getLockoutFailureCount() > 0) ||
+ ((policy.getLastLoginTimeAttribute() != null) &&
+ (policy.getLastLoginTimeFormat() != null))) &&
+ ((DirectoryServer.getWritabilityMode() == WritabilityMode.DISABLED) ||
+ (backend.getWritabilityMode() == WritabilityMode.DISABLED)))
+ {
+ // This policy isn't applicable to root users, so if it's a root
+ // user then ignore it.
+ if (! DirectoryServer.isRootDN(userEntry.getDN()))
+ {
+ throw new DirectoryException(ResultCode.INVALID_CREDENTIALS,
+ ERR_BIND_OPERATION_WRITABILITY_DISABLED.get(
+ String.valueOf(userEntry.getDN())));
+ }
+ }
+
+
+ // Check to see if the authentication must be done in a secure
+ // manner. If so, then the client connection must be secure.
+ if (policy.requireSecureAuthentication() && (! clientConnection.isSecure()))
+ {
+ if (isSASLBind)
+ {
+ if (! saslHandler.isSecure(saslMechanism))
+ {
+ throw new DirectoryException(ResultCode.INVALID_CREDENTIALS,
+ ERR_BIND_OPERATION_INSECURE_SASL_BIND.get(
+ saslMechanism,
+ String.valueOf(userEntry.getDN())));
+ }
+ }
+ else
+ {
+ throw new DirectoryException(ResultCode.INVALID_CREDENTIALS,
+ ERR_BIND_OPERATION_INSECURE_SIMPLE_BIND.get(
+ String.valueOf(userEntry.getDN())));
+ }
+ }
+
+
+ // Check to see if the user is administratively disabled or locked.
+ if (pwPolicyState.isDisabled())
+ {
+ throw new DirectoryException(ResultCode.INVALID_CREDENTIALS,
+ ERR_BIND_OPERATION_ACCOUNT_DISABLED.get(
+ String.valueOf(userEntry.getDN())));
+ }
+ else if (pwPolicyState.isAccountExpired())
+ {
+ Message m = ERR_BIND_OPERATION_ACCOUNT_EXPIRED.get(
+ String.valueOf(userEntry.getDN()));
+ pwPolicyState.generateAccountStatusNotification(
+ AccountStatusNotificationType.ACCOUNT_EXPIRED, userEntry, m,
+ AccountStatusNotification.createProperties(pwPolicyState,
+ false, -1, null, null));
+
+ throw new DirectoryException(ResultCode.INVALID_CREDENTIALS, m);
+ }
+ else if (pwPolicyState.lockedDueToFailures())
+ {
+ if (pwPolicyErrorType == null)
+ {
+ pwPolicyErrorType = PasswordPolicyErrorType.ACCOUNT_LOCKED;
+ }
+
+ throw new DirectoryException(ResultCode.INVALID_CREDENTIALS,
+ ERR_BIND_OPERATION_ACCOUNT_FAILURE_LOCKED.get(
+ String.valueOf(userEntry.getDN())));
+ }
+ else if (pwPolicyState.lockedDueToIdleInterval())
+ {
+ Message m = ERR_BIND_OPERATION_ACCOUNT_IDLE_LOCKED.get(
+ String.valueOf(userEntry.getDN()));
+
+ if (pwPolicyErrorType == null)
+ {
+ pwPolicyErrorType = PasswordPolicyErrorType.ACCOUNT_LOCKED;
+ }
+
+ pwPolicyState.generateAccountStatusNotification(
+ AccountStatusNotificationType.ACCOUNT_IDLE_LOCKED, userEntry, m,
+ AccountStatusNotification.createProperties(pwPolicyState, false, -1,
+ null, null));
+
+ throw new DirectoryException(ResultCode.INVALID_CREDENTIALS, m);
+ }
+
+
+ // If it's a simple bind, or if it's a password-based SASL bind, then
+ // perform a number of password-based checks.
+ if ((! isSASLBind) || saslHandler.isPasswordBased(saslMechanism))
+ {
+ // Check to see if the account is locked due to the maximum reset age.
+ if (pwPolicyState.lockedDueToMaximumResetAge())
+ {
+ Message m = ERR_BIND_OPERATION_ACCOUNT_RESET_LOCKED.get(
+ String.valueOf(userEntry.getDN()));
+
+ if (pwPolicyErrorType == null)
+ {
+ pwPolicyErrorType = PasswordPolicyErrorType.ACCOUNT_LOCKED;
+ }
+
+ pwPolicyState.generateAccountStatusNotification(
+ AccountStatusNotificationType.ACCOUNT_RESET_LOCKED, userEntry, m,
+ AccountStatusNotification.createProperties(pwPolicyState, false,
+ -1, null, null));
+
+ throw new DirectoryException(ResultCode.INVALID_CREDENTIALS, m);
+ }
+
+
+ // Determine whether the password is expired, or whether the user
+ // should be warned about an upcoming expiration.
+ if (pwPolicyState.isPasswordExpired())
+ {
+ if (pwPolicyErrorType == null)
+ {
+ pwPolicyErrorType = PasswordPolicyErrorType.PASSWORD_EXPIRED;
+ }
+
+ int maxGraceLogins = policy.getGraceLoginCount();
+ if ((maxGraceLogins > 0) && pwPolicyState.mayUseGraceLogin())
+ {
+ List<Long> graceLoginTimes = pwPolicyState.getGraceLoginTimes();
+ if ((graceLoginTimes == null) ||
+ (graceLoginTimes.size() < maxGraceLogins))
+ {
+ isGraceLogin = true;
+ mustChangePassword = true;
+
+ if (pwPolicyWarningType == null)
+ {
+ pwPolicyWarningType =
+ PasswordPolicyWarningType.GRACE_LOGINS_REMAINING;
+ pwPolicyWarningValue = maxGraceLogins -
+ (graceLoginTimes.size() + 1);
+ }
+ }
+ else
+ {
+ Message m = ERR_BIND_OPERATION_PASSWORD_EXPIRED.get(
+ String.valueOf(userEntry.getDN()));
+
+ pwPolicyState.generateAccountStatusNotification(
+ AccountStatusNotificationType.PASSWORD_EXPIRED, userEntry, m,
+ AccountStatusNotification.createProperties(pwPolicyState,
+ false, -1, null,
+ null));
+
+ throw new DirectoryException(ResultCode.INVALID_CREDENTIALS, m);
+ }
+ }
+ else
+ {
+ Message m = ERR_BIND_OPERATION_PASSWORD_EXPIRED.get(
+ String.valueOf(userEntry.getDN()));
+
+ pwPolicyState.generateAccountStatusNotification(
+ AccountStatusNotificationType.PASSWORD_EXPIRED, userEntry, m,
+ AccountStatusNotification.createProperties(pwPolicyState, false,
+ -1, null, null));
+
+ throw new DirectoryException(ResultCode.INVALID_CREDENTIALS, m);
+ }
+ }
+ else if (pwPolicyState.shouldWarn())
+ {
+ int numSeconds = pwPolicyState.getSecondsUntilExpiration();
+
+ if (pwPolicyWarningType == null)
+ {
+ pwPolicyWarningType =
+ PasswordPolicyWarningType.TIME_BEFORE_EXPIRATION;
+ pwPolicyWarningValue = numSeconds;
+ }
+
+ isFirstWarning = pwPolicyState.isFirstWarning();
+ }
+
+
+ // Check to see if the user's password has been reset.
+ if (pwPolicyState.mustChangePassword())
+ {
+ mustChangePassword = true;
+
+ if (pwPolicyErrorType == null)
+ {
+ pwPolicyErrorType = PasswordPolicyErrorType.CHANGE_AFTER_RESET;
+ }
+ }
+ }
+ }
+
+
+
+ /**
+ * Sets resource limits for the authenticated user.
+ *
+ * @param userEntry The entry for the authenticated user.
+ */
+ private void setResourceLimits(Entry userEntry)
+ {
+ // See if the user's entry contains a custom size limit.
+ AttributeType attrType =
+ DirectoryServer.getAttributeType(OP_ATTR_USER_SIZE_LIMIT, true);
+ List<Attribute> attrList = userEntry.getAttribute(attrType);
+ if ((attrList != null) && (attrList.size() == 1))
+ {
+ Attribute a = attrList.get(0);
+ LinkedHashSet<AttributeValue> values = a.getValues();
+ Iterator<AttributeValue> iterator = values.iterator();
+ if (iterator.hasNext())
+ {
+ AttributeValue v = iterator.next();
+ if (iterator.hasNext())
+ {
+ logError(WARN_BIND_MULTIPLE_USER_SIZE_LIMITS.get(
+ String.valueOf(userEntry.getDN())));
+ }
+ else
+ {
+ try
+ {
+ sizeLimit = Integer.parseInt(v.getStringValue());
+ }
+ catch (Exception e)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ }
+
+ logError(WARN_BIND_CANNOT_PROCESS_USER_SIZE_LIMIT.get(
+ v.getStringValue(),
+ String.valueOf(userEntry.getDN())));
+ }
+ }
+ }
+ }
+
+
+ // See if the user's entry contains a custom time limit.
+ attrType = DirectoryServer.getAttributeType(OP_ATTR_USER_TIME_LIMIT, true);
+ attrList = userEntry.getAttribute(attrType);
+ if ((attrList != null) && (attrList.size() == 1))
+ {
+ Attribute a = attrList.get(0);
+ LinkedHashSet<AttributeValue> values = a.getValues();
+ Iterator<AttributeValue> iterator = values.iterator();
+ if (iterator.hasNext())
+ {
+ AttributeValue v = iterator.next();
+ if (iterator.hasNext())
+ {
+ logError(WARN_BIND_MULTIPLE_USER_TIME_LIMITS.get(
+ String.valueOf(userEntry.getDN())));
+ }
+ else
+ {
+ try
+ {
+ timeLimit = Integer.parseInt(v.getStringValue());
+ }
+ catch (Exception e)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ }
+
+ logError(WARN_BIND_CANNOT_PROCESS_USER_TIME_LIMIT.get(
+ v.getStringValue(),
+ String.valueOf(userEntry.getDN())));
+ }
+ }
+ }
+ }
+
+
+ // See if the user's entry contains a custom idle time limit.
+ attrType = DirectoryServer.getAttributeType(OP_ATTR_USER_IDLE_TIME_LIMIT,
+ true);
+ attrList = userEntry.getAttribute(attrType);
+ if ((attrList != null) && (attrList.size() == 1))
+ {
+ Attribute a = attrList.get(0);
+ LinkedHashSet<AttributeValue> values = a.getValues();
+ Iterator<AttributeValue> iterator = values.iterator();
+ if (iterator.hasNext())
+ {
+ AttributeValue v = iterator.next();
+ if (iterator.hasNext())
+ {
+ logError(WARN_BIND_MULTIPLE_USER_IDLE_TIME_LIMITS.get(
+ String.valueOf(userEntry.getDN())));
+ }
+ else
+ {
+ try
+ {
+ idleTimeLimit = 1000L * Long.parseLong(v.getStringValue());
+ }
+ catch (Exception e)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ }
+
+ logError(WARN_BIND_CANNOT_PROCESS_USER_IDLE_TIME_LIMIT.get(
+ v.getStringValue(),
+ String.valueOf(userEntry.getDN())));
+ }
+ }
+ }
+ }
+
+
+ // See if the user's entry contains a custom lookthrough limit.
+ attrType = DirectoryServer.getAttributeType(OP_ATTR_USER_LOOKTHROUGH_LIMIT,
+ true);
+ attrList = userEntry.getAttribute(attrType);
+ if ((attrList != null) && (attrList.size() == 1))
+ {
+ Attribute a = attrList.get(0);
+ LinkedHashSet<AttributeValue> values = a.getValues();
+ Iterator<AttributeValue> iterator = values.iterator();
+ if (iterator.hasNext())
+ {
+ AttributeValue v = iterator.next();
+ if (iterator.hasNext())
+ {
+ logError(WARN_BIND_MULTIPLE_USER_LOOKTHROUGH_LIMITS.get(
+ String.valueOf(userEntry.getDN())));
+ }
+ else
+ {
+ try
+ {
+ lookthroughLimit = Integer.parseInt(v.getStringValue());
+ }
+ catch (Exception e)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ }
+
+ logError(WARN_BIND_CANNOT_PROCESS_USER_LOOKTHROUGH_LIMIT.get(
+ v.getStringValue(),
+ String.valueOf(userEntry.getDN())));
+ }
+ }
+ }
+ }
+ }
}
+
diff --git a/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendCompareOperation.java b/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendCompareOperation.java
index 947b3cc..206ac65 100644
--- a/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendCompareOperation.java
+++ b/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendCompareOperation.java
@@ -27,28 +27,82 @@
package org.opends.server.workflowelement.localbackend;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.concurrent.locks.Lock;
+
+import org.opends.server.api.Backend;
+import org.opends.server.api.ClientConnection;
+import org.opends.server.api.plugin.PostOperationPluginResult;
+import org.opends.server.api.plugin.PreOperationPluginResult;
+import org.opends.server.controls.LDAPAssertionRequestControl;
+import org.opends.server.controls.ProxiedAuthV1Control;
+import org.opends.server.controls.ProxiedAuthV2Control;
+import org.opends.server.core.AccessControlConfigManager;
import org.opends.server.core.CompareOperation;
import org.opends.server.core.CompareOperationWrapper;
+import org.opends.server.core.DirectoryServer;
+import org.opends.server.core.PluginConfigManager;
+import org.opends.server.loggers.debug.DebugTracer;
+import org.opends.server.types.Attribute;
+import org.opends.server.types.AttributeType;
+import org.opends.server.types.AttributeValue;
+import org.opends.server.types.Control;
+import org.opends.server.types.DebugLogLevel;
+import org.opends.server.types.DirectoryException;
+import org.opends.server.types.DN;
import org.opends.server.types.Entry;
+import org.opends.server.types.LDAPException;
+import org.opends.server.types.LockManager;
+import org.opends.server.types.Privilege;
+import org.opends.server.types.ResultCode;
+import org.opends.server.types.SearchFilter;
import org.opends.server.types.operation.PostOperationCompareOperation;
import org.opends.server.types.operation.PostResponseCompareOperation;
import org.opends.server.types.operation.PreOperationCompareOperation;
+import static org.opends.messages.CoreMessages.*;
+import static org.opends.server.loggers.debug.DebugLogger.*;
+import static org.opends.server.util.ServerConstants.*;
+import static org.opends.server.util.StaticUtils.*;
+
+
/**
* This class defines an operation that may be used to determine whether a
* specified entry in the Directory Server contains a given attribute-value
* pair.
*/
-public class LocalBackendCompareOperation extends CompareOperationWrapper
- implements PreOperationCompareOperation,
- PostOperationCompareOperation,
- PostResponseCompareOperation
+public class LocalBackendCompareOperation
+ extends CompareOperationWrapper
+ implements PreOperationCompareOperation, PostOperationCompareOperation,
+ PostResponseCompareOperation
{
+ /**
+ * The tracer object for the debug logger.
+ */
+ private static final DebugTracer TRACER = getTracer();
+
+
+
+ // The backend in which the comparison is to be performed.
+ private Backend backend;
+
+ // Indicates whether to skip post-operation processing.
+ private boolean skipPostOperation;
+
+ // The client connection for this operation.
+ private ClientConnection clientConnection;
+
+ // The DN of the entry to compare.
+ private DN entryDN;
+
// The entry to be compared.
private Entry entry = null;
+
/**
* Creates a new compare operation based on the provided compare operation.
*
@@ -61,6 +115,7 @@
}
+
/**
* Retrieves the entry to target with the compare operation.
*
@@ -73,14 +128,505 @@
}
+
/**
- * Set the entry to target with the compare operation.
+ * Process this compare operation in a local backend.
*
- * @param entry The entry to target with the compare operation.
+ * @param backend The backend in which the compare operation should be
+ * processed.
*/
- public void setEntryToCompare(Entry entry)
+ void processLocalCompare(Backend backend)
{
- this.entry = entry;
+ this.backend = backend;
+
+ clientConnection = getClientConnection();
+ skipPostOperation = false;
+
+ // Get the plugin config manager that will be used for invoking plugins.
+ PluginConfigManager pluginConfigManager =
+ DirectoryServer.getPluginConfigManager();
+
+
+ // Get a reference to the client connection
+ ClientConnection clientConnection = getClientConnection();
+
+
+ // Check for a request to cancel this operation.
+ if (getCancelRequest() != null)
+ {
+ return;
+ }
+
+
+ // Create a labeled block of code that we can break out of if a problem is
+ // detected.
+compareProcessing:
+ {
+ // Process the entry DN to convert it from the raw form to the form
+ // required for the rest of the compare processing.
+ entryDN = getEntryDN();
+ if (entryDN == null)
+ {
+ skipPostOperation = true;
+ break compareProcessing;
+ }
+
+
+ // If the target entry is in the server configuration, then make sure the
+ // requester has the CONFIG_READ privilege.
+ if (DirectoryServer.getConfigHandler().handlesEntry(entryDN) &&
+ (! clientConnection.hasPrivilege(Privilege.CONFIG_READ, this)))
+ {
+ appendErrorMessage(ERR_COMPARE_CONFIG_INSUFFICIENT_PRIVILEGES.get());
+ setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS);
+ skipPostOperation = true;
+ break compareProcessing;
+ }
+
+
+ // Check for a request to cancel this operation.
+ if (getCancelRequest() != null)
+ {
+ return;
+ }
+
+
+ // Grab a read lock on the entry.
+ Lock readLock = null;
+ for (int i=0; i < 3; i++)
+ {
+ readLock = LockManager.lockRead(entryDN);
+ if (readLock != null)
+ {
+ break;
+ }
+ }
+
+ if (readLock == null)
+ {
+ setResultCode(DirectoryServer.getServerErrorResultCode());
+ appendErrorMessage(ERR_COMPARE_CANNOT_LOCK_ENTRY.get(
+ String.valueOf(entryDN)));
+
+ skipPostOperation = true;
+ break compareProcessing;
+ }
+
+ try
+ {
+ // Get the entry. If it does not exist, then fail.
+ try
+ {
+ entry = DirectoryServer.getEntry(entryDN);
+
+ if (entry == null)
+ {
+ setResultCode(ResultCode.NO_SUCH_OBJECT);
+ appendErrorMessage(
+ ERR_COMPARE_NO_SUCH_ENTRY.get(String.valueOf(entryDN)));
+
+ // See if one of the entry's ancestors exists.
+ DN parentDN = entryDN.getParentDNInSuffix();
+ while (parentDN != null)
+ {
+ try
+ {
+ if (DirectoryServer.entryExists(parentDN))
+ {
+ setMatchedDN(parentDN);
+ break;
+ }
+ }
+ catch (Exception e)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ }
+ break;
+ }
+
+ parentDN = parentDN.getParentDNInSuffix();
+ }
+
+ break compareProcessing;
+ }
+ }
+ catch (DirectoryException de)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, de);
+ }
+
+ setResultCode(de.getResultCode());
+ appendErrorMessage(de.getMessageObject());
+ break compareProcessing;
+ }
+
+ // Check to see if there are any controls in the request. If so, then
+ // see if there is any special processing required.
+ try
+ {
+ handleRequestControls();
+ }
+ catch (DirectoryException de)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, de);
+ }
+
+ setResponseData(de);
+ break compareProcessing;
+ }
+
+
+ // Check to see if the client has permission to perform the
+ // compare.
+
+ // FIXME: for now assume that this will check all permission
+ // pertinent to the operation. This includes proxy authorization
+ // and any other controls specified.
+
+ // FIXME: earlier checks to see if the entry already exists may
+ // have already exposed sensitive information to the client.
+ if (! AccessControlConfigManager.getInstance().
+ getAccessControlHandler().isAllowed(this))
+ {
+ setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS);
+ appendErrorMessage(ERR_COMPARE_AUTHZ_INSUFFICIENT_ACCESS_RIGHTS.get(
+ String.valueOf(entryDN)));
+ skipPostOperation = true;
+ break compareProcessing;
+ }
+
+ // Check for a request to cancel this operation.
+ if (getCancelRequest() != null)
+ {
+ return;
+ }
+
+
+ // Invoke the pre-operation compare plugins.
+ PreOperationPluginResult preOpResult =
+ pluginConfigManager.invokePreOperationComparePlugins(this);
+ if (preOpResult.connectionTerminated())
+ {
+ // There's no point in continuing with anything. Log the request and
+ // result and return.
+ setResultCode(ResultCode.CANCELED);
+ appendErrorMessage(ERR_CANCELED_BY_PREOP_DISCONNECT.get());
+ setProcessingStopTime();
+ return;
+ }
+ else if (preOpResult.sendResponseImmediately())
+ {
+ skipPostOperation = true;
+ break compareProcessing;
+ }
+ else if (preOpResult.skipCoreProcessing())
+ {
+ skipPostOperation = false;
+ break compareProcessing;
+ }
+
+
+ // Get the base attribute type and set of options.
+ String baseName;
+ HashSet<String> options;
+ String rawAttributeType = getRawAttributeType();
+ int semicolonPos = rawAttributeType.indexOf(';');
+ if (semicolonPos > 0)
+ {
+ baseName = toLowerCase(rawAttributeType.substring(0, semicolonPos));
+
+ options = new HashSet<String>();
+ int nextPos = rawAttributeType.indexOf(';', semicolonPos+1);
+ while (nextPos > 0)
+ {
+ options.add(rawAttributeType.substring(semicolonPos+1, nextPos));
+ semicolonPos = nextPos;
+ nextPos = rawAttributeType.indexOf(';', semicolonPos+1);
+ }
+
+ options.add(rawAttributeType.substring(semicolonPos+1));
+ }
+ else
+ {
+ baseName = toLowerCase(rawAttributeType);
+ options = null;
+ }
+
+
+ // Actually perform the compare operation.
+ AttributeType attrType = getAttributeType();
+ if (attrType == null)
+ {
+ attrType = DirectoryServer.getAttributeType(baseName, true);
+ setAttributeType(attrType);
+ }
+
+ List<Attribute> attrList = entry.getAttribute(attrType, options);
+ if ((attrList == null) || attrList.isEmpty())
+ {
+ setResultCode(ResultCode.NO_SUCH_ATTRIBUTE);
+ if (options == null)
+ {
+ appendErrorMessage(WARN_COMPARE_OP_NO_SUCH_ATTR.get(
+ String.valueOf(entryDN), baseName));
+ }
+ else
+ {
+ appendErrorMessage(WARN_COMPARE_OP_NO_SUCH_ATTR_WITH_OPTIONS.get(
+ String.valueOf(entryDN), baseName));
+ }
+ }
+ else
+ {
+ AttributeValue value = new AttributeValue(attrType,
+ getAssertionValue());
+
+ boolean matchFound = false;
+ for (Attribute a : attrList)
+ {
+ if (a.hasValue(value))
+ {
+ matchFound = true;
+ break;
+ }
+ }
+
+ if (matchFound)
+ {
+ setResultCode(ResultCode.COMPARE_TRUE);
+ }
+ else
+ {
+ setResultCode(ResultCode.COMPARE_FALSE);
+ }
+ }
+ }
+ finally
+ {
+ LockManager.unlock(entryDN, readLock);
+ }
+ }
+
+
+ // Check for a request to cancel this operation.
+ if (getCancelRequest() != null)
+ {
+ return;
+ }
+
+
+ // Invoke the post-operation compare plugins.
+ if (! skipPostOperation)
+ {
+ PostOperationPluginResult postOperationResult =
+ pluginConfigManager.invokePostOperationComparePlugins(this);
+ if (postOperationResult.connectionTerminated())
+ {
+ setResultCode(ResultCode.CANCELED);
+ appendErrorMessage(ERR_CANCELED_BY_POSTOP_DISCONNECT.get());
+ return;
+ }
+ }
}
+
+
+ /**
+ * Performs any processing required for the controls included in the request.
+ *
+ * @throws DirectoryException If a problem occurs that should prevent the
+ * operation from succeeding.
+ */
+ private void handleRequestControls()
+ throws DirectoryException
+ {
+ List<Control> requestControls = getRequestControls();
+ if ((requestControls != null) && (! requestControls.isEmpty()))
+ {
+ for (int i=0; i < requestControls.size(); i++)
+ {
+ Control c = requestControls.get(i);
+ String oid = c.getOID();
+
+ if (! AccessControlConfigManager.getInstance().
+ getAccessControlHandler().isAllowed(entryDN, this, c))
+ {
+ skipPostOperation = true;
+ throw new DirectoryException(ResultCode.INSUFFICIENT_ACCESS_RIGHTS,
+ ERR_CONTROL_INSUFFICIENT_ACCESS_RIGHTS.get(oid));
+ }
+
+ if (oid.equals(OID_LDAP_ASSERTION))
+ {
+ LDAPAssertionRequestControl assertControl;
+ if (c instanceof LDAPAssertionRequestControl)
+ {
+ assertControl = (LDAPAssertionRequestControl) c;
+ }
+ else
+ {
+ try
+ {
+ assertControl = LDAPAssertionRequestControl.decodeControl(c);
+ requestControls.set(i, assertControl);
+ }
+ catch (LDAPException le)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, le);
+ }
+
+ throw new DirectoryException(
+ ResultCode.valueOf(le.getResultCode()),
+ le.getMessageObject());
+ }
+ }
+
+ try
+ {
+ // FIXME -- We need to determine whether the current user has
+ // permission to make this determination.
+ SearchFilter filter = assertControl.getSearchFilter();
+ if (! filter.matchesEntry(entry))
+ {
+ throw new DirectoryException(ResultCode.ASSERTION_FAILED,
+ ERR_COMPARE_ASSERTION_FAILED.get(
+ String.valueOf(entryDN)));
+ }
+ }
+ catch (DirectoryException de)
+ {
+ if (de.getResultCode() == ResultCode.ASSERTION_FAILED)
+ {
+ throw de;
+ }
+
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, de);
+ }
+
+ throw new DirectoryException(ResultCode.PROTOCOL_ERROR,
+ ERR_COMPARE_CANNOT_PROCESS_ASSERTION_FILTER.get(
+ String.valueOf(entryDN),
+ de.getMessageObject()));
+ }
+ }
+ else if (oid.equals(OID_PROXIED_AUTH_V1))
+ {
+ // The requester must have the PROXIED_AUTH privilige in order to
+ // be able to use this control.
+ if (! clientConnection.hasPrivilege(Privilege.PROXIED_AUTH, this))
+ {
+ throw new DirectoryException(ResultCode.AUTHORIZATION_DENIED,
+ ERR_PROXYAUTH_INSUFFICIENT_PRIVILEGES.get());
+ }
+
+
+ ProxiedAuthV1Control proxyControl;
+ if (c instanceof ProxiedAuthV1Control)
+ {
+ proxyControl = (ProxiedAuthV1Control) c;
+ }
+ else
+ {
+ try
+ {
+ proxyControl = ProxiedAuthV1Control.decodeControl(c);
+ }
+ catch (LDAPException le)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, le);
+ }
+
+ throw new DirectoryException(
+ ResultCode.valueOf(le.getResultCode()),
+ le.getMessageObject());
+ }
+ }
+
+
+ Entry authorizationEntry = proxyControl.getAuthorizationEntry();
+ setAuthorizationEntry(authorizationEntry);
+ if (authorizationEntry == null)
+ {
+ setProxiedAuthorizationDN(DN.nullDN());
+ }
+ else
+ {
+ setProxiedAuthorizationDN(authorizationEntry.getDN());
+ }
+ }
+ else if (oid.equals(OID_PROXIED_AUTH_V2))
+ {
+ // The requester must have the PROXIED_AUTH privilige in order to
+ // be able to use this control.
+ if (! clientConnection.hasPrivilege(Privilege.PROXIED_AUTH, this))
+ {
+ throw new DirectoryException(ResultCode.AUTHORIZATION_DENIED,
+ ERR_PROXYAUTH_INSUFFICIENT_PRIVILEGES.get());
+ }
+
+
+ ProxiedAuthV2Control proxyControl;
+ if (c instanceof ProxiedAuthV2Control)
+ {
+ proxyControl = (ProxiedAuthV2Control) c;
+ }
+ else
+ {
+ try
+ {
+ proxyControl = ProxiedAuthV2Control.decodeControl(c);
+ }
+ catch (LDAPException le)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, le);
+ }
+
+ throw new DirectoryException(
+ ResultCode.valueOf(le.getResultCode()),
+ le.getMessageObject());
+ }
+ }
+
+
+ Entry authorizationEntry = proxyControl.getAuthorizationEntry();
+ setAuthorizationEntry(authorizationEntry);
+ if (authorizationEntry == null)
+ {
+ setProxiedAuthorizationDN(DN.nullDN());
+ }
+ else
+ {
+ setProxiedAuthorizationDN(authorizationEntry.getDN());
+ }
+ }
+
+ // NYI -- Add support for additional controls.
+ else if (c.isCritical())
+ {
+ if ((backend == null) || (! backend.supportsControl(oid)))
+ {
+ throw new DirectoryException(
+ ResultCode.UNAVAILABLE_CRITICAL_EXTENSION,
+ ERR_COMPARE_UNSUPPORTED_CRITICAL_CONTROL.get(
+ String.valueOf(entryDN), oid));
+ }
+ }
+ }
+ }
+ }
}
+
diff --git a/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendDeleteOperation.java b/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendDeleteOperation.java
index ebd4182..02e2887 100644
--- a/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendDeleteOperation.java
+++ b/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendDeleteOperation.java
@@ -27,27 +27,97 @@
package org.opends.server.workflowelement.localbackend;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.locks.Lock;
+
+import org.opends.messages.Message;
+import org.opends.server.api.Backend;
+import org.opends.server.api.ChangeNotificationListener;
+import org.opends.server.api.ClientConnection;
+import org.opends.server.api.SynchronizationProvider;
+import org.opends.server.api.plugin.PostOperationPluginResult;
+import org.opends.server.api.plugin.PreOperationPluginResult;
+import org.opends.server.controls.LDAPAssertionRequestControl;
+import org.opends.server.controls.LDAPPreReadRequestControl;
+import org.opends.server.controls.LDAPPreReadResponseControl;
+import org.opends.server.controls.ProxiedAuthV1Control;
+import org.opends.server.controls.ProxiedAuthV2Control;
+import org.opends.server.core.AccessControlConfigManager;
import org.opends.server.core.DeleteOperationWrapper;
import org.opends.server.core.DeleteOperation;
+import org.opends.server.core.DirectoryServer;
+import org.opends.server.core.PluginConfigManager;
+import org.opends.server.loggers.debug.DebugTracer;
+import org.opends.server.types.AttributeType;
+import org.opends.server.types.CancelledOperationException;
+import org.opends.server.types.CancelResult;
+import org.opends.server.types.Control;
+import org.opends.server.types.DebugLogLevel;
+import org.opends.server.types.DirectoryException;
+import org.opends.server.types.DN;
import org.opends.server.types.Entry;
+import org.opends.server.types.LDAPException;
+import org.opends.server.types.LockManager;
+import org.opends.server.types.Privilege;
+import org.opends.server.types.ResultCode;
+import org.opends.server.types.SearchFilter;
+import org.opends.server.types.SearchResultEntry;
+import org.opends.server.types.SynchronizationProviderResult;
import org.opends.server.types.operation.PostOperationDeleteOperation;
import org.opends.server.types.operation.PostResponseDeleteOperation;
import org.opends.server.types.operation.PreOperationDeleteOperation;
import org.opends.server.types.operation.PostSynchronizationDeleteOperation;
+import static org.opends.messages.CoreMessages.*;
+import static org.opends.server.loggers.ErrorLogger.*;
+import static org.opends.server.loggers.debug.DebugLogger.*;
+import static org.opends.server.util.ServerConstants.*;
+import static org.opends.server.util.StaticUtils.*;
+
+
+
/**
* This class defines an operation used to delete an entry in a local backend
* of the Directory Server.
*/
-public class LocalBackendDeleteOperation extends DeleteOperationWrapper
- implements PreOperationDeleteOperation,
- PostOperationDeleteOperation,
- PostResponseDeleteOperation,
- PostSynchronizationDeleteOperation
+public class LocalBackendDeleteOperation
+ extends DeleteOperationWrapper
+ implements PreOperationDeleteOperation, PostOperationDeleteOperation,
+ PostResponseDeleteOperation,
+ PostSynchronizationDeleteOperation
{
+ /**
+ * The tracer object for the debug logger.
+ */
+ private static final DebugTracer TRACER = getTracer();
+
+
+
+ // The backend in which the operation is to be processed.
+ private Backend backend;
+
+ // Indicates whether the LDAP no-op control has been requested.
+ private boolean noOp;
+
+ // Indicates whether to skip post-operation processing.
+ private boolean skipPostOperation;
+
+ // The client connection on which this operation was requested.
+ private ClientConnection clientConnection;
+
+ // The DN of the entry to be deleted.
+ private DN entryDN;
+
// The entry to be deleted.
private Entry entry;
+ // The pre-read request control included in the request, if applicable.
+ private LDAPPreReadRequestControl preReadRequest;
+
+
+
/**
* Creates a new operation that may be used to delete an entry from a
* local backend of the Directory Server.
@@ -61,6 +131,7 @@
}
+
/**
* Retrieves the entry to be deleted.
*
@@ -72,13 +143,731 @@
return entry;
}
+
+
/**
- * Sets the entry to be deleted.
+ * Process this delete operation in a local backend.
*
- * @param entry - The entry to be deleted
+ * @param backend The backend in which the delete operation should be
+ * processed.
*/
- public void setEntryToDelete(Entry entry){
- this.entry = entry;
+ void processLocalDelete(Backend backend)
+ {
+ this.backend = backend;
+
+ clientConnection = getClientConnection();
+
+ // Get the plugin config manager that will be used for invoking plugins.
+ PluginConfigManager pluginConfigManager =
+ DirectoryServer.getPluginConfigManager();
+ skipPostOperation = false;
+
+ // Check for a request to cancel this operation.
+ if (getCancelRequest() != null)
+ {
+ return;
+ }
+
+ // Create a labeled block of code that we can break out of if a problem is
+ // detected.
+deleteProcessing:
+ {
+ // Process the entry DN to convert it from its raw form as provided by the
+ // client to the form required for the rest of the delete processing.
+ entryDN = getEntryDN();
+ if (entryDN == null){
+ break deleteProcessing;
+ }
+
+ // Grab a write lock on the entry.
+ Lock entryLock = null;
+ for (int i=0; i < 3; i++)
+ {
+ entryLock = LockManager.lockWrite(entryDN);
+ if (entryLock != null)
+ {
+ break;
+ }
+ }
+
+ if (entryLock == null)
+ {
+ setResultCode(DirectoryServer.getServerErrorResultCode());
+ appendErrorMessage(ERR_DELETE_CANNOT_LOCK_ENTRY.get(
+ String.valueOf(entryDN)));
+ break deleteProcessing;
+ }
+
+ try
+ {
+ // Get the entry to delete. If it doesn't exist, then fail.
+ try
+ {
+ entry = backend.getEntry(entryDN);
+ if (entry == null)
+ {
+ setResultCode(ResultCode.NO_SUCH_OBJECT);
+ appendErrorMessage(ERR_DELETE_NO_SUCH_ENTRY.get(
+ String.valueOf(entryDN)));
+
+ try
+ {
+ DN parentDN = entryDN.getParentDNInSuffix();
+ while (parentDN != null)
+ {
+ if (DirectoryServer.entryExists(parentDN))
+ {
+ setMatchedDN(parentDN);
+ break;
+ }
+
+ parentDN = parentDN.getParentDNInSuffix();
+ }
+ }
+ catch (Exception e)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ }
+ }
+
+ break deleteProcessing;
+ }
+ }
+ catch (DirectoryException de)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, de);
+ }
+
+ setResponseData(de);
+ break deleteProcessing;
+ }
+
+
+ // Invoke any conflict resolution processing that might be needed by the
+ // synchronization provider.
+ for (SynchronizationProvider provider :
+ DirectoryServer.getSynchronizationProviders())
+ {
+ try
+ {
+ SynchronizationProviderResult result =
+ provider.handleConflictResolution(this);
+ if (! result.continueOperationProcessing())
+ {
+ break deleteProcessing;
+ }
+ }
+ catch (DirectoryException de)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, de);
+ }
+
+ logError(ERR_DELETE_SYNCH_CONFLICT_RESOLUTION_FAILED.get(
+ getConnectionID(), getOperationID(),
+ getExceptionMessage(de)));
+ setResponseData(de);
+ break deleteProcessing;
+ }
+ }
+
+ // Check to see if the client has permission to perform the
+ // delete.
+
+ // Check to see if there are any controls in the request. If so, then
+ // see if there is any special processing required.
+ try
+ {
+ handleRequestControls();
+ }
+ catch (DirectoryException de)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, de);
+ }
+
+ setResponseData(de);
+ break deleteProcessing;
+ }
+
+
+ // FIXME: for now assume that this will check all permission
+ // pertinent to the operation. This includes proxy authorization
+ // and any other controls specified.
+
+ // FIXME: earlier checks to see if the entry already exists may
+ // have already exposed sensitive information to the client.
+ if (! AccessControlConfigManager.getInstance().
+ getAccessControlHandler().isAllowed(this))
+ {
+ setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS);
+ appendErrorMessage(ERR_DELETE_AUTHZ_INSUFFICIENT_ACCESS_RIGHTS.get(
+ String.valueOf(entryDN)));
+ skipPostOperation = true;
+ break deleteProcessing;
+ }
+
+ // Check for a request to cancel this operation.
+ if (getCancelRequest() != null)
+ {
+ return;
+ }
+
+
+ // If the operation is not a synchronization operation,
+ // invoke the pre-delete plugins.
+ if (! isSynchronizationOperation())
+ {
+ PreOperationPluginResult preOpResult =
+ pluginConfigManager.invokePreOperationDeletePlugins(this);
+ if (preOpResult.connectionTerminated())
+ {
+ // There's no point in continuing with anything. Log the request
+ // and result and return.
+ setResultCode(ResultCode.CANCELED);
+ appendErrorMessage(ERR_CANCELED_BY_PREOP_DISCONNECT.get());
+ setProcessingStopTime();
+ return;
+ }
+ else if (preOpResult.sendResponseImmediately() ||
+ preOpResult.skipCoreProcessing())
+ {
+ skipPostOperation = true;
+ break deleteProcessing;
+ }
+ }
+
+
+ // Check for a request to cancel this operation.
+ if (getCancelRequest() != null)
+ {
+ return;
+ }
+
+
+ // Get the backend to use for the delete. If there is none, then fail.
+ if (backend == null)
+ {
+ setResultCode(ResultCode.NO_SUCH_OBJECT);
+ appendErrorMessage(ERR_DELETE_NO_SUCH_ENTRY.get(
+ String.valueOf(entryDN)));
+ break deleteProcessing;
+ }
+
+
+ // If it is not a private backend, then check to see if the server or
+ // backend is operating in read-only mode.
+ if (! backend.isPrivateBackend())
+ {
+ switch (DirectoryServer.getWritabilityMode())
+ {
+ case DISABLED:
+ setResultCode(ResultCode.UNWILLING_TO_PERFORM);
+ appendErrorMessage(ERR_DELETE_SERVER_READONLY.get(
+ String.valueOf(entryDN)));
+ break deleteProcessing;
+
+ case INTERNAL_ONLY:
+ if (! (isInternalOperation() || isSynchronizationOperation()))
+ {
+ setResultCode(ResultCode.UNWILLING_TO_PERFORM);
+ appendErrorMessage(ERR_DELETE_SERVER_READONLY.get(
+ String.valueOf(entryDN)));
+ break deleteProcessing;
+ }
+ }
+
+ switch (backend.getWritabilityMode())
+ {
+ case DISABLED:
+ setResultCode(ResultCode.UNWILLING_TO_PERFORM);
+ appendErrorMessage(ERR_DELETE_BACKEND_READONLY.get(
+ String.valueOf(entryDN)));
+ break deleteProcessing;
+
+ case INTERNAL_ONLY:
+ if (! (isInternalOperation() || isSynchronizationOperation()))
+ {
+ setResultCode(ResultCode.UNWILLING_TO_PERFORM);
+ appendErrorMessage(ERR_DELETE_BACKEND_READONLY.get(
+ String.valueOf(entryDN)));
+ break deleteProcessing;
+ }
+ }
+ }
+
+
+ // The selected backend will have the responsibility of making sure that
+ // the entry actually exists and does not have any children (or possibly
+ // handling a subtree delete). But we will need to check if there are
+ // any subordinate backends that should stop us from attempting the
+ // delete.
+ Backend[] subBackends = backend.getSubordinateBackends();
+ for (Backend b : subBackends)
+ {
+ DN[] baseDNs = b.getBaseDNs();
+ for (DN dn : baseDNs)
+ {
+ if (dn.isDescendantOf(entryDN))
+ {
+ setResultCode(ResultCode.NOT_ALLOWED_ON_NONLEAF);
+ appendErrorMessage(ERR_DELETE_HAS_SUB_BACKEND.get(
+ String.valueOf(entryDN),
+ String.valueOf(dn)));
+ break deleteProcessing;
+ }
+ }
+ }
+
+
+ // Actually perform the delete.
+ try
+ {
+ if (noOp)
+ {
+ setResultCode(ResultCode.NO_OPERATION);
+ appendErrorMessage(INFO_DELETE_NOOP.get());
+ }
+ else
+ {
+ for (SynchronizationProvider provider :
+ DirectoryServer.getSynchronizationProviders())
+ {
+ try
+ {
+ SynchronizationProviderResult result =
+ provider.doPreOperation(this);
+ if (! result.continueOperationProcessing())
+ {
+ break deleteProcessing;
+ }
+ }
+ catch (DirectoryException de)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, de);
+ }
+
+ logError(ERR_DELETE_SYNCH_PREOP_FAILED.get(getConnectionID(),
+ getOperationID(), getExceptionMessage(de)));
+ setResponseData(de);
+ break deleteProcessing;
+ }
+ }
+
+ backend.deleteEntry(entryDN, this);
+ }
+
+
+ processPreReadControl();
+
+
+ if (! noOp)
+ {
+ setResultCode(ResultCode.SUCCESS);
+ }
+ }
+ catch (DirectoryException de)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, de);
+ }
+
+ setResponseData(de);
+ break deleteProcessing;
+ }
+ catch (CancelledOperationException coe)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, coe);
+ }
+
+ CancelResult cancelResult = coe.getCancelResult();
+
+ setCancelResult(cancelResult);
+ setResultCode(cancelResult.getResultCode());
+
+ Message message = coe.getMessageObject();
+ if ((message != null) && (message.length() > 0))
+ {
+ appendErrorMessage(message);
+ }
+ break deleteProcessing;
+ }
+ }
+ finally
+ {
+ LockManager.unlock(entryDN, entryLock);
+
+ for (SynchronizationProvider provider :
+ DirectoryServer.getSynchronizationProviders())
+ {
+ try
+ {
+ provider.doPostOperation(this);
+ }
+ catch (DirectoryException de)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, de);
+ }
+
+ logError(ERR_DELETE_SYNCH_POSTOP_FAILED.get(getConnectionID(),
+ getOperationID(), getExceptionMessage(de)));
+ setResponseData(de);
+ break;
+ }
+ }
+ }
+ }
+
+
+ // Indicate that it is now too late to attempt to cancel the operation.
+ setCancelResult(CancelResult.TOO_LATE);
+
+
+ // Invoke the post-operation or post-synchronization delete plugins.
+ if (isSynchronizationOperation())
+ {
+ if (getResultCode() == ResultCode.SUCCESS)
+ {
+ pluginConfigManager.invokePostSynchronizationDeletePlugins(this);
+ }
+ }
+ else if (! skipPostOperation)
+ {
+ PostOperationPluginResult postOperationResult =
+ pluginConfigManager.invokePostOperationDeletePlugins(this);
+ if (postOperationResult.connectionTerminated())
+ {
+ setResultCode(ResultCode.CANCELED);
+ appendErrorMessage(ERR_CANCELED_BY_POSTOP_DISCONNECT.get());
+ setProcessingStopTime();
+ return;
+ }
+ }
+
+
+ // Notify any change notification listeners that might be registered with
+ // the server.
+ if (getResultCode() == ResultCode.SUCCESS)
+ {
+ for (ChangeNotificationListener changeListener :
+ DirectoryServer.getChangeNotificationListeners())
+ {
+ try
+ {
+ changeListener.handleDeleteOperation(this, entry);
+ }
+ catch (Exception e)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ }
+
+ Message message = ERR_DELETE_ERROR_NOTIFYING_CHANGE_LISTENER.get(
+ getExceptionMessage(e));
+ logError(message);
+ }
+ }
+ }
+
+ // Stop the processing timer.
+ setProcessingStopTime();
}
+
+
+ /**
+ * Performs any request control processing needed for this operation.
+ *
+ * @throws DirectoryException If a problem occurs that should cause the
+ * operation to fail.
+ */
+ private void handleRequestControls()
+ throws DirectoryException
+ {
+ List<Control> requestControls = getRequestControls();
+ if ((requestControls != null) && (! requestControls.isEmpty()))
+ {
+ for (int i=0; i < requestControls.size(); i++)
+ {
+ Control c = requestControls.get(i);
+ String oid = c.getOID();
+
+ if (!AccessControlConfigManager.getInstance().
+ getAccessControlHandler().isAllowed(entryDN, this, c))
+ {
+ skipPostOperation = true;
+ throw new DirectoryException(ResultCode.INSUFFICIENT_ACCESS_RIGHTS,
+ ERR_CONTROL_INSUFFICIENT_ACCESS_RIGHTS.get(oid));
+ }
+
+ if (oid.equals(OID_LDAP_ASSERTION))
+ {
+ LDAPAssertionRequestControl assertControl;
+ if (c instanceof LDAPAssertionRequestControl)
+ {
+ assertControl = (LDAPAssertionRequestControl) c;
+ }
+ else
+ {
+ try
+ {
+ assertControl = LDAPAssertionRequestControl.decodeControl(c);
+ requestControls.set(i, assertControl);
+ }
+ catch (LDAPException le)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, le);
+ }
+
+ throw new DirectoryException(
+ ResultCode.valueOf(le.getResultCode()),
+ le.getMessageObject());
+ }
+ }
+
+ try
+ {
+ // FIXME -- We need to determine whether the current user has
+ // permission to make this determination.
+ SearchFilter filter = assertControl.getSearchFilter();
+ if (! filter.matchesEntry(entry))
+ {
+ throw new DirectoryException(ResultCode.ASSERTION_FAILED,
+ ERR_DELETE_ASSERTION_FAILED.get(
+ String.valueOf(entryDN)));
+ }
+ }
+ catch (DirectoryException de)
+ {
+ if (de.getResultCode() == ResultCode.ASSERTION_FAILED)
+ {
+ throw de;
+ }
+
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, de);
+ }
+
+ throw new DirectoryException(ResultCode.PROTOCOL_ERROR,
+ ERR_DELETE_CANNOT_PROCESS_ASSERTION_FILTER.get(
+ String.valueOf(entryDN),
+ de.getMessageObject()));
+ }
+ }
+ else if (oid.equals(OID_LDAP_NOOP_OPENLDAP_ASSIGNED))
+ {
+ noOp = true;
+ }
+ else if (oid.equals(OID_LDAP_READENTRY_PREREAD))
+ {
+ if (c instanceof LDAPPreReadRequestControl)
+ {
+ preReadRequest = (LDAPPreReadRequestControl) c;
+ }
+ else
+ {
+ try
+ {
+ preReadRequest = LDAPPreReadRequestControl.decodeControl(c);
+ requestControls.set(i, preReadRequest);
+ }
+ catch (LDAPException le)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, le);
+ }
+
+ throw new DirectoryException(
+ ResultCode.valueOf(le.getResultCode()),
+ le.getMessageObject());
+ }
+ }
+ }
+ else if (oid.equals(OID_PROXIED_AUTH_V1))
+ {
+ // The requester must have the PROXIED_AUTH privilige in order to
+ // be able to use this control.
+ if (! clientConnection.hasPrivilege(Privilege.PROXIED_AUTH, this))
+ {
+ throw new DirectoryException(ResultCode.AUTHORIZATION_DENIED,
+ ERR_PROXYAUTH_INSUFFICIENT_PRIVILEGES.get());
+ }
+
+
+ ProxiedAuthV1Control proxyControl;
+ if (c instanceof ProxiedAuthV1Control)
+ {
+ proxyControl = (ProxiedAuthV1Control) c;
+ }
+ else
+ {
+ try
+ {
+ proxyControl = ProxiedAuthV1Control.decodeControl(c);
+ }
+ catch (LDAPException le)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, le);
+ }
+
+ throw new DirectoryException(
+ ResultCode.valueOf(le.getResultCode()),
+ le.getMessageObject());
+ }
+ }
+
+
+ Entry authorizationEntry = proxyControl.getAuthorizationEntry();
+ setAuthorizationEntry(authorizationEntry);
+ if (authorizationEntry == null)
+ {
+ setProxiedAuthorizationDN(DN.nullDN());
+ }
+ else
+ {
+ setProxiedAuthorizationDN(authorizationEntry.getDN());
+ }
+ }
+ else if (oid.equals(OID_PROXIED_AUTH_V2))
+ {
+ // The requester must have the PROXIED_AUTH privilige in order to
+ // be able to use this control.
+ if (! clientConnection.hasPrivilege(Privilege.PROXIED_AUTH, this))
+ {
+ throw new DirectoryException(ResultCode.AUTHORIZATION_DENIED,
+ ERR_PROXYAUTH_INSUFFICIENT_PRIVILEGES.get());
+ }
+
+
+ ProxiedAuthV2Control proxyControl;
+ if (c instanceof ProxiedAuthV2Control)
+ {
+ proxyControl = (ProxiedAuthV2Control) c;
+ }
+ else
+ {
+ try
+ {
+ proxyControl = ProxiedAuthV2Control.decodeControl(c);
+ }
+ catch (LDAPException le)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, le);
+ }
+
+ throw new DirectoryException(
+ ResultCode.valueOf(le.getResultCode()),
+ le.getMessageObject());
+ }
+ }
+
+
+ Entry authorizationEntry = proxyControl.getAuthorizationEntry();
+ setAuthorizationEntry(authorizationEntry);
+ if (authorizationEntry == null)
+ {
+ setProxiedAuthorizationDN(DN.nullDN());
+ }
+ else
+ {
+ setProxiedAuthorizationDN(authorizationEntry.getDN());
+ }
+ }
+
+ // NYI -- Add support for additional controls.
+
+ else if (c.isCritical())
+ {
+ if ((backend == null) || (! backend.supportsControl(oid)))
+ {
+ throw new DirectoryException(
+ ResultCode.UNAVAILABLE_CRITICAL_EXTENSION,
+ ERR_DELETE_UNSUPPORTED_CRITICAL_CONTROL.get(
+ String.valueOf(entryDN), oid));
+ }
+ }
+ }
+ }
+ }
+
+
+
+ /**
+ * Performs any processing needed for the LDAP pre-read control.
+ */
+ private void processPreReadControl()
+ {
+ if (preReadRequest != null)
+ {
+ Entry entryCopy = entry.duplicate(true);
+
+ if (! preReadRequest.allowsAttribute(
+ DirectoryServer.getObjectClassAttributeType()))
+ {
+ entryCopy.removeAttribute(
+ DirectoryServer.getObjectClassAttributeType());
+ }
+
+ if (! preReadRequest.returnAllUserAttributes())
+ {
+ Iterator<AttributeType> iterator =
+ entryCopy.getUserAttributes().keySet().iterator();
+ while (iterator.hasNext())
+ {
+ AttributeType attrType = iterator.next();
+ if (! preReadRequest.allowsAttribute(attrType))
+ {
+ iterator.remove();
+ }
+ }
+ }
+
+ if (! preReadRequest.returnAllOperationalAttributes())
+ {
+ Iterator<AttributeType> iterator =
+ entryCopy.getOperationalAttributes().keySet().iterator();
+ while (iterator.hasNext())
+ {
+ AttributeType attrType = iterator.next();
+ if (! preReadRequest.allowsAttribute(attrType))
+ {
+ iterator.remove();
+ }
+ }
+ }
+
+ // FIXME -- Check access controls on the entry to see if it should
+ // be returned or if any attributes need to be stripped
+ // out..
+ SearchResultEntry searchEntry = new SearchResultEntry(entryCopy);
+ LDAPPreReadResponseControl responseControl =
+ new LDAPPreReadResponseControl(preReadRequest.getOID(),
+ preReadRequest.isCritical(),
+ searchEntry);
+ addResponseControl(responseControl);
+ }
+ }
}
+
diff --git a/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendModifyDNOperation.java b/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendModifyDNOperation.java
index b1e1b32..8e4ec8d 100644
--- a/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendModifyDNOperation.java
+++ b/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendModifyDNOperation.java
@@ -26,14 +26,71 @@
*/
package org.opends.server.workflowelement.localbackend;
+
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.locks.Lock;
+
+import org.opends.messages.Message;
+import org.opends.messages.MessageBuilder;
+import org.opends.server.api.Backend;
+import org.opends.server.api.ChangeNotificationListener;
+import org.opends.server.api.ClientConnection;
+import org.opends.server.api.SynchronizationProvider;
+import org.opends.server.api.plugin.PostOperationPluginResult;
+import org.opends.server.api.plugin.PreOperationPluginResult;
+import org.opends.server.controls.LDAPAssertionRequestControl;
+import org.opends.server.controls.LDAPPostReadRequestControl;
+import org.opends.server.controls.LDAPPostReadResponseControl;
+import org.opends.server.controls.LDAPPreReadRequestControl;
+import org.opends.server.controls.LDAPPreReadResponseControl;
+import org.opends.server.controls.ProxiedAuthV1Control;
+import org.opends.server.controls.ProxiedAuthV2Control;
+import org.opends.server.core.AccessControlConfigManager;
+import org.opends.server.core.DirectoryServer;
import org.opends.server.core.ModifyDNOperation;
import org.opends.server.core.ModifyDNOperationWrapper;
+import org.opends.server.core.PluginConfigManager;
+import org.opends.server.loggers.debug.DebugTracer;
+import org.opends.server.protocols.asn1.ASN1OctetString;
+import org.opends.server.types.Attribute;
+import org.opends.server.types.AttributeType;
+import org.opends.server.types.AttributeValue;
+import org.opends.server.types.ByteString;
+import org.opends.server.types.CancelledOperationException;
+import org.opends.server.types.CancelResult;
+import org.opends.server.types.Control;
+import org.opends.server.types.DebugLogLevel;
+import org.opends.server.types.DirectoryException;
+import org.opends.server.types.DN;
import org.opends.server.types.Entry;
+import org.opends.server.types.LDAPException;
+import org.opends.server.types.LockManager;
+import org.opends.server.types.Modification;
+import org.opends.server.types.ModificationType;
+import org.opends.server.types.Privilege;
+import org.opends.server.types.RDN;
+import org.opends.server.types.ResultCode;
+import org.opends.server.types.SearchFilter;
+import org.opends.server.types.SearchResultEntry;
+import org.opends.server.types.SynchronizationProviderResult;
import org.opends.server.types.operation.PostOperationModifyDNOperation;
import org.opends.server.types.operation.PostResponseModifyDNOperation;
import org.opends.server.types.operation.PreOperationModifyDNOperation;
import org.opends.server.types.operation.PostSynchronizationModifyDNOperation;
+import static org.opends.messages.CoreMessages.*;
+import static org.opends.server.loggers.ErrorLogger.*;
+import static org.opends.server.loggers.debug.DebugLogger.*;
+import static org.opends.server.util.ServerConstants.*;
+import static org.opends.server.util.StaticUtils.*;
+
+
+
/**
* This class defines an operation used to move an entry in a local backend
* of the Directory Server.
@@ -45,12 +102,45 @@
PostResponseModifyDNOperation,
PostSynchronizationModifyDNOperation
{
+ /**
+ * The tracer object for the debug logger.
+ */
+ private static final DebugTracer TRACER = getTracer();
+
+
+
+ // The backend in which the operation is to be processed.
+ private Backend backend;
+
+ // Indicates whether the no-op control was included in the request.
+ private boolean noOp;
+
+ // Indicates whether to skip post-operation plugin processing.
+ private boolean skipPostOperation;
+
+ // The client connection on which this operation was requested.
+ private ClientConnection clientConnection;
+
+ // The original DN of the entry.
+ DN entryDN;
+
// The current entry, before it is renamed.
private Entry currentEntry;
// The new entry, as it will appear after it has been renamed.
private Entry newEntry;
+ // The LDAP post-read request control, if present in the request.
+ private LDAPPostReadRequestControl postReadRequest;
+
+ // The LDAP pre-read request control, if present in the request.
+ private LDAPPreReadRequestControl preReadRequest;
+
+ // The new RDN for the entry.
+ private RDN newRDN;
+
+
+
/**
* Creates a new operation that may be used to move an entry in a
* local backend of the Directory Server.
@@ -63,6 +153,8 @@
LocalBackendWorkflowElement.attachLocalOperation (operation, this);
}
+
+
/**
* Retrieves the current entry, before it is renamed. This will not be
* available to pre-parse plugins or during the conflict resolution portion of
@@ -76,6 +168,8 @@
return currentEntry;
}
+
+
/**
* Retrieves the new entry, as it will appear after it is renamed. This will
* not be available to pre-parse plugins or during the conflict resolution
@@ -89,31 +183,1246 @@
return newEntry;
}
- /**
- * Sets the current entry, before it is renamed. This will not be
- * available to pre-parse plugins or during the conflict resolution portion of
- * the synchronization processing.
- *
- * @param entry The current entry, or <CODE>null</CODE> if it is not yet
- * available.
- */
- public final void setOriginalEntry(Entry entry)
- {
- this.currentEntry = entry;
- }
+
/**
- * Sets the new entry, as it will appear after it is renamed. This will
- * not be available to pre-parse plugins or during the conflict resolution
- * portion of the synchronization processing.
+ * Process this modify DN operation in a local backend.
*
- * @param entry The updated entry, or <CODE>null</CODE> if it is not yet
- * available.
+ * @param backend The backend in which the modify DN operation should be
+ * processed.
*/
- public final void setUpdatedEntry(Entry entry)
+ void processLocalModifyDN(Backend backend)
{
- this.newEntry = entry;
+ this.backend = backend;
+
+ clientConnection = getClientConnection();
+
+ // Get the plugin config manager that will be used for invoking plugins.
+ PluginConfigManager pluginConfigManager =
+ DirectoryServer.getPluginConfigManager();
+ skipPostOperation = false;
+
+ // Check for a request to cancel this operation.
+ if (getCancelRequest() != null)
+ {
+ return;
+ }
+
+ // Create a labeled block of code that we can break out of if a problem is
+ // detected.
+modifyDNProcessing:
+ {
+ // Process the entry DN, newRDN, and newSuperior elements from their raw
+ // forms as provided by the client to the forms required for the rest of
+ // the modify DN processing.
+ entryDN = getEntryDN();
+
+ newRDN = getNewRDN();
+ if (newRDN == null)
+ {
+ skipPostOperation = true;
+ break modifyDNProcessing;
+ }
+
+ DN newSuperior = getNewSuperior();
+ if ((newSuperior == null) &&
+ (getRawNewSuperior() != null))
+ {
+ skipPostOperation = true;
+ break modifyDNProcessing;
+ }
+
+ // Construct the new DN to use for the entry.
+ DN parentDN;
+ if (newSuperior == null)
+ {
+ parentDN = entryDN.getParentDNInSuffix();
+ }
+ else
+ {
+ parentDN = newSuperior;
+ }
+
+ if ((parentDN == null) || parentDN.isNullDN())
+ {
+ setResultCode(ResultCode.UNWILLING_TO_PERFORM);
+ appendErrorMessage(ERR_MODDN_NO_PARENT.get(String.valueOf(entryDN)));
+ break modifyDNProcessing;
+ }
+
+ DN newDN = parentDN.concat(newRDN);
+
+ // Get the backend for the current entry, and the backend for the new
+ // entry. If either is null, or if they are different, then fail.
+ Backend currentBackend = backend;
+ if (currentBackend == null)
+ {
+ setResultCode(ResultCode.NO_SUCH_OBJECT);
+ appendErrorMessage(ERR_MODDN_NO_BACKEND_FOR_CURRENT_ENTRY.get(
+ String.valueOf(entryDN)));
+ break modifyDNProcessing;
+ }
+
+ Backend newBackend = DirectoryServer.getBackend(newDN);
+ if (newBackend == null)
+ {
+ setResultCode(ResultCode.NO_SUCH_OBJECT);
+ appendErrorMessage(ERR_MODDN_NO_BACKEND_FOR_NEW_ENTRY.get(
+ String.valueOf(entryDN),
+ String.valueOf(newDN)));
+ break modifyDNProcessing;
+ }
+ else if (! currentBackend.equals(newBackend))
+ {
+ setResultCode(ResultCode.UNWILLING_TO_PERFORM);
+ appendErrorMessage(ERR_MODDN_DIFFERENT_BACKENDS.get(
+ String.valueOf(entryDN),
+ String.valueOf(newDN)));
+ break modifyDNProcessing;
+ }
+
+
+ // Check for a request to cancel this operation.
+ if (getCancelRequest() != null)
+ {
+ return;
+ }
+
+
+ // Acquire write locks for the current and new DN.
+ Lock currentLock = null;
+ for (int i=0; i < 3; i++)
+ {
+ currentLock = LockManager.lockWrite(entryDN);
+ if (currentLock != null)
+ {
+ break;
+ }
+ }
+
+ if (currentLock == null)
+ {
+ setResultCode(DirectoryServer.getServerErrorResultCode());
+ appendErrorMessage(ERR_MODDN_CANNOT_LOCK_CURRENT_DN.get(
+ String.valueOf(entryDN)));
+ skipPostOperation = true;
+ break modifyDNProcessing;
+ }
+
+ Lock newLock = null;
+ try
+ {
+ for (int i=0; i < 3; i++)
+ {
+ newLock = LockManager.lockWrite(newDN);
+ if (newLock != null)
+ {
+ break;
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ }
+
+ LockManager.unlock(entryDN, currentLock);
+
+ if (newLock != null)
+ {
+ LockManager.unlock(newDN, newLock);
+ }
+
+ setResultCode(DirectoryServer.getServerErrorResultCode());
+ appendErrorMessage(ERR_MODDN_EXCEPTION_LOCKING_NEW_DN.get(
+ String.valueOf(entryDN), String.valueOf(newDN),
+ getExceptionMessage(e)));
+
+ skipPostOperation = true;
+ break modifyDNProcessing;
+ }
+
+ if (newLock == null)
+ {
+ LockManager.unlock(entryDN, currentLock);
+
+ setResultCode(DirectoryServer.getServerErrorResultCode());
+ appendErrorMessage(ERR_MODDN_CANNOT_LOCK_NEW_DN.get(
+ String.valueOf(entryDN),
+ String.valueOf(newDN)));
+ skipPostOperation = true;
+ break modifyDNProcessing;
+ }
+
+ try
+ {
+ // Check for a request to cancel this operation.
+ if (getCancelRequest() != null)
+ {
+ return;
+ }
+
+
+ // Get the current entry from the appropriate backend. If it doesn't
+ // exist, then fail.
+ try
+ {
+ currentEntry = currentBackend.getEntry(entryDN);
+ }
+ catch (DirectoryException de)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, de);
+ }
+
+ setResponseData(de);
+ break modifyDNProcessing;
+ }
+
+ if (getOriginalEntry() == null)
+ {
+ // See if one of the entry's ancestors exists.
+ parentDN = entryDN.getParentDNInSuffix();
+ while (parentDN != null)
+ {
+ try
+ {
+ if (DirectoryServer.entryExists(parentDN))
+ {
+ setMatchedDN(parentDN);
+ break;
+ }
+ }
+ catch (Exception e)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ }
+ break;
+ }
+
+ parentDN = parentDN.getParentDNInSuffix();
+ }
+
+ setResultCode(ResultCode.NO_SUCH_OBJECT);
+ appendErrorMessage(ERR_MODDN_NO_CURRENT_ENTRY.get(
+ String.valueOf(entryDN)));
+ break modifyDNProcessing;
+ }
+
+
+ // Invoke any conflict resolution processing that might be needed by the
+ // synchronization provider.
+ for (SynchronizationProvider provider :
+ DirectoryServer.getSynchronizationProviders())
+ {
+ try
+ {
+ SynchronizationProviderResult result =
+ provider.handleConflictResolution(this);
+ if (! result.continueOperationProcessing())
+ {
+ break modifyDNProcessing;
+ }
+ }
+ catch (DirectoryException de)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, de);
+ }
+
+ logError(ERR_MODDN_SYNCH_CONFLICT_RESOLUTION_FAILED.get(
+ getConnectionID(), getOperationID(),
+ getExceptionMessage(de)));
+
+ setResponseData(de);
+ break modifyDNProcessing;
+ }
+ }
+
+
+ // Check to see if there are any controls in the request. If so, then
+ // see if there is any special processing required.
+ try
+ {
+ handleRequestControls();
+ }
+ catch (DirectoryException de)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, de);
+ }
+
+ setResponseData(de);
+ break modifyDNProcessing;
+ }
+
+
+ // Check to see if the client has permission to perform the
+ // modify DN.
+
+ // FIXME: for now assume that this will check all permission
+ // pertinent to the operation. This includes proxy authorization
+ // and any other controls specified.
+
+ // FIXME: earlier checks to see if the entry or new superior
+ // already exists may have already exposed sensitive information
+ // to the client.
+ if (! AccessControlConfigManager.getInstance().
+ getAccessControlHandler().isAllowed(this))
+ {
+ setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS);
+ appendErrorMessage(ERR_MODDN_AUTHZ_INSUFFICIENT_ACCESS_RIGHTS.get(
+ String.valueOf(entryDN)));
+ skipPostOperation = true;
+ break modifyDNProcessing;
+ }
+
+ // Duplicate the entry and set its new DN. Also, create an empty list
+ // to hold the attribute-level modifications.
+ newEntry = currentEntry.duplicate(false);
+ newEntry.setDN(newDN);
+
+ // init the modifications
+ addModification(null);
+ List<Modification> modifications = this.getModifications();
+
+
+
+ // Apply any changes to the entry based on the change in its RDN. Also,
+ // perform schema checking on the updated entry.
+ try
+ {
+ applyRDNChanges(modifications);
+ }
+ catch (DirectoryException de)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, de);
+ }
+
+ setResponseData(de);
+ break modifyDNProcessing;
+ }
+
+
+ // Check for a request to cancel this operation.
+ if (getCancelRequest() != null)
+ {
+ return;
+ }
+
+
+ // Get a count of the current number of modifications. The
+ // pre-operation plugins may alter this list, and we need to be able to
+ // identify which changes were made after they're done.
+ int modCount = modifications.size();
+
+
+ // If the operation is not a synchronization operation,
+ // Invoke the pre-operation modify DN plugins.
+ if (! isSynchronizationOperation())
+ {
+ PreOperationPluginResult preOpResult =
+ pluginConfigManager.invokePreOperationModifyDNPlugins(this);
+ if (preOpResult.connectionTerminated())
+ {
+ // There's no point in continuing with anything. Log the request
+ // and result and return.
+ setResultCode(ResultCode.CANCELED);
+ appendErrorMessage(ERR_CANCELED_BY_PREOP_DISCONNECT.get());
+ return;
+ }
+ else if (preOpResult.sendResponseImmediately())
+ {
+ skipPostOperation = true;
+ break modifyDNProcessing;
+ }
+ else if (preOpResult.skipCoreProcessing())
+ {
+ skipPostOperation = false;
+ break modifyDNProcessing;
+ }
+ }
+
+
+ // Check to see if any of the pre-operation plugins made any changes to
+ // the entry. If so, then apply them.
+ if (modifications.size() > modCount)
+ {
+ try
+ {
+ applyPreOpModifications(modifications, modCount);
+ }
+ catch (DirectoryException de)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, de);
+ }
+
+ setResponseData(de);
+ break modifyDNProcessing;
+ }
+ }
+
+
+ // Check for a request to cancel this operation.
+ if (getCancelRequest() != null)
+ {
+ return;
+ }
+
+
+ // Actually perform the modify DN operation.
+ // This should include taking
+ // care of any synchronization that might be needed.
+ try
+ {
+ // If it is not a private backend, then check to see if the server or
+ // backend is operating in read-only mode.
+ if (! currentBackend.isPrivateBackend())
+ {
+ switch (DirectoryServer.getWritabilityMode())
+ {
+ case DISABLED:
+ setResultCode(ResultCode.UNWILLING_TO_PERFORM);
+ appendErrorMessage(ERR_MODDN_SERVER_READONLY.get(
+ String.valueOf(entryDN)));
+ break modifyDNProcessing;
+
+ case INTERNAL_ONLY:
+ if (! (isInternalOperation() || isSynchronizationOperation()))
+ {
+ setResultCode(ResultCode.UNWILLING_TO_PERFORM);
+ appendErrorMessage(ERR_MODDN_SERVER_READONLY.get(
+ String.valueOf(entryDN)));
+ break modifyDNProcessing;
+ }
+ }
+
+ switch (currentBackend.getWritabilityMode())
+ {
+ case DISABLED:
+ setResultCode(ResultCode.UNWILLING_TO_PERFORM);
+ appendErrorMessage(ERR_MODDN_BACKEND_READONLY.get(
+ String.valueOf(entryDN)));
+ break modifyDNProcessing;
+
+ case INTERNAL_ONLY:
+ if (! (isInternalOperation() || isSynchronizationOperation()))
+ {
+ setResultCode(ResultCode.UNWILLING_TO_PERFORM);
+ appendErrorMessage(ERR_MODDN_BACKEND_READONLY.get(
+ String.valueOf(entryDN)));
+ break modifyDNProcessing;
+ }
+ }
+ }
+
+
+ if (noOp)
+ {
+ appendErrorMessage(INFO_MODDN_NOOP.get());
+ setResultCode(ResultCode.NO_OPERATION);
+ }
+ else
+ {
+ for (SynchronizationProvider provider :
+ DirectoryServer.getSynchronizationProviders())
+ {
+ try
+ {
+ SynchronizationProviderResult result =
+ provider.doPreOperation(this);
+ if (! result.continueOperationProcessing())
+ {
+ break modifyDNProcessing;
+ }
+ }
+ catch (DirectoryException de)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, de);
+ }
+
+ logError(ERR_MODDN_SYNCH_PREOP_FAILED.get(getConnectionID(),
+ getOperationID(), getExceptionMessage(de)));
+ setResponseData(de);
+ break modifyDNProcessing;
+ }
+ }
+
+ currentBackend.renameEntry(entryDN, newEntry, this);
+ }
+
+
+ // Attach the pre-read and/or post-read controls to the response if
+ // appropriate.
+ processReadEntryControls();
+
+
+ if (! noOp)
+ {
+ setResultCode(ResultCode.SUCCESS);
+ }
+ }
+ catch (DirectoryException de)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, de);
+ }
+
+ setResponseData(de);
+ break modifyDNProcessing;
+ }
+ catch (CancelledOperationException coe)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, coe);
+ }
+
+ CancelResult cancelResult = coe.getCancelResult();
+
+ setCancelResult(cancelResult);
+ setResultCode(cancelResult.getResultCode());
+
+ Message message = coe.getMessageObject();
+ if ((message != null) && (message.length() > 0))
+ {
+ appendErrorMessage(message);
+ }
+
+ break modifyDNProcessing;
+ }
+ }
+ finally
+ {
+ LockManager.unlock(entryDN, currentLock);
+ LockManager.unlock(newDN, newLock);
+
+ for (SynchronizationProvider provider :
+ DirectoryServer.getSynchronizationProviders())
+ {
+ try
+ {
+ provider.doPostOperation(this);
+ }
+ catch (DirectoryException de)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, de);
+ }
+
+ logError(ERR_MODDN_SYNCH_POSTOP_FAILED.get(getConnectionID(),
+ getOperationID(), getExceptionMessage(de)));
+ setResponseData(de);
+ break;
+ }
+ }
+ }
+ }
+
+
+ // Indicate that it is now too late to attempt to cancel the operation.
+ setCancelResult(CancelResult.TOO_LATE);
+
+
+ // Invoke the post-operation or post-synchronization modify DN plugins.
+ if (isSynchronizationOperation())
+ {
+ if (getResultCode() == ResultCode.SUCCESS)
+ {
+ pluginConfigManager.invokePostSynchronizationModifyDNPlugins(this);
+ }
+ }
+ else if (! skipPostOperation)
+ {
+ PostOperationPluginResult postOperationResult =
+ pluginConfigManager.invokePostOperationModifyDNPlugins(this);
+ if (postOperationResult.connectionTerminated())
+ {
+ setResultCode(ResultCode.CANCELED);
+ appendErrorMessage(ERR_CANCELED_BY_POSTOP_DISCONNECT.get());
+ return;
+ }
+ }
+
+
+ // Notify any change notification listeners that might be registered with
+ // the server.
+ if (getResultCode() == ResultCode.SUCCESS)
+ {
+ for (ChangeNotificationListener changeListener :
+ DirectoryServer.getChangeNotificationListeners())
+ {
+ try
+ {
+ changeListener.handleModifyDNOperation(this, currentEntry, newEntry);
+ }
+ catch (Exception e)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ }
+
+ Message message = ERR_MODDN_ERROR_NOTIFYING_CHANGE_LISTENER.get(
+ getExceptionMessage(e));
+ logError(message);
+ }
+ }
+ }
}
+
+ /**
+ * Processes the set of controls included in the request.
+ *
+ * @throws DirectoryException If a problem occurs that should cause the
+ * modify DN operation to fail.
+ */
+ private void handleRequestControls()
+ throws DirectoryException
+ {
+ List<Control> requestControls = getRequestControls();
+ if ((requestControls != null) && (! requestControls.isEmpty()))
+ {
+ for (int i=0; i < requestControls.size(); i++)
+ {
+ Control c = requestControls.get(i);
+ String oid = c.getOID();
+
+ if (! AccessControlConfigManager.getInstance().
+ getAccessControlHandler().isAllowed(entryDN, this, c))
+ {
+ skipPostOperation = true;
+ throw new DirectoryException(ResultCode.INSUFFICIENT_ACCESS_RIGHTS,
+ ERR_CONTROL_INSUFFICIENT_ACCESS_RIGHTS.get(oid));
+ }
+
+ if (oid.equals(OID_LDAP_ASSERTION))
+ {
+ LDAPAssertionRequestControl assertControl;
+ if (c instanceof LDAPAssertionRequestControl)
+ {
+ assertControl = (LDAPAssertionRequestControl) c;
+ }
+ else
+ {
+ try
+ {
+ assertControl = LDAPAssertionRequestControl.decodeControl(c);
+ requestControls.set(i, assertControl);
+ }
+ catch (LDAPException le)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, le);
+ }
+
+ throw new DirectoryException(
+ ResultCode.valueOf(le.getResultCode()),
+ le.getMessageObject());
+ }
+ }
+
+ try
+ {
+ // FIXME -- We need to determine whether the current user has
+ // permission to make this determination.
+ SearchFilter filter = assertControl.getSearchFilter();
+ if (! filter.matchesEntry(currentEntry))
+ {
+ throw new DirectoryException(ResultCode.ASSERTION_FAILED,
+ ERR_MODDN_ASSERTION_FAILED.get(
+ String.valueOf(entryDN)));
+ }
+ }
+ catch (DirectoryException de)
+ {
+ if (de.getResultCode() == ResultCode.ASSERTION_FAILED)
+ {
+ throw de;
+ }
+
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, de);
+ }
+
+ throw new DirectoryException(ResultCode.PROTOCOL_ERROR,
+ ERR_MODDN_CANNOT_PROCESS_ASSERTION_FILTER.get(
+ String.valueOf(entryDN),
+ de.getMessageObject()));
+ }
+ }
+ else if (oid.equals(OID_LDAP_NOOP_OPENLDAP_ASSIGNED))
+ {
+ noOp = true;
+ }
+ else if (oid.equals(OID_LDAP_READENTRY_PREREAD))
+ {
+ if (c instanceof LDAPPreReadRequestControl)
+ {
+ preReadRequest = (LDAPPreReadRequestControl) c;
+ }
+ else
+ {
+ try
+ {
+ preReadRequest = LDAPPreReadRequestControl.decodeControl(c);
+ requestControls.set(i, preReadRequest);
+ }
+ catch (LDAPException le)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, le);
+ }
+
+ throw new DirectoryException(
+ ResultCode.valueOf(le.getResultCode()),
+ le.getMessageObject());
+ }
+ }
+ }
+ else if (oid.equals(OID_LDAP_READENTRY_POSTREAD))
+ {
+ if (c instanceof LDAPPostReadRequestControl)
+ {
+ postReadRequest = (LDAPPostReadRequestControl) c;
+ }
+ else
+ {
+ try
+ {
+ postReadRequest = LDAPPostReadRequestControl.decodeControl(c);
+ requestControls.set(i, postReadRequest);
+ }
+ catch (LDAPException le)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, le);
+ }
+
+ throw new DirectoryException(
+ ResultCode.valueOf(le.getResultCode()),
+ le.getMessageObject());
+ }
+ }
+ }
+ else if (oid.equals(OID_PROXIED_AUTH_V1))
+ {
+ // The requester must have the PROXIED_AUTH privilige in order to
+ // be able to use this control.
+ if (! clientConnection.hasPrivilege(Privilege.PROXIED_AUTH, this))
+ {
+ throw new DirectoryException(ResultCode.AUTHORIZATION_DENIED,
+ ERR_PROXYAUTH_INSUFFICIENT_PRIVILEGES.get());
+ }
+
+
+ ProxiedAuthV1Control proxyControl;
+ if (c instanceof ProxiedAuthV1Control)
+ {
+ proxyControl = (ProxiedAuthV1Control) c;
+ }
+ else
+ {
+ try
+ {
+ proxyControl = ProxiedAuthV1Control.decodeControl(c);
+ }
+ catch (LDAPException le)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, le);
+ }
+
+ throw new DirectoryException(
+ ResultCode.valueOf(le.getResultCode()),
+ le.getMessageObject());
+ }
+ }
+
+
+ Entry authorizationEntry = proxyControl.getAuthorizationEntry();
+ setAuthorizationEntry(authorizationEntry);
+ if (authorizationEntry == null)
+ {
+ setProxiedAuthorizationDN(DN.nullDN());
+ }
+ else
+ {
+ setProxiedAuthorizationDN(authorizationEntry.getDN());
+ }
+ }
+ else if (oid.equals(OID_PROXIED_AUTH_V2))
+ {
+ // The requester must have the PROXIED_AUTH privilige in order to
+ // be able to use this control.
+ if (! clientConnection.hasPrivilege(Privilege.PROXIED_AUTH, this))
+ {
+ throw new DirectoryException(ResultCode.AUTHORIZATION_DENIED,
+ ERR_PROXYAUTH_INSUFFICIENT_PRIVILEGES.get());
+ }
+
+
+ ProxiedAuthV2Control proxyControl;
+ if (c instanceof ProxiedAuthV2Control)
+ {
+ proxyControl = (ProxiedAuthV2Control) c;
+ }
+ else
+ {
+ try
+ {
+ proxyControl = ProxiedAuthV2Control.decodeControl(c);
+ }
+ catch (LDAPException le)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, le);
+ }
+
+ throw new DirectoryException(
+ ResultCode.valueOf(le.getResultCode()),
+ le.getMessageObject());
+ }
+ }
+
+
+ Entry authorizationEntry = proxyControl.getAuthorizationEntry();
+ setAuthorizationEntry(authorizationEntry);
+ if (authorizationEntry == null)
+ {
+ setProxiedAuthorizationDN(DN.nullDN());
+ }
+ else
+ {
+ setProxiedAuthorizationDN(authorizationEntry.getDN());
+ }
+ }
+
+ // NYI -- Add support for additional controls.
+
+ else if (c.isCritical())
+ {
+ if ((backend == null) || (! backend.supportsControl(oid)))
+ {
+ throw new DirectoryException(
+ ResultCode.UNAVAILABLE_CRITICAL_EXTENSION,
+ ERR_MODDN_UNSUPPORTED_CRITICAL_CONTROL.get(
+ String.valueOf(entryDN), oid));
+ }
+ }
+ }
+ }
+ }
+
+
+
+ /**
+ * Updates the entry so that its attributes are changed to reflect the changes
+ * to the RDN. This also performs schema checking on the updated entry.
+ *
+ * @param modifications A list to hold the modifications made to the entry.
+ *
+ * @throws DirectoryException If a problem occurs that should cause the
+ * modify DN operation to fail.
+ */
+ private void applyRDNChanges(List<Modification> modifications)
+ throws DirectoryException
+ {
+ // If we should delete the old RDN values from the entry, then do so.
+ if (deleteOldRDN())
+ {
+ RDN currentRDN = entryDN.getRDN();
+ int numValues = currentRDN.getNumValues();
+ for (int i=0; i < numValues; i++)
+ {
+ LinkedHashSet<AttributeValue> valueSet =
+ new LinkedHashSet<AttributeValue>(1);
+ valueSet.add(currentRDN.getAttributeValue(i));
+
+ Attribute a = new Attribute(currentRDN.getAttributeType(i),
+ currentRDN.getAttributeName(i), valueSet);
+
+ // If the associated attribute type is marked NO-USER-MODIFICATION, then
+ // refuse the update.
+ if (a.getAttributeType().isNoUserModification())
+ {
+ if (! (isInternalOperation() || isSynchronizationOperation()))
+ {
+ throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
+ ERR_MODDN_OLD_RDN_ATTR_IS_NO_USER_MOD.get(
+ String.valueOf(entryDN), a.getName()));
+ }
+ }
+
+ LinkedList<AttributeValue> missingValues =
+ new LinkedList<AttributeValue>();
+ newEntry.removeAttribute(a, missingValues);
+
+ if (missingValues.isEmpty())
+ {
+ modifications.add(new Modification(ModificationType.DELETE, a));
+ }
+ }
+ }
+
+
+ // Add the new RDN values to the entry.
+ int newRDNValues = newRDN.getNumValues();
+ for (int i=0; i < newRDNValues; i++)
+ {
+ LinkedHashSet<AttributeValue> valueSet =
+ new LinkedHashSet<AttributeValue>(1);
+ valueSet.add(newRDN.getAttributeValue(i));
+
+ Attribute a = new Attribute(newRDN.getAttributeType(i),
+ newRDN.getAttributeName(i), valueSet);
+
+ LinkedList<AttributeValue> duplicateValues =
+ new LinkedList<AttributeValue>();
+ newEntry.addAttribute(a, duplicateValues);
+
+ if (duplicateValues.isEmpty())
+ {
+ // If the associated attribute type is marked NO-USER-MODIFICATION, then
+ // refuse the update.
+ if (a.getAttributeType().isNoUserModification())
+ {
+ if (! (isInternalOperation() || isSynchronizationOperation()))
+ {
+ throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
+ ERR_MODDN_NEW_RDN_ATTR_IS_NO_USER_MOD.get(
+ String.valueOf(entryDN), a.getName()));
+ }
+ }
+ else
+ {
+ modifications.add(new Modification(ModificationType.ADD, a));
+ }
+ }
+ }
+
+ // If the server is configured to check the schema and the operation is not
+ // a synchronization operation, make sure that the resulting entry is valid
+ // as per the server schema.
+ if ((DirectoryServer.checkSchema()) && (! isSynchronizationOperation()))
+ {
+ MessageBuilder invalidReason = new MessageBuilder();
+ if (! newEntry.conformsToSchema(null, false, true, true,
+ invalidReason))
+ {
+ throw new DirectoryException(ResultCode.OBJECTCLASS_VIOLATION,
+ ERR_MODDN_VIOLATES_SCHEMA.get(
+ String.valueOf(entryDN),
+ String.valueOf(invalidReason)));
+ }
+
+ for (int i=0; i < newRDNValues; i++)
+ {
+ AttributeType at = newRDN.getAttributeType(i);
+ if (at.isObsolete())
+ {
+ throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
+ ERR_MODDN_NEWRDN_ATTR_IS_OBSOLETE.get(
+ String.valueOf(entryDN),
+ at.getNameOrOID()));
+ }
+ }
+ }
+ }
+
+
+
+ /**
+ * Applies any modifications performed during pre-operation plugin processing.
+ * This also performs schema checking for the updated entry.
+ *
+ * @param modifications A list containing the modifications made to the
+ * entry.
+ * @param startPos The position in the list at which the pre-operation
+ * modifications start.
+ *
+ * @throws DirectoryException If a problem occurs that should cause the
+ * modify DN operation to fail.
+ */
+ private void applyPreOpModifications(List<Modification> modifications,
+ int startPos)
+ throws DirectoryException
+ {
+ for (int i=startPos; i < modifications.size(); i++)
+ {
+ Modification m = modifications.get(i);
+ Attribute a = m.getAttribute();
+
+ switch (m.getModificationType())
+ {
+ case ADD:
+ LinkedList<AttributeValue> duplicateValues =
+ new LinkedList<AttributeValue>();
+ newEntry.addAttribute(a, duplicateValues);
+ break;
+
+ case DELETE:
+ LinkedList<AttributeValue> missingValues =
+ new LinkedList<AttributeValue>();
+ newEntry.removeAttribute(a, missingValues);
+ break;
+
+ case REPLACE:
+ duplicateValues = new LinkedList<AttributeValue>();
+ newEntry.removeAttribute(a.getAttributeType(), a.getOptions());
+ newEntry.addAttribute(a, duplicateValues);
+ break;
+
+ case INCREMENT:
+ List<Attribute> attrList =
+ newEntry.getAttribute(a.getAttributeType(),
+ a.getOptions());
+ if ((attrList == null) || attrList.isEmpty())
+ {
+ throw new DirectoryException(ResultCode.NO_SUCH_ATTRIBUTE,
+ ERR_MODDN_PREOP_INCREMENT_NO_ATTR.get(
+ String.valueOf(entryDN),
+ a.getName()));
+ }
+ else if (attrList.size() > 1)
+ {
+ throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION,
+ ERR_MODDN_PREOP_INCREMENT_MULTIPLE_VALUES.get(
+ String.valueOf(entryDN), a.getName()));
+ }
+
+ LinkedHashSet<AttributeValue> values =
+ attrList.get(0).getValues();
+ if ((values == null) || values.isEmpty())
+ {
+ throw new DirectoryException(ResultCode.NO_SUCH_ATTRIBUTE,
+ ERR_MODDN_PREOP_INCREMENT_NO_ATTR.get(
+ String.valueOf(entryDN),
+ a.getName()));
+ }
+ else if (values.size() > 1)
+ {
+ throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION,
+ ERR_MODDN_PREOP_INCREMENT_MULTIPLE_VALUES.get(
+ String.valueOf(entryDN), a.getName()));
+ }
+
+ long currentLongValue;
+ try
+ {
+ AttributeValue v = values.iterator().next();
+ currentLongValue = Long.parseLong(v.getStringValue());
+ }
+ catch (Exception e)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ }
+
+ throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION,
+ ERR_MODDN_PREOP_INCREMENT_VALUE_NOT_INTEGER.get(
+ String.valueOf(entryDN), a.getName()));
+ }
+
+ LinkedHashSet<AttributeValue> newValues = a.getValues();
+ if ((newValues == null) || newValues.isEmpty())
+ {
+ throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION,
+ ERR_MODDN_PREOP_INCREMENT_NO_AMOUNT.get(
+ String.valueOf(entryDN), a.getName()));
+ }
+ else if (newValues.size() > 1)
+ {
+ throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION,
+ ERR_MODDN_PREOP_INCREMENT_MULTIPLE_AMOUNTS.get(
+ String.valueOf(entryDN), a.getName()));
+ }
+
+ long incrementAmount;
+ try
+ {
+ AttributeValue v = values.iterator().next();
+ incrementAmount = Long.parseLong(v.getStringValue());
+ }
+ catch (Exception e)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ }
+
+ throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION,
+ ERR_MODDN_PREOP_INCREMENT_AMOUNT_NOT_INTEGER.get(
+ String.valueOf(entryDN), a.getName()));
+ }
+
+ long newLongValue = currentLongValue + incrementAmount;
+ ByteString newValueOS =
+ new ASN1OctetString(String.valueOf(newLongValue));
+
+ newValues = new LinkedHashSet<AttributeValue>(1);
+ newValues.add(new AttributeValue(a.getAttributeType(),
+ newValueOS));
+
+ List<Attribute> newAttrList = new ArrayList<Attribute>(1);
+ newAttrList.add(new Attribute(a.getAttributeType(),
+ a.getName(), newValues));
+ newEntry.putAttribute(a.getAttributeType(), newAttrList);
+
+ break;
+ }
+ }
+
+
+ // Make sure that the updated entry still conforms to the server
+ // schema.
+ if (DirectoryServer.checkSchema())
+ {
+ MessageBuilder invalidReason = new MessageBuilder();
+ if (! newEntry.conformsToSchema(null, false, true, true,
+ invalidReason))
+ {
+ throw new DirectoryException(ResultCode.OBJECTCLASS_VIOLATION,
+ ERR_MODDN_PREOP_VIOLATES_SCHEMA.get(
+ String.valueOf(entryDN),
+ String.valueOf(invalidReason)));
+ }
+ }
+ }
+
+
+
+ /**
+ * Performs any necessary processing to create the pre-read and/or post-read
+ * response controls and attach them to the response.
+ */
+ private void processReadEntryControls()
+ {
+ if (preReadRequest != null)
+ {
+ Entry entry = currentEntry.duplicate(true);
+
+ if (! preReadRequest.allowsAttribute(
+ DirectoryServer.getObjectClassAttributeType()))
+ {
+ entry.removeAttribute(
+ DirectoryServer.getObjectClassAttributeType());
+ }
+
+ if (! preReadRequest.returnAllUserAttributes())
+ {
+ Iterator<AttributeType> iterator =
+ entry.getUserAttributes().keySet().iterator();
+ while (iterator.hasNext())
+ {
+ AttributeType attrType = iterator.next();
+ if (! preReadRequest.allowsAttribute(attrType))
+ {
+ iterator.remove();
+ }
+ }
+ }
+
+ if (! preReadRequest.returnAllOperationalAttributes())
+ {
+ Iterator<AttributeType> iterator =
+ entry.getOperationalAttributes().keySet().iterator();
+ while (iterator.hasNext())
+ {
+ AttributeType attrType = iterator.next();
+ if (! preReadRequest.allowsAttribute(attrType))
+ {
+ iterator.remove();
+ }
+ }
+ }
+
+ // FIXME -- Check access controls on the entry to see if it should
+ // be returned or if any attributes need to be stripped
+ // out..
+ SearchResultEntry searchEntry = new SearchResultEntry(entry);
+ LDAPPreReadResponseControl responseControl =
+ new LDAPPreReadResponseControl(preReadRequest.getOID(),
+ preReadRequest.isCritical(),
+ searchEntry);
+
+ addResponseControl(responseControl);
+ }
+
+ if (postReadRequest != null)
+ {
+ Entry entry = newEntry.duplicate(true);
+
+ if (! postReadRequest.allowsAttribute(
+ DirectoryServer.getObjectClassAttributeType()))
+ {
+ entry.removeAttribute(
+ DirectoryServer.getObjectClassAttributeType());
+ }
+
+ if (! postReadRequest.returnAllUserAttributes())
+ {
+ Iterator<AttributeType> iterator =
+ entry.getUserAttributes().keySet().iterator();
+ while (iterator.hasNext())
+ {
+ AttributeType attrType = iterator.next();
+ if (! postReadRequest.allowsAttribute(attrType))
+ {
+ iterator.remove();
+ }
+ }
+ }
+
+ if (! postReadRequest.returnAllOperationalAttributes())
+ {
+ Iterator<AttributeType> iterator =
+ entry.getOperationalAttributes().keySet().iterator();
+ while (iterator.hasNext())
+ {
+ AttributeType attrType = iterator.next();
+ if (! postReadRequest.allowsAttribute(attrType))
+ {
+ iterator.remove();
+ }
+ }
+ }
+
+ // FIXME -- Check access controls on the entry to see if it should
+ // be returned or if any attributes need to be stripped
+ // out..
+ SearchResultEntry searchEntry = new SearchResultEntry(entry);
+ LDAPPostReadResponseControl responseControl =
+ new LDAPPostReadResponseControl(postReadRequest.getOID(),
+ postReadRequest.isCritical(),
+ searchEntry);
+
+ addResponseControl(responseControl);
+ }
+ }
}
+
diff --git a/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendModifyOperation.java b/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendModifyOperation.java
index 2339824..4caefd9 100644
--- a/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendModifyOperation.java
+++ b/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendModifyOperation.java
@@ -26,41 +26,172 @@
*/
package org.opends.server.workflowelement.localbackend;
-import java.util.List;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.locks.Lock;
+
+import org.opends.messages.Message;
+import org.opends.messages.MessageBuilder;
+import org.opends.server.api.AttributeSyntax;
+import org.opends.server.api.Backend;
+import org.opends.server.api.ChangeNotificationListener;
+import org.opends.server.api.ClientConnection;
+import org.opends.server.api.PasswordStorageScheme;
+import org.opends.server.api.SynchronizationProvider;
+import org.opends.server.api.plugin.PostOperationPluginResult;
+import org.opends.server.api.plugin.PreOperationPluginResult;
+import org.opends.server.controls.LDAPAssertionRequestControl;
+import org.opends.server.controls.LDAPPostReadRequestControl;
+import org.opends.server.controls.LDAPPostReadResponseControl;
+import org.opends.server.controls.LDAPPreReadRequestControl;
+import org.opends.server.controls.LDAPPreReadResponseControl;
+import org.opends.server.controls.PasswordPolicyErrorType;
+import org.opends.server.controls.PasswordPolicyResponseControl;
+import org.opends.server.controls.ProxiedAuthV1Control;
+import org.opends.server.controls.ProxiedAuthV2Control;
+import org.opends.server.core.AccessControlConfigManager;
+import org.opends.server.core.DirectoryServer;
import org.opends.server.core.ModifyOperation;
import org.opends.server.core.ModifyOperationWrapper;
+import org.opends.server.core.PasswordPolicyState;
+import org.opends.server.core.PluginConfigManager;
+import org.opends.server.loggers.debug.DebugTracer;
+import org.opends.server.protocols.asn1.ASN1OctetString;
+import org.opends.server.schema.AuthPasswordSyntax;
+import org.opends.server.schema.BooleanSyntax;
+import org.opends.server.schema.UserPasswordSyntax;
+import org.opends.server.types.AccountStatusNotification;
+import org.opends.server.types.AccountStatusNotificationType;
+import org.opends.server.types.AcceptRejectWarn;
+import org.opends.server.types.Attribute;
+import org.opends.server.types.AttributeType;
import org.opends.server.types.AttributeValue;
+import org.opends.server.types.AuthenticationInfo;
+import org.opends.server.types.ByteString;
+import org.opends.server.types.CancelledOperationException;
+import org.opends.server.types.CancelResult;
+import org.opends.server.types.Control;
+import org.opends.server.types.DebugLogLevel;
import org.opends.server.types.DirectoryException;
+import org.opends.server.types.DN;
import org.opends.server.types.Entry;
+import org.opends.server.types.LDAPException;
+import org.opends.server.types.LockManager;
import org.opends.server.types.Modification;
+import org.opends.server.types.ModificationType;
+import org.opends.server.types.Privilege;
+import org.opends.server.types.RDN;
+import org.opends.server.types.ResultCode;
+import org.opends.server.types.SearchFilter;
+import org.opends.server.types.SearchResultEntry;
+import org.opends.server.types.SynchronizationProviderResult;
import org.opends.server.types.operation.PostOperationModifyOperation;
import org.opends.server.types.operation.PostResponseModifyOperation;
import org.opends.server.types.operation.PreOperationModifyOperation;
import org.opends.server.types.operation.PostSynchronizationModifyOperation;
+import static org.opends.messages.CoreMessages.*;
+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.util.ServerConstants.*;
+import static org.opends.server.util.StaticUtils.*;
+
+
+
/**
* This class defines an operation used to modify an entry in a local backend
* of the Directory Server.
*/
-public class LocalBackendModifyOperation extends ModifyOperationWrapper
- implements PreOperationModifyOperation,
- PostOperationModifyOperation,
- PostResponseModifyOperation,
- PostSynchronizationModifyOperation
+public class LocalBackendModifyOperation
+ extends ModifyOperationWrapper
+ implements PreOperationModifyOperation, PostOperationModifyOperation,
+ PostResponseModifyOperation,
+ PostSynchronizationModifyOperation
{
+ /**
+ * The tracer object for the debug logger.
+ */
+ private static final DebugTracer TRACER = getTracer();
+
+
+
+ // The backend in which the target entry exists.
+ private Backend backend;
+
+ // Indicates whether the request included the user's current password.
+ private boolean currentPasswordProvided;
+
+ // Indicates whether the user's account has been enabled or disabled by this
+ // modify operation.
+ private boolean enabledStateChanged;
+
+ // Indicates whether the user's account is currently enabled.
+ private boolean isEnabled;
+
+ // Indicates whether the request included the LDAP no-op control.
+ private boolean noOp;
+
+ // Indicates whether this modify operation includees a password change.
+ private boolean passwordChanged;
+
+ // Indicates whether the request included the password policy request control.
+ private boolean pwPolicyControlRequested;
+
+ // Indicates whether the password change is a self-change.
+ private boolean selfChange;
+
+ // Indicates whether to skip post-operation plugin processing.
+ private boolean skipPostOperation;
+
+ // Indicates whether the user's account was locked before this change.
+ private boolean wasLocked;
+
+ // The client connection associated with this operation.
+ private ClientConnection clientConnection;
+
+ // The DN of the entry to modify.
+ private DN entryDN;
+
// The current entry, before any changes are applied.
private Entry currentEntry = null;
// The modified entry that will be stored in the backend.
private Entry modifiedEntry = null;
+ // The number of passwords contained in the modify operation.
+ private int numPasswords;
+
+ // The post-read request control, if present.
+ private LDAPPostReadRequestControl postReadRequest;
+
+ // The pre-read request control, if present.
+ private LDAPPreReadRequestControl preReadRequest;
+
// The set of clear-text current passwords (if any were provided).
private List<AttributeValue> currentPasswords = null;
// The set of clear-text new passwords (if any were provided).
private List<AttributeValue> newPasswords = null;
+ // The set of modifications contained in this request.
+ private List<Modification> modifications;
+
+ // The password policy error type for this operation.
+ private PasswordPolicyErrorType pwpErrorType;
+
+ // The password policy state for this modify operation.
+ private PasswordPolicyState pwPolicyState;
+
+
+
/**
* Creates a new operation that may be used to modify an entry in a
* local backend of the Directory Server.
@@ -73,6 +204,8 @@
LocalBackendWorkflowElement.attachLocalOperation (modify, this);
}
+
+
/**
* Retrieves the current entry before any modifications are applied. This
* will not be available to pre-parse plugins.
@@ -85,6 +218,8 @@
return currentEntry;
}
+
+
/**
* Retrieves the set of clear-text current passwords for the user, if
* available. This will only be available if the modify operation contains
@@ -101,6 +236,8 @@
return currentPasswords;
}
+
+
/**
* Retrieves the modified entry that is to be written to the backend. This
* will be available to pre-operation plugins, and if such a plugin does make
@@ -115,6 +252,8 @@
return modifiedEntry;
}
+
+
/**
* Retrieves the set of clear-text new passwords for the user, if available.
* This will only be available if the modify operation contains one or more
@@ -130,57 +269,6 @@
return newPasswords;
}
- /**
- * Retrieves the current entry before any modifications are applied. This
- * will not be available to pre-parse plugins.
- *
- * @param currentEntry The current entry.
- */
- public final void setCurrentEntry(Entry currentEntry)
- {
- this.currentEntry = currentEntry;
- }
-
- /**
- * Register the set of clear-text current passwords for the user, if
- * available. This will only be available if the modify operation contains
- * one or more delete elements that target the password attribute and provide
- * the values to delete in the clear.
- *
- * @param currentPasswords The set of clear-text current password values as
- * provided in the modify request.
- */
- public final void setCurrentPasswords(List<AttributeValue> currentPasswords)
- {
- this.currentPasswords = currentPasswords;
- }
-
- /**
- * Register the modified entry that is to be written to the backend.
- *
- * @param modifiedEntry The modified entry that is to be written to the
- * backend, or <CODE>null</CODE> if it is not yet
- * available.
- */
- public final void setModifiedEntry(Entry modifiedEntry)
- {
- this.modifiedEntry = modifiedEntry;
- }
-
- /**
- * Register the set of clear-text new passwords for the user, if available.
- * This will only be available if the modify operation contains one or more
- * add or replace elements that target the password attribute and provide the
- * values in the clear.
- *
- * @param newPasswords The set of clear-text new passwords as provided in
- * the modify request, or <CODE>null</CODE> if there
- * were none or this information is not yet available.
- */
- public final void setNewPasswords(List<AttributeValue> newPasswords)
- {
- this.newPasswords = newPasswords;
- }
/**
@@ -202,4 +290,2123 @@
modifiedEntry.applyModification(modification);
super.addModification(modification);
}
+
+
+
+ /**
+ * Process this modify operation against a local backend.
+ *
+ * @param backend The backend in which the modify operation should be
+ * performed.
+ */
+ void processLocalModify(Backend backend)
+ {
+ this.backend = backend;
+
+ clientConnection = getClientConnection();
+
+ // Get the plugin config manager that will be used for invoking plugins.
+ PluginConfigManager pluginConfigManager =
+ DirectoryServer.getPluginConfigManager();
+ skipPostOperation = false;
+
+ // Create a labeled block of code that we can break out of if a problem is
+ // detected.
+modifyProcessing:
+ {
+ entryDN = getEntryDN();
+ if (entryDN == null){
+ break modifyProcessing;
+ }
+
+ // Process the modifications to convert them from their raw form to the
+ // form required for the rest of the modify processing.
+ modifications = getModifications();
+ if (modifications == null)
+ {
+ break modifyProcessing;
+ }
+
+ if (modifications.isEmpty())
+ {
+ setResultCode(ResultCode.CONSTRAINT_VIOLATION);
+ appendErrorMessage(ERR_MODIFY_NO_MODIFICATIONS.get(
+ String.valueOf(entryDN)));
+ break modifyProcessing;
+ }
+
+
+ // If the user must change their password before doing anything else, and
+ // if the target of the modify operation isn't the user's own entry, then
+ // reject the request.
+ if ((! isInternalOperation()) && clientConnection.mustChangePassword())
+ {
+ DN authzDN = getAuthorizationDN();
+ if ((authzDN != null) && (! authzDN.equals(entryDN)))
+ {
+ // The user will not be allowed to do anything else before the
+ // password gets changed. Also note that we haven't yet checked the
+ // request controls so we need to do that now to see if the password
+ // policy request control was provided.
+ for (Control c : getRequestControls())
+ {
+ if (c.getOID().equals(OID_PASSWORD_POLICY_CONTROL))
+ {
+ pwPolicyControlRequested = true;
+ pwpErrorType = PasswordPolicyErrorType.CHANGE_AFTER_RESET;
+ break;
+ }
+ }
+
+ setResultCode(ResultCode.UNWILLING_TO_PERFORM);
+ appendErrorMessage(ERR_MODIFY_MUST_CHANGE_PASSWORD.get());
+ break modifyProcessing;
+ }
+ }
+
+
+ // Check for a request to cancel this operation.
+ if (getCancelRequest() != null)
+ {
+ return;
+ }
+
+ // Acquire a write lock on the target entry.
+ Lock entryLock = null;
+ for (int i=0; i < 3; i++)
+ {
+ entryLock = LockManager.lockWrite(entryDN);
+ if (entryLock != null)
+ {
+ break;
+ }
+ }
+
+ if (entryLock == null)
+ {
+ setResultCode(DirectoryServer.getServerErrorResultCode());
+ appendErrorMessage(ERR_MODIFY_CANNOT_LOCK_ENTRY.get(
+ String.valueOf(entryDN)));
+ skipPostOperation = true;
+ break modifyProcessing;
+ }
+
+
+ try
+ {
+ // Check for a request to cancel this operation.
+ if (getCancelRequest() != null)
+ {
+ return;
+ }
+
+
+ // Get the entry to modify. If it does not exist, then fail.
+ try
+ {
+ currentEntry = backend.getEntry(entryDN);
+ }
+ catch (DirectoryException de)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, de);
+ }
+
+ setResponseData(de);
+ break modifyProcessing;
+ }
+
+ if (currentEntry == null)
+ {
+ setResultCode(ResultCode.NO_SUCH_OBJECT);
+ appendErrorMessage(ERR_MODIFY_NO_SUCH_ENTRY.get(
+ String.valueOf(entryDN)));
+
+ // See if one of the entry's ancestors exists.
+ DN parentDN = entryDN.getParentDNInSuffix();
+ while (parentDN != null)
+ {
+ try
+ {
+ if (DirectoryServer.entryExists(parentDN))
+ {
+ setMatchedDN(parentDN);
+ break;
+ }
+ }
+ catch (Exception e)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ }
+ break;
+ }
+
+ parentDN = parentDN.getParentDNInSuffix();
+ }
+
+ break modifyProcessing;
+ }
+
+
+ // Check to see if there are any controls in the request. If so, then
+ // see if there is any special processing required.
+ try
+ {
+ processRequestControls();
+ }
+ catch (DirectoryException de)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, de);
+ }
+
+ setResponseData(de);
+ break modifyProcessing;
+ }
+
+
+ // Get the password policy state object for the entry that can be used
+ // to perform any appropriate password policy processing. Also, see if
+ // the entry is being updated by the end user or an administrator.
+ selfChange = entryDN.equals(getAuthorizationDN());
+ try
+ {
+ // FIXME -- Need a way to enable debug mode.
+ pwPolicyState = new PasswordPolicyState(currentEntry, false, false);
+ }
+ catch (DirectoryException de)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, de);
+ }
+
+ setResultCode(de.getResultCode());
+ appendErrorMessage(de.getMessageObject());
+ break modifyProcessing;
+ }
+
+
+ // Create a duplicate of the entry and apply the changes to it.
+ modifiedEntry = currentEntry.duplicate(false);
+
+ if (! noOp)
+ {
+ // Invoke any conflict resolution processing that might be needed by
+ // the synchronization provider.
+ for (SynchronizationProvider provider :
+ DirectoryServer.getSynchronizationProviders())
+ {
+ try
+ {
+ SynchronizationProviderResult result =
+ provider.handleConflictResolution(this);
+ if (! result.continueOperationProcessing())
+ {
+ break modifyProcessing;
+ }
+ }
+ catch (DirectoryException de)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, de);
+ }
+
+ logError(ERR_MODIFY_SYNCH_CONFLICT_RESOLUTION_FAILED.get(
+ getConnectionID(), getOperationID(),
+ getExceptionMessage(de)));
+ setResponseData(de);
+ break modifyProcessing;
+ }
+ }
+ }
+
+ try
+ {
+ handleInitialPasswordPolicyAndSchemaProcessing();
+ }
+ catch (DirectoryException de)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, de);
+ }
+
+ setResponseData(de);
+ break modifyProcessing;
+ }
+
+
+ // If there was a password change, then perform any additional checks
+ // that may be necessary.
+ wasLocked = false;
+ if (passwordChanged)
+ {
+ try
+ {
+ performAdditionalPasswordChangedProcessing();
+ }
+ catch (DirectoryException de)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, de);
+ }
+
+ setResponseData(de);
+ break modifyProcessing;
+ }
+ }
+
+
+ // Check to see if the client has permission to perform the modify.
+ // The access control check is not made any earlier because the handler
+ // needs access to the modified entry.
+
+ // FIXME: for now assume that this will check all permissions
+ // pertinent to the operation. This includes proxy authorization
+ // and any other controls specified.
+
+ // FIXME: earlier checks to see if the entry already exists may have
+ // already exposed sensitive information to the client.
+ if (! AccessControlConfigManager.getInstance().
+ getAccessControlHandler().isAllowed(this))
+ {
+ setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS);
+ appendErrorMessage(ERR_MODIFY_AUTHZ_INSUFFICIENT_ACCESS_RIGHTS.get(
+ String.valueOf(entryDN)));
+ skipPostOperation = true;
+ break modifyProcessing;
+ }
+
+ if ((! passwordChanged) && (! isInternalOperation()) &&
+ pwPolicyState.mustChangePassword())
+ {
+ // The user will not be allowed to do anything else before the
+ // password gets changed.
+ pwpErrorType = PasswordPolicyErrorType.CHANGE_AFTER_RESET;
+ setResultCode(ResultCode.UNWILLING_TO_PERFORM);
+ appendErrorMessage(ERR_MODIFY_MUST_CHANGE_PASSWORD.get());
+ break modifyProcessing;
+ }
+
+
+ // If the server is configured to check the schema and the
+ // operation is not a sycnhronization operation,
+ // make sure that the new entry is valid per the server schema.
+ if ((DirectoryServer.checkSchema()) && (! isSynchronizationOperation()))
+ {
+ MessageBuilder invalidReason = new MessageBuilder();
+ if (! modifiedEntry.conformsToSchema(null, false, false, false,
+ invalidReason))
+ {
+ setResultCode(ResultCode.OBJECTCLASS_VIOLATION);
+ appendErrorMessage(ERR_MODIFY_VIOLATES_SCHEMA.get(
+ String.valueOf(entryDN), invalidReason));
+ break modifyProcessing;
+ }
+ }
+
+
+ // Check for a request to cancel this operation.
+ if (getCancelRequest() != null)
+ {
+ return;
+ }
+
+ // If the operation is not a synchronization operation,
+ // Invoke the pre-operation modify plugins.
+ if (! isSynchronizationOperation())
+ {
+ PreOperationPluginResult preOpResult =
+ pluginConfigManager.invokePreOperationModifyPlugins(this);
+ if (preOpResult.connectionTerminated())
+ {
+ // There's no point in continuing with anything. Log the result
+ // and return.
+ setResultCode(ResultCode.CANCELED);
+ appendErrorMessage(ERR_CANCELED_BY_PREOP_DISCONNECT.get());
+ setProcessingStopTime();
+ return;
+ }
+ else if (preOpResult.sendResponseImmediately())
+ {
+ skipPostOperation = true;
+ break modifyProcessing;
+ }
+ else if (preOpResult.skipCoreProcessing())
+ {
+ skipPostOperation = false;
+ break modifyProcessing;
+ }
+ }
+
+
+ // Check for a request to cancel this operation.
+ if (getCancelRequest() != null)
+ {
+ return;
+ }
+
+
+ // Actually perform the modify operation. This should also include
+ // taking care of any synchronization that might be needed.
+ if (backend == null)
+ {
+ setResultCode(ResultCode.NO_SUCH_OBJECT);
+ appendErrorMessage(ERR_MODIFY_NO_BACKEND_FOR_ENTRY.get(
+ String.valueOf(entryDN)));
+ break modifyProcessing;
+ }
+
+ try
+ {
+ try
+ {
+ checkWritability();
+ }
+ catch (DirectoryException de)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, de);
+ }
+
+ setResponseData(de);
+ break modifyProcessing;
+ }
+
+
+ if (noOp)
+ {
+ appendErrorMessage(INFO_MODIFY_NOOP.get());
+ setResultCode(ResultCode.NO_OPERATION);
+ }
+ else
+ {
+ for (SynchronizationProvider provider :
+ DirectoryServer.getSynchronizationProviders())
+ {
+ try
+ {
+ SynchronizationProviderResult result =
+ provider.doPreOperation(this);
+ if (! result.continueOperationProcessing())
+ {
+ break modifyProcessing;
+ }
+ }
+ catch (DirectoryException de)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, de);
+ }
+
+ logError(ERR_MODIFY_SYNCH_PREOP_FAILED.get(getConnectionID(),
+ getOperationID(), getExceptionMessage(de)));
+ setResponseData(de);
+ break modifyProcessing;
+ }
+ }
+
+ backend.replaceEntry(modifiedEntry, this);
+
+
+
+ // See if we need to generate any account status notifications as a
+ // result of the changes.
+ if (passwordChanged || enabledStateChanged || wasLocked)
+ {
+ handleAccountStatusNotifications();
+ }
+ }
+
+
+ // Handle any processing that may be needed for the pre-read and/or
+ // post-read controls.
+ handleReadEntryProcessing();
+
+
+ if (! noOp)
+ {
+ setResultCode(ResultCode.SUCCESS);
+ }
+ }
+ catch (DirectoryException de)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, de);
+ }
+
+ setResponseData(de);
+ break modifyProcessing;
+ }
+ catch (CancelledOperationException coe)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, coe);
+ }
+
+ CancelResult cancelResult = coe.getCancelResult();
+
+ setCancelResult(cancelResult);
+ setResultCode(cancelResult.getResultCode());
+
+ Message message = coe.getMessageObject();
+ if ((message != null) && (message.length() > 0))
+ {
+ appendErrorMessage(message);
+ }
+ break modifyProcessing;
+ }
+ }
+ finally
+ {
+ LockManager.unlock(entryDN, entryLock);
+
+ for (SynchronizationProvider provider :
+ DirectoryServer.getSynchronizationProviders())
+ {
+ try
+ {
+ provider.doPostOperation(this);
+ }
+ catch (DirectoryException de)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, de);
+ }
+
+ logError(ERR_MODIFY_SYNCH_POSTOP_FAILED.get(getConnectionID(),
+ getOperationID(), getExceptionMessage(de)));
+ setResponseData(de);
+ break;
+ }
+ }
+ }
+ }
+
+
+ // If the password policy request control was included, then make sure we
+ // send the corresponding response control.
+ if (pwPolicyControlRequested)
+ {
+ addResponseControl(new PasswordPolicyResponseControl(null, 0,
+ pwpErrorType));
+ }
+
+
+ // Indicate that it is now too late to attempt to cancel the operation.
+ setCancelResult(CancelResult.TOO_LATE);
+
+ // Invoke the post-operation or post-synchronization modify plugins.
+ if (isSynchronizationOperation())
+ {
+ if (getResultCode() == ResultCode.SUCCESS)
+ {
+ pluginConfigManager.invokePostSynchronizationModifyPlugins(this);
+ }
+ }
+ else if (! skipPostOperation)
+ {
+ // FIXME -- Should this also be done while holding the locks?
+ PostOperationPluginResult postOpResult =
+ pluginConfigManager.invokePostOperationModifyPlugins(this);
+ if (postOpResult.connectionTerminated())
+ {
+ // There's no point in continuing with anything. Log the result and
+ // return.
+ setResultCode(ResultCode.CANCELED);
+ appendErrorMessage(ERR_CANCELED_BY_PREOP_DISCONNECT.get());
+ setProcessingStopTime();
+ return;
+ }
+ }
+
+ // Notify any change notification listeners that might be registered with
+ // the server.
+ if (getResultCode() == ResultCode.SUCCESS)
+ {
+ for (ChangeNotificationListener changeListener :
+ DirectoryServer.getChangeNotificationListeners())
+ {
+ try
+ {
+ changeListener.handleModifyOperation(this, currentEntry,
+ modifiedEntry);
+ }
+ catch (Exception e)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ }
+
+ Message message = ERR_MODIFY_ERROR_NOTIFYING_CHANGE_LISTENER.get(
+ getExceptionMessage(e));
+ logError(message);
+ }
+ }
+ }
+
+
+
+ // Stop the processing timer.
+ setProcessingStopTime();
+ }
+
+
+
+ /**
+ * Processes any controls contained in the modify request.
+ *
+ * @throws DirectoryException If a problem is encountered with any of the
+ * controls.
+ */
+ private void processRequestControls()
+ throws DirectoryException
+ {
+ List<Control> requestControls = getRequestControls();
+ if ((requestControls != null) && (! requestControls.isEmpty()))
+ {
+ for (int i=0; i < requestControls.size(); i++)
+ {
+ Control c = requestControls.get(i);
+ String oid = c.getOID();
+
+ if (! AccessControlConfigManager.getInstance().
+ getAccessControlHandler().isAllowed(entryDN, this, c))
+ {
+ skipPostOperation = true;
+ throw new DirectoryException(ResultCode.INSUFFICIENT_ACCESS_RIGHTS,
+ ERR_CONTROL_INSUFFICIENT_ACCESS_RIGHTS.get(oid));
+ }
+
+
+ if (oid.equals(OID_LDAP_ASSERTION))
+ {
+ LDAPAssertionRequestControl assertControl;
+ if (c instanceof LDAPAssertionRequestControl)
+ {
+ assertControl = (LDAPAssertionRequestControl) c;
+ }
+ else
+ {
+ try
+ {
+ assertControl = LDAPAssertionRequestControl.decodeControl(c);
+ requestControls.set(i, assertControl);
+ }
+ catch (LDAPException le)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, le);
+ }
+
+ throw new DirectoryException(
+ ResultCode.valueOf(le.getResultCode()),
+ le.getMessageObject());
+ }
+ }
+
+ try
+ {
+ // FIXME -- We need to determine whether the current user has
+ // permission to make this determination.
+ SearchFilter filter = assertControl.getSearchFilter();
+ if (! filter.matchesEntry(currentEntry))
+ {
+ throw new DirectoryException(ResultCode.ASSERTION_FAILED,
+ ERR_MODIFY_ASSERTION_FAILED.get(
+ String.valueOf(entryDN)));
+ }
+ }
+ catch (DirectoryException de)
+ {
+ if (de.getResultCode() == ResultCode.ASSERTION_FAILED)
+ {
+ throw de;
+ }
+
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, de);
+ }
+
+ throw new DirectoryException(ResultCode.PROTOCOL_ERROR,
+ ERR_MODIFY_CANNOT_PROCESS_ASSERTION_FILTER.get(
+ String.valueOf(entryDN),
+ de.getMessageObject()));
+ }
+ }
+ else if (oid.equals(OID_LDAP_NOOP_OPENLDAP_ASSIGNED))
+ {
+ noOp = true;
+ }
+ else if (oid.equals(OID_LDAP_READENTRY_PREREAD))
+ {
+ if (c instanceof LDAPPreReadRequestControl)
+ {
+ preReadRequest = (LDAPPreReadRequestControl) c;
+ }
+ else
+ {
+ try
+ {
+ preReadRequest = LDAPPreReadRequestControl.decodeControl(c);
+ requestControls.set(i, preReadRequest);
+ }
+ catch (LDAPException le)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, le);
+ }
+
+ throw new DirectoryException(
+ ResultCode.valueOf(le.getResultCode()),
+ le.getMessageObject());
+ }
+ }
+ }
+ else if (oid.equals(OID_LDAP_READENTRY_POSTREAD))
+ {
+ if (c instanceof LDAPPostReadRequestControl)
+ {
+ postReadRequest = (LDAPPostReadRequestControl) c;
+ }
+ else
+ {
+ try
+ {
+ postReadRequest = LDAPPostReadRequestControl.decodeControl(c);
+ requestControls.set(i, postReadRequest);
+ }
+ catch (LDAPException le)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, le);
+ }
+
+ throw new DirectoryException(
+ ResultCode.valueOf(le.getResultCode()),
+ le.getMessageObject());
+ }
+ }
+ }
+ else if (oid.equals(OID_PROXIED_AUTH_V1))
+ {
+ // The requester must have the PROXIED_AUTH privilige in order to
+ // be able to use this control.
+ if (! clientConnection.hasPrivilege(Privilege.PROXIED_AUTH, this))
+ {
+ throw new DirectoryException(ResultCode.AUTHORIZATION_DENIED,
+ ERR_PROXYAUTH_INSUFFICIENT_PRIVILEGES.get());
+ }
+
+
+ ProxiedAuthV1Control proxyControl;
+ if (c instanceof ProxiedAuthV1Control)
+ {
+ proxyControl = (ProxiedAuthV1Control) c;
+ }
+ else
+ {
+ try
+ {
+ proxyControl = ProxiedAuthV1Control.decodeControl(c);
+ }
+ catch (LDAPException le)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, le);
+ }
+
+ throw new DirectoryException(
+ ResultCode.valueOf(le.getResultCode()),
+ le.getMessageObject());
+ }
+ }
+
+
+ Entry authorizationEntry = proxyControl.getAuthorizationEntry();
+ setAuthorizationEntry(authorizationEntry);
+ if (authorizationEntry == null)
+ {
+ setProxiedAuthorizationDN(DN.nullDN());
+ }
+ else
+ {
+ setProxiedAuthorizationDN(authorizationEntry.getDN());
+ }
+ }
+ else if (oid.equals(OID_PROXIED_AUTH_V2))
+ {
+ // The requester must have the PROXIED_AUTH privilige in order to
+ // be able to use this control.
+ if (! clientConnection.hasPrivilege(Privilege.PROXIED_AUTH, this))
+ {
+ throw new DirectoryException(ResultCode.AUTHORIZATION_DENIED,
+ ERR_PROXYAUTH_INSUFFICIENT_PRIVILEGES.get());
+ }
+
+
+ ProxiedAuthV2Control proxyControl;
+ if (c instanceof ProxiedAuthV2Control)
+ {
+ proxyControl = (ProxiedAuthV2Control) c;
+ }
+ else
+ {
+ try
+ {
+ proxyControl = ProxiedAuthV2Control.decodeControl(c);
+ }
+ catch (LDAPException le)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, le);
+ }
+
+ throw new DirectoryException(
+ ResultCode.valueOf(le.getResultCode()),
+ le.getMessageObject());
+ }
+ }
+
+
+ Entry authorizationEntry = proxyControl.getAuthorizationEntry();
+ setAuthorizationEntry(authorizationEntry);
+ if (authorizationEntry == null)
+ {
+ setProxiedAuthorizationDN(DN.nullDN());
+ }
+ else
+ {
+ setProxiedAuthorizationDN(authorizationEntry.getDN());
+ }
+ }
+ else if (oid.equals(OID_PASSWORD_POLICY_CONTROL))
+ {
+ pwPolicyControlRequested = true;
+ }
+
+ // NYI -- Add support for additional controls.
+ else if (c.isCritical())
+ {
+ if ((backend == null) || (! backend.supportsControl(oid)))
+ {
+ throw new DirectoryException(
+ ResultCode.UNAVAILABLE_CRITICAL_EXTENSION,
+ ERR_MODIFY_UNSUPPORTED_CRITICAL_CONTROL.get(
+ String.valueOf(entryDN), oid));
+ }
+ }
+ }
+ }
+ }
+
+
+
+ /**
+ * Handles the initial set of password policy and schema processing for this
+ * modify operation.
+ *
+ * @throws DirectoryException If a problem is encountered that should cause
+ * the modify operation to fail.
+ */
+ private void handleInitialPasswordPolicyAndSchemaProcessing()
+ throws DirectoryException
+ {
+ // Declare variables used for password policy state processing.
+ currentPasswordProvided = false;
+ isEnabled = true;
+ enabledStateChanged = false;
+ if (currentEntry.hasAttribute(
+ pwPolicyState.getPolicy().getPasswordAttribute()))
+ {
+ // It may actually have more than one, but we can't tell the difference if
+ // the values are encoded, and its enough for our purposes just to know
+ // that there is at least one.
+ numPasswords = 1;
+ }
+ else
+ {
+ numPasswords = 0;
+ }
+
+
+ // If it's not an internal or synchronization operation, then iterate
+ // through the set of modifications to see if a password is included in the
+ // changes. If so, then add the appropriate state changes to the set of
+ // modifications.
+ if (! (isInternalOperation() || isSynchronizationOperation()))
+ {
+ for (Modification m : modifications)
+ {
+ if (m.getAttribute().getAttributeType().equals(
+ pwPolicyState.getPolicy().getPasswordAttribute()))
+ {
+ passwordChanged = true;
+ if (! selfChange)
+ {
+ if (! clientConnection.hasPrivilege(Privilege.PASSWORD_RESET, this))
+ {
+ pwpErrorType = PasswordPolicyErrorType.PASSWORD_MOD_NOT_ALLOWED;
+ throw new DirectoryException(
+ ResultCode.INSUFFICIENT_ACCESS_RIGHTS,
+ ERR_MODIFY_PWRESET_INSUFFICIENT_PRIVILEGES.get());
+ }
+ }
+
+ break;
+ }
+ }
+ }
+
+
+ for (Modification m : modifications)
+ {
+ Attribute a = m.getAttribute();
+ AttributeType t = a.getAttributeType();
+
+
+ // If the attribute type is marked "NO-USER-MODIFICATION" then fail unless
+ // this is an internal operation or is related to synchronization in some
+ // way.
+ if (t.isNoUserModification())
+ {
+ if (! (isInternalOperation() || isSynchronizationOperation() ||
+ m.isInternal()))
+ {
+ throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
+ ERR_MODIFY_ATTR_IS_NO_USER_MOD.get(
+ String.valueOf(entryDN), a.getName()));
+ }
+ }
+
+ // If the attribute type is marked "OBSOLETE" and the modification is
+ // setting new values, then fail unless this is an internal operation or
+ // is related to synchronization in some way.
+ if (t.isObsolete())
+ {
+ if (a.hasValue() &&
+ (m.getModificationType() != ModificationType.DELETE))
+ {
+ if (! (isInternalOperation() || isSynchronizationOperation() ||
+ m.isInternal()))
+ {
+ throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
+ ERR_MODIFY_ATTR_IS_OBSOLETE.get(
+ String.valueOf(entryDN), a.getName()));
+ }
+ }
+ }
+
+
+ // See if the attribute is one which controls the privileges available for
+ // a user. If it is, then the client must have the PRIVILEGE_CHANGE
+ // privilege.
+ if (t.hasName(OP_ATTR_PRIVILEGE_NAME))
+ {
+ if (! clientConnection.hasPrivilege(Privilege.PRIVILEGE_CHANGE, this))
+ {
+ throw new DirectoryException(ResultCode.INSUFFICIENT_ACCESS_RIGHTS,
+ ERR_MODIFY_CHANGE_PRIVILEGE_INSUFFICIENT_PRIVILEGES.get());
+ }
+ }
+
+
+ // If the modification is updating the password attribute, then perform
+ // any necessary password policy processing. This processing should be
+ // skipped for synchronization operations.
+ boolean isPassword =
+ t.equals(pwPolicyState.getPolicy().getPasswordAttribute());
+ if (isPassword && (!(isSynchronizationOperation())))
+ {
+ // If the attribute contains any options, then reject it. Passwords
+ // will not be allowed to have options. Skipped for internal operations.
+ if(! isInternalOperation())
+ {
+ if (a.hasOptions())
+ {
+ throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
+ ERR_MODIFY_PASSWORDS_CANNOT_HAVE_OPTIONS.get());
+ }
+
+
+ // If it's a self change, then see if that's allowed.
+ if (selfChange &&
+ (! pwPolicyState.getPolicy().allowUserPasswordChanges()))
+ {
+ pwpErrorType = PasswordPolicyErrorType.PASSWORD_MOD_NOT_ALLOWED;
+ throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
+ ERR_MODIFY_NO_USER_PW_CHANGES.get());
+ }
+
+
+ // If we require secure password changes, then makes sure it's a
+ // secure communication channel.
+ if (pwPolicyState.getPolicy().requireSecurePasswordChanges() &&
+ (! clientConnection.isSecure()))
+ {
+ pwpErrorType = PasswordPolicyErrorType.PASSWORD_MOD_NOT_ALLOWED;
+ throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
+ ERR_MODIFY_REQUIRE_SECURE_CHANGES.get());
+ }
+
+
+ // If it's a self change and it's not been long enough since the
+ // previous change, then reject it.
+ if (selfChange && pwPolicyState.isWithinMinimumAge())
+ {
+ pwpErrorType = PasswordPolicyErrorType.PASSWORD_TOO_YOUNG;
+ throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
+ ERR_MODIFY_WITHIN_MINIMUM_AGE.get());
+ }
+ }
+
+ // Check to see whether this will adding, deleting, or replacing
+ // password values (increment doesn't make any sense for passwords).
+ // Then perform the appropriate type of processing for that kind of
+ // modification.
+ boolean isAdd = (m.getModificationType() == ModificationType.ADD);
+ LinkedHashSet<AttributeValue> pwValues = a.getValues();
+ LinkedHashSet<AttributeValue> encodedValues =
+ new LinkedHashSet<AttributeValue>();
+ switch (m.getModificationType())
+ {
+ case ADD:
+ case REPLACE:
+ processInitialAddOrReplacePW(isAdd, pwValues, encodedValues, a);
+ break;
+
+ case DELETE:
+ processInitialDeletePW(pwValues, encodedValues, a);
+ break;
+
+ default:
+ throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
+ ERR_MODIFY_INVALID_MOD_TYPE_FOR_PASSWORD.get(
+ String.valueOf(m.getModificationType()),
+ a.getName()));
+ }
+ }
+ else
+ {
+ // See if it's an attribute used to maintain the account
+ // enabled/disabled state.
+ AttributeType disabledAttr =
+ DirectoryServer.getAttributeType(OP_ATTR_ACCOUNT_DISABLED, true);
+ if (t.equals(disabledAttr))
+ {
+ enabledStateChanged = true;
+ for (AttributeValue v : a.getValues())
+ {
+ try
+ {
+ isEnabled =
+ (! BooleanSyntax.decodeBooleanValue(v.getNormalizedValue()));
+ }
+ catch (DirectoryException de)
+ {
+ throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
+ ERR_MODIFY_INVALID_DISABLED_VALUE.get(
+ OP_ATTR_ACCOUNT_DISABLED,
+ String.valueOf(de.getMessageObject())), de);
+ }
+ }
+ }
+ }
+
+
+ switch (m.getModificationType())
+ {
+ case ADD:
+ processInitialAddSchema(a);
+ break;
+
+ case DELETE:
+ processInitialDeleteSchema(a);
+ break;
+
+ case REPLACE:
+ processInitialReplaceSchema(a);
+ break;
+
+ case INCREMENT:
+ processInitialIncrementSchema(a);
+ break;
+ }
+ }
+ }
+
+
+
+ /**
+ * Performs the initial password policy add or replace processing.
+ *
+ * @param isAdd Indicates whether it is an add or replace update.
+ * @param pwValues The set of password values as included in the
+ * request.
+ * @param encodedValues The set of encoded password values.
+ * @param pwAttr The attribute involved in the password change.
+ *
+ * @throws DirectoryException If a problem occurs that should cause the
+ * modify operation to fail.
+ */
+ private void processInitialAddOrReplacePW(boolean isAdd,
+ LinkedHashSet<AttributeValue> pwValues,
+ LinkedHashSet<AttributeValue> encodedValues,
+ Attribute pwAttr)
+ throws DirectoryException
+ {
+ int passwordsToAdd = pwValues.size();
+
+ if (isAdd)
+ {
+ numPasswords += passwordsToAdd;
+ }
+ else
+ {
+ numPasswords = passwordsToAdd;
+ }
+
+
+ // If there were multiple password values, then make sure that's OK.
+ if ((! isInternalOperation()) &&
+ (! pwPolicyState.getPolicy().allowMultiplePasswordValues()) &&
+ (passwordsToAdd > 1))
+ {
+ pwpErrorType = PasswordPolicyErrorType.PASSWORD_MOD_NOT_ALLOWED;
+ throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
+ ERR_MODIFY_MULTIPLE_VALUES_NOT_ALLOWED.get());
+ }
+
+
+ // Iterate through the password values and see if any of them are
+ // pre-encoded. If so, then check to see if we'll allow it. Otherwise,
+ // store the clear-text values for later validation and update the attribute
+ // with the encoded values.
+ for (AttributeValue v : pwValues)
+ {
+ if (pwPolicyState.passwordIsPreEncoded(v.getValue()))
+ {
+ if ((! isInternalOperation()) &&
+ ! pwPolicyState.getPolicy().allowPreEncodedPasswords())
+ {
+ pwpErrorType = PasswordPolicyErrorType.INSUFFICIENT_PASSWORD_QUALITY;
+ throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
+ ERR_MODIFY_NO_PREENCODED_PASSWORDS.get());
+ }
+ else
+ {
+ encodedValues.add(v);
+ }
+ }
+ else
+ {
+ if (isAdd)
+ {
+ // Make sure that the password value doesn't already exist.
+ if (pwPolicyState.passwordMatches(v.getValue()))
+ {
+ pwpErrorType = PasswordPolicyErrorType.PASSWORD_IN_HISTORY;
+ throw new DirectoryException(ResultCode.ATTRIBUTE_OR_VALUE_EXISTS,
+ ERR_MODIFY_PASSWORD_EXISTS.get());
+ }
+ }
+
+ if (newPasswords == null)
+ {
+ newPasswords = new LinkedList<AttributeValue>();
+ }
+
+ newPasswords.add(v);
+
+ for (ByteString s : pwPolicyState.encodePassword(v.getValue()))
+ {
+ encodedValues.add(new AttributeValue(pwAttr.getAttributeType(), s));
+ }
+ }
+ }
+
+ pwAttr.setValues(encodedValues);
+ }
+
+
+
+ /**
+ * Performs the initial password policy delete processing.
+ *
+ * @param pwValues The set of password values as included in the
+ * request.
+ * @param encodedValues The set of encoded password values.
+ * @param pwAttr The attribute involved in the password change.
+ *
+ * @throws DirectoryException If a problem occurs that should cause the
+ * modify operation to fail.
+ */
+ private void processInitialDeletePW(LinkedHashSet<AttributeValue> pwValues,
+ LinkedHashSet<AttributeValue> encodedValues,
+ Attribute pwAttr)
+ throws DirectoryException
+ {
+ // Iterate through the password values and see if any of them are
+ // pre-encoded. We will never allow pre-encoded passwords for user password
+ // changes, but we will allow them for administrators. For each clear-text
+ // value, verify that at least one value in the entry matches and replace
+ // the clear-text value with the appropriate encoded forms.
+ for (AttributeValue v : pwValues)
+ {
+ if (pwPolicyState.passwordIsPreEncoded(v.getValue()))
+ {
+ if ((! isInternalOperation()) && selfChange)
+ {
+ pwpErrorType = PasswordPolicyErrorType.INSUFFICIENT_PASSWORD_QUALITY;
+ throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
+ ERR_MODIFY_NO_PREENCODED_PASSWORDS.get());
+ }
+ else
+ {
+ encodedValues.add(v);
+ }
+ }
+ else
+ {
+ List<Attribute> attrList =
+ currentEntry.getAttribute(pwAttr.getAttributeType());
+ if ((attrList == null) || (attrList.isEmpty()))
+ {
+ throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
+ ERR_MODIFY_NO_EXISTING_VALUES.get());
+ }
+ boolean found = false;
+ for (Attribute attr : attrList)
+ {
+ for (AttributeValue av : attr.getValues())
+ {
+ if (pwPolicyState.getPolicy().usesAuthPasswordSyntax())
+ {
+ if (AuthPasswordSyntax.isEncoded(av.getValue()))
+ {
+ StringBuilder[] compoenents =
+ AuthPasswordSyntax.decodeAuthPassword(av.getStringValue());
+ PasswordStorageScheme scheme =
+ DirectoryServer.getAuthPasswordStorageScheme(
+ compoenents[0].toString());
+ if (scheme != null)
+ {
+ if (scheme.authPasswordMatches(v.getValue(),
+ compoenents[1].toString(),
+ compoenents[2].toString()))
+ {
+ encodedValues.add(av);
+ found = true;
+ }
+ }
+ }
+ else
+ {
+ if (av.equals(v))
+ {
+ encodedValues.add(v);
+ found = true;
+ }
+ }
+ }
+ else
+ {
+ if (UserPasswordSyntax.isEncoded(av.getValue()))
+ {
+ String[] compoenents = UserPasswordSyntax.decodeUserPassword(
+ av.getStringValue());
+ PasswordStorageScheme scheme =
+ DirectoryServer.getPasswordStorageScheme(
+ toLowerCase(compoenents[0]));
+ if (scheme != null)
+ {
+ if (scheme.passwordMatches(v.getValue(),
+ new ASN1OctetString(compoenents[1])))
+ {
+ encodedValues.add(av);
+ found = true;
+ }
+ }
+ }
+ else
+ {
+ if (av.equals(v))
+ {
+ encodedValues.add(v);
+ found = true;
+ }
+ }
+ }
+ }
+ }
+
+ if (found)
+ {
+ if (currentPasswords == null)
+ {
+ currentPasswords = new LinkedList<AttributeValue>();
+ }
+ currentPasswords.add(v);
+ numPasswords--;
+ }
+ else
+ {
+ throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
+ ERR_MODIFY_INVALID_PASSWORD.get());
+ }
+
+ currentPasswordProvided = true;
+ }
+ }
+
+ pwAttr.setValues(encodedValues);
+ }
+
+
+
+ /**
+ * Performs the initial schema processing for an add modification.
+ *
+ * @param attr The attribute being added.
+ *
+ * @throws DirectoryException If a problem occurs that should cause the
+ * modify operation to fail.
+ */
+ private void processInitialAddSchema(Attribute attr)
+ throws DirectoryException
+ {
+ // Make sure that one or more values have been provided for the attribute.
+ LinkedHashSet<AttributeValue> newValues = attr.getValues();
+ if ((newValues == null) || newValues.isEmpty())
+ {
+ throw new DirectoryException(ResultCode.PROTOCOL_ERROR,
+ ERR_MODIFY_ADD_NO_VALUES.get(String.valueOf(entryDN),
+ attr.getName()));
+ }
+
+ // If the server is configured to check schema and the operation is not a
+ // synchronization operation, make sure that all the new values are valid
+ // according to the associated syntax.
+ if ((DirectoryServer.checkSchema()) && (! isSynchronizationOperation()))
+ {
+ AcceptRejectWarn syntaxPolicy =
+ DirectoryServer.getSyntaxEnforcementPolicy();
+ AttributeSyntax syntax = attr.getAttributeType().getSyntax();
+
+ if (syntaxPolicy == AcceptRejectWarn.REJECT)
+ {
+ MessageBuilder invalidReason = new MessageBuilder();
+ for (AttributeValue v : newValues)
+ {
+ if (! syntax.valueIsAcceptable(v.getValue(), invalidReason))
+ {
+ throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
+ ERR_MODIFY_ADD_INVALID_SYNTAX.get(
+ String.valueOf(entryDN), attr.getName(),
+ v.getStringValue(), invalidReason));
+ }
+ }
+ }
+ else if (syntaxPolicy == AcceptRejectWarn.WARN)
+ {
+ MessageBuilder invalidReason = new MessageBuilder();
+ for (AttributeValue v : newValues)
+ {
+ if (! syntax.valueIsAcceptable(v.getValue(), invalidReason))
+ {
+ setResultCode(ResultCode.INVALID_ATTRIBUTE_SYNTAX);
+ logError(ERR_MODIFY_ADD_INVALID_SYNTAX.get(String.valueOf(entryDN),
+ attr.getName(), v.getStringValue(), invalidReason));
+ invalidReason = new MessageBuilder();
+ }
+ }
+ }
+ }
+
+
+ // Add the provided attribute or merge an existing attribute with
+ // the values of the new attribute. If there are any duplicates,
+ // then fail.
+ if (attr.getAttributeType().isObjectClassType())
+ {
+ modifiedEntry.addObjectClasses(newValues);
+ }
+ else
+ {
+ LinkedList<AttributeValue> duplicateValues =
+ new LinkedList<AttributeValue>();
+ modifiedEntry.addAttribute(attr, duplicateValues);
+ if (! duplicateValues.isEmpty())
+ {
+ StringBuilder buffer = new StringBuilder();
+ Iterator<AttributeValue> iterator = duplicateValues.iterator();
+ buffer.append(iterator.next().getStringValue());
+ while (iterator.hasNext())
+ {
+ buffer.append(", ");
+ buffer.append(iterator.next().getStringValue());
+ }
+
+ throw new DirectoryException(ResultCode.ATTRIBUTE_OR_VALUE_EXISTS,
+ ERR_MODIFY_ADD_DUPLICATE_VALUE.get(
+ String.valueOf(entryDN), attr.getName(), buffer));
+ }
+ }
+ }
+
+
+
+ /**
+ * Performs the initial schema processing for a delete modification.
+ *
+ * @param attr The attribute being deleted.
+ *
+ * @throws DirectoryException If a problem occurs that should cause the
+ * modify operation to fail.
+ */
+ private void processInitialDeleteSchema(Attribute attr)
+ throws DirectoryException
+ {
+ // Remove the specified attribute values or the entire attribute from the
+ // value. If there are any specified values that were not present, then
+ // fail. If the RDN attribute value would be removed, then fail.
+ LinkedList<AttributeValue> missingValues = new LinkedList<AttributeValue>();
+ boolean attrExists = modifiedEntry.removeAttribute(attr, missingValues);
+
+ if (attrExists)
+ {
+ if (missingValues.isEmpty())
+ {
+ AttributeType t = attr.getAttributeType();
+
+ RDN rdn = modifiedEntry.getDN().getRDN();
+ if ((rdn != null) && rdn.hasAttributeType(t) &&
+ (! modifiedEntry.hasValue(t, attr.getOptions(),
+ rdn.getAttributeValue(t))))
+ {
+ throw new DirectoryException(ResultCode.NOT_ALLOWED_ON_RDN,
+ ERR_MODIFY_DELETE_RDN_ATTR.get(
+ String.valueOf(entryDN),
+ attr.getName()));
+ }
+ }
+ else
+ {
+ StringBuilder buffer = new StringBuilder();
+ Iterator<AttributeValue> iterator = missingValues.iterator();
+ buffer.append(iterator.next().getStringValue());
+ while (iterator.hasNext())
+ {
+ buffer.append(", ");
+ buffer.append(iterator.next().getStringValue());
+ }
+
+ throw new DirectoryException(ResultCode.NO_SUCH_ATTRIBUTE,
+ ERR_MODIFY_DELETE_MISSING_VALUES.get(
+ String.valueOf(entryDN), attr.getName(), buffer));
+ }
+ }
+ else
+ {
+ throw new DirectoryException(ResultCode.NO_SUCH_ATTRIBUTE,
+ ERR_MODIFY_DELETE_NO_SUCH_ATTR.get(
+ String.valueOf(entryDN), attr.getName()));
+ }
+ }
+
+
+
+ /**
+ * Performs the initial schema processing for a replace modification.
+ *
+ * @param attr The attribute being replaced.
+ *
+ * @throws DirectoryException If a problem occurs that should cause the
+ * modify operation to fail.
+ */
+ private void processInitialReplaceSchema(Attribute attr)
+ throws DirectoryException
+ {
+ // If it is the objectclass attribute, then treat that separately.
+ if (attr.getAttributeType().isObjectClassType())
+ {
+ modifiedEntry.setObjectClasses(attr.getValues());
+ return;
+ }
+
+
+ // If the provided attribute does not have any values, then we will simply
+ // remove the attribute from the entry (if it exists).
+ AttributeType t = attr.getAttributeType();
+ if (! attr.hasValue())
+ {
+ modifiedEntry.removeAttribute(t, attr.getOptions());
+ RDN rdn = modifiedEntry.getDN().getRDN();
+ if ((rdn != null) && rdn.hasAttributeType(t) &&
+ (! modifiedEntry.hasValue(t, attr.getOptions(),
+ rdn.getAttributeValue(t))))
+ {
+ throw new DirectoryException(ResultCode.NOT_ALLOWED_ON_RDN,
+ ERR_MODIFY_DELETE_RDN_ATTR.get(String.valueOf(entryDN),
+ attr.getName()));
+ }
+
+ return;
+ }
+
+ // If the server is configured to check schema and the operation is not a
+ // synchronization operation, make sure that all the new values are valid
+ // according to the associated syntax.
+ LinkedHashSet<AttributeValue> newValues = attr.getValues();
+ if ((DirectoryServer.checkSchema()) && (! isSynchronizationOperation()))
+ {
+ AcceptRejectWarn syntaxPolicy =
+ DirectoryServer.getSyntaxEnforcementPolicy();
+ AttributeSyntax syntax = t.getSyntax();
+
+ if (syntaxPolicy == AcceptRejectWarn.REJECT)
+ {
+ MessageBuilder invalidReason = new MessageBuilder();
+ for (AttributeValue v : newValues)
+ {
+ if (! syntax.valueIsAcceptable(v.getValue(), invalidReason))
+ {
+ throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
+ ERR_MODIFY_REPLACE_INVALID_SYNTAX.get(
+ String.valueOf(entryDN), attr.getName(),
+ v.getStringValue(), invalidReason));
+ }
+ }
+ }
+ else if (syntaxPolicy == AcceptRejectWarn.WARN)
+ {
+ MessageBuilder invalidReason = new MessageBuilder();
+ for (AttributeValue v : newValues)
+ {
+ if (! syntax.valueIsAcceptable(v.getValue(), invalidReason))
+ {
+ setResultCode(ResultCode.INVALID_ATTRIBUTE_SYNTAX);
+ logError(ERR_MODIFY_REPLACE_INVALID_SYNTAX.get(
+ String.valueOf(entryDN), attr.getName(),
+ v.getStringValue(), invalidReason));
+ invalidReason = new MessageBuilder();
+ }
+ }
+ }
+ }
+
+
+ // If the provided attribute does not have any options, then we will simply
+ // use it in place of any existing attribute of the provided type (or add it
+ // if it doesn't exist).
+ if (! attr.hasOptions())
+ {
+ List<Attribute> attrList = new ArrayList<Attribute>(1);
+ attrList.add(attr);
+ modifiedEntry.putAttribute(t, attrList);
+
+ RDN rdn = modifiedEntry.getDN().getRDN();
+ if ((rdn != null) && rdn.hasAttributeType(t) &&
+ (! modifiedEntry.hasValue(t, attr.getOptions(),
+ rdn.getAttributeValue(t))))
+ {
+ throw new DirectoryException(ResultCode.NOT_ALLOWED_ON_RDN,
+ ERR_MODIFY_DELETE_RDN_ATTR.get(
+ String.valueOf(entryDN),
+ attr.getName()));
+ }
+
+ return;
+ }
+
+
+ // See if there is an existing attribute of the provided type. If not, then
+ // we'll use the new one.
+ List<Attribute> attrList = modifiedEntry.getAttribute(t);
+ if ((attrList == null) || attrList.isEmpty())
+ {
+ attrList = new ArrayList<Attribute>(1);
+ attrList.add(attr);
+ modifiedEntry.putAttribute(t, attrList);
+
+ RDN rdn = modifiedEntry.getDN().getRDN();
+ if ((rdn != null) && rdn.hasAttributeType(t) &&
+ (! modifiedEntry.hasValue(t, attr.getOptions(),
+ rdn.getAttributeValue(t))))
+ {
+ throw new DirectoryException(ResultCode.NOT_ALLOWED_ON_RDN,
+ ERR_MODIFY_DELETE_RDN_ATTR.get(
+ String.valueOf(entryDN),
+ attr.getName()));
+ }
+
+ return;
+ }
+
+
+ // There must be an existing occurrence of the provided attribute in the
+ // entry. If there is a version with exactly the set of options provided,
+ // then replace it. Otherwise, add a new one.
+ boolean found = false;
+ for (int i=0; i < attrList.size(); i++)
+ {
+ if (attrList.get(i).optionsEqual(attr.getOptions()))
+ {
+ attrList.set(i, attr);
+ found = true;
+ break;
+ }
+ }
+
+ if (! found)
+ {
+ attrList.add(attr);
+ }
+
+ RDN rdn = modifiedEntry.getDN().getRDN();
+ if ((rdn != null) && rdn.hasAttributeType(t) &&
+ (! modifiedEntry.hasValue(t, attr.getOptions(),
+ rdn.getAttributeValue(t))))
+ {
+ throw new DirectoryException(ResultCode.NOT_ALLOWED_ON_RDN,
+ ERR_MODIFY_DELETE_RDN_ATTR.get(
+ String.valueOf(entryDN),
+ attr.getName()));
+ }
+ }
+
+
+
+ /**
+ * Performs the initial schema processing for an increment modification.
+ *
+ * @param attr The attribute being incremented.
+ *
+ * @throws DirectoryException If a problem occurs that should cause the
+ * modify operation to fail.
+ */
+ private void processInitialIncrementSchema(Attribute attr)
+ throws DirectoryException
+ {
+ // The specified attribute type must not be an RDN attribute.
+ AttributeType t = attr.getAttributeType();
+ RDN rdn = modifiedEntry.getDN().getRDN();
+ if ((rdn != null) && rdn.hasAttributeType(t))
+ {
+ throw new DirectoryException(ResultCode.NOT_ALLOWED_ON_RDN,
+ ERR_MODIFY_INCREMENT_RDN.get(
+ String.valueOf(entryDN),
+ attr.getName()));
+ }
+
+
+ // The provided attribute must have a single value, and it must be an
+ // integer.
+ LinkedHashSet<AttributeValue> values = attr.getValues();
+ if ((values == null) || values.isEmpty())
+ {
+ throw new DirectoryException(ResultCode.PROTOCOL_ERROR,
+ ERR_MODIFY_INCREMENT_REQUIRES_VALUE.get(
+ String.valueOf(entryDN),
+ attr.getName()));
+ }
+ else if (values.size() > 1)
+ {
+ throw new DirectoryException(ResultCode.PROTOCOL_ERROR,
+ ERR_MODIFY_INCREMENT_REQUIRES_SINGLE_VALUE.get(
+ String.valueOf(entryDN), attr.getName()));
+ }
+
+ AttributeValue v = values.iterator().next();
+
+ long incrementValue;
+ try
+ {
+ incrementValue = Long.parseLong(v.getNormalizedStringValue());
+ }
+ catch (Exception e)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ }
+
+ throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
+ ERR_MODIFY_INCREMENT_PROVIDED_VALUE_NOT_INTEGER.get(
+ String.valueOf(entryDN), attr.getName(),
+ v.getStringValue()), e);
+ }
+
+
+ // Get the corresponding attribute from the entry and make sure that it has
+ // a single integer value.
+ List<Attribute> attrList = modifiedEntry.getAttribute(t, attr.getOptions());
+ if ((attrList == null) || attrList.isEmpty())
+ {
+ throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION,
+ ERR_MODIFY_INCREMENT_REQUIRES_EXISTING_VALUE.get(
+ String.valueOf(entryDN), attr.getName()));
+ }
+
+ boolean updated = false;
+ for (Attribute a : attrList)
+ {
+ LinkedHashSet<AttributeValue> valueList = a.getValues();
+ if ((valueList == null) || valueList.isEmpty())
+ {
+ continue;
+ }
+
+ LinkedHashSet<AttributeValue> newValueList =
+ new LinkedHashSet<AttributeValue>(valueList.size());
+ for (AttributeValue existingValue : valueList)
+ {
+ long newIntValue;
+ try
+ {
+ long existingIntValue =
+ Long.parseLong(existingValue.getStringValue());
+ newIntValue = existingIntValue + incrementValue;
+ }
+ catch (Exception e)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ }
+
+ throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
+ ERR_MODIFY_INCREMENT_REQUIRES_INTEGER_VALUE.get(
+ String.valueOf(entryDN), a.getName(),
+ existingValue.getStringValue()), e);
+ }
+
+ ByteString newValue = new ASN1OctetString(String.valueOf(newIntValue));
+ newValueList.add(new AttributeValue(t, newValue));
+ }
+
+ a.setValues(newValueList);
+ updated = true;
+ }
+
+ if (! updated)
+ {
+ throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION,
+ ERR_MODIFY_INCREMENT_REQUIRES_EXISTING_VALUE.get(
+ String.valueOf(entryDN), attr.getName()));
+ }
+ }
+
+
+
+ /**
+ * Performs additional preliminary processing that is required for a password
+ * change.
+ *
+ * @throws DirectoryException If a problem occurs that should cause the
+ * modify operation to fail.
+ */
+ public void performAdditionalPasswordChangedProcessing()
+ throws DirectoryException
+ {
+ // If it was a self change, then see if the current password was provided
+ // and handle accordingly.
+ if (selfChange &&
+ pwPolicyState.getPolicy().requireCurrentPassword() &&
+ (! currentPasswordProvided))
+ {
+ pwpErrorType = PasswordPolicyErrorType.MUST_SUPPLY_OLD_PASSWORD;
+
+ throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
+ ERR_MODIFY_PW_CHANGE_REQUIRES_CURRENT_PW.get());
+ }
+
+
+ // If this change would result in multiple password values, then see if
+ // that's OK.
+ if ((numPasswords > 1) &&
+ (! pwPolicyState.getPolicy().allowMultiplePasswordValues()))
+ {
+ pwpErrorType = PasswordPolicyErrorType.PASSWORD_MOD_NOT_ALLOWED;
+ throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
+ ERR_MODIFY_MULTIPLE_PASSWORDS_NOT_ALLOWED.get());
+ }
+
+
+ // If any of the password values should be validated, then do so now.
+ if (selfChange ||
+ (! pwPolicyState.getPolicy().skipValidationForAdministrators()))
+ {
+ if (newPasswords != null)
+ {
+ HashSet<ByteString> clearPasswords = new HashSet<ByteString>();
+ clearPasswords.addAll(pwPolicyState.getClearPasswords());
+
+ if (currentPasswords != null)
+ {
+ if (clearPasswords.isEmpty())
+ {
+ for (AttributeValue v : currentPasswords)
+ {
+ clearPasswords.add(v.getValue());
+ }
+ }
+ else
+ {
+ // NOTE: We can't rely on the fact that Set doesn't allow
+ // duplicates because technically it's possible that the values
+ // aren't duplicates if they are ASN.1 elements with different types
+ // (like 0x04 for a standard universal octet string type versus 0x80
+ // for a simple password in a bind operation). So we have to
+ // manually check for duplicates.
+ for (AttributeValue v : currentPasswords)
+ {
+ ByteString pw = v.getValue();
+
+ boolean found = false;
+ for (ByteString s : clearPasswords)
+ {
+ if (Arrays.equals(s.value(), pw.value()))
+ {
+ found = true;
+ break;
+ }
+ }
+
+ if (! found)
+ {
+ clearPasswords.add(pw);
+ }
+ }
+ }
+ }
+
+ for (AttributeValue v : newPasswords)
+ {
+ MessageBuilder invalidReason = new MessageBuilder();
+ if (! pwPolicyState.passwordIsAcceptable(this, modifiedEntry,
+ v.getValue(), clearPasswords, invalidReason))
+ {
+ pwpErrorType =
+ PasswordPolicyErrorType.INSUFFICIENT_PASSWORD_QUALITY;
+ throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
+ ERR_MODIFY_PW_VALIDATION_FAILED.get(
+ invalidReason));
+ }
+ }
+ }
+ }
+
+
+ // If we should check the password history, then do so now.
+ if (pwPolicyState.maintainHistory())
+ {
+ if (newPasswords != null)
+ {
+ for (AttributeValue v : newPasswords)
+ {
+ if (pwPolicyState.isPasswordInHistory(v.getValue()))
+ {
+ if (selfChange || (! pwPolicyState.getPolicy().
+ skipValidationForAdministrators()))
+ {
+ pwpErrorType = PasswordPolicyErrorType.PASSWORD_IN_HISTORY;
+ throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
+ ERR_MODIFY_PW_IN_HISTORY.get());
+ }
+ }
+ }
+
+ pwPolicyState.updatePasswordHistory();
+ }
+ }
+
+
+ // See if the account was locked for any reason.
+ wasLocked = pwPolicyState.lockedDueToIdleInterval() ||
+ pwPolicyState.lockedDueToMaximumResetAge() ||
+ pwPolicyState.lockedDueToFailures();
+
+ // Update the password policy state attributes in the user's entry. If the
+ // modification fails, then these changes won't be applied.
+ pwPolicyState.setPasswordChangedTime();
+ pwPolicyState.clearFailureLockout();
+ pwPolicyState.clearGraceLoginTimes();
+ pwPolicyState.clearWarnedTime();
+
+ if (pwPolicyState.getPolicy().forceChangeOnAdd() ||
+ pwPolicyState.getPolicy().forceChangeOnReset())
+ {
+ if (selfChange)
+ {
+ pwPolicyState.setMustChangePassword(false);
+ }
+ else
+ {
+ if ((pwpErrorType == null) &&
+ pwPolicyState.getPolicy().forceChangeOnReset())
+ {
+ pwpErrorType = PasswordPolicyErrorType.CHANGE_AFTER_RESET;
+ }
+
+ pwPolicyState.setMustChangePassword(
+ pwPolicyState.getPolicy().forceChangeOnReset());
+ }
+ }
+
+ if (pwPolicyState.getPolicy().getRequireChangeByTime() > 0)
+ {
+ pwPolicyState.setRequiredChangeTime();
+ }
+
+ modifications.addAll(pwPolicyState.getModifications());
+ modifiedEntry.applyModifications(pwPolicyState.getModifications());
+ }
+
+
+
+ /**
+ * Checks to ensure that both the Directory Server and the backend are
+ * writable.
+ *
+ * @throws DirectoryException If the modify operation should not be allowed
+ * as a result of the writability check.
+ */
+ private void checkWritability()
+ throws DirectoryException
+ {
+ // If it is not a private backend, then check to see if the server or
+ // backend is operating in read-only mode.
+ if (! backend.isPrivateBackend())
+ {
+ switch (DirectoryServer.getWritabilityMode())
+ {
+ case DISABLED:
+ throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
+ ERR_MODIFY_SERVER_READONLY.get(
+ String.valueOf(entryDN)));
+
+ case INTERNAL_ONLY:
+ if (! (isInternalOperation() || isSynchronizationOperation()))
+ {
+ throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
+ ERR_MODIFY_SERVER_READONLY.get(
+ String.valueOf(entryDN)));
+ }
+ }
+
+ switch (backend.getWritabilityMode())
+ {
+ case DISABLED:
+ throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
+ ERR_MODIFY_BACKEND_READONLY.get(
+ String.valueOf(entryDN)));
+
+ case INTERNAL_ONLY:
+ if (! isInternalOperation() || isSynchronizationOperation())
+ {
+ throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
+ ERR_MODIFY_BACKEND_READONLY.get(
+ String.valueOf(entryDN)));
+ }
+ }
+ }
+ }
+
+
+
+ /**
+ * Handles any account status notifications that may be needed as a result of
+ * modify processing.
+ */
+ private void handleAccountStatusNotifications()
+ {
+ if (passwordChanged)
+ {
+ if (selfChange)
+ {
+ AuthenticationInfo authInfo = clientConnection.getAuthenticationInfo();
+ if (authInfo.getAuthenticationDN().equals(modifiedEntry.getDN()))
+ {
+ clientConnection.setMustChangePassword(false);
+ }
+
+ Message message = INFO_MODIFY_PASSWORD_CHANGED.get();
+ pwPolicyState.generateAccountStatusNotification(
+ AccountStatusNotificationType.PASSWORD_CHANGED,
+ modifiedEntry, message,
+ AccountStatusNotification.createProperties(pwPolicyState, false, -1,
+ currentPasswords, newPasswords));
+ }
+ else
+ {
+ Message message = INFO_MODIFY_PASSWORD_RESET.get();
+ pwPolicyState.generateAccountStatusNotification(
+ AccountStatusNotificationType.PASSWORD_RESET, modifiedEntry,
+ message,
+ AccountStatusNotification.createProperties(pwPolicyState, false, -1,
+ currentPasswords, newPasswords));
+ }
+ }
+
+ if (enabledStateChanged)
+ {
+ if (isEnabled)
+ {
+ Message message = INFO_MODIFY_ACCOUNT_ENABLED.get();
+ pwPolicyState.generateAccountStatusNotification(
+ AccountStatusNotificationType.ACCOUNT_ENABLED,
+ modifiedEntry, message,
+ AccountStatusNotification.createProperties(pwPolicyState, false, -1,
+ null, null));
+ }
+ else
+ {
+ Message message = INFO_MODIFY_ACCOUNT_DISABLED.get();
+ pwPolicyState.generateAccountStatusNotification(
+ AccountStatusNotificationType.ACCOUNT_DISABLED,
+ modifiedEntry, message,
+ AccountStatusNotification.createProperties(pwPolicyState, false, -1,
+ null, null));
+ }
+ }
+
+ if (wasLocked)
+ {
+ Message message = INFO_MODIFY_ACCOUNT_UNLOCKED.get();
+ pwPolicyState.generateAccountStatusNotification(
+ AccountStatusNotificationType.ACCOUNT_UNLOCKED, modifiedEntry,
+ message,
+ AccountStatusNotification.createProperties(pwPolicyState, false, -1,
+ null, null));
+ }
+ }
+
+
+
+ /**
+ * Handles any processing that is required for the LDAP pre-read and/or
+ * post-read controls.
+ */
+ private void handleReadEntryProcessing()
+ {
+ if (preReadRequest != null)
+ {
+ Entry entry = currentEntry.duplicate(true);
+
+ if (! preReadRequest.allowsAttribute(
+ DirectoryServer.getObjectClassAttributeType()))
+ {
+ entry.removeAttribute(
+ DirectoryServer.getObjectClassAttributeType());
+ }
+
+ if (! preReadRequest.returnAllUserAttributes())
+ {
+ Iterator<AttributeType> iterator =
+ entry.getUserAttributes().keySet().iterator();
+ while (iterator.hasNext())
+ {
+ AttributeType attrType = iterator.next();
+ if (! preReadRequest.allowsAttribute(attrType))
+ {
+ iterator.remove();
+ }
+ }
+ }
+
+ if (! preReadRequest.returnAllOperationalAttributes())
+ {
+ Iterator<AttributeType> iterator =
+ entry.getOperationalAttributes().keySet().iterator();
+ while (iterator.hasNext())
+ {
+ AttributeType attrType = iterator.next();
+ if (! preReadRequest.allowsAttribute(attrType))
+ {
+ iterator.remove();
+ }
+ }
+ }
+
+ // FIXME -- Check access controls on the entry to see if it should be
+ // returned or if any attributes need to be stripped out..
+ SearchResultEntry searchEntry = new SearchResultEntry(entry);
+ LDAPPreReadResponseControl responseControl =
+ new LDAPPreReadResponseControl(preReadRequest.getOID(),
+ preReadRequest.isCritical(),
+ searchEntry);
+ getResponseControls().add(responseControl);
+ }
+
+ if (postReadRequest != null)
+ {
+ Entry entry = modifiedEntry.duplicate(true);
+
+ if (! postReadRequest.allowsAttribute(
+ DirectoryServer.getObjectClassAttributeType()))
+ {
+ entry.removeAttribute(
+ DirectoryServer.getObjectClassAttributeType());
+ }
+
+ if (! postReadRequest.returnAllUserAttributes())
+ {
+ Iterator<AttributeType> iterator =
+ entry.getUserAttributes().keySet().iterator();
+ while (iterator.hasNext())
+ {
+ AttributeType attrType = iterator.next();
+ if (! postReadRequest.allowsAttribute(attrType))
+ {
+ iterator.remove();
+ }
+ }
+ }
+
+ if (! postReadRequest.returnAllOperationalAttributes())
+ {
+ Iterator<AttributeType> iterator =
+ entry.getOperationalAttributes().keySet().iterator();
+ while (iterator.hasNext())
+ {
+ AttributeType attrType = iterator.next();
+ if (! postReadRequest.allowsAttribute(attrType))
+ {
+ iterator.remove();
+ }
+ }
+ }
+
+ // FIXME -- Check access controls on the entry to see if it should be
+ // returned or if any attributes need to be stripped out..
+ SearchResultEntry searchEntry = new SearchResultEntry(entry);
+ LDAPPostReadResponseControl responseControl =
+ new LDAPPostReadResponseControl(postReadRequest.getOID(),
+ postReadRequest.isCritical(),
+ searchEntry);
+
+ getResponseControls().add(responseControl);
+ }
+ }
}
+
diff --git a/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendSearchOperation.java b/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendSearchOperation.java
index c13fe27..15b5543 100644
--- a/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendSearchOperation.java
+++ b/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendSearchOperation.java
@@ -27,85 +27,621 @@
package org.opends.server.workflowelement.localbackend;
+
+import java.util.List;
+
+import org.opends.messages.Message;
import org.opends.server.api.Backend;
+import org.opends.server.api.ClientConnection;
+import org.opends.server.api.plugin.PostOperationPluginResult;
+import org.opends.server.api.plugin.PreOperationPluginResult;
+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.core.AccessControlConfigManager;
+import org.opends.server.core.DirectoryServer;
+import org.opends.server.core.PersistentSearch;
+import org.opends.server.core.PluginConfigManager;
import org.opends.server.core.SearchOperationWrapper;
import org.opends.server.core.SearchOperation;
+import org.opends.server.loggers.debug.DebugTracer;
import org.opends.server.types.CancelResult;
import org.opends.server.types.CancelledOperationException;
+import org.opends.server.types.Control;
+import org.opends.server.types.DebugLogLevel;
import org.opends.server.types.DirectoryException;
+import org.opends.server.types.DN;
+import org.opends.server.types.Entry;
+import org.opends.server.types.LDAPException;
+import org.opends.server.types.Privilege;
+import org.opends.server.types.ResultCode;
+import org.opends.server.types.SearchFilter;
import org.opends.server.types.operation.PostOperationSearchOperation;
import org.opends.server.types.operation.PreOperationSearchOperation;
import org.opends.server.types.operation.SearchEntrySearchOperation;
import org.opends.server.types.operation.SearchReferenceSearchOperation;
+import static org.opends.messages.CoreMessages.*;
+import static org.opends.server.loggers.debug.DebugLogger.*;
+import static org.opends.server.util.ServerConstants.*;
+import static org.opends.server.util.StaticUtils.*;
+
+
+
/**
* This class defines an operation used to search for entries in a local backend
* of the Directory Server.
*/
-public class LocalBackendSearchOperation extends SearchOperationWrapper
- implements PreOperationSearchOperation,
- PostOperationSearchOperation,
- SearchEntrySearchOperation,
- SearchReferenceSearchOperation
+public class LocalBackendSearchOperation
+ extends SearchOperationWrapper
+ implements PreOperationSearchOperation, PostOperationSearchOperation,
+ SearchEntrySearchOperation, SearchReferenceSearchOperation
{
+ /**
+ * The tracer object for the debug logger.
+ */
+ private static final DebugTracer TRACER = getTracer();
+
+
+
+ // The backend in which the search is to be performed.
+ private Backend backend;
+
+ // Indicates whether we should actually process the search. This should
+ // only be false if it's a persistent search with changesOnly=true.
+ private boolean processSearch;
+
+ // Indicates whether to skip post-operation plugin processing.
+ private boolean skipPostOperation;
+
+ // The client connection for the search operation.
+ private ClientConnection clientConnection;
+
+ // The base DN for the search.
+ private DN baseDN;
+
+ // The persistent search request, if applicable.
+ private PersistentSearch persistentSearch;
+
+ // The filter for the search.
+ private SearchFilter filter;
+
+
/**
- * Creates a new operation that may be used to search for entries in a
- * local backend of the Directory Server.
+ * Creates a new operation that may be used to search for entries in a local
+ * backend of the Directory Server.
*
- * @param search The operation to enhance.
+ * @param search The operation to process.
*/
- public LocalBackendSearchOperation(SearchOperation search){
+ public LocalBackendSearchOperation(SearchOperation search)
+ {
super(search);
LocalBackendWorkflowElement.attachLocalOperation(search, this);
}
+
+
/**
- * Processes the search in the provided backend and recursively through its
- * subordinate backends.
+ * Process this search operation against a local backend.
*
- * @param backend The backend in which to process the search.
- *
- * @throws DirectoryException If a problem occurs while processing the
- * search.
- *
- * @throws CancelledOperationException If the backend noticed and reacted
- * to a request to cancel or abandon the
- * search operation.
+ * @param backend The backend in which the search operation should be
+ * performed.
*/
- public final void searchBackend(Backend backend)
- throws DirectoryException, CancelledOperationException
+ void processLocalSearch(Backend backend)
{
- // Check for and handle a request to cancel this operation.
+ this.backend = backend;
+
+ clientConnection = getClientConnection();
+
+ // Get the plugin config manager that will be used for invoking plugins.
+ PluginConfigManager pluginConfigManager =
+ DirectoryServer.getPluginConfigManager();
+ skipPostOperation = false;
+ processSearch = true;
+
+ // Create a labeled block of code that we can break out of if a problem is
+ // detected.
+searchProcessing:
+ {
+ // Process the search base and filter to convert them from their raw forms
+ // as provided by the client to the forms required for the rest of the
+ // search processing.
+ baseDN = getBaseDN();
+ filter = getFilter();
+
+ if ((baseDN == null) || (filter == null)){
+ break searchProcessing;
+ }
+
+ // Check to see if there are any controls in the request. If so, then
+ // see if there is any special processing required.
+ try
+ {
+ handleRequestControls();
+ }
+ catch (DirectoryException de)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, de);
+ }
+
+ setResponseData(de);
+ break searchProcessing;
+ }
+
+
+ // Check to see if the client has permission to perform the
+ // search.
+
+ // FIXME: for now assume that this will check all permission
+ // pertinent to the operation. This includes proxy authorization
+ // and any other controls specified.
+ if (! AccessControlConfigManager.getInstance().getAccessControlHandler().
+ isAllowed(this))
+ {
+ setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS);
+ appendErrorMessage(ERR_SEARCH_AUTHZ_INSUFFICIENT_ACCESS_RIGHTS.get(
+ String.valueOf(baseDN)));
+ skipPostOperation = true;
+ break searchProcessing;
+ }
+
+ // Check for a request to cancel this operation.
+ if (getCancelRequest() != null)
+ {
+ return;
+ }
+
+
+ // Invoke the pre-operation search plugins.
+ PreOperationPluginResult preOpResult =
+ pluginConfigManager.invokePreOperationSearchPlugins(this);
+ if (preOpResult.connectionTerminated())
+ {
+ // There's no point in continuing with anything. Log the request and
+ // result and return.
+ setResultCode(ResultCode.CANCELED);
+ appendErrorMessage(ERR_CANCELED_BY_PREOP_DISCONNECT.get());
+ setProcessingStopTime();
+ return;
+ }
+ else if (preOpResult.sendResponseImmediately())
+ {
+ skipPostOperation = true;
+ break searchProcessing;
+ }
+ else if (preOpResult.skipCoreProcessing())
+ {
+ skipPostOperation = false;
+ break searchProcessing;
+ }
+
+
+ // Check for a request to cancel this operation.
+ if (getCancelRequest() != null)
+ {
+ return;
+ }
+
+
+ // Get the backend that should hold the search base. If there is none,
+ // then fail.
+ if (backend == null)
+ {
+ setResultCode(ResultCode.NO_SUCH_OBJECT);
+ appendErrorMessage(ERR_SEARCH_BASE_DOESNT_EXIST.get(
+ String.valueOf(baseDN)));
+ break searchProcessing;
+ }
+
+
+ // We'll set the result code to "success". If a problem occurs, then it
+ // will be overwritten.
+ setResultCode(ResultCode.SUCCESS);
+
+
+ // If there's a persistent search, then register it with the server.
+ if (persistentSearch != null)
+ {
+ DirectoryServer.registerPersistentSearch(persistentSearch);
+ setSendResponse(false);
+ }
+
+
+ // Process the search in the backend and all its subordinates.
+ try
+ {
+ if (processSearch)
+ {
+ backend.search(this);
+ }
+ }
+ catch (DirectoryException de)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, de);
+ }
+
+ setResponseData(de);
+
+ if (persistentSearch != null)
+ {
+ DirectoryServer.deregisterPersistentSearch(persistentSearch);
+ setSendResponse(true);
+ }
+
+ break searchProcessing;
+ }
+ catch (CancelledOperationException coe)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, coe);
+ }
+
+ CancelResult cancelResult = coe.getCancelResult();
+
+ setCancelResult(cancelResult);
+ setResultCode(cancelResult.getResultCode());
+
+ Message message = coe.getMessageObject();
+ if ((message != null) && (message.length() > 0))
+ {
+ appendErrorMessage(message);
+ }
+
+ if (persistentSearch != null)
+ {
+ DirectoryServer.deregisterPersistentSearch(persistentSearch);
+ setSendResponse(true);
+ }
+
+ skipPostOperation = true;
+ break searchProcessing;
+ }
+ catch (Exception e)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ }
+
+ setResultCode(DirectoryServer.getServerErrorResultCode());
+ appendErrorMessage(ERR_SEARCH_BACKEND_EXCEPTION.get(
+ getExceptionMessage(e)));
+
+ if (persistentSearch != null)
+ {
+ DirectoryServer.deregisterPersistentSearch(persistentSearch);
+ setSendResponse(true);
+ }
+
+ skipPostOperation = true;
+ break searchProcessing;
+ }
+ }
+
+
+ // Check for a request to cancel this operation.
if (getCancelRequest() != null)
{
- setCancelResult(CancelResult.CANCELED);
- setProcessingStopTime();
return;
}
- // Perform the search in the provided backend.
- backend.search(this);
- // Search in the subordinate backends is now done by the workflows.
+ // Invoke the post-operation search plugins.
+ if (! skipPostOperation)
+ {
+ PostOperationPluginResult postOperationResult =
+ pluginConfigManager.invokePostOperationSearchPlugins(this);
+ if (postOperationResult.connectionTerminated())
+ {
+ setResultCode(ResultCode.CANCELED);
+ appendErrorMessage(ERR_CANCELED_BY_POSTOP_DISCONNECT.get());
+ setProcessingStopTime();
+ return;
+ }
+ }
+ }
- // If there are any subordinate backends, then process the search there as
- // well.
- // FIXME jdemendi - From now on, do not search in the subordinate backends
- // because this is done by the workflow topology.
-// Backend[] subBackends = backend.getSubordinateBackends();
-// for (Backend b : subBackends)
-// {
-// DN[] baseDNs = b.getBaseDNs();
-// for (DN dn : baseDNs)
-// {
-// if (dn.isDescendantOf(getBaseDN()))
-// {
-// searchBackend(b);
-// break;
-// }
-// }
-// }
+
+ /**
+ * Handles any controls contained in the request.
+ *
+ * @throws DirectoryException If there is a problem with any of the request
+ * controls.
+ */
+ private void handleRequestControls()
+ throws DirectoryException
+ {
+ List<Control> requestControls = getRequestControls();
+ if ((requestControls != null) && (! requestControls.isEmpty()))
+ {
+ for (int i=0; i < requestControls.size(); i++)
+ {
+ Control c = requestControls.get(i);
+ String oid = c.getOID();
+ if (! AccessControlConfigManager.getInstance().
+ getAccessControlHandler().isAllowed(baseDN, this, c))
+ {
+ skipPostOperation = true;
+ throw new DirectoryException(ResultCode.INSUFFICIENT_ACCESS_RIGHTS,
+ ERR_CONTROL_INSUFFICIENT_ACCESS_RIGHTS.get(oid));
+ }
+
+ if (oid.equals(OID_LDAP_ASSERTION))
+ {
+ LDAPAssertionRequestControl assertControl;
+ if (c instanceof LDAPAssertionRequestControl)
+ {
+ assertControl = (LDAPAssertionRequestControl) c;
+ }
+ else
+ {
+ try
+ {
+ assertControl = LDAPAssertionRequestControl.decodeControl(c);
+ requestControls.set(i, assertControl);
+ }
+ catch (LDAPException le)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, le);
+ }
+
+ throw new DirectoryException(
+ ResultCode.valueOf(le.getResultCode()),
+ le.getMessageObject(), le);
+ }
+ }
+
+ try
+ {
+ // FIXME -- We need to determine whether the current user has
+ // permission to make this determination.
+ SearchFilter assertionFilter = assertControl.getSearchFilter();
+ Entry entry;
+ try
+ {
+ entry = DirectoryServer.getEntry(baseDN);
+ }
+ catch (DirectoryException de)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, de);
+ }
+
+ throw new DirectoryException(de.getResultCode(),
+ ERR_SEARCH_CANNOT_GET_ENTRY_FOR_ASSERTION.get(
+ de.getMessageObject()));
+ }
+
+ if (entry == null)
+ {
+ throw new DirectoryException(ResultCode.NO_SUCH_OBJECT,
+ ERR_SEARCH_NO_SUCH_ENTRY_FOR_ASSERTION.get());
+ }
+
+ if (! assertionFilter.matchesEntry(entry))
+ {
+ throw new DirectoryException(ResultCode.ASSERTION_FAILED,
+ ERR_SEARCH_ASSERTION_FAILED.get());
+ }
+ }
+ catch (DirectoryException de)
+ {
+ if (de.getResultCode() == ResultCode.ASSERTION_FAILED)
+ {
+ throw de;
+ }
+
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, de);
+ }
+
+ throw new DirectoryException(ResultCode.PROTOCOL_ERROR,
+ ERR_SEARCH_CANNOT_PROCESS_ASSERTION_FILTER.get(
+ de.getMessageObject()), de);
+ }
+ }
+ else if (oid.equals(OID_PROXIED_AUTH_V1))
+ {
+ // The requester must have the PROXIED_AUTH privilige in order to be
+ // able to use this control.
+ if (! clientConnection.hasPrivilege(Privilege.PROXIED_AUTH, this))
+ {
+ throw new DirectoryException(ResultCode.AUTHORIZATION_DENIED,
+ ERR_PROXYAUTH_INSUFFICIENT_PRIVILEGES.get());
+ }
+
+
+ ProxiedAuthV1Control proxyControl;
+ if (c instanceof ProxiedAuthV1Control)
+ {
+ proxyControl = (ProxiedAuthV1Control) c;
+ }
+ else
+ {
+ try
+ {
+ proxyControl = ProxiedAuthV1Control.decodeControl(c);
+ }
+ catch (LDAPException le)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, le);
+ }
+
+ throw new DirectoryException(
+ ResultCode.valueOf(le.getResultCode()),
+ le.getMessageObject(), le);
+ }
+ }
+
+
+ Entry authorizationEntry = proxyControl.getAuthorizationEntry();
+ setAuthorizationEntry(authorizationEntry);
+ if (authorizationEntry == null)
+ {
+ setProxiedAuthorizationDN(DN.nullDN());
+ }
+ else
+ {
+ setProxiedAuthorizationDN(authorizationEntry.getDN());
+ }
+ }
+ else if (oid.equals(OID_PROXIED_AUTH_V2))
+ {
+ // The requester must have the PROXIED_AUTH privilige in order to be
+ // able to use this control.
+ if (! clientConnection.hasPrivilege(Privilege.PROXIED_AUTH, this))
+ {
+ throw new DirectoryException(ResultCode.AUTHORIZATION_DENIED,
+ ERR_PROXYAUTH_INSUFFICIENT_PRIVILEGES.get());
+ }
+
+
+ ProxiedAuthV2Control proxyControl;
+ if (c instanceof ProxiedAuthV2Control)
+ {
+ proxyControl = (ProxiedAuthV2Control) c;
+ }
+ else
+ {
+ try
+ {
+ proxyControl = ProxiedAuthV2Control.decodeControl(c);
+ }
+ catch (LDAPException le)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, le);
+ }
+
+ throw new DirectoryException(
+ ResultCode.valueOf(le.getResultCode()),
+ le.getMessageObject(), le);
+ }
+ }
+
+
+ Entry authorizationEntry = proxyControl.getAuthorizationEntry();
+ setAuthorizationEntry(authorizationEntry);
+ if (authorizationEntry == null)
+ {
+ setProxiedAuthorizationDN(DN.nullDN());
+ }
+ else
+ {
+ setProxiedAuthorizationDN(authorizationEntry.getDN());
+ }
+ }
+ else if (oid.equals(OID_PERSISTENT_SEARCH))
+ {
+ PersistentSearchControl psearchControl;
+ if (c instanceof PersistentSearchControl)
+ {
+ psearchControl = (PersistentSearchControl) c;
+ }
+ else
+ {
+ try
+ {
+ psearchControl = PersistentSearchControl.decodeControl(c);
+ }
+ catch (LDAPException le)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, le);
+ }
+
+ throw new DirectoryException(
+ ResultCode.valueOf(le.getResultCode()),
+ le.getMessageObject(), le);
+ }
+ }
+
+ persistentSearch = new PersistentSearch(this,
+ psearchControl.getChangeTypes(),
+ psearchControl.getReturnECs());
+ setPersistentSearch(persistentSearch);
+
+ // If we're only interested in changes, then we don't actually want
+ // to process the search now.
+ if (psearchControl.getChangesOnly())
+ {
+ processSearch = false;
+ }
+ }
+ else if (oid.equals(OID_LDAP_SUBENTRIES))
+ {
+ setReturnLDAPSubentries(true);
+ }
+ else if (oid.equals(OID_MATCHED_VALUES))
+ {
+ if (c instanceof MatchedValuesControl)
+ {
+ setMatchedValuesControl((MatchedValuesControl) c);
+ }
+ else
+ {
+ try
+ {
+ MatchedValuesControl matchedValuesControl =
+ MatchedValuesControl.decodeControl(c);
+ setMatchedValuesControl(matchedValuesControl);
+ }
+ catch (LDAPException le)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, le);
+ }
+
+ throw new DirectoryException(
+ ResultCode.valueOf(le.getResultCode()),
+ le.getMessageObject(), le);
+ }
+ }
+ }
+ else if (oid.equals(OID_ACCOUNT_USABLE_CONTROL))
+ {
+ setIncludeUsableControl(true);
+ }
+ else if (oid.equals(OID_REAL_ATTRS_ONLY))
+ {
+ setRealAttributesOnly(true);
+ }
+ else if (oid.equals(OID_VIRTUAL_ATTRS_ONLY))
+ {
+ setVirtualAttributesOnly(true);
+ }
+
+ // NYI -- Add support for additional controls.
+
+ else if (c.isCritical())
+ {
+ if ((backend == null) || (! backend.supportsControl(oid)))
+ {
+ throw new DirectoryException(
+ ResultCode.UNAVAILABLE_CRITICAL_EXTENSION,
+ ERR_SEARCH_UNSUPPORTED_CRITICAL_CONTROL.get(oid));
+ }
+ }
+ }
+ }
}
}
+
diff --git a/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElement.java b/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElement.java
index 8e1b78d..2cfc903 100644
--- a/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElement.java
+++ b/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElement.java
@@ -26,108 +26,24 @@
*/
package org.opends.server.workflowelement.localbackend;
-import org.opends.messages.Message;
-import org.opends.messages.MessageBuilder;
-import static org.opends.server.config.ConfigConstants.*;
-import static org.opends.server.loggers.debug.DebugLogger.debugEnabled;
-import static org.opends.messages.CoreMessages.*;
-import static org.opends.messages.ToolMessages.*;
-import static org.opends.server.util.ServerConstants.*;
-import static org.opends.server.util.StaticUtils.getExceptionMessage;
-import static org.opends.server.util.StaticUtils.secondsToTimeString;
-import static org.opends.server.util.StaticUtils.toLowerCase;
import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.LinkedHashSet;
-import java.util.LinkedList;
import java.util.List;
-import java.util.Map;
import java.util.TreeMap;
-import java.util.concurrent.locks.Lock;
-import org.opends.server.admin.std.meta.PasswordPolicyCfgDefn;
-import org.opends.server.api.AttributeSyntax;
import org.opends.server.api.Backend;
-import org.opends.server.api.ChangeNotificationListener;
-import org.opends.server.api.ClientConnection;
-import org.opends.server.api.PasswordStorageScheme;
-import org.opends.server.api.SASLMechanismHandler;
-import org.opends.server.api.SynchronizationProvider;
-import org.opends.server.api.plugin.PostOperationPluginResult;
-import org.opends.server.api.plugin.PreOperationPluginResult;
-import org.opends.server.controls.AuthorizationIdentityResponseControl;
-import org.opends.server.controls.LDAPAssertionRequestControl;
-import org.opends.server.controls.LDAPPostReadRequestControl;
-import org.opends.server.controls.LDAPPostReadResponseControl;
-import org.opends.server.controls.LDAPPreReadRequestControl;
-import org.opends.server.controls.LDAPPreReadResponseControl;
-import org.opends.server.controls.MatchedValuesControl;
-import org.opends.server.controls.PasswordExpiredControl;
-import org.opends.server.controls.PasswordExpiringControl;
-import org.opends.server.controls.PasswordPolicyErrorType;
-import org.opends.server.controls.PasswordPolicyResponseControl;
-import org.opends.server.controls.PasswordPolicyWarningType;
-import org.opends.server.controls.PersistentSearchControl;
-import org.opends.server.controls.ProxiedAuthV1Control;
-import org.opends.server.controls.ProxiedAuthV2Control;
-import org.opends.server.core.AccessControlConfigManager;
import org.opends.server.core.AddOperation;
import org.opends.server.core.BindOperation;
import org.opends.server.core.CompareOperation;
import org.opends.server.core.DeleteOperation;
-import org.opends.server.core.DirectoryServer;
import org.opends.server.core.ModifyDNOperation;
import org.opends.server.core.ModifyOperation;
-import org.opends.server.core.PasswordPolicy;
-import org.opends.server.core.PasswordPolicyState;
-import org.opends.server.core.PersistentSearch;
-import org.opends.server.core.PluginConfigManager;
import org.opends.server.core.SearchOperation;
-import org.opends.server.loggers.debug.DebugLogger;
-import org.opends.server.loggers.debug.DebugTracer;
-import org.opends.server.protocols.asn1.ASN1OctetString;
-import org.opends.server.types.LDAPException;
-import org.opends.server.schema.AuthPasswordSyntax;
-import org.opends.server.schema.BooleanSyntax;
-import org.opends.server.schema.UserPasswordSyntax;
-import org.opends.server.types.AcceptRejectWarn;
-import org.opends.server.types.AccountStatusNotification;
-import org.opends.server.types.AccountStatusNotificationType;
-import org.opends.server.types.Attribute;
-import org.opends.server.types.AttributeType;
-import org.opends.server.types.AttributeValue;
-import org.opends.server.types.AuthenticationInfo;
-import org.opends.server.types.ByteString;
-import org.opends.server.types.CancelResult;
-import org.opends.server.types.CancelledOperationException;
-import org.opends.server.types.Control;
-import org.opends.server.types.DN;
-import org.opends.server.types.DebugLogLevel;
-import org.opends.server.types.DirectoryException;
-import org.opends.server.types.Entry;
-
-
-import org.opends.server.types.LockManager;
-import org.opends.server.types.Modification;
-import org.opends.server.types.ModificationType;
-import org.opends.server.types.ObjectClass;
import org.opends.server.types.Operation;
-import org.opends.server.types.Privilege;
-import org.opends.server.types.RDN;
-import org.opends.server.types.ResultCode;
-import org.opends.server.types.SearchFilter;
-import org.opends.server.types.SearchResultEntry;
-import org.opends.server.types.SynchronizationProviderResult;
-import org.opends.server.types.WritabilityMode;
-import org.opends.server.util.Validator;
import org.opends.server.workflowelement.LeafWorkflowElement;
-import static org.opends.server.loggers.ErrorLogger.*;
-import static org.opends.server.loggers.AccessLogger.*;
+
/**
* This class defines a local backend workflow element; e-g an entity that
@@ -135,46 +51,36 @@
*/
public class LocalBackendWorkflowElement extends LeafWorkflowElement
{
- /**
- * The tracer object for the debug logger.
- */
- private static final DebugTracer TRACER = DebugLogger.getTracer();
-
// the backend associated with the local workflow element
private Backend backend;
// the set of local backend workflow elements registered with the server
- private static
- TreeMap<String, LocalBackendWorkflowElement> registeredLocalBackends =
- new TreeMap<String, LocalBackendWorkflowElement>();
+ private static TreeMap<String, LocalBackendWorkflowElement>
+ registeredLocalBackends =
+ new TreeMap<String, LocalBackendWorkflowElement>();
// a lock to guarantee safe concurrent access to the registeredLocalBackends
// variable
private static Object registeredLocalBackendsLock = new Object();
+
/**
* Creates a new instance of the local backend workflow element.
*
* @param workflowElementID the workflow element identifier
* @param backend the backend associated to that workflow element
*/
- private LocalBackendWorkflowElement(
- String workflowElementID,
- Backend backend
- )
+ private LocalBackendWorkflowElement(String workflowElementID, Backend backend)
{
super(workflowElementID);
this.backend = backend;
-
- if (this.backend != null)
- {
- isPrivate = this.backend.isPrivateBackend();
- }
+ setPrivate(backend.isPrivateBackend());
}
+
/**
* Creates and registers a local backend with the server.
*
@@ -186,10 +92,8 @@
* already created or a newly created local backend workflow
* element.
*/
- public static LocalBackendWorkflowElement create(
- String workflowElementID,
- Backend backend
- )
+ public static LocalBackendWorkflowElement create(String workflowElementID,
+ Backend backend)
{
LocalBackendWorkflowElement localBackend = null;
@@ -197,8 +101,8 @@
localBackend = registeredLocalBackends.get(workflowElementID);
if (localBackend == null)
{
- localBackend = new LocalBackendWorkflowElement(
- workflowElementID, backend);
+ localBackend = new LocalBackendWorkflowElement(workflowElementID,
+ backend);
// store the new local backend in the list of registered backends
registerLocalBackend(localBackend);
@@ -208,19 +112,19 @@
}
+
/**
* Removes a local backend that was registered with the server.
*
* @param workflowElementID the identifier of the workflow element to remove
*/
- public static void remove(
- String workflowElementID
- )
+ public static void remove(String workflowElementID)
{
deregisterLocalBackend(workflowElementID);
}
+
/**
* Removes all the local backends that were registered with the server.
* This function is intended to be called when the server is shutting down.
@@ -238,14 +142,14 @@
}
+
/**
* Registers a local backend with the server.
*
* @param localBackend the local backend to register with the server
*/
private static void registerLocalBackend(
- LocalBackendWorkflowElement localBackend
- )
+ LocalBackendWorkflowElement localBackend)
{
synchronized (registeredLocalBackendsLock)
{
@@ -265,14 +169,13 @@
}
+
/**
* Deregisters a local backend with the server.
*
* @param workflowElementID the identifier of the workflow element to remove
*/
- private static void deregisterLocalBackend(
- String workflowElementID
- )
+ private static void deregisterLocalBackend(String workflowElementID)
{
synchronized (registeredLocalBackendsLock)
{
@@ -282,8 +185,8 @@
if (existingLocalBackend != null)
{
TreeMap<String, LocalBackendWorkflowElement> newLocalBackends =
- new TreeMap
- <String, LocalBackendWorkflowElement>(registeredLocalBackends);
+ new TreeMap<String, LocalBackendWorkflowElement>(
+ registeredLocalBackends);
newLocalBackends.remove(workflowElementID);
registeredLocalBackends = newLocalBackends;
}
@@ -291,6 +194,7 @@
}
+
/**
* {@inheritDoc}
*/
@@ -298,8432 +202,60 @@
{
switch (operation.getOperationType())
{
- case BIND:
- processBind((BindOperation) operation);
- break;
- case SEARCH:
- processSearch((SearchOperation) operation);
- break;
- case ADD:
- processAdd((AddOperation) operation);
- break;
- case DELETE:
- processDelete((DeleteOperation) operation);
- break;
- case MODIFY:
- processModify((ModifyOperation) operation);
- break;
- case MODIFY_DN:
- processModifyDN((ModifyDNOperation) operation);
- break;
- case COMPARE:
- processCompare((CompareOperation) operation);
- break;
- case ABANDON:
- // There is no processing for an abandon operation.
- break;
- default:
- // jdemendi - temporary code, just make sure that we are not falling
- // into that incomplete code...
- Validator.ensureTrue(false);
- break;
+ case BIND:
+ LocalBackendBindOperation bindOperation =
+ new LocalBackendBindOperation((BindOperation) operation);
+ bindOperation.processLocalBind(backend);
+ break;
+
+ case SEARCH:
+ LocalBackendSearchOperation searchOperation =
+ new LocalBackendSearchOperation((SearchOperation) operation);
+ searchOperation.processLocalSearch(backend);
+ break;
+
+ case ADD:
+ LocalBackendAddOperation addOperation =
+ new LocalBackendAddOperation((AddOperation) operation);
+ addOperation.processLocalAdd(backend);
+ break;
+
+ case DELETE:
+ LocalBackendDeleteOperation deleteOperation =
+ new LocalBackendDeleteOperation((DeleteOperation) operation);
+ deleteOperation.processLocalDelete(backend);
+ break;
+
+ case MODIFY:
+ LocalBackendModifyOperation modifyOperation =
+ new LocalBackendModifyOperation((ModifyOperation) operation);
+ modifyOperation.processLocalModify(backend);
+ break;
+
+ case MODIFY_DN:
+ LocalBackendModifyDNOperation modifyDNOperation =
+ new LocalBackendModifyDNOperation((ModifyDNOperation) operation);
+ modifyDNOperation.processLocalModifyDN(backend);
+ break;
+
+ case COMPARE:
+ LocalBackendCompareOperation compareOperation =
+ new LocalBackendCompareOperation((CompareOperation) operation);
+ compareOperation.processLocalCompare(backend);
+ break;
+
+ case ABANDON:
+ // There is no processing for an abandon operation.
+ break;
+
+ default:
+ throw new AssertionError("Attempted to execute an invalid operation " +
+ "type: " + operation.getOperationType() +
+ " (" + operation + ")");
}
}
- /**
- * Perform a modify operation against a local backend.
- *
- * @param operation - The operation to perform
- */
- public void processModify(ModifyOperation operation)
- {
- LocalBackendModifyOperation localOperation =
- new LocalBackendModifyOperation(operation);
-
- processLocalModify(localOperation);
- }
-
- /**
- * Perform a local modify operation against the local backend.
- *
- * @param localOp - The operation to perform
- */
- private void processLocalModify(LocalBackendModifyOperation localOp)
- {
- ClientConnection clientConnection = localOp.getClientConnection();
-
- // Get the plugin config manager that will be used for invoking plugins.
- PluginConfigManager pluginConfigManager =
- DirectoryServer.getPluginConfigManager();
- boolean skipPostOperation = false;
-
- // Create a labeled block of code that we can break out of if a problem is
- // detected.
- boolean pwPolicyControlRequested = false;
- PasswordPolicyErrorType pwpErrorType = null;
- modifyProcessing:
- {
- DN entryDN = localOp.getEntryDN();
- if (entryDN == null){
- break modifyProcessing;
- }
-
- // Process the modifications to convert them from their raw form to the
- // form required for the rest of the modify processing.
- List<Modification> modifications = localOp.getModifications();
- if (modifications == null)
- {
- break modifyProcessing;
- }
-
- if (modifications.isEmpty())
- {
- localOp.setResultCode(ResultCode.CONSTRAINT_VIOLATION);
- localOp.appendErrorMessage(
- ERR_MODIFY_NO_MODIFICATIONS.get(String.valueOf(entryDN)));
- break modifyProcessing;
- }
-
-
- // If the user must change their password before doing anything else, and
- // if the target of the modify operation isn't the user's own entry, then
- // reject the request.
- if ((! localOp.isInternalOperation()) &&
- clientConnection.mustChangePassword())
- {
- DN authzDN = localOp.getAuthorizationDN();
- if ((authzDN != null) && (! authzDN.equals(entryDN)))
- {
- // The user will not be allowed to do anything else before the
- // password gets changed. Also note that we haven't yet checked the
- // request controls so we need to do that now to see if the password
- // policy request control was provided.
- for (Control c : localOp.getRequestControls())
- {
- if (c.getOID().equals(OID_PASSWORD_POLICY_CONTROL))
- {
- pwPolicyControlRequested = true;
- pwpErrorType = PasswordPolicyErrorType.CHANGE_AFTER_RESET;
- break;
- }
- }
-
- localOp.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
-
-
- localOp.appendErrorMessage(ERR_MODIFY_MUST_CHANGE_PASSWORD.get());
- break modifyProcessing;
- }
- }
-
-
- // Check for a request to cancel this operation.
- if (localOp.getCancelRequest() != null)
- {
- return;
- }
-
- // Acquire a write lock on the target entry.
- Lock entryLock = null;
- for (int i=0; i < 3; i++)
- {
- entryLock = LockManager.lockWrite(entryDN);
- if (entryLock != null)
- {
- break;
- }
- }
-
- if (entryLock == null)
- {
- localOp.setResultCode(DirectoryServer.getServerErrorResultCode());
- localOp.appendErrorMessage(
- ERR_MODIFY_CANNOT_LOCK_ENTRY.get(String.valueOf(entryDN)));
-
- skipPostOperation = true;
- break modifyProcessing;
- }
-
-
- try
- {
- // Check for a request to cancel this operation.
- if (localOp.getCancelRequest() != null)
- {
- return;
- }
-
-
- // Get the entry to modify. If it does not exist, then fail.
- Entry currentEntry = null;
- try
- {
- currentEntry = backend.getEntry(entryDN);
- localOp.setCurrentEntry(currentEntry);
- }
- catch (DirectoryException de)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, de);
- }
-
- localOp.setResponseData(de);
- break modifyProcessing;
- }
-
- if (currentEntry == null)
- {
- localOp.setResultCode(ResultCode.NO_SUCH_OBJECT);
- localOp.appendErrorMessage(ERR_MODIFY_NO_SUCH_ENTRY.get(
- String.valueOf(entryDN)));
-
- // See if one of the entry's ancestors exists.
- DN parentDN = entryDN.getParentDNInSuffix();
- while (parentDN != null)
- {
- try
- {
- if (DirectoryServer.entryExists(parentDN))
- {
- localOp.setMatchedDN(parentDN);
- break;
- }
- }
- catch (Exception e)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, e);
- }
- break;
- }
-
- parentDN = parentDN.getParentDNInSuffix();
- }
-
- break modifyProcessing;
- }
-
- // Check to see if there are any controls in the request. If so, then
- // see if there is any special processing required.
- boolean noOp = false;
- LDAPPreReadRequestControl preReadRequest = null;
- LDAPPostReadRequestControl postReadRequest = null;
- List<Control> requestControls = localOp.getRequestControls();
- if ((requestControls != null) && (! requestControls.isEmpty()))
- {
- for (int i=0; i < requestControls.size(); i++)
- {
- Control c = requestControls.get(i);
- String oid = c.getOID();
-
- if (!AccessControlConfigManager.getInstance().
- getAccessControlHandler().
- isAllowed(entryDN, localOp, c))
- {
- localOp.setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS);
-
- localOp.appendErrorMessage(
- ERR_CONTROL_INSUFFICIENT_ACCESS_RIGHTS.get(oid));
- skipPostOperation = true;
- break modifyProcessing;
- }
-
- if (oid.equals(OID_LDAP_ASSERTION))
- {
- LDAPAssertionRequestControl assertControl;
- if (c instanceof LDAPAssertionRequestControl)
- {
- assertControl = (LDAPAssertionRequestControl) c;
- }
- else
- {
- try
- {
- assertControl = LDAPAssertionRequestControl.decodeControl(c);
- requestControls.set(i, assertControl);
- }
- catch (LDAPException le)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, le);
- }
-
- localOp.setResultCode(ResultCode.valueOf(le.getResultCode()));
- localOp.appendErrorMessage(le.getMessageObject());
-
- break modifyProcessing;
- }
- }
-
- try
- {
- // FIXME -- We need to determine whether the current user has
- // permission to make this determination.
- SearchFilter filter = assertControl.getSearchFilter();
- if (! filter.matchesEntry(currentEntry))
- {
- localOp.setResultCode(ResultCode.ASSERTION_FAILED);
-
- localOp.appendErrorMessage(
- ERR_MODIFY_ASSERTION_FAILED.get(String.valueOf(entryDN)));
-
- break modifyProcessing;
- }
- }
- catch (DirectoryException de)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, de);
- }
-
- localOp.setResultCode(ResultCode.PROTOCOL_ERROR);
-
- localOp.appendErrorMessage(
- ERR_MODIFY_CANNOT_PROCESS_ASSERTION_FILTER.get(
- String.valueOf(entryDN),
- de.getMessageObject()));
-
- break modifyProcessing;
- }
- }
- else if (oid.equals(OID_LDAP_NOOP_OPENLDAP_ASSIGNED))
- {
- noOp = true;
- }
- else if (oid.equals(OID_LDAP_READENTRY_PREREAD))
- {
- if (c instanceof LDAPPreReadRequestControl)
- {
- preReadRequest = (LDAPPreReadRequestControl) c;
- }
- else
- {
- try
- {
- preReadRequest = LDAPPreReadRequestControl.decodeControl(c);
- requestControls.set(i, preReadRequest);
- }
- catch (LDAPException le)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, le);
- }
-
- localOp.setResultCode(ResultCode.valueOf(le.getResultCode()));
- localOp.appendErrorMessage(le.getMessageObject());
-
- break modifyProcessing;
- }
- }
- }
- else if (oid.equals(OID_LDAP_READENTRY_POSTREAD))
- {
- if (c instanceof LDAPPostReadRequestControl)
- {
- postReadRequest = (LDAPPostReadRequestControl) c;
- }
- else
- {
- try
- {
- postReadRequest = LDAPPostReadRequestControl.decodeControl(c);
- requestControls.set(i, postReadRequest);
- }
- catch (LDAPException le)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, le);
- }
-
- localOp.setResultCode(ResultCode.valueOf(le.getResultCode()));
- localOp.appendErrorMessage(le.getMessageObject());
-
- break modifyProcessing;
- }
- }
- }
- else if (oid.equals(OID_PROXIED_AUTH_V1))
- {
- // The requester must have the PROXIED_AUTH privilige in order to
- // be able to use this control.
- if (! clientConnection.hasPrivilege(Privilege.PROXIED_AUTH,
- localOp))
- {
- localOp.appendErrorMessage(
- ERR_PROXYAUTH_INSUFFICIENT_PRIVILEGES.get());
- localOp.setResultCode(ResultCode.AUTHORIZATION_DENIED);
- break modifyProcessing;
- }
-
-
- ProxiedAuthV1Control proxyControl;
- if (c instanceof ProxiedAuthV1Control)
- {
- proxyControl = (ProxiedAuthV1Control) c;
- }
- else
- {
- try
- {
- proxyControl = ProxiedAuthV1Control.decodeControl(c);
- }
- catch (LDAPException le)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, le);
- }
-
- localOp.setResultCode(ResultCode.valueOf(le.getResultCode()));
- localOp.appendErrorMessage(le.getMessageObject());
-
- break modifyProcessing;
- }
- }
-
-
- Entry authorizationEntry;
- try
- {
- authorizationEntry = proxyControl.getAuthorizationEntry();
- }
- catch (DirectoryException de)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, de);
- }
-
- localOp.setResultCode(de.getResultCode());
- localOp.appendErrorMessage(de.getMessageObject());
-
- break modifyProcessing;
- }
-
- localOp.setAuthorizationEntry(authorizationEntry);
- if (authorizationEntry == null)
- {
- localOp.setProxiedAuthorizationDN(DN.nullDN());
- }
- else
- {
- localOp.setProxiedAuthorizationDN(authorizationEntry.getDN());
- }
- }
- else if (oid.equals(OID_PROXIED_AUTH_V2))
- {
- // The requester must have the PROXIED_AUTH privilige in order to
- // be able to use this control.
- if (! clientConnection.hasPrivilege(Privilege.PROXIED_AUTH,
- localOp))
- {
- localOp.appendErrorMessage(
- ERR_PROXYAUTH_INSUFFICIENT_PRIVILEGES.get());
- localOp.setResultCode(ResultCode.AUTHORIZATION_DENIED);
- break modifyProcessing;
- }
-
-
- ProxiedAuthV2Control proxyControl;
- if (c instanceof ProxiedAuthV2Control)
- {
- proxyControl = (ProxiedAuthV2Control) c;
- }
- else
- {
- try
- {
- proxyControl = ProxiedAuthV2Control.decodeControl(c);
- }
- catch (LDAPException le)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, le);
- }
-
- localOp.setResultCode(ResultCode.valueOf(le.getResultCode()));
- localOp.appendErrorMessage(le.getMessageObject());
-
- break modifyProcessing;
- }
- }
-
-
- Entry authorizationEntry;
- try
- {
- authorizationEntry = proxyControl.getAuthorizationEntry();
- }
- catch (DirectoryException de)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, de);
- }
-
- localOp.setResultCode(de.getResultCode());
- localOp.appendErrorMessage(de.getMessageObject());
-
- break modifyProcessing;
- }
-
- localOp.setAuthorizationEntry(authorizationEntry);
- if (authorizationEntry == null)
- {
- localOp.setProxiedAuthorizationDN(DN.nullDN());
- }
- else
- {
- localOp.setProxiedAuthorizationDN(authorizationEntry.getDN());
- }
- }
- else if (oid.equals(OID_PASSWORD_POLICY_CONTROL))
- {
- pwPolicyControlRequested = true;
- }
-
- // NYI -- Add support for additional controls.
- else if (c.isCritical())
- {
- if ((backend == null) || (! backend.supportsControl(oid)))
- {
- localOp.setResultCode(
- ResultCode.UNAVAILABLE_CRITICAL_EXTENSION);
-
- localOp.appendErrorMessage(
- ERR_MODIFY_UNSUPPORTED_CRITICAL_CONTROL.get(
- String.valueOf(entryDN),
- oid));
-
- break modifyProcessing;
- }
- }
- }
- }
-
-
- // Get the password policy state object for the entry that can be used
- // to perform any appropriate password policy processing. Also, see if
- // the entry is being updated by the end user or an administrator.
- PasswordPolicyState pwPolicyState;
- boolean selfChange = entryDN.equals(localOp.getAuthorizationDN());
- try
- {
- // FIXME -- Need a way to enable debug mode.
- pwPolicyState = new PasswordPolicyState(currentEntry, false, false);
- }
- catch (DirectoryException de)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, de);
- }
-
- localOp.setResultCode(de.getResultCode());
- localOp.appendErrorMessage(de.getMessageObject());
-
- break modifyProcessing;
- }
-
-
- // Create a duplicate of the entry and apply the changes to it.
- Entry modifiedEntry = currentEntry.duplicate(false);
- localOp.setModifiedEntry(modifiedEntry);
-
- if (! noOp)
- {
- // Invoke any conflict resolution processing that might be needed by
- // the synchronization provider.
- for (SynchronizationProvider provider :
- DirectoryServer.getSynchronizationProviders())
- {
- try
- {
- SynchronizationProviderResult result =
- provider.handleConflictResolution(localOp);
- if (! result.continueOperationProcessing())
- {
- break modifyProcessing;
- }
- }
- catch (DirectoryException de)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, de);
- }
-
- logError(ERR_MODIFY_SYNCH_CONFLICT_RESOLUTION_FAILED.
- get(localOp.getConnectionID(), localOp.getOperationID(),
- getExceptionMessage(de)));
-
- localOp.setResponseData(de);
- break modifyProcessing;
- }
- }
- }
-
-
- // Declare variables used for password policy state processing.
- boolean passwordChanged = false;
- boolean currentPasswordProvided = false;
- boolean isEnabled = true;
- boolean enabledStateChanged = false;
- int numPasswords;
- if (currentEntry.hasAttribute(
- pwPolicyState.getPolicy().getPasswordAttribute()))
- {
- // It may actually have more than one, but we can't tell the
- // difference if the values are encoded, and its enough for our
- // purposes just to know that there is at least one.
- numPasswords = 1;
- }
- else
- {
- numPasswords = 0;
- }
-
-
- // If it's not an internal or synchronization operation, then iterate
- // through the set of modifications to see if a password is included in
- // the changes. If so, then add the appropriate state changes to the
- // set of modifications.
- if (! (localOp.isInternalOperation()
- || localOp.isSynchronizationOperation()))
- {
- for (Modification m : modifications)
- {
- if (m.getAttribute().getAttributeType().equals(
- pwPolicyState.getPolicy().getPasswordAttribute()))
- {
- passwordChanged = true;
- if (! selfChange)
- {
- if (! clientConnection.hasPrivilege(
- Privilege.PASSWORD_RESET,
- localOp))
- {
- pwpErrorType =
- PasswordPolicyErrorType.PASSWORD_MOD_NOT_ALLOWED;
-
- localOp.appendErrorMessage(
- ERR_MODIFY_PWRESET_INSUFFICIENT_PRIVILEGES.get());
- localOp.setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS);
- break modifyProcessing;
- }
- }
-
- break;
- }
- }
- }
-
-
- for (Modification m : modifications)
- {
- Attribute a = m.getAttribute();
- AttributeType t = a.getAttributeType();
-
-
- // If the attribute type is marked "NO-USER-MODIFICATION" then fail
- // unless this is an internal operation or is related to
- // synchronization in some way.
- if (t.isNoUserModification())
- {
- if (! (localOp.isInternalOperation() ||
- localOp.isSynchronizationOperation() ||
- m.isInternal()))
- {
- localOp.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
- localOp.appendErrorMessage(
- ERR_MODIFY_ATTR_IS_NO_USER_MOD.get(String.valueOf(entryDN),
- a.getName()));
- break modifyProcessing;
- }
- }
-
- // If the attribute type is marked "OBSOLETE" and the modification
- // is setting new values, then fail unless this is an internal
- // operation or is related to synchronization in some way.
- if (t.isObsolete())
- {
- if (a.hasValue() &&
- (m.getModificationType() != ModificationType.DELETE))
- {
- if (! (localOp.isInternalOperation() ||
- localOp.isSynchronizationOperation() ||
- m.isInternal()))
- {
- localOp.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
- localOp.appendErrorMessage(
- ERR_MODIFY_ATTR_IS_OBSOLETE.get(String.valueOf(entryDN),
- a.getName()));
- break modifyProcessing;
- }
- }
- }
-
-
- // See if the attribute is one which controls the privileges available
- // for a user. If it is, then the client must have the
- // PRIVILEGE_CHANGE privilege.
- if (t.hasName(OP_ATTR_PRIVILEGE_NAME))
- {
- if (! clientConnection.hasPrivilege(Privilege.PRIVILEGE_CHANGE,
- localOp))
- {
- localOp.appendErrorMessage(
- ERR_MODIFY_CHANGE_PRIVILEGE_INSUFFICIENT_PRIVILEGES
- .get());
- localOp.setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS);
- break modifyProcessing;
- }
- }
-
-
- // If the modification is updating the password attribute, then
- // perform any necessary password policy processing. This processing
- // should be skipped for synchronization operations.
- boolean isPassword
- = t.equals(pwPolicyState.getPolicy().getPasswordAttribute());
- if (isPassword && (!(localOp.isSynchronizationOperation())))
- {
- // If the attribute contains any options, then reject it. Passwords
- // will not be allowed to have options. Skipped for internal
- // operations.
- if(!localOp.isInternalOperation())
- {
- if (a.hasOptions())
- {
- localOp.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
-
- localOp.appendErrorMessage(
- ERR_MODIFY_PASSWORDS_CANNOT_HAVE_OPTIONS.get());
- break modifyProcessing;
- }
-
-
- // If it's a self change, then see if that's allowed.
- if (selfChange &&
- (! pwPolicyState.getPolicy().allowUserPasswordChanges()))
- {
- pwpErrorType = PasswordPolicyErrorType.PASSWORD_MOD_NOT_ALLOWED;
-
- localOp.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
-
- localOp.appendErrorMessage(
- ERR_MODIFY_NO_USER_PW_CHANGES.get());
- break modifyProcessing;
- }
-
-
- // If we require secure password changes, then makes sure it's a
- // secure communication channel.
- if (pwPolicyState.getPolicy().requireSecurePasswordChanges() &&
- (! clientConnection.isSecure()))
- {
- pwpErrorType = PasswordPolicyErrorType.PASSWORD_MOD_NOT_ALLOWED;
-
- localOp.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
-
- localOp.appendErrorMessage(
- ERR_MODIFY_REQUIRE_SECURE_CHANGES.get());
- break modifyProcessing;
- }
-
-
- // If it's a self change and it's not been long enough since the
- // previous change, then reject it.
- if (selfChange && pwPolicyState.isWithinMinimumAge())
- {
- pwpErrorType = PasswordPolicyErrorType.PASSWORD_TOO_YOUNG;
-
- localOp.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
-
- localOp.appendErrorMessage(ERR_MODIFY_WITHIN_MINIMUM_AGE.get());
- break modifyProcessing;
- }
- }
-
- // Check to see whether this will adding, deleting, or replacing
- // password values (increment doesn't make any sense for passwords).
- // Then perform the appropriate type of processing for that kind of
- // modification.
- boolean isAdd = false;
- LinkedHashSet<AttributeValue> pwValues = a.getValues();
- LinkedHashSet<AttributeValue> encodedValues =
- new LinkedHashSet<AttributeValue>();
- switch (m.getModificationType())
- {
- case ADD:
- case REPLACE:
- int passwordsToAdd = pwValues.size();
-
- if (m.getModificationType() == ModificationType.ADD)
- {
- numPasswords += passwordsToAdd;
- isAdd = true;
- }
- else
- {
- numPasswords = passwordsToAdd;
- }
- // If there were multiple password values provided, then make
- // sure that's OK.
-
- if ((! localOp.isInternalOperation()) &&
- (! pwPolicyState.getPolicy().allowMultiplePasswordValues()) &&
- (passwordsToAdd > 1))
- {
- pwpErrorType = PasswordPolicyErrorType.PASSWORD_MOD_NOT_ALLOWED;
-
- localOp.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
-
- localOp.appendErrorMessage(
- ERR_MODIFY_MULTIPLE_VALUES_NOT_ALLOWED.get());
- break modifyProcessing;
- }
-
- // Iterate through the password values and see if any of them
- // are pre-encoded. If so, then check to see if we'll allow it.
- // Otherwise, store the clear-text values for later validation
- // and update the attribute with the encoded values.
- for (AttributeValue v : pwValues)
- {
- if (pwPolicyState.passwordIsPreEncoded(v.getValue()))
- {
- if ((!localOp.isInternalOperation()) &&
- ! pwPolicyState.getPolicy().allowPreEncodedPasswords())
- {
- pwpErrorType =
- PasswordPolicyErrorType.INSUFFICIENT_PASSWORD_QUALITY;
-
- localOp.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
-
- localOp.appendErrorMessage(
- ERR_MODIFY_NO_PREENCODED_PASSWORDS.get());
- break modifyProcessing;
- }
- else
- {
- encodedValues.add(v);
- }
- }
- else
- {
- if (isAdd)
- {
- // Make sure that the password value doesn't already
- // exist.
- if (pwPolicyState.passwordMatches(v.getValue()))
- {
- pwpErrorType =
- PasswordPolicyErrorType.PASSWORD_IN_HISTORY;
-
- localOp.setResultCode(
- ResultCode.ATTRIBUTE_OR_VALUE_EXISTS);
-
- localOp.appendErrorMessage(
- ERR_MODIFY_PASSWORD_EXISTS.get());
- break modifyProcessing;
- }
- }
-
- List<AttributeValue> newPasswords =
- localOp.getNewPasswords() ;
- if (newPasswords == null)
- {
- newPasswords = new LinkedList<AttributeValue>();
- localOp.setNewPasswords(newPasswords);
- }
-
- newPasswords.add(v);
-
- try
- {
- for (ByteString s :
- pwPolicyState.encodePassword(v.getValue()))
- {
- encodedValues.add(new AttributeValue(
- a.getAttributeType(), s));
- }
- }
- catch (DirectoryException de)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, de);
- }
-
- localOp.setResultCode(de.getResultCode());
- localOp.appendErrorMessage(de.getMessageObject());
- break modifyProcessing;
- }
- }
- }
-
- a.setValues(encodedValues);
-
- break;
-
- case DELETE:
- // Iterate through the password values and see if any of them
- // are pre-encoded. We will never allow pre-encoded passwords
- // for user password changes, but we will allow them for
- // administrators. For each clear-text value, verify that at
- // least one value in the entry matches and replace the
- // clear-text value with the appropriate encoded forms.
- for (AttributeValue v : pwValues)
- {
- if (pwPolicyState.passwordIsPreEncoded(v.getValue()))
- {
- if ((!localOp.isInternalOperation()) && selfChange)
- {
- pwpErrorType =
- PasswordPolicyErrorType.INSUFFICIENT_PASSWORD_QUALITY;
-
- localOp.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
-
- localOp.appendErrorMessage(
- ERR_MODIFY_NO_PREENCODED_PASSWORDS.get());
- break modifyProcessing;
- }
- else
- {
- encodedValues.add(v);
- }
- }
- else
- {
- List<Attribute> attrList = currentEntry.getAttribute(t);
- if ((attrList == null) || (attrList.isEmpty()))
- {
- localOp.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
-
- localOp.appendErrorMessage(
- ERR_MODIFY_NO_EXISTING_VALUES.get());
- break modifyProcessing;
- }
- boolean found = false;
- for (Attribute attr : attrList)
- {
- for (AttributeValue av : attr.getValues())
- {
- if (pwPolicyState.getPolicy().usesAuthPasswordSyntax())
- {
- if (AuthPasswordSyntax.isEncoded(av.getValue()))
- {
- try
- {
- StringBuilder[] compoenents =
- AuthPasswordSyntax.decodeAuthPassword(
- av.getStringValue());
- PasswordStorageScheme scheme =
- DirectoryServer.
- getAuthPasswordStorageScheme(
- compoenents[0].toString());
- if (scheme != null)
- {
- if (scheme.authPasswordMatches(
- v.getValue(),
- compoenents[1].toString(),
- compoenents[2].toString()))
- {
- encodedValues.add(av);
- found = true;
- }
- }
- }
- catch (DirectoryException de)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(
- DebugLogLevel.ERROR, de);
- }
-
- localOp.setResultCode(de.getResultCode());
-
- localOp.appendErrorMessage(
- ERR_MODIFY_CANNOT_DECODE_PW.get(
- de.getMessageObject()));
- break modifyProcessing;
- }
- }
- else
- {
- if (av.equals(v))
- {
- encodedValues.add(v);
- found = true;
- }
- }
- }
- else
- {
- if (UserPasswordSyntax.isEncoded(av.getValue()))
- {
- try
- {
- String[] compoenents =
- UserPasswordSyntax.decodeUserPassword(
- av.getStringValue());
- PasswordStorageScheme scheme =
- DirectoryServer.getPasswordStorageScheme(
- toLowerCase(compoenents[0]));
- if (scheme != null)
- {
- if (scheme.passwordMatches(
- v.getValue(),
- new ASN1OctetString(compoenents[1])))
- {
- encodedValues.add(av);
- found = true;
- }
- }
- }
- catch (DirectoryException de)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(
- DebugLogLevel.ERROR, de);
- }
-
- localOp.setResultCode(de.getResultCode());
-
- localOp.appendErrorMessage(
- ERR_MODIFY_CANNOT_DECODE_PW.get(
- de.getMessageObject()));
- break modifyProcessing;
- }
- }
- else
- {
- if (av.equals(v))
- {
- encodedValues.add(v);
- found = true;
- }
- }
- }
- }
- }
-
- if (found)
- {
- List<AttributeValue> currentPasswords =
- localOp.getCurrentPasswords();
- if (currentPasswords == null)
- {
- currentPasswords = new LinkedList<AttributeValue>();
- localOp.setCurrentPasswords(currentPasswords);
- }
- currentPasswords.add(v);
-
- numPasswords--;
- }
- else
- {
- localOp.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
-
- localOp.appendErrorMessage(
- ERR_MODIFY_INVALID_PASSWORD.get());
- break modifyProcessing;
- }
-
- currentPasswordProvided = true;
- }
- }
-
- a.setValues(encodedValues);
-
- break;
-
- default:
- localOp.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
-
- localOp.appendErrorMessage(
- ERR_MODIFY_INVALID_MOD_TYPE_FOR_PASSWORD.get(
- String.valueOf(m.getModificationType()),
- a.getName()));
-
- break modifyProcessing;
- }
- }
- else
- {
- // See if it's an attribute used to maintain the account
- // enabled/disabled state.
- AttributeType disabledAttr =
- DirectoryServer.getAttributeType(OP_ATTR_ACCOUNT_DISABLED,
- true);
- if (t.equals(disabledAttr))
- {
- enabledStateChanged = true;
- for (AttributeValue v : a.getValues())
- {
- try
- {
- isEnabled = (! BooleanSyntax.decodeBooleanValue(
- v.getNormalizedValue()));
- }
- catch (DirectoryException de)
- {
- localOp.setResultCode(ResultCode.INVALID_ATTRIBUTE_SYNTAX);
-
- Message message =
- ERR_MODIFY_INVALID_DISABLED_VALUE.get(
- OP_ATTR_ACCOUNT_DISABLED,
- String.valueOf(de.getMessageObject()));
- localOp.appendErrorMessage(message);
- break modifyProcessing;
- }
- }
- }
- }
-
-
- switch (m.getModificationType())
- {
- case ADD:
- // Make sure that one or more values have been provided for the
- // attribute.
- LinkedHashSet<AttributeValue> newValues = a.getValues();
- if ((newValues == null) || newValues.isEmpty())
- {
- localOp.setResultCode(ResultCode.PROTOCOL_ERROR);
- localOp.appendErrorMessage(ERR_MODIFY_ADD_NO_VALUES.get(
- String.valueOf(entryDN),
- a.getName()));
- break modifyProcessing;
- }
-
- // If the server is configured to check schema and the
- // operation is not a synchronization operation,
- // make sure that all the new values are valid according to the
- // associated syntax.
- if ((DirectoryServer.checkSchema()) &&
- (!localOp.isSynchronizationOperation()) )
- {
- AcceptRejectWarn syntaxPolicy =
- DirectoryServer.getSyntaxEnforcementPolicy();
- AttributeSyntax syntax = t.getSyntax();
-
- if (syntaxPolicy == AcceptRejectWarn.REJECT)
- {
- MessageBuilder invalidReason =
- new MessageBuilder();
-
- for (AttributeValue v : newValues)
- {
- if (! syntax.valueIsAcceptable(v.getValue(), invalidReason))
- {
- localOp.setResultCode(ResultCode.INVALID_ATTRIBUTE_SYNTAX);
-
-
- localOp.appendErrorMessage(ERR_MODIFY_ADD_INVALID_SYNTAX
- .get(String.valueOf(entryDN),
- a.getName(),
- v.getStringValue(),
- invalidReason.toString()));
-
- break modifyProcessing;
- }
- }
- }
- else if (syntaxPolicy == AcceptRejectWarn.WARN)
- {
- MessageBuilder invalidReason = new MessageBuilder();
-
- for (AttributeValue v : newValues)
- {
- if (! syntax.valueIsAcceptable(v.getValue(), invalidReason))
- {
- localOp.setResultCode(
- ResultCode.INVALID_ATTRIBUTE_SYNTAX);
-
- logError(
- ERR_MODIFY_ADD_INVALID_SYNTAX.
- get(String.valueOf(entryDN), a.getName(),
- v.getStringValue(), invalidReason.toString()));
-
- invalidReason = new MessageBuilder();
- }
- }
- }
- }
-
-
- // Add the provided attribute or merge an existing attribute with
- // the values of the new attribute. If there are any duplicates,
- // then fail.
- LinkedList<AttributeValue> duplicateValues =
- new LinkedList<AttributeValue>();
- if (a.getAttributeType().isObjectClassType())
- {
- try
- {
- modifiedEntry.addObjectClasses(newValues);
- break;
- }
- catch (DirectoryException de)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, de);
- }
-
- localOp.setResponseData(de);
- break modifyProcessing;
- }
- }
- else
- {
- modifiedEntry.addAttribute(a, duplicateValues);
- if (duplicateValues.isEmpty())
- {
- break;
- }
- else
- {
- StringBuilder buffer = new StringBuilder();
- Iterator<AttributeValue> iterator =
- duplicateValues.iterator();
- buffer.append(iterator.next().getStringValue());
- while (iterator.hasNext())
- {
- buffer.append(", ");
- buffer.append(iterator.next().getStringValue());
- }
-
- localOp.setResultCode(ResultCode.ATTRIBUTE_OR_VALUE_EXISTS);
-
- localOp.appendErrorMessage(
- ERR_MODIFY_ADD_DUPLICATE_VALUE.get(String.valueOf(entryDN),
- a.getName(),
- buffer.toString()));
-
- break modifyProcessing;
- }
- }
-
-
- case DELETE:
- // Remove the specified attribute values or the entire attribute
- // from the value. If there are any specified values that were
- // not present, then fail. If the RDN attribute value would be
- // removed, then fail.
- LinkedList<AttributeValue> missingValues =
- new LinkedList<AttributeValue>();
- boolean attrExists =
- modifiedEntry.removeAttribute(a, missingValues);
-
- if (attrExists)
- {
- if (missingValues.isEmpty())
- {
- RDN rdn = modifiedEntry.getDN().getRDN();
- if ((rdn != null) && rdn.hasAttributeType(t) &&
- (! modifiedEntry.hasValue(t, a.getOptions(),
- rdn.getAttributeValue(t))))
- {
- localOp.setResultCode(ResultCode.NOT_ALLOWED_ON_RDN);
-
- localOp.appendErrorMessage(ERR_MODIFY_DELETE_RDN_ATTR.get(
- String.valueOf(entryDN),
- a.getName()));
- break modifyProcessing;
- }
-
- break;
- }
- else
- {
- StringBuilder buffer = new StringBuilder();
- Iterator<AttributeValue> iterator = missingValues.iterator();
- buffer.append(iterator.next().getStringValue());
- while (iterator.hasNext())
- {
- buffer.append(", ");
- buffer.append(iterator.next().getStringValue());
- }
-
- localOp.setResultCode(ResultCode.NO_SUCH_ATTRIBUTE);
-
- localOp.appendErrorMessage(
- ERR_MODIFY_DELETE_MISSING_VALUES.get(
- String.valueOf(entryDN),
- a.getName(),
- buffer.toString()));
-
- break modifyProcessing;
- }
- }
- else
- {
- localOp.setResultCode(ResultCode.NO_SUCH_ATTRIBUTE);
-
- localOp.appendErrorMessage(
- ERR_MODIFY_DELETE_NO_SUCH_ATTR.get(String.valueOf(entryDN),
- a.getName()));
- break modifyProcessing;
- }
-
-
- case REPLACE:
- // If it is the objectclass attribute, then treat that separately.
- if (a.getAttributeType().isObjectClassType())
- {
- try
- {
- modifiedEntry.setObjectClasses(a.getValues());
- break;
- }
- catch (DirectoryException de)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, de);
- }
-
- localOp.setResponseData(de);
- break modifyProcessing;
- }
- }
-
-
- // If the provided attribute does not have any values, then we
- // will simply remove the attribute from the entry (if it exists).
- if (! a.hasValue())
- {
- modifiedEntry.removeAttribute(t, a.getOptions());
- RDN rdn = modifiedEntry.getDN().getRDN();
- if ((rdn != null) && rdn.hasAttributeType(t) &&
- (! modifiedEntry.hasValue(t, a.getOptions(),
- rdn.getAttributeValue(t))))
- {
- localOp.setResultCode(ResultCode.NOT_ALLOWED_ON_RDN);
-
- localOp.appendErrorMessage(
- ERR_MODIFY_DELETE_RDN_ATTR.get(String.valueOf(entryDN),
- a.getName()));
- break modifyProcessing;
- }
- break;
- }
-
- // If the server is configured to check schema and the
- // operation is not a synchronization operation,
- // make sure that all the new values are valid according to the
- // associated syntax.
- newValues = a.getValues();
- if ((DirectoryServer.checkSchema()) &&
- (!localOp.isSynchronizationOperation()) )
- {
- AcceptRejectWarn syntaxPolicy =
- DirectoryServer.getSyntaxEnforcementPolicy();
- AttributeSyntax syntax = t.getSyntax();
-
- if (syntaxPolicy == AcceptRejectWarn.REJECT)
- {
- MessageBuilder invalidReason = new MessageBuilder();
-
- for (AttributeValue v : newValues)
- {
- if (! syntax.valueIsAcceptable(v.getValue(), invalidReason))
- {
- localOp.setResultCode(ResultCode.INVALID_ATTRIBUTE_SYNTAX);
-
- localOp.appendErrorMessage(
- ERR_MODIFY_REPLACE_INVALID_SYNTAX.get(
- String.valueOf(entryDN),
- a.getName(),
- v.getStringValue(),
- invalidReason.toString()));
-
- break modifyProcessing;
- }
- }
- }
- else if (syntaxPolicy == AcceptRejectWarn.WARN)
- {
- MessageBuilder invalidReason = new MessageBuilder();
- for (AttributeValue v : newValues)
- {
- if (! syntax.valueIsAcceptable(v.getValue(), invalidReason))
- {
- localOp.setResultCode(ResultCode.INVALID_ATTRIBUTE_SYNTAX);
-
- logError(
- ERR_MODIFY_REPLACE_INVALID_SYNTAX.
- get(String.valueOf(entryDN), a.getName(),
- v.getStringValue(), invalidReason.toString()));
-
- invalidReason = new MessageBuilder();
- }
- }
- }
- }
-
-
- // If the provided attribute does not have any options, then we
- // will simply use it in place of any existing attribute of the
- // provided type (or add it if it doesn't exist).
- if (! a.hasOptions())
- {
- List<Attribute> attrList = new ArrayList<Attribute>(1);
- attrList.add(a);
- modifiedEntry.putAttribute(t, attrList);
-
- RDN rdn = modifiedEntry.getDN().getRDN();
- if ((rdn != null) && rdn.hasAttributeType(t) &&
- (! modifiedEntry.hasValue(t, a.getOptions(),
- rdn.getAttributeValue(t))))
- {
- localOp.setResultCode(ResultCode.NOT_ALLOWED_ON_RDN);
-
- localOp.appendErrorMessage(
- ERR_MODIFY_DELETE_RDN_ATTR.get(String.valueOf(entryDN),
- a.getName()));
- break modifyProcessing;
- }
- break;
- }
-
-
- // See if there is an existing attribute of the provided type. If
- // not, then we'll use the new one.
- List<Attribute> attrList = modifiedEntry.getAttribute(t);
- if ((attrList == null) || attrList.isEmpty())
- {
- attrList = new ArrayList<Attribute>(1);
- attrList.add(a);
- modifiedEntry.putAttribute(t, attrList);
-
- RDN rdn = modifiedEntry.getDN().getRDN();
- if ((rdn != null) && rdn.hasAttributeType(t) &&
- (! modifiedEntry.hasValue(t, a.getOptions(),
- rdn.getAttributeValue(t))))
- {
- localOp.setResultCode(ResultCode.NOT_ALLOWED_ON_RDN);
-
- localOp.appendErrorMessage(
- ERR_MODIFY_DELETE_RDN_ATTR.get(String.valueOf(entryDN),
- a.getName()));
- break modifyProcessing;
- }
- break;
- }
-
-
- // There must be an existing occurrence of the provided attribute
- // in the entry. If there is a version with exactly the set of
- // options provided, then replace it. Otherwise, add a new one.
- boolean found = false;
- for (int i=0; i < attrList.size(); i++)
- {
- if (attrList.get(i).optionsEqual(a.getOptions()))
- {
- attrList.set(i, a);
- found = true;
- break;
- }
- }
-
- if (! found)
- {
- attrList.add(a);
- }
-
- RDN rdn = modifiedEntry.getDN().getRDN();
- if ((rdn != null) && rdn.hasAttributeType(t) &&
- (! modifiedEntry.hasValue(t, a.getOptions(),
- rdn.getAttributeValue(t))))
- {
- localOp.setResultCode(ResultCode.NOT_ALLOWED_ON_RDN);
-
- localOp.appendErrorMessage(
- ERR_MODIFY_DELETE_RDN_ATTR.get(String.valueOf(entryDN),
- a.getName()));
- break modifyProcessing;
- }
- break;
-
-
- case INCREMENT:
- // The specified attribute type must not be an RDN attribute.
- rdn = modifiedEntry.getDN().getRDN();
- if ((rdn != null) && rdn.hasAttributeType(t))
- {
- localOp.setResultCode(ResultCode.NOT_ALLOWED_ON_RDN);
- localOp.appendErrorMessage(ERR_MODIFY_INCREMENT_RDN.get(
- String.valueOf(entryDN),
- a.getName()));
- }
-
-
- // The provided attribute must have a single value, and it must be
- // an integer.
- LinkedHashSet<AttributeValue> values = a.getValues();
- if ((values == null) || values.isEmpty())
- {
- localOp.setResultCode(ResultCode.PROTOCOL_ERROR);
-
- localOp.appendErrorMessage(
- ERR_MODIFY_INCREMENT_REQUIRES_VALUE.get(
- String.valueOf(entryDN),
- a.getName()));
-
- break modifyProcessing;
- }
- else if (values.size() > 1)
- {
- localOp.setResultCode(ResultCode.PROTOCOL_ERROR);
-
- localOp.appendErrorMessage(
- ERR_MODIFY_INCREMENT_REQUIRES_SINGLE_VALUE.get(
- String.valueOf(entryDN),
- a.getName()));
- break modifyProcessing;
- }
-
- AttributeValue v = values.iterator().next();
-
- long incrementValue;
- try
- {
- incrementValue = Long.parseLong(v.getNormalizedStringValue());
- }
- catch (Exception e)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, e);
- }
-
- localOp.setResultCode(ResultCode.INVALID_ATTRIBUTE_SYNTAX);
-
- localOp.appendErrorMessage(
- ERR_MODIFY_INCREMENT_PROVIDED_VALUE_NOT_INTEGER.get(
- String.valueOf(entryDN),
- a.getName(), v.getStringValue()));
-
- break modifyProcessing;
- }
-
-
- // Get the corresponding attribute from the entry and make sure
- // that it has a single integer value.
- attrList = modifiedEntry.getAttribute(t, a.getOptions());
- if ((attrList == null) || attrList.isEmpty())
- {
- localOp.setResultCode(ResultCode.CONSTRAINT_VIOLATION);
-
- localOp.appendErrorMessage(
- ERR_MODIFY_INCREMENT_REQUIRES_EXISTING_VALUE.get(
- String.valueOf(entryDN),
- a.getName()));
-
- break modifyProcessing;
- }
-
- boolean updated = false;
- for (Attribute attr : attrList)
- {
- LinkedHashSet<AttributeValue> valueList = attr.getValues();
- if ((valueList == null) || valueList.isEmpty())
- {
- continue;
- }
-
- LinkedHashSet<AttributeValue> newValueList =
- new LinkedHashSet<AttributeValue>(valueList.size());
- for (AttributeValue existingValue : valueList)
- {
- long newIntValue;
- try
- {
- long existingIntValue =
- Long.parseLong(existingValue.getStringValue());
- newIntValue = existingIntValue + incrementValue;
- }
- catch (Exception e)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, e);
- }
-
- localOp.setResultCode(ResultCode.INVALID_ATTRIBUTE_SYNTAX);
-
- localOp.appendErrorMessage(
- ERR_MODIFY_INCREMENT_REQUIRES_INTEGER_VALUE.get(
- String.valueOf(entryDN),
- a.getName(),
- existingValue.getStringValue()));
- break modifyProcessing;
- }
-
- ByteString newValue =
- new ASN1OctetString(String.valueOf(newIntValue));
- newValueList.add(new AttributeValue(t, newValue));
- }
-
- attr.setValues(newValueList);
- updated = true;
- }
-
- if (! updated)
- {
- localOp.setResultCode(ResultCode.CONSTRAINT_VIOLATION);
-
- localOp.appendErrorMessage(
- ERR_MODIFY_INCREMENT_REQUIRES_EXISTING_VALUE.get(
- String.valueOf(entryDN),
- a.getName()));
-
- break modifyProcessing;
- }
-
- break;
-
- default:
- }
- }
-
-
- // If there was a password change, then perform any additional checks
- // that may be necessary.
- if (passwordChanged)
- {
- // If it was a self change, then see if the current password was
- // provided and handle accordingly.
- if (selfChange &&
- pwPolicyState.getPolicy().requireCurrentPassword() &&
- (! currentPasswordProvided))
- {
- pwpErrorType = PasswordPolicyErrorType.MUST_SUPPLY_OLD_PASSWORD;
-
- localOp.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
-
- localOp.appendErrorMessage(
- ERR_MODIFY_PW_CHANGE_REQUIRES_CURRENT_PW.get());
- break modifyProcessing;
- }
-
-
- // If this change would result in multiple password values, then see
- // if that's OK.
- if ((numPasswords > 1) &&
- (! pwPolicyState.getPolicy().allowMultiplePasswordValues()))
- {
- pwpErrorType = PasswordPolicyErrorType.PASSWORD_MOD_NOT_ALLOWED;
-
- localOp.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
-
- localOp.appendErrorMessage(
- ERR_MODIFY_MULTIPLE_PASSWORDS_NOT_ALLOWED.get());
- break modifyProcessing;
- }
-
-
- // If any of the password values should be validated, then do so now.
- if (selfChange ||
- (! pwPolicyState.getPolicy().skipValidationForAdministrators()))
- {
- List<AttributeValue> newPasswords =
- localOp.getNewPasswords();
- List<AttributeValue> currentPasswords =
- localOp.getCurrentPasswords();
-
- if (newPasswords != null)
- {
- HashSet<ByteString> clearPasswords = new HashSet<ByteString>();
- clearPasswords.addAll(pwPolicyState.getClearPasswords());
-
- if (currentPasswords != null)
- {
- if (clearPasswords.isEmpty())
- {
- for (AttributeValue v : currentPasswords)
- {
- clearPasswords.add(v.getValue());
- }
- }
- else
- {
- // NOTE: We can't rely on the fact that Set doesn't allow
- // duplicates because technically it's possible that the
- // values aren't duplicates if they are ASN.1 elements with
- // different types (like 0x04 for a standard universal octet
- // string type versus 0x80 for a simple password in a bind
- // operation). So we have to manually check for duplicates.
- for (AttributeValue v : currentPasswords)
- {
- ByteString pw = v.getValue();
-
- boolean found = false;
- for (ByteString s : clearPasswords)
- {
- if (Arrays.equals(s.value(), pw.value()))
- {
- found = true;
- break;
- }
- }
-
- if (! found)
- {
- clearPasswords.add(pw);
- }
- }
- }
- }
-
- for (AttributeValue v : newPasswords)
- {
- MessageBuilder invalidReason = new MessageBuilder();
- if (! pwPolicyState.passwordIsAcceptable(localOp, modifiedEntry,
- v.getValue(),
- clearPasswords,
- invalidReason))
- {
- pwpErrorType =
- PasswordPolicyErrorType.INSUFFICIENT_PASSWORD_QUALITY;
-
- localOp.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
-
- localOp.appendErrorMessage(ERR_MODIFY_PW_VALIDATION_FAILED
- .get(invalidReason.toString()));
- break modifyProcessing;
- }
- }
- }
- }
-
-
- // If we should check the password history, then do so now.
- if (pwPolicyState.maintainHistory())
- {
- List<AttributeValue> newPasswords = localOp.getNewPasswords();
- if (newPasswords != null)
- {
- for (AttributeValue v : newPasswords)
- {
- if (pwPolicyState.isPasswordInHistory(v.getValue()))
- {
- if (selfChange || (! pwPolicyState.getPolicy().
- skipValidationForAdministrators()))
- {
- pwpErrorType = PasswordPolicyErrorType.PASSWORD_IN_HISTORY;
-
- localOp.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
-
- localOp.appendErrorMessage(ERR_MODIFY_PW_IN_HISTORY.get());
- break modifyProcessing;
- }
- }
- }
-
- pwPolicyState.updatePasswordHistory();
- }
- }
- }
-
-
- // Check to see if the client has permission to perform the
- // modify.
- // The access control check is not made any earlier because the
- // handler needs access to the modified entry.
-
- // FIXME: for now assume that this will check all permission
- // pertinent to the operation. This includes proxy authorization
- // and any other controls specified.
-
- // FIXME: earlier checks to see if the entry already exists may
- // have already exposed sensitive information to the client.
- if (!AccessControlConfigManager.getInstance()
- .getAccessControlHandler().isAllowed(localOp)) {
- localOp.setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS);
-
- localOp.appendErrorMessage(
- ERR_MODIFY_AUTHZ_INSUFFICIENT_ACCESS_RIGHTS.get(
- String.valueOf(entryDN)));
-
- skipPostOperation = true;
- break modifyProcessing;
- }
-
- boolean wasLocked = false;
- if (passwordChanged)
- {
- // See if the account was locked for any reason.
- wasLocked = pwPolicyState.lockedDueToIdleInterval() ||
- pwPolicyState.lockedDueToMaximumResetAge() ||
- pwPolicyState.lockedDueToFailures();
-
- // Update the password policy state attributes in the user's entry.
- // If the modification fails, then these changes won't be applied.
- pwPolicyState.setPasswordChangedTime();
- pwPolicyState.clearFailureLockout();
- pwPolicyState.clearGraceLoginTimes();
- pwPolicyState.clearWarnedTime();
-
- if (pwPolicyState.getPolicy().forceChangeOnAdd() ||
- pwPolicyState.getPolicy().forceChangeOnReset())
- {
- if (selfChange)
- {
- pwPolicyState.setMustChangePassword(false);
- }
- else
- {
- if ((pwpErrorType == null) &&
- pwPolicyState.getPolicy().forceChangeOnReset())
- {
- pwpErrorType = PasswordPolicyErrorType.CHANGE_AFTER_RESET;
- }
-
- pwPolicyState.setMustChangePassword(
- pwPolicyState.getPolicy().forceChangeOnReset());
- }
- }
-
- if (pwPolicyState.getPolicy().getRequireChangeByTime() > 0)
- {
- pwPolicyState.setRequiredChangeTime();
- }
- modifications.addAll(pwPolicyState.getModifications());
- //Apply pwd Policy modifications to modified entry.
- try {
- modifiedEntry.applyModifications(pwPolicyState.getModifications());
- } catch (DirectoryException e) {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, e);
- }
-
- localOp.setResponseData(e);
- break modifyProcessing;
- }
- }
- else if((! localOp.isInternalOperation()) &&
- pwPolicyState.mustChangePassword())
- {
- // The user will not be allowed to do anything else before
- // the password gets changed.
- pwpErrorType = PasswordPolicyErrorType.CHANGE_AFTER_RESET;
-
- localOp.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
-
- localOp.appendErrorMessage(ERR_MODIFY_MUST_CHANGE_PASSWORD.get());
- break modifyProcessing;
- }
-
- // If the server is configured to check the schema and the
- // operation is not a sycnhronization operation,
- // make sure that the new entry is valid per the server schema.
- if ((DirectoryServer.checkSchema()) &&
- (!localOp.isSynchronizationOperation()) )
- {
- MessageBuilder invalidReason = new MessageBuilder();
- if (! modifiedEntry.conformsToSchema(null, false, false, false,
- invalidReason))
- {
- localOp.setResultCode(ResultCode.OBJECTCLASS_VIOLATION);
- localOp.appendErrorMessage(ERR_MODIFY_VIOLATES_SCHEMA.get(
- String.valueOf(entryDN),
- invalidReason.toString()));
- break modifyProcessing;
- }
- }
-
-
- // Check for a request to cancel this operation.
- if (localOp.getCancelRequest() != null)
- {
- return;
- }
-
- // If the operation is not a synchronization operation,
- // Invoke the pre-operation modify plugins.
- if (!localOp.isSynchronizationOperation())
- {
- PreOperationPluginResult preOpResult =
- pluginConfigManager.invokePreOperationModifyPlugins(localOp);
- if (preOpResult.connectionTerminated())
- {
- // There's no point in continuing with anything. Log the result
- // and return.
- localOp.setResultCode(ResultCode.CANCELED);
-
- localOp.appendErrorMessage(ERR_CANCELED_BY_PREOP_DISCONNECT.get());
-
- localOp.setProcessingStopTime();
-
- return;
- }
- else if (preOpResult.sendResponseImmediately())
- {
- skipPostOperation = true;
- break modifyProcessing;
- }
- else if (preOpResult.skipCoreProcessing())
- {
- skipPostOperation = false;
- break modifyProcessing;
- }
- }
-
-
- // Check for a request to cancel this operation.
- if (localOp.getCancelRequest() != null)
- {
- return;
- }
-
-
- // Actually perform the modify operation. This should also include
- // taking care of any synchronization that might be needed.
- if (backend == null)
- {
- localOp.setResultCode(ResultCode.NO_SUCH_OBJECT);
- localOp.appendErrorMessage(
- ERR_MODIFY_NO_BACKEND_FOR_ENTRY.get(String.valueOf(entryDN)));
- break modifyProcessing;
- }
-
- try
- {
- // If it is not a private backend, then check to see if the server or
- // backend is operating in read-only mode.
- if (! backend.isPrivateBackend())
- {
- switch (DirectoryServer.getWritabilityMode())
- {
- case DISABLED:
- localOp.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
- localOp.appendErrorMessage(
- ERR_MODIFY_SERVER_READONLY.get(String.valueOf(entryDN)));
- break modifyProcessing;
-
- case INTERNAL_ONLY:
- if (! (localOp.isInternalOperation() ||
- localOp.isSynchronizationOperation()))
- {
- localOp.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
- localOp.appendErrorMessage(
- ERR_MODIFY_SERVER_READONLY.get(String.valueOf(entryDN)));
- break modifyProcessing;
- }
- }
-
- switch (backend.getWritabilityMode())
- {
- case DISABLED:
- localOp.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
- localOp.appendErrorMessage(
- ERR_MODIFY_BACKEND_READONLY.get(String.valueOf(entryDN)));
- break modifyProcessing;
-
- case INTERNAL_ONLY:
- if (! localOp.isInternalOperation() ||
- localOp.isSynchronizationOperation())
- {
- localOp.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
- localOp.appendErrorMessage(
- ERR_MODIFY_BACKEND_READONLY.get(String.valueOf(entryDN)));
- break modifyProcessing;
- }
- }
- }
-
-
- if (noOp)
- {
- localOp.appendErrorMessage(INFO_MODIFY_NOOP.get());
-
- localOp.setResultCode(ResultCode.NO_OPERATION);
- }
- else
- {
- for (SynchronizationProvider provider :
- DirectoryServer.getSynchronizationProviders())
- {
- try
- {
- SynchronizationProviderResult result =
- provider.doPreOperation(localOp);
- if (! result.continueOperationProcessing())
- {
- break modifyProcessing;
- }
- }
- catch (DirectoryException de)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, de);
- }
-
- logError(ERR_MODIFY_SYNCH_PREOP_FAILED.
- get(localOp.getConnectionID(), localOp.getOperationID(),
- getExceptionMessage(de)));
-
- localOp.setResponseData(de);
- break modifyProcessing;
- }
- }
-
- backend.replaceEntry(modifiedEntry, localOp);
-
-
-
- // See if we need to generate any account status notifications as a
- // result of the changes.
- if (passwordChanged || enabledStateChanged || wasLocked)
- {
- handleAccountStatusNotifications(passwordChanged, selfChange,
- enabledStateChanged, isEnabled,
- wasLocked, localOp,
- pwPolicyState, modifiedEntry);
- }
- }
-
- if (preReadRequest != null)
- {
- Entry entry = currentEntry.duplicate(true);
-
- if (! preReadRequest.allowsAttribute(
- DirectoryServer.getObjectClassAttributeType()))
- {
- entry.removeAttribute(
- DirectoryServer.getObjectClassAttributeType());
- }
-
- if (! preReadRequest.returnAllUserAttributes())
- {
- Iterator<AttributeType> iterator =
- entry.getUserAttributes().keySet().iterator();
- while (iterator.hasNext())
- {
- AttributeType attrType = iterator.next();
- if (! preReadRequest.allowsAttribute(attrType))
- {
- iterator.remove();
- }
- }
- }
-
- if (! preReadRequest.returnAllOperationalAttributes())
- {
- Iterator<AttributeType> iterator =
- entry.getOperationalAttributes().keySet().iterator();
- while (iterator.hasNext())
- {
- AttributeType attrType = iterator.next();
- if (! preReadRequest.allowsAttribute(attrType))
- {
- iterator.remove();
- }
- }
- }
-
- // FIXME -- Check access controls on the entry to see if it should
- // be returned or if any attributes need to be stripped
- // out..
- SearchResultEntry searchEntry = new SearchResultEntry(entry);
- LDAPPreReadResponseControl responseControl =
- new LDAPPreReadResponseControl(preReadRequest.getOID(),
- preReadRequest.isCritical(),
- searchEntry);
-
- localOp.getResponseControls().add(responseControl);
- }
-
- if (postReadRequest != null)
- {
- Entry entry = modifiedEntry.duplicate(true);
-
- if (! postReadRequest.allowsAttribute(
- DirectoryServer.getObjectClassAttributeType()))
- {
- entry.removeAttribute(
- DirectoryServer.getObjectClassAttributeType());
- }
-
- if (! postReadRequest.returnAllUserAttributes())
- {
- Iterator<AttributeType> iterator =
- entry.getUserAttributes().keySet().iterator();
- while (iterator.hasNext())
- {
- AttributeType attrType = iterator.next();
- if (! postReadRequest.allowsAttribute(attrType))
- {
- iterator.remove();
- }
- }
- }
-
- if (! postReadRequest.returnAllOperationalAttributes())
- {
- Iterator<AttributeType> iterator =
- entry.getOperationalAttributes().keySet().iterator();
- while (iterator.hasNext())
- {
- AttributeType attrType = iterator.next();
- if (! postReadRequest.allowsAttribute(attrType))
- {
- iterator.remove();
- }
- }
- }
-
- // FIXME -- Check access controls on the entry to see if it should
- // be returned or if any attributes need to be stripped
- // out..
- SearchResultEntry searchEntry = new SearchResultEntry(entry);
- LDAPPostReadResponseControl responseControl =
- new LDAPPostReadResponseControl(postReadRequest.getOID(),
- postReadRequest.isCritical(),
- searchEntry);
-
- localOp.getResponseControls().add(responseControl);
- }
-
- if (! noOp)
- {
- localOp.setResultCode(ResultCode.SUCCESS);
- }
- }
- catch (DirectoryException de)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, de);
- }
-
- localOp.setResultCode(de.getResultCode());
- localOp.appendErrorMessage(de.getMessageObject());
- localOp.setMatchedDN(de.getMatchedDN());
- localOp.setReferralURLs(de.getReferralURLs());
-
- break modifyProcessing;
- }
- catch (CancelledOperationException coe)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, coe);
- }
-
- CancelResult cancelResult = coe.getCancelResult();
-
- localOp.setCancelResult(cancelResult);
- localOp.setResultCode(cancelResult.getResultCode());
-
- Message message = coe.getMessageObject();
- if ((message != null) && (message.length() > 0))
- {
- localOp.appendErrorMessage(message);
- }
-
- break modifyProcessing;
- }
- }
- finally
- {
- LockManager.unlock(entryDN, entryLock);
-
- for (SynchronizationProvider provider :
- DirectoryServer.getSynchronizationProviders())
- {
- try
- {
- provider.doPostOperation(localOp);
- }
- catch (DirectoryException de)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, de);
- }
-
- logError(ERR_MODIFY_SYNCH_POSTOP_FAILED.
- get(localOp.getConnectionID(), localOp.getOperationID(),
- getExceptionMessage(de)));
-
- localOp.setResponseData(de);
- break;
- }
- }
- }
- }
-
-
- // If the password policy request control was included, then make sure we
- // send the corresponding response control.
- if (pwPolicyControlRequested)
- {
- localOp.addResponseControl(
- new PasswordPolicyResponseControl(null, 0, pwpErrorType));
- }
-
-
- // Indicate that it is now too late to attempt to cancel the operation.
- localOp.setCancelResult(CancelResult.TOO_LATE);
-
- // Invoke the post-operation or post-synchronization modify plugins.
- if (localOp.isSynchronizationOperation())
- {
- if (localOp.getResultCode() == ResultCode.SUCCESS)
- {
- pluginConfigManager.invokePostSynchronizationModifyPlugins(localOp);
- }
- }
- else if (! skipPostOperation)
- {
- // FIXME -- Should this also be done while holding the locks?
- PostOperationPluginResult postOpResult =
- pluginConfigManager.invokePostOperationModifyPlugins(localOp);
- if (postOpResult.connectionTerminated())
- {
- // There's no point in continuing with anything. Log the result and
- // return.
- localOp.setResultCode(ResultCode.CANCELED);
-
- localOp.appendErrorMessage(ERR_CANCELED_BY_PREOP_DISCONNECT.get());
-
- localOp.setProcessingStopTime();
-
- return;
- }
- }
-
- // Notify any change notification listeners that might be registered with
- // the server.
- if (localOp.getResultCode() == ResultCode.SUCCESS)
- {
- for (ChangeNotificationListener changeListener :
- DirectoryServer.getChangeNotificationListeners())
- {
- try
- {
- changeListener.handleModifyOperation(localOp,
- localOp.getCurrentEntry(),
- localOp.getModifiedEntry());
- }
- catch (Exception e)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, e);
- }
-
- Message message = ERR_MODIFY_ERROR_NOTIFYING_CHANGE_LISTENER.get(
- getExceptionMessage(e));
- logError(message);
- }
- }
- }
-
-
-
- // Stop the processing timer.
- localOp.setProcessingStopTime();
- }
-
- /**
- * Handles any account status notifications that may be needed as a result of
- * modify processing using the provided information.
- *
- * @param passwordChanged Indicates whether the modify operation
- * included a password change.
- * @param selfChange Indicates whether the password change was
- * performed by the end user or an administrator.
- * @param enabledStateChanged Indicates whether the user's account changed
- * from enabled to disabled (or vice versa)
- * @param isEnabled Indicates whether the user's account is now
- * enabled.
- * @param wasLocked Indicates whether the user's account was
- * previously locked.
- * @param localOp The modify operation being processed.
- * @param pwPolicyState The password policy state for the user.
- * @param modifiedEntry The updated version of the entry.
- */
- private void handleAccountStatusNotifications(boolean passwordChanged,
- boolean selfChange, boolean enabledStateChanged,
- boolean isEnabled, boolean wasLocked,
- LocalBackendModifyOperation localOp,
- PasswordPolicyState pwPolicyState, Entry modifiedEntry)
- {
- if (passwordChanged)
- {
- if (selfChange)
- {
- AuthenticationInfo authInfo =
- localOp.getClientConnection().getAuthenticationInfo();
- if (authInfo.getAuthenticationDN().equals(modifiedEntry.getDN()))
- {
- localOp.getClientConnection().setMustChangePassword(false);
- }
-
- Message message = INFO_MODIFY_PASSWORD_CHANGED.get();
- pwPolicyState.generateAccountStatusNotification(
- AccountStatusNotificationType.PASSWORD_CHANGED,
- modifiedEntry, message,
- AccountStatusNotification.createProperties(pwPolicyState, false, -1,
- localOp.getCurrentPasswords(), localOp.getNewPasswords()));
- }
- else
- {
- Message message = INFO_MODIFY_PASSWORD_RESET.get();
- pwPolicyState.generateAccountStatusNotification(
- AccountStatusNotificationType.PASSWORD_RESET, modifiedEntry,
- message,
- AccountStatusNotification.createProperties(pwPolicyState, false, -1,
- localOp.getCurrentPasswords(), localOp.getNewPasswords()));
- }
- }
-
- if (enabledStateChanged)
- {
- if (isEnabled)
- {
- Message message = INFO_MODIFY_ACCOUNT_ENABLED.get();
- pwPolicyState.generateAccountStatusNotification(
- AccountStatusNotificationType.ACCOUNT_ENABLED,
- modifiedEntry, message,
- AccountStatusNotification.createProperties(pwPolicyState, false, -1,
- null, null));
- }
- else
- {
- Message message = INFO_MODIFY_ACCOUNT_DISABLED.get();
- pwPolicyState.generateAccountStatusNotification(
- AccountStatusNotificationType.ACCOUNT_DISABLED,
- modifiedEntry, message,
- AccountStatusNotification.createProperties(pwPolicyState, false, -1,
- null, null));
- }
- }
-
- if (wasLocked)
- {
- Message message = INFO_MODIFY_ACCOUNT_UNLOCKED.get();
- pwPolicyState.generateAccountStatusNotification(
- AccountStatusNotificationType.ACCOUNT_UNLOCKED, modifiedEntry,
- message,
- AccountStatusNotification.createProperties(pwPolicyState, false, -1,
- null, null));
- }
- }
-
- /**
- * Perform a search operation against a local backend.
- *
- * @param operation - The operation to perform
- */
- public void processSearch(SearchOperation operation)
- {
-
- LocalBackendSearchOperation localOp =
- new LocalBackendSearchOperation(operation);
-
- PersistentSearch persistentSearch = null;
-
- ClientConnection clientConnection = localOp.getClientConnection();
-
- // Get the plugin config manager that will be used for invoking plugins.
- PluginConfigManager pluginConfigManager =
- DirectoryServer.getPluginConfigManager();
- boolean skipPostOperation = false;
-
- // Create a labeled block of code that we can break out of if a problem is
- // detected.
- searchProcessing:
- {
- // Process the search base and filter to convert them from their raw forms
- // as provided by the client to the forms required for the rest of the
- // search processing.
- DN baseDN = localOp.getBaseDN();
- SearchFilter filter = localOp.getFilter();
-
- if ((baseDN == null) || (filter == null)){
- break searchProcessing;
- }
-
- // Check to see if there are any controls in the request. If so, then
- // see if there is any special processing required.
- boolean processSearch = true;
- List<Control> requestControls = localOp.getRequestControls();
- if ((requestControls != null) && (! requestControls.isEmpty()))
- {
- for (int i=0; i < requestControls.size(); i++)
- {
- Control c = requestControls.get(i);
- String oid = c.getOID();
- if (!AccessControlConfigManager.getInstance().
- getAccessControlHandler().
- isAllowed(baseDN, localOp, c))
- {
- localOp.setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS);
-
- localOp.appendErrorMessage(
- ERR_CONTROL_INSUFFICIENT_ACCESS_RIGHTS.get(oid));
- skipPostOperation = true;
- break searchProcessing;
- }
-
- if (oid.equals(OID_LDAP_ASSERTION))
- {
- LDAPAssertionRequestControl assertControl;
- if (c instanceof LDAPAssertionRequestControl)
- {
- assertControl = (LDAPAssertionRequestControl) c;
- }
- else
- {
- try
- {
- assertControl = LDAPAssertionRequestControl.decodeControl(c);
- requestControls.set(i, assertControl);
- }
- catch (LDAPException le)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, le);
- }
-
- localOp.setResultCode(
- ResultCode.valueOf(le.getResultCode()));
- localOp.appendErrorMessage(le.getMessageObject());
-
- break searchProcessing;
- }
- }
-
- try
- {
- // FIXME -- We need to determine whether the current user has
- // permission to make this determination.
- SearchFilter assertionFilter = assertControl.getSearchFilter();
- Entry entry;
- try
- {
- entry = DirectoryServer.getEntry(baseDN);
- }
- catch (DirectoryException de)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, de);
- }
-
- localOp.setResultCode(de.getResultCode());
-
- localOp.appendErrorMessage(
- ERR_SEARCH_CANNOT_GET_ENTRY_FOR_ASSERTION.get(
- de.getMessageObject()));
-
- break searchProcessing;
- }
-
- if (entry == null)
- {
- localOp.setResultCode(ResultCode.NO_SUCH_OBJECT);
-
- localOp.appendErrorMessage(
- ERR_SEARCH_NO_SUCH_ENTRY_FOR_ASSERTION.get());
-
- break searchProcessing;
- }
-
-
- if (! assertionFilter.matchesEntry(entry))
- {
- localOp.setResultCode(ResultCode.ASSERTION_FAILED);
-
- localOp.appendErrorMessage(
- ERR_SEARCH_ASSERTION_FAILED.get());
-
- break searchProcessing;
- }
- }
- catch (DirectoryException de)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, de);
- }
-
- localOp.setResultCode(ResultCode.PROTOCOL_ERROR);
-
- localOp.appendErrorMessage(
- ERR_SEARCH_CANNOT_PROCESS_ASSERTION_FILTER.get(
- de.getMessageObject()));
-
- break searchProcessing;
- }
- }
- else if (oid.equals(OID_PROXIED_AUTH_V1))
- {
- // The requester must have the PROXIED_AUTH privilige in order to be
- // able to use this control.
- if (! clientConnection.hasPrivilege(
- Privilege.PROXIED_AUTH, localOp))
- {
- localOp.appendErrorMessage(
- ERR_PROXYAUTH_INSUFFICIENT_PRIVILEGES.get());
- localOp.setResultCode(ResultCode.AUTHORIZATION_DENIED);
- break searchProcessing;
- }
-
-
- ProxiedAuthV1Control proxyControl;
- if (c instanceof ProxiedAuthV1Control)
- {
- proxyControl = (ProxiedAuthV1Control) c;
- }
- else
- {
- try
- {
- proxyControl = ProxiedAuthV1Control.decodeControl(c);
- }
- catch (LDAPException le)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, le);
- }
-
- localOp.setResultCode(
- ResultCode.valueOf(le.getResultCode()));
- localOp.appendErrorMessage(le.getMessageObject());
-
- break searchProcessing;
- }
- }
-
-
- Entry authorizationEntry;
- try
- {
- authorizationEntry = proxyControl.getAuthorizationEntry();
- }
- catch (DirectoryException de)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, de);
- }
-
- localOp.setResultCode(de.getResultCode());
- localOp.appendErrorMessage(de.getMessageObject());
-
- break searchProcessing;
- }
-
- localOp.setAuthorizationEntry(authorizationEntry);
- if (authorizationEntry == null)
- {
- localOp.setProxiedAuthorizationDN(DN.nullDN());
- }
- else
- {
- localOp.setProxiedAuthorizationDN(authorizationEntry.getDN());
- }
- }
- else if (oid.equals(OID_PROXIED_AUTH_V2))
- {
- // The requester must have the PROXIED_AUTH privilige in order to be
- // able to use this control.
- if (! clientConnection.hasPrivilege(
- Privilege.PROXIED_AUTH, localOp))
- {
- localOp.appendErrorMessage(
- ERR_PROXYAUTH_INSUFFICIENT_PRIVILEGES.get());
- localOp.setResultCode(ResultCode.AUTHORIZATION_DENIED);
- break searchProcessing;
- }
-
-
- ProxiedAuthV2Control proxyControl;
- if (c instanceof ProxiedAuthV2Control)
- {
- proxyControl = (ProxiedAuthV2Control) c;
- }
- else
- {
- try
- {
- proxyControl = ProxiedAuthV2Control.decodeControl(c);
- }
- catch (LDAPException le)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, le);
- }
-
- localOp.setResultCode(
- ResultCode.valueOf(le.getResultCode()));
- localOp.appendErrorMessage(le.getMessageObject());
-
- break searchProcessing;
- }
- }
-
-
- Entry authorizationEntry;
- try
- {
- authorizationEntry = proxyControl.getAuthorizationEntry();
- }
- catch (DirectoryException de)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, de);
- }
-
- localOp.setResultCode(de.getResultCode());
- localOp.appendErrorMessage(de.getMessageObject());
-
- break searchProcessing;
- }
-
- localOp.setAuthorizationEntry(authorizationEntry);
- if (authorizationEntry == null)
- {
- localOp.setProxiedAuthorizationDN(DN.nullDN());
- }
- else
- {
- localOp.setProxiedAuthorizationDN(authorizationEntry.getDN());
- }
- }
- else if (oid.equals(OID_PERSISTENT_SEARCH))
- {
- PersistentSearchControl psearchControl;
- if (c instanceof PersistentSearchControl)
- {
- psearchControl = (PersistentSearchControl) c;
- }
- else
- {
- try
- {
- psearchControl = PersistentSearchControl.decodeControl(c);
- }
- catch (LDAPException le)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, le);
- }
-
- localOp.setResultCode(
- ResultCode.valueOf(le.getResultCode()));
- localOp.appendErrorMessage(le.getMessageObject());
-
- break searchProcessing;
- }
- }
-
- persistentSearch =
- new PersistentSearch(operation, psearchControl.getChangeTypes(),
- psearchControl.getReturnECs());
- localOp.setPersistentSearch(persistentSearch);
-
- // If we're only interested in changes, then we don't actually want
- // to process the search now.
- if (psearchControl.getChangesOnly())
- {
- processSearch = false;
- }
- }
- else if (oid.equals(OID_LDAP_SUBENTRIES))
- {
- localOp.setReturnLDAPSubentries(true);
- }
- else if (oid.equals(OID_MATCHED_VALUES))
- {
- if (c instanceof MatchedValuesControl)
- {
- localOp.setMatchedValuesControl((MatchedValuesControl) c);
- }
- else
- {
- try
- {
- MatchedValuesControl matchedValuesControl =
- MatchedValuesControl.decodeControl(c);
- localOp.setMatchedValuesControl(matchedValuesControl);
- }
- catch (LDAPException le)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, le);
- }
-
- localOp.setResultCode(
- ResultCode.valueOf(le.getResultCode()));
- localOp.appendErrorMessage(le.getMessageObject());
-
- break searchProcessing;
- }
- }
- }
- else if (oid.equals(OID_ACCOUNT_USABLE_CONTROL))
- {
- localOp.setIncludeUsableControl(true);
- }
- else if (oid.equals(OID_REAL_ATTRS_ONLY))
- {
- localOp.setRealAttributesOnly(true);
- }
- else if (oid.equals(OID_VIRTUAL_ATTRS_ONLY))
- {
- localOp.setVirtualAttributesOnly(true);
- }
- // NYI -- Add support for additional controls.
- else if (c.isCritical())
- {
- if ((backend == null) || (! backend.supportsControl(oid)))
- {
- localOp.setResultCode(
- ResultCode.UNAVAILABLE_CRITICAL_EXTENSION);
-
- localOp.appendErrorMessage(
- ERR_SEARCH_UNSUPPORTED_CRITICAL_CONTROL.get(oid));
-
- break searchProcessing;
- }
- }
- }
- }
-
-
- // Check to see if the client has permission to perform the
- // search.
-
- // FIXME: for now assume that this will check all permission
- // pertinent to the operation. This includes proxy authorization
- // and any other controls specified.
- if (AccessControlConfigManager.getInstance()
- .getAccessControlHandler().isAllowed(localOp) == false) {
- localOp.setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS);
-
- localOp.appendErrorMessage(
- ERR_SEARCH_AUTHZ_INSUFFICIENT_ACCESS_RIGHTS.get(
- String.valueOf(baseDN)));
-
- skipPostOperation = true;
- break searchProcessing;
- }
-
- // Check for a request to cancel this operation.
- if (localOp.getCancelRequest() != null)
- {
- return;
- }
-
-
- // Invoke the pre-operation search plugins.
- PreOperationPluginResult preOpResult =
- pluginConfigManager.invokePreOperationSearchPlugins(localOp);
- if (preOpResult.connectionTerminated())
- {
- // There's no point in continuing with anything. Log the request and
- // result and return.
- localOp.setResultCode(ResultCode.CANCELED);
-
- localOp.appendErrorMessage(ERR_CANCELED_BY_PREOP_DISCONNECT.get());
-
- localOp.setProcessingStopTime();
- return;
- }
- else if (preOpResult.sendResponseImmediately())
- {
- skipPostOperation = true;
- break searchProcessing;
- }
- else if (preOpResult.skipCoreProcessing())
- {
- skipPostOperation = false;
- break searchProcessing;
- }
-
-
- // Check for a request to cancel this operation.
- if (localOp.getCancelRequest() != null)
- {
- return;
- }
-
-
- // Get the backend that should hold the search base. If there is none,
- // then fail.
- if (backend == null)
- {
- localOp.setResultCode(ResultCode.NO_SUCH_OBJECT);
- localOp.appendErrorMessage(
- ERR_SEARCH_BASE_DOESNT_EXIST.get(String.valueOf(baseDN)));
- break searchProcessing;
- }
-
-
- // We'll set the result code to "success". If a problem occurs, then it
- // will be overwritten.
- localOp.setResultCode(ResultCode.SUCCESS);
-
-
- // If there's a persistent search, then register it with the server.
- if (persistentSearch != null)
- {
- DirectoryServer.registerPersistentSearch(persistentSearch);
- localOp.setSendResponse(false);
- }
-
-
- // Process the search in the backend and all its subordinates.
- try
- {
- if (processSearch)
- {
- localOp.searchBackend(backend);
- }
- }
- catch (DirectoryException de)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, de);
- }
-
- localOp.setResultCode(de.getResultCode());
- localOp.appendErrorMessage(de.getMessageObject());
- localOp.setMatchedDN(de.getMatchedDN());
- localOp.setReferralURLs(de.getReferralURLs());
-
- if (persistentSearch != null)
- {
- DirectoryServer.deregisterPersistentSearch(persistentSearch);
- localOp.setSendResponse(true);
- }
-
- break searchProcessing;
- }
- catch (CancelledOperationException coe)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, coe);
- }
-
- CancelResult cancelResult = coe.getCancelResult();
-
- localOp.setCancelResult(cancelResult);
- localOp.setResultCode(cancelResult.getResultCode());
-
- Message message = coe.getMessageObject();
- if ((message != null) && (message.length() > 0))
- {
- localOp.appendErrorMessage(message);
- }
-
- if (persistentSearch != null)
- {
- DirectoryServer.deregisterPersistentSearch(persistentSearch);
- localOp.setSendResponse(true);
- }
-
- skipPostOperation = true;
- break searchProcessing;
- }
- catch (Exception e)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, e);
- }
-
- localOp.setResultCode(
- DirectoryServer.getServerErrorResultCode());
-
- localOp.appendErrorMessage(
- ERR_SEARCH_BACKEND_EXCEPTION.get(getExceptionMessage(e)));
-
- if (persistentSearch != null)
- {
- DirectoryServer.deregisterPersistentSearch(persistentSearch);
- localOp.setSendResponse(true);
- }
-
- skipPostOperation = true;
- break searchProcessing;
- }
- }
-
-
- // Check for a request to cancel this operation.
- if (localOp.getCancelRequest() != null)
- {
- return;
- }
-
-
- // Invoke the post-operation search plugins.
- if (! skipPostOperation)
- {
- PostOperationPluginResult postOperationResult =
- pluginConfigManager.invokePostOperationSearchPlugins(localOp);
- if (postOperationResult.connectionTerminated())
- {
- localOp.setResultCode(ResultCode.CANCELED);
-
- localOp.appendErrorMessage(ERR_CANCELED_BY_POSTOP_DISCONNECT.get());
-
- localOp.setProcessingStopTime();
- return;
- }
- }
-
- }
-
- /**
- * Perform a bind operation against a local backend.
- *
- * @param operation - The operation to perform
- */
- public void processBind(BindOperation operation)
- {
- LocalBackendBindOperation localOperation =
- new LocalBackendBindOperation(operation);
-
- processLocalBind(localOperation);
- }
-
- /**
- * Perform a local bind operation against a local backend.
- *
- * @param localOp - The operation to perform
- */
- private void processLocalBind(LocalBackendBindOperation localOp)
- {
- ClientConnection clientConnection = localOp.getClientConnection();
-
- boolean returnAuthzID = false;
- int sizeLimit = DirectoryServer.getSizeLimit();
- int timeLimit = DirectoryServer.getTimeLimit();
- int lookthroughLimit = DirectoryServer.getLookthroughLimit();
- long idleTimeLimit = DirectoryServer.getIdleTimeLimit();
- boolean skipPostOperation = false;
-
- // The password policy state information for this bind operation.
- PasswordPolicyState pwPolicyState = null;
-
- // The password policy error type that should be included in the response
- // control
- PasswordPolicyErrorType pwPolicyErrorType = null;
-
- // Indicates whether the client included the password policy control in the
- // bind request.
- boolean pwPolicyControlRequested = false;
-
- // Indicates whether the authentication should use a grace login if it is
- // successful.
- boolean isGraceLogin = false;
-
- // Indicates whether the user's password must be changed before any other
- // operations will be allowed.
- boolean mustChangePassword = false;
-
- // The password policy warning type that should be included in the response
- // control
- PasswordPolicyWarningType pwPolicyWarningType = null;
-
- // The password policy warning value that should be included in the response
- // control.
- int pwPolicyWarningValue = -1 ;
-
- String saslMechanism = localOp.getSASLMechanism();
-
- // Indicates whether the warning notification that should be sent to
- // the user would be the first warning.
- boolean isFirstWarning = false;
-
- // The entry of the user that successfully authenticated during processing
- // of this bind operation.
- Entry authenticatedUserEntry = null;
-
- // The password policy state information for this bind operation.
- pwPolicyState = null;
-
- // Get the plugin config manager that will be used for invoking plugins.
- PluginConfigManager pluginConfigManager =
- DirectoryServer.getPluginConfigManager();
-
-
- // Create a labeled block of code that we can break out of if a problem is
- // detected.
-bindProcessing:
- {
- DN bindDN = localOp.getBindDN();
- // Check to see if the client has permission to perform the
- // bind.
-
- // FIXME: for now assume that this will check all permission
- // pertinent to the operation. This includes any controls
- // specified.
- if (AccessControlConfigManager.getInstance()
- .getAccessControlHandler().isAllowed(localOp) == false) {
- localOp.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
- Message message = ERR_BIND_AUTHZ_INSUFFICIENT_ACCESS_RIGHTS.get(
- String.valueOf(bindDN));
- localOp.setAuthFailureReason(message);
-
- skipPostOperation = true;
- break bindProcessing;
- }
-
- // Check to see if there are any controls in the request. If so, then see
- // if there is any special processing required.
- List<Control> requestControls = localOp.getRequestControls();
- if ((requestControls != null) && (! requestControls.isEmpty()))
- {
- for (int i=0; i < requestControls.size(); i++)
- {
- Control c = requestControls.get(i);
- String oid = c.getOID();
-
- if (!AccessControlConfigManager.getInstance().
- getAccessControlHandler(). isAllowed(bindDN, localOp, c))
- {
- localOp.setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS);
- localOp.appendErrorMessage(ERR_CONTROL_INSUFFICIENT_ACCESS_RIGHTS
- .get(oid));
- skipPostOperation = true;
- break bindProcessing;
- }
-
- if (oid.equals(OID_AUTHZID_REQUEST))
- {
- returnAuthzID = true;
- }
- else if (oid.equals(OID_PASSWORD_POLICY_CONTROL))
- {
- pwPolicyControlRequested = true;
- }
-
- // NYI -- Add support for additional controls.
- else if (c.isCritical())
- {
- localOp.setResultCode(ResultCode.UNAVAILABLE_CRITICAL_EXTENSION);
-
- localOp.appendErrorMessage(ERR_BIND_UNSUPPORTED_CRITICAL_CONTROL
- .get(String.valueOf(oid)));
-
- break bindProcessing;
- }
- }
- }
-
-
- // Check to see if this is a simple bind or a SASL bind and process
- // accordingly.
- switch (localOp.getAuthenticationType())
- {
- case SIMPLE:
- // See if this is an anonymous bind. If so, then determine whether
- // to allow it.
-
- ByteString simplePassword = localOp.getSimplePassword();
- if ((simplePassword == null) || (simplePassword.value().length == 0))
- {
- // If the server is in lockdown mode, then fail.
- if (DirectoryServer.lockdownMode())
- {
- localOp.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
- Message message = ERR_BIND_REJECTED_LOCKDOWN_MODE.get();
- localOp.setAuthFailureReason(message);
-
- localOp.setProcessingStopTime();
- logBindResponse(localOp);
- break bindProcessing;
- }
-
- // If there is a bind DN, then see whether that is acceptable.
- if (DirectoryServer.bindWithDNRequiresPassword() &&
- ((bindDN != null) && (! bindDN.isNullDN())))
- {
- localOp.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
-
- Message message = ERR_BIND_DN_BUT_NO_PASSWORD.get();
- localOp.setAuthFailureReason(message);
- break bindProcessing;
- }
-
-
- // Invoke the pre-operation bind plugins.
- PreOperationPluginResult preOpResult =
- pluginConfigManager.invokePreOperationBindPlugins(localOp);
- if (preOpResult.connectionTerminated())
- {
- // There's no point in continuing with anything. Log the result
- // and return.
- localOp.setResultCode(ResultCode.CANCELED);
-
- localOp.appendErrorMessage(
- ERR_CANCELED_BY_PREOP_DISCONNECT.get());
-
- return;
- }
- else if (preOpResult.sendResponseImmediately())
- {
- skipPostOperation = true;
- break bindProcessing;
- }
- else if (preOpResult.skipCoreProcessing())
- {
- skipPostOperation = false;
- break bindProcessing;
- }
-
- localOp.setResultCode(ResultCode.SUCCESS);
- localOp.setAuthenticationInfo(new AuthenticationInfo());
- break bindProcessing;
- }
-
- // See if the bind DN is actually one of the alternate root DNs
- // defined in the server. If so, then replace it with the actual DN
- // for that user.
- DN actualRootDN = DirectoryServer.getActualRootBindDN(bindDN);
- if (actualRootDN != null)
- {
- bindDN = actualRootDN;
- }
-
- // Get the user entry based on the bind DN. If it does not exist,
- // then fail.
- Lock userLock = null;
- for (int i=0; i < 3; i++)
- {
- userLock = LockManager.lockRead(bindDN);
- if (userLock != null)
- {
- break;
- }
- }
-
- if (userLock == null)
- {
- Message message = ERR_BIND_OPERATION_CANNOT_LOCK_USER.get(
- String.valueOf(bindDN));
-
- localOp.setResultCode(DirectoryServer.getServerErrorResultCode());
- localOp.setAuthFailureReason(message);
- break bindProcessing;
- }
-
- try
- {
- Entry userEntry;
- try
- {
- userEntry = backend.getEntry(bindDN);
- }
- catch (DirectoryException de)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, de);
- }
-
- localOp.setResultCode(ResultCode.INVALID_CREDENTIALS);
- localOp.setAuthFailureReason(de.getMessageObject());
-
- userEntry = null;
- break bindProcessing;
- }
-
- if (userEntry == null)
- {
-
- Message message = ERR_BIND_OPERATION_UNKNOWN_USER.get(
- String.valueOf(bindDN));
-
- localOp.setResultCode(ResultCode.INVALID_CREDENTIALS);
- localOp.setAuthFailureReason(message);
- break bindProcessing;
- }
- else
- {
- localOp.setUserEntryDN(userEntry.getDN());
- }
-
-
- // Check to see if the user has a password. If not, then fail.
- // FIXME -- We need to have a way to enable/disable debugging.
- pwPolicyState = new PasswordPolicyState(userEntry, false, false);
- PasswordPolicy policy = pwPolicyState.getPolicy();
- AttributeType pwType = policy.getPasswordAttribute();
-
- List<Attribute> pwAttr = userEntry.getAttribute(pwType);
- if ((pwAttr == null) || (pwAttr.isEmpty()))
- {
- Message message = ERR_BIND_OPERATION_NO_PASSWORD.get(
- String.valueOf(bindDN));
-
- localOp.setResultCode(ResultCode.INVALID_CREDENTIALS);
- localOp.setAuthFailureReason(message);
- break bindProcessing;
- }
-
-
- // If the password policy is configured to track authentication
- // failures or keep the last login time and the associated backend
- // is disabled, then we may need to reject the bind immediately.
- if ((policy.getStateUpdateFailurePolicy() ==
- PasswordPolicyCfgDefn.StateUpdateFailurePolicy.PROACTIVE) &&
- ((policy.getLockoutFailureCount() > 0) ||
- ((policy.getLastLoginTimeAttribute() != null) &&
- (policy.getLastLoginTimeFormat() != null))) &&
- ((DirectoryServer.getWritabilityMode() ==
- WritabilityMode.DISABLED) ||
- (backend.getWritabilityMode() == WritabilityMode.DISABLED)))
- {
- // This policy isn't applicable to root users, so if it's a root
- // user then ignore it.
- if (! DirectoryServer.isRootDN(bindDN))
- {
- Message message = ERR_BIND_OPERATION_WRITABILITY_DISABLED.get(
- String.valueOf(bindDN));
-
- localOp.setResultCode(ResultCode.INVALID_CREDENTIALS);
- localOp.setAuthFailureReason(message);
- break bindProcessing;
- }
- }
-
-
- // Check to see if the authentication must be done in a secure
- // manner. If so, then the client connection must be secure.
- if (policy.requireSecureAuthentication() &&
- (! clientConnection.isSecure()))
- {
- Message message = ERR_BIND_OPERATION_INSECURE_SIMPLE_BIND.get(
- String.valueOf(bindDN));
-
- localOp.setResultCode(ResultCode.INVALID_CREDENTIALS);
- localOp.setAuthFailureReason(message);
- break bindProcessing;
- }
-
-
- // Check to see if the user is administratively disabled or locked.
- if (pwPolicyState.isDisabled())
- {
- Message message = ERR_BIND_OPERATION_ACCOUNT_DISABLED.get(
- String.valueOf(bindDN));
-
- localOp.setResultCode(ResultCode.INVALID_CREDENTIALS);
- localOp.setAuthFailureReason(message);
- break bindProcessing;
- }
- else if (pwPolicyState.isAccountExpired())
- {
- Message message = ERR_BIND_OPERATION_ACCOUNT_EXPIRED.get(
- String.valueOf(bindDN));
-
- localOp.setResultCode(ResultCode.INVALID_CREDENTIALS);
- localOp.setAuthFailureReason(message);
-
- pwPolicyState.generateAccountStatusNotification(
- AccountStatusNotificationType.ACCOUNT_EXPIRED, userEntry,
- message,
- AccountStatusNotification.createProperties(pwPolicyState,
- false, -1, null, null));
-
- break bindProcessing;
- }
- else if (pwPolicyState.lockedDueToFailures())
- {
- Message message = ERR_BIND_OPERATION_ACCOUNT_FAILURE_LOCKED.get(
- String.valueOf(bindDN));
-
- if (pwPolicyErrorType == null)
- {
- pwPolicyErrorType = PasswordPolicyErrorType.ACCOUNT_LOCKED;
- }
-
- localOp.setResultCode(ResultCode.INVALID_CREDENTIALS);
- localOp.setAuthFailureReason(message);
- break bindProcessing;
- }
- else if (pwPolicyState.lockedDueToMaximumResetAge())
- {
- Message message = ERR_BIND_OPERATION_ACCOUNT_RESET_LOCKED.get(
- String.valueOf(bindDN));
-
- if (pwPolicyErrorType == null)
- {
- pwPolicyErrorType = PasswordPolicyErrorType.ACCOUNT_LOCKED;
- }
-
- localOp.setResultCode(ResultCode.INVALID_CREDENTIALS);
- localOp.setAuthFailureReason(message);
-
- pwPolicyState.generateAccountStatusNotification(
- AccountStatusNotificationType.ACCOUNT_RESET_LOCKED,
- userEntry, message,
- AccountStatusNotification.createProperties(pwPolicyState,
- false, -1, null, null));
-
- break bindProcessing;
- }
- else if (pwPolicyState.lockedDueToIdleInterval())
- {
- Message message = ERR_BIND_OPERATION_ACCOUNT_IDLE_LOCKED.get(
- String.valueOf(bindDN));
-
- if (pwPolicyErrorType == null)
- {
- pwPolicyErrorType = PasswordPolicyErrorType.ACCOUNT_LOCKED;
- }
-
- localOp.setResultCode(ResultCode.INVALID_CREDENTIALS);
- localOp.setAuthFailureReason(message);
-
- pwPolicyState.generateAccountStatusNotification(
- AccountStatusNotificationType.ACCOUNT_IDLE_LOCKED, userEntry,
- message,
- AccountStatusNotification.createProperties(pwPolicyState,
- false, -1, null, null));
-
- break bindProcessing;
- }
-
-
- // Determine whether the password is expired, or whether the user
- // should be warned about an upcoming expiration.
- if (pwPolicyState.isPasswordExpired())
- {
- if (pwPolicyErrorType == null)
- {
- pwPolicyErrorType = PasswordPolicyErrorType.PASSWORD_EXPIRED;
- }
-
- int maxGraceLogins = policy.getGraceLoginCount();
- if ((maxGraceLogins > 0) && pwPolicyState.mayUseGraceLogin())
- {
- List<Long> graceLoginTimes = pwPolicyState.getGraceLoginTimes();
- if ((graceLoginTimes == null) ||
- (graceLoginTimes.size() < maxGraceLogins))
- {
- isGraceLogin = true;
- mustChangePassword = true;
-
- if (pwPolicyWarningType == null)
- {
- pwPolicyWarningType =
- PasswordPolicyWarningType.GRACE_LOGINS_REMAINING;
- pwPolicyWarningValue = maxGraceLogins -
- (graceLoginTimes.size() + 1);
- }
- }
- else
- {
- Message message = ERR_BIND_OPERATION_PASSWORD_EXPIRED.get(
- String.valueOf(bindDN));
-
- localOp.setResultCode(ResultCode.INVALID_CREDENTIALS);
- localOp.setAuthFailureReason(message);
-
- pwPolicyState.generateAccountStatusNotification(
- AccountStatusNotificationType.PASSWORD_EXPIRED,
- userEntry, message,
- AccountStatusNotification.createProperties(pwPolicyState,
- false, -1, null, null));
-
- break bindProcessing;
- }
- }
- else
- {
- Message message = ERR_BIND_OPERATION_PASSWORD_EXPIRED.get(
- String.valueOf(bindDN));
-
- localOp.setResultCode(ResultCode.INVALID_CREDENTIALS);
- localOp.setAuthFailureReason(message);
-
- pwPolicyState.generateAccountStatusNotification(
- AccountStatusNotificationType.PASSWORD_EXPIRED, userEntry,
- message,
- AccountStatusNotification.createProperties(pwPolicyState,
- false, -1, null, null));
-
- break bindProcessing;
- }
- }
- else if (pwPolicyState.shouldWarn())
- {
- int numSeconds = pwPolicyState.getSecondsUntilExpiration();
-
- if (pwPolicyWarningType == null)
- {
- pwPolicyWarningType =
- PasswordPolicyWarningType.TIME_BEFORE_EXPIRATION;
- pwPolicyWarningValue = numSeconds;
- }
-
- isFirstWarning = pwPolicyState.isFirstWarning();
- }
-
-
- // Check to see if the user's password has been reset.
- if (pwPolicyState.mustChangePassword())
- {
- mustChangePassword = true;
-
- if (pwPolicyErrorType == null)
- {
- pwPolicyErrorType = PasswordPolicyErrorType.CHANGE_AFTER_RESET;
- }
- }
-
-
- // Invoke the pre-operation bind plugins.
- PreOperationPluginResult preOpResult =
- pluginConfigManager.invokePreOperationBindPlugins(localOp);
- if (preOpResult.connectionTerminated())
- {
- // There's no point in continuing with anything. Log the result
- // and return.
- localOp.setResultCode(ResultCode.CANCELED);
-
- localOp.appendErrorMessage(
- ERR_CANCELED_BY_PREOP_DISCONNECT.get());
-
- return;
- }
- else if (preOpResult.sendResponseImmediately())
- {
- skipPostOperation = true;
- break bindProcessing;
- }
- else if (preOpResult.skipCoreProcessing())
- {
- skipPostOperation = false;
- break bindProcessing;
- }
-
-
- // Determine whether the provided password matches any of the stored
- // passwords for the user.
- if (pwPolicyState.passwordMatches(simplePassword))
- {
- localOp.setResultCode(ResultCode.SUCCESS);
-
- boolean isRoot = DirectoryServer.isRootDN(userEntry.getDN());
- if (DirectoryServer.lockdownMode() && (! isRoot))
- {
- localOp.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
- Message message = ERR_BIND_REJECTED_LOCKDOWN_MODE.get();
- localOp.setAuthFailureReason(message);
-
- break bindProcessing;
- }
- localOp.setAuthenticationInfo(new AuthenticationInfo(
- userEntry,
- simplePassword,
- isRoot));
-
-
- // See if the user's entry contains a custom size limit.
- AttributeType attrType =
- DirectoryServer.getAttributeType(OP_ATTR_USER_SIZE_LIMIT,
- true);
- List<Attribute> attrList = userEntry.getAttribute(attrType);
- if ((attrList != null) && (attrList.size() == 1))
- {
- Attribute a = attrList.get(0);
- LinkedHashSet<AttributeValue> values = a.getValues();
- Iterator<AttributeValue> iterator = values.iterator();
- if (iterator.hasNext())
- {
- AttributeValue v = iterator.next();
- if (iterator.hasNext())
- {
- Message message = WARN_BIND_MULTIPLE_USER_SIZE_LIMITS.get(
- String.valueOf(userEntry.getDN()));
- logError(message);
- }
- else
- {
- try
- {
- sizeLimit = Integer.parseInt(v.getStringValue());
- }
- catch (Exception e)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, e);
- }
-
- Message message = WARN_BIND_CANNOT_PROCESS_USER_SIZE_LIMIT
- .get(v.getStringValue(),
- String.valueOf(userEntry.getDN()));
- logError(message);
- }
- }
- }
- }
-
-
- // See if the user's entry contains a custom time limit.
- attrType =
- DirectoryServer.getAttributeType(OP_ATTR_USER_TIME_LIMIT,
- true);
- attrList = userEntry.getAttribute(attrType);
- if ((attrList != null) && (attrList.size() == 1))
- {
- Attribute a = attrList.get(0);
- LinkedHashSet<AttributeValue> values = a.getValues();
- Iterator<AttributeValue> iterator = values.iterator();
- if (iterator.hasNext())
- {
- AttributeValue v = iterator.next();
- if (iterator.hasNext())
- {
- Message message = WARN_BIND_MULTIPLE_USER_TIME_LIMITS.get(
- String.valueOf(userEntry.getDN()));
- logError(message);
- }
- else
- {
- try
- {
- timeLimit = Integer.parseInt(v.getStringValue());
- }
- catch (Exception e)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, e);
- }
-
- Message message =
- WARN_BIND_CANNOT_PROCESS_USER_TIME_LIMIT.
- get(v.getStringValue(),
- String.valueOf(userEntry.getDN()));
- logError(message);
- }
- }
- }
- }
-
-
- // See if the user's entry contains a custom idle time limit.
- attrType = DirectoryServer.getAttributeType(
- OP_ATTR_USER_IDLE_TIME_LIMIT, true);
- attrList = userEntry.getAttribute(attrType);
- if ((attrList != null) && (attrList.size() == 1))
- {
- Attribute a = attrList.get(0);
- LinkedHashSet<AttributeValue> values = a.getValues();
- Iterator<AttributeValue> iterator = values.iterator();
- if (iterator.hasNext())
- {
- AttributeValue v = iterator.next();
- if (iterator.hasNext())
- {
- Message message = WARN_BIND_MULTIPLE_USER_IDLE_TIME_LIMITS.
- get(String.valueOf(userEntry.getDN()));
- logError(message);
- }
- else
- {
- try
- {
- idleTimeLimit =
- 1000L * Long.parseLong(v.getStringValue());
- }
- catch (Exception e)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, e);
- }
-
- Message message =
- WARN_BIND_CANNOT_PROCESS_USER_IDLE_TIME_LIMIT.
- get(v.getStringValue(),
- String.valueOf(userEntry.getDN()));
- logError(message);
- }
- }
- }
- }
-
-
- // See if the user's entry contains a custom lookthrough limit.
- attrType =
- DirectoryServer.getAttributeType(
- OP_ATTR_USER_LOOKTHROUGH_LIMIT, true);
- attrList = userEntry.getAttribute(attrType);
- if ((attrList != null) && (attrList.size() == 1))
- {
- Attribute a = attrList.get(0);
- LinkedHashSet<AttributeValue> values = a.getValues();
- Iterator<AttributeValue> iterator = values.iterator();
- if (iterator.hasNext())
- {
- AttributeValue v = iterator.next();
- if (iterator.hasNext())
- {
- Message message =
- WARN_BIND_MULTIPLE_USER_LOOKTHROUGH_LIMITS.
- get(String.valueOf(userEntry.getDN()));
- logError(message);
- }
- else
- {
- try
- {
- lookthroughLimit = Integer.parseInt(v.getStringValue());
- }
- catch (Exception e)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, e);
- }
-
- Message message =
- WARN_BIND_CANNOT_PROCESS_USER_LOOKTHROUGH_LIMIT.
- get(v.getStringValue(),
- String.valueOf(userEntry.getDN()));
- logError(message);
- }
- }
- }
- }
-
-
- pwPolicyState.handleDeprecatedStorageSchemes(simplePassword);
- pwPolicyState.clearFailureLockout();
-
- if (isFirstWarning)
- {
- pwPolicyState.setWarnedTime();
-
- int numSeconds = pwPolicyState.getSecondsUntilExpiration();
- Message timeToExpiration = secondsToTimeString(numSeconds);
-
- Message message = WARN_BIND_PASSWORD_EXPIRING.get(
- timeToExpiration);
-
- pwPolicyState.generateAccountStatusNotification(
- AccountStatusNotificationType.PASSWORD_EXPIRING, userEntry,
- message,
- AccountStatusNotification.createProperties(pwPolicyState,
- false, numSeconds, null, null));
- }
-
- if (isGraceLogin)
- {
- pwPolicyState.updateGraceLoginTimes();
- }
-
- pwPolicyState.setLastLoginTime();
- }
- else
- {
- Message message = ERR_BIND_OPERATION_WRONG_PASSWORD.get();
-
- localOp.setResultCode(ResultCode.INVALID_CREDENTIALS);
- localOp.setAuthFailureReason(message);
-
- if (policy.getLockoutFailureCount() > 0)
- {
- pwPolicyState.updateAuthFailureTimes();
- if (pwPolicyState.lockedDueToFailures())
- {
- AccountStatusNotificationType notificationType;
-
- boolean tempLocked;
- int lockoutDuration = pwPolicyState.getSecondsUntilUnlock();
- if (lockoutDuration > -1)
- {
- notificationType = AccountStatusNotificationType.
- ACCOUNT_TEMPORARILY_LOCKED;
- tempLocked = true;
-
- message = ERR_BIND_ACCOUNT_TEMPORARILY_LOCKED.get(
- secondsToTimeString(lockoutDuration));
- }
- else
- {
- notificationType = AccountStatusNotificationType.
- ACCOUNT_PERMANENTLY_LOCKED;
- tempLocked = false;
-
- message = ERR_BIND_ACCOUNT_PERMANENTLY_LOCKED.get();
- }
-
- pwPolicyState.generateAccountStatusNotification(
- notificationType, userEntry, message,
- AccountStatusNotification.createProperties(pwPolicyState,
- tempLocked, -1, null, null));
- }
- }
- }
- }
- catch (Exception e)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, e);
- }
-
- Message message = ERR_BIND_OPERATION_PASSWORD_VALIDATION_EXCEPTION
- .get(getExceptionMessage(e));
-
- localOp.setResultCode(DirectoryServer.getServerErrorResultCode());
- localOp.setAuthFailureReason(message);
- break bindProcessing;
- }
- finally
- {
- // No matter what, make sure to unlock the user's entry.
- LockManager.unlock(bindDN, userLock);
- }
-
- break;
-
-
- case SASL:
- // Get the appropriate authentication handler for this request based
- // on the SASL mechanism. If there is none, then fail.
- SASLMechanismHandler saslHandler =
- DirectoryServer.getSASLMechanismHandler(saslMechanism);
- if (saslHandler == null)
- {
- localOp.setResultCode(ResultCode.AUTH_METHOD_NOT_SUPPORTED);
-
- Message message = ERR_BIND_OPERATION_UNKNOWN_SASL_MECHANISM.get(
- saslMechanism);
-
- localOp.appendErrorMessage(message);
- localOp.setAuthFailureReason(message);
- break bindProcessing;
- }
-
-
- // Check to see if the client has sufficient permission to perform the
- // bind.
- // NYI
-
-
- // Invoke the pre-operation bind plugins.
- PreOperationPluginResult preOpResult =
- pluginConfigManager.invokePreOperationBindPlugins(localOp);
- if (preOpResult.connectionTerminated())
- {
- // There's no point in continuing with anything. Log the result
- // and return.
- localOp.setResultCode(ResultCode.CANCELED);
-
- localOp.appendErrorMessage(ERR_CANCELED_BY_PREOP_DISCONNECT.get());
-
- return;
- }
- else if (preOpResult.sendResponseImmediately())
- {
- skipPostOperation = true;
- break bindProcessing;
- }
- else if (preOpResult.skipCoreProcessing())
- {
- skipPostOperation = false;
- break bindProcessing;
- }
-
- // Actually process the SASL bind.
- saslHandler.processSASLBind(localOp);
-
- // If the server is operating in lockdown mode, then we will need to
- // ensure that the authentication was successful and performed as a
- // root user to continue.
- if (DirectoryServer.lockdownMode())
- {
- ResultCode resultCode = localOp.getResultCode();
- if (resultCode != ResultCode.SASL_BIND_IN_PROGRESS)
- {
- if ((resultCode != ResultCode.SUCCESS) ||
- (localOp.getSASLAuthUserEntry() == null) ||
- (! DirectoryServer.isRootDN(
- localOp.getSASLAuthUserEntry().getDN())))
- {
- localOp.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
- Message message = ERR_BIND_REJECTED_LOCKDOWN_MODE.get();
- localOp.setAuthFailureReason(message);
-
- break bindProcessing;
- }
- }
- }
-
- // Create the password policy state object.
- String userDNString;
- Entry saslAuthUserEntry = localOp.getSASLAuthUserEntry();
- if (saslAuthUserEntry == null)
- {
- pwPolicyState = null;
- userDNString = null;
- }
- else
- {
- try
- {
- // FIXME -- Need to have a way to enable debugging.
- pwPolicyState = new PasswordPolicyState(saslAuthUserEntry, false,
- false);
- localOp.setUserEntryDN(saslAuthUserEntry.getDN());
- userDNString = String.valueOf(localOp.getUserEntryDN());
- }
- catch (DirectoryException de)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, de);
- }
-
- localOp.setResponseData(de);
- break bindProcessing;
- }
- }
-
-
- // Perform password policy checks that will need to be completed
- // regardless of whether the authentication was successful.
- if (pwPolicyState != null)
- {
- PasswordPolicy policy = pwPolicyState.getPolicy();
-
- // If the password policy is configured to track authentication
- // failures or keep the last login time and the associated backend
- // is disabled, then we may need to reject the bind immediately.
- if ((policy.getStateUpdateFailurePolicy() ==
- PasswordPolicyCfgDefn.StateUpdateFailurePolicy.PROACTIVE) &&
- ((policy.getLockoutFailureCount() > 0) ||
- ((policy.getLastLoginTimeAttribute() != null) &&
- (policy.getLastLoginTimeFormat() != null))) &&
- ((DirectoryServer.getWritabilityMode() ==
- WritabilityMode.DISABLED) ||
- (backend.getWritabilityMode() == WritabilityMode.DISABLED)))
- {
- // This policy isn't applicable to root users, so if it's a root
- // user then ignore it.
- if (! DirectoryServer.isRootDN(bindDN))
- {
- Message message = ERR_BIND_OPERATION_WRITABILITY_DISABLED.get(
- userDNString);
-
- localOp.setResultCode(ResultCode.INVALID_CREDENTIALS);
- localOp.setAuthFailureReason(message);
- break bindProcessing;
- }
- }
- else if (pwPolicyState.isDisabled())
- {
- localOp.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
- Message message = ERR_BIND_OPERATION_ACCOUNT_DISABLED.get(
- userDNString);
- localOp.setAuthFailureReason(message);
- break bindProcessing;
- }
- else if (pwPolicyState.isAccountExpired())
- {
- localOp.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
- Message message = ERR_BIND_OPERATION_ACCOUNT_EXPIRED.get(
- userDNString);
- localOp.setAuthFailureReason(message);
-
- pwPolicyState.generateAccountStatusNotification(
- AccountStatusNotificationType.ACCOUNT_EXPIRED,
- saslAuthUserEntry, message,
- AccountStatusNotification.createProperties(pwPolicyState,
- false, -1, null, null));
-
- break bindProcessing;
- }
-
- if (policy.requireSecureAuthentication() &&
- (! clientConnection.isSecure()) &&
- (! saslHandler.isSecure(saslMechanism)))
- {
- localOp.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
- Message message = ERR_BIND_OPERATION_INSECURE_SASL_BIND.get(
- saslMechanism, userDNString);
- localOp.setAuthFailureReason(message);
- break bindProcessing;
- }
-
- if (pwPolicyState.lockedDueToFailures())
- {
- localOp.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
- if (pwPolicyErrorType == null)
- {
- pwPolicyErrorType = PasswordPolicyErrorType.ACCOUNT_LOCKED;
- }
-
- Message message = ERR_BIND_OPERATION_ACCOUNT_FAILURE_LOCKED.get(
- userDNString);
- localOp.setAuthFailureReason(message);
- break bindProcessing;
- }
-
- if (pwPolicyState.lockedDueToIdleInterval())
- {
- localOp.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
- if (pwPolicyErrorType == null)
- {
- pwPolicyErrorType = PasswordPolicyErrorType.ACCOUNT_LOCKED;
- }
-
- Message message = ERR_BIND_OPERATION_ACCOUNT_IDLE_LOCKED.get(
- userDNString);
- localOp.setAuthFailureReason(message);
-
- pwPolicyState.generateAccountStatusNotification(
- AccountStatusNotificationType.ACCOUNT_IDLE_LOCKED,
- saslAuthUserEntry, message,
- AccountStatusNotification.createProperties(pwPolicyState,
- false, -1, null, null));
-
- break bindProcessing;
- }
-
-
- if (saslHandler.isPasswordBased(saslMechanism))
- {
- if (pwPolicyState.lockedDueToMaximumResetAge())
- {
- localOp.setResultCode(ResultCode.INVALID_CREDENTIALS);
-
- if (pwPolicyErrorType == null)
- {
- pwPolicyErrorType = PasswordPolicyErrorType.ACCOUNT_LOCKED;
- }
-
- Message message = ERR_BIND_OPERATION_ACCOUNT_RESET_LOCKED.get(
- userDNString);
- localOp.setAuthFailureReason(message);
-
- pwPolicyState.generateAccountStatusNotification(
- AccountStatusNotificationType.ACCOUNT_RESET_LOCKED,
- saslAuthUserEntry, message,
- AccountStatusNotification.createProperties(pwPolicyState,
- false, -1, null, null));
-
- break bindProcessing;
- }
-
- if (pwPolicyState.isPasswordExpired())
- {
- if (pwPolicyErrorType == null)
- {
- pwPolicyErrorType = PasswordPolicyErrorType.PASSWORD_EXPIRED;
- }
-
- int maxGraceLogins = policy.getGraceLoginCount();
- if ((maxGraceLogins > 0) && pwPolicyState.mayUseGraceLogin())
- {
- List<Long> graceLoginTimes =
- pwPolicyState.getGraceLoginTimes();
- if ((graceLoginTimes == null) ||
- (graceLoginTimes.size() < maxGraceLogins))
- {
- isGraceLogin = true;
- mustChangePassword = true;
-
- if (pwPolicyWarningType == null)
- {
- pwPolicyWarningType =
- PasswordPolicyWarningType.GRACE_LOGINS_REMAINING;
- pwPolicyWarningValue =
- maxGraceLogins - (graceLoginTimes.size() + 1);
- }
- }
- else
- {
- Message message = ERR_BIND_OPERATION_PASSWORD_EXPIRED.get(
- String.valueOf(bindDN));
-
- localOp.setResultCode(ResultCode.INVALID_CREDENTIALS);
- localOp.setAuthFailureReason(message);
-
- pwPolicyState.generateAccountStatusNotification(
- AccountStatusNotificationType.PASSWORD_EXPIRED,
- saslAuthUserEntry, message,
- AccountStatusNotification.createProperties(
- pwPolicyState, false, -1, null, null));
-
- break bindProcessing;
- }
- }
- else
- {
- Message message = ERR_BIND_OPERATION_PASSWORD_EXPIRED.get(
- String.valueOf(bindDN));
-
- localOp.setResultCode(ResultCode.INVALID_CREDENTIALS);
- localOp.setAuthFailureReason(message);
-
- pwPolicyState.generateAccountStatusNotification(
- AccountStatusNotificationType.PASSWORD_EXPIRED,
- saslAuthUserEntry, message,
- AccountStatusNotification.createProperties(pwPolicyState,
- false, -1, null, null));
-
- break bindProcessing;
- }
- }
- else if (pwPolicyState.shouldWarn())
- {
- int numSeconds = pwPolicyState.getSecondsUntilExpiration();
-
- if (pwPolicyWarningType == null)
- {
- pwPolicyWarningType =
- PasswordPolicyWarningType.TIME_BEFORE_EXPIRATION;
- pwPolicyWarningValue = numSeconds;
- }
-
- isFirstWarning = pwPolicyState.isFirstWarning();
- }
- }
- }
-
-
- // Determine whether the authentication was successful and perform
- // any remaining password policy processing accordingly. Also check
- // for a custom size/time limit.
- ResultCode resultCode = localOp.getResultCode();
- if (resultCode == ResultCode.SUCCESS)
- {
- if (pwPolicyState != null)
- {
- if (saslHandler.isPasswordBased(saslMechanism) &&
- pwPolicyState.mustChangePassword())
- {
- mustChangePassword = true;
- }
-
- if (isFirstWarning)
- {
- pwPolicyState.setWarnedTime();
-
- int numSeconds = pwPolicyState.getSecondsUntilExpiration();
- Message timeToExpiration = secondsToTimeString(numSeconds);
-
- Message message = WARN_BIND_PASSWORD_EXPIRING.get(
- timeToExpiration);
-
- pwPolicyState.generateAccountStatusNotification(
- AccountStatusNotificationType.PASSWORD_EXPIRING,
- saslAuthUserEntry, message,
- AccountStatusNotification.createProperties(pwPolicyState,
- false, numSeconds, null, null));
- }
-
- if (isGraceLogin)
- {
- pwPolicyState.updateGraceLoginTimes();
- }
-
- pwPolicyState.setLastLoginTime();
-
-
- // See if the user's entry contains a custom size limit.
- AttributeType attrType =
- DirectoryServer.getAttributeType(OP_ATTR_USER_SIZE_LIMIT,
- true);
- List<Attribute> attrList =
- saslAuthUserEntry.getAttribute(attrType);
- if ((attrList != null) && (attrList.size() == 1))
- {
- Attribute a = attrList.get(0);
- LinkedHashSet<AttributeValue> values = a.getValues();
- Iterator<AttributeValue> iterator = values.iterator();
- if (iterator.hasNext())
- {
- AttributeValue v = iterator.next();
- if (iterator.hasNext())
- {
- Message message =
- WARN_BIND_MULTIPLE_USER_SIZE_LIMITS.get(userDNString);
- logError(message);
- }
- else
- {
- try
- {
- sizeLimit = Integer.parseInt(v.getStringValue());
- }
- catch (Exception e)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, e);
- }
-
- Message message =
- WARN_BIND_CANNOT_PROCESS_USER_SIZE_LIMIT.
- get(v.getStringValue(), userDNString);
- logError(message);
- }
- }
- }
- }
-
-
- // See if the user's entry contains a custom time limit.
- attrType =
- DirectoryServer.getAttributeType(OP_ATTR_USER_TIME_LIMIT,
- true);
- attrList = saslAuthUserEntry.getAttribute(attrType);
- if ((attrList != null) && (attrList.size() == 1))
- {
- Attribute a = attrList.get(0);
- LinkedHashSet<AttributeValue> values = a.getValues();
- Iterator<AttributeValue> iterator = values.iterator();
- if (iterator.hasNext())
- {
- AttributeValue v = iterator.next();
- if (iterator.hasNext())
- {
- Message message =
- WARN_BIND_MULTIPLE_USER_TIME_LIMITS.get(userDNString);
- logError(message);
- }
- else
- {
- try
- {
- timeLimit = Integer.parseInt(v.getStringValue());
- }
- catch (Exception e)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, e);
- }
-
- Message message =
- WARN_BIND_CANNOT_PROCESS_USER_TIME_LIMIT.
- get(v.getStringValue(), userDNString);
- logError(message);
- }
- }
- }
- }
-
-
- // See if the user's entry contains a custom idle time limit.
- attrType = DirectoryServer.getAttributeType(
- OP_ATTR_USER_IDLE_TIME_LIMIT, true);
- attrList = saslAuthUserEntry.getAttribute(attrType);
- if ((attrList != null) && (attrList.size() == 1))
- {
- Attribute a = attrList.get(0);
- LinkedHashSet<AttributeValue> values = a.getValues();
- Iterator<AttributeValue> iterator = values.iterator();
- if (iterator.hasNext())
- {
- AttributeValue v = iterator.next();
- if (iterator.hasNext())
- {
- Message message = WARN_BIND_MULTIPLE_USER_IDLE_TIME_LIMITS.
- get(String.valueOf(userDNString));
- logError(message);
- }
- else
- {
- try
- {
- idleTimeLimit =
- 1000L * Long.parseLong(v.getStringValue());
- }
- catch (Exception e)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, e);
- }
-
- Message message =
- WARN_BIND_CANNOT_PROCESS_USER_IDLE_TIME_LIMIT.
- get(v.getStringValue(),
- String.valueOf(userDNString));
- logError(message);
- }
- }
- }
- }
-
-
- // See if the user's entry contains a custom lookthrough limit.
- attrType =
- DirectoryServer.getAttributeType(
- OP_ATTR_USER_LOOKTHROUGH_LIMIT, true);
- attrList = saslAuthUserEntry.getAttribute(attrType);
- if ((attrList != null) && (attrList.size() == 1))
- {
- Attribute a = attrList.get(0);
- LinkedHashSet<AttributeValue> values = a.getValues();
- Iterator<AttributeValue> iterator = values.iterator();
- if (iterator.hasNext())
- {
- AttributeValue v = iterator.next();
- if (iterator.hasNext())
- {
- Message message =
- WARN_BIND_MULTIPLE_USER_LOOKTHROUGH_LIMITS.
- get(userDNString);
- logError(message);
- }
- else
- {
- try
- {
- lookthroughLimit = Integer.parseInt(v.getStringValue());
- }
- catch (Exception e)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, e);
- }
-
- Message message =
- WARN_BIND_CANNOT_PROCESS_USER_LOOKTHROUGH_LIMIT.
- get(v.getStringValue(), userDNString);
- logError(message);
- }
- }
- }
- }
- }
- }
- else if (resultCode == ResultCode.SASL_BIND_IN_PROGRESS)
- {
- // FIXME -- Is any special processing needed here?
- }
- else
- {
- if (pwPolicyState != null)
- {
- if (saslHandler.isPasswordBased(saslMechanism))
- {
-
- if (pwPolicyState.getPolicy().getLockoutFailureCount() > 0)
- {
- pwPolicyState.updateAuthFailureTimes();
- if (pwPolicyState.lockedDueToFailures())
- {
- AccountStatusNotificationType notificationType;
- boolean tempLocked;
- Message message;
-
- int lockoutDuration = pwPolicyState.getSecondsUntilUnlock();
- if (lockoutDuration > -1)
- {
- notificationType = AccountStatusNotificationType.
- ACCOUNT_TEMPORARILY_LOCKED;
- tempLocked = true;
- message = ERR_BIND_ACCOUNT_TEMPORARILY_LOCKED.get(
- secondsToTimeString(lockoutDuration));
- }
- else
- {
- notificationType = AccountStatusNotificationType.
- ACCOUNT_PERMANENTLY_LOCKED;
- tempLocked = false;
- message = ERR_BIND_ACCOUNT_PERMANENTLY_LOCKED.get();
- }
-
- pwPolicyState.generateAccountStatusNotification(
- notificationType, saslAuthUserEntry, message,
- AccountStatusNotification.createProperties(
- pwPolicyState, tempLocked, -1, null, null));
- }
- }
- }
- }
- }
-
- break;
-
-
- default:
- // Send a protocol error response to the client and disconnect.
- // NYI
- return;
- }
- }
-
-
- // Update the user's account with any password policy changes that may be
- // required.
- try
- {
- if (pwPolicyState != null)
- {
- pwPolicyState.updateUserEntry();
- }
- }
- catch (DirectoryException de)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, de);
- }
-
- localOp.setResponseData(de);
- }
-
-
- // Invoke the post-operation bind plugins.
- if (! skipPostOperation)
- {
- PostOperationPluginResult postOpResult =
- pluginConfigManager.invokePostOperationBindPlugins(localOp);
- if (postOpResult.connectionTerminated())
- {
- // There's no point in continuing with anything. Log the result
- // and return.
- localOp.setResultCode(ResultCode.CANCELED);
-
- localOp.appendErrorMessage(ERR_CANCELED_BY_PREOP_DISCONNECT.get());
-
- return;
- }
- }
-
-
- // Update the authentication information for the user.
- AuthenticationInfo authInfo = localOp.getAuthenticationInfo();
- if ((localOp.getResultCode() == ResultCode.SUCCESS) &&
- (authInfo != null))
- {
- authenticatedUserEntry = authInfo.getAuthenticationEntry();
- clientConnection.setAuthenticationInfo(authInfo);
- clientConnection.setSizeLimit(sizeLimit);
- clientConnection.setTimeLimit(timeLimit);
- clientConnection.setIdleTimeLimit(idleTimeLimit);
- clientConnection.setLookthroughLimit(lookthroughLimit);
- clientConnection.setMustChangePassword(mustChangePassword);
-
- if (returnAuthzID)
- {
- localOp.addResponseControl(
- new AuthorizationIdentityResponseControl(
- authInfo.getAuthorizationDN()));
- }
- }
-
-
- // See if we need to send a password policy control to the client. If so,
- // then add it to the response.
- if (localOp.getResultCode() == ResultCode.SUCCESS)
- {
- if (pwPolicyControlRequested)
- {
- PasswordPolicyResponseControl pwpControl =
- new PasswordPolicyResponseControl(pwPolicyWarningType,
- pwPolicyWarningValue,
- pwPolicyErrorType);
- localOp.addResponseControl(pwpControl);
- }
- else
- {
- if (pwPolicyErrorType == PasswordPolicyErrorType.PASSWORD_EXPIRED)
- {
- localOp.addResponseControl(new PasswordExpiredControl());
- }
- else if (pwPolicyWarningType ==
- PasswordPolicyWarningType.TIME_BEFORE_EXPIRATION)
- {
- localOp.addResponseControl(new PasswordExpiringControl(
- pwPolicyWarningValue));
- }
- }
- }
- else
- {
- if (pwPolicyControlRequested)
- {
- PasswordPolicyResponseControl pwpControl =
- new PasswordPolicyResponseControl(pwPolicyWarningType,
- pwPolicyWarningValue,
- pwPolicyErrorType);
- localOp.addResponseControl(pwpControl);
- }
- else
- {
- if (pwPolicyErrorType == PasswordPolicyErrorType.PASSWORD_EXPIRED)
- {
- localOp.addResponseControl(new PasswordExpiredControl());
- }
- }
- }
-
- // Stop the processing timer.
- localOp.setProcessingStopTime();
-
- }
-
- /**
- * Perform an add operation against a local backend.
- *
- * @param operation - The operation to perform
- */
- public void processAdd(AddOperation operation)
- {
- LocalBackendAddOperation localOperation =
- new LocalBackendAddOperation(operation);
-
- processLocalAdd(localOperation);
- }
-
- /**
- * Perform a local add operation against a local backend.
- *
- * @param localOp - The operation to perform
- */
- private void processLocalAdd(LocalBackendAddOperation localOp)
- {
- ClientConnection clientConnection = localOp.getClientConnection();
-
- // Get the plugin config manager that will be used for invoking plugins.
- PluginConfigManager pluginConfigManager =
- DirectoryServer.getPluginConfigManager();
- boolean skipPostOperation = false;
-
- // Check for a request to cancel this operation.
- if (localOp.getCancelRequest() != null)
- {
- return;
- }
-
- // Create a labeled block of code that we can break out of if a problem is
- // detected.
-addProcessing:
- {
- // Process the entry DN and set of attributes to convert them from their
- // raw forms as provided by the client to the forms required for the rest
- // of the add processing.
- DN entryDN = localOp.getEntryDN();
- if (entryDN == null){
- break addProcessing;
- }
-
- Map<ObjectClass, String> objectClasses =
- localOp.getObjectClasses();
- Map<AttributeType, List<Attribute>> userAttributes =
- localOp.getUserAttributes();
- Map<AttributeType, List<Attribute>> operationalAttributes =
- localOp.getOperationalAttributes();
-
- if ((objectClasses == null ) ||
- (userAttributes == null) ||
- (operationalAttributes == null)){
- break addProcessing;
- }
-
- // Check for a request to cancel this operation.
- if (localOp.getCancelRequest() != null)
- {
- return;
- }
-
-
- // Grab a read lock on the parent entry, if there is one. We need to do
- // this to ensure that the parent is not deleted or renamed while this add
- // is in progress, and we could also need it to check the entry against
- // a DIT structure rule.
- Lock parentLock = null;
- Lock entryLock = null;
-
- DN parentDN = entryDN.getParentDNInSuffix();
- if (parentDN == null)
- {
- // Either this entry is a suffix or doesn't belong in the directory.
- if (DirectoryServer.isNamingContext(entryDN))
- {
- // This is fine. This entry is one of the configured suffixes.
- parentLock = null;
- }
- else if (entryDN.isNullDN())
- {
- // This is not fine. The root DSE cannot be added.
- localOp.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
- localOp.appendErrorMessage(ERR_ADD_CANNOT_ADD_ROOT_DSE.get());
- break addProcessing;
- }
- else
- {
- // The entry doesn't have a parent but isn't a suffix. This is not
- // allowed.
- localOp.setResultCode(ResultCode.NO_SUCH_OBJECT);
- localOp.appendErrorMessage(ERR_ADD_ENTRY_NOT_SUFFIX.get(
- String.valueOf(entryDN)));
- break addProcessing;
- }
- }
- else
- {
- for (int i=0; i < 3; i++)
- {
- parentLock = LockManager.lockRead(parentDN);
- if (parentLock != null)
- {
- break;
- }
- }
-
- if (parentLock == null)
- {
- localOp.setResultCode(DirectoryServer.getServerErrorResultCode());
- localOp.appendErrorMessage(ERR_ADD_CANNOT_LOCK_PARENT.get(
- String.valueOf(entryDN),
- String.valueOf(parentDN)));
-
- skipPostOperation = true;
- break addProcessing;
- }
- }
-
-
- try
- {
- // Check for a request to cancel this operation.
- if (localOp.getCancelRequest() != null)
- {
- return;
- }
-
-
- // Grab a write lock on the target entry. We'll need to do this
- // eventually anyway, and we want to make sure that the two locks are
- // always released when exiting this method, no matter what. Since
- // the entry shouldn't exist yet, locking earlier than necessary
- // shouldn't cause a problem.
- for (int i=0; i < 3; i++)
- {
- entryLock = LockManager.lockWrite(entryDN);
- if (entryLock != null)
- {
- break;
- }
- }
-
- if (entryLock == null)
- {
- localOp.setResultCode(DirectoryServer.getServerErrorResultCode());
- localOp.appendErrorMessage(ERR_ADD_CANNOT_LOCK_ENTRY.get(
- String.valueOf(entryDN)));
-
- skipPostOperation = true;
- break addProcessing;
- }
-
-
- // Invoke any conflict resolution processing that might be needed by the
- // synchronization provider.
- for (SynchronizationProvider provider :
- DirectoryServer.getSynchronizationProviders())
- {
- try
- {
- SynchronizationProviderResult result =
- provider.handleConflictResolution(localOp);
- if (! result.continueOperationProcessing())
- {
- break addProcessing;
- }
- }
- catch (DirectoryException de)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, de);
- }
-
- logError(ERR_ADD_SYNCH_CONFLICT_RESOLUTION_FAILED.
- get(localOp.getConnectionID(), localOp.getOperationID(),
- getExceptionMessage(de)));
-
- localOp.setResponseData(de);
- break addProcessing;
- }
- }
-
- // If the attribute type is marked "NO-USER-MODIFICATION" then fail
- // unless this is an internal operation or is related to
- // synchronization in some way.
- // This must be done before running the password policy code
- // and any other code that may add attributes marked as
- // "NO-USER-MODIFICATION"
- //
- // Note that doing this checks at this time
- // of the processing does not make it possible for pre-parse plugins
- // to add NO-USER-MODIFICATION attributes to the entry.
- for (AttributeType at : userAttributes.keySet())
- {
- if (at.isNoUserModification())
- {
- if (! (localOp.isInternalOperation() ||
- localOp.isSynchronizationOperation()))
- {
- localOp.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
- localOp.appendErrorMessage(ERR_ADD_ATTR_IS_NO_USER_MOD.get(
- String.valueOf(entryDN), at.getNameOrOID()));
-
- break addProcessing;
- }
- }
- }
-
- for (AttributeType at : operationalAttributes.keySet())
- {
- if (at.isNoUserModification())
- {
- if (! (localOp.isInternalOperation() ||
- localOp.isSynchronizationOperation()))
- {
- localOp.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
- localOp.appendErrorMessage(ERR_ADD_ATTR_IS_NO_USER_MOD.get(
- String.valueOf(entryDN), at.getNameOrOID()));
-
- break addProcessing;
- }
- }
- }
-
- // Check to see if the entry already exists. We do this before
- // checking whether the parent exists to ensure a referral entry
- // above the parent results in a correct referral.
- try
- {
- if (DirectoryServer.entryExists(entryDN))
- {
- localOp.setResultCode(ResultCode.ENTRY_ALREADY_EXISTS);
- localOp.appendErrorMessage(ERR_ADD_ENTRY_ALREADY_EXISTS.get(
- String.valueOf(entryDN)));
- break addProcessing;
- }
- }
- catch (DirectoryException de)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, de);
- }
-
- localOp.setResultCode(de.getResultCode());
- localOp.appendErrorMessage(de.getMessageObject());
- localOp.setMatchedDN(de.getMatchedDN());
- localOp.setReferralURLs(de.getReferralURLs());
- break addProcessing;
- }
-
-
- // Get the parent entry, if it exists.
- Entry parentEntry = null;
- if (parentDN != null)
- {
- try
- {
- parentEntry = DirectoryServer.getEntry(parentDN);
-
- if (parentEntry == null)
- {
- DN matchedDN = parentDN.getParentDNInSuffix();
- while (matchedDN != null)
- {
- try
- {
- if (DirectoryServer.entryExists(matchedDN))
- {
- localOp.setMatchedDN(matchedDN);
- break;
- }
- }
- catch (Exception e)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, e);
- }
- break;
- }
-
- matchedDN = matchedDN.getParentDNInSuffix();
- }
-
-
- // The parent doesn't exist, so this add can't be successful.
- localOp.setResultCode(ResultCode.NO_SUCH_OBJECT);
- localOp.appendErrorMessage(ERR_ADD_NO_PARENT.get(
- String.valueOf(entryDN),
- String.valueOf(parentDN)));
- break addProcessing;
- }
- }
- catch (DirectoryException de)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, de);
- }
-
- localOp.setResultCode(de.getResultCode());
- localOp.appendErrorMessage(de.getMessageObject());
- localOp.setMatchedDN(de.getMatchedDN());
- localOp.setReferralURLs(de.getReferralURLs());
- break addProcessing;
- }
- }
-
-
- // Check to make sure that all of the RDN attributes are included as
- // attribute values. If not, then either add them or report an error.
- RDN rdn = entryDN.getRDN();
- int numAVAs = rdn.getNumValues();
- for (int i=0; i < numAVAs; i++)
- {
- AttributeType t = rdn.getAttributeType(i);
- AttributeValue v = rdn.getAttributeValue(i);
- String n = rdn.getAttributeName(i);
- if (t.isOperational())
- {
- List<Attribute> attrList = operationalAttributes.get(t);
- if (attrList == null)
- {
- if (localOp.isSynchronizationOperation() ||
- DirectoryServer.addMissingRDNAttributes())
- {
- LinkedHashSet<AttributeValue> valueList =
- new LinkedHashSet<AttributeValue>(1);
- valueList.add(v);
-
- attrList = new ArrayList<Attribute>();
- attrList.add(new Attribute(t, n, valueList));
-
- operationalAttributes.put(t, attrList);
- }
- else
- {
- localOp.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
-
- localOp.appendErrorMessage(ERR_ADD_MISSING_RDN_ATTRIBUTE.get(
- String.valueOf(entryDN),
- n));
-
- break addProcessing;
- }
- }
- else
- {
- boolean found = false;
- for (Attribute a : attrList)
- {
- if (a.hasOptions())
- {
- continue;
- }
- else
- {
- if (! a.hasValue(v))
- {
- a.getValues().add(v);
- }
-
- found = true;
- break;
- }
- }
-
- if (! found)
- {
- if (localOp.isSynchronizationOperation() ||
- DirectoryServer.addMissingRDNAttributes())
- {
- LinkedHashSet<AttributeValue> valueList =
- new LinkedHashSet<AttributeValue>(1);
- valueList.add(v);
- attrList.add(new Attribute(t, n, valueList));
- }
- else
- {
- localOp.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
-
- localOp.appendErrorMessage(
- ERR_ADD_MISSING_RDN_ATTRIBUTE.get(
- String.valueOf(entryDN), n));
-
- break addProcessing;
- }
- }
- }
- }
- else
- {
- List<Attribute> attrList = userAttributes.get(t);
- if (attrList == null)
- {
- if (localOp.isSynchronizationOperation() ||
- DirectoryServer.addMissingRDNAttributes())
- {
- LinkedHashSet<AttributeValue> valueList =
- new LinkedHashSet<AttributeValue>(1);
- valueList.add(v);
-
- attrList = new ArrayList<Attribute>();
- attrList.add(new Attribute(t, n, valueList));
-
- userAttributes.put(t, attrList);
- }
- else
- {
- localOp.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
-
- localOp.appendErrorMessage(
- ERR_ADD_MISSING_RDN_ATTRIBUTE.get(
- String.valueOf(entryDN),n));
-
- break addProcessing;
- }
- }
- else
- {
- boolean found = false;
- for (Attribute a : attrList)
- {
- if (a.hasOptions())
- {
- continue;
- }
- else
- {
- if (! a.hasValue(v))
- {
- a.getValues().add(v);
- }
-
- found = true;
- break;
- }
- }
-
- if (! found)
- {
- if (localOp.isSynchronizationOperation() ||
- DirectoryServer.addMissingRDNAttributes())
- {
- LinkedHashSet<AttributeValue> valueList =
- new LinkedHashSet<AttributeValue>(1);
- valueList.add(v);
- attrList.add(new Attribute(t, n, valueList));
- }
- else
- {
- localOp.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
-
- localOp.appendErrorMessage(
- ERR_ADD_MISSING_RDN_ATTRIBUTE.get(
- String.valueOf(entryDN),n));
-
- break addProcessing;
- }
- }
- }
- }
- }
-
-
- // Check to make sure that all objectclasses have their superior classes
- // listed in the entry. If not, then add them.
- HashSet<ObjectClass> additionalClasses = null;
- for (ObjectClass oc : objectClasses.keySet())
- {
- ObjectClass superiorClass = oc.getSuperiorClass();
- if ((superiorClass != null) &&
- (! objectClasses.containsKey(superiorClass)))
- {
- if (additionalClasses == null)
- {
- additionalClasses = new HashSet<ObjectClass>();
- }
-
- additionalClasses.add(superiorClass);
- }
- }
-
- if (additionalClasses != null)
- {
- for (ObjectClass oc : additionalClasses)
- {
- localOp.addObjectClassChain(oc);
- }
- }
-
-
- // Create an entry object to encapsulate the set of attributes and
- // objectclasses.
- Entry entry = new Entry(entryDN, objectClasses, userAttributes,
- operationalAttributes);
- localOp.setEntryToAdd(entry);
-
- // Check to see if the entry includes a privilege specification. If so,
- // then the requester must have the PRIVILEGE_CHANGE privilege.
- AttributeType privType =
- DirectoryServer.getAttributeType(OP_ATTR_PRIVILEGE_NAME, true);
- if (entry.hasAttribute(privType) &&
- (! clientConnection.hasPrivilege(Privilege.PRIVILEGE_CHANGE,
- localOp)))
- {
-
- localOp.appendErrorMessage(
- ERR_ADD_CHANGE_PRIVILEGE_INSUFFICIENT_PRIVILEGES.get());
- localOp.setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS);
- break addProcessing;
- }
-
-
- // If it's not a synchronization operation, then check
- // to see if the entry contains one or more passwords and if they
- // are valid in accordance with the password policies associated with
- // the user. Also perform any encoding that might be required by
- // password storage schemes.
- if (! localOp.isSynchronizationOperation())
- {
- // FIXME -- We need to check to see if the password policy subentry
- // might be specified virtually rather than as a real
- // attribute.
- PasswordPolicy pwPolicy = null;
- List<Attribute> pwAttrList =
- entry.getAttribute(OP_ATTR_PWPOLICY_POLICY_DN);
- if ((pwAttrList != null) && (! pwAttrList.isEmpty()))
- {
- Attribute a = pwAttrList.get(0);
- LinkedHashSet<AttributeValue> valueSet = a.getValues();
- Iterator<AttributeValue> iterator = valueSet.iterator();
- if (iterator.hasNext())
- {
- DN policyDN;
- try
- {
- policyDN = DN.decode(iterator.next().getValue());
- }
- catch (DirectoryException de)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, de);
- }
-
- localOp.appendErrorMessage(
- ERR_ADD_INVALID_PWPOLICY_DN_SYNTAX.get(
- String.valueOf(entryDN),
- de.getMessageObject()));
-
- localOp.setResultCode(ResultCode.INVALID_ATTRIBUTE_SYNTAX);
- break addProcessing;
- }
-
- pwPolicy = DirectoryServer.getPasswordPolicy(policyDN);
- if (pwPolicy == null)
- {
- localOp.appendErrorMessage(
- ERR_ADD_NO_SUCH_PWPOLICY.get(String.valueOf(entryDN),
- String.valueOf(policyDN)));
-
- localOp.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
- break addProcessing;
- }
- }
- }
-
- if (pwPolicy == null)
- {
- pwPolicy = DirectoryServer.getDefaultPasswordPolicy();
- }
-
- try
- {
- localOp.handlePasswordPolicy(pwPolicy, entry);
- }
- catch (DirectoryException de)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, de);
- }
-
- localOp.setResponseData(de);
- break addProcessing;
- }
- }
-
- // If the server is configured to check schema and the
- // operation is not a synchronization operation,
- // check to see if the entry is valid according to the server schema,
- // and also whether its attributes are valid according to their syntax.
- if ((DirectoryServer.checkSchema()) &&
- (!localOp.isSynchronizationOperation()) )
- {
- MessageBuilder invalidReason = new MessageBuilder();
- if (! entry.conformsToSchema(parentEntry, true, true, true,
- invalidReason))
- {
- localOp.setResultCode(ResultCode.OBJECTCLASS_VIOLATION);
- localOp.setErrorMessage(invalidReason);
- break addProcessing;
- }
- else
- {
- switch (DirectoryServer.getSyntaxEnforcementPolicy())
- {
- case REJECT:
- invalidReason = new MessageBuilder();
- for (List<Attribute> attrList : userAttributes.values())
- {
- for (Attribute a : attrList)
- {
- AttributeSyntax syntax = a.getAttributeType().getSyntax();
- if (syntax != null)
- {
- for (AttributeValue v : a.getValues())
- {
- if (! syntax.valueIsAcceptable(v.getValue(),
- invalidReason))
- {
- Message message = WARN_ADD_OP_INVALID_SYNTAX.
- get(String.valueOf(entryDN),
- String.valueOf(v.getStringValue()),
- String.valueOf(a.getName()),
- String.valueOf(invalidReason));
- invalidReason = new MessageBuilder(message);
-
- localOp.setResultCode(
- ResultCode.INVALID_ATTRIBUTE_SYNTAX);
- localOp.setErrorMessage(invalidReason);
- break addProcessing;
- }
- }
- }
- }
- }
-
- for (List<Attribute> attrList :
- operationalAttributes.values())
- {
- for (Attribute a : attrList)
- {
- AttributeSyntax syntax = a.getAttributeType().getSyntax();
- if (syntax != null)
- {
- for (AttributeValue v : a.getValues())
- {
- if (! syntax.valueIsAcceptable(v.getValue(),
- invalidReason))
- {
- Message message = WARN_ADD_OP_INVALID_SYNTAX.
- get(String.valueOf(entryDN),
- String.valueOf(v.getStringValue()),
- String.valueOf(a.getName()),
- String.valueOf(invalidReason));
- invalidReason = new MessageBuilder(message);
-
- localOp.setResultCode(
- ResultCode.INVALID_ATTRIBUTE_SYNTAX);
- localOp.setErrorMessage(invalidReason);
- break addProcessing;
- }
- }
- }
- }
- }
-
- break;
-
-
- case WARN:
- invalidReason = new MessageBuilder();
- for (List<Attribute> attrList : userAttributes.values())
- {
- for (Attribute a : attrList)
- {
- AttributeSyntax syntax = a.getAttributeType().getSyntax();
- if (syntax != null)
- {
- for (AttributeValue v : a.getValues())
- {
- if (! syntax.valueIsAcceptable(v.getValue(),
- invalidReason))
- {
- logError(WARN_ADD_OP_INVALID_SYNTAX.
- get(String.valueOf(entryDN),
- String.valueOf(v.getStringValue()),
- String.valueOf(a.getName()),
- String.valueOf(invalidReason)));
- }
- }
- }
- }
- }
-
- for (List<Attribute> attrList : operationalAttributes.values())
- {
- for (Attribute a : attrList)
- {
- AttributeSyntax syntax = a.getAttributeType().getSyntax();
- if (syntax != null)
- {
- for (AttributeValue v : a.getValues())
- {
- if (! syntax.valueIsAcceptable(v.getValue(),
- invalidReason))
- {
- logError(WARN_ADD_OP_INVALID_SYNTAX.
- get(String.valueOf(entryDN),
- String.valueOf(v.getStringValue()),
- String.valueOf(a.getName()),
- String.valueOf(invalidReason)));
- }
- }
- }
- }
- }
-
- break;
- }
- }
-
-
- // See if the entry contains any attributes or object classes marked
- // OBSOLETE. If so, then reject the entry.
- for (AttributeType at : userAttributes.keySet())
- {
- if (at.isObsolete())
- {
- Message message = WARN_ADD_ATTR_IS_OBSOLETE.get(
- String.valueOf(entryDN),
- at.getNameOrOID());
- localOp.appendErrorMessage(message);
- localOp.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
- break addProcessing;
- }
- }
-
- for (AttributeType at : operationalAttributes.keySet())
- {
- if (at.isObsolete())
- {
- Message message = WARN_ADD_ATTR_IS_OBSOLETE.get(
- String.valueOf(entryDN),
- at.getNameOrOID());
- localOp.appendErrorMessage(message);
- localOp.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
- break addProcessing;
- }
- }
-
- for (ObjectClass oc : objectClasses.keySet())
- {
- if (oc.isObsolete())
- {
- Message message = WARN_ADD_OC_IS_OBSOLETE.get(
- String.valueOf(entryDN),
- oc.getNameOrOID());
- localOp.appendErrorMessage(message);
- localOp.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
- break addProcessing;
- }
- }
- }
-
- // Check to see if there are any controls in the request. If so, then
- // see if there is any special processing required.
- boolean noOp = false;
- LDAPPostReadRequestControl postReadRequest = null;
- List<Control> requestControls = localOp.getRequestControls();
- if ((requestControls != null) && (! requestControls.isEmpty()))
- {
- for (int i=0; i < requestControls.size(); i++)
- {
- Control c = requestControls.get(i);
- String oid = c.getOID();
-
- if (!AccessControlConfigManager.getInstance().
- getAccessControlHandler().isAllowed(parentDN, localOp, c))
- {
- localOp.setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS);
- localOp.appendErrorMessage(
- ERR_CONTROL_INSUFFICIENT_ACCESS_RIGHTS.get(oid));
- skipPostOperation = true;
- break addProcessing;
- }
-
- if (oid.equals(OID_LDAP_ASSERTION))
- {
- LDAPAssertionRequestControl assertControl;
- if (c instanceof LDAPAssertionRequestControl)
- {
- assertControl = (LDAPAssertionRequestControl) c;
- }
- else
- {
- try
- {
- assertControl = LDAPAssertionRequestControl.decodeControl(c);
- requestControls.set(i, assertControl);
- }
- catch (LDAPException le)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, le);
- }
-
- localOp.setResultCode(ResultCode.valueOf(le.getResultCode()));
- localOp.appendErrorMessage(le.getMessageObject());
-
- break addProcessing;
- }
- }
-
- try
- {
- // FIXME -- We need to determine whether the current user has
- // permission to make this determination.
- SearchFilter filter = assertControl.getSearchFilter();
- if (! filter.matchesEntry(entry))
- {
- localOp.setResultCode(ResultCode.ASSERTION_FAILED);
-
- localOp.appendErrorMessage(
- ERR_ADD_ASSERTION_FAILED.get(
- String.valueOf(entryDN)));
-
- break addProcessing;
- }
- }
- catch (DirectoryException de)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, de);
- }
-
- localOp.setResultCode(ResultCode.PROTOCOL_ERROR);
-
- localOp.appendErrorMessage(
- ERR_ADD_CANNOT_PROCESS_ASSERTION_FILTER.get(
- String.valueOf(entryDN),
- de.getMessageObject()));
-
- break addProcessing;
- }
- }
- else if (oid.equals(OID_LDAP_NOOP_OPENLDAP_ASSIGNED))
- {
- noOp = true;
- }
- else if (oid.equals(OID_LDAP_READENTRY_POSTREAD))
- {
- if (c instanceof LDAPPostReadRequestControl)
- {
- postReadRequest = (LDAPPostReadRequestControl) c;
- }
- else
- {
- try
- {
- postReadRequest = LDAPPostReadRequestControl.decodeControl(c);
- requestControls.set(i, postReadRequest);
- }
- catch (LDAPException le)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, le);
- }
-
- localOp.setResultCode(ResultCode.valueOf(le.getResultCode()));
- localOp.appendErrorMessage(le.getMessageObject());
-
- break addProcessing;
- }
- }
- }
- else if (oid.equals(OID_PROXIED_AUTH_V1))
- {
- // The requester must have the PROXIED_AUTH privilige in order to
- // be able to use this control.
- if (! clientConnection.hasPrivilege(Privilege.PROXIED_AUTH,
- localOp))
- {
- localOp.appendErrorMessage(
- ERR_PROXYAUTH_INSUFFICIENT_PRIVILEGES.get());
- localOp.setResultCode(ResultCode.AUTHORIZATION_DENIED);
- break addProcessing;
- }
-
-
- ProxiedAuthV1Control proxyControl;
- if (c instanceof ProxiedAuthV1Control)
- {
- proxyControl = (ProxiedAuthV1Control) c;
- }
- else
- {
- try
- {
- proxyControl = ProxiedAuthV1Control.decodeControl(c);
- }
- catch (LDAPException le)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, le);
- }
-
- localOp.setResultCode(ResultCode.valueOf(le.getResultCode()));
- localOp.appendErrorMessage(le.getMessageObject());
-
- break addProcessing;
- }
- }
-
-
- Entry authorizationEntry;
- try
- {
- authorizationEntry = proxyControl.getAuthorizationEntry();
- }
- catch (DirectoryException de)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, de);
- }
-
- localOp.setResultCode(de.getResultCode());
- localOp.appendErrorMessage(de.getMessageObject());
-
- break addProcessing;
- }
-
- localOp.setAuthorizationEntry(authorizationEntry);
- if (authorizationEntry == null)
- {
- localOp.setProxiedAuthorizationDN(DN.nullDN());
- }
- else
- {
- localOp.setProxiedAuthorizationDN(authorizationEntry.getDN());
- }
- }
- else if (oid.equals(OID_PROXIED_AUTH_V2))
- {
- // The requester must have the PROXIED_AUTH privilige in order to
- // be able to use this control.
- if (! clientConnection.hasPrivilege(Privilege.PROXIED_AUTH,
- localOp))
- {
- localOp.appendErrorMessage(
- ERR_PROXYAUTH_INSUFFICIENT_PRIVILEGES.get());
- localOp.setResultCode(ResultCode.AUTHORIZATION_DENIED);
- break addProcessing;
- }
-
-
- ProxiedAuthV2Control proxyControl;
- if (c instanceof ProxiedAuthV2Control)
- {
- proxyControl = (ProxiedAuthV2Control) c;
- }
- else
- {
- try
- {
- proxyControl = ProxiedAuthV2Control.decodeControl(c);
- }
- catch (LDAPException le)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, le);
- }
-
- localOp.setResultCode(ResultCode.valueOf(le.getResultCode()));
- localOp.appendErrorMessage(le.getMessageObject());
-
- break addProcessing;
- }
- }
-
-
- Entry authorizationEntry;
- try
- {
- authorizationEntry = proxyControl.getAuthorizationEntry();
- }
- catch (DirectoryException de)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, de);
- }
-
- localOp.setResultCode(de.getResultCode());
- localOp.appendErrorMessage(de.getMessageObject());
-
- break addProcessing;
- }
-
- localOp.setAuthorizationEntry(authorizationEntry);
- if (authorizationEntry == null)
- {
- localOp.setProxiedAuthorizationDN(DN.nullDN());
- }
- else
- {
- localOp.setProxiedAuthorizationDN(authorizationEntry.getDN());
- }
- }
- else if (oid.equals(OID_PASSWORD_POLICY_CONTROL))
- {
- // We don't need to do anything here because it's already handled
- // in LocalBackendAddOperation.handlePasswordPolicy().
- }
-
- // NYI -- Add support for additional controls.
- else if (c.isCritical())
- {
- if ((backend == null) || (! backend.supportsControl(oid)))
- {
- localOp.setResultCode(
- ResultCode.UNAVAILABLE_CRITICAL_EXTENSION);
-
- localOp.appendErrorMessage(
- ERR_ADD_UNSUPPORTED_CRITICAL_CONTROL.get(
- String.valueOf(entryDN),
- oid));
-
- break addProcessing;
- }
- }
- }
- }
-
-
- // Check to see if the client has permission to perform the add.
-
- // FIXME: for now assume that this will check all permission
- // pertinent to the operation. This includes proxy authorization
- // and any other controls specified.
-
- // FIXME: earlier checks to see if the entry already exists or
- // if the parent entry does not exist may have already exposed
- // sensitive information to the client.
- if (AccessControlConfigManager.getInstance()
- .getAccessControlHandler().isAllowed(localOp) == false) {
- localOp.setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS);
-
- localOp.appendErrorMessage(ERR_ADD_AUTHZ_INSUFFICIENT_ACCESS_RIGHTS
- .get(String.valueOf(entryDN)));
-
- skipPostOperation = true;
- break addProcessing;
- }
-
- // Check for a request to cancel this operation.
- if (localOp.getCancelRequest() != null)
- {
- return;
- }
-
-
- // If the operation is not a synchronization operation,
- // Invoke the pre-operation add plugins.
- if (!localOp.isSynchronizationOperation())
- {
- PreOperationPluginResult preOpResult =
- pluginConfigManager.invokePreOperationAddPlugins(localOp);
- if (preOpResult.connectionTerminated())
- {
- // There's no point in continuing with anything. Log the result
- // and return.
- localOp.setResultCode(ResultCode.CANCELED);
-
- localOp.appendErrorMessage(ERR_CANCELED_BY_PREOP_DISCONNECT.get());
-
- return;
- }
- else if (preOpResult.sendResponseImmediately())
- {
- skipPostOperation = true;
- break addProcessing;
- }
- else if (preOpResult.skipCoreProcessing())
- {
- skipPostOperation = false;
- break addProcessing;
- }
- }
-
-
- // Check for a request to cancel this operation.
- if (localOp.getCancelRequest() != null)
- {
- return;
- }
-
-
- // Actually perform the add operation. This should also include taking
- // care of any synchronization that might be needed.
- Backend backend = DirectoryServer.getBackend(entryDN);
- if (backend == null)
- {
- localOp.setResultCode(ResultCode.NO_SUCH_OBJECT);
- localOp.appendErrorMessage(Message.raw("No backend for entry " +
- entryDN.toString())); // TODO: i18n
- }
- else
- {
- // If it is not a private backend, then check to see if the server or
- // backend is operating in read-only mode.
- if (! backend.isPrivateBackend())
- {
- switch (DirectoryServer.getWritabilityMode())
- {
- case DISABLED:
- localOp.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
- localOp.appendErrorMessage(ERR_ADD_SERVER_READONLY.get(
- String.valueOf(entryDN)));
- break addProcessing;
-
- case INTERNAL_ONLY:
- if (! (localOp.isInternalOperation() ||
- localOp.isSynchronizationOperation()))
- {
- localOp.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
- localOp.appendErrorMessage(ERR_ADD_SERVER_READONLY.get(
- String.valueOf(entryDN)));
- break addProcessing;
- }
- }
-
- switch (backend.getWritabilityMode())
- {
- case DISABLED:
- localOp.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
- localOp.appendErrorMessage(ERR_ADD_BACKEND_READONLY.get(
- String.valueOf(entryDN)));
- break addProcessing;
-
- case INTERNAL_ONLY:
- if (! (localOp.isInternalOperation() ||
- localOp.isSynchronizationOperation()))
- {
- localOp.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
- localOp.appendErrorMessage(ERR_ADD_BACKEND_READONLY.get(
- String.valueOf(entryDN)));
- break addProcessing;
- }
- }
- }
-
-
- try
- {
- if (noOp)
- {
- localOp.appendErrorMessage(INFO_ADD_NOOP.get());
-
- localOp.setResultCode(ResultCode.NO_OPERATION);
- }
- else
- {
- for (SynchronizationProvider provider :
- DirectoryServer.getSynchronizationProviders())
- {
- try
- {
- SynchronizationProviderResult result =
- provider.doPreOperation(localOp);
- if (! result.continueOperationProcessing())
- {
- break addProcessing;
- }
- }
- catch (DirectoryException de)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, de);
- }
-
- logError(ERR_ADD_SYNCH_PREOP_FAILED.
- get(localOp.getConnectionID(), localOp.getOperationID(),
- getExceptionMessage(de)));
-
- localOp.setResponseData(de);
- break addProcessing;
- }
- }
-
- backend.addEntry(entry, localOp);
- }
-
- if (postReadRequest != null)
- {
- Entry addedEntry = entry.duplicate(true);
-
- if (! postReadRequest.allowsAttribute(
- DirectoryServer.getObjectClassAttributeType()))
- {
- addedEntry.removeAttribute(
- DirectoryServer.getObjectClassAttributeType());
- }
-
- if (! postReadRequest.returnAllUserAttributes())
- {
- Iterator<AttributeType> iterator =
- addedEntry.getUserAttributes().keySet().iterator();
- while (iterator.hasNext())
- {
- AttributeType attrType = iterator.next();
- if (! postReadRequest.allowsAttribute(attrType))
- {
- iterator.remove();
- }
- }
- }
-
- if (! postReadRequest.returnAllOperationalAttributes())
- {
- Iterator<AttributeType> iterator =
- addedEntry.getOperationalAttributes().keySet().iterator();
- while (iterator.hasNext())
- {
- AttributeType attrType = iterator.next();
- if (! postReadRequest.allowsAttribute(attrType))
- {
- iterator.remove();
- }
- }
- }
-
- // FIXME -- Check access controls on the entry to see if it should
- // be returned or if any attributes need to be stripped
- // out..
- SearchResultEntry searchEntry = new SearchResultEntry(addedEntry);
- LDAPPostReadResponseControl responseControl =
- new LDAPPostReadResponseControl(postReadRequest.getOID(),
- postReadRequest.isCritical(),
- searchEntry);
-
- localOp.addResponseControl(responseControl);
- }
-
-
- if (! noOp)
- {
- localOp.setResultCode(ResultCode.SUCCESS);
- }
- }
- catch (DirectoryException de)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, de);
- }
-
- localOp.setResultCode(de.getResultCode());
- localOp.appendErrorMessage(de.getMessageObject());
- localOp.setMatchedDN(de.getMatchedDN());
- localOp.setReferralURLs(de.getReferralURLs());
-
- break addProcessing;
- }
- catch (CancelledOperationException coe)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, coe);
- }
-
- CancelResult cancelResult = coe.getCancelResult();
-
- localOp.setCancelResult(cancelResult);
- localOp.setResultCode(cancelResult.getResultCode());
-
- Message message = coe.getMessageObject();
- if ((message != null) && (message.length() > 0))
- {
- localOp.appendErrorMessage(message);
- }
-
- break addProcessing;
- }
- }
- }
- finally
- {
- if (entryLock != null)
- {
- LockManager.unlock(entryDN, entryLock);
- }
-
- if (parentLock != null)
- {
- LockManager.unlock(parentDN, parentLock);
- }
-
-
- for (SynchronizationProvider provider :
- DirectoryServer.getSynchronizationProviders())
- {
- try
- {
- provider.doPostOperation(localOp);
- }
- catch (DirectoryException de)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, de);
- }
-
- logError(ERR_ADD_SYNCH_POSTOP_FAILED.
- get(localOp.getConnectionID(), localOp.getOperationID(),
- getExceptionMessage(de)));
-
- localOp.setResponseData(de);
- break;
- }
- }
- }
- }
-
-
- // Indicate that it is now too late to attempt to cancel the operation.
- localOp.setCancelResult(CancelResult.TOO_LATE);
-
-
- // Invoke the post-operation or post-synchronization add plugins.
- if (localOp.isSynchronizationOperation())
- {
- if (localOp.getResultCode() == ResultCode.SUCCESS)
- {
- pluginConfigManager.invokePostSynchronizationAddPlugins(localOp);
- }
- }
- else if (! skipPostOperation)
- {
- // FIXME -- Should this also be done while holding the locks?
- PostOperationPluginResult postOpResult =
- pluginConfigManager.invokePostOperationAddPlugins(localOp);
- if (postOpResult.connectionTerminated())
- {
- // There's no point in continuing with anything. Log the result and
- // return.
- localOp.setResultCode(ResultCode.CANCELED);
-
- localOp.appendErrorMessage(ERR_CANCELED_BY_PREOP_DISCONNECT.get());
-
- return;
- }
- }
-
-
- // Notify any change notification listeners that might be registered with
- // the server.
- if ((localOp.getResultCode() == ResultCode.SUCCESS) &&
- (localOp.getEntryToAdd() != null))
- {
- for (ChangeNotificationListener changeListener :
- DirectoryServer.getChangeNotificationListeners())
- {
- try
- {
- changeListener.handleAddOperation(localOp, localOp.getEntryToAdd());
- }
- catch (Exception e)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, e);
- }
-
- Message message = ERR_ADD_ERROR_NOTIFYING_CHANGE_LISTENER.get(
- getExceptionMessage(e));
- logError(message);
- }
- }
- }
- }
-
-
- /**
- * Performs a delete operation against a local backend.
- *
- * @param operation the operation to perform
- */
- public void processDelete(DeleteOperation operation){
- LocalBackendDeleteOperation localOperation =
- new LocalBackendDeleteOperation(operation);
- processLocalDelete(localOperation);
- }
-
- /**
- * Performs a local delete operation against a local backend.
- *
- * @param localOp the operation to perform
- */
- private void processLocalDelete(LocalBackendDeleteOperation localOp)
- {
- ClientConnection clientConnection = localOp.getClientConnection();
-
- // Get the plugin config manager that will be used for invoking plugins.
- PluginConfigManager pluginConfigManager =
- DirectoryServer.getPluginConfigManager();
- boolean skipPostOperation = false;
-
- // Check for a request to cancel this operation.
- if (localOp.getCancelRequest() != null)
- {
- return;
- }
-
- // Create a labeled block of code that we can break out of if a problem is
- // detected.
-deleteProcessing:
- {
- // Process the entry DN to convert it from its raw form as provided by the
- // client to the form required for the rest of the delete processing.
- DN entryDN = localOp.getEntryDN();
- if (entryDN == null){
- break deleteProcessing;
- }
-
- // Grab a write lock on the entry.
- Lock entryLock = null;
- for (int i=0; i < 3; i++)
- {
- entryLock = LockManager.lockWrite(entryDN);
- if (entryLock != null)
- {
- break;
- }
- }
-
- if (entryLock == null)
- {
- localOp.setResultCode(DirectoryServer.getServerErrorResultCode());
- localOp.appendErrorMessage(ERR_DELETE_CANNOT_LOCK_ENTRY.get(
- String.valueOf(entryDN)));
- break deleteProcessing;
- }
-
- Entry entry = null;
- try
- {
- // Get the entry to delete. If it doesn't exist, then fail.
- try
- {
- entry = backend.getEntry(entryDN);
- localOp.setEntryToDelete(entry);
- if (entry == null)
- {
- localOp.setResultCode(ResultCode.NO_SUCH_OBJECT);
- localOp.appendErrorMessage(ERR_DELETE_NO_SUCH_ENTRY.get(
- String.valueOf(entryDN)));
-
- try
- {
- DN parentDN = entryDN.getParentDNInSuffix();
- while (parentDN != null)
- {
- if (DirectoryServer.entryExists(parentDN))
- {
- localOp.setMatchedDN(parentDN);
- break;
- }
-
- parentDN = parentDN.getParentDNInSuffix();
- }
- }
- catch (Exception e)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, e);
- }
- }
-
- break deleteProcessing;
- }
- }
- catch (DirectoryException de)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, de);
- }
-
- localOp.setResultCode(de.getResultCode());
- localOp.appendErrorMessage(de.getMessageObject());
- localOp.setMatchedDN(de.getMatchedDN());
- localOp.setReferralURLs(de.getReferralURLs());
- break deleteProcessing;
- }
-
-
- // Invoke any conflict resolution processing that might be needed by the
- // synchronization provider.
- for (SynchronizationProvider provider :
- DirectoryServer.getSynchronizationProviders())
- {
- try
- {
- SynchronizationProviderResult result =
- provider.handleConflictResolution(localOp);
- if (! result.continueOperationProcessing())
- {
- break deleteProcessing;
- }
- }
- catch (DirectoryException de)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, de);
- }
-
- logError(ERR_DELETE_SYNCH_CONFLICT_RESOLUTION_FAILED.
- get(localOp.getConnectionID(), localOp.getOperationID(),
- getExceptionMessage(de)));
-
- localOp.setResponseData(de);
- break deleteProcessing;
- }
- }
-
- // Check to see if the client has permission to perform the
- // delete.
-
- // Check to see if there are any controls in the request. If so, then
- // see if there is any special processing required.
- boolean noOp = false;
- LDAPPreReadRequestControl preReadRequest = null;
- List<Control> requestControls =
- localOp.getRequestControls();
- if ((requestControls != null) && (! requestControls.isEmpty()))
- {
- for (int i=0; i < requestControls.size(); i++)
- {
- Control c = requestControls.get(i);
- String oid = c.getOID();
-
- if (!AccessControlConfigManager.getInstance().
- getAccessControlHandler().isAllowed(entryDN, localOp, c))
- {
- localOp.setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS);
-
- localOp.appendErrorMessage(
- ERR_CONTROL_INSUFFICIENT_ACCESS_RIGHTS.get(oid));
- skipPostOperation = true;
- break deleteProcessing;
- }
-
- if (oid.equals(OID_LDAP_ASSERTION))
- {
- LDAPAssertionRequestControl assertControl;
- if (c instanceof LDAPAssertionRequestControl)
- {
- assertControl = (LDAPAssertionRequestControl) c;
- }
- else
- {
- try
- {
- assertControl = LDAPAssertionRequestControl.decodeControl(c);
- requestControls.set(i, assertControl);
- }
- catch (LDAPException le)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, le);
- }
-
- localOp.setResultCode(ResultCode.valueOf(le.getResultCode()));
- localOp.appendErrorMessage(le.getMessageObject());
-
- break deleteProcessing;
- }
- }
-
- try
- {
- // FIXME -- We need to determine whether the current user has
- // permission to make this determination.
- SearchFilter filter = assertControl.getSearchFilter();
- if (! filter.matchesEntry(entry))
- {
- localOp.setResultCode(ResultCode.ASSERTION_FAILED);
-
- localOp.appendErrorMessage(
- ERR_DELETE_ASSERTION_FAILED.get(
- String.valueOf(entryDN)));
-
- break deleteProcessing;
- }
- }
- catch (DirectoryException de)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, de);
- }
-
- localOp.setResultCode(ResultCode.PROTOCOL_ERROR);
-
- localOp.appendErrorMessage(
- ERR_DELETE_CANNOT_PROCESS_ASSERTION_FILTER.get(
- String.valueOf(entryDN),
- de.getMessageObject()));
-
- break deleteProcessing;
- }
- }
- else if (oid.equals(OID_LDAP_NOOP_OPENLDAP_ASSIGNED))
- {
- noOp = true;
- }
- else if (oid.equals(OID_LDAP_READENTRY_PREREAD))
- {
- if (c instanceof LDAPPreReadRequestControl)
- {
- preReadRequest = (LDAPPreReadRequestControl) c;
- }
- else
- {
- try
- {
- preReadRequest = LDAPPreReadRequestControl.decodeControl(c);
- requestControls.set(i, preReadRequest);
- }
- catch (LDAPException le)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, le);
- }
-
- localOp.setResultCode(ResultCode.valueOf(le.getResultCode()));
- localOp.appendErrorMessage(le.getMessageObject());
-
- break deleteProcessing;
- }
- }
- }
- else if (oid.equals(OID_PROXIED_AUTH_V1))
- {
- // The requester must have the PROXIED_AUTH privilige in order to
- // be able to use this control.
- if (! clientConnection.hasPrivilege(Privilege.PROXIED_AUTH,
- localOp))
- {
-
- localOp.appendErrorMessage(
- ERR_PROXYAUTH_INSUFFICIENT_PRIVILEGES.get());
- localOp.setResultCode(ResultCode.AUTHORIZATION_DENIED);
- break deleteProcessing;
- }
-
-
- ProxiedAuthV1Control proxyControl;
- if (c instanceof ProxiedAuthV1Control)
- {
- proxyControl = (ProxiedAuthV1Control) c;
- }
- else
- {
- try
- {
- proxyControl = ProxiedAuthV1Control.decodeControl(c);
- }
- catch (LDAPException le)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, le);
- }
-
- localOp.setResultCode(ResultCode.valueOf(le.getResultCode()));
- localOp.appendErrorMessage(le.getMessageObject());
-
- break deleteProcessing;
- }
- }
-
-
- Entry authorizationEntry;
- try
- {
- authorizationEntry = proxyControl.getAuthorizationEntry();
- }
- catch (DirectoryException de)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, de);
- }
-
- localOp.setResultCode(de.getResultCode());
- localOp.appendErrorMessage(de.getMessageObject());
-
- break deleteProcessing;
- }
-
- localOp.setAuthorizationEntry(authorizationEntry);
- if (authorizationEntry == null)
- {
- localOp.setProxiedAuthorizationDN(DN.nullDN());
- }
- else
- {
- localOp.setProxiedAuthorizationDN(authorizationEntry.getDN());
- }
- }
- else if (oid.equals(OID_PROXIED_AUTH_V2))
- {
- // The requester must have the PROXIED_AUTH privilige in order to
- // be able to use this control.
- if (! clientConnection.hasPrivilege(Privilege.PROXIED_AUTH,
- localOp))
- {
- localOp.appendErrorMessage(
- ERR_PROXYAUTH_INSUFFICIENT_PRIVILEGES.get());
- localOp.setResultCode(ResultCode.AUTHORIZATION_DENIED);
- break deleteProcessing;
- }
-
-
- ProxiedAuthV2Control proxyControl;
- if (c instanceof ProxiedAuthV2Control)
- {
- proxyControl = (ProxiedAuthV2Control) c;
- }
- else
- {
- try
- {
- proxyControl = ProxiedAuthV2Control.decodeControl(c);
- }
- catch (LDAPException le)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, le);
- }
-
- localOp.setResultCode(ResultCode.valueOf(le.getResultCode()));
- localOp.appendErrorMessage(le.getMessageObject());
-
- break deleteProcessing;
- }
- }
-
-
- Entry authorizationEntry;
- try
- {
- authorizationEntry = proxyControl.getAuthorizationEntry();
- }
- catch (DirectoryException de)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, de);
- }
-
- localOp.setResultCode(de.getResultCode());
- localOp.appendErrorMessage(de.getMessageObject());
-
- break deleteProcessing;
- }
-
- localOp.setAuthorizationEntry(authorizationEntry);
- if (authorizationEntry == null)
- {
- localOp.setProxiedAuthorizationDN(DN.nullDN());
- }
- else
- {
- localOp.setProxiedAuthorizationDN(authorizationEntry.getDN());
- }
- }
-
- // NYI -- Add support for additional controls.
- else if (c.isCritical())
- {
- Backend backend = DirectoryServer.getBackend(entryDN);
- if ((backend == null) || (! backend.supportsControl(oid)))
- {
- localOp.setResultCode(
- ResultCode.UNAVAILABLE_CRITICAL_EXTENSION);
-
- localOp.appendErrorMessage(
- ERR_DELETE_UNSUPPORTED_CRITICAL_CONTROL.get(
- String.valueOf(entryDN),
- oid));
-
- break deleteProcessing;
- }
- }
- }
- }
-
-
- // FIXME: for now assume that this will check all permission
- // pertinent to the operation. This includes proxy authorization
- // and any other controls specified.
-
- // FIXME: earlier checks to see if the entry already exists may
- // have already exposed sensitive information to the client.
- if (AccessControlConfigManager.getInstance()
- .getAccessControlHandler().isAllowed(localOp) == false) {
- localOp.setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS);
-
- localOp.appendErrorMessage(
- ERR_DELETE_AUTHZ_INSUFFICIENT_ACCESS_RIGHTS.get(
- String.valueOf(entryDN)));
-
- skipPostOperation = true;
- break deleteProcessing;
- }
-
- // Check for a request to cancel this operation.
- if (localOp.getCancelRequest() != null)
- {
- return;
- }
-
-
- // If the operation is not a synchronization operation,
- // invoke the pre-delete plugins.
- if (!localOp.isSynchronizationOperation())
- {
- PreOperationPluginResult preOpResult =
- pluginConfigManager.invokePreOperationDeletePlugins(localOp);
- if (preOpResult.connectionTerminated())
- {
- // There's no point in continuing with anything. Log the request
- // and result and return.
- localOp.setResultCode(ResultCode.CANCELED);
-
- localOp.appendErrorMessage(ERR_CANCELED_BY_PREOP_DISCONNECT.get());
-
- localOp.setProcessingStopTime();
- return;
- }
- else if (preOpResult.sendResponseImmediately())
- {
- skipPostOperation = true;
- break deleteProcessing;
- }
- else if (preOpResult.skipCoreProcessing())
- {
- skipPostOperation = false;
- break deleteProcessing;
- }
- }
-
-
- // Check for a request to cancel this operation.
- if (localOp.getCancelRequest() != null)
- {
- return;
- }
-
-
- // Get the backend to use for the delete. If there is none, then fail.
- if (backend == null)
- {
- localOp.setResultCode(ResultCode.NO_SUCH_OBJECT);
- localOp.appendErrorMessage(
- ERR_DELETE_NO_SUCH_ENTRY.get(String.valueOf(entryDN)));
- break deleteProcessing;
- }
-
-
- // If it is not a private backend, then check to see if the server or
- // backend is operating in read-only mode.
- if (! backend.isPrivateBackend())
- {
- switch (DirectoryServer.getWritabilityMode())
- {
- case DISABLED:
- localOp.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
- localOp.appendErrorMessage(
- ERR_DELETE_SERVER_READONLY.get(String.valueOf(entryDN)));
- break deleteProcessing;
-
- case INTERNAL_ONLY:
- if (! (localOp.isInternalOperation() ||
- localOp.isSynchronizationOperation()))
- {
- localOp.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
- localOp.appendErrorMessage(
- ERR_DELETE_SERVER_READONLY.get(
- String.valueOf(entryDN)));
- break deleteProcessing;
- }
- }
-
- switch (backend.getWritabilityMode())
- {
- case DISABLED:
- localOp.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
- localOp.appendErrorMessage(
- ERR_DELETE_BACKEND_READONLY.get(String.valueOf(entryDN)));
- break deleteProcessing;
-
- case INTERNAL_ONLY:
- if (! (localOp.isInternalOperation() ||
- localOp.isSynchronizationOperation()))
- {
- localOp.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
- localOp.appendErrorMessage(
- ERR_DELETE_BACKEND_READONLY.get(
- String.valueOf(entryDN)));
- break deleteProcessing;
- }
- }
- }
-
-
- // The selected backend will have the responsibility of making sure that
- // the entry actually exists and does not have any children (or possibly
- // handling a subtree delete). But we will need to check if there are
- // any subordinate backends that should stop us from attempting the
- // delete.
- Backend[] subBackends = backend.getSubordinateBackends();
- for (Backend b : subBackends)
- {
- DN[] baseDNs = b.getBaseDNs();
- for (DN dn : baseDNs)
- {
- if (dn.isDescendantOf(entryDN))
- {
- localOp.setResultCode(ResultCode.NOT_ALLOWED_ON_NONLEAF);
- localOp.appendErrorMessage(ERR_DELETE_HAS_SUB_BACKEND.get(
- String.valueOf(entryDN),
- String.valueOf(dn)));
- break deleteProcessing;
- }
- }
- }
-
-
- // Actually perform the delete.
- try
- {
- if (noOp)
- {
- localOp.appendErrorMessage(INFO_DELETE_NOOP.get());
-
- localOp.setResultCode(ResultCode.NO_OPERATION);
- }
- else
- {
- for (SynchronizationProvider provider :
- DirectoryServer.getSynchronizationProviders())
- {
- try
- {
- SynchronizationProviderResult result =
- provider.doPreOperation(localOp);
- if (! result.continueOperationProcessing())
- {
- break deleteProcessing;
- }
- }
- catch (DirectoryException de)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, de);
- }
-
- logError(ERR_DELETE_SYNCH_PREOP_FAILED.
- get(localOp.getConnectionID(), localOp.getOperationID(),
- getExceptionMessage(de)));
-
- localOp.setResponseData(de);
- break deleteProcessing;
- }
- }
-
- backend.deleteEntry(entryDN, localOp);
- }
-
- if (preReadRequest != null)
- {
- Entry entryCopy = entry.duplicate(true);
-
- if (! preReadRequest.allowsAttribute(
- DirectoryServer.getObjectClassAttributeType()))
- {
- entryCopy.removeAttribute(
- DirectoryServer.getObjectClassAttributeType());
- }
-
- if (! preReadRequest.returnAllUserAttributes())
- {
- Iterator<AttributeType> iterator =
- entryCopy.getUserAttributes().keySet().iterator();
- while (iterator.hasNext())
- {
- AttributeType attrType = iterator.next();
- if (! preReadRequest.allowsAttribute(attrType))
- {
- iterator.remove();
- }
- }
- }
-
- if (! preReadRequest.returnAllOperationalAttributes())
- {
- Iterator<AttributeType> iterator =
- entryCopy.getOperationalAttributes().keySet().iterator();
- while (iterator.hasNext())
- {
- AttributeType attrType = iterator.next();
- if (! preReadRequest.allowsAttribute(attrType))
- {
- iterator.remove();
- }
- }
- }
-
- // FIXME -- Check access controls on the entry to see if it should
- // be returned or if any attributes need to be stripped
- // out..
- SearchResultEntry searchEntry = new SearchResultEntry(entryCopy);
- LDAPPreReadResponseControl responseControl =
- new LDAPPreReadResponseControl(preReadRequest.getOID(),
- preReadRequest.isCritical(),
- searchEntry);
-
- localOp.addResponseControl(responseControl);
- }
-
-
- if (! noOp)
- {
- localOp.setResultCode(ResultCode.SUCCESS);
- }
- }
- catch (DirectoryException de)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, de);
- }
-
- localOp.setResultCode(de.getResultCode());
- localOp.appendErrorMessage(de.getMessageObject());
- localOp.setMatchedDN(de.getMatchedDN());
- localOp.setReferralURLs(de.getReferralURLs());
-
- break deleteProcessing;
- }
- catch (CancelledOperationException coe)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, coe);
- }
-
- CancelResult cancelResult = coe.getCancelResult();
-
- localOp.setCancelResult(cancelResult);
- localOp.setResultCode(cancelResult.getResultCode());
-
- Message message = coe.getMessageObject();
- if ((message != null) && (message.length() > 0))
- {
- localOp.appendErrorMessage(message);
- }
-
- break deleteProcessing;
- }
- }
- finally
- {
- LockManager.unlock(entryDN, entryLock);
-
- for (SynchronizationProvider provider :
- DirectoryServer.getSynchronizationProviders())
- {
- try
- {
- provider.doPostOperation(localOp);
- }
- catch (DirectoryException de)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, de);
- }
-
- logError(ERR_DELETE_SYNCH_POSTOP_FAILED.
- get(localOp.getConnectionID(), localOp.getOperationID(),
- getExceptionMessage(de)));
-
- localOp.setResponseData(de);
- break;
- }
- }
- }
- }
-
-
- // Indicate that it is now too late to attempt to cancel the operation.
- localOp.setCancelResult(CancelResult.TOO_LATE);
-
-
- // Invoke the post-operation or post-synchronization delete plugins.
- if (localOp.isSynchronizationOperation())
- {
- if (localOp.getResultCode() == ResultCode.SUCCESS)
- {
- pluginConfigManager.invokePostSynchronizationDeletePlugins(localOp);
- }
- }
- else if (! skipPostOperation)
- {
- PostOperationPluginResult postOperationResult =
- pluginConfigManager.invokePostOperationDeletePlugins(localOp);
- if (postOperationResult.connectionTerminated())
- {
- localOp.setResultCode(ResultCode.CANCELED);
-
- localOp.appendErrorMessage(ERR_CANCELED_BY_POSTOP_DISCONNECT.get());
-
- localOp.setProcessingStopTime();
- return;
- }
- }
-
-
- // Notify any change notification listeners that might be registered with
- // the server.
- if (localOp.getResultCode() == ResultCode.SUCCESS)
- {
- for (ChangeNotificationListener changeListener :
- DirectoryServer.getChangeNotificationListeners())
- {
- try
- {
- changeListener.handleDeleteOperation(localOp,
- localOp.getEntryToDelete());
- }
- catch (Exception e)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, e);
- }
-
- Message message = ERR_DELETE_ERROR_NOTIFYING_CHANGE_LISTENER.get(
- getExceptionMessage(e));
- logError(message);
- }
- }
- }
-
- // Stop the processing timer.
- localOp.setProcessingStopTime();
- }
-
-
-
- /**
- * Perform a compare operation against a local backend.
- *
- * @param operation - The operation to perform
- */
- public void processCompare(CompareOperation operation)
- {
- LocalBackendCompareOperation localOperation =
- new LocalBackendCompareOperation(operation);
- processLocalCompare(localOperation);
- }
-
-
- /**
- * Perform a local compare operation against a local backend.
- *
- * @param localOp - The operation to perform
- */
- private void processLocalCompare(LocalBackendCompareOperation localOp)
- {
- // Get the plugin config manager that will be used for invoking plugins.
- PluginConfigManager pluginConfigManager =
- DirectoryServer.getPluginConfigManager();
- boolean skipPostOperation = false;
-
-
- // Get a reference to the client connection
- ClientConnection clientConnection = localOp.getClientConnection();
-
-
- // Check for a request to cancel this operation.
- if (localOp.getCancelRequest() != null)
- {
- return;
- }
-
-
- // Create a labeled block of code that we can break out of if a problem is
- // detected.
-compareProcessing:
- {
- // Process the entry DN to convert it from the raw form to the form
- // required for the rest of the compare processing.
- DN entryDN = localOp.getEntryDN();
- if (entryDN == null)
- {
- skipPostOperation = true;
- break compareProcessing;
- }
-
-
- // If the target entry is in the server configuration, then make sure the
- // requester has the CONFIG_READ privilege.
- if (DirectoryServer.getConfigHandler().handlesEntry(entryDN) &&
- (! clientConnection.hasPrivilege(Privilege.CONFIG_READ, localOp)))
- {
- localOp.appendErrorMessage(
- ERR_COMPARE_CONFIG_INSUFFICIENT_PRIVILEGES.get());
- localOp.setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS);
- skipPostOperation = true;
- break compareProcessing;
- }
-
-
- // Check for a request to cancel this operation.
- if (localOp.getCancelRequest() != null)
- {
- return;
- }
-
-
- // Grab a read lock on the entry.
- Lock readLock = null;
- for (int i=0; i < 3; i++)
- {
- readLock = LockManager.lockRead(entryDN);
- if (readLock != null)
- {
- break;
- }
- }
-
- if (readLock == null)
- {
- Message message = ERR_COMPARE_CANNOT_LOCK_ENTRY.get(
- String.valueOf(entryDN));
-
- localOp.setResultCode(DirectoryServer.getServerErrorResultCode());
- localOp.appendErrorMessage(message);
- skipPostOperation = true;
- break compareProcessing;
- }
-
- Entry entry = null;
- try
- {
- // Get the entry. If it does not exist, then fail.
- try
- {
- entry = DirectoryServer.getEntry(entryDN);
- localOp.setEntryToCompare(entry);
-
- if (entry == null)
- {
- localOp.setResultCode(ResultCode.NO_SUCH_OBJECT);
- localOp.appendErrorMessage(
- ERR_COMPARE_NO_SUCH_ENTRY.get(String.valueOf(entryDN)));
-
- // See if one of the entry's ancestors exists.
- DN parentDN = entryDN.getParentDNInSuffix();
- while (parentDN != null)
- {
- try
- {
- if (DirectoryServer.entryExists(parentDN))
- {
- localOp.setMatchedDN(parentDN);
- break;
- }
- }
- catch (Exception e)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, e);
- }
- break;
- }
-
- parentDN = parentDN.getParentDNInSuffix();
- }
-
- break compareProcessing;
- }
- }
- catch (DirectoryException de)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, de);
- }
-
- localOp.setResultCode(de.getResultCode());
- localOp.appendErrorMessage(de.getMessageObject());
- break compareProcessing;
- }
-
- // Check to see if there are any controls in the request. If so, then
- // see if there is any special processing required.
- List<Control> requestControls = localOp.getRequestControls();
- if ((requestControls != null) && (! requestControls.isEmpty()))
- {
- for (int i=0; i < requestControls.size(); i++)
- {
- Control c = requestControls.get(i);
- String oid = c.getOID();
-
- if (!AccessControlConfigManager.getInstance().
- getAccessControlHandler().
- isAllowed(entryDN, localOp, c))
- {
- localOp.setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS);
-
- localOp.appendErrorMessage(
- ERR_CONTROL_INSUFFICIENT_ACCESS_RIGHTS.get(oid));
- skipPostOperation = true;
- break compareProcessing;
- }
-
- if (oid.equals(OID_LDAP_ASSERTION))
- {
- LDAPAssertionRequestControl assertControl;
- if (c instanceof LDAPAssertionRequestControl)
- {
- assertControl = (LDAPAssertionRequestControl) c;
- }
- else
- {
- try
- {
- assertControl = LDAPAssertionRequestControl.decodeControl(c);
- requestControls.set(i, assertControl);
- }
- catch (LDAPException le)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, le);
- }
-
- localOp.setResultCode(ResultCode.valueOf(le.getResultCode()));
- localOp.appendErrorMessage(le.getMessageObject());
-
- break compareProcessing;
- }
- }
-
- try
- {
- // FIXME -- We need to determine whether the current user has
- // permission to make this determination.
- SearchFilter filter = assertControl.getSearchFilter();
- if (! filter.matchesEntry(entry))
- {
- localOp.setResultCode(ResultCode.ASSERTION_FAILED);
-
- localOp.appendErrorMessage(
- ERR_COMPARE_ASSERTION_FAILED.get(
- String.valueOf(entryDN)));
-
- break compareProcessing;
- }
- }
- catch (DirectoryException de)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, de);
- }
-
- localOp.setResultCode(ResultCode.PROTOCOL_ERROR);
-
- localOp.appendErrorMessage(
- ERR_COMPARE_CANNOT_PROCESS_ASSERTION_FILTER.get(
- String.valueOf(entryDN),
- de.getMessageObject()));
-
- break compareProcessing;
- }
- }
- else if (oid.equals(OID_PROXIED_AUTH_V1))
- {
- // The requester must have the PROXIED_AUTH privilige in order to
- // be able to use this control.
- if (! clientConnection.hasPrivilege(
- Privilege.PROXIED_AUTH, localOp))
- {
- localOp.appendErrorMessage(
- ERR_PROXYAUTH_INSUFFICIENT_PRIVILEGES.get());
- localOp.setResultCode(ResultCode.AUTHORIZATION_DENIED);
- break compareProcessing;
- }
-
-
- ProxiedAuthV1Control proxyControl;
- if (c instanceof ProxiedAuthV1Control)
- {
- proxyControl = (ProxiedAuthV1Control) c;
- }
- else
- {
- try
- {
- proxyControl = ProxiedAuthV1Control.decodeControl(c);
- }
- catch (LDAPException le)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, le);
- }
-
- localOp.setResultCode(ResultCode.valueOf(le.getResultCode()));
- localOp.appendErrorMessage(le.getMessageObject());
-
- break compareProcessing;
- }
- }
-
-
- Entry authorizationEntry;
- try
- {
- authorizationEntry = proxyControl.getAuthorizationEntry();
- }
- catch (DirectoryException de)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, de);
- }
-
- localOp.setResultCode(de.getResultCode());
- localOp.appendErrorMessage(de.getMessageObject());
-
- break compareProcessing;
- }
-
- localOp.setAuthorizationEntry(authorizationEntry);
- if (authorizationEntry == null)
- {
- localOp.setProxiedAuthorizationDN(DN.nullDN());
- }
- else
- {
- localOp.setProxiedAuthorizationDN(authorizationEntry.getDN());
- }
- }
- else if (oid.equals(OID_PROXIED_AUTH_V2))
- {
- // The requester must have the PROXIED_AUTH privilige in order to
- // be able to use this control.
- if (! clientConnection.hasPrivilege(
- Privilege.PROXIED_AUTH, localOp))
- {
- localOp.appendErrorMessage(
- ERR_PROXYAUTH_INSUFFICIENT_PRIVILEGES.get());
- localOp.setResultCode(ResultCode.AUTHORIZATION_DENIED);
- break compareProcessing;
- }
-
-
- ProxiedAuthV2Control proxyControl;
- if (c instanceof ProxiedAuthV2Control)
- {
- proxyControl = (ProxiedAuthV2Control) c;
- }
- else
- {
- try
- {
- proxyControl = ProxiedAuthV2Control.decodeControl(c);
- }
- catch (LDAPException le)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, le);
- }
-
- localOp.setResultCode(ResultCode.valueOf(le.getResultCode()));
- localOp.appendErrorMessage(le.getMessageObject());
-
- break compareProcessing;
- }
- }
-
-
- Entry authorizationEntry;
- try
- {
- authorizationEntry = proxyControl.getAuthorizationEntry();
- }
- catch (DirectoryException de)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, de);
- }
-
- localOp.setResultCode(de.getResultCode());
- localOp.appendErrorMessage(de.getMessageObject());
-
- break compareProcessing;
- }
-
- localOp.setAuthorizationEntry(authorizationEntry);
- if (authorizationEntry == null)
- {
- localOp.setProxiedAuthorizationDN(DN.nullDN());
- }
- else
- {
- localOp.setProxiedAuthorizationDN(authorizationEntry.getDN());
- }
- }
-
- // NYI -- Add support for additional controls.
- else if (c.isCritical())
- {
- Backend backend = DirectoryServer.getBackend(entryDN);
- if ((backend == null) || (! backend.supportsControl(oid)))
- {
- localOp.setResultCode(
- ResultCode.UNAVAILABLE_CRITICAL_EXTENSION);
-
- localOp.appendErrorMessage(
- ERR_COMPARE_UNSUPPORTED_CRITICAL_CONTROL.get(
- String.valueOf(entryDN), oid));
-
- break compareProcessing;
- }
- }
- }
- }
-
-
- // Check to see if the client has permission to perform the
- // compare.
-
- // FIXME: for now assume that this will check all permission
- // pertinent to the operation. This includes proxy authorization
- // and any other controls specified.
-
- // FIXME: earlier checks to see if the entry already exists may
- // have already exposed sensitive information to the client.
- if (AccessControlConfigManager.getInstance()
- .getAccessControlHandler().isAllowed(localOp) == false) {
- localOp.setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS);
-
- localOp.appendErrorMessage(
- ERR_COMPARE_AUTHZ_INSUFFICIENT_ACCESS_RIGHTS.get(
- String.valueOf(entryDN)));
-
- skipPostOperation = true;
- break compareProcessing;
- }
-
- // Check for a request to cancel this operation.
- if (localOp.getCancelRequest() != null)
- {
- return;
- }
-
-
- // Invoke the pre-operation compare plugins.
- PreOperationPluginResult preOpResult =
- pluginConfigManager.invokePreOperationComparePlugins(localOp);
- if (preOpResult.connectionTerminated())
- {
- // There's no point in continuing with anything. Log the request and
- // result and return.
- localOp.setResultCode(ResultCode.CANCELED);
-
- localOp.appendErrorMessage(ERR_CANCELED_BY_PREOP_DISCONNECT.get());
-
- return;
- }
- else if (preOpResult.sendResponseImmediately())
- {
- skipPostOperation = true;
- break compareProcessing;
- }
- else if (preOpResult.skipCoreProcessing())
- {
- skipPostOperation = false;
- break compareProcessing;
- }
-
-
- // Get the base attribute type and set of options.
- String baseName;
- HashSet<String> options;
- String rawAttributeType = localOp.getRawAttributeType();
- int semicolonPos = rawAttributeType.indexOf(';');
- if (semicolonPos > 0)
- {
- baseName = toLowerCase(rawAttributeType.substring(0, semicolonPos));
-
- options = new HashSet<String>();
- int nextPos = rawAttributeType.indexOf(';', semicolonPos+1);
- while (nextPos > 0)
- {
- options.add(rawAttributeType.substring(semicolonPos+1, nextPos));
- semicolonPos = nextPos;
- nextPos = rawAttributeType.indexOf(';', semicolonPos+1);
- }
-
- options.add(rawAttributeType.substring(semicolonPos+1));
- }
- else
- {
- baseName = toLowerCase(rawAttributeType);
- options = null;
- }
-
-
- // Actually perform the compare operation.
- List<Attribute> attrList = null;
- if (localOp.getAttributeType() == null)
- {
- localOp.setAttributeType(DirectoryServer.getAttributeType(baseName));
- }
- if (localOp.getAttributeType() == null)
- {
- attrList = entry.getAttribute(baseName, options);
- localOp.setAttributeType(
- DirectoryServer.getDefaultAttributeType(baseName));
- }
- else
- {
- attrList = entry.getAttribute(localOp.getAttributeType(), options);
- }
-
- if ((attrList == null) || attrList.isEmpty())
- {
- localOp.setResultCode(ResultCode.NO_SUCH_ATTRIBUTE);
- if (options == null)
- {
- localOp.appendErrorMessage(WARN_COMPARE_OP_NO_SUCH_ATTR.get(
- String.valueOf(entryDN), baseName));
- }
- else
- {
- localOp.appendErrorMessage(
- WARN_COMPARE_OP_NO_SUCH_ATTR_WITH_OPTIONS.get(
- String.valueOf(entryDN), baseName));
- }
- }
- else
- {
- AttributeValue value = new AttributeValue(
- localOp.getAttributeType(),
- localOp.getAssertionValue());
-
- boolean matchFound = false;
- for (Attribute a : attrList)
- {
- if (a.hasValue(value))
- {
- matchFound = true;
- break;
- }
- }
-
- if (matchFound)
- {
- localOp.setResultCode(ResultCode.COMPARE_TRUE);
- }
- else
- {
- localOp.setResultCode(ResultCode.COMPARE_FALSE);
- }
- }
- }
- finally
- {
- LockManager.unlock(entryDN, readLock);
- }
- }
-
-
- // Check for a request to cancel this operation.
- if (localOp.getCancelRequest() != null)
- {
- return;
- }
-
-
- // Invoke the post-operation compare plugins.
- if (! skipPostOperation)
- {
- PostOperationPluginResult postOperationResult =
- pluginConfigManager.invokePostOperationComparePlugins(localOp);
- if (postOperationResult.connectionTerminated())
- {
- localOp.setResultCode(ResultCode.CANCELED);
-
- localOp.appendErrorMessage(ERR_CANCELED_BY_POSTOP_DISCONNECT.get());
-
- return;
- }
- }
- }
-
- /**
- * Perform a moddn operation against a local backend.
- *
- * @param op The operation to perform
- */
- public void processModifyDN(ModifyDNOperation op)
- {
- LocalBackendModifyDNOperation localOp =
- new LocalBackendModifyDNOperation(op);
- processLocalModifyDN(localOp);
- }
-
- /**
- * Perform a local moddn operation against the local backend.
- *
- * @param op - The operation to perform
- */
- private void processLocalModifyDN(LocalBackendModifyDNOperation op)
- {
-
- ClientConnection clientConnection = op.getClientConnection();
-
- // Get the plugin config manager that will be used for invoking plugins.
- PluginConfigManager pluginConfigManager =
- DirectoryServer.getPluginConfigManager();
- boolean skipPostOperation = false;
-
- // Check for a request to cancel this operation.
- if (op.getCancelRequest() != null)
- {
- return;
- }
-
- // Create a labeled block of code that we can break out of if a problem is
- // detected.
-modifyDNProcessing:
- {
- // Process the entry DN, newRDN, and newSuperior elements from their raw
- // forms as provided by the client to the forms required for the rest of
- // the modify DN processing.
- DN entryDN = op.getEntryDN();
-
- RDN newRDN = op.getNewRDN();
- if (newRDN == null)
- {
- skipPostOperation = true;
- break modifyDNProcessing;
- }
-
- DN newSuperior = op.getNewSuperior();
- if ((newSuperior == null) &&
- (op.getRawNewSuperior() != null))
- {
- skipPostOperation = true;
- break modifyDNProcessing;
- }
-
- // Construct the new DN to use for the entry.
- DN parentDN;
- if (newSuperior == null)
- {
- parentDN = entryDN.getParentDNInSuffix();
- }
- else
- {
- parentDN = newSuperior;
- }
-
- if ((parentDN == null) || parentDN.isNullDN())
- {
- op.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
- op.appendErrorMessage(ERR_MODDN_NO_PARENT.get(String.valueOf(entryDN)));
- break modifyDNProcessing;
- }
-
- DN newDN = parentDN.concat(newRDN);
-
- // Get the backend for the current entry, and the backend for the new
- // entry. If either is null, or if they are different, then fail.
- Backend currentBackend = backend;
- if (currentBackend == null)
- {
- op.setResultCode(ResultCode.NO_SUCH_OBJECT);
- op.appendErrorMessage(ERR_MODDN_NO_BACKEND_FOR_CURRENT_ENTRY.get(
- String.valueOf(entryDN)));
- break modifyDNProcessing;
- }
-
- Backend newBackend = DirectoryServer.getBackend(newDN);
- if (newBackend == null)
- {
- op.setResultCode(ResultCode.NO_SUCH_OBJECT);
- op.appendErrorMessage(ERR_MODDN_NO_BACKEND_FOR_NEW_ENTRY.get(
- String.valueOf(entryDN),
- String.valueOf(newDN)));
- break modifyDNProcessing;
- }
- else if (! currentBackend.equals(newBackend))
- {
- op.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
- op.appendErrorMessage(ERR_MODDN_DIFFERENT_BACKENDS.get(
- String.valueOf(entryDN),
- String.valueOf(newDN)));
- break modifyDNProcessing;
- }
-
-
- // Check for a request to cancel this operation.
- if (op.getCancelRequest() != null)
- {
- return;
- }
-
-
- // Acquire write locks for the current and new DN.
- Lock currentLock = null;
- for (int i=0; i < 3; i++)
- {
- currentLock = LockManager.lockWrite(entryDN);
- if (currentLock != null)
- {
- break;
- }
- }
-
- if (currentLock == null)
- {
- op.setResultCode(DirectoryServer.getServerErrorResultCode());
- op.appendErrorMessage(ERR_MODDN_CANNOT_LOCK_CURRENT_DN.get(
- String.valueOf(entryDN)));
-
- skipPostOperation = true;
- break modifyDNProcessing;
- }
-
- Lock newLock = null;
- try
- {
- for (int i=0; i < 3; i++)
- {
- newLock = LockManager.lockWrite(newDN);
- if (newLock != null)
- {
- break;
- }
- }
- }
- catch (Exception e)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, e);
- }
-
- LockManager.unlock(entryDN, currentLock);
-
- if (newLock != null)
- {
- LockManager.unlock(newDN, newLock);
- }
-
- op.setResultCode(DirectoryServer.getServerErrorResultCode());
- op.appendErrorMessage(ERR_MODDN_EXCEPTION_LOCKING_NEW_DN.get(
- String.valueOf(entryDN),
- String.valueOf(newDN),
- getExceptionMessage(e)));
-
- skipPostOperation = true;
- break modifyDNProcessing;
- }
-
- if (newLock == null)
- {
- LockManager.unlock(entryDN, currentLock);
-
- op.setResultCode(DirectoryServer.getServerErrorResultCode());
- op.appendErrorMessage(ERR_MODDN_CANNOT_LOCK_NEW_DN.get(
- String.valueOf(entryDN),
- String.valueOf(newDN)));
-
- skipPostOperation = true;
- break modifyDNProcessing;
- }
-
- Entry currentEntry = null;
- try
- {
- // Check for a request to cancel this operation.
- if (op.getCancelRequest() != null)
- {
- return;
- }
-
-
- // Get the current entry from the appropriate backend. If it doesn't
- // exist, then fail.
- try
- {
- currentEntry = currentBackend.getEntry(entryDN);
- op.setOriginalEntry(currentEntry);
- }
- catch (DirectoryException de)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, de);
- }
-
- op.setResultCode(de.getResultCode());
- op.appendErrorMessage(de.getMessageObject());
- op.setMatchedDN(de.getMatchedDN());
- op.setReferralURLs(de.getReferralURLs());
-
- break modifyDNProcessing;
- }
-
- if (op.getOriginalEntry() == null)
- {
- // See if one of the entry's ancestors exists.
- parentDN = entryDN.getParentDNInSuffix();
- while (parentDN != null)
- {
- try
- {
- if (DirectoryServer.entryExists(parentDN))
- {
- op.setMatchedDN(parentDN);
- break;
- }
- }
- catch (Exception e)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, e);
- }
- break;
- }
-
- parentDN = parentDN.getParentDNInSuffix();
- }
-
- op.setResultCode(ResultCode.NO_SUCH_OBJECT);
- op.appendErrorMessage(ERR_MODDN_NO_CURRENT_ENTRY.get(
- String.valueOf(entryDN)));
-
- break modifyDNProcessing;
- }
-
-
- // Invoke any conflict resolution processing that might be needed by the
- // synchronization provider.
- for (SynchronizationProvider provider :
- DirectoryServer.getSynchronizationProviders())
- {
- try
- {
- SynchronizationProviderResult result =
- provider.handleConflictResolution(op);
- if (! result.continueOperationProcessing())
- {
- break modifyDNProcessing;
- }
- }
- catch (DirectoryException de)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, de);
- }
-
- logError(ERR_MODDN_SYNCH_CONFLICT_RESOLUTION_FAILED.
- get(op.getConnectionID(), op.getOperationID(),
- getExceptionMessage(de)));
-
- op.setResponseData(de);
- break modifyDNProcessing;
- }
- }
-
-
- // Check to see if there are any controls in the request. If so, then
- // see if there is any special processing required.
- boolean noOp = false;
- LDAPPreReadRequestControl preReadRequest = null;
- LDAPPostReadRequestControl postReadRequest = null;
- List<Control> requestControls = op.getRequestControls();
- if ((requestControls != null) && (! requestControls.isEmpty()))
- {
- for (int i=0; i < requestControls.size(); i++)
- {
- Control c = requestControls.get(i);
- String oid = c.getOID();
-
- if (!AccessControlConfigManager.getInstance().
- getAccessControlHandler().isAllowed(entryDN, op, c))
- {
- op.setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS);
-
- op.appendErrorMessage(
- ERR_CONTROL_INSUFFICIENT_ACCESS_RIGHTS.get(oid));
- skipPostOperation = true;
- break modifyDNProcessing;
- }
-
- if (oid.equals(OID_LDAP_ASSERTION))
- {
- LDAPAssertionRequestControl assertControl;
- if (c instanceof LDAPAssertionRequestControl)
- {
- assertControl = (LDAPAssertionRequestControl) c;
- }
- else
- {
- try
- {
- assertControl = LDAPAssertionRequestControl.decodeControl(c);
- requestControls.set(i, assertControl);
- }
- catch (LDAPException le)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, le);
- }
-
- op.setResultCode(ResultCode.valueOf(le.getResultCode()));
- op.appendErrorMessage(le.getMessageObject());
-
- break modifyDNProcessing;
- }
- }
-
- try
- {
- // FIXME -- We need to determine whether the current user has
- // permission to make this determination.
- SearchFilter filter = assertControl.getSearchFilter();
- if (! filter.matchesEntry(currentEntry))
- {
- op.setResultCode(ResultCode.ASSERTION_FAILED);
-
- op.appendErrorMessage(ERR_MODDN_ASSERTION_FAILED.get(
- String.valueOf(entryDN)));
-
- break modifyDNProcessing;
- }
- }
- catch (DirectoryException de)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, de);
- }
-
- op.setResultCode(ResultCode.PROTOCOL_ERROR);
-
- op.appendErrorMessage(
- ERR_MODDN_CANNOT_PROCESS_ASSERTION_FILTER.get(
- String.valueOf(entryDN),
- de.getMessageObject()));
-
- break modifyDNProcessing;
- }
- }
- else if (oid.equals(OID_LDAP_NOOP_OPENLDAP_ASSIGNED))
- {
- noOp = true;
- }
- else if (oid.equals(OID_LDAP_READENTRY_PREREAD))
- {
- if (c instanceof LDAPPreReadRequestControl)
- {
- preReadRequest = (LDAPPreReadRequestControl) c;
- }
- else
- {
- try
- {
- preReadRequest = LDAPPreReadRequestControl.decodeControl(c);
- requestControls.set(i, preReadRequest);
- }
- catch (LDAPException le)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, le);
- }
-
- op.setResultCode(ResultCode.valueOf(le.getResultCode()));
- op.appendErrorMessage(le.getMessageObject());
-
- break modifyDNProcessing;
- }
- }
- }
- else if (oid.equals(OID_LDAP_READENTRY_POSTREAD))
- {
- if (c instanceof LDAPPostReadRequestControl)
- {
- postReadRequest = (LDAPPostReadRequestControl) c;
- }
- else
- {
- try
- {
- postReadRequest = LDAPPostReadRequestControl.decodeControl(c);
- requestControls.set(i, postReadRequest);
- }
- catch (LDAPException le)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, le);
- }
-
- op.setResultCode(ResultCode.valueOf(le.getResultCode()));
- op.appendErrorMessage(le.getMessageObject());
-
- break modifyDNProcessing;
- }
- }
- }
- else if (oid.equals(OID_PROXIED_AUTH_V1))
- {
- // The requester must have the PROXIED_AUTH privilige in order to
- // be able to use this control.
- if (! clientConnection.hasPrivilege(Privilege.PROXIED_AUTH, op))
- {
-
- op.appendErrorMessage(
- ERR_PROXYAUTH_INSUFFICIENT_PRIVILEGES.get());
- op.setResultCode(ResultCode.AUTHORIZATION_DENIED);
- break modifyDNProcessing;
- }
-
-
- ProxiedAuthV1Control proxyControl;
- if (c instanceof ProxiedAuthV1Control)
- {
- proxyControl = (ProxiedAuthV1Control) c;
- }
- else
- {
- try
- {
- proxyControl = ProxiedAuthV1Control.decodeControl(c);
- }
- catch (LDAPException le)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, le);
- }
-
- op.setResultCode(ResultCode.valueOf(le.getResultCode()));
- op.appendErrorMessage(le.getMessageObject());
-
- break modifyDNProcessing;
- }
- }
-
-
- Entry authorizationEntry;
- try
- {
- authorizationEntry = proxyControl.getAuthorizationEntry();
- }
- catch (DirectoryException de)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, de);
- }
-
- op.setResultCode(de.getResultCode());
- op.appendErrorMessage(de.getMessageObject());
-
- break modifyDNProcessing;
- }
-
- op.setAuthorizationEntry(authorizationEntry);
- if (authorizationEntry == null)
- {
- op.setProxiedAuthorizationDN(DN.nullDN());
- }
- else
- {
- op.setProxiedAuthorizationDN(authorizationEntry.getDN());
- }
- }
- else if (oid.equals(OID_PROXIED_AUTH_V2))
- {
- // The requester must have the PROXIED_AUTH privilige in order to
- // be able to use this control.
- if (! clientConnection.hasPrivilege(Privilege.PROXIED_AUTH, op))
- {
-
- op.appendErrorMessage(
- ERR_PROXYAUTH_INSUFFICIENT_PRIVILEGES.get());
- op.setResultCode(ResultCode.AUTHORIZATION_DENIED);
- break modifyDNProcessing;
- }
-
-
- ProxiedAuthV2Control proxyControl;
- if (c instanceof ProxiedAuthV2Control)
- {
- proxyControl = (ProxiedAuthV2Control) c;
- }
- else
- {
- try
- {
- proxyControl = ProxiedAuthV2Control.decodeControl(c);
- }
- catch (LDAPException le)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, le);
- }
-
- op.setResultCode(ResultCode.valueOf(le.getResultCode()));
- op.appendErrorMessage(le.getMessageObject());
-
- break modifyDNProcessing;
- }
- }
-
-
- Entry authorizationEntry;
- try
- {
- authorizationEntry = proxyControl.getAuthorizationEntry();
- }
- catch (DirectoryException de)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, de);
- }
-
- op.setResultCode(de.getResultCode());
- op.appendErrorMessage(de.getMessageObject());
-
- break modifyDNProcessing;
- }
-
- op.setAuthorizationEntry(authorizationEntry);
- if (authorizationEntry == null)
- {
- op.setProxiedAuthorizationDN(DN.nullDN());
- }
- else
- {
- op.setProxiedAuthorizationDN(authorizationEntry.getDN());
- }
- }
-
- // NYI -- Add support for additional controls.
- else if (c.isCritical())
- {
- Backend backend = DirectoryServer.getBackend(entryDN);
- if ((backend == null) || (! backend.supportsControl(oid)))
- {
- op.setResultCode(ResultCode.UNAVAILABLE_CRITICAL_EXTENSION);
-
-
- op.appendErrorMessage(
- ERR_MODDN_UNSUPPORTED_CRITICAL_CONTROL.get(
- String.valueOf(entryDN),
- oid));
-
- break modifyDNProcessing;
- }
- }
- }
- }
-
-
- // Check to see if the client has permission to perform the
- // modify DN.
-
- // FIXME: for now assume that this will check all permission
- // pertinent to the operation. This includes proxy authorization
- // and any other controls specified.
-
- // FIXME: earlier checks to see if the entry or new superior
- // already exists may have already exposed sensitive information
- // to the client.
- if (AccessControlConfigManager.getInstance()
- .getAccessControlHandler().isAllowed(op) == false) {
- op.setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS);
-
- op.appendErrorMessage(ERR_MODDN_AUTHZ_INSUFFICIENT_ACCESS_RIGHTS.get(
- String.valueOf(entryDN)));
-
- skipPostOperation = true;
- break modifyDNProcessing;
- }
-
- // Duplicate the entry and set its new DN. Also, create an empty list
- // to hold the attribute-level modifications.
- Entry newEntry = currentEntry.duplicate(false);
- newEntry.setDN(newDN);
- op.setUpdatedEntry(newEntry);
-
- // init the modifications
- op.addModification(null);
- List<Modification> modifications = op.getModifications();
-
-
- // If we should delete the old RDN values from the entry, then do so.
- if (op.deleteOldRDN())
- {
- RDN currentRDN = entryDN.getRDN();
- int numValues = currentRDN.getNumValues();
- for (int i=0; i < numValues; i++)
- {
- LinkedHashSet<AttributeValue> valueSet =
- new LinkedHashSet<AttributeValue>(1);
- valueSet.add(currentRDN.getAttributeValue(i));
-
- Attribute a = new Attribute(currentRDN.getAttributeType(i),
- currentRDN.getAttributeName(i),
- valueSet);
-
- // If the associated attribute type is marked NO-USER-MODIFICATION,
- // then refuse the update.
- if (a.getAttributeType().isNoUserModification())
- {
- if (! (op.isInternalOperation() ||
- op.isSynchronizationOperation()))
- {
- op.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
-
- op.appendErrorMessage(ERR_MODDN_OLD_RDN_ATTR_IS_NO_USER_MOD.get(
- String.valueOf(entryDN), a.getName()));
- break modifyDNProcessing;
- }
- }
-
- LinkedList<AttributeValue> missingValues =
- new LinkedList<AttributeValue>();
- newEntry.removeAttribute(a, missingValues);
-
- if (missingValues.isEmpty())
- {
- modifications.add(new Modification(ModificationType.DELETE, a));
- }
- }
- }
-
-
- // Add the new RDN values to the entry.
- int newRDNValues = newRDN.getNumValues();
- for (int i=0; i < newRDNValues; i++)
- {
- LinkedHashSet<AttributeValue> valueSet =
- new LinkedHashSet<AttributeValue>(1);
- valueSet.add(newRDN.getAttributeValue(i));
-
- Attribute a = new Attribute(newRDN.getAttributeType(i),
- newRDN.getAttributeName(i),
- valueSet);
-
- LinkedList<AttributeValue> duplicateValues =
- new LinkedList<AttributeValue>();
- newEntry.addAttribute(a, duplicateValues);
-
- if (duplicateValues.isEmpty())
- {
- // If the associated attribute type is marked NO-USER-MODIFICATION,
- // then refuse the update.
- if (a.getAttributeType().isNoUserModification())
- {
- if (! (op.isInternalOperation() ||
- op.isSynchronizationOperation()))
- {
- op.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
-
- op.appendErrorMessage(ERR_MODDN_NEW_RDN_ATTR_IS_NO_USER_MOD.get(
- String.valueOf(entryDN), a.getName()));
- break modifyDNProcessing;
- }
- }
- else
- {
- modifications.add(new Modification(ModificationType.ADD, a));
- }
- }
- }
-
- // If the server is configured to check the schema and the
- // operation is not a synchronization operation,
- // make sure that the resulting entry is valid as per the server schema.
- if ((DirectoryServer.checkSchema()) &&
- (!op.isSynchronizationOperation()) )
- {
- MessageBuilder invalidReason = new MessageBuilder();
- if (! newEntry.conformsToSchema(null, false, true, true,
- invalidReason))
- {
- op.setResultCode(ResultCode.OBJECTCLASS_VIOLATION);
- op.appendErrorMessage(ERR_MODDN_VIOLATES_SCHEMA.get(
- String.valueOf(entryDN),
- String.valueOf(invalidReason)));
- break modifyDNProcessing;
- }
-
- for (int i=0; i < newRDNValues; i++)
- {
- AttributeType at = newRDN.getAttributeType(i);
- if (at.isObsolete())
- {
- op.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
- op.appendErrorMessage(ERR_MODDN_NEWRDN_ATTR_IS_OBSOLETE.get(
- String.valueOf(entryDN),
- at.getNameOrOID()));
- break modifyDNProcessing;
- }
- }
- }
-
-
- // Check for a request to cancel this operation.
- if (op.getCancelRequest() != null)
- {
- return;
- }
-
-
- // Get a count of the current number of modifications. The
- // pre-operation plugins may alter this list, and we need to be able to
- // identify which changes were made after they're done.
- int modCount = op.getModifications().size();
-
-
- // If the operation is not a synchronization operation,
- // Invoke the pre-operation modify DN plugins.
- if (!op.isSynchronizationOperation())
- {
- PreOperationPluginResult preOpResult =
- pluginConfigManager.invokePreOperationModifyDNPlugins(op);
- if (preOpResult.connectionTerminated())
- {
- // There's no point in continuing with anything. Log the request
- // and result and return.
- op.setResultCode(ResultCode.CANCELED);
-
-
- op.appendErrorMessage(ERR_CANCELED_BY_PREOP_DISCONNECT.get());
- return;
- }
- else if (preOpResult.sendResponseImmediately())
- {
- skipPostOperation = true;
- break modifyDNProcessing;
- }
- else if (preOpResult.skipCoreProcessing())
- {
- skipPostOperation = false;
- break modifyDNProcessing;
- }
- }
-
- // Check to see if any of the pre-operation plugins made any changes to
- // the entry. If so, then apply them.
- if (modifications.size() > modCount)
- {
- for (int i=modCount; i < modifications.size(); i++)
- {
- Modification m = modifications.get(i);
- Attribute a = m.getAttribute();
-
- switch (m.getModificationType())
- {
- case ADD:
- LinkedList<AttributeValue> duplicateValues =
- new LinkedList<AttributeValue>();
- newEntry.addAttribute(a, duplicateValues);
- break;
- case DELETE:
- LinkedList<AttributeValue> missingValues =
- new LinkedList<AttributeValue>();
- newEntry.removeAttribute(a, missingValues);
- break;
- case REPLACE:
- duplicateValues = new LinkedList<AttributeValue>();
- newEntry.removeAttribute(a.getAttributeType(), a.getOptions());
- newEntry.addAttribute(a, duplicateValues);
- break;
- case INCREMENT:
- List<Attribute> attrList =
- newEntry.getAttribute(a.getAttributeType(),
- a.getOptions());
- if ((attrList == null) || attrList.isEmpty())
- {
- op.setResultCode(ResultCode.NO_SUCH_ATTRIBUTE);
-
- op.appendErrorMessage(ERR_MODDN_PREOP_INCREMENT_NO_ATTR.get(
- String.valueOf(entryDN), a.getName()));
-
- break modifyDNProcessing;
- }
- else if (attrList.size() > 1)
- {
- op.setResultCode(ResultCode.CONSTRAINT_VIOLATION);
-
- op.appendErrorMessage(
- ERR_MODDN_PREOP_INCREMENT_MULTIPLE_VALUES.get(
- String.valueOf(entryDN),
- a.getName()));
-
- break modifyDNProcessing;
- }
-
- LinkedHashSet<AttributeValue> values =
- attrList.get(0).getValues();
- if ((values == null) || values.isEmpty())
- {
- op.setResultCode(ResultCode.NO_SUCH_ATTRIBUTE);
-
- op.appendErrorMessage(ERR_MODDN_PREOP_INCREMENT_NO_ATTR.get(
- String.valueOf(entryDN),
- a.getName()));
-
- break modifyDNProcessing;
- }
- else if (values.size() > 1)
- {
- op.setResultCode(ResultCode.CONSTRAINT_VIOLATION);
-
- op.appendErrorMessage(
- ERR_MODDN_PREOP_INCREMENT_MULTIPLE_VALUES.get(
- String.valueOf(entryDN),
- a.getName()));
-
- break modifyDNProcessing;
- }
-
- long currentLongValue;
- try
- {
- AttributeValue v = values.iterator().next();
- currentLongValue = Long.parseLong(v.getStringValue());
- }
- catch (Exception e)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, e);
- }
-
- op.setResultCode(ResultCode.CONSTRAINT_VIOLATION);
-
- op.appendErrorMessage(
- ERR_MODDN_PREOP_INCREMENT_VALUE_NOT_INTEGER.get(
- String.valueOf(entryDN),
- a.getName()));
-
- break modifyDNProcessing;
- }
-
- LinkedHashSet<AttributeValue> newValues = a.getValues();
- if ((newValues == null) || newValues.isEmpty())
- {
- op.setResultCode(ResultCode.CONSTRAINT_VIOLATION);
-
- op.appendErrorMessage(
- ERR_MODDN_PREOP_INCREMENT_NO_AMOUNT.get(
- String.valueOf(entryDN),
- a.getName()));
-
- break modifyDNProcessing;
- }
- else if (newValues.size() > 1)
- {
- op.setResultCode(ResultCode.CONSTRAINT_VIOLATION);
-
- op.appendErrorMessage(
- ERR_MODDN_PREOP_INCREMENT_MULTIPLE_AMOUNTS.get(
- String.valueOf(entryDN),
- a.getName()));
-
- break modifyDNProcessing;
- }
-
- long incrementAmount;
- try
- {
- AttributeValue v = values.iterator().next();
- incrementAmount = Long.parseLong(v.getStringValue());
- }
- catch (Exception e)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, e);
- }
-
- op.setResultCode(ResultCode.CONSTRAINT_VIOLATION);
-
- op.appendErrorMessage(
- ERR_MODDN_PREOP_INCREMENT_AMOUNT_NOT_INTEGER.get(
- String.valueOf(entryDN),
- a.getName()));
-
- break modifyDNProcessing;
- }
-
- long newLongValue = currentLongValue + incrementAmount;
- ByteString newValueOS =
- new ASN1OctetString(String.valueOf(newLongValue));
-
- newValues = new LinkedHashSet<AttributeValue>(1);
- newValues.add(new AttributeValue(a.getAttributeType(),
- newValueOS));
-
- List<Attribute> newAttrList = new ArrayList<Attribute>(1);
- newAttrList.add(new Attribute(a.getAttributeType(),
- a.getName(), newValues));
- newEntry.putAttribute(a.getAttributeType(), newAttrList);
-
- break;
- }
- }
-
-
- // Make sure that the updated entry still conforms to the server
- // schema.
- if (DirectoryServer.checkSchema())
- {
- MessageBuilder invalidReason = new MessageBuilder();
- if (! newEntry.conformsToSchema(null, false, true, true,
- invalidReason))
- {
- op.setResultCode(ResultCode.OBJECTCLASS_VIOLATION);
-
- op.appendErrorMessage(ERR_MODDN_PREOP_VIOLATES_SCHEMA.get(
- String.valueOf(entryDN),
- String.valueOf(invalidReason)));
- break modifyDNProcessing;
- }
- }
- }
-
-
- // Check for a request to cancel this operation.
- if (op.getCancelRequest() != null)
- {
- return;
- }
-
-
- // Actually perform the modify DN operation.
- // This should include taking
- // care of any synchronization that might be needed.
- try
- {
- // If it is not a private backend, then check to see if the server or
- // backend is operating in read-only mode.
- if (! currentBackend.isPrivateBackend())
- {
- switch (DirectoryServer.getWritabilityMode())
- {
- case DISABLED:
- op.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
- op.appendErrorMessage(ERR_MODDN_SERVER_READONLY.get(
- String.valueOf(entryDN)));
- break modifyDNProcessing;
-
- case INTERNAL_ONLY:
- if (! (op.isInternalOperation() ||
- op.isSynchronizationOperation()))
- {
- op.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
- op.appendErrorMessage(ERR_MODDN_SERVER_READONLY.get(
- String.valueOf(entryDN)));
- break modifyDNProcessing;
- }
- }
-
- switch (currentBackend.getWritabilityMode())
- {
- case DISABLED:
- op.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
- op.appendErrorMessage(ERR_MODDN_BACKEND_READONLY.get(
- String.valueOf(entryDN)));
- break modifyDNProcessing;
-
- case INTERNAL_ONLY:
- if (! (op.isInternalOperation() ||
- op.isSynchronizationOperation()))
- {
- op.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
- op.appendErrorMessage(ERR_MODDN_BACKEND_READONLY.get(
- String.valueOf(entryDN)));
- break modifyDNProcessing;
- }
- }
- }
-
-
- if (noOp)
- {
- op.appendErrorMessage(INFO_MODDN_NOOP.get());
-
- op.setResultCode(ResultCode.NO_OPERATION);
- }
- else
- {
- for (SynchronizationProvider provider :
- DirectoryServer.getSynchronizationProviders())
- {
- try
- {
- SynchronizationProviderResult result =
- provider.doPreOperation(op);
- if (! result.continueOperationProcessing())
- {
- break modifyDNProcessing;
- }
- }
- catch (DirectoryException de)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, de);
- }
-
- logError(ERR_MODDN_SYNCH_PREOP_FAILED.
- get(op.getConnectionID(), op.getOperationID(),
- getExceptionMessage(de)));
-
- op.setResponseData(de);
- break modifyDNProcessing;
- }
- }
-
- currentBackend.renameEntry(entryDN, newEntry, op);
- }
-
- if (preReadRequest != null)
- {
- Entry entry = currentEntry.duplicate(true);
-
- if (! preReadRequest.allowsAttribute(
- DirectoryServer.getObjectClassAttributeType()))
- {
- entry.removeAttribute(
- DirectoryServer.getObjectClassAttributeType());
- }
-
- if (! preReadRequest.returnAllUserAttributes())
- {
- Iterator<AttributeType> iterator =
- entry.getUserAttributes().keySet().iterator();
- while (iterator.hasNext())
- {
- AttributeType attrType = iterator.next();
- if (! preReadRequest.allowsAttribute(attrType))
- {
- iterator.remove();
- }
- }
- }
-
- if (! preReadRequest.returnAllOperationalAttributes())
- {
- Iterator<AttributeType> iterator =
- entry.getOperationalAttributes().keySet().iterator();
- while (iterator.hasNext())
- {
- AttributeType attrType = iterator.next();
- if (! preReadRequest.allowsAttribute(attrType))
- {
- iterator.remove();
- }
- }
- }
-
- // FIXME -- Check access controls on the entry to see if it should
- // be returned or if any attributes need to be stripped
- // out..
- SearchResultEntry searchEntry = new SearchResultEntry(entry);
- LDAPPreReadResponseControl responseControl =
- new LDAPPreReadResponseControl(preReadRequest.getOID(),
- preReadRequest.isCritical(),
- searchEntry);
-
- op.addResponseControl(responseControl);
- }
-
- if (postReadRequest != null)
- {
- Entry entry = newEntry.duplicate(true);
-
- if (! postReadRequest.allowsAttribute(
- DirectoryServer.getObjectClassAttributeType()))
- {
- entry.removeAttribute(
- DirectoryServer.getObjectClassAttributeType());
- }
-
- if (! postReadRequest.returnAllUserAttributes())
- {
- Iterator<AttributeType> iterator =
- entry.getUserAttributes().keySet().iterator();
- while (iterator.hasNext())
- {
- AttributeType attrType = iterator.next();
- if (! postReadRequest.allowsAttribute(attrType))
- {
- iterator.remove();
- }
- }
- }
-
- if (! postReadRequest.returnAllOperationalAttributes())
- {
- Iterator<AttributeType> iterator =
- entry.getOperationalAttributes().keySet().iterator();
- while (iterator.hasNext())
- {
- AttributeType attrType = iterator.next();
- if (! postReadRequest.allowsAttribute(attrType))
- {
- iterator.remove();
- }
- }
- }
-
- // FIXME -- Check access controls on the entry to see if it should
- // be returned or if any attributes need to be stripped
- // out..
- SearchResultEntry searchEntry = new SearchResultEntry(entry);
- LDAPPostReadResponseControl responseControl =
- new LDAPPostReadResponseControl(postReadRequest.getOID(),
- postReadRequest.isCritical(),
- searchEntry);
-
- op.addResponseControl(responseControl);
- }
-
-
- if (! noOp)
- {
- op.setResultCode(ResultCode.SUCCESS);
- }
- }
- catch (DirectoryException de)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, de);
- }
-
- op.setResultCode(de.getResultCode());
- op.appendErrorMessage(de.getMessageObject());
- op.setMatchedDN(de.getMatchedDN());
- op.setReferralURLs(de.getReferralURLs());
-
- break modifyDNProcessing;
- }
- catch (CancelledOperationException coe)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, coe);
- }
-
- CancelResult cancelResult = coe.getCancelResult();
-
- op.setCancelResult(cancelResult);
- op.setResultCode(cancelResult.getResultCode());
-
- Message message = coe.getMessageObject();
- if ((message != null) && (message.length() > 0))
- {
- op.appendErrorMessage(message);
- }
-
- break modifyDNProcessing;
- }
- }
- finally
- {
- LockManager.unlock(entryDN, currentLock);
- LockManager.unlock(newDN, newLock);
-
- for (SynchronizationProvider provider :
- DirectoryServer.getSynchronizationProviders())
- {
- try
- {
- provider.doPostOperation(op);
- }
- catch (DirectoryException de)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, de);
- }
-
- logError(ERR_MODDN_SYNCH_POSTOP_FAILED.
- get(op.getConnectionID(), op.getOperationID(),
- getExceptionMessage(de)));
-
- op.setResponseData(de);
- break;
- }
- }
- }
- }
-
-
- // Indicate that it is now too late to attempt to cancel the operation.
- op.setCancelResult(CancelResult.TOO_LATE);
-
-
- // Invoke the post-operation or post-synchronization modify DN plugins.
- if (op.isSynchronizationOperation())
- {
- if (op.getResultCode() == ResultCode.SUCCESS)
- {
- pluginConfigManager.invokePostSynchronizationModifyDNPlugins(op);
- }
- }
- else if (! skipPostOperation)
- {
- PostOperationPluginResult postOperationResult =
- pluginConfigManager.invokePostOperationModifyDNPlugins(op);
- if (postOperationResult.connectionTerminated())
- {
- op.setResultCode(ResultCode.CANCELED);
-
-
- op.appendErrorMessage(ERR_CANCELED_BY_POSTOP_DISCONNECT.get());
- return;
- }
- }
-
-
- // Notify any change notification listeners that might be registered with
- // the server.
- if (op.getResultCode() == ResultCode.SUCCESS)
- {
- for (ChangeNotificationListener changeListener :
- DirectoryServer.getChangeNotificationListeners())
- {
- try
- {
- changeListener.handleModifyDNOperation(op,
- op.getOriginalEntry(),
- op.getUpdatedEntry());
- }
- catch (Exception e)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, e);
- }
-
- Message message = ERR_MODDN_ERROR_NOTIFYING_CHANGE_LISTENER.get(
- getExceptionMessage(e));
- logError(message);
- }
- }
- }
-
- }
-
/**
* Attaches the current local operation to the global operation so that
@@ -8737,11 +269,8 @@
* operation
*/
@SuppressWarnings("unchecked")
- public static final <O extends Operation, L>
- void attachLocalOperation (
- O globalOperation,
- L currentLocalOperation
- )
+ public static final <O extends Operation,L> void
+ attachLocalOperation (O globalOperation, L currentLocalOperation)
{
List<?> existingAttachment =
(List<?>) globalOperation.getAttachment(Operation.LOCALBACKENDOPERATIONS);
@@ -8756,8 +285,8 @@
newAttachment.addAll ((List<L>) existingAttachment);
}
newAttachment.add (currentLocalOperation);
- globalOperation.setAttachment(
- Operation.LOCALBACKENDOPERATIONS, newAttachment);
+ globalOperation.setAttachment(Operation.LOCALBACKENDOPERATIONS,
+ newAttachment);
}
-
}
+
--
Gitblit v1.10.0