| | |
| | | * 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; |
| | |
| | | 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. |
| | |
| | | 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 |
| | |
| | | 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. |
| | |
| | | passwordPolicy.getPasswordValidators().values()) |
| | | { |
| | | if (! validator.passwordIsAcceptable(value, currentPasswords, this, |
| | | userEntry, invalidReason)) |
| | | entry, invalidReason)) |
| | | { |
| | | addPWPolicyControl( |
| | | PasswordPolicyErrorType.INSUFFICIENT_PASSWORD_QUALITY); |
| | |
| | | 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. |
| | |
| | | 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. |
| | |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 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); |
| | | } |
| | | } |
| | | |