LocalBackendAddOperation.java revision 5cd7bdbbda0fa9f1aa6e12d9171c3811b73feb07
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at legal-notices/CDDLv1_0.txt.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information:
* Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*
*
* Copyright 2008-2010 Sun Microsystems, Inc.
* Portions Copyright 2011-2015 ForgeRock AS
*/
/**
* 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,
{
/** The backend in which the entry is to be added. */
/** Indicates whether the request includes the LDAP no-op control. */
private boolean noOp;
/** The DN of the entry to be added. */
/** The entry being added to the server. */
/** The post-read request control included in the request, if applicable. */
/** The set of object classes for the entry to add. */
/** The set of operational attributes for the entry to add. */
/** The set of user attributes for the entry to add. */
/**
* Creates a new operation that may be used to add a new entry in a
* local backend of the Directory Server.
*
* @param add The operation to enhance.
*/
{
super(add);
}
/**
* 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
* the synchronization processing.
*
* @return The entry to be added to the server, or <CODE>null</CODE> if it is
* not yet available.
*/
public final Entry getEntryToAdd()
{
return entry;
}
/**
* Process this add operation against a local backend.
*
* @param wfe
* The local backend work-flow element.
* @throws CanceledOperationException
* if this operation should be cancelled
*/
throws CanceledOperationException
{
// Check for a request to cancel this operation.
checkIfCanceled(false);
try
{
// Invoke the post-operation or post-synchronization add plugins.
if (isSynchronizationOperation())
{
{
}
}
else if (executePostOpPlugins.get())
{
// FIXME -- Should this also be done while holding the locks?
if (!postOpResult.continueProcessing())
{
return;
}
}
}
finally
{
}
// Register a post-response call-back which will notify persistent
// searches and change listeners.
{
{
public void run()
{
{
}
}
});
}
}
{
// 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();
{
return;
}
// Check for a request to cancel this operation.
checkIfCanceled(false);
// 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.
try
{
{
return;
}
{
{
// This is not fine. The root DSE cannot be added.
}
else
{
// The entry doesn't have a parent but isn't a suffix. This is not
// allowed.
}
}
// Check for a request to cancel this operation.
checkIfCanceled(false);
// Invoke any conflict resolution processing that might be needed by the
// synchronization provider.
{
try
{
provider.handleConflictResolution(this);
if (!result.continueProcessing())
{
return;
}
}
catch (DirectoryException de)
{
throw de;
}
}
if (objectClasses == null
|| userAttributes == null
|| operationalAttributes == null)
{
return;
}
// 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.
{
return;
}
// 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.
{
return;
}
// Get the parent entry, if it exists.
{
if (parentEntry == null)
{
// The parent doesn't exist, so this add can't be successful.
{
// check whether matchedDN allows to disclose info
}
else
{
// no matched DN either, so let's return normal error code
}
return;
}
}
// 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.
// Add any superior objectclass(s) missing in an entries
// objectclass map.
// Create an entry object to encapsulate the set of attributes and
// objectclasses.
// Check to see if the entry includes a privilege specification. If so,
// then the requester must have the PRIVILEGE_CHANGE privilege.
{
.get());
return;
}
// 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())
{
}
// 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.
{
}
// Get the backend in which the add is to be performed.
{
return;
}
// Check to see if there are any controls in the request. If so, then
// see if there is any special processing required.
// 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
{
.isAllowed(this))
{
return;
}
}
catch (DirectoryException e)
{
setResultCode(e.getResultCode());
return;
}
// 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.set(true);
.invokePreOperationAddPlugins(this);
if (!preOpResult.continueProcessing())
{
return;
}
}
if (noOp)
{
}
else
{
{
try
{
provider.doPreOperation(this);
if (!result.continueProcessing())
{
return;
}
}
catch (DirectoryException de)
{
throw de;
}
}
}
entry);
if (!noOp)
{
}
}
catch (DirectoryException de)
{
}
finally
{
{
}
}
}
private void processSynchPostOperationPlugins()
{
{
try
{
provider.doPostOperation(this);
}
catch (DirectoryException de)
{
break;
}
}
}
{
try
{
{
{
return matchedDN;
}
}
}
catch (Exception e)
{
logger.traceException(e);
}
return null;
}
private boolean checkHasReadOnlyAttributes(
{
{
if (at.isNoUserModification()
&& !isInternalOperation()
&& !isSynchronizationOperation())
{
return true;
}
}
return false;
}
{
}
{
}
/**
* 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
{
for (int i=0; i < numAVAs; i++)
{
if (t.isOperational())
{
addRDNAttributesIfNecessary(operationalAttributes, t, v, n);
}
else
{
addRDNAttributesIfNecessary(userAttributes, t, v, n);
}
}
}
private void addRDNAttributesIfNecessary(
{
{
if (isSynchronizationOperation() ||
{
}
else
{
}
}
else
{
boolean found = false;
if (a.hasOptions())
{
continue;
}
if (!a.contains(v))
{
}
found = true;
break;
}
if (!found)
{
if (isSynchronizationOperation() ||
{
}
else
{
}
}
}
}
/**
* Adds the provided objectClass to the entry, along with its superior classes
* if appropriate.
*
* @param objectClass The objectclass to add to the entry.
*/
{
if (objectClasses != null){
{
}
{
{
}
}
}
}
/**
* Performs all password policy processing necessary for the provided add
* operation.
*
* @throws DirectoryException If a problem occurs while performing password
* policy processing for the add operation.
*/
public final void handlePasswordPolicy()
throws DirectoryException
{
// Construct any virtual/collective attributes which might
// contain a value for the OP_ATTR_PWPOLICY_POLICY_DN attribute.
if (!policy.isPasswordPolicy())
{
// The entry doesn't have a locally managed password, so no action is
// required.
return;
}
// See if a password was specified.
{
// The entry doesn't have a password, so no action is required.
return;
}
{
// This must mean there are attribute options, which we won't allow for
// passwords.
}
if (passwordAttr.hasOptions())
{
}
if (passwordAttr.isEmpty())
{
// This will be treated the same as not having a password.
return;
}
if (!isInternalOperation()
{
// FIXME -- What if they're pre-encoded and might all be the
// same?
}
{
// See if the password is pre-encoded.
{
{
if (isInternalOperation()
{
continue;
}
else
{
}
}
}
{
if (isInternalOperation()
{
continue;
}
else
{
}
}
// See if the password passes validation. We should only do this if
// validation should be performed for administrators.
{
// There are never any current passwords for an add operation.
// Work on a copy of the entry without the password to avoid
// false positives from some validators.
for (PasswordValidator<?> validator :
{
{
}
}
}
// Encode the password.
{
for (PasswordStorageScheme<?> s : defaultStorageSchemes)
{
}
}
else
{
for (PasswordStorageScheme<?> s : defaultStorageSchemes)
{
}
}
}
// Put the new encoded values in the entry.
// Set the password changed time attribute.
// If we should force change on add, then set the appropriate flag.
if (passwordPolicy.isForceChangeOnAdd())
{
}
}
/**
* Adds a password policy response control if the corresponding request
* control was included.
*
* @param errorType The error type to use for the response control.
*/
{
for (Control c : getRequestControls())
{
{
}
}
}
/**
* 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.
*/
{
{
}
invalidReason = new LocalizableMessageBuilder();
// See if the entry contains any attributes or object classes marked
// OBSOLETE. If so, then reject the entry.
{
if (at.isObsolete())
{
}
}
{
if (at.isObsolete())
{
}
}
{
if (oc.isObsolete())
{
}
}
}
{
{
{
{
for (ByteString v : a)
{
{
{
// Value is not human-readable
}
else
{
}
switch (DirectoryServer.getSyntaxEnforcementPolicy())
{
case REJECT:
throw new DirectoryException(
case WARN:
}
}
}
}
}
}
}
/**
* 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.
*/
{
{
for (Control c : requestControls)
{
{
// RFC 4528 mandates support for Add operation basically
// suggesting an assertion on self. As daft as it may be
// we gonna have to support this for RFC compliance.
try
{
}
catch (DirectoryException de)
{
}
// Check if the current user has permission to make
// this determination.
{
throw new DirectoryException(
}
try
{
{
}
}
catch (DirectoryException de)
{
{
throw de;
}
}
}
{
noOp = true;
}
{
}
{
continue;
}
{
// 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()
{
throw newDirectoryException(entryDN,
}
}
}
}
{
}
}