public void processLocalAdd(final LocalBackendWorkflowElement wfe)
throws CanceledOperationException {
boolean executePostOpPlugins = false;
this.backend = wfe.getBackend();
BackendImpl ndbBackend = (BackendImpl) backend;
ClientConnection clientConnection = getClientConnection();
// Get the plugin config manager that will be used for invoking plugins.
PluginConfigManager pluginConfigManager =
DirectoryServer.getPluginConfigManager();
// Check for a request to cancel this operation.
checkIfCanceled(false);
// 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.
checkIfCanceled(false);
DN parentDN = entryDN.getParentDNInSuffix();
AbstractTransaction txn =
new AbstractTransaction(ndbBackend.getRootContainer());
try
{
// Check for a request to cancel this operation.
checkIfCanceled(false);
// 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.continueProcessing())
{
setResultCode(result.getResultCode());
appendErrorMessage(result.getErrorMessage());
setMatchedDN(result.getMatchedDN());
setReferralURLs(result.getReferralURLs());
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;
}
}
}
// Get the parent entry, if it exists.
Entry parentEntry = null;
if (parentDN != null)
{
try
{
parentEntry = ndbBackend.getEntryNoCommit(parentDN, txn,
NdbOperation.LockMode.LM_Read);
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())
{
for(ObjectClass superiorClass : oc.getSuperiorClasses())
{
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.
try
{
if (AccessControlConfigManager.getInstance()
.getAccessControlHandler().isAllowed(this) == false)
{
setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS);
appendErrorMessage(ERR_ADD_AUTHZ_INSUFFICIENT_ACCESS_RIGHTS
.get(String.valueOf(entryDN)));
break addProcessing;
}
}
catch (DirectoryException e)
{
setResultCode(e.getResultCode());
appendErrorMessage(e.getMessageObject());
break addProcessing;
}
// Check for a request to cancel this operation.
checkIfCanceled(false);
// If the operation is not a synchronization operation,
// Invoke the pre-operation add plugins.
if (! isSynchronizationOperation())
{
executePostOpPlugins = true;
PluginResult.PreOperation preOpResult =
pluginConfigManager.invokePreOperationAddPlugins(this);
if (!preOpResult.continueProcessing())
{
setResultCode(preOpResult.getResultCode());
appendErrorMessage(preOpResult.getErrorMessage());
setMatchedDN(preOpResult.getMatchedDN());
setReferralURLs(preOpResult.getReferralURLs());
break addProcessing;
}
}
// 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.continueProcessing())
{
setResultCode(result.getResultCode());
appendErrorMessage(result.getErrorMessage());
setMatchedDN(result.getMatchedDN());
setReferralURLs(result.getReferralURLs());
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;
}
}
ndbBackend.addEntry(entry, this, txn);
}
if (postReadRequest != null)
{
addPostReadResponse();