From ff9755c40feeaf5a3208229d48b80b608ab7a68c Mon Sep 17 00:00:00 2001
From: jarnou <jarnou@localhost>
Date: Tue, 17 Jul 2007 08:14:43 +0000
Subject: [PATCH] This fix is the refactoring of the modrdn operation [issue 1180]
---
opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElement.java | 1300 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 1,300 insertions(+), 0 deletions(-)
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 7d85435..2012bee 100644
--- a/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElement.java
+++ b/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElement.java
@@ -77,6 +77,7 @@
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;
@@ -179,6 +180,9 @@
case MODIFY:
processModify((ModifyOperation) operation);
break;
+ case MODIFY_DN:
+ processModifyDN((ModifyDNOperation) operation);
+ break;
case COMPARE:
processCompare((CompareOperation) operation);
break;
@@ -6679,6 +6683,7 @@
}
+
/**
* Perform a compare operation against a local backend.
*
@@ -7248,6 +7253,1301 @@
}
}
+ /**
+ * 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 operation - 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 and handle a request to cancel this operation.
+ if (op.getCancelRequest() != null)
+ {
+ op.indicateCancelled(op.getCancelRequest());
+ 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(getMessage(MSGID_MODDN_NO_PARENT,
+ 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(getMessage(
+ MSGID_MODDN_NO_BACKEND_FOR_CURRENT_ENTRY,
+ String.valueOf(entryDN)));
+ break modifyDNProcessing;
+ }
+
+ Backend newBackend = DirectoryServer.getBackend(newDN);
+ if (newBackend == null)
+ {
+ op.setResultCode(ResultCode.NO_SUCH_OBJECT);
+ op.appendErrorMessage(getMessage(MSGID_MODDN_NO_BACKEND_FOR_NEW_ENTRY,
+ String.valueOf(entryDN),
+ String.valueOf(newDN)));
+ break modifyDNProcessing;
+ }
+ else if (! currentBackend.equals(newBackend))
+ {
+ op.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
+ op.appendErrorMessage(getMessage(MSGID_MODDN_DIFFERENT_BACKENDS,
+ String.valueOf(entryDN),
+ String.valueOf(newDN)));
+ break modifyDNProcessing;
+ }
+
+
+ // Check for and handle a request to cancel this operation.
+ if (op.getCancelRequest() != null)
+ {
+ op.indicateCancelled(op.getCancelRequest());
+ 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(getMessage(MSGID_MODDN_CANNOT_LOCK_CURRENT_DN,
+ 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(getMessage(MSGID_MODDN_EXCEPTION_LOCKING_NEW_DN,
+ 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(getMessage(MSGID_MODDN_CANNOT_LOCK_NEW_DN,
+ String.valueOf(entryDN),
+ String.valueOf(newDN)));
+
+ skipPostOperation = true;
+ break modifyDNProcessing;
+ }
+
+ Entry currentEntry = null;
+ try
+ {
+ // Check for and handle a request to cancel this operation.
+ if (op.getCancelRequest() != null)
+ {
+ op.indicateCancelled(op.getCancelRequest());
+ 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.getErrorMessage());
+ 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(getMessage(MSGID_MODDN_NO_CURRENT_ENTRY,
+ 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(ErrorLogCategory.SYNCHRONIZATION,
+ ErrorLogSeverity.SEVERE_ERROR,
+ MSGID_MODDN_SYNCH_CONFLICT_RESOLUTION_FAILED,
+ 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 (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.getMessage());
+
+ 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(getMessage(MSGID_MODDN_ASSERTION_FAILED,
+ String.valueOf(entryDN)));
+
+ break modifyDNProcessing;
+ }
+ }
+ catch (DirectoryException de)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, de);
+ }
+
+ op.setResultCode(ResultCode.PROTOCOL_ERROR);
+
+ int msgID = MSGID_MODDN_CANNOT_PROCESS_ASSERTION_FILTER;
+ op.appendErrorMessage(getMessage(msgID, String.valueOf(entryDN),
+ de.getErrorMessage()));
+
+ break modifyDNProcessing;
+ }
+ }
+ else if (oid.equals(OID_LDAP_NOOP_OPENLDAP_ASSIGNED))
+ {
+ noOp = true;
+ }
+ else if (oid.equals(OID_LDAP_READENTRY_PREREAD))
+ {
+ if (c instanceof LDAPAssertionRequestControl)
+ {
+ 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.getMessage());
+
+ break modifyDNProcessing;
+ }
+ }
+ }
+ else if (oid.equals(OID_LDAP_READENTRY_POSTREAD))
+ {
+ if (c instanceof LDAPAssertionRequestControl)
+ {
+ 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.getMessage());
+
+ 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))
+ {
+ int msgID = MSGID_PROXYAUTH_INSUFFICIENT_PRIVILEGES;
+ op.appendErrorMessage(getMessage(msgID));
+ 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.getMessage());
+
+ 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.getErrorMessage());
+
+ break modifyDNProcessing;
+ }
+
+ if (AccessControlConfigManager.getInstance()
+ .getAccessControlHandler().isProxiedAuthAllowed(op,
+ authorizationEntry) == false) {
+ op.setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS);
+
+ int msgID = MSGID_MODDN_AUTHZ_INSUFFICIENT_ACCESS_RIGHTS;
+ op.appendErrorMessage(getMessage(msgID,
+ String.valueOf(entryDN)));
+
+ skipPostOperation = true;
+ 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))
+ {
+ int msgID = MSGID_PROXYAUTH_INSUFFICIENT_PRIVILEGES;
+ op.appendErrorMessage(getMessage(msgID));
+ 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.getMessage());
+
+ 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.getErrorMessage());
+
+ break modifyDNProcessing;
+ }
+ if (AccessControlConfigManager.getInstance()
+ .getAccessControlHandler().isProxiedAuthAllowed(op,
+ authorizationEntry) == false) {
+ op.setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS);
+
+ int msgID = MSGID_MODDN_AUTHZ_INSUFFICIENT_ACCESS_RIGHTS;
+ op.appendErrorMessage(getMessage(msgID,
+ String.valueOf(entryDN)));
+
+ skipPostOperation = true;
+ 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);
+
+ int msgID = MSGID_MODDN_UNSUPPORTED_CRITICAL_CONTROL;
+ op.appendErrorMessage(getMessage(msgID, 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);
+
+ int msgID = MSGID_MODDN_AUTHZ_INSUFFICIENT_ACCESS_RIGHTS;
+ op.appendErrorMessage(getMessage(msgID, 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);
+
+ int msgID = MSGID_MODDN_OLD_RDN_ATTR_IS_NO_USER_MOD;
+ op.appendErrorMessage(getMessage(msgID, 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);
+
+ int msgID = MSGID_MODDN_NEW_RDN_ATTR_IS_NO_USER_MOD;
+ op.appendErrorMessage(getMessage(msgID, 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()) )
+ {
+ StringBuilder invalidReason = new StringBuilder();
+ if (! newEntry.conformsToSchema(null, false, true, true,
+ invalidReason))
+ {
+ op.setResultCode(ResultCode.OBJECTCLASS_VIOLATION);
+ op.appendErrorMessage(getMessage(MSGID_MODDN_VIOLATES_SCHEMA,
+ 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.CONSTRAINT_VIOLATION);
+ op.appendErrorMessage(getMessage(
+ MSGID_MODDN_NEWRDN_ATTR_IS_OBSOLETE,
+ String.valueOf(entryDN),
+ at.getNameOrOID()));
+ break modifyDNProcessing;
+ }
+ }
+ }
+
+
+ // Check for and handle a request to cancel this operation.
+ if (op.getCancelRequest() != null)
+ {
+ op.indicateCancelled(op.getCancelRequest());
+ 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);
+
+ int msgID = MSGID_CANCELED_BY_PREOP_DISCONNECT;
+ op.appendErrorMessage(getMessage(msgID));
+ 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);
+
+ int msgID = MSGID_MODDN_PREOP_INCREMENT_NO_ATTR;
+ op.appendErrorMessage(getMessage(msgID,
+ String.valueOf(entryDN),
+ a.getName()));
+
+ break modifyDNProcessing;
+ }
+ else if (attrList.size() > 1)
+ {
+ op.setResultCode(ResultCode.CONSTRAINT_VIOLATION);
+
+ int msgID = MSGID_MODDN_PREOP_INCREMENT_MULTIPLE_VALUES;
+ op.appendErrorMessage(getMessage(msgID,
+ 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);
+
+ int msgID = MSGID_MODDN_PREOP_INCREMENT_NO_ATTR;
+ op.appendErrorMessage(getMessage(msgID,
+ String.valueOf(entryDN),
+ a.getName()));
+
+ break modifyDNProcessing;
+ }
+ else if (values.size() > 1)
+ {
+ op.setResultCode(ResultCode.CONSTRAINT_VIOLATION);
+
+ int msgID = MSGID_MODDN_PREOP_INCREMENT_MULTIPLE_VALUES;
+ op.appendErrorMessage(getMessage(msgID,
+ 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);
+
+ int msgID = MSGID_MODDN_PREOP_INCREMENT_VALUE_NOT_INTEGER;
+ op.appendErrorMessage(getMessage(msgID,
+ String.valueOf(entryDN),
+ a.getName()));
+
+ break modifyDNProcessing;
+ }
+
+ LinkedHashSet<AttributeValue> newValues = a.getValues();
+ if ((newValues == null) || newValues.isEmpty())
+ {
+ op.setResultCode(ResultCode.CONSTRAINT_VIOLATION);
+
+ int msgID = MSGID_MODDN_PREOP_INCREMENT_NO_AMOUNT;
+ op.appendErrorMessage(getMessage(msgID,
+ String.valueOf(entryDN),
+ a.getName()));
+
+ break modifyDNProcessing;
+ }
+ else if (newValues.size() > 1)
+ {
+ op.setResultCode(ResultCode.CONSTRAINT_VIOLATION);
+
+ int msgID = MSGID_MODDN_PREOP_INCREMENT_MULTIPLE_AMOUNTS;
+ op.appendErrorMessage(getMessage(msgID,
+ 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);
+
+ int msgID = MSGID_MODDN_PREOP_INCREMENT_AMOUNT_NOT_INTEGER;
+ op.appendErrorMessage(getMessage(msgID,
+ 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())
+ {
+ StringBuilder invalidReason = new StringBuilder();
+ if (! newEntry.conformsToSchema(null, false, true, true,
+ invalidReason))
+ {
+ op.setResultCode(ResultCode.OBJECTCLASS_VIOLATION);
+
+ op.appendErrorMessage(getMessage(
+ MSGID_MODDN_PREOP_VIOLATES_SCHEMA,
+ String.valueOf(entryDN),
+ String.valueOf(invalidReason)));
+ break modifyDNProcessing;
+ }
+ }
+ }
+
+
+ // Check for and handle a request to cancel this operation.
+ if (op.getCancelRequest() != null)
+ {
+ op.indicateCancelled(op.getCancelRequest());
+ 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(getMessage(MSGID_MODDN_SERVER_READONLY,
+ String.valueOf(entryDN)));
+ break modifyDNProcessing;
+
+ case INTERNAL_ONLY:
+ if (! (op.isInternalOperation() ||
+ op.isSynchronizationOperation()))
+ {
+ op.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
+ op.appendErrorMessage(getMessage(MSGID_MODDN_SERVER_READONLY,
+ String.valueOf(entryDN)));
+ break modifyDNProcessing;
+ }
+ }
+
+ switch (currentBackend.getWritabilityMode())
+ {
+ case DISABLED:
+ op.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
+ op.appendErrorMessage(getMessage(MSGID_MODDN_BACKEND_READONLY,
+ String.valueOf(entryDN)));
+ break modifyDNProcessing;
+
+ case INTERNAL_ONLY:
+ if (! (op.isInternalOperation() ||
+ op.isSynchronizationOperation()))
+ {
+ op.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
+ op.appendErrorMessage(getMessage(MSGID_MODDN_BACKEND_READONLY,
+ String.valueOf(entryDN)));
+ break modifyDNProcessing;
+ }
+ }
+ }
+
+
+ if (noOp)
+ {
+ op.appendErrorMessage(getMessage(MSGID_MODDN_NOOP));
+
+ 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(ErrorLogCategory.SYNCHRONIZATION,
+ ErrorLogSeverity.SEVERE_ERROR,
+ MSGID_MODDN_SYNCH_PREOP_FAILED, 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.getErrorMessage());
+ 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());
+
+ String message = coe.getMessage();
+ 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(ErrorLogCategory.SYNCHRONIZATION,
+ ErrorLogSeverity.SEVERE_ERROR,
+ MSGID_MODDN_SYNCH_POSTOP_FAILED, 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 modify DN plugins.
+ if (! skipPostOperation)
+ {
+ PostOperationPluginResult postOperationResult =
+ pluginConfigManager.invokePostOperationModifyDNPlugins(op);
+ if (postOperationResult.connectionTerminated())
+ {
+ op.setResultCode(ResultCode.CANCELED);
+
+ int msgID = MSGID_CANCELED_BY_POSTOP_DISCONNECT;
+ op.appendErrorMessage(getMessage(msgID));
+ 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);
+ }
+
+ int msgID = MSGID_MODDN_ERROR_NOTIFYING_CHANGE_LISTENER;
+ String message = getMessage(msgID, getExceptionMessage(e));
+ logError(ErrorLogCategory.CORE_SERVER, ErrorLogSeverity.SEVERE_ERROR,
+ message, msgID);
+ }
+ }
+ }
+
+ }
+
/**
* Attaches the current local operation to the global operation so that
--
Gitblit v1.10.0