Commits the refactoring of the core server to provide support for proxy/distribution/virtual functionnalities.
This includes the new set of local operations, as well as the workflow and networkgroup support.
25 files added
79 files modified
| | |
| | | package org.opends.server.api; |
| | | |
| | | |
| | | |
| | | import org.opends.server.core.*; |
| | | import org.opends.server.types.*; |
| | | import org.opends.server.workflowelement.localbackend.*; |
| | | |
| | | |
| | | /** |
| | |
| | | * the access control configuration, or <CODE>false</CODE> |
| | | * if not. |
| | | */ |
| | | public abstract boolean isAllowed(AddOperation addOperation); |
| | | public abstract boolean isAllowed(LocalBackendAddOperation |
| | | addOperation); |
| | | |
| | | |
| | | |
| | |
| | | * the access control configuration, or <CODE>false</CODE> |
| | | * if not. |
| | | */ |
| | | public abstract boolean isAllowed(BindOperation bindOperation); |
| | | public abstract boolean isAllowed(LocalBackendBindOperation |
| | | bindOperation); |
| | | |
| | | |
| | | |
| | |
| | | * the access control configuration, or <CODE>false</CODE> |
| | | * if not. |
| | | */ |
| | | public abstract boolean isAllowed(DeleteOperation deleteOperation); |
| | | public abstract boolean isAllowed(LocalBackendDeleteOperation |
| | | deleteOperation); |
| | | |
| | | |
| | | |
| | |
| | | * the access control configuration, or <CODE>false</CODE> |
| | | * if not. |
| | | */ |
| | | public abstract boolean isAllowed(ModifyOperation modifyOperation); |
| | | public abstract boolean isAllowed(LocalBackendModifyOperation |
| | | modifyOperation); |
| | | |
| | | |
| | | |
| | |
| | | * the access control configuration, or <CODE>false</CODE> |
| | | * if not. |
| | | */ |
| | | public abstract boolean isAllowed(SearchOperation searchOperation); |
| | | public abstract boolean isAllowed(LocalBackendSearchOperation |
| | | searchOperation); |
| | | |
| | | |
| | | |
| | |
| | | * the access control configuration, or <CODE>false</CODE> |
| | | * if not. |
| | | */ |
| | | public abstract boolean maySend(SearchOperation searchOperation, |
| | | public abstract boolean maySend( |
| | | SearchOperation searchOperation, |
| | | SearchResultEntry searchEntry); |
| | | |
| | | |
| | |
| | | * removed. |
| | | */ |
| | | public abstract SearchResultEntry filterEntry( |
| | | SearchOperation searchOperation, SearchResultEntry searchEntry); |
| | | SearchOperation searchOperation, |
| | | SearchResultEntry searchEntry); |
| | | |
| | | |
| | | |
| | |
| | | * the access control configuration, or <CODE>false</CODE> |
| | | * if not. |
| | | */ |
| | | public abstract boolean maySend(SearchOperation searchOperation, |
| | | public abstract boolean maySend( |
| | | SearchOperation searchOperation, |
| | | SearchResultReference searchReference); |
| | | |
| | | |
| | |
| | | import org.opends.server.core.PersistentSearch; |
| | | import org.opends.server.core.PluginConfigManager; |
| | | import org.opends.server.core.SearchOperation; |
| | | import org.opends.server.core.NetworkGroup; |
| | | import org.opends.server.types.AbstractOperation; |
| | | import org.opends.server.types.Attribute; |
| | | import org.opends.server.types.AttributeType; |
| | | import org.opends.server.types.AttributeValue; |
| | |
| | | // A set of persistent searches registered for this client. |
| | | private CopyOnWriteArrayList<PersistentSearch> persistentSearches; |
| | | |
| | | // The network group to which the connection belongs to. |
| | | private NetworkGroup networkGroup; |
| | | |
| | | |
| | | |
| | | /** |
| | |
| | | lookthroughLimit = DirectoryServer.getLookthroughLimit(); |
| | | finalized = false; |
| | | privileges = new HashSet<Privilege>(); |
| | | networkGroup = NetworkGroup.getDefaultNetworkGroup(); |
| | | } |
| | | |
| | | |
| | |
| | | * @return The set of operations in progress for this client |
| | | * connection. |
| | | */ |
| | | public abstract Collection<Operation> getOperationsInProgress(); |
| | | public abstract Collection<AbstractOperation> |
| | | getOperationsInProgress(); |
| | | |
| | | |
| | | |
| | |
| | | * or <CODE>null</CODE> if no such operation could be |
| | | * found. |
| | | */ |
| | | public abstract Operation getOperationInProgress(int messageID); |
| | | public abstract AbstractOperation |
| | | getOperationInProgress(int messageID); |
| | | |
| | | |
| | | |
| | |
| | | { |
| | | finalizeConnectionInternal(); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Returns the network group to which the connection belongs. |
| | | * |
| | | * @return the network group attached to the connection |
| | | */ |
| | | public NetworkGroup getNetworkGroup() |
| | | { |
| | | return networkGroup; |
| | | } |
| | | |
| | | /** |
| | | * Sets the network group to which the connection belongs. |
| | | * |
| | | * @param networkGroup the network group to which the |
| | | * connections belongs to |
| | | */ |
| | | public void setNetworkGroup (NetworkGroup networkGroup) |
| | | { |
| | | this.networkGroup = networkGroup; |
| | | } |
| | | |
| | | } |
| | | |
| | |
| | | |
| | | import org.opends.server.admin.std.server.SynchronizationProviderCfg; |
| | | import org.opends.server.config.ConfigException; |
| | | import org.opends.server.core.AddOperation; |
| | | import org.opends.server.core.DeleteOperation; |
| | | import org.opends.server.core.ModifyOperation; |
| | | import org.opends.server.core.ModifyDNOperation; |
| | | import org.opends.server.types.DirectoryException; |
| | | import org.opends.server.types.InitializationException; |
| | | import org.opends.server.types.Modification; |
| | | import org.opends.server.types.SynchronizationProviderResult; |
| | | import org.opends.server.workflowelement.localbackend.*; |
| | | |
| | | |
| | | |
| | |
| | | * synchronization processing. |
| | | */ |
| | | public SynchronizationProviderResult |
| | | handleConflictResolution(AddOperation addOperation) |
| | | handleConflictResolution(LocalBackendAddOperation |
| | | addOperation) |
| | | throws DirectoryException |
| | | { |
| | | // No processing is required by default. |
| | |
| | | * synchronization processing. |
| | | */ |
| | | public abstract SynchronizationProviderResult |
| | | doPreOperation(AddOperation addOperation) |
| | | doPreOperation(LocalBackendAddOperation |
| | | addOperation) |
| | | throws DirectoryException; |
| | | |
| | | |
| | |
| | | * @throws DirectoryException If a problem occurs during |
| | | * synchronization processing. |
| | | */ |
| | | public abstract void doPostOperation(AddOperation addOperation) |
| | | public abstract void doPostOperation(LocalBackendAddOperation |
| | | addOperation) |
| | | throws DirectoryException; |
| | | |
| | | |
| | |
| | | * synchronization processing. |
| | | */ |
| | | public SynchronizationProviderResult |
| | | handleConflictResolution(DeleteOperation deleteOperation) |
| | | handleConflictResolution(LocalBackendDeleteOperation |
| | | deleteOperation) |
| | | throws DirectoryException |
| | | { |
| | | // No processing is required by default. |
| | |
| | | * synchronization processing. |
| | | */ |
| | | public abstract SynchronizationProviderResult |
| | | doPreOperation(DeleteOperation deleteOperation) |
| | | doPreOperation(LocalBackendDeleteOperation |
| | | deleteOperation) |
| | | throws DirectoryException; |
| | | |
| | | |
| | |
| | | * synchronization processing. |
| | | */ |
| | | public abstract void doPostOperation( |
| | | DeleteOperation deleteOperation) |
| | | LocalBackendDeleteOperation |
| | | deleteOperation) |
| | | throws DirectoryException; |
| | | |
| | | |
| | |
| | | * synchronization processing. |
| | | */ |
| | | public SynchronizationProviderResult |
| | | handleConflictResolution(ModifyOperation |
| | | handleConflictResolution(LocalBackendModifyOperation |
| | | modifyOperation) |
| | | throws DirectoryException |
| | | { |
| | |
| | | * synchronization processing. |
| | | */ |
| | | public abstract SynchronizationProviderResult |
| | | doPreOperation(ModifyOperation modifyOperation) |
| | | doPreOperation(LocalBackendModifyOperation |
| | | modifyOperation) |
| | | throws DirectoryException; |
| | | |
| | | |
| | |
| | | * synchronization processing. |
| | | */ |
| | | public abstract void doPostOperation( |
| | | ModifyOperation modifyOperation) |
| | | LocalBackendModifyOperation |
| | | modifyOperation) |
| | | throws DirectoryException; |
| | | |
| | | |
| | |
| | | |
| | | import org.opends.server.admin.std.server.WorkQueueCfg; |
| | | import org.opends.server.config.ConfigException; |
| | | import org.opends.server.types.AbstractOperation; |
| | | import org.opends.server.types.DirectoryException; |
| | | import org.opends.server.types.InitializationException; |
| | | import org.opends.server.types.Operation; |
| | | |
| | | |
| | | |
| | |
| | | * already has too many pending |
| | | * requests in the queue). |
| | | */ |
| | | public abstract void submitOperation(Operation operation) |
| | | public abstract void submitOperation(AbstractOperation operation) |
| | | throws DirectoryException; |
| | | |
| | | |
| | |
| | | import org.opends.server.types.*; |
| | | import org.opends.server.api.ClientConnection; |
| | | import org.opends.server.api.Group; |
| | | import org.opends.server.core.AddOperationBasis; |
| | | import org.opends.server.api.ConnectionSecurityProvider; |
| | | import org.opends.server.core.AddOperation; |
| | | import org.opends.server.core.SearchOperation; |
| | | import org.opends.server.extensions.TLSConnectionSecurityProvider; |
| | | import org.opends.server.types.Operation; |
| | |
| | | this.resourceEntry=entry; |
| | | this.operation=operation; |
| | | this.clientConnection=operation.getClientConnection(); |
| | | if(operation instanceof AddOperation) |
| | | if(operation instanceof AddOperationBasis) |
| | | this.isAddOp=true; |
| | | |
| | | //If the proxied authorization control was processed, then the operation |
| | |
| | | |
| | | package org.opends.server.authorization.dseecompat; |
| | | |
| | | |
| | | import static org.opends.server.authorization.dseecompat.Aci.*; |
| | | |
| | | import org.opends.server.admin.std.server.DseeCompatAccessControlHandlerCfg; |
| | | import org.opends.server.api.AccessControlHandler; |
| | | import static org.opends.server.authorization.dseecompat.Aci.*; |
| | | import static org.opends.server.config.ConfigConstants.ATTR_AUTHZ_GLOBAL_ACI; |
| | | import org.opends.server.core.*; |
| | | |
| | | import static org.opends.server.loggers.ErrorLogger.logError; |
| | | import static org.opends.server.loggers.debug.DebugLogger.debugEnabled; |
| | | import static org.opends.server.loggers.debug.DebugLogger.getTracer; |
| | |
| | | import java.util.*; |
| | | import java.util.concurrent.locks.Lock; |
| | | |
| | | import org.opends.server.workflowelement.localbackend.*; |
| | | |
| | | /** |
| | | * The AciHandler class performs the main processing for the |
| | | * dseecompat package. |
| | |
| | | * @return True if access is allowed. |
| | | */ |
| | | private boolean aciCheckMods(AciLDAPOperationContainer container, |
| | | ModifyOperation operation, |
| | | LocalBackendModifyOperation operation, |
| | | boolean skipAccessCheck) { |
| | | Entry resourceEntry=container.getResourceEntry(); |
| | | DN dn=resourceEntry.getDN(); |
| | |
| | | * @param operation The add operation to check access on. |
| | | * @return True if access is allowed. |
| | | */ |
| | | public boolean isAllowed(AddOperation operation) { |
| | | public boolean isAllowed(LocalBackendAddOperation operation) { |
| | | AciLDAPOperationContainer operationContainer = |
| | | new AciLDAPOperationContainer(operation, ACI_ADD); |
| | | boolean ret=isAllowed(operationContainer,operation); |
| | |
| | | * @param operation The delete operation to check access on. |
| | | * @return True if access is allowed. |
| | | */ |
| | | public boolean isAllowed(DeleteOperation operation) { |
| | | public boolean isAllowed(LocalBackendDeleteOperation operation) { |
| | | AciLDAPOperationContainer operationContainer= |
| | | new AciLDAPOperationContainer(operation, ACI_DELETE); |
| | | return isAllowed(operationContainer, operation); |
| | |
| | | * @return True if access is allowed. |
| | | */ |
| | | |
| | | public boolean isAllowed(ModifyOperation operation) { |
| | | public boolean isAllowed(LocalBackendModifyOperation operation) { |
| | | AciLDAPOperationContainer operationContainer= |
| | | new AciLDAPOperationContainer(operation, ACI_NULL); |
| | | return aciCheckMods(operationContainer, operation, |
| | |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public boolean isAllowed(BindOperation bindOperation) { |
| | | public boolean isAllowed(LocalBackendBindOperation bindOperation) { |
| | | //Not planned to be implemented. |
| | | return true; |
| | | } |
| | |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public boolean isAllowed(SearchOperation searchOperation) { |
| | | public boolean isAllowed(LocalBackendSearchOperation searchOperation) { |
| | | //Not planned to be implemented. |
| | | return true; |
| | | } |
| | |
| | | import org.opends.server.types.Modification; |
| | | import org.opends.server.types.SearchResultEntry; |
| | | import org.opends.server.types.Entry; |
| | | import org.opends.server.workflowelement.localbackend.*; |
| | | |
| | | /** |
| | | * The AciLDAPOperationContainer is an AciContainer |
| | |
| | | * @param operation The add operation to evaluate. |
| | | * @param rights The rights of an add operation. |
| | | */ |
| | | public AciLDAPOperationContainer(AddOperation operation, int rights) { |
| | | public AciLDAPOperationContainer(LocalBackendAddOperation operation, |
| | | int rights) |
| | | { |
| | | super(operation, rights, operation.getEntryToAdd()); |
| | | } |
| | | |
| | |
| | | * @param operation The add operation to evaluate. |
| | | * @param rights The rights of a delete operation. |
| | | */ |
| | | public AciLDAPOperationContainer(DeleteOperation operation, int rights) { |
| | | public AciLDAPOperationContainer(LocalBackendDeleteOperation operation, |
| | | int rights) |
| | | { |
| | | super(operation, rights, operation.getEntryToDelete()); |
| | | } |
| | | |
| | |
| | | * @param rights The rights of modify operation. |
| | | * @param operation The add operation to evaluate. |
| | | */ |
| | | public AciLDAPOperationContainer(ModifyOperation operation, int rights) { |
| | | public AciLDAPOperationContainer(LocalBackendModifyOperation operation, |
| | | int rights) |
| | | { |
| | | super(operation, rights, operation.getCurrentEntry()); |
| | | this.modifications=operation.getModifications(); |
| | | } |
| | |
| | | * @param rights The rights of a search operation. |
| | | * @param entry The entry to be evaluated for this search. |
| | | */ |
| | | public AciLDAPOperationContainer(SearchOperation operation, int rights, |
| | | SearchResultEntry entry) { |
| | | public AciLDAPOperationContainer(SearchOperation operation, |
| | | int rights, |
| | | SearchResultEntry entry) |
| | | { |
| | | super(operation, rights, entry); |
| | | this.searchEntry = entry; |
| | | } |
| | |
| | | |
| | | package org.opends.server.authorization.dseecompat; |
| | | |
| | | import org.opends.server.workflowelement.localbackend.*; |
| | | import org.opends.server.api.ChangeNotificationListener; |
| | | import org.opends.server.api.BackendInitializationListener; |
| | | import org.opends.server.api.Backend; |
| | |
| | | null, baseDN, SearchScope.WHOLE_SUBTREE, |
| | | DereferencePolicy.NEVER_DEREF_ALIASES, |
| | | 0, 0, false, aciFilter, attrs, null); |
| | | LocalBackendSearchOperation localInternalSearch = |
| | | new LocalBackendSearchOperation(internalSearch); |
| | | try { |
| | | backend.search(internalSearch); |
| | | backend.search(localInternalSearch); |
| | | } catch (Exception e) { |
| | | if (debugEnabled()) |
| | | { |
| | |
| | | import org.opends.server.protocols.ldap.LDAPModification; |
| | | import org.opends.server.protocols.ldap.LDAPAttribute ; |
| | | import org.opends.server.protocols.internal.InternalSearchOperation ; |
| | | import org.opends.server.core.ModifyOperation ; |
| | | import org.opends.server.core.ModifyOperationBasis ; |
| | | import org.opends.server.types.LDAPException; |
| | | import org.opends.server.types.ModificationType; |
| | | |
| | |
| | | |
| | | // |
| | | // Process the modify |
| | | ModifyOperation op = jmxClientConnection.processModify( |
| | | ModifyOperationBasis op = jmxClientConnection.processModify( |
| | | new ASN1OctetString(configEntryDN.toString()), |
| | | ldapModList); |
| | | |
| | |
| | | |
| | | |
| | | |
| | | import static org.opends.server.core.CoreConstants.LOG_ELEMENT_ERROR_MESSAGE; |
| | | import static org.opends.server.core.CoreConstants.LOG_ELEMENT_ID_TO_ABANDON; |
| | | import static org.opends.server.core.CoreConstants.LOG_ELEMENT_PROCESSING_TIME; |
| | | import static org.opends.server.core.CoreConstants.LOG_ELEMENT_RESULT_CODE; |
| | | import static org.opends.server.messages.CoreMessages.*; |
| | | import static org.opends.server.messages.MessageHandler.getMessage; |
| | | |
| | | import java.util.List; |
| | | |
| | | import org.opends.server.api.ClientConnection; |
| | | import org.opends.server.api.plugin.PreParsePluginResult; |
| | | import org.opends.server.loggers.debug.DebugLogger; |
| | | import org.opends.server.loggers.debug.DebugTracer; |
| | | import org.opends.server.types.AbstractOperation; |
| | | import org.opends.server.types.CancelRequest; |
| | | import org.opends.server.types.CancelResult; |
| | | import org.opends.server.types.Control; |
| | | import org.opends.server.types.DisconnectReason; |
| | | import org.opends.server.types.Operation; |
| | | import org.opends.server.types.OperationType; |
| | | import org.opends.server.types.ResultCode; |
| | | import org.opends.server.types.operation.PostOperationAbandonOperation; |
| | | import org.opends.server.types.operation.PreParseAbandonOperation; |
| | | |
| | | import static org.opends.server.core.CoreConstants.*; |
| | | import static org.opends.server.loggers.AccessLogger.*; |
| | | import static org.opends.server.messages.CoreMessages.*; |
| | | import static org.opends.server.messages.MessageHandler.*; |
| | | |
| | | |
| | | |
| | | |
| | | /** |
| | |
| | | * may already be in progress in the Directory Server. |
| | | */ |
| | | public class AbandonOperation |
| | | extends Operation |
| | | extends AbstractOperation |
| | | implements PreParseAbandonOperation, PostOperationAbandonOperation |
| | | { |
| | | |
| | | |
| | | /** |
| | | * The tracer object for the debug logger. |
| | | */ |
| | | private static final DebugTracer TRACER = DebugLogger.getTracer(); |
| | | |
| | | // The message ID of the operation that should be abandoned. |
| | | private final int idToAbandon; |
| | | |
| | | // The time that processing started on this operation. |
| | | private long processingStartTime; |
| | | |
| | | // The time that processing ended on this operation. |
| | | private long processingStopTime; |
| | | |
| | | |
| | | |
| | | /** |
| | | * Creates a new abandon operation with the provided information. |
| | | * |
| | |
| | | return idToAbandon; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final long getProcessingStartTime() |
| | | { |
| | | return processingStartTime; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final long getProcessingStopTime() |
| | | { |
| | | return processingStopTime; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final long getProcessingTime() |
| | | { |
| | | return (processingStopTime - processingStartTime); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | |
| | | } |
| | | |
| | | String processingTime = |
| | | String.valueOf(processingStopTime - processingStartTime); |
| | | String.valueOf(getProcessingTime()); |
| | | |
| | | return new String[][] |
| | | { |
| | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | * Performs the work of actually processing this operation. This |
| | | * should include all processing for the operation, including |
| | | * invoking plugins, logging messages, performing access control, |
| | | * managing synchronization, and any other work that might need to |
| | | * be done in the course of processing. |
| | | */ |
| | | @Override() |
| | | public final void run() |
| | | { |
| | | setResultCode(ResultCode.UNDEFINED); |
| | |
| | | |
| | | |
| | | // Start the processing timer. |
| | | processingStartTime = System.currentTimeMillis(); |
| | | setProcessingStartTime(); |
| | | |
| | | |
| | | // Create a labeled block of code that we can break out of if a problem is |
| | |
| | | int msgID = MSGID_CANCELED_BY_PREPARSE_DISCONNECT; |
| | | appendErrorMessage(getMessage(msgID)); |
| | | |
| | | processingStopTime = System.currentTimeMillis(); |
| | | setProcessingStopTime(); |
| | | |
| | | logAbandonRequest(this); |
| | | logAbandonResult(this); |
| | |
| | | // code to reflect whether the abandon was successful and an error message |
| | | // if it was not. Even though there is no response, the result should |
| | | // still be logged. |
| | | Operation operation = |
| | | AbstractOperation operation = |
| | | clientConnection.getOperationInProgress(idToAbandon); |
| | | if (operation == null) |
| | | { |
| | |
| | | |
| | | |
| | | // Stop the processing timer. |
| | | processingStopTime = System.currentTimeMillis(); |
| | | setProcessingStopTime(); |
| | | |
| | | |
| | | // Log the result of the abandon operation. |
| | |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | protected boolean setCancelRequest(CancelRequest cancelRequest) |
| | | public boolean setCancelRequest(CancelRequest cancelRequest) |
| | | { |
| | | // Abandon operations cannot be canceled. |
| | | return false; |
| | |
| | | buffer.append(idToAbandon); |
| | | buffer.append(")"); |
| | | } |
| | | |
| | | } |
| | | |
| | |
| | | */ |
| | | package org.opends.server.core; |
| | | |
| | | |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.Iterator; |
| | | import java.util.LinkedHashSet; |
| | | import java.util.HashMap; |
| | | import java.util.HashSet; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.concurrent.CopyOnWriteArrayList; |
| | | import java.util.concurrent.locks.Lock; |
| | | |
| | | 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.api.plugin.PreParsePluginResult; |
| | | import org.opends.server.controls.LDAPAssertionRequestControl; |
| | | import org.opends.server.controls.LDAPPostReadRequestControl; |
| | | import org.opends.server.controls.LDAPPostReadResponseControl; |
| | | import org.opends.server.controls.ProxiedAuthV1Control; |
| | | import org.opends.server.controls.ProxiedAuthV2Control; |
| | | import org.opends.server.schema.AuthPasswordSyntax; |
| | | import org.opends.server.schema.BooleanSyntax; |
| | | import org.opends.server.schema.UserPasswordSyntax; |
| | | import org.opends.server.protocols.asn1.ASN1OctetString; |
| | | import org.opends.server.protocols.ldap.LDAPAttribute; |
| | | import org.opends.server.types.Attribute; |
| | | import org.opends.server.types.AttributeType; |
| | | import org.opends.server.types.AttributeValue; |
| | | import org.opends.server.types.ByteString; |
| | | import org.opends.server.types.CancelledOperationException; |
| | | import org.opends.server.types.CancelRequest; |
| | | import org.opends.server.types.CancelResult; |
| | | import org.opends.server.types.Control; |
| | | import org.opends.server.types.DirectoryException; |
| | | import org.opends.server.types.DisconnectReason; |
| | | import org.opends.server.types.DN; |
| | | import org.opends.server.types.Entry; |
| | | import org.opends.server.types.ErrorLogCategory; |
| | | import org.opends.server.types.ErrorLogSeverity; |
| | | import org.opends.server.types.LDAPException; |
| | | import org.opends.server.types.LockManager; |
| | | import org.opends.server.types.ObjectClass; |
| | | import org.opends.server.types.Operation; |
| | | import org.opends.server.types.OperationType; |
| | | import org.opends.server.types.Privilege; |
| | | import org.opends.server.types.RawAttribute; |
| | | 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.PreParseAddOperation; |
| | | import org.opends.server.util.TimeThread; |
| | | |
| | | import static org.opends.server.config.ConfigConstants.*; |
| | | import static org.opends.server.core.CoreConstants.*; |
| | | import static org.opends.server.loggers.AccessLogger.*; |
| | | import org.opends.server.types.DebugLogLevel; |
| | | import static org.opends.server.loggers.ErrorLogger.*; |
| | | import static org.opends.server.loggers.debug.DebugLogger.*; |
| | | import org.opends.server.loggers.debug.DebugTracer; |
| | | import static org.opends.server.messages.CoreMessages.*; |
| | | import static org.opends.server.messages.MessageHandler.*; |
| | | import static org.opends.server.util.ServerConstants.*; |
| | | import static org.opends.server.util.StaticUtils.*; |
| | | |
| | | |
| | | |
| | | |
| | | /** |
| | | * This class defines an operation that may be used to add a new entry to the |
| | | * Directory Server. |
| | | * This interface defines an operation that may be used to add a new entry to |
| | | * the Directory Server. |
| | | */ |
| | | public class AddOperation |
| | | extends Operation |
| | | implements PreParseAddOperation, PreOperationAddOperation, |
| | | PostOperationAddOperation, PostResponseAddOperation |
| | | public interface AddOperation extends Operation |
| | | { |
| | | /** |
| | | * The tracer object for the debug logger. |
| | | */ |
| | | private static final DebugTracer TRACER = getTracer(); |
| | | |
| | | // The set of response controls to send to the client. |
| | | private ArrayList<Control> responseControls; |
| | | |
| | | // The raw, unprocessed entry DN as provided in the request. This may or may |
| | | // not be a valid DN. |
| | | private ByteString rawEntryDN; |
| | | |
| | | // The cancel request that has been issued for this add operation. |
| | | private CancelRequest cancelRequest; |
| | | |
| | | // The processed DN of the entry to add. |
| | | private DN entryDN; |
| | | |
| | | // The proxied authorization target DN for this operation. |
| | | private DN proxiedAuthorizationDN; |
| | | |
| | | // The entry being added to the server. |
| | | private Entry entry; |
| | | |
| | | // The set of attributes (including the objectclass attribute) in a raw, |
| | | // unprocessed form as provided in the request. One or more of these |
| | | // attributes may be invalid. |
| | | private List<RawAttribute> rawAttributes; |
| | | |
| | | // 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; |
| | | |
| | | // The set of objectclasses for the entry to add. |
| | | private Map<ObjectClass,String> objectClasses; |
| | | |
| | | // The change number that has been assigned to this operation. |
| | | private long changeNumber; |
| | | |
| | | // The time that processing started on this operation. |
| | | private long processingStartTime; |
| | | |
| | | // The time that processing ended on this operation. |
| | | private long processingStopTime; |
| | | |
| | | |
| | | |
| | | /** |
| | | * Creates a new add operation with the provided information. |
| | | * |
| | | * @param clientConnection The client connection with which this operation |
| | | * is associated. |
| | | * @param operationID The operation ID for this operation. |
| | | * @param messageID The message ID of the request with which this |
| | | * operation is associated. |
| | | * @param requestControls The set of controls included in the request. |
| | | * @param rawEntryDN The raw DN of the entry to add from the client |
| | | * request. This may or may not be a valid DN. |
| | | * @param rawAttributes The raw set of attributes from the client |
| | | * request (including the objectclass attribute). |
| | | * This may contain invalid attributes. |
| | | */ |
| | | public AddOperation(ClientConnection clientConnection, long operationID, |
| | | int messageID, List<Control> requestControls, |
| | | ByteString rawEntryDN, List<RawAttribute> rawAttributes) |
| | | { |
| | | super(clientConnection, operationID, messageID, requestControls); |
| | | |
| | | |
| | | this.rawEntryDN = rawEntryDN; |
| | | this.rawAttributes = rawAttributes; |
| | | |
| | | responseControls = new ArrayList<Control>(); |
| | | cancelRequest = null; |
| | | entry = null; |
| | | entryDN = null; |
| | | userAttributes = null; |
| | | operationalAttributes = null; |
| | | objectClasses = null; |
| | | proxiedAuthorizationDN = null; |
| | | changeNumber = -1; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Creates a new add operation with the provided information. |
| | | * |
| | | * @param clientConnection The client connection with which this |
| | | * operation is associated. |
| | | * @param operationID The operation ID for this operation. |
| | | * @param messageID The message ID of the request with which |
| | | * this operation is associated. |
| | | * @param requestControls The set of controls included in the request. |
| | | * @param entryDN The DN for the entry. |
| | | * @param objectClasses The set of objectclasses for the entry. |
| | | * @param userAttributes The set of user attributes for the entry. |
| | | * @param operationalAttributes The set of operational attributes for the |
| | | * entry. |
| | | */ |
| | | public AddOperation(ClientConnection clientConnection, long operationID, |
| | | int messageID, List<Control> requestControls, |
| | | DN entryDN, Map<ObjectClass,String> objectClasses, |
| | | Map<AttributeType,List<Attribute>> userAttributes, |
| | | Map<AttributeType,List<Attribute>> operationalAttributes) |
| | | { |
| | | super(clientConnection, operationID, messageID, requestControls); |
| | | |
| | | |
| | | this.entryDN = entryDN; |
| | | this.objectClasses = objectClasses; |
| | | this.userAttributes = userAttributes; |
| | | this.operationalAttributes = operationalAttributes; |
| | | |
| | | entry = null; |
| | | |
| | | rawEntryDN = new ASN1OctetString(entryDN.toString()); |
| | | |
| | | rawAttributes = new ArrayList<RawAttribute>(); |
| | | |
| | | ArrayList<ASN1OctetString> ocValues = new ArrayList<ASN1OctetString>(); |
| | | for (String s : objectClasses.values()) |
| | | { |
| | | ocValues.add(new ASN1OctetString(s)); |
| | | } |
| | | |
| | | LDAPAttribute ocAttr = new LDAPAttribute(ATTR_OBJECTCLASS, ocValues); |
| | | rawAttributes.add(ocAttr); |
| | | |
| | | for (List<Attribute> attrList : userAttributes.values()) |
| | | { |
| | | for (Attribute a : attrList) |
| | | { |
| | | rawAttributes.add(new LDAPAttribute(a)); |
| | | } |
| | | } |
| | | |
| | | for (List<Attribute> attrList : operationalAttributes.values()) |
| | | { |
| | | for (Attribute a : attrList) |
| | | { |
| | | rawAttributes.add(new LDAPAttribute(a)); |
| | | } |
| | | } |
| | | |
| | | responseControls = new ArrayList<Control>(); |
| | | proxiedAuthorizationDN = null; |
| | | cancelRequest = null; |
| | | changeNumber = -1; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Retrieves the DN of the entry to add in a raw, unparsed form as it was |
| | |
| | | * |
| | | * @return The DN of the entry in a raw, unparsed form. |
| | | */ |
| | | public final ByteString getRawEntryDN() |
| | | { |
| | | return rawEntryDN; |
| | | } |
| | | |
| | | |
| | | public abstract ByteString getRawEntryDN(); |
| | | |
| | | /** |
| | | * Specifies the raw entry DN for the entry to add. This should only be |
| | |
| | | * |
| | | * @param rawEntryDN The raw entry DN for the entry to add. |
| | | */ |
| | | public final void setRawEntryDN(ByteString rawEntryDN) |
| | | { |
| | | this.rawEntryDN = rawEntryDN; |
| | | |
| | | entryDN = null; |
| | | } |
| | | |
| | | |
| | | public abstract void setRawEntryDN(ByteString rawEntryDN); |
| | | |
| | | /** |
| | | * Retrieves the DN of the entry to add. This method should not be called |
| | |
| | | * @return The DN of the entry to add, or <CODE>null</CODE> if it has not yet |
| | | * been parsed from the raw DN. |
| | | */ |
| | | public final DN getEntryDN() |
| | | { |
| | | return entryDN; |
| | | } |
| | | |
| | | |
| | | public abstract DN getEntryDN(); |
| | | |
| | | /** |
| | | * Retrieves the set of attributes in their raw, unparsed form as read from |
| | |
| | | * @return The set of attributes in their raw, unparsed form as read from the |
| | | * client request. |
| | | */ |
| | | public final List<RawAttribute> getRawAttributes() |
| | | { |
| | | return rawAttributes; |
| | | } |
| | | |
| | | |
| | | public abstract List<RawAttribute> getRawAttributes(); |
| | | |
| | | /** |
| | | * Adds the provided attribute to the set of raw attributes for this add |
| | |
| | | * @param rawAttribute The attribute to add to the set of raw attributes for |
| | | * this add operation. |
| | | */ |
| | | public final void addRawAttribute(RawAttribute rawAttribute) |
| | | { |
| | | rawAttributes.add(rawAttribute); |
| | | |
| | | objectClasses = null; |
| | | userAttributes = null; |
| | | operationalAttributes = null; |
| | | } |
| | | |
| | | |
| | | public abstract void addRawAttribute(RawAttribute rawAttribute); |
| | | |
| | | /** |
| | | * Replaces the set of raw attributes for this add operation. This should |
| | |
| | | * |
| | | * @param rawAttributes The set of raw attributes for this add operation. |
| | | */ |
| | | public final void setRawAttributes(List<RawAttribute> rawAttributes) |
| | | { |
| | | this.rawAttributes = rawAttributes; |
| | | |
| | | objectClasses = null; |
| | | userAttributes = null; |
| | | operationalAttributes = null; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Retrieves the set of processed objectclasses for the entry to add. This |
| | | * should not be called by pre-parse plugins because this information will not |
| | | * yet be available. The contents of the returned map may not be altered by |
| | | * the caller. |
| | | * |
| | | * @return The set of processed objectclasses for the entry to add, or |
| | | * <CODE>null</CODE> if that information is not yet available. |
| | | */ |
| | | public final Map<ObjectClass,String> getObjectClasses() |
| | | { |
| | | return objectClasses; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Adds the provided objectclass to the entry to add. This should only be |
| | | * called from pre-operation plugins. Note that pre-operation plugin |
| | | * processing is invoked after access control and schema validation, so |
| | | * plugins should be careful to only make changes that will not violate either |
| | | * schema or access control rules. |
| | | * |
| | | * @param objectClass The objectclass to add to the entry. |
| | | * @param name The name to use for the objectclass. |
| | | */ |
| | | public final void addObjectClass(ObjectClass objectClass, String name) |
| | | { |
| | | objectClasses.put(objectClass, name); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Removes the provided objectclass from the entry to add. This should only |
| | | * be called from pre-operation plugins. Note that pre-operation plugin |
| | | * processing is invoked after access control and schema validation, so |
| | | * plugins should be careful to only make changes that will not violate either |
| | | * schema or access control rules. |
| | | * |
| | | * @param objectClass The objectclass to remove from the entry. |
| | | */ |
| | | public final void removeObjectClass(ObjectClass objectClass) |
| | | { |
| | | objectClasses.remove(objectClass); |
| | | } |
| | | |
| | | |
| | | public abstract void setRawAttributes(List<RawAttribute> rawAttributes); |
| | | |
| | | /** |
| | | * Retrieves the set of processed user attributes for the entry to add. This |
| | |
| | | * @return The set of processed user attributes for the entry to add, or |
| | | * <CODE>null</CODE> if that information is not yet available. |
| | | */ |
| | | public final Map<AttributeType,List<Attribute>> getUserAttributes() |
| | | { |
| | | return userAttributes; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Retrieves the set of processed operational attributes for the entry to add. |
| | | * This should not be called by pre-parse plugins because this information |
| | | * will not yet be available. The contents of the returned map may be altered |
| | | * by the caller. |
| | | * |
| | | * @return The set of processed operational attributes for the entry to add, |
| | | * or <CODE>null</CODE> if that information is not yet available. |
| | | */ |
| | | public final Map<AttributeType,List<Attribute>> getOperationalAttributes() |
| | | { |
| | | return operationalAttributes; |
| | | } |
| | | |
| | | |
| | | public abstract Map<AttributeType, List<Attribute>> getUserAttributes(); |
| | | |
| | | /** |
| | | * Sets the specified attribute in the entry to add, overwriting any existing |
| | |
| | | * @param attributeType The attribute type for the attribute. |
| | | * @param attributeList The attribute list for the provided attribute type. |
| | | */ |
| | | public final void setAttribute(AttributeType attributeType, |
| | | List<Attribute> attributeList) |
| | | { |
| | | if (attributeType.isOperational()) |
| | | { |
| | | if ((attributeList == null) || (attributeList.isEmpty())) |
| | | { |
| | | operationalAttributes.remove(attributeType); |
| | | } |
| | | else |
| | | { |
| | | operationalAttributes.put(attributeType, attributeList); |
| | | } |
| | | } |
| | | else |
| | | { |
| | | if ((attributeList == null) || (attributeList.isEmpty())) |
| | | { |
| | | userAttributes.remove(attributeType); |
| | | } |
| | | else |
| | | { |
| | | userAttributes.put(attributeType, attributeList); |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | public abstract void setAttribute(AttributeType attributeType, |
| | | List<Attribute> attributeList); |
| | | |
| | | /** |
| | | * Removes the specified attribute from the entry to add. This should only be |
| | |
| | | * |
| | | * @param attributeType The attribute tyep for the attribute to remove. |
| | | */ |
| | | public final void removeAttribute(AttributeType attributeType) |
| | | { |
| | | if (attributeType.isOperational()) |
| | | { |
| | | operationalAttributes.remove(attributeType); |
| | | } |
| | | else |
| | | { |
| | | userAttributes.remove(attributeType); |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * 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; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final long getProcessingStartTime() |
| | | { |
| | | return processingStartTime; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final long getProcessingStopTime() |
| | | { |
| | | return processingStopTime; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final long getProcessingTime() |
| | | { |
| | | return (processingStopTime - processingStartTime); |
| | | } |
| | | |
| | | |
| | | public abstract void removeAttribute(AttributeType attributeType); |
| | | |
| | | /** |
| | | * Retrieves the change number that has been assigned to this operation. |
| | |
| | | * if none has been assigned yet or if there is no applicable |
| | | * synchronization mechanism in place that uses change numbers. |
| | | */ |
| | | public final long getChangeNumber() |
| | | { |
| | | return changeNumber; |
| | | } |
| | | |
| | | |
| | | public abstract long getChangeNumber(); |
| | | |
| | | /** |
| | | * Specifies the change number that has been assigned to this operation by the |
| | |
| | | * @param changeNumber The change number that has been assigned to this |
| | | * operation by the synchronization mechanism. |
| | | */ |
| | | public final void setChangeNumber(long changeNumber) |
| | | { |
| | | this.changeNumber = changeNumber; |
| | | } |
| | | |
| | | |
| | | public abstract void setChangeNumber(long changeNumber); |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | * Retrieves the set of processed objectclasses for the entry to add. This |
| | | * should not be called by pre-parse plugins because this information will not |
| | | * yet be available. The contents of the returned map may not be altered by |
| | | * the caller. |
| | | * |
| | | * @return The set of processed objectclasses for the entry to add, or |
| | | * <CODE>null</CODE> if that information is not yet available. |
| | | */ |
| | | @Override() |
| | | public final OperationType getOperationType() |
| | | { |
| | | // Note that no debugging will be done in this method because it is a likely |
| | | // candidate for being called by the logging subsystem. |
| | | |
| | | return OperationType.ADD; |
| | | } |
| | | |
| | | |
| | | public abstract Map<ObjectClass,String> getObjectClasses(); |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | * Adds the provided objectclass to the entry to add. This should only be |
| | | * called from pre-operation plugins. Note that pre-operation plugin |
| | | * processing is invoked after access control and schema validation, so |
| | | * plugins should be careful to only make changes that will not violate either |
| | | * schema or access control rules. |
| | | * |
| | | * @param objectClass The objectclass to add to the entry. |
| | | * @param name The name to use for the objectclass. |
| | | */ |
| | | @Override() |
| | | public final void disconnectClient(DisconnectReason disconnectReason, |
| | | boolean sendNotification, String message, |
| | | int messageID) |
| | | { |
| | | // Before calling clientConnection.disconnect, we need to mark this |
| | | // operation as cancelled so that the attempt to cancel it later won't cause |
| | | // an unnecessary delay. |
| | | setCancelResult(CancelResult.CANCELED); |
| | | |
| | | clientConnection.disconnect(disconnectReason, sendNotification, message, |
| | | messageID); |
| | | } |
| | | |
| | | |
| | | public abstract void addObjectClass(ObjectClass objectClass, String name); |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | * Removes the provided objectclass from the entry to add. This should only |
| | | * be called from pre-operation plugins. Note that pre-operation plugin |
| | | * processing is invoked after access control and schema validation, so |
| | | * plugins should be careful to only make changes that will not violate either |
| | | * schema or access control rules. |
| | | * |
| | | * @param objectClass The objectclass to remove from the entry. |
| | | */ |
| | | @Override() |
| | | public final String[][] getRequestLogElements() |
| | | { |
| | | // Note that no debugging will be done in this method because it is a likely |
| | | // candidate for being called by the logging subsystem. |
| | | |
| | | return new String[][] |
| | | { |
| | | new String[] { LOG_ELEMENT_ENTRY_DN, String.valueOf(rawEntryDN) } |
| | | }; |
| | | } |
| | | |
| | | |
| | | public abstract void removeObjectClass(ObjectClass objectClass); |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | * Retrieves the set of processed operational attributes for the entry to add. |
| | | * This should not be called by pre-parse plugins because this information |
| | | * will not yet be available. The contents of the returned map may be altered |
| | | * by the caller. |
| | | * |
| | | * @return The set of processed operational attributes for the entry to add, |
| | | * or <CODE>null</CODE> if that information is not yet available. |
| | | */ |
| | | @Override() |
| | | public final String[][] getResponseLogElements() |
| | | { |
| | | // Note that no debugging will be done in this method because it is a likely |
| | | // candidate for being called by the logging subsystem. |
| | | |
| | | String resultCode = String.valueOf(getResultCode().getIntValue()); |
| | | |
| | | String errorMessage; |
| | | StringBuilder errorMessageBuffer = getErrorMessage(); |
| | | if (errorMessageBuffer == null) |
| | | { |
| | | errorMessage = null; |
| | | } |
| | | else |
| | | { |
| | | errorMessage = errorMessageBuffer.toString(); |
| | | } |
| | | |
| | | String matchedDNStr; |
| | | DN matchedDN = getMatchedDN(); |
| | | if (matchedDN == null) |
| | | { |
| | | matchedDNStr = null; |
| | | } |
| | | else |
| | | { |
| | | matchedDNStr = matchedDN.toString(); |
| | | } |
| | | |
| | | String referrals; |
| | | List<String> referralURLs = getReferralURLs(); |
| | | if ((referralURLs == null) || referralURLs.isEmpty()) |
| | | { |
| | | referrals = null; |
| | | } |
| | | else |
| | | { |
| | | StringBuilder buffer = new StringBuilder(); |
| | | Iterator<String> iterator = referralURLs.iterator(); |
| | | buffer.append(iterator.next()); |
| | | |
| | | while (iterator.hasNext()) |
| | | { |
| | | buffer.append(", "); |
| | | buffer.append(iterator.next()); |
| | | } |
| | | |
| | | referrals = buffer.toString(); |
| | | } |
| | | |
| | | String processingTime = |
| | | String.valueOf(processingStopTime - processingStartTime); |
| | | |
| | | return new String[][] |
| | | { |
| | | new String[] { LOG_ELEMENT_RESULT_CODE, resultCode }, |
| | | new String[] { LOG_ELEMENT_ERROR_MESSAGE, errorMessage }, |
| | | new String[] { LOG_ELEMENT_MATCHED_DN, matchedDNStr }, |
| | | new String[] { LOG_ELEMENT_REFERRAL_URLS, referrals }, |
| | | new String[] { LOG_ELEMENT_PROCESSING_TIME, processingTime } |
| | | }; |
| | | } |
| | | |
| | | |
| | | public abstract Map<AttributeType,List<Attribute>> getOperationalAttributes(); |
| | | |
| | | /** |
| | | * Retrieves the proxied authorization DN for this operation if proxied |
| | |
| | | * authorization has been requested, or {@code null} if proxied |
| | | * authorization has not been requested. |
| | | */ |
| | | public DN getProxiedAuthorizationDN() |
| | | { |
| | | return proxiedAuthorizationDN; |
| | | } |
| | | |
| | | |
| | | public abstract DN getProxiedAuthorizationDN(); |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final ArrayList<Control> getResponseControls() |
| | | { |
| | | return responseControls; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final void addResponseControl(Control control) |
| | | { |
| | | responseControls.add(control); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final void removeResponseControl(Control control) |
| | | { |
| | | responseControls.remove(control); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final void run() |
| | | { |
| | | // Start the processing timer. |
| | | processingStartTime = System.currentTimeMillis(); |
| | | setResultCode(ResultCode.UNDEFINED); |
| | | |
| | | |
| | | // Check for and handle a request to cancel this operation. |
| | | if (cancelRequest != null) |
| | | { |
| | | indicateCancelled(cancelRequest); |
| | | processingStopTime = System.currentTimeMillis(); |
| | | return; |
| | | } |
| | | |
| | | |
| | | // Get the plugin config manager that will be used for invoking plugins. |
| | | PluginConfigManager pluginConfigManager = |
| | | DirectoryServer.getPluginConfigManager(); |
| | | boolean skipPostOperation = false; |
| | | |
| | | |
| | | // Create a labeled block of code that we can break out of if a problem is |
| | | // detected. |
| | | addProcessing: |
| | | { |
| | | // Invoke the pre-parse add plugins. |
| | | PreParsePluginResult preParseResult = |
| | | pluginConfigManager.invokePreParseAddPlugins(this); |
| | | if (preParseResult.connectionTerminated()) |
| | | { |
| | | // There's no point in continuing with anything. Log the request and |
| | | // result and return. |
| | | setResultCode(ResultCode.CANCELED); |
| | | |
| | | int msgID = MSGID_CANCELED_BY_PREPARSE_DISCONNECT; |
| | | appendErrorMessage(getMessage(msgID)); |
| | | |
| | | processingStopTime = System.currentTimeMillis(); |
| | | |
| | | logAddRequest(this); |
| | | logAddResponse(this); |
| | | pluginConfigManager.invokePostResponseAddPlugins(this); |
| | | return; |
| | | } |
| | | else if (preParseResult.sendResponseImmediately()) |
| | | { |
| | | skipPostOperation = true; |
| | | logAddRequest(this); |
| | | break addProcessing; |
| | | } |
| | | else if (preParseResult.skipCoreProcessing()) |
| | | { |
| | | skipPostOperation = false; |
| | | break addProcessing; |
| | | } |
| | | |
| | | |
| | | // Log the add request message. |
| | | logAddRequest(this); |
| | | |
| | | |
| | | // Check for and handle a request to cancel this operation. |
| | | if (cancelRequest != null) |
| | | { |
| | | indicateCancelled(cancelRequest); |
| | | processingStopTime = System.currentTimeMillis(); |
| | | logAddResponse(this); |
| | | pluginConfigManager.invokePostResponseAddPlugins(this); |
| | | return; |
| | | } |
| | | |
| | | |
| | | // 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. |
| | | try |
| | | { |
| | | if (entryDN == null) |
| | | { |
| | | entryDN = DN.decode(rawEntryDN); |
| | | } |
| | | } |
| | | catch (DirectoryException de) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, de); |
| | | } |
| | | |
| | | setResultCode(de.getResultCode()); |
| | | appendErrorMessage(de.getErrorMessage()); |
| | | setMatchedDN(de.getMatchedDN()); |
| | | setReferralURLs(de.getReferralURLs()); |
| | | |
| | | break addProcessing; |
| | | } |
| | | |
| | | |
| | | if ((objectClasses == null) || (userAttributes == null) || |
| | | (operationalAttributes == null)) |
| | | { |
| | | objectClasses = new HashMap<ObjectClass,String>(); |
| | | userAttributes = new HashMap<AttributeType,List<Attribute>>(); |
| | | operationalAttributes = new HashMap<AttributeType,List<Attribute>>(); |
| | | for (RawAttribute a : rawAttributes) |
| | | { |
| | | try |
| | | { |
| | | Attribute attr = a.toAttribute(); |
| | | AttributeType attrType = attr.getAttributeType(); |
| | | |
| | | |
| | | // If the attribute type is marked "NO-USER-MODIFICATION" then fail |
| | | // unless this is an internal operation or is related to |
| | | // synchronization in some way. |
| | | if (attrType.isNoUserModification()) |
| | | { |
| | | if (! (isInternalOperation() || isSynchronizationOperation())) |
| | | { |
| | | setResultCode(ResultCode.UNWILLING_TO_PERFORM); |
| | | appendErrorMessage(getMessage(MSGID_ADD_ATTR_IS_NO_USER_MOD, |
| | | String.valueOf(entryDN), |
| | | attr.getName())); |
| | | break addProcessing; |
| | | } |
| | | } |
| | | |
| | | |
| | | if (attrType.isObjectClassType()) |
| | | { |
| | | for (ByteString os : a.getValues()) |
| | | { |
| | | String ocName = os.toString(); |
| | | ObjectClass oc = |
| | | DirectoryServer.getObjectClass(toLowerCase(ocName)); |
| | | if (oc == null) |
| | | { |
| | | oc = DirectoryServer.getDefaultObjectClass(ocName); |
| | | } |
| | | |
| | | objectClasses.put(oc,ocName); |
| | | } |
| | | } |
| | | else if (attrType.isOperational()) |
| | | { |
| | | List<Attribute> attrs = operationalAttributes.get(attrType); |
| | | if (attrs == null) |
| | | { |
| | | attrs = new ArrayList<Attribute>(1); |
| | | attrs.add(attr); |
| | | operationalAttributes.put(attrType, attrs); |
| | | } |
| | | else |
| | | { |
| | | attrs.add(attr); |
| | | } |
| | | } |
| | | else |
| | | { |
| | | List<Attribute> attrs = userAttributes.get(attrType); |
| | | if (attrs == null) |
| | | { |
| | | attrs = new ArrayList<Attribute>(1); |
| | | attrs.add(attr); |
| | | userAttributes.put(attrType, attrs); |
| | | } |
| | | else |
| | | { |
| | | // Check to see if any of the existing attributes in the list |
| | | // have the same set of options. If so, then add the values |
| | | // to that attribute. |
| | | boolean attributeSeen = false; |
| | | for (Attribute ea : attrs) |
| | | { |
| | | if (ea.optionsEqual(attr.getOptions())) |
| | | { |
| | | LinkedHashSet<AttributeValue> valueSet = ea.getValues(); |
| | | valueSet.addAll(attr.getValues()); |
| | | attributeSeen = true; |
| | | } |
| | | } |
| | | if (!attributeSeen) |
| | | { |
| | | // This is the first occurrence of the attribute and options. |
| | | attrs.add(attr); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | catch (LDAPException le) |
| | | { |
| | | setResultCode(ResultCode.valueOf(le.getResultCode())); |
| | | appendErrorMessage(le.getMessage()); |
| | | |
| | | break addProcessing; |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | // Check for and handle a request to cancel this operation. |
| | | if (cancelRequest != null) |
| | | { |
| | | indicateCancelled(cancelRequest); |
| | | processingStopTime = System.currentTimeMillis(); |
| | | logAddResponse(this); |
| | | pluginConfigManager.invokePostResponseAddPlugins(this); |
| | | return; |
| | | } |
| | | |
| | | |
| | | // Grab a read lock on the parent entry, if there is one. We need to do |
| | | // this to ensure that the parent is not deleted or renamed while this add |
| | | // is in progress, and we could also need it to check the entry against |
| | | // a DIT structure rule. |
| | | Lock parentLock = null; |
| | | Lock entryLock = null; |
| | | |
| | | DN parentDN = entryDN.getParentDNInSuffix(); |
| | | if (parentDN == null) |
| | | { |
| | | // Either this entry is a suffix or doesn't belong in the directory. |
| | | if (DirectoryServer.isNamingContext(entryDN)) |
| | | { |
| | | // This is fine. This entry is one of the configured suffixes. |
| | | parentLock = null; |
| | | } |
| | | else if (entryDN.isNullDN()) |
| | | { |
| | | // This is not fine. The root DSE cannot be added. |
| | | setResultCode(ResultCode.UNWILLING_TO_PERFORM); |
| | | appendErrorMessage(getMessage(MSGID_ADD_CANNOT_ADD_ROOT_DSE)); |
| | | break addProcessing; |
| | | } |
| | | else |
| | | { |
| | | // The entry doesn't have a parent but isn't a suffix. This is not |
| | | // allowed. |
| | | setResultCode(ResultCode.NO_SUCH_OBJECT); |
| | | appendErrorMessage(getMessage(MSGID_ADD_ENTRY_NOT_SUFFIX, |
| | | String.valueOf(entryDN))); |
| | | break addProcessing; |
| | | } |
| | | } |
| | | else |
| | | { |
| | | for (int i=0; i < 3; i++) |
| | | { |
| | | parentLock = LockManager.lockRead(parentDN); |
| | | if (parentLock != null) |
| | | { |
| | | break; |
| | | } |
| | | } |
| | | |
| | | if (parentLock == null) |
| | | { |
| | | setResultCode(DirectoryServer.getServerErrorResultCode()); |
| | | appendErrorMessage(getMessage(MSGID_ADD_CANNOT_LOCK_PARENT, |
| | | String.valueOf(entryDN), |
| | | String.valueOf(parentDN))); |
| | | |
| | | skipPostOperation = true; |
| | | break addProcessing; |
| | | } |
| | | } |
| | | |
| | | |
| | | try |
| | | { |
| | | // Check for and handle a request to cancel this operation. |
| | | if (cancelRequest != null) |
| | | { |
| | | indicateCancelled(cancelRequest); |
| | | processingStopTime = System.currentTimeMillis(); |
| | | logAddResponse(this); |
| | | pluginConfigManager.invokePostResponseAddPlugins(this); |
| | | 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(getMessage(MSGID_ADD_CANNOT_LOCK_ENTRY, |
| | | 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(ErrorLogCategory.SYNCHRONIZATION, |
| | | ErrorLogSeverity.SEVERE_ERROR, |
| | | MSGID_ADD_SYNCH_CONFLICT_RESOLUTION_FAILED, |
| | | getConnectionID(), getOperationID(), |
| | | getExceptionMessage(de)); |
| | | |
| | | setResponseData(de); |
| | | 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(getMessage(MSGID_ADD_ENTRY_ALREADY_EXISTS, |
| | | String.valueOf(entryDN))); |
| | | break addProcessing; |
| | | } |
| | | } |
| | | catch (DirectoryException de) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, de); |
| | | } |
| | | |
| | | setResultCode(de.getResultCode()); |
| | | appendErrorMessage(de.getErrorMessage()); |
| | | setMatchedDN(de.getMatchedDN()); |
| | | setReferralURLs(de.getReferralURLs()); |
| | | break addProcessing; |
| | | } |
| | | |
| | | |
| | | // Get the parent entry, if it exists. |
| | | Entry parentEntry = null; |
| | | if (parentDN != null) |
| | | { |
| | | try |
| | | { |
| | | parentEntry = DirectoryServer.getEntry(parentDN); |
| | | |
| | | if (parentEntry == null) |
| | | { |
| | | DN matchedDN = parentDN.getParentDNInSuffix(); |
| | | while (matchedDN != null) |
| | | { |
| | | try |
| | | { |
| | | if (DirectoryServer.entryExists(matchedDN)) |
| | | { |
| | | 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(getMessage(MSGID_ADD_NO_PARENT, |
| | | String.valueOf(entryDN), |
| | | String.valueOf(parentDN))); |
| | | break addProcessing; |
| | | } |
| | | } |
| | | catch (DirectoryException de) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, de); |
| | | } |
| | | |
| | | setResultCode(de.getResultCode()); |
| | | appendErrorMessage(de.getErrorMessage()); |
| | | setMatchedDN(de.getMatchedDN()); |
| | | setReferralURLs(de.getReferralURLs()); |
| | | break addProcessing; |
| | | } |
| | | } |
| | | |
| | | |
| | | // Check to make sure that all of the RDN attributes are included as |
| | | // attribute values. If not, then either add them or report an error. |
| | | RDN rdn = entryDN.getRDN(); |
| | | int numAVAs = rdn.getNumValues(); |
| | | for (int i=0; i < numAVAs; i++) |
| | | { |
| | | AttributeType t = rdn.getAttributeType(i); |
| | | AttributeValue v = rdn.getAttributeValue(i); |
| | | String n = rdn.getAttributeName(i); |
| | | if (t.isOperational()) |
| | | { |
| | | List<Attribute> attrList = operationalAttributes.get(t); |
| | | if (attrList == null) |
| | | { |
| | | if (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 |
| | | { |
| | | setResultCode(ResultCode.CONSTRAINT_VIOLATION); |
| | | |
| | | int msgID = MSGID_ADD_MISSING_RDN_ATTRIBUTE; |
| | | appendErrorMessage(getMessage(msgID, String.valueOf(entryDN), |
| | | n)); |
| | | |
| | | break addProcessing; |
| | | } |
| | | } |
| | | else |
| | | { |
| | | boolean found = false; |
| | | for (Attribute a : attrList) |
| | | { |
| | | if (a.hasOptions()) |
| | | { |
| | | continue; |
| | | } |
| | | else |
| | | { |
| | | if (! a.hasValue(v)) |
| | | { |
| | | a.getValues().add(v); |
| | | } |
| | | |
| | | found = true; |
| | | break; |
| | | } |
| | | } |
| | | |
| | | if (! found) |
| | | { |
| | | if (isSynchronizationOperation() || |
| | | DirectoryServer.addMissingRDNAttributes()) |
| | | { |
| | | LinkedHashSet<AttributeValue> valueList = |
| | | new LinkedHashSet<AttributeValue>(1); |
| | | valueList.add(v); |
| | | attrList.add(new Attribute(t, n, valueList)); |
| | | } |
| | | else |
| | | { |
| | | setResultCode(ResultCode.CONSTRAINT_VIOLATION); |
| | | |
| | | int msgID = MSGID_ADD_MISSING_RDN_ATTRIBUTE; |
| | | appendErrorMessage(getMessage(msgID, String.valueOf(entryDN), |
| | | n)); |
| | | |
| | | break addProcessing; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | 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 |
| | | { |
| | | setResultCode(ResultCode.CONSTRAINT_VIOLATION); |
| | | |
| | | int msgID = MSGID_ADD_MISSING_RDN_ATTRIBUTE; |
| | | appendErrorMessage(getMessage(msgID, String.valueOf(entryDN), |
| | | n)); |
| | | |
| | | break addProcessing; |
| | | } |
| | | } |
| | | else |
| | | { |
| | | boolean found = false; |
| | | for (Attribute a : attrList) |
| | | { |
| | | if (a.hasOptions()) |
| | | { |
| | | continue; |
| | | } |
| | | else |
| | | { |
| | | if (! a.hasValue(v)) |
| | | { |
| | | a.getValues().add(v); |
| | | } |
| | | |
| | | found = true; |
| | | break; |
| | | } |
| | | } |
| | | |
| | | if (! found) |
| | | { |
| | | if (isSynchronizationOperation() || |
| | | DirectoryServer.addMissingRDNAttributes()) |
| | | { |
| | | LinkedHashSet<AttributeValue> valueList = |
| | | new LinkedHashSet<AttributeValue>(1); |
| | | valueList.add(v); |
| | | attrList.add(new Attribute(t, n, valueList)); |
| | | } |
| | | else |
| | | { |
| | | setResultCode(ResultCode.CONSTRAINT_VIOLATION); |
| | | |
| | | int msgID = MSGID_ADD_MISSING_RDN_ATTRIBUTE; |
| | | appendErrorMessage(getMessage(msgID, String.valueOf(entryDN), |
| | | n)); |
| | | |
| | | break addProcessing; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | // Check to make sure that all objectclasses have their superior classes |
| | | // listed in the entry. If not, then add them. |
| | | HashSet<ObjectClass> additionalClasses = null; |
| | | for (ObjectClass oc : objectClasses.keySet()) |
| | | { |
| | | ObjectClass superiorClass = oc.getSuperiorClass(); |
| | | if ((superiorClass != null) && |
| | | (! objectClasses.containsKey(superiorClass))) |
| | | { |
| | | if (additionalClasses == null) |
| | | { |
| | | additionalClasses = new HashSet<ObjectClass>(); |
| | | } |
| | | |
| | | additionalClasses.add(superiorClass); |
| | | } |
| | | } |
| | | |
| | | if (additionalClasses != null) |
| | | { |
| | | for (ObjectClass oc : additionalClasses) |
| | | { |
| | | 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))) |
| | | { |
| | | int msgID = MSGID_ADD_CHANGE_PRIVILEGE_INSUFFICIENT_PRIVILEGES; |
| | | appendErrorMessage(getMessage(msgID)); |
| | | 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()) |
| | | { |
| | | // FIXME -- We need to check to see if the password policy subentry |
| | | // might be specified virtually rather than as a real |
| | | // attribute. |
| | | PasswordPolicy pwPolicy = null; |
| | | List<Attribute> pwAttrList = |
| | | entry.getAttribute(OP_ATTR_PWPOLICY_POLICY_DN); |
| | | if ((pwAttrList != null) && (! pwAttrList.isEmpty())) |
| | | { |
| | | Attribute a = pwAttrList.get(0); |
| | | LinkedHashSet<AttributeValue> valueSet = a.getValues(); |
| | | Iterator<AttributeValue> iterator = valueSet.iterator(); |
| | | if (iterator.hasNext()) |
| | | { |
| | | DN policyDN; |
| | | try |
| | | { |
| | | policyDN = DN.decode(iterator.next().getValue()); |
| | | } |
| | | catch (DirectoryException de) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, de); |
| | | } |
| | | |
| | | int msgID = MSGID_ADD_INVALID_PWPOLICY_DN_SYNTAX; |
| | | appendErrorMessage(getMessage(msgID, String.valueOf(entryDN), |
| | | de.getErrorMessage())); |
| | | |
| | | setResultCode(ResultCode.INVALID_ATTRIBUTE_SYNTAX); |
| | | break addProcessing; |
| | | } |
| | | |
| | | pwPolicy = DirectoryServer.getPasswordPolicy(policyDN); |
| | | if (pwPolicy == null) |
| | | { |
| | | int msgID = MSGID_ADD_NO_SUCH_PWPOLICY; |
| | | appendErrorMessage(getMessage(msgID, String.valueOf(entryDN), |
| | | String.valueOf(policyDN))); |
| | | |
| | | setResultCode(ResultCode.CONSTRAINT_VIOLATION); |
| | | break addProcessing; |
| | | } |
| | | } |
| | | } |
| | | |
| | | if (pwPolicy == null) |
| | | { |
| | | pwPolicy = DirectoryServer.getDefaultPasswordPolicy(); |
| | | } |
| | | |
| | | try |
| | | { |
| | | handlePasswordPolicy(pwPolicy, entry); |
| | | } |
| | | catch (DirectoryException de) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, de); |
| | | } |
| | | |
| | | setResponseData(de); |
| | | break addProcessing; |
| | | } |
| | | } |
| | | |
| | | |
| | | // 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()) |
| | | { |
| | | StringBuilder invalidReason = new StringBuilder(); |
| | | if (! entry.conformsToSchema(parentEntry, true, true, true, |
| | | invalidReason)) |
| | | { |
| | | setResultCode(ResultCode.OBJECTCLASS_VIOLATION); |
| | | setErrorMessage(invalidReason); |
| | | break addProcessing; |
| | | } |
| | | else |
| | | { |
| | | switch (DirectoryServer.getSyntaxEnforcementPolicy()) |
| | | { |
| | | case REJECT: |
| | | invalidReason = new StringBuilder(); |
| | | 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)) |
| | | { |
| | | String message = |
| | | getMessage(MSGID_ADD_OP_INVALID_SYNTAX, |
| | | String.valueOf(entryDN), |
| | | String.valueOf(v.getStringValue()), |
| | | String.valueOf(a.getName()), |
| | | String.valueOf(invalidReason)); |
| | | invalidReason = new StringBuilder(message); |
| | | |
| | | setResultCode(ResultCode.INVALID_ATTRIBUTE_SYNTAX); |
| | | setErrorMessage(invalidReason); |
| | | break addProcessing; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | for (List<Attribute> attrList : |
| | | operationalAttributes.values()) |
| | | { |
| | | for (Attribute a : attrList) |
| | | { |
| | | AttributeSyntax syntax = a.getAttributeType().getSyntax(); |
| | | if (syntax != null) |
| | | { |
| | | for (AttributeValue v : a.getValues()) |
| | | { |
| | | if (! syntax.valueIsAcceptable(v.getValue(), |
| | | invalidReason)) |
| | | { |
| | | String message = |
| | | getMessage(MSGID_ADD_OP_INVALID_SYNTAX, |
| | | String.valueOf(entryDN), |
| | | String.valueOf(v.getStringValue()), |
| | | String.valueOf(a.getName()), |
| | | String.valueOf(invalidReason)); |
| | | invalidReason = new StringBuilder(message); |
| | | |
| | | setResultCode(ResultCode.INVALID_ATTRIBUTE_SYNTAX); |
| | | setErrorMessage(invalidReason); |
| | | break addProcessing; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | break; |
| | | |
| | | |
| | | case WARN: |
| | | invalidReason = new StringBuilder(); |
| | | 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(ErrorLogCategory.SCHEMA, |
| | | ErrorLogSeverity.SEVERE_WARNING, |
| | | MSGID_ADD_OP_INVALID_SYNTAX, |
| | | 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(ErrorLogCategory.SCHEMA, |
| | | ErrorLogSeverity.SEVERE_WARNING, |
| | | MSGID_ADD_OP_INVALID_SYNTAX, |
| | | 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()) |
| | | { |
| | | int msgID = MSGID_ADD_ATTR_IS_OBSOLETE; |
| | | String message = getMessage(msgID, String.valueOf(entryDN), |
| | | at.getNameOrOID()); |
| | | appendErrorMessage(message); |
| | | setResultCode(ResultCode.CONSTRAINT_VIOLATION); |
| | | break addProcessing; |
| | | } |
| | | } |
| | | |
| | | for (AttributeType at : operationalAttributes.keySet()) |
| | | { |
| | | if (at.isObsolete()) |
| | | { |
| | | int msgID = MSGID_ADD_ATTR_IS_OBSOLETE; |
| | | String message = getMessage(msgID, String.valueOf(entryDN), |
| | | at.getNameOrOID()); |
| | | appendErrorMessage(message); |
| | | setResultCode(ResultCode.CONSTRAINT_VIOLATION); |
| | | break addProcessing; |
| | | } |
| | | } |
| | | |
| | | for (ObjectClass oc : objectClasses.keySet()) |
| | | { |
| | | if (oc.isObsolete()) |
| | | { |
| | | int msgID = MSGID_ADD_OC_IS_OBSOLETE; |
| | | String message = getMessage(msgID, String.valueOf(entryDN), |
| | | oc.getNameOrOID()); |
| | | appendErrorMessage(message); |
| | | setResultCode(ResultCode.CONSTRAINT_VIOLATION); |
| | | break addProcessing; |
| | | } |
| | | } |
| | | } |
| | | |
| | | // Check to see if there are any controls in the request. If so, |
| | | // then |
| | | // see if there is any special processing required. |
| | | boolean noOp = false; |
| | | LDAPPostReadRequestControl postReadRequest = null; |
| | | List<Control> requestControls = 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); |
| | | } |
| | | |
| | | setResultCode(ResultCode.valueOf(le.getResultCode())); |
| | | appendErrorMessage(le.getMessage()); |
| | | |
| | | break addProcessing; |
| | | } |
| | | } |
| | | |
| | | try |
| | | { |
| | | // FIXME -- We need to determine whether the current user has |
| | | // permission to make this determination. |
| | | SearchFilter filter = assertControl.getSearchFilter(); |
| | | if (! filter.matchesEntry(entry)) |
| | | { |
| | | setResultCode(ResultCode.ASSERTION_FAILED); |
| | | |
| | | appendErrorMessage(getMessage(MSGID_ADD_ASSERTION_FAILED, |
| | | String.valueOf(entryDN))); |
| | | |
| | | break addProcessing; |
| | | } |
| | | } |
| | | catch (DirectoryException de) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, de); |
| | | } |
| | | |
| | | setResultCode(ResultCode.PROTOCOL_ERROR); |
| | | |
| | | int msgID = MSGID_ADD_CANNOT_PROCESS_ASSERTION_FILTER; |
| | | appendErrorMessage(getMessage(msgID, String.valueOf(entryDN), |
| | | de.getErrorMessage())); |
| | | |
| | | break addProcessing; |
| | | } |
| | | } |
| | | else if (oid.equals(OID_LDAP_NOOP_OPENLDAP_ASSIGNED)) |
| | | { |
| | | noOp = true; |
| | | } |
| | | 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); |
| | | } |
| | | |
| | | setResultCode(ResultCode.valueOf(le.getResultCode())); |
| | | appendErrorMessage(le.getMessage()); |
| | | |
| | | break addProcessing; |
| | | } |
| | | } |
| | | } |
| | | else if (oid.equals(OID_PROXIED_AUTH_V1)) |
| | | { |
| | | // The requester must have the PROXIED_AUTH privilige in order to |
| | | // be able to use this control. |
| | | if (! clientConnection.hasPrivilege(Privilege.PROXIED_AUTH, this)) |
| | | { |
| | | int msgID = MSGID_PROXYAUTH_INSUFFICIENT_PRIVILEGES; |
| | | appendErrorMessage(getMessage(msgID)); |
| | | setResultCode(ResultCode.AUTHORIZATION_DENIED); |
| | | break addProcessing; |
| | | } |
| | | |
| | | |
| | | ProxiedAuthV1Control proxyControl; |
| | | if (c instanceof ProxiedAuthV1Control) |
| | | { |
| | | proxyControl = (ProxiedAuthV1Control) c; |
| | | } |
| | | else |
| | | { |
| | | try |
| | | { |
| | | proxyControl = ProxiedAuthV1Control.decodeControl(c); |
| | | } |
| | | catch (LDAPException le) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, le); |
| | | } |
| | | |
| | | setResultCode(ResultCode.valueOf(le.getResultCode())); |
| | | appendErrorMessage(le.getMessage()); |
| | | |
| | | break addProcessing; |
| | | } |
| | | } |
| | | |
| | | |
| | | Entry authorizationEntry; |
| | | try |
| | | { |
| | | authorizationEntry = proxyControl.getAuthorizationEntry(); |
| | | } |
| | | catch (DirectoryException de) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, de); |
| | | } |
| | | |
| | | setResultCode(de.getResultCode()); |
| | | appendErrorMessage(de.getErrorMessage()); |
| | | |
| | | break addProcessing; |
| | | } |
| | | |
| | | if (AccessControlConfigManager.getInstance() |
| | | .getAccessControlHandler().isProxiedAuthAllowed(this, |
| | | authorizationEntry) == false) { |
| | | setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS); |
| | | |
| | | int msgID = MSGID_ADD_AUTHZ_INSUFFICIENT_ACCESS_RIGHTS; |
| | | appendErrorMessage(getMessage(msgID, String.valueOf(entryDN))); |
| | | |
| | | skipPostOperation = true; |
| | | break addProcessing; |
| | | } |
| | | setAuthorizationEntry(authorizationEntry); |
| | | if (authorizationEntry == null) |
| | | { |
| | | proxiedAuthorizationDN = DN.nullDN(); |
| | | } |
| | | else |
| | | { |
| | | proxiedAuthorizationDN = authorizationEntry.getDN(); |
| | | } |
| | | } |
| | | else if (oid.equals(OID_PROXIED_AUTH_V2)) |
| | | { |
| | | // The requester must have the PROXIED_AUTH privilige in order to |
| | | // be able to use this control. |
| | | if (! clientConnection.hasPrivilege(Privilege.PROXIED_AUTH, this)) |
| | | { |
| | | int msgID = MSGID_PROXYAUTH_INSUFFICIENT_PRIVILEGES; |
| | | appendErrorMessage(getMessage(msgID)); |
| | | setResultCode(ResultCode.AUTHORIZATION_DENIED); |
| | | break addProcessing; |
| | | } |
| | | |
| | | |
| | | ProxiedAuthV2Control proxyControl; |
| | | if (c instanceof ProxiedAuthV2Control) |
| | | { |
| | | proxyControl = (ProxiedAuthV2Control) c; |
| | | } |
| | | else |
| | | { |
| | | try |
| | | { |
| | | proxyControl = ProxiedAuthV2Control.decodeControl(c); |
| | | } |
| | | catch (LDAPException le) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, le); |
| | | } |
| | | |
| | | setResultCode(ResultCode.valueOf(le.getResultCode())); |
| | | appendErrorMessage(le.getMessage()); |
| | | |
| | | break addProcessing; |
| | | } |
| | | } |
| | | |
| | | |
| | | Entry authorizationEntry; |
| | | try |
| | | { |
| | | authorizationEntry = proxyControl.getAuthorizationEntry(); |
| | | } |
| | | catch (DirectoryException de) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, de); |
| | | } |
| | | |
| | | setResultCode(de.getResultCode()); |
| | | appendErrorMessage(de.getErrorMessage()); |
| | | |
| | | break addProcessing; |
| | | } |
| | | |
| | | if (AccessControlConfigManager.getInstance() |
| | | .getAccessControlHandler().isProxiedAuthAllowed(this, |
| | | authorizationEntry) == false) { |
| | | setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS); |
| | | |
| | | int msgID = MSGID_ADD_AUTHZ_INSUFFICIENT_ACCESS_RIGHTS; |
| | | appendErrorMessage(getMessage(msgID, String.valueOf(entryDN))); |
| | | |
| | | skipPostOperation = true; |
| | | break addProcessing; |
| | | } |
| | | setAuthorizationEntry(authorizationEntry); |
| | | if (authorizationEntry == null) |
| | | { |
| | | proxiedAuthorizationDN = DN.nullDN(); |
| | | } |
| | | else |
| | | { |
| | | proxiedAuthorizationDN = authorizationEntry.getDN(); |
| | | } |
| | | } |
| | | |
| | | // NYI -- Add support for additional controls. |
| | | else if (c.isCritical()) |
| | | { |
| | | Backend backend = DirectoryServer.getBackend(entryDN); |
| | | if ((backend == null) || (! backend.supportsControl(oid))) |
| | | { |
| | | setResultCode(ResultCode.UNAVAILABLE_CRITICAL_EXTENSION); |
| | | |
| | | int msgID = MSGID_ADD_UNSUPPORTED_CRITICAL_CONTROL; |
| | | appendErrorMessage(getMessage(msgID, String.valueOf(entryDN), |
| | | oid)); |
| | | |
| | | break addProcessing; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | // Check to see if the client has permission to perform the add. |
| | | |
| | | // FIXME: for now assume that this will check all permission |
| | | // pertinent to the operation. This includes proxy authorization |
| | | // and any other controls specified. |
| | | |
| | | // FIXME: earlier checks to see if the entry already exists or |
| | | // if the parent entry does not exist may have already exposed |
| | | // sensitive information to the client. |
| | | if (AccessControlConfigManager.getInstance() |
| | | .getAccessControlHandler().isAllowed(this) == false) { |
| | | setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS); |
| | | |
| | | int msgID = MSGID_ADD_AUTHZ_INSUFFICIENT_ACCESS_RIGHTS; |
| | | appendErrorMessage(getMessage(msgID, String.valueOf(entryDN))); |
| | | |
| | | skipPostOperation = true; |
| | | break addProcessing; |
| | | } |
| | | |
| | | // Check for and handle a request to cancel this operation. |
| | | if (cancelRequest != null) |
| | | { |
| | | indicateCancelled(cancelRequest); |
| | | processingStopTime = System.currentTimeMillis(); |
| | | logAddResponse(this); |
| | | pluginConfigManager.invokePostResponseAddPlugins(this); |
| | | return; |
| | | } |
| | | |
| | | |
| | | // If the operation is not a synchronization operation, |
| | | // Invoke the pre-operation modify 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); |
| | | |
| | | int msgID = MSGID_CANCELED_BY_PREOP_DISCONNECT; |
| | | appendErrorMessage(getMessage(msgID)); |
| | | |
| | | processingStopTime = System.currentTimeMillis(); |
| | | |
| | | logAddResponse(this); |
| | | pluginConfigManager.invokePostResponseAddPlugins(this); |
| | | return; |
| | | } |
| | | else if (preOpResult.sendResponseImmediately()) |
| | | { |
| | | skipPostOperation = true; |
| | | break addProcessing; |
| | | } |
| | | else if (preOpResult.skipCoreProcessing()) |
| | | { |
| | | skipPostOperation = false; |
| | | break addProcessing; |
| | | } |
| | | } |
| | | |
| | | |
| | | // Check for and handle a request to cancel this operation. |
| | | if (cancelRequest != null) |
| | | { |
| | | indicateCancelled(cancelRequest); |
| | | processingStopTime = System.currentTimeMillis(); |
| | | logAddResponse(this); |
| | | pluginConfigManager.invokePostResponseAddPlugins(this); |
| | | return; |
| | | } |
| | | |
| | | |
| | | // Actually perform the add operation. This should also include taking |
| | | // care of any synchronization that might be needed. |
| | | Backend backend = DirectoryServer.getBackend(entryDN); |
| | | if (backend == null) |
| | | { |
| | | setResultCode(ResultCode.NO_SUCH_OBJECT); |
| | | appendErrorMessage("No backend for entry " + entryDN.toString()); |
| | | } |
| | | else |
| | | { |
| | | // If it is not a private backend, then check to see if the server or |
| | | // backend is operating in read-only mode. |
| | | if (! backend.isPrivateBackend()) |
| | | { |
| | | switch (DirectoryServer.getWritabilityMode()) |
| | | { |
| | | case DISABLED: |
| | | setResultCode(ResultCode.UNWILLING_TO_PERFORM); |
| | | appendErrorMessage(getMessage(MSGID_ADD_SERVER_READONLY, |
| | | String.valueOf(entryDN))); |
| | | break addProcessing; |
| | | |
| | | case INTERNAL_ONLY: |
| | | if (! (isInternalOperation() || isSynchronizationOperation())) |
| | | { |
| | | setResultCode(ResultCode.UNWILLING_TO_PERFORM); |
| | | appendErrorMessage(getMessage(MSGID_ADD_SERVER_READONLY, |
| | | String.valueOf(entryDN))); |
| | | break addProcessing; |
| | | } |
| | | } |
| | | |
| | | switch (backend.getWritabilityMode()) |
| | | { |
| | | case DISABLED: |
| | | setResultCode(ResultCode.UNWILLING_TO_PERFORM); |
| | | appendErrorMessage(getMessage(MSGID_ADD_BACKEND_READONLY, |
| | | String.valueOf(entryDN))); |
| | | break addProcessing; |
| | | |
| | | case INTERNAL_ONLY: |
| | | if (! (isInternalOperation() || isSynchronizationOperation())) |
| | | { |
| | | setResultCode(ResultCode.UNWILLING_TO_PERFORM); |
| | | appendErrorMessage(getMessage(MSGID_ADD_BACKEND_READONLY, |
| | | String.valueOf(entryDN))); |
| | | break addProcessing; |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | try |
| | | { |
| | | if (noOp) |
| | | { |
| | | appendErrorMessage(getMessage(MSGID_ADD_NOOP)); |
| | | |
| | | // FIXME -- We must set a result code other than SUCCESS. |
| | | } |
| | | 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(ErrorLogCategory.SYNCHRONIZATION, |
| | | ErrorLogSeverity.SEVERE_ERROR, |
| | | MSGID_ADD_SYNCH_PREOP_FAILED, getConnectionID(), |
| | | getOperationID(), getExceptionMessage(de)); |
| | | |
| | | setResponseData(de); |
| | | break addProcessing; |
| | | } |
| | | } |
| | | |
| | | backend.addEntry(entry, this); |
| | | } |
| | | |
| | | if (postReadRequest != null) |
| | | { |
| | | Entry addedEntry = entry.duplicate(true); |
| | | |
| | | if (! postReadRequest.allowsAttribute( |
| | | DirectoryServer.getObjectClassAttributeType())) |
| | | { |
| | | addedEntry.removeAttribute( |
| | | DirectoryServer.getObjectClassAttributeType()); |
| | | } |
| | | |
| | | if (! postReadRequest.returnAllUserAttributes()) |
| | | { |
| | | Iterator<AttributeType> iterator = |
| | | addedEntry.getUserAttributes().keySet().iterator(); |
| | | while (iterator.hasNext()) |
| | | { |
| | | AttributeType attrType = iterator.next(); |
| | | if (! postReadRequest.allowsAttribute(attrType)) |
| | | { |
| | | iterator.remove(); |
| | | } |
| | | } |
| | | } |
| | | |
| | | if (! postReadRequest.returnAllOperationalAttributes()) |
| | | { |
| | | Iterator<AttributeType> iterator = |
| | | addedEntry.getOperationalAttributes().keySet().iterator(); |
| | | while (iterator.hasNext()) |
| | | { |
| | | AttributeType attrType = iterator.next(); |
| | | if (! postReadRequest.allowsAttribute(attrType)) |
| | | { |
| | | iterator.remove(); |
| | | } |
| | | } |
| | | } |
| | | |
| | | // FIXME -- Check access controls on the entry to see if it should |
| | | // be returned or if any attributes need to be stripped |
| | | // out.. |
| | | SearchResultEntry searchEntry = new SearchResultEntry(addedEntry); |
| | | LDAPPostReadResponseControl responseControl = |
| | | new LDAPPostReadResponseControl(postReadRequest.getOID(), |
| | | postReadRequest.isCritical(), |
| | | searchEntry); |
| | | |
| | | responseControls.add(responseControl); |
| | | } |
| | | |
| | | setResultCode(ResultCode.SUCCESS); |
| | | } |
| | | catch (DirectoryException de) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, de); |
| | | } |
| | | |
| | | setResultCode(de.getResultCode()); |
| | | appendErrorMessage(de.getErrorMessage()); |
| | | setMatchedDN(de.getMatchedDN()); |
| | | setReferralURLs(de.getReferralURLs()); |
| | | |
| | | break addProcessing; |
| | | } |
| | | catch (CancelledOperationException coe) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, coe); |
| | | } |
| | | |
| | | CancelResult cancelResult = coe.getCancelResult(); |
| | | |
| | | setCancelResult(cancelResult); |
| | | setResultCode(cancelResult.getResultCode()); |
| | | |
| | | String message = coe.getMessage(); |
| | | 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(ErrorLogCategory.SYNCHRONIZATION, |
| | | ErrorLogSeverity.SEVERE_ERROR, |
| | | MSGID_ADD_SYNCH_POSTOP_FAILED, 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 add plugins. |
| | | 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); |
| | | |
| | | int msgID = MSGID_CANCELED_BY_PREOP_DISCONNECT; |
| | | appendErrorMessage(getMessage(msgID)); |
| | | |
| | | processingStopTime = System.currentTimeMillis(); |
| | | |
| | | logAddResponse(this); |
| | | pluginConfigManager.invokePostResponseAddPlugins(this); |
| | | 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); |
| | | } |
| | | |
| | | int msgID = MSGID_ADD_ERROR_NOTIFYING_CHANGE_LISTENER; |
| | | String message = getMessage(msgID, getExceptionMessage(e)); |
| | | logError(ErrorLogCategory.CORE_SERVER, ErrorLogSeverity.SEVERE_ERROR, |
| | | message, msgID); |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | // Stop the processing timer. |
| | | processingStopTime = System.currentTimeMillis(); |
| | | |
| | | |
| | | // Send the add response to the client. |
| | | getClientConnection().sendResponse(this); |
| | | |
| | | |
| | | // Log the add response. |
| | | logAddResponse(this); |
| | | |
| | | |
| | | // Notify any persistent searches that might be registered with the server. |
| | | if ((getResultCode() == ResultCode.SUCCESS) && (entry != null)) |
| | | { |
| | | for (PersistentSearch persistentSearch : |
| | | DirectoryServer.getPersistentSearches()) |
| | | { |
| | | try |
| | | { |
| | | persistentSearch.processAdd(this, entry); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | |
| | | int msgID = MSGID_ADD_ERROR_NOTIFYING_PERSISTENT_SEARCH; |
| | | String message = getMessage(msgID, String.valueOf(persistentSearch), |
| | | getExceptionMessage(e)); |
| | | logError(ErrorLogCategory.CORE_SERVER, ErrorLogSeverity.SEVERE_ERROR, |
| | | message, msgID); |
| | | |
| | | DirectoryServer.deregisterPersistentSearch(persistentSearch); |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | // Invoke the post-response add plugins. |
| | | pluginConfigManager.invokePostResponseAddPlugins(this); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Adds the provided objectClass to the entry, along with its superior classes |
| | | * if appropriate. |
| | | * Set the proxied authorization DN for this operation if proxied |
| | | * authorization has been requested. |
| | | * |
| | | * @param objectClass The objectclass to add to the entry. |
| | | * @param proxiedAuthorizationDN |
| | | * The proxied authorization DN for this operation if proxied |
| | | * authorization has been requested, or {@code null} if proxied |
| | | * authorization has not been requested. |
| | | */ |
| | | private final void addObjectClassChain(ObjectClass objectClass) |
| | | { |
| | | if (! objectClasses.containsKey(objectClass)) |
| | | { |
| | | objectClasses.put(objectClass, objectClass.getNameOrOID()); |
| | | public abstract void setProxiedAuthorizationDN(DN proxiedAuthorizationDN); |
| | | |
| | | } |
| | | |
| | | 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. |
| | | */ |
| | | private final void handlePasswordPolicy(PasswordPolicy passwordPolicy, |
| | | Entry userEntry) |
| | | throws DirectoryException |
| | | { |
| | | // See if a password was specified. |
| | | AttributeType passwordAttribute = passwordPolicy.getPasswordAttribute(); |
| | | List<Attribute> attrList = userEntry.getAttribute(passwordAttribute); |
| | | if ((attrList == null) || attrList.isEmpty()) |
| | | { |
| | | // The entry doesn't have a password, so no action is required. |
| | | return; |
| | | } |
| | | else if (attrList.size() > 1) |
| | | { |
| | | // This must mean there are attribute options, which we won't allow for |
| | | // passwords. |
| | | int msgID = MSGID_PWPOLICY_ATTRIBUTE_OPTIONS_NOT_ALLOWED; |
| | | String message = getMessage(msgID, passwordAttribute.getNameOrOID()); |
| | | throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message, |
| | | msgID); |
| | | } |
| | | |
| | | Attribute passwordAttr = attrList.get(0); |
| | | if (passwordAttr.hasOptions()) |
| | | { |
| | | int msgID = MSGID_PWPOLICY_ATTRIBUTE_OPTIONS_NOT_ALLOWED; |
| | | String message = getMessage(msgID, passwordAttribute.getNameOrOID()); |
| | | throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message, |
| | | msgID); |
| | | } |
| | | |
| | | LinkedHashSet<AttributeValue> values = passwordAttr.getValues(); |
| | | if (values.isEmpty()) |
| | | { |
| | | // This will be treated the same as not having a password. |
| | | return; |
| | | } |
| | | |
| | | if ((! passwordPolicy.allowMultiplePasswordValues()) && (values.size() > 1)) |
| | | { |
| | | // FIXME -- What if they're pre-encoded and might all be the same? |
| | | int msgID = MSGID_PWPOLICY_MULTIPLE_PW_VALUES_NOT_ALLOWED; |
| | | String message = getMessage(msgID, passwordAttribute.getNameOrOID()); |
| | | throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message, |
| | | msgID); |
| | | } |
| | | |
| | | CopyOnWriteArrayList<PasswordStorageScheme> defaultStorageSchemes = |
| | | passwordPolicy.getDefaultStorageSchemes(); |
| | | LinkedHashSet<AttributeValue> newValues = |
| | | new LinkedHashSet<AttributeValue>(defaultStorageSchemes.size()); |
| | | for (AttributeValue v : values) |
| | | { |
| | | ByteString value = v.getValue(); |
| | | |
| | | // See if the password is pre-encoded. |
| | | if (passwordPolicy.usesAuthPasswordSyntax()) |
| | | { |
| | | if (AuthPasswordSyntax.isEncoded(value)) |
| | | { |
| | | if (passwordPolicy.allowPreEncodedPasswords()) |
| | | { |
| | | newValues.add(v); |
| | | continue; |
| | | } |
| | | else |
| | | { |
| | | int msgID = MSGID_PWPOLICY_PREENCODED_NOT_ALLOWED; |
| | | String message = getMessage(msgID, |
| | | passwordAttribute.getNameOrOID()); |
| | | throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, |
| | | message, msgID); |
| | | } |
| | | } |
| | | } |
| | | else |
| | | { |
| | | if (UserPasswordSyntax.isEncoded(value)) |
| | | { |
| | | if (passwordPolicy.allowPreEncodedPasswords()) |
| | | { |
| | | newValues.add(v); |
| | | continue; |
| | | } |
| | | else |
| | | { |
| | | int msgID = MSGID_PWPOLICY_PREENCODED_NOT_ALLOWED; |
| | | String message = getMessage(msgID, |
| | | passwordAttribute.getNameOrOID()); |
| | | throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, |
| | | message, msgID); |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | // See if the password passes validation. We should only do this if |
| | | // validation should be performed for administrators. |
| | | if (! passwordPolicy.skipValidationForAdministrators()) |
| | | { |
| | | // There are never any current passwords for an add operation. |
| | | HashSet<ByteString> currentPasswords = new HashSet<ByteString>(0); |
| | | StringBuilder invalidReason = new StringBuilder(); |
| | | for (PasswordValidator<?> validator : |
| | | passwordPolicy.getPasswordValidators().values()) |
| | | { |
| | | if (! validator.passwordIsAcceptable(value, currentPasswords, this, |
| | | userEntry, invalidReason)) |
| | | { |
| | | int msgID = MSGID_PWPOLICY_VALIDATION_FAILED; |
| | | String message = getMessage(msgID, passwordAttribute.getNameOrOID(), |
| | | String.valueOf(invalidReason)); |
| | | throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, |
| | | message, msgID); |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | // Encode the password. |
| | | if (passwordPolicy.usesAuthPasswordSyntax()) |
| | | { |
| | | for (PasswordStorageScheme s : defaultStorageSchemes) |
| | | { |
| | | ByteString encodedValue = s.encodeAuthPassword(value); |
| | | newValues.add(new AttributeValue(passwordAttribute, encodedValue)); |
| | | } |
| | | } |
| | | else |
| | | { |
| | | for (PasswordStorageScheme s : defaultStorageSchemes) |
| | | { |
| | | ByteString encodedValue = s.encodePasswordWithScheme(value); |
| | | newValues.add(new AttributeValue(passwordAttribute, encodedValue)); |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | // Put the new encoded values in the entry. |
| | | passwordAttr.setValues(newValues); |
| | | |
| | | |
| | | // Set the password changed time attribute. |
| | | ByteString timeString = |
| | | new ASN1OctetString(TimeThread.getGeneralizedTime()); |
| | | AttributeType changedTimeType = |
| | | DirectoryServer.getAttributeType(OP_ATTR_PWPOLICY_CHANGED_TIME_LC); |
| | | if (changedTimeType == null) |
| | | { |
| | | changedTimeType = DirectoryServer.getDefaultAttributeType( |
| | | OP_ATTR_PWPOLICY_CHANGED_TIME); |
| | | } |
| | | |
| | | LinkedHashSet<AttributeValue> changedTimeValues = |
| | | new LinkedHashSet<AttributeValue>(1); |
| | | changedTimeValues.add(new AttributeValue(changedTimeType, timeString)); |
| | | |
| | | ArrayList<Attribute> changedTimeList = new ArrayList<Attribute>(1); |
| | | changedTimeList.add(new Attribute(changedTimeType, |
| | | OP_ATTR_PWPOLICY_CHANGED_TIME, |
| | | changedTimeValues)); |
| | | |
| | | userEntry.putAttribute(changedTimeType, changedTimeList); |
| | | |
| | | |
| | | // If we should force change on add, then set the appropriate flag. |
| | | if (passwordPolicy.forceChangeOnAdd()) |
| | | { |
| | | AttributeType resetType = |
| | | DirectoryServer.getAttributeType(OP_ATTR_PWPOLICY_RESET_REQUIRED_LC); |
| | | if (resetType == null) |
| | | { |
| | | resetType = DirectoryServer.getDefaultAttributeType( |
| | | OP_ATTR_PWPOLICY_RESET_REQUIRED); |
| | | } |
| | | |
| | | LinkedHashSet<AttributeValue> resetValues = new |
| | | LinkedHashSet<AttributeValue>(1); |
| | | resetValues.add(BooleanSyntax.createBooleanValue(true)); |
| | | |
| | | ArrayList<Attribute> resetList = new ArrayList<Attribute>(1); |
| | | resetList.add(new Attribute(resetType, OP_ATTR_PWPOLICY_RESET_REQUIRED, |
| | | resetValues)); |
| | | userEntry.putAttribute(resetType, resetList); |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final CancelResult cancel(CancelRequest cancelRequest) |
| | | { |
| | | this.cancelRequest = cancelRequest; |
| | | |
| | | CancelResult cancelResult = getCancelResult(); |
| | | long stopWaitingTime = System.currentTimeMillis() + 5000; |
| | | while ((cancelResult == null) && |
| | | (System.currentTimeMillis() < stopWaitingTime)) |
| | | { |
| | | try |
| | | { |
| | | Thread.sleep(50); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | } |
| | | |
| | | cancelResult = getCancelResult(); |
| | | } |
| | | |
| | | if (cancelResult == null) |
| | | { |
| | | // This can happen in some rare cases (e.g., if a client disconnects and |
| | | // there is still a lot of data to send to that client), and in this case |
| | | // we'll prevent the cancel thread from blocking for a long period of |
| | | // time. |
| | | cancelResult = CancelResult.CANNOT_CANCEL; |
| | | } |
| | | |
| | | return cancelResult; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final CancelRequest getCancelRequest() |
| | | { |
| | | return cancelRequest; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | protected boolean setCancelRequest(CancelRequest cancelRequest) |
| | | { |
| | | this.cancelRequest = cancelRequest; |
| | | return true; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final void toString(StringBuilder buffer) |
| | | { |
| | | buffer.append("AddOperation(connID="); |
| | | buffer.append(clientConnection.getConnectionID()); |
| | | buffer.append(", opID="); |
| | | buffer.append(operationID); |
| | | buffer.append(", dn="); |
| | | buffer.append(rawEntryDN); |
| | | buffer.append(")"); |
| | | } |
| | | } |
| | | |
| New file |
| | |
| | | /* |
| | | * 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 |
| | | * trunk/opends/resource/legal-notices/OpenDS.LICENSE |
| | | * or https://OpenDS.dev.java.net/OpenDS.LICENSE. |
| | | * 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 |
| | | * trunk/opends/resource/legal-notices/OpenDS.LICENSE. 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 |
| | | * |
| | | * |
| | | * Portions Copyright 2007 Sun Microsystems, Inc. |
| | | */ |
| | | package org.opends.server.core; |
| | | |
| | | |
| | | |
| | | import static org.opends.server.config.ConfigConstants.ATTR_OBJECTCLASS; |
| | | import static org.opends.server.core.CoreConstants.LOG_ELEMENT_ENTRY_DN; |
| | | import static org.opends.server.core.CoreConstants.LOG_ELEMENT_ERROR_MESSAGE; |
| | | import static org.opends.server.core.CoreConstants.LOG_ELEMENT_MATCHED_DN; |
| | | import static org.opends.server.core.CoreConstants.LOG_ELEMENT_PROCESSING_TIME; |
| | | import static org.opends.server.core.CoreConstants.LOG_ELEMENT_REFERRAL_URLS; |
| | | import static org.opends.server.core.CoreConstants.LOG_ELEMENT_RESULT_CODE; |
| | | import static org.opends.server.loggers.AccessLogger.logAddRequest; |
| | | import static org.opends.server.loggers.AccessLogger.logAddResponse; |
| | | import static org.opends.server.loggers.ErrorLogger.logError; |
| | | import static org.opends.server.loggers.debug.DebugLogger.debugEnabled; |
| | | import static org.opends.server.messages.CoreMessages.*; |
| | | import static org.opends.server.messages.MessageHandler.getMessage; |
| | | import static org.opends.server.util.StaticUtils.getExceptionMessage; |
| | | import static org.opends.server.util.StaticUtils.toLowerCase; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.HashMap; |
| | | import java.util.Iterator; |
| | | import java.util.LinkedHashSet; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | | import org.opends.server.api.ClientConnection; |
| | | import org.opends.server.api.plugin.PreParsePluginResult; |
| | | import org.opends.server.loggers.debug.DebugLogger; |
| | | import org.opends.server.loggers.debug.DebugTracer; |
| | | import org.opends.server.protocols.asn1.ASN1OctetString; |
| | | import org.opends.server.protocols.ldap.LDAPAttribute; |
| | | import org.opends.server.types.Entry; |
| | | import org.opends.server.types.ErrorLogCategory; |
| | | import org.opends.server.types.ErrorLogSeverity; |
| | | import org.opends.server.types.LDAPException; |
| | | import org.opends.server.types.AbstractOperation; |
| | | import org.opends.server.types.Attribute; |
| | | import org.opends.server.types.AttributeType; |
| | | import org.opends.server.types.AttributeValue; |
| | | import org.opends.server.types.ByteString; |
| | | import org.opends.server.types.CancelRequest; |
| | | import org.opends.server.types.CancelResult; |
| | | import org.opends.server.types.Control; |
| | | import org.opends.server.types.DN; |
| | | import org.opends.server.types.DebugLogLevel; |
| | | import org.opends.server.types.DirectoryException; |
| | | import org.opends.server.types.DisconnectReason; |
| | | import org.opends.server.types.ObjectClass; |
| | | import org.opends.server.types.Operation; |
| | | import org.opends.server.types.OperationType; |
| | | import org.opends.server.types.RawAttribute; |
| | | import org.opends.server.types.ResultCode; |
| | | import org.opends.server.types.operation.PostResponseAddOperation; |
| | | import org.opends.server.types.operation.PreParseAddOperation; |
| | | import org.opends.server.workflowelement.localbackend.LocalBackendAddOperation; |
| | | |
| | | |
| | | |
| | | |
| | | /** |
| | | * This class defines an operation that may be used to add a new entry to the |
| | | * Directory Server. |
| | | */ |
| | | public class AddOperationBasis |
| | | extends AbstractOperation |
| | | implements PreParseAddOperation, AddOperation, Runnable, |
| | | PostResponseAddOperation |
| | | { |
| | | |
| | | /** |
| | | * The tracer object for the debug logger. |
| | | */ |
| | | private static final DebugTracer TRACER = DebugLogger.getTracer(); |
| | | |
| | | // The set of response controls to send to the client. |
| | | private ArrayList<Control> responseControls; |
| | | |
| | | // The raw, unprocessed entry DN as provided in the request. This may or may |
| | | // not be a valid DN. |
| | | private ByteString rawEntryDN; |
| | | |
| | | // The cancel request that has been issued for this add operation. |
| | | private CancelRequest cancelRequest; |
| | | |
| | | // The processed DN of the entry to add. |
| | | private DN entryDN; |
| | | |
| | | // The proxied authorization target DN for this operation. |
| | | private DN proxiedAuthorizationDN; |
| | | |
| | | // The set of attributes (including the objectclass attribute) in a raw, |
| | | // unprocessed form as provided in the request. One or more of these |
| | | // attributes may be invalid. |
| | | private List<RawAttribute> rawAttributes; |
| | | |
| | | // 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; |
| | | |
| | | // The set of objectclasses for the entry to add. |
| | | private Map<ObjectClass,String> objectClasses; |
| | | |
| | | // The change number that has been assigned to this operation. |
| | | private long changeNumber; |
| | | |
| | | |
| | | /** |
| | | * Creates a new add operation with the provided information. |
| | | * |
| | | * @param clientConnection The client connection with which this operation |
| | | * is associated. |
| | | * @param operationID The operation ID for this operation. |
| | | * @param messageID The message ID of the request with which this |
| | | * operation is associated. |
| | | * @param requestControls The set of controls included in the request. |
| | | * @param rawEntryDN The raw DN of the entry to add from the client |
| | | * request. This may or may not be a valid DN. |
| | | * @param rawAttributes The raw set of attributes from the client |
| | | * request (including the objectclass attribute). |
| | | * This may contain invalid attributes. |
| | | */ |
| | | public AddOperationBasis(ClientConnection clientConnection, long operationID, |
| | | int messageID, List<Control> requestControls, |
| | | ByteString rawEntryDN, List<RawAttribute> rawAttributes) |
| | | { |
| | | super(clientConnection, operationID, messageID, requestControls); |
| | | |
| | | |
| | | this.rawEntryDN = rawEntryDN; |
| | | this.rawAttributes = rawAttributes; |
| | | |
| | | responseControls = new ArrayList<Control>(); |
| | | cancelRequest = null; |
| | | entryDN = null; |
| | | userAttributes = null; |
| | | operationalAttributes = null; |
| | | objectClasses = null; |
| | | proxiedAuthorizationDN = null; |
| | | changeNumber = -1; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Creates a new add operation with the provided information. |
| | | * |
| | | * @param clientConnection The client connection with which this |
| | | * operation is associated. |
| | | * @param operationID The operation ID for this operation. |
| | | * @param messageID The message ID of the request with which |
| | | * this operation is associated. |
| | | * @param requestControls The set of controls included in the request. |
| | | * @param entryDN The DN for the entry. |
| | | * @param objectClasses The set of objectclasses for the entry. |
| | | * @param userAttributes The set of user attributes for the entry. |
| | | * @param operationalAttributes The set of operational attributes for the |
| | | * entry. |
| | | */ |
| | | public AddOperationBasis(ClientConnection clientConnection, long operationID, |
| | | int messageID, List<Control> requestControls, |
| | | DN entryDN, Map<ObjectClass,String> objectClasses, |
| | | Map<AttributeType,List<Attribute>> userAttributes, |
| | | Map<AttributeType,List<Attribute>> operationalAttributes) |
| | | { |
| | | super(clientConnection, operationID, messageID, requestControls); |
| | | |
| | | |
| | | this.entryDN = entryDN; |
| | | this.objectClasses = objectClasses; |
| | | this.userAttributes = userAttributes; |
| | | this.operationalAttributes = operationalAttributes; |
| | | |
| | | rawEntryDN = new ASN1OctetString(entryDN.toString()); |
| | | |
| | | rawAttributes = new ArrayList<RawAttribute>(); |
| | | |
| | | ArrayList<ASN1OctetString> ocValues = new ArrayList<ASN1OctetString>(); |
| | | for (String s : objectClasses.values()) |
| | | { |
| | | ocValues.add(new ASN1OctetString(s)); |
| | | } |
| | | |
| | | LDAPAttribute ocAttr = new LDAPAttribute(ATTR_OBJECTCLASS, ocValues); |
| | | rawAttributes.add(ocAttr); |
| | | |
| | | for (List<Attribute> attrList : userAttributes.values()) |
| | | { |
| | | for (Attribute a : attrList) |
| | | { |
| | | rawAttributes.add(new LDAPAttribute(a)); |
| | | } |
| | | } |
| | | |
| | | for (List<Attribute> attrList : operationalAttributes.values()) |
| | | { |
| | | for (Attribute a : attrList) |
| | | { |
| | | rawAttributes.add(new LDAPAttribute(a)); |
| | | } |
| | | } |
| | | |
| | | responseControls = new ArrayList<Control>(); |
| | | proxiedAuthorizationDN = null; |
| | | cancelRequest = null; |
| | | changeNumber = -1; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public final ByteString getRawEntryDN() |
| | | { |
| | | return rawEntryDN; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public final void setRawEntryDN(ByteString rawEntryDN) |
| | | { |
| | | this.rawEntryDN = rawEntryDN; |
| | | |
| | | entryDN = null; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public final DN getEntryDN() |
| | | { |
| | | try |
| | | { |
| | | if (entryDN == null) |
| | | { |
| | | entryDN = DN.decode(rawEntryDN); |
| | | } |
| | | } |
| | | catch (DirectoryException de) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, de); |
| | | } |
| | | |
| | | setResultCode(de.getResultCode()); |
| | | appendErrorMessage(de.getErrorMessage()); |
| | | setMatchedDN(de.getMatchedDN()); |
| | | setReferralURLs(de.getReferralURLs()); |
| | | } |
| | | return entryDN; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public final List<RawAttribute> getRawAttributes() |
| | | { |
| | | return rawAttributes; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public final void addRawAttribute(RawAttribute rawAttribute) |
| | | { |
| | | rawAttributes.add(rawAttribute); |
| | | |
| | | objectClasses = null; |
| | | userAttributes = null; |
| | | operationalAttributes = null; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public final void setRawAttributes(List<RawAttribute> rawAttributes) |
| | | { |
| | | this.rawAttributes = rawAttributes; |
| | | |
| | | objectClasses = null; |
| | | userAttributes = null; |
| | | operationalAttributes = null; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public final Map<ObjectClass,String> getObjectClasses() |
| | | { |
| | | if (objectClasses == null){ |
| | | computeObjectClassesAndAttributes(); |
| | | } |
| | | return objectClasses; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public final void addObjectClass(ObjectClass objectClass, String name) |
| | | { |
| | | objectClasses.put(objectClass, name); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public final void removeObjectClass(ObjectClass objectClass) |
| | | { |
| | | objectClasses.remove(objectClass); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public final Map<AttributeType,List<Attribute>> getUserAttributes() |
| | | { |
| | | if (userAttributes == null){ |
| | | computeObjectClassesAndAttributes(); |
| | | } |
| | | return userAttributes; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public final Map<AttributeType,List<Attribute>> getOperationalAttributes() |
| | | { |
| | | if (operationalAttributes == null){ |
| | | computeObjectClassesAndAttributes(); |
| | | } |
| | | return operationalAttributes; |
| | | } |
| | | |
| | | /** |
| | | * Build the objectclasses, the user attributes and the operational attributes |
| | | * if there are not already computed. |
| | | */ |
| | | private final void computeObjectClassesAndAttributes() |
| | | { |
| | | if ((objectClasses == null) || (userAttributes == null) || |
| | | (operationalAttributes == null)) |
| | | { |
| | | objectClasses = new HashMap<ObjectClass,String>(); |
| | | userAttributes = new HashMap<AttributeType,List<Attribute>>(); |
| | | operationalAttributes = new HashMap<AttributeType,List<Attribute>>(); |
| | | |
| | | for (RawAttribute a : rawAttributes) |
| | | { |
| | | try |
| | | { |
| | | Attribute attr = a.toAttribute(); |
| | | AttributeType attrType = attr.getAttributeType(); |
| | | |
| | | // If the attribute type is marked "NO-USER-MODIFICATION" then fail |
| | | // unless this is an internal operation or is related to |
| | | // synchronization in some way. |
| | | if (attrType.isNoUserModification()) |
| | | { |
| | | if (! (isInternalOperation() || isSynchronizationOperation())) |
| | | { |
| | | setResultCode(ResultCode.UNWILLING_TO_PERFORM); |
| | | appendErrorMessage(getMessage(MSGID_ADD_ATTR_IS_NO_USER_MOD, |
| | | String.valueOf(entryDN), |
| | | attr.getName())); |
| | | |
| | | objectClasses = null; |
| | | userAttributes = null; |
| | | operationalAttributes = null; |
| | | return; |
| | | } |
| | | } |
| | | |
| | | if (attrType.isObjectClassType()) |
| | | { |
| | | for (ByteString os : a.getValues()) |
| | | { |
| | | String ocName = os.toString(); |
| | | ObjectClass oc = |
| | | DirectoryServer.getObjectClass(toLowerCase(ocName)); |
| | | if (oc == null) |
| | | { |
| | | oc = DirectoryServer.getDefaultObjectClass(ocName); |
| | | } |
| | | |
| | | objectClasses.put(oc,ocName); |
| | | } |
| | | } |
| | | else if (attrType.isOperational()) |
| | | { |
| | | List<Attribute> attrs = operationalAttributes.get(attrType); |
| | | if (attrs == null) |
| | | { |
| | | attrs = new ArrayList<Attribute>(1); |
| | | attrs.add(attr); |
| | | operationalAttributes.put(attrType, attrs); |
| | | } |
| | | else |
| | | { |
| | | attrs.add(attr); |
| | | } |
| | | } |
| | | else |
| | | { |
| | | List<Attribute> attrs = userAttributes.get(attrType); |
| | | if (attrs == null) |
| | | { |
| | | attrs = new ArrayList<Attribute>(1); |
| | | attrs.add(attr); |
| | | userAttributes.put(attrType, attrs); |
| | | } |
| | | else |
| | | { |
| | | // Check to see if any of the existing attributes in the list |
| | | // have the same set of options. If so, then add the values |
| | | // to that attribute. |
| | | boolean attributeSeen = false; |
| | | for (Attribute ea : attrs) |
| | | { |
| | | if (ea.optionsEqual(attr.getOptions())) |
| | | { |
| | | LinkedHashSet<AttributeValue> valueSet = ea.getValues(); |
| | | valueSet.addAll(attr.getValues()); |
| | | attributeSeen = true; |
| | | } |
| | | } |
| | | if (!attributeSeen) |
| | | { |
| | | // This is the first occurrence of the attribute and options. |
| | | attrs.add(attr); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | catch (LDAPException le) |
| | | { |
| | | setResultCode(ResultCode.valueOf(le.getResultCode())); |
| | | appendErrorMessage(le.getMessage()); |
| | | |
| | | objectClasses = null; |
| | | userAttributes = null; |
| | | operationalAttributes = null; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public final void setAttribute(AttributeType attributeType, |
| | | List<Attribute> attributeList) |
| | | { |
| | | if (attributeType.isOperational()) |
| | | { |
| | | if ((attributeList == null) || (attributeList.isEmpty())) |
| | | { |
| | | operationalAttributes.remove(attributeType); |
| | | } |
| | | else |
| | | { |
| | | operationalAttributes.put(attributeType, attributeList); |
| | | } |
| | | } |
| | | else |
| | | { |
| | | if ((attributeList == null) || (attributeList.isEmpty())) |
| | | { |
| | | userAttributes.remove(attributeType); |
| | | } |
| | | else |
| | | { |
| | | userAttributes.put(attributeType, attributeList); |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public final void removeAttribute(AttributeType attributeType) |
| | | { |
| | | if (attributeType.isOperational()) |
| | | { |
| | | operationalAttributes.remove(attributeType); |
| | | } |
| | | else |
| | | { |
| | | userAttributes.remove(attributeType); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public final long getChangeNumber() |
| | | { |
| | | return changeNumber; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public final void setChangeNumber(long changeNumber) |
| | | { |
| | | this.changeNumber = changeNumber; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public final OperationType getOperationType() |
| | | { |
| | | // Note that no debugging will be done in this method because it is a likely |
| | | // candidate for being called by the logging subsystem. |
| | | |
| | | return OperationType.ADD; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public final void disconnectClient(DisconnectReason disconnectReason, |
| | | boolean sendNotification, String message, |
| | | int messageID) |
| | | { |
| | | // Before calling clientConnection.disconnect, we need to mark this |
| | | // operation as cancelled so that the attempt to cancel it later won't cause |
| | | // an unnecessary delay. |
| | | setCancelResult(CancelResult.CANCELED); |
| | | |
| | | clientConnection.disconnect(disconnectReason, sendNotification, message, |
| | | messageID); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public final String[][] getRequestLogElements() |
| | | { |
| | | // Note that no debugging will be done in this method because it is a likely |
| | | // candidate for being called by the logging subsystem. |
| | | |
| | | return new String[][] |
| | | { |
| | | new String[] { LOG_ELEMENT_ENTRY_DN, String.valueOf(rawEntryDN) } |
| | | }; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public final String[][] getResponseLogElements() |
| | | { |
| | | // Note that no debugging will be done in this method because it is a likely |
| | | // candidate for being called by the logging subsystem. |
| | | |
| | | String resultCode = String.valueOf(getResultCode().getIntValue()); |
| | | |
| | | String errorMessage; |
| | | StringBuilder errorMessageBuffer = getErrorMessage(); |
| | | if (errorMessageBuffer == null) |
| | | { |
| | | errorMessage = null; |
| | | } |
| | | else |
| | | { |
| | | errorMessage = errorMessageBuffer.toString(); |
| | | } |
| | | |
| | | String matchedDNStr; |
| | | DN matchedDN = getMatchedDN(); |
| | | if (matchedDN == null) |
| | | { |
| | | matchedDNStr = null; |
| | | } |
| | | else |
| | | { |
| | | matchedDNStr = matchedDN.toString(); |
| | | } |
| | | |
| | | String referrals; |
| | | List<String> referralURLs = getReferralURLs(); |
| | | if ((referralURLs == null) || referralURLs.isEmpty()) |
| | | { |
| | | referrals = null; |
| | | } |
| | | else |
| | | { |
| | | StringBuilder buffer = new StringBuilder(); |
| | | Iterator<String> iterator = referralURLs.iterator(); |
| | | buffer.append(iterator.next()); |
| | | |
| | | while (iterator.hasNext()) |
| | | { |
| | | buffer.append(", "); |
| | | buffer.append(iterator.next()); |
| | | } |
| | | |
| | | referrals = buffer.toString(); |
| | | } |
| | | |
| | | String processingTime = |
| | | String.valueOf(getProcessingTime()); |
| | | |
| | | return new String[][] |
| | | { |
| | | new String[] { LOG_ELEMENT_RESULT_CODE, resultCode }, |
| | | new String[] { LOG_ELEMENT_ERROR_MESSAGE, errorMessage }, |
| | | new String[] { LOG_ELEMENT_MATCHED_DN, matchedDNStr }, |
| | | new String[] { LOG_ELEMENT_REFERRAL_URLS, referrals }, |
| | | new String[] { LOG_ELEMENT_PROCESSING_TIME, processingTime } |
| | | }; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public DN getProxiedAuthorizationDN() |
| | | { |
| | | return proxiedAuthorizationDN; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public final ArrayList<Control> getResponseControls() |
| | | { |
| | | return responseControls; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public final void addResponseControl(Control control) |
| | | { |
| | | responseControls.add(control); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public final void removeResponseControl(Control control) |
| | | { |
| | | responseControls.remove(control); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public final CancelResult cancel(CancelRequest cancelRequest) |
| | | { |
| | | this.cancelRequest = cancelRequest; |
| | | |
| | | CancelResult cancelResult = getCancelResult(); |
| | | long stopWaitingTime = System.currentTimeMillis() + 5000; |
| | | while ((cancelResult == null) && |
| | | (System.currentTimeMillis() < stopWaitingTime)) |
| | | { |
| | | try |
| | | { |
| | | Thread.sleep(50); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | } |
| | | |
| | | cancelResult = getCancelResult(); |
| | | } |
| | | |
| | | if (cancelResult == null) |
| | | { |
| | | // This can happen in some rare cases (e.g., if a client disconnects and |
| | | // there is still a lot of data to send to that client), and in this case |
| | | // we'll prevent the cancel thread from blocking for a long period of |
| | | // time. |
| | | cancelResult = CancelResult.CANNOT_CANCEL; |
| | | } |
| | | |
| | | return cancelResult; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public final CancelRequest getCancelRequest() |
| | | { |
| | | return cancelRequest; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public |
| | | boolean setCancelRequest(CancelRequest cancelRequest) |
| | | { |
| | | this.cancelRequest = cancelRequest; |
| | | return true; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public final void toString(StringBuilder buffer) |
| | | { |
| | | buffer.append("AddOperation(connID="); |
| | | buffer.append(clientConnection.getConnectionID()); |
| | | buffer.append(", opID="); |
| | | buffer.append(operationID); |
| | | buffer.append(", dn="); |
| | | buffer.append(rawEntryDN); |
| | | buffer.append(")"); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setProxiedAuthorizationDN(DN proxiedAuthorizationDN) |
| | | { |
| | | this.proxiedAuthorizationDN = proxiedAuthorizationDN; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public final void run() |
| | | { |
| | | // Start the processing timer. |
| | | setProcessingStartTime(); |
| | | setResultCode(ResultCode.UNDEFINED); |
| | | |
| | | |
| | | // Check for and handle a request to cancel this operation. |
| | | if (getCancelRequest() != null) |
| | | { |
| | | indicateCancelled(getCancelRequest()); |
| | | setProcessingStopTime(); |
| | | return; |
| | | } |
| | | |
| | | |
| | | // Get the plugin config manager that will be used for invoking plugins. |
| | | PluginConfigManager pluginConfigManager = |
| | | DirectoryServer.getPluginConfigManager(); |
| | | |
| | | // Create a labeled block of code that we can break out of if a problem is |
| | | // detected. |
| | | addProcessing: |
| | | { |
| | | // Invoke the pre-parse add plugins. |
| | | PreParsePluginResult preParseResult = |
| | | pluginConfigManager.invokePreParseAddPlugins(this); |
| | | if (preParseResult.connectionTerminated()) |
| | | { |
| | | // There's no point in continuing with anything. Log the request and |
| | | // result and return. |
| | | setResultCode(ResultCode.CANCELED); |
| | | |
| | | int msgID = MSGID_CANCELED_BY_PREPARSE_DISCONNECT; |
| | | appendErrorMessage(getMessage(msgID)); |
| | | |
| | | setProcessingStopTime(); |
| | | |
| | | logAddRequest(this); |
| | | logAddResponse(this); |
| | | pluginConfigManager.invokePostResponseAddPlugins(this); |
| | | return; |
| | | } |
| | | else if (preParseResult.sendResponseImmediately()) |
| | | { |
| | | logAddRequest(this); |
| | | break addProcessing; |
| | | } |
| | | else if (preParseResult.skipCoreProcessing()) |
| | | { |
| | | break addProcessing; |
| | | } |
| | | |
| | | // Log the add request message. |
| | | logAddRequest(this); |
| | | |
| | | |
| | | // Check for and handle a request to cancel this operation. |
| | | if (getCancelRequest() != null) |
| | | { |
| | | indicateCancelled(getCancelRequest()); |
| | | setProcessingStopTime(); |
| | | logAddResponse(this); |
| | | pluginConfigManager.invokePostResponseAddPlugins(this); |
| | | return; |
| | | } |
| | | |
| | | |
| | | // Process the entry DN and set of attributes to convert them from their |
| | | // raw forms as provided by the client to the forms required for the rest |
| | | // of the add processing. |
| | | DN entryDN = getEntryDN(); |
| | | if (entryDN == null){ |
| | | break addProcessing; |
| | | } |
| | | |
| | | |
| | | // Retrieve the network group attached to the client connection |
| | | // and get a workflow to process the operation. |
| | | NetworkGroup ng = getClientConnection().getNetworkGroup(); |
| | | Workflow workflow = ng.getWorkflowCandidate(entryDN); |
| | | if (workflow == null) |
| | | { |
| | | // We have found no workflow for the requested base DN, just return |
| | | // a no such entry result code and stop the processing. |
| | | updateOperationErrMsgAndResCode(); |
| | | break addProcessing; |
| | | } |
| | | workflow.execute(this); |
| | | } |
| | | |
| | | // Check for and handle a request to cancel this operation. |
| | | if ((getCancelRequest() != null) || |
| | | (getCancelResult() == CancelResult.CANCELED)) |
| | | { |
| | | if (getCancelRequest() != null){ |
| | | indicateCancelled(getCancelRequest()); |
| | | } |
| | | setProcessingStopTime(); |
| | | logAddResponse(this); |
| | | invokePostResponsePlugins(); |
| | | return; |
| | | } |
| | | |
| | | // Indicate that it is now too late to attempt to cancel the operation. |
| | | setCancelResult(CancelResult.TOO_LATE); |
| | | |
| | | // Stop the processing timer. |
| | | setProcessingStopTime(); |
| | | |
| | | // Send the add response to the client. |
| | | getClientConnection().sendResponse(this); |
| | | |
| | | // Log the add response. |
| | | logAddResponse(this); |
| | | |
| | | // Check wether there are local operations in attachments |
| | | List localOperations = |
| | | (List)getAttachment(Operation.LOCALBACKENDOPERATIONS); |
| | | if (localOperations != null && (! localOperations.isEmpty())){ |
| | | for (Object localOp : localOperations) |
| | | { |
| | | LocalBackendAddOperation localOperation = |
| | | (LocalBackendAddOperation)localOp; |
| | | // Notify any persistent searches that might be registered with the |
| | | // server. |
| | | if ((getResultCode() == ResultCode.SUCCESS) && |
| | | (localOperation.getEntryToAdd() != null)) |
| | | { |
| | | for (PersistentSearch persistentSearch : |
| | | DirectoryServer.getPersistentSearches()) |
| | | { |
| | | try |
| | | { |
| | | persistentSearch.processAdd(localOperation, |
| | | localOperation.getEntryToAdd()); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | |
| | | int msgID = MSGID_ADD_ERROR_NOTIFYING_PERSISTENT_SEARCH; |
| | | String message = getMessage(msgID, |
| | | String.valueOf(persistentSearch), |
| | | getExceptionMessage(e)); |
| | | logError(ErrorLogCategory.CORE_SERVER, |
| | | ErrorLogSeverity.SEVERE_ERROR, |
| | | message, msgID); |
| | | |
| | | DirectoryServer.deregisterPersistentSearch(persistentSearch); |
| | | } |
| | | } |
| | | } |
| | | |
| | | // Invoke the post-response add plugins. |
| | | pluginConfigManager.invokePostResponseAddPlugins(localOperation); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Updates the error message and the result code of the operation. |
| | | * |
| | | * This method is called because no workflows were found to process |
| | | * the operation. |
| | | */ |
| | | private void updateOperationErrMsgAndResCode() |
| | | { |
| | | DN entryDN = getEntryDN(); |
| | | DN parentDN = entryDN.getParentDNInSuffix(); |
| | | if (parentDN == null) |
| | | { |
| | | // Either this entry is a suffix or doesn't belong in the directory. |
| | | if (DirectoryServer.isNamingContext(entryDN)) |
| | | { |
| | | // This is fine. This entry is one of the configured suffixes. |
| | | } |
| | | else if (entryDN.isNullDN()) |
| | | { |
| | | // This is not fine. The root DSE cannot be added. |
| | | setResultCode(ResultCode.UNWILLING_TO_PERFORM); |
| | | appendErrorMessage(getMessage(MSGID_ADD_CANNOT_ADD_ROOT_DSE)); |
| | | } |
| | | else |
| | | { |
| | | // The entry doesn't have a parent but isn't a suffix. This is not |
| | | // allowed. |
| | | setResultCode(ResultCode.NO_SUCH_OBJECT); |
| | | appendErrorMessage(getMessage(MSGID_ADD_ENTRY_NOT_SUFFIX, |
| | | String.valueOf(entryDN))); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Execute the postResponseAddPlugins. |
| | | */ |
| | | private void invokePostResponsePlugins() |
| | | { |
| | | // Get the plugin config manager that will be used for invoking plugins. |
| | | PluginConfigManager pluginConfigManager = |
| | | DirectoryServer.getPluginConfigManager(); |
| | | |
| | | // Check wether there are local operations in attachments |
| | | List localOperations = |
| | | (List)getAttachment(Operation.LOCALBACKENDOPERATIONS); |
| | | |
| | | if (localOperations != null && (! localOperations.isEmpty())) |
| | | { |
| | | for (Object localOp : localOperations) |
| | | { |
| | | LocalBackendAddOperation localOperation = |
| | | (LocalBackendAddOperation)localOp; |
| | | // Invoke the post-response add plugins. |
| | | pluginConfigManager.invokePostResponseAddPlugins(localOperation); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | * |
| | | * This method always returns null. |
| | | */ |
| | | public Entry getEntryToAdd() |
| | | { |
| | | return null; |
| | | } |
| | | |
| | | } |
| | | |
| New file |
| | |
| | | /* |
| | | * 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 |
| | | * trunk/opends/resource/legal-notices/OpenDS.LICENSE |
| | | * or https://OpenDS.dev.java.net/OpenDS.LICENSE. |
| | | * 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 |
| | | * trunk/opends/resource/legal-notices/OpenDS.LICENSE. 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 |
| | | * |
| | | * |
| | | * Portions Copyright 2007 Sun Microsystems, Inc. |
| | | */ |
| | | package org.opends.server.core; |
| | | |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | | import org.opends.server.api.ClientConnection; |
| | | import org.opends.server.types.Attribute; |
| | | import org.opends.server.types.AttributeType; |
| | | import org.opends.server.types.ByteString; |
| | | import org.opends.server.types.CancelRequest; |
| | | import org.opends.server.types.CancelResult; |
| | | import org.opends.server.types.Control; |
| | | import org.opends.server.types.DN; |
| | | import org.opends.server.types.DirectoryException; |
| | | import org.opends.server.types.DisconnectReason; |
| | | import org.opends.server.types.Entry; |
| | | import org.opends.server.types.ObjectClass; |
| | | import org.opends.server.types.OperationType; |
| | | import org.opends.server.types.RawAttribute; |
| | | import org.opends.server.types.ResultCode; |
| | | |
| | | /** |
| | | * This abstract class wraps/decorates a given add operation. |
| | | * This class will be extended by sub-classes to enhance the |
| | | * functionnality of the AddOperationBasis. |
| | | */ |
| | | public abstract class AddOperationWrapper implements AddOperation |
| | | { |
| | | private AddOperation add; |
| | | |
| | | /** |
| | | * Creates a new add operation based on the provided add operation. |
| | | * |
| | | * @param add The add operation to wrap |
| | | */ |
| | | public AddOperationWrapper(AddOperation add){ |
| | | this.add = add; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void addObjectClass(ObjectClass objectClass, String name) |
| | | { |
| | | add.addObjectClass(objectClass, name); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void addRawAttribute(RawAttribute rawAttribute) |
| | | { |
| | | add.addRawAttribute(rawAttribute); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void addRequestControl(Control control) |
| | | { |
| | | add.addRequestControl(control); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void addResponseControl(Control control) |
| | | { |
| | | add.addResponseControl(control); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void appendAdditionalLogMessage(String message) |
| | | { |
| | | add.appendAdditionalLogMessage(message); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void appendErrorMessage(String message) |
| | | { |
| | | add.appendErrorMessage(message); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public CancelResult cancel(CancelRequest cancelRequest) |
| | | { |
| | | return add.cancel(cancelRequest); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void disconnectClient(DisconnectReason disconnectReason, |
| | | boolean sendNotification, String message, int messageID) |
| | | { |
| | | add.disconnectClient(disconnectReason, sendNotification, message, |
| | | messageID); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public boolean dontSynchronize() |
| | | { |
| | | return add.dontSynchronize(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public StringBuilder getAdditionalLogMessage() |
| | | { |
| | | return add.getAdditionalLogMessage(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public Object getAttachment(String name) |
| | | { |
| | | return add.getAttachment(name); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public Map<String, Object> getAttachments() |
| | | { |
| | | return add.getAttachments(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public DN getAuthorizationDN() |
| | | { |
| | | return add.getAuthorizationDN(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public Entry getAuthorizationEntry() |
| | | { |
| | | return add.getAuthorizationEntry(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public CancelRequest getCancelRequest() |
| | | { |
| | | return add.getCancelRequest(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public CancelResult getCancelResult() |
| | | { |
| | | return add.getCancelResult(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public long getChangeNumber() |
| | | { |
| | | return add.getChangeNumber(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public ClientConnection getClientConnection() |
| | | { |
| | | return add.getClientConnection(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public String[][] getCommonLogElements() |
| | | { |
| | | return add.getCommonLogElements(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public long getConnectionID() |
| | | { |
| | | return add.getConnectionID(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public DN getEntryDN() |
| | | { |
| | | return add.getEntryDN(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public StringBuilder getErrorMessage() |
| | | { |
| | | return add.getErrorMessage(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public DN getMatchedDN() |
| | | { |
| | | return add.getMatchedDN(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public int getMessageID() |
| | | { |
| | | return add.getMessageID(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public Map<ObjectClass, String> getObjectClasses() |
| | | { |
| | | return add.getObjectClasses(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public Map<AttributeType, List<Attribute>> getOperationalAttributes() |
| | | { |
| | | return add.getOperationalAttributes(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public long getOperationID() |
| | | { |
| | | return add.getOperationID(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public OperationType getOperationType() |
| | | { |
| | | return add.getOperationType(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public long getProcessingStartTime() |
| | | { |
| | | return add.getProcessingStartTime(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public long getProcessingStopTime() |
| | | { |
| | | return add.getProcessingStopTime(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public long getProcessingTime() |
| | | { |
| | | return add.getProcessingTime(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public List<RawAttribute> getRawAttributes() |
| | | { |
| | | return add.getRawAttributes(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public ByteString getRawEntryDN() |
| | | { |
| | | return add.getRawEntryDN(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public List<String> getReferralURLs() |
| | | { |
| | | return add.getReferralURLs(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public List<Control> getRequestControls() |
| | | { |
| | | return add.getRequestControls(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public String[][] getRequestLogElements() |
| | | { |
| | | return add.getRequestLogElements(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public List<Control> getResponseControls() |
| | | { |
| | | return add.getResponseControls(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public String[][] getResponseLogElements() |
| | | { |
| | | return add.getResponseLogElements(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public ResultCode getResultCode() |
| | | { |
| | | return add.getResultCode(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public Map<AttributeType, List<Attribute>> getUserAttributes() |
| | | { |
| | | return add.getUserAttributes(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void indicateCancelled(CancelRequest cancelRequest) |
| | | { |
| | | add.indicateCancelled(cancelRequest); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public boolean isInternalOperation() |
| | | { |
| | | return add.isInternalOperation(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public boolean isSynchronizationOperation() |
| | | { |
| | | return add.isSynchronizationOperation(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void operationCompleted() |
| | | { |
| | | add.operationCompleted(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public Object removeAttachment(String name) |
| | | { |
| | | return add.removeAttachment(name); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void removeAttribute(AttributeType attributeType) |
| | | { |
| | | add.removeAttribute(attributeType); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void removeObjectClass(ObjectClass objectClass) |
| | | { |
| | | add.removeObjectClass(objectClass); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void removeRequestControl(Control control) |
| | | { |
| | | add.removeRequestControl(control); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void removeResponseControl(Control control) |
| | | { |
| | | add.removeResponseControl(control); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setAdditionalLogMessage(StringBuilder additionalLogMessage) |
| | | { |
| | | add.setAdditionalLogMessage(additionalLogMessage); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public Object setAttachment(String name, Object value) |
| | | { |
| | | return add.setAttachment(name, value); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setAttachments(Map<String, Object> attachments) |
| | | { |
| | | add.setAttachments(attachments); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setAttribute(AttributeType attributeType, |
| | | List<Attribute> attributeList) |
| | | { |
| | | add.setAttribute(attributeType, attributeList); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setAuthorizationEntry(Entry authorizationEntry) |
| | | { |
| | | add.setAuthorizationEntry(authorizationEntry); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public boolean setCancelRequest(CancelRequest cancelRequest) |
| | | { |
| | | return add.setCancelRequest(cancelRequest); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setCancelResult(CancelResult cancelResult) |
| | | { |
| | | add.setCancelResult(cancelResult); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setChangeNumber(long changeNumber) |
| | | { |
| | | add.setChangeNumber(changeNumber); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setDontSynchronize(boolean dontSynchronize) |
| | | { |
| | | add.setDontSynchronize(dontSynchronize); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setErrorMessage(StringBuilder errorMessage) |
| | | { |
| | | add.setErrorMessage(errorMessage); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setInternalOperation(boolean isInternalOperation) |
| | | { |
| | | add.setInternalOperation(isInternalOperation); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setMatchedDN(DN matchedDN) |
| | | { |
| | | add.setMatchedDN(matchedDN); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setProcessingStartTime() |
| | | { |
| | | add.setProcessingStartTime(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setProcessingStopTime() |
| | | { |
| | | add.setProcessingStopTime(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setRawAttributes(List<RawAttribute> rawAttributes) |
| | | { |
| | | add.setRawAttributes(rawAttributes); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setRawEntryDN(ByteString rawEntryDN) |
| | | { |
| | | add.setRawEntryDN(rawEntryDN); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setReferralURLs(List<String> referralURLs) |
| | | { |
| | | add.setReferralURLs(referralURLs); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setResponseData(DirectoryException directoryException) |
| | | { |
| | | add.setResponseData(directoryException); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setResultCode(ResultCode resultCode) |
| | | { |
| | | add.setResultCode(resultCode); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setSynchronizationOperation(boolean isSynchronizationOperation) |
| | | { |
| | | add.setSynchronizationOperation(isSynchronizationOperation); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public String toString() |
| | | { |
| | | return add.toString(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void toString(StringBuilder buffer) |
| | | { |
| | | add.toString(buffer); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public DN getProxiedAuthorizationDN() |
| | | { |
| | | return add.getProxiedAuthorizationDN(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setProxiedAuthorizationDN(DN proxiedAuthorizationDN) |
| | | { |
| | | add.setProxiedAuthorizationDN(proxiedAuthorizationDN); |
| | | } |
| | | |
| | | } |
| | |
| | | */ |
| | | package org.opends.server.core; |
| | | |
| | | |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.Iterator; |
| | | import java.util.LinkedHashSet; |
| | | import java.util.List; |
| | | import java.util.concurrent.locks.Lock; |
| | | |
| | | import org.opends.server.api.ClientConnection; |
| | | import org.opends.server.api.SASLMechanismHandler; |
| | | import org.opends.server.api.plugin.PostOperationPluginResult; |
| | | import org.opends.server.api.plugin.PreOperationPluginResult; |
| | | import org.opends.server.api.plugin.PreParsePluginResult; |
| | | import org.opends.server.controls.AuthorizationIdentityResponseControl; |
| | | import org.opends.server.controls.PasswordExpiredControl; |
| | | import org.opends.server.controls.PasswordExpiringControl; |
| | | import org.opends.server.controls.PasswordPolicyErrorType; |
| | | import org.opends.server.controls.PasswordPolicyResponseControl; |
| | | import org.opends.server.controls.PasswordPolicyWarningType; |
| | | import org.opends.server.protocols.asn1.ASN1OctetString; |
| | | import org.opends.server.types.AccountStatusNotificationType; |
| | | import org.opends.server.types.Attribute; |
| | | import org.opends.server.types.AttributeType; |
| | | import org.opends.server.types.AttributeValue; |
| | | import org.opends.server.types.AuthenticationInfo; |
| | | import org.opends.server.types.AuthenticationType; |
| | | import org.opends.server.types.ByteString; |
| | | import org.opends.server.types.CancelRequest; |
| | | import org.opends.server.types.CancelResult; |
| | | import org.opends.server.types.Control; |
| | | import org.opends.server.types.DirectoryException; |
| | | import org.opends.server.types.DisconnectReason; |
| | | import org.opends.server.types.DN; |
| | | import org.opends.server.types.Entry; |
| | | import org.opends.server.types.ErrorLogCategory; |
| | | import org.opends.server.types.ErrorLogSeverity; |
| | | import org.opends.server.types.LockManager; |
| | | import org.opends.server.types.Operation; |
| | | import org.opends.server.types.OperationType; |
| | | import org.opends.server.types.ResultCode; |
| | | import org.opends.server.types.operation.PostOperationBindOperation; |
| | | import org.opends.server.types.operation.PostResponseBindOperation; |
| | | import org.opends.server.types.operation.PreOperationBindOperation; |
| | | import org.opends.server.types.operation.PreParseBindOperation; |
| | | |
| | | import static org.opends.server.config.ConfigConstants.*; |
| | | import static org.opends.server.core.CoreConstants.*; |
| | | import static org.opends.server.loggers.AccessLogger.*; |
| | | import static org.opends.server.loggers.ErrorLogger.*; |
| | | import static org.opends.server.loggers.debug.DebugLogger.*; |
| | | import org.opends.server.loggers.debug.DebugTracer; |
| | | import org.opends.server.types.DebugLogLevel; |
| | | import static org.opends.server.messages.CoreMessages.*; |
| | | import static org.opends.server.messages.MessageHandler.*; |
| | | import static org.opends.server.util.ServerConstants.*; |
| | | import static org.opends.server.util.StaticUtils.*; |
| | | |
| | | |
| | | |
| | | |
| | | /** |
| | | * This class defines an operation that may be used to authenticate a user to |
| | | * the Directory Server. Note that for security restrictions, response messages |
| | | * that may be returned to the client must be carefully cleaned to ensure that |
| | | * they do not provide a malicious client with information that may be useful in |
| | | * an attack. This does impact the debugability of the server, but that can |
| | | * be addressed by calling the <CODE>setAuthFailureReason</CODE> method, which |
| | | * can provide a reason for a failure in a form that will not be returned to the |
| | | * client but may be written to a log file. |
| | | * This interface defines an operation that may be used to authenticate a user |
| | | * to the Directory Server. Note that for security restrictions, response |
| | | * messages that may be returned to the client must be carefully cleaned to |
| | | * ensure that they do not provide a malicious client with information that may |
| | | * be useful in an attack. This does impact the debugability of the server, |
| | | * but that can be addressed by calling the <CODE>setAuthFailureReason</CODE> |
| | | * method, which can provide a reason for a failure in a form that will not be |
| | | * returned to the client but may be written to a log file. |
| | | */ |
| | | public class BindOperation |
| | | extends Operation |
| | | implements PreParseBindOperation, PreOperationBindOperation, |
| | | PostOperationBindOperation, PostResponseBindOperation |
| | | public interface BindOperation extends Operation |
| | | { |
| | | /** |
| | | * The tracer object for the debug logger. |
| | | */ |
| | | private static final DebugTracer TRACER = getTracer(); |
| | | |
| | | // The credentials used for SASL authentication. |
| | | private ASN1OctetString saslCredentials; |
| | | |
| | | // The server SASL credentials provided to the client in the response. |
| | | private ASN1OctetString serverSASLCredentials; |
| | | |
| | | // The authentication info for this bind operation. |
| | | private AuthenticationInfo authInfo; |
| | | |
| | | // The authentication type used for this bind operation. |
| | | private AuthenticationType authType; |
| | | |
| | | // Indicates whether the warning notification that should be sent to the user |
| | | // would be the first warning. |
| | | private boolean isFirstWarning; |
| | | |
| | | // Indicates whether the authentication should use a grace login if it is |
| | | // successful. |
| | | private boolean isGraceLogin; |
| | | |
| | | // Indicates whether the user's password must be changed before any other |
| | | // operations will be allowed. |
| | | private boolean mustChangePassword; |
| | | |
| | | // Indicates whether the client included the password policy control in the |
| | | // bind request. |
| | | private boolean pwPolicyControlRequested; |
| | | |
| | | // The raw, unprocessed bind DN as contained in the client request. |
| | | private ByteString rawBindDN; |
| | | |
| | | // The password used for simple authentication. |
| | | private ByteString simplePassword; |
| | | |
| | | // The bind DN used for this bind operation. |
| | | private DN bindDN; |
| | | |
| | | // The DN of the user entry that is attempting to authenticate. |
| | | private DN userEntryDN; |
| | | |
| | | // The entry of the user that successfully authenticated during processing of |
| | | // this bind operation. |
| | | private Entry authenticatedUserEntry; |
| | | |
| | | // The DN of the user as whom a SASL authentication was attempted (regardless |
| | | // of whether the authentication was successful) for the purpose of updating |
| | | // password policy state information. |
| | | private Entry saslAuthUserEntry; |
| | | |
| | | // The unique ID associated with the failure reason message. |
| | | private int authFailureID; |
| | | |
| | | // The password policy warning value that should be included in the response |
| | | // control. |
| | | private int pwPolicyWarningValue; |
| | | |
| | | // The set of response controls for this bind operation. |
| | | private List<Control> responseControls; |
| | | |
| | | // The time that processing started on this operation. |
| | | private long processingStartTime; |
| | | |
| | | // The time that processing ended on this operation. |
| | | private long processingStopTime; |
| | | |
| | | // The password policy error type that should be included in the response |
| | | // control |
| | | private PasswordPolicyErrorType pwPolicyErrorType; |
| | | |
| | | // The password policy warning type that should be included in the response |
| | | // control |
| | | private PasswordPolicyWarningType pwPolicyWarningType; |
| | | |
| | | // The password policy state information for this bind operation. |
| | | private PasswordPolicyState pwPolicyState; |
| | | |
| | | // A message explaining the reason for the authentication failure. |
| | | private String authFailureReason; |
| | | |
| | | // A string representation of the protocol version for this bind operation. |
| | | private String protocolVersion; |
| | | |
| | | // The SASL mechanism used for SASL authentication. |
| | | private String saslMechanism; |
| | | |
| | | |
| | | |
| | | /** |
| | | * Creates a new simple bind operation with the provided information. |
| | | * |
| | | * @param clientConnection The client connection with which this operation |
| | | * is associated. |
| | | * @param operationID The operation ID for this operation. |
| | | * @param messageID The message ID of the request with which this |
| | | * operation is associated. |
| | | * @param requestControls The set of controls included in the request. |
| | | * @param protocolVersion The string representation of the protocol version |
| | | * associated with this bind request. |
| | | * @param rawBindDN The raw, unprocessed bind DN as provided in the |
| | | * request from the client. |
| | | * @param simplePassword The password to use for the simple |
| | | * authentication. |
| | | */ |
| | | public BindOperation(ClientConnection clientConnection, long operationID, |
| | | int messageID, List<Control> requestControls, |
| | | String protocolVersion, ByteString rawBindDN, |
| | | ByteString simplePassword) |
| | | { |
| | | super(clientConnection, operationID, messageID, requestControls); |
| | | |
| | | |
| | | this.protocolVersion = protocolVersion; |
| | | this.authType = AuthenticationType.SIMPLE; |
| | | this.saslMechanism = null; |
| | | this.saslCredentials = null; |
| | | |
| | | if (rawBindDN == null) |
| | | { |
| | | this.rawBindDN = new ASN1OctetString(); |
| | | } |
| | | else |
| | | { |
| | | this.rawBindDN = rawBindDN; |
| | | } |
| | | |
| | | if (simplePassword == null) |
| | | { |
| | | this.simplePassword = new ASN1OctetString(); |
| | | } |
| | | else |
| | | { |
| | | this.simplePassword = simplePassword; |
| | | } |
| | | |
| | | bindDN = null; |
| | | userEntryDN = null; |
| | | responseControls = new ArrayList<Control>(0); |
| | | authFailureID = 0; |
| | | authFailureReason = null; |
| | | authenticatedUserEntry = null; |
| | | saslAuthUserEntry = null; |
| | | isFirstWarning = false; |
| | | isGraceLogin = false; |
| | | mustChangePassword = false; |
| | | pwPolicyControlRequested = false; |
| | | pwPolicyErrorType = null; |
| | | pwPolicyWarningType = null; |
| | | pwPolicyWarningValue = -1; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Creates a new SASL bind operation with the provided information. |
| | | * |
| | | * @param clientConnection The client connection with which this operation |
| | | * is associated. |
| | | * @param operationID The operation ID for this operation. |
| | | * @param messageID The message ID of the request with which this |
| | | * operation is associated. |
| | | * @param requestControls The set of controls included in the request. |
| | | * @param protocolVersion The string representation of the protocol version |
| | | * associated with this bind request. |
| | | * @param rawBindDN The raw, unprocessed bind DN as provided in the |
| | | * request from the client. |
| | | * @param saslMechanism The SASL mechanism included in the request. |
| | | * @param saslCredentials The optional SASL credentials included in the |
| | | * request. |
| | | */ |
| | | public BindOperation(ClientConnection clientConnection, long operationID, |
| | | int messageID, List<Control> requestControls, |
| | | String protocolVersion, ByteString rawBindDN, |
| | | String saslMechanism, ASN1OctetString saslCredentials) |
| | | { |
| | | super(clientConnection, operationID, messageID, requestControls); |
| | | |
| | | |
| | | this.protocolVersion = protocolVersion; |
| | | this.authType = AuthenticationType.SASL; |
| | | this.saslMechanism = saslMechanism; |
| | | this.saslCredentials = saslCredentials; |
| | | this.simplePassword = null; |
| | | |
| | | if (rawBindDN == null) |
| | | { |
| | | this.rawBindDN = new ASN1OctetString(); |
| | | } |
| | | else |
| | | { |
| | | this.rawBindDN = rawBindDN; |
| | | } |
| | | |
| | | bindDN = null; |
| | | userEntryDN = null; |
| | | responseControls = new ArrayList<Control>(0); |
| | | authFailureID = 0; |
| | | authFailureReason = null; |
| | | authenticatedUserEntry = null; |
| | | saslAuthUserEntry = null; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Creates a new simple bind operation with the provided information. |
| | | * |
| | | * @param clientConnection The client connection with which this operation |
| | | * is associated. |
| | | * @param operationID The operation ID for this operation. |
| | | * @param messageID The message ID of the request with which this |
| | | * operation is associated. |
| | | * @param requestControls The set of controls included in the request. |
| | | * @param protocolVersion The string representation of the protocol version |
| | | * associated with this bind request. |
| | | * @param bindDN The bind DN for this bind operation. |
| | | * @param simplePassword The password to use for the simple |
| | | * authentication. |
| | | */ |
| | | public BindOperation(ClientConnection clientConnection, long operationID, |
| | | int messageID, List<Control> requestControls, |
| | | String protocolVersion, DN bindDN, |
| | | ByteString simplePassword) |
| | | { |
| | | super(clientConnection, operationID, messageID, requestControls); |
| | | |
| | | |
| | | this.protocolVersion = protocolVersion; |
| | | this.authType = AuthenticationType.SIMPLE; |
| | | this.bindDN = bindDN; |
| | | this.saslMechanism = null; |
| | | this.saslCredentials = null; |
| | | |
| | | if (bindDN == null) |
| | | { |
| | | rawBindDN = new ASN1OctetString(); |
| | | } |
| | | else |
| | | { |
| | | rawBindDN = new ASN1OctetString(bindDN.toString()); |
| | | } |
| | | |
| | | if (simplePassword == null) |
| | | { |
| | | this.simplePassword = new ASN1OctetString(); |
| | | } |
| | | else |
| | | { |
| | | this.simplePassword = simplePassword; |
| | | } |
| | | |
| | | responseControls = new ArrayList<Control>(0); |
| | | authFailureID = 0; |
| | | authFailureReason = null; |
| | | authenticatedUserEntry = null; |
| | | saslAuthUserEntry = null; |
| | | isFirstWarning = false; |
| | | isGraceLogin = false; |
| | | mustChangePassword = false; |
| | | pwPolicyControlRequested = false; |
| | | pwPolicyErrorType = null; |
| | | pwPolicyWarningType = null; |
| | | pwPolicyWarningValue = -1; |
| | | userEntryDN = null; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Creates a new SASL bind operation with the provided information. |
| | | * |
| | | * @param clientConnection The client connection with which this operation |
| | | * is associated. |
| | | * @param operationID The operation ID for this operation. |
| | | * @param messageID The message ID of the request with which this |
| | | * operation is associated. |
| | | * @param requestControls The set of controls included in the request. |
| | | * @param protocolVersion The string representation of the protocol version |
| | | * associated with this bind request. |
| | | * @param bindDN The bind DN for this bind operation. |
| | | * @param saslMechanism The SASL mechanism included in the request. |
| | | * @param saslCredentials The optional SASL credentials included in the |
| | | * request. |
| | | */ |
| | | public BindOperation(ClientConnection clientConnection, long operationID, |
| | | int messageID, List<Control> requestControls, |
| | | String protocolVersion, DN bindDN, |
| | | String saslMechanism, ASN1OctetString saslCredentials) |
| | | { |
| | | super(clientConnection, operationID, messageID, requestControls); |
| | | |
| | | |
| | | this.protocolVersion = protocolVersion; |
| | | this.authType = AuthenticationType.SASL; |
| | | this.bindDN = bindDN; |
| | | this.saslMechanism = saslMechanism; |
| | | this.saslCredentials = saslCredentials; |
| | | this.simplePassword = null; |
| | | |
| | | if (bindDN == null) |
| | | { |
| | | rawBindDN = new ASN1OctetString(); |
| | | } |
| | | else |
| | | { |
| | | rawBindDN = new ASN1OctetString(bindDN.toString()); |
| | | } |
| | | |
| | | responseControls = new ArrayList<Control>(0); |
| | | authFailureID = 0; |
| | | authFailureReason = null; |
| | | authenticatedUserEntry = null; |
| | | saslAuthUserEntry = null; |
| | | userEntryDN = null; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Retrieves the authentication type for this bind operation. |
| | | * |
| | | * @return The authentication type for this bind operation. |
| | | */ |
| | | public final AuthenticationType getAuthenticationType() |
| | | { |
| | | return authType; |
| | | } |
| | | |
| | | |
| | | public abstract AuthenticationType getAuthenticationType(); |
| | | |
| | | /** |
| | | * Retrieves the raw, unprocessed bind DN for this bind operation as contained |
| | |
| | | * @return The raw, unprocessed bind DN for this bind operation as contained |
| | | * in the client request. |
| | | */ |
| | | public final ByteString getRawBindDN() |
| | | { |
| | | return rawBindDN; |
| | | } |
| | | |
| | | |
| | | public abstract ByteString getRawBindDN(); |
| | | |
| | | /** |
| | | * Specifies the raw, unprocessed bind DN for this bind operation. This |
| | |
| | | * |
| | | * @param rawBindDN The raw, unprocessed bind DN for this bind operation. |
| | | */ |
| | | public final void setRawBindDN(ByteString rawBindDN) |
| | | { |
| | | if (rawBindDN == null) |
| | | { |
| | | this.rawBindDN = new ASN1OctetString(); |
| | | } |
| | | else |
| | | { |
| | | this.rawBindDN = rawBindDN; |
| | | } |
| | | |
| | | bindDN = null; |
| | | } |
| | | |
| | | |
| | | public abstract void setRawBindDN(ByteString rawBindDN); |
| | | |
| | | /** |
| | | * Retrieves a string representation of the protocol version associated with |
| | |
| | | * @return A string representation of the protocol version associated with |
| | | * this bind request. |
| | | */ |
| | | public String getProtocolVersion() |
| | | { |
| | | return protocolVersion; |
| | | } |
| | | |
| | | |
| | | public String getProtocolVersion(); |
| | | |
| | | /** |
| | | * Specifies the string representation of the protocol version associated with |
| | |
| | | * @param protocolVersion The string representation of the protocol version |
| | | * associated with this bind request. |
| | | */ |
| | | public void setProtocolVersion(String protocolVersion) |
| | | { |
| | | this.protocolVersion = protocolVersion; |
| | | } |
| | | |
| | | |
| | | public void setProtocolVersion(String protocolVersion); |
| | | |
| | | /** |
| | | * Retrieves the bind DN for this bind operation. This method should not be |
| | |
| | | * @return The bind DN for this bind operation, or <CODE>null</CODE> if the |
| | | * raw DN has not yet been processed. |
| | | */ |
| | | public final DN getBindDN() |
| | | { |
| | | return bindDN; |
| | | } |
| | | |
| | | |
| | | public abstract DN getBindDN(); |
| | | |
| | | /** |
| | | * Retrieves the simple authentication password for this bind operation. |
| | | * |
| | | * @return The simple authentication password for this bind operation. |
| | | */ |
| | | public final ByteString getSimplePassword() |
| | | { |
| | | return simplePassword; |
| | | } |
| | | |
| | | |
| | | public abstract ByteString getSimplePassword(); |
| | | |
| | | /** |
| | | * Specifies the simple authentication password for this bind operation. |
| | |
| | | * @param simplePassword The simple authentication password for this bind |
| | | * operation. |
| | | */ |
| | | public final void setSimplePassword(ByteString simplePassword) |
| | | { |
| | | if (simplePassword == null) |
| | | { |
| | | this.simplePassword = new ASN1OctetString(); |
| | | } |
| | | else |
| | | { |
| | | this.simplePassword = simplePassword; |
| | | } |
| | | |
| | | authType = AuthenticationType.SIMPLE; |
| | | saslMechanism = null; |
| | | saslCredentials = null; |
| | | } |
| | | |
| | | |
| | | public abstract void setSimplePassword(ByteString simplePassword); |
| | | |
| | | /** |
| | | * Retrieves the SASL mechanism for this bind operation. |
| | |
| | | * @return The SASL mechanism for this bind operation, or <CODE>null</CODE> |
| | | * if the bind does not use SASL authentication. |
| | | */ |
| | | public final String getSASLMechanism() |
| | | { |
| | | return saslMechanism; |
| | | } |
| | | |
| | | |
| | | public abstract String getSASLMechanism(); |
| | | |
| | | /** |
| | | * Retrieves the SASL credentials for this bind operation. |
| | |
| | | * @return The SASL credentials for this bind operation, or <CODE>null</CODE> |
| | | * if there are none or if the bind does not use SASL authentication. |
| | | */ |
| | | public final ASN1OctetString getSASLCredentials() |
| | | { |
| | | return saslCredentials; |
| | | } |
| | | |
| | | |
| | | public abstract ASN1OctetString getSASLCredentials(); |
| | | |
| | | /** |
| | | * Specifies the SASL credentials for this bind operation. |
| | |
| | | * @param saslCredentials The SASL credentials for this bind operation, or |
| | | * <CODE>null</CODE> if there are none. |
| | | */ |
| | | public final void setSASLCredentials(String saslMechanism, |
| | | ASN1OctetString saslCredentials) |
| | | { |
| | | this.saslMechanism = saslMechanism; |
| | | this.saslCredentials = saslCredentials; |
| | | |
| | | authType = AuthenticationType.SASL; |
| | | simplePassword = null; |
| | | } |
| | | |
| | | |
| | | public abstract void setSASLCredentials(String saslMechanism, |
| | | ASN1OctetString saslCredentials); |
| | | |
| | | /** |
| | | * Retrieves the set of server SASL credentials to include in the bind |
| | |
| | | * @return The set of server SASL credentials to include in the bind |
| | | * response, or <CODE>null</CODE> if there are none. |
| | | */ |
| | | public final ASN1OctetString getServerSASLCredentials() |
| | | { |
| | | return serverSASLCredentials; |
| | | } |
| | | |
| | | |
| | | public abstract ASN1OctetString getServerSASLCredentials(); |
| | | |
| | | /** |
| | | * Specifies the set of server SASL credentials to include in the bind |
| | |
| | | * @param serverSASLCredentials The set of server SASL credentials to |
| | | * include in the bind response. |
| | | */ |
| | | public final void setServerSASLCredentials(ASN1OctetString |
| | | serverSASLCredentials) |
| | | { |
| | | this.serverSASLCredentials = serverSASLCredentials; |
| | | } |
| | | |
| | | |
| | | public abstract void setServerSASLCredentials( |
| | | ASN1OctetString serverSASLCredentials); |
| | | |
| | | /** |
| | | * Retrieves the user entry associated with the SASL authentication attempt. |
| | |
| | | * <CODE>null</CODE> if it was not a SASL authentication or the SASL |
| | | * processing was not able to map the request to a user. |
| | | */ |
| | | public final Entry getSASLAuthUserEntry() |
| | | { |
| | | return saslAuthUserEntry; |
| | | } |
| | | |
| | | |
| | | public abstract Entry getSASLAuthUserEntry(); |
| | | |
| | | /** |
| | | * Specifies the user entry associated with the SASL authentication attempt. |
| | |
| | | * @param saslAuthUserEntry The user entry associated with the SASL |
| | | * authentication attempt. |
| | | */ |
| | | public final void setSASLAuthUserEntry(Entry saslAuthUserEntry) |
| | | { |
| | | this.saslAuthUserEntry = saslAuthUserEntry; |
| | | } |
| | | |
| | | |
| | | public abstract void setSASLAuthUserEntry(Entry saslAuthUserEntry); |
| | | |
| | | /** |
| | | * Retrieves a human-readable message providing the reason that the |
| | |
| | | * @return A human-readable message providing the reason that the |
| | | * authentication failed, or <CODE>null</CODE> if none is available. |
| | | */ |
| | | public final String getAuthFailureReason() |
| | | { |
| | | return authFailureReason; |
| | | } |
| | | |
| | | |
| | | public abstract String getAuthFailureReason(); |
| | | |
| | | /** |
| | | * Retrieves the unique identifier for the authentication failure reason, if |
| | |
| | | * @return The unique identifier for the authentication failure reason, or |
| | | * zero if none is available. |
| | | */ |
| | | public final int getAuthFailureID() |
| | | { |
| | | return authFailureID; |
| | | } |
| | | |
| | | |
| | | public abstract int getAuthFailureID(); |
| | | |
| | | /** |
| | | * Specifies the reason that the authentication failed. |
| | |
| | | * @param reason A human-readable message providing the reason that the |
| | | * authentication failed. |
| | | */ |
| | | public final void setAuthFailureReason(int id, String reason) |
| | | { |
| | | if (id < 0) |
| | | { |
| | | authFailureID = 0; |
| | | } |
| | | else |
| | | { |
| | | authFailureID = id; |
| | | } |
| | | |
| | | authFailureReason = reason; |
| | | } |
| | | |
| | | |
| | | public abstract void setAuthFailureReason(int id, String reason); |
| | | |
| | | /** |
| | | * Retrieves the user entry DN for this bind operation. It will only be |
| | |
| | | * the bind processing has not progressed far enough to identify the |
| | | * user or if the user DN could not be determined. |
| | | */ |
| | | public final DN getUserEntryDN() |
| | | { |
| | | return userEntryDN; |
| | | } |
| | | |
| | | |
| | | public abstract DN getUserEntryDN(); |
| | | |
| | | /** |
| | | * Retrieves the authentication info that resulted from processing this bind |
| | |
| | | * @return The authentication info that resulted from processing this bind |
| | | * operation. |
| | | */ |
| | | public final AuthenticationInfo getAuthenticationInfo() |
| | | { |
| | | return authInfo; |
| | | } |
| | | |
| | | |
| | | public abstract AuthenticationInfo getAuthenticationInfo(); |
| | | |
| | | /** |
| | | * Specifies the authentication info that resulted from processing this bind |
| | |
| | | * @param authInfo The authentication info that resulted from processing |
| | | * this bind operation. |
| | | */ |
| | | public final void setAuthenticationInfo(AuthenticationInfo authInfo) |
| | | { |
| | | this.authInfo = authInfo; |
| | | } |
| | | |
| | | |
| | | public abstract void setAuthenticationInfo(AuthenticationInfo authInfo); |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | * Set the user entry DN for this bind operation. |
| | | * |
| | | * @param userEntryDN The user entry DN for this bind operation, or |
| | | * <CODE>null</CODE> if the bind processing has not |
| | | * progressed far enough to identify the user or if |
| | | * the user DN could not be determined. |
| | | */ |
| | | @Override() |
| | | public final long getProcessingStartTime() |
| | | { |
| | | return processingStartTime; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final long getProcessingStopTime() |
| | | { |
| | | return processingStopTime; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final long getProcessingTime() |
| | | { |
| | | return (processingStopTime - processingStartTime); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final OperationType getOperationType() |
| | | { |
| | | // Note that no debugging will be done in this method because it is a likely |
| | | // candidate for being called by the logging subsystem. |
| | | |
| | | return OperationType.BIND; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final void disconnectClient(DisconnectReason disconnectReason, |
| | | boolean sendNotification, String message, |
| | | int messageID) |
| | | { |
| | | // Since bind operations can't be cancelled, we don't need to do anything |
| | | // but forward the request on to the client connection. |
| | | clientConnection.disconnect(disconnectReason, sendNotification, message, |
| | | messageID); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final String[][] getRequestLogElements() |
| | | { |
| | | // Note that no debugging will be done in this method because it is a likely |
| | | // candidate for being called by the logging subsystem. |
| | | |
| | | if (authType == AuthenticationType.SASL) |
| | | { |
| | | return new String[][] |
| | | { |
| | | new String[] { LOG_ELEMENT_BIND_DN, String.valueOf(rawBindDN) }, |
| | | new String[] { LOG_ELEMENT_AUTH_TYPE, authType.toString() }, |
| | | new String[] { LOG_ELEMENT_SASL_MECHANISM, saslMechanism } |
| | | }; |
| | | } |
| | | else |
| | | { |
| | | return new String[][] |
| | | { |
| | | new String[] { LOG_ELEMENT_BIND_DN, String.valueOf(rawBindDN) }, |
| | | new String[] { LOG_ELEMENT_AUTH_TYPE, authType.toString() } |
| | | }; |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final String[][] getResponseLogElements() |
| | | { |
| | | // Note that no debugging will be done in this method because it is a likely |
| | | // candidate for being called by the logging subsystem. |
| | | |
| | | String resultCode = String.valueOf(getResultCode().getIntValue()); |
| | | |
| | | String errorMessage; |
| | | StringBuilder errorMessageBuffer = getErrorMessage(); |
| | | if (errorMessageBuffer == null) |
| | | { |
| | | errorMessage = null; |
| | | } |
| | | else |
| | | { |
| | | errorMessage = errorMessageBuffer.toString(); |
| | | } |
| | | |
| | | String matchedDNStr; |
| | | DN matchedDN = getMatchedDN(); |
| | | if (matchedDN == null) |
| | | { |
| | | matchedDNStr = null; |
| | | } |
| | | else |
| | | { |
| | | matchedDNStr = matchedDN.toString(); |
| | | } |
| | | |
| | | String referrals; |
| | | List<String> referralURLs = getReferralURLs(); |
| | | if ((referralURLs == null) || referralURLs.isEmpty()) |
| | | { |
| | | referrals = null; |
| | | } |
| | | else |
| | | { |
| | | StringBuilder buffer = new StringBuilder(); |
| | | Iterator<String> iterator = referralURLs.iterator(); |
| | | buffer.append(iterator.next()); |
| | | |
| | | while (iterator.hasNext()) |
| | | { |
| | | buffer.append(", "); |
| | | buffer.append(iterator.next()); |
| | | } |
| | | |
| | | referrals = buffer.toString(); |
| | | } |
| | | |
| | | String processingTime = |
| | | String.valueOf(processingStopTime - processingStartTime); |
| | | |
| | | return new String[][] |
| | | { |
| | | new String[] { LOG_ELEMENT_RESULT_CODE, resultCode }, |
| | | new String[] { LOG_ELEMENT_ERROR_MESSAGE, errorMessage }, |
| | | new String[] { LOG_ELEMENT_MATCHED_DN, matchedDNStr }, |
| | | new String[] { LOG_ELEMENT_REFERRAL_URLS, referrals }, |
| | | new String[] { LOG_ELEMENT_PROCESSING_TIME, processingTime } |
| | | }; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final List<Control> getResponseControls() |
| | | { |
| | | return responseControls; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final void addResponseControl(Control control) |
| | | { |
| | | responseControls.add(control); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final void removeResponseControl(Control control) |
| | | { |
| | | responseControls.remove(control); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final void run() |
| | | { |
| | | // Start the processing timer and initially set the result to indicate that |
| | | // the result is unknown. |
| | | processingStartTime = System.currentTimeMillis(); |
| | | setResultCode(ResultCode.UNDEFINED); |
| | | boolean returnAuthzID = false; |
| | | int sizeLimit = DirectoryServer.getSizeLimit(); |
| | | int timeLimit = DirectoryServer.getTimeLimit(); |
| | | int lookthroughLimit = DirectoryServer.getLookthroughLimit(); |
| | | |
| | | |
| | | // Set a flag to indicate that a bind operation is in progress. This should |
| | | // ensure that no new operations will be accepted for this client until the |
| | | // bind is complete. |
| | | clientConnection.setBindInProgress(true); |
| | | |
| | | |
| | | // Wipe out any existing authentication for the client connection and create |
| | | // a placeholder that will be used if the bind is successful. |
| | | clientConnection.setUnauthenticated(); |
| | | authInfo = null; |
| | | |
| | | |
| | | // Abandon any operations that may be in progress for the client. |
| | | String cancelReason = getMessage(MSGID_CANCELED_BY_BIND_REQUEST); |
| | | CancelRequest cancelRequest = new CancelRequest(true, cancelReason); |
| | | clientConnection.cancelAllOperationsExcept(cancelRequest, getMessageID()); |
| | | |
| | | |
| | | // Get the plugin config manager that will be used for invoking plugins. |
| | | PluginConfigManager pluginConfigManager = |
| | | DirectoryServer.getPluginConfigManager(); |
| | | boolean skipPostOperation = false; |
| | | |
| | | |
| | | // Create a labeled block of code that we can break out of if a problem is |
| | | // detected. |
| | | bindProcessing: |
| | | { |
| | | // Invoke the pre-parse bind plugins. |
| | | PreParsePluginResult preParseResult = |
| | | pluginConfigManager.invokePreParseBindPlugins(this); |
| | | if (preParseResult.connectionTerminated()) |
| | | { |
| | | // There's no point in continuing with anything. Log the request and |
| | | // result and return. |
| | | setResultCode(ResultCode.CANCELED); |
| | | |
| | | int msgID = MSGID_CANCELED_BY_PREPARSE_DISCONNECT; |
| | | appendErrorMessage(getMessage(msgID)); |
| | | |
| | | processingStopTime = System.currentTimeMillis(); |
| | | |
| | | logBindRequest(this); |
| | | logBindResponse(this); |
| | | pluginConfigManager.invokePostResponseBindPlugins(this); |
| | | return; |
| | | } |
| | | else if (preParseResult.sendResponseImmediately()) |
| | | { |
| | | skipPostOperation = true; |
| | | logBindRequest(this); |
| | | break bindProcessing; |
| | | } |
| | | else if (preParseResult.skipCoreProcessing()) |
| | | { |
| | | skipPostOperation = false; |
| | | break bindProcessing; |
| | | } |
| | | |
| | | |
| | | // Log the bind request message. |
| | | logBindRequest(this); |
| | | |
| | | |
| | | // Process the bind DN to convert it from the raw form as provided by the |
| | | // client into the form required for the rest of the bind processing. |
| | | try |
| | | { |
| | | if (bindDN == null) |
| | | { |
| | | bindDN = DN.decode(rawBindDN); |
| | | } |
| | | } |
| | | catch (DirectoryException de) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, de); |
| | | } |
| | | |
| | | setResultCode(ResultCode.INVALID_CREDENTIALS); |
| | | setAuthFailureReason(de.getMessageID(), de.getErrorMessage()); |
| | | break bindProcessing; |
| | | } |
| | | |
| | | // Check to see if the client has permission to perform the |
| | | // bind. |
| | | |
| | | // FIXME: for now assume that this will check all permission |
| | | // pertinent to the operation. This includes any controls |
| | | // specified. |
| | | if (AccessControlConfigManager.getInstance() |
| | | .getAccessControlHandler().isAllowed(this) == false) { |
| | | setResultCode(ResultCode.INVALID_CREDENTIALS); |
| | | |
| | | int msgID = MSGID_BIND_AUTHZ_INSUFFICIENT_ACCESS_RIGHTS; |
| | | String message = getMessage(msgID, String.valueOf(bindDN)); |
| | | setAuthFailureReason(msgID, message); |
| | | |
| | | skipPostOperation = true; |
| | | break bindProcessing; |
| | | } |
| | | |
| | | // Check to see if there are any controls in the request. If so, then see |
| | | // if there is any special processing required. |
| | | List<Control> requestControls = 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_AUTHZID_REQUEST)) |
| | | { |
| | | returnAuthzID = true; |
| | | } |
| | | else if (oid.equals(OID_PASSWORD_POLICY_CONTROL)) |
| | | { |
| | | pwPolicyControlRequested = true; |
| | | } |
| | | |
| | | // NYI -- Add support for additional controls. |
| | | else if (c.isCritical()) |
| | | { |
| | | setResultCode(ResultCode.UNAVAILABLE_CRITICAL_EXTENSION); |
| | | |
| | | int msgID = MSGID_BIND_UNSUPPORTED_CRITICAL_CONTROL; |
| | | appendErrorMessage(getMessage(msgID, String.valueOf(oid))); |
| | | |
| | | break bindProcessing; |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | // Check to see if this is a simple bind or a SASL bind and process |
| | | // accordingly. |
| | | switch (authType) |
| | | { |
| | | case SIMPLE: |
| | | // See if this is an anonymous bind. If so, then determine whether |
| | | // to allow it. |
| | | if ((simplePassword == null) || (simplePassword.value().length == 0)) |
| | | { |
| | | // If the server is in lockdown mode, then fail. |
| | | if (DirectoryServer.lockdownMode()) |
| | | { |
| | | setResultCode(ResultCode.INVALID_CREDENTIALS); |
| | | |
| | | int msgID = MSGID_BIND_REJECTED_LOCKDOWN_MODE; |
| | | setAuthFailureReason(msgID, getMessage(msgID)); |
| | | |
| | | processingStopTime = System.currentTimeMillis(); |
| | | logBindResponse(this); |
| | | break bindProcessing; |
| | | } |
| | | |
| | | // If there is a bind DN, then see whether that is acceptable. |
| | | if (DirectoryServer.bindWithDNRequiresPassword() && |
| | | ((bindDN != null) && (! bindDN.isNullDN()))) |
| | | { |
| | | setResultCode(ResultCode.UNWILLING_TO_PERFORM); |
| | | |
| | | int msgID = MSGID_BIND_DN_BUT_NO_PASSWORD; |
| | | String message = getMessage(msgID); |
| | | setAuthFailureReason(msgID, message); |
| | | break bindProcessing; |
| | | } |
| | | |
| | | |
| | | // Invoke the pre-operation bind plugins. |
| | | PreOperationPluginResult preOpResult = |
| | | pluginConfigManager.invokePreOperationBindPlugins(this); |
| | | if (preOpResult.connectionTerminated()) |
| | | { |
| | | // There's no point in continuing with anything. Log the result |
| | | // and return. |
| | | setResultCode(ResultCode.CANCELED); |
| | | |
| | | int msgID = MSGID_CANCELED_BY_PREOP_DISCONNECT; |
| | | appendErrorMessage(getMessage(msgID)); |
| | | |
| | | processingStopTime = System.currentTimeMillis(); |
| | | |
| | | logBindResponse(this); |
| | | pluginConfigManager.invokePostResponseBindPlugins(this); |
| | | return; |
| | | } |
| | | else if (preOpResult.sendResponseImmediately()) |
| | | { |
| | | skipPostOperation = true; |
| | | break bindProcessing; |
| | | } |
| | | else if (preOpResult.skipCoreProcessing()) |
| | | { |
| | | skipPostOperation = false; |
| | | break bindProcessing; |
| | | } |
| | | |
| | | setResultCode(ResultCode.SUCCESS); |
| | | authInfo = new AuthenticationInfo(); |
| | | break bindProcessing; |
| | | } |
| | | |
| | | |
| | | // See if the bind DN is actually one of the alternate root DNs |
| | | // defined in the server. If so, then replace it with the actual DN |
| | | // for that user. |
| | | DN actualRootDN = DirectoryServer.getActualRootBindDN(bindDN); |
| | | if (actualRootDN != null) |
| | | { |
| | | bindDN = actualRootDN; |
| | | } |
| | | |
| | | |
| | | // Get the user entry based on the bind DN. If it does not exist, |
| | | // then fail. |
| | | Lock userLock = null; |
| | | for (int i=0; i < 3; i++) |
| | | { |
| | | userLock = LockManager.lockRead(bindDN); |
| | | if (userLock != null) |
| | | { |
| | | break; |
| | | } |
| | | } |
| | | |
| | | if (userLock == null) |
| | | { |
| | | int msgID = MSGID_BIND_OPERATION_CANNOT_LOCK_USER; |
| | | String message = getMessage(msgID, String.valueOf(bindDN)); |
| | | |
| | | setResultCode(DirectoryServer.getServerErrorResultCode()); |
| | | setAuthFailureReason(msgID, message); |
| | | break bindProcessing; |
| | | } |
| | | |
| | | try |
| | | { |
| | | Entry userEntry; |
| | | try |
| | | { |
| | | userEntry = DirectoryServer.getEntry(bindDN); |
| | | } |
| | | catch (DirectoryException de) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, de); |
| | | } |
| | | |
| | | setResultCode(ResultCode.INVALID_CREDENTIALS); |
| | | setAuthFailureReason(de.getMessageID(), |
| | | de.getErrorMessage()); |
| | | |
| | | userEntry = null; |
| | | break bindProcessing; |
| | | } |
| | | |
| | | if (userEntry == null) |
| | | { |
| | | |
| | | int msgID = MSGID_BIND_OPERATION_UNKNOWN_USER; |
| | | String message = getMessage(msgID, String.valueOf(bindDN)); |
| | | |
| | | setResultCode(ResultCode.INVALID_CREDENTIALS); |
| | | setAuthFailureReason(msgID, message); |
| | | break bindProcessing; |
| | | } |
| | | else |
| | | { |
| | | userEntryDN = userEntry.getDN(); |
| | | } |
| | | |
| | | |
| | | // Check to see if the user has a password. If not, then fail. |
| | | // FIXME -- We need to have a way to enable/disable debugging. |
| | | pwPolicyState = new PasswordPolicyState(userEntry, false, false); |
| | | AttributeType pwType |
| | | = pwPolicyState.getPolicy().getPasswordAttribute(); |
| | | |
| | | List<Attribute> pwAttr = userEntry.getAttribute(pwType); |
| | | if ((pwAttr == null) || (pwAttr.isEmpty())) |
| | | { |
| | | int msgID = MSGID_BIND_OPERATION_NO_PASSWORD; |
| | | String message = getMessage(msgID, String.valueOf(bindDN)); |
| | | |
| | | setResultCode(ResultCode.INVALID_CREDENTIALS); |
| | | setAuthFailureReason(msgID, message); |
| | | break bindProcessing; |
| | | } |
| | | |
| | | |
| | | // Check to see if the authentication must be done in a secure |
| | | // manner. If so, then the client connection must be secure. |
| | | if (pwPolicyState.getPolicy().requireSecureAuthentication() && |
| | | (! clientConnection.isSecure())) |
| | | { |
| | | int msgID = MSGID_BIND_OPERATION_INSECURE_SIMPLE_BIND; |
| | | String message = getMessage(msgID, String.valueOf(bindDN)); |
| | | |
| | | setResultCode(ResultCode.INVALID_CREDENTIALS); |
| | | setAuthFailureReason(msgID, message); |
| | | break bindProcessing; |
| | | } |
| | | |
| | | |
| | | // Check to see if the user is administratively disabled or locked. |
| | | if (pwPolicyState.isDisabled()) |
| | | { |
| | | int msgID = MSGID_BIND_OPERATION_ACCOUNT_DISABLED; |
| | | String message = getMessage(msgID, String.valueOf(bindDN)); |
| | | |
| | | setResultCode(ResultCode.INVALID_CREDENTIALS); |
| | | setAuthFailureReason(msgID, message); |
| | | break bindProcessing; |
| | | } |
| | | else if (pwPolicyState.isAccountExpired()) |
| | | { |
| | | int msgID = MSGID_BIND_OPERATION_ACCOUNT_EXPIRED; |
| | | String message = getMessage(msgID, String.valueOf(bindDN)); |
| | | |
| | | setResultCode(ResultCode.INVALID_CREDENTIALS); |
| | | setAuthFailureReason(msgID, message); |
| | | |
| | | pwPolicyState.generateAccountStatusNotification( |
| | | AccountStatusNotificationType.ACCOUNT_EXPIRED, bindDN, msgID, |
| | | message); |
| | | |
| | | break bindProcessing; |
| | | } |
| | | else if (pwPolicyState.lockedDueToFailures()) |
| | | { |
| | | int msgID = MSGID_BIND_OPERATION_ACCOUNT_FAILURE_LOCKED; |
| | | String message = getMessage(msgID, String.valueOf(bindDN)); |
| | | |
| | | if (pwPolicyErrorType == null) |
| | | { |
| | | pwPolicyErrorType = PasswordPolicyErrorType.ACCOUNT_LOCKED; |
| | | } |
| | | |
| | | setResultCode(ResultCode.INVALID_CREDENTIALS); |
| | | setAuthFailureReason(msgID, message); |
| | | break bindProcessing; |
| | | } |
| | | else if (pwPolicyState.lockedDueToMaximumResetAge()) |
| | | { |
| | | int msgID = MSGID_BIND_OPERATION_ACCOUNT_RESET_LOCKED; |
| | | String message = getMessage(msgID, String.valueOf(bindDN)); |
| | | |
| | | if (pwPolicyErrorType == null) |
| | | { |
| | | pwPolicyErrorType = PasswordPolicyErrorType.ACCOUNT_LOCKED; |
| | | } |
| | | |
| | | setResultCode(ResultCode.INVALID_CREDENTIALS); |
| | | setAuthFailureReason(msgID, message); |
| | | |
| | | pwPolicyState.generateAccountStatusNotification( |
| | | AccountStatusNotificationType.ACCOUNT_RESET_LOCKED, bindDN, |
| | | msgID, message); |
| | | |
| | | break bindProcessing; |
| | | } |
| | | else if (pwPolicyState.lockedDueToIdleInterval()) |
| | | { |
| | | int msgID = MSGID_BIND_OPERATION_ACCOUNT_IDLE_LOCKED; |
| | | String message = getMessage(msgID, String.valueOf(bindDN)); |
| | | |
| | | if (pwPolicyErrorType == null) |
| | | { |
| | | pwPolicyErrorType = PasswordPolicyErrorType.ACCOUNT_LOCKED; |
| | | } |
| | | |
| | | setResultCode(ResultCode.INVALID_CREDENTIALS); |
| | | setAuthFailureReason(msgID, message); |
| | | |
| | | pwPolicyState.generateAccountStatusNotification( |
| | | AccountStatusNotificationType.ACCOUNT_IDLE_LOCKED, bindDN, |
| | | msgID, message); |
| | | |
| | | break bindProcessing; |
| | | } |
| | | |
| | | |
| | | // Determine whether the password is expired, or whether the user |
| | | // should be warned about an upcoming expiration. |
| | | if (pwPolicyState.isPasswordExpired()) |
| | | { |
| | | if (pwPolicyErrorType == null) |
| | | { |
| | | pwPolicyErrorType = PasswordPolicyErrorType.PASSWORD_EXPIRED; |
| | | } |
| | | |
| | | int maxGraceLogins |
| | | = pwPolicyState.getPolicy().getGraceLoginCount(); |
| | | if ((maxGraceLogins > 0) && pwPolicyState.mayUseGraceLogin()) |
| | | { |
| | | List<Long> graceLoginTimes = pwPolicyState.getGraceLoginTimes(); |
| | | if ((graceLoginTimes == null) || |
| | | (graceLoginTimes.size() < maxGraceLogins)) |
| | | { |
| | | isGraceLogin = true; |
| | | mustChangePassword = true; |
| | | |
| | | if (pwPolicyWarningType == null) |
| | | { |
| | | pwPolicyWarningType = |
| | | PasswordPolicyWarningType.GRACE_LOGINS_REMAINING; |
| | | pwPolicyWarningValue = maxGraceLogins - |
| | | (graceLoginTimes.size() + 1); |
| | | } |
| | | } |
| | | else |
| | | { |
| | | int msgID = MSGID_BIND_OPERATION_PASSWORD_EXPIRED; |
| | | String message = getMessage(msgID, String.valueOf(bindDN)); |
| | | |
| | | setResultCode(ResultCode.INVALID_CREDENTIALS); |
| | | setAuthFailureReason(msgID, message); |
| | | |
| | | pwPolicyState.generateAccountStatusNotification( |
| | | AccountStatusNotificationType.PASSWORD_EXPIRED, bindDN, |
| | | msgID, message); |
| | | |
| | | break bindProcessing; |
| | | } |
| | | } |
| | | else |
| | | { |
| | | int msgID = MSGID_BIND_OPERATION_PASSWORD_EXPIRED; |
| | | String message = getMessage(msgID, String.valueOf(bindDN)); |
| | | |
| | | setResultCode(ResultCode.INVALID_CREDENTIALS); |
| | | setAuthFailureReason(msgID, message); |
| | | |
| | | pwPolicyState.generateAccountStatusNotification( |
| | | AccountStatusNotificationType.PASSWORD_EXPIRED, bindDN, |
| | | msgID, message); |
| | | |
| | | break bindProcessing; |
| | | } |
| | | } |
| | | else if (pwPolicyState.shouldWarn()) |
| | | { |
| | | int numSeconds = pwPolicyState.getSecondsUntilExpiration(); |
| | | String timeToExpiration = secondsToTimeString(numSeconds); |
| | | |
| | | int msgID = MSGID_BIND_PASSWORD_EXPIRING; |
| | | String message = getMessage(msgID, timeToExpiration); |
| | | appendErrorMessage(message); |
| | | |
| | | if (pwPolicyWarningType == null) |
| | | { |
| | | pwPolicyWarningType = |
| | | PasswordPolicyWarningType.TIME_BEFORE_EXPIRATION; |
| | | pwPolicyWarningValue = numSeconds; |
| | | } |
| | | |
| | | isFirstWarning = pwPolicyState.isFirstWarning(); |
| | | } |
| | | |
| | | |
| | | // Check to see if the user's password has been reset. |
| | | if (pwPolicyState.mustChangePassword()) |
| | | { |
| | | mustChangePassword = true; |
| | | |
| | | if (pwPolicyErrorType == null) |
| | | { |
| | | pwPolicyErrorType = PasswordPolicyErrorType.CHANGE_AFTER_RESET; |
| | | } |
| | | } |
| | | |
| | | |
| | | // Invoke the pre-operation bind plugins. |
| | | PreOperationPluginResult preOpResult = |
| | | pluginConfigManager.invokePreOperationBindPlugins(this); |
| | | if (preOpResult.connectionTerminated()) |
| | | { |
| | | // There's no point in continuing with anything. Log the result |
| | | // and return. |
| | | setResultCode(ResultCode.CANCELED); |
| | | |
| | | int msgID = MSGID_CANCELED_BY_PREOP_DISCONNECT; |
| | | appendErrorMessage(getMessage(msgID)); |
| | | |
| | | processingStopTime = System.currentTimeMillis(); |
| | | |
| | | logBindResponse(this); |
| | | pluginConfigManager.invokePostResponseBindPlugins(this); |
| | | return; |
| | | } |
| | | else if (preOpResult.sendResponseImmediately()) |
| | | { |
| | | skipPostOperation = true; |
| | | break bindProcessing; |
| | | } |
| | | else if (preOpResult.skipCoreProcessing()) |
| | | { |
| | | skipPostOperation = false; |
| | | break bindProcessing; |
| | | } |
| | | |
| | | |
| | | // Determine whether the provided password matches any of the stored |
| | | // passwords for the user. |
| | | if (pwPolicyState.passwordMatches(simplePassword)) |
| | | { |
| | | setResultCode(ResultCode.SUCCESS); |
| | | |
| | | boolean isRoot = DirectoryServer.isRootDN(userEntry.getDN()); |
| | | if (DirectoryServer.lockdownMode() && (! isRoot)) |
| | | { |
| | | setResultCode(ResultCode.INVALID_CREDENTIALS); |
| | | |
| | | int msgID = MSGID_BIND_REJECTED_LOCKDOWN_MODE; |
| | | setAuthFailureReason(msgID, getMessage(msgID)); |
| | | |
| | | break bindProcessing; |
| | | } |
| | | |
| | | authInfo = new AuthenticationInfo(userEntry, simplePassword, |
| | | isRoot); |
| | | |
| | | |
| | | // See if the user's entry contains a custom size limit. |
| | | AttributeType attrType = |
| | | DirectoryServer.getAttributeType(OP_ATTR_USER_SIZE_LIMIT, |
| | | true); |
| | | List<Attribute> attrList = userEntry.getAttribute(attrType); |
| | | if ((attrList != null) && (attrList.size() == 1)) |
| | | { |
| | | Attribute a = attrList.get(0); |
| | | LinkedHashSet<AttributeValue> values = a.getValues(); |
| | | Iterator<AttributeValue> iterator = values.iterator(); |
| | | if (iterator.hasNext()) |
| | | { |
| | | AttributeValue v = iterator.next(); |
| | | if (iterator.hasNext()) |
| | | { |
| | | int msgID = MSGID_BIND_MULTIPLE_USER_SIZE_LIMITS; |
| | | String message = |
| | | getMessage(msgID, String.valueOf(userEntry.getDN())); |
| | | logError(ErrorLogCategory.CORE_SERVER, |
| | | ErrorLogSeverity.SEVERE_WARNING, message, msgID); |
| | | } |
| | | else |
| | | { |
| | | try |
| | | { |
| | | sizeLimit = Integer.parseInt(v.getStringValue()); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | |
| | | int msgID = MSGID_BIND_CANNOT_PROCESS_USER_SIZE_LIMIT; |
| | | String message = |
| | | getMessage(msgID, v.getStringValue(), |
| | | String.valueOf(userEntry.getDN())); |
| | | logError(ErrorLogCategory.CORE_SERVER, |
| | | ErrorLogSeverity.SEVERE_WARNING, message, msgID); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | // See if the user's entry contains a custom time limit. |
| | | attrType = |
| | | DirectoryServer.getAttributeType(OP_ATTR_USER_TIME_LIMIT, |
| | | true); |
| | | attrList = userEntry.getAttribute(attrType); |
| | | if ((attrList != null) && (attrList.size() == 1)) |
| | | { |
| | | Attribute a = attrList.get(0); |
| | | LinkedHashSet<AttributeValue> values = a.getValues(); |
| | | Iterator<AttributeValue> iterator = values.iterator(); |
| | | if (iterator.hasNext()) |
| | | { |
| | | AttributeValue v = iterator.next(); |
| | | if (iterator.hasNext()) |
| | | { |
| | | int msgID = MSGID_BIND_MULTIPLE_USER_TIME_LIMITS; |
| | | String message = |
| | | getMessage(msgID, String.valueOf(userEntry.getDN())); |
| | | logError(ErrorLogCategory.CORE_SERVER, |
| | | ErrorLogSeverity.SEVERE_WARNING, message, msgID); |
| | | } |
| | | else |
| | | { |
| | | try |
| | | { |
| | | timeLimit = Integer.parseInt(v.getStringValue()); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | |
| | | int msgID = MSGID_BIND_CANNOT_PROCESS_USER_TIME_LIMIT; |
| | | String message = |
| | | getMessage(msgID, v.getStringValue(), |
| | | String.valueOf(userEntry.getDN())); |
| | | logError(ErrorLogCategory.CORE_SERVER, |
| | | ErrorLogSeverity.SEVERE_WARNING, message, msgID); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | // See if the user's entry contains a custom lookthrough limit. |
| | | attrType = |
| | | DirectoryServer.getAttributeType( |
| | | OP_ATTR_USER_LOOKTHROUGH_LIMIT, true); |
| | | attrList = userEntry.getAttribute(attrType); |
| | | if ((attrList != null) && (attrList.size() == 1)) |
| | | { |
| | | Attribute a = attrList.get(0); |
| | | LinkedHashSet<AttributeValue> values = a.getValues(); |
| | | Iterator<AttributeValue> iterator = values.iterator(); |
| | | if (iterator.hasNext()) |
| | | { |
| | | AttributeValue v = iterator.next(); |
| | | if (iterator.hasNext()) |
| | | { |
| | | int msgID = MSGID_BIND_MULTIPLE_USER_LOOKTHROUGH_LIMITS; |
| | | String message = |
| | | getMessage(msgID, String.valueOf(userEntry.getDN())); |
| | | logError(ErrorLogCategory.CORE_SERVER, |
| | | ErrorLogSeverity.SEVERE_WARNING, message, msgID); |
| | | } |
| | | else |
| | | { |
| | | try |
| | | { |
| | | lookthroughLimit = Integer.parseInt(v.getStringValue()); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | |
| | | int msgID = |
| | | MSGID_BIND_CANNOT_PROCESS_USER_LOOKTHROUGH_LIMIT; |
| | | String message = |
| | | getMessage(msgID, v.getStringValue(), |
| | | String.valueOf(userEntry.getDN())); |
| | | logError(ErrorLogCategory.CORE_SERVER, |
| | | ErrorLogSeverity.SEVERE_WARNING, message, msgID); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | pwPolicyState.handleDeprecatedStorageSchemes(simplePassword); |
| | | pwPolicyState.clearFailureLockout(); |
| | | |
| | | if (isFirstWarning) |
| | | { |
| | | pwPolicyState.setWarnedTime(); |
| | | |
| | | int numSeconds = pwPolicyState.getSecondsUntilExpiration(); |
| | | String timeToExpiration = secondsToTimeString(numSeconds); |
| | | |
| | | int msgID = MSGID_BIND_PASSWORD_EXPIRING; |
| | | String message = getMessage(msgID, timeToExpiration); |
| | | |
| | | pwPolicyState.generateAccountStatusNotification( |
| | | AccountStatusNotificationType.PASSWORD_EXPIRING, bindDN, |
| | | msgID, message); |
| | | } |
| | | |
| | | if (isGraceLogin) |
| | | { |
| | | pwPolicyState.updateGraceLoginTimes(); |
| | | } |
| | | |
| | | pwPolicyState.setLastLoginTime(); |
| | | } |
| | | else |
| | | { |
| | | int msgID = MSGID_BIND_OPERATION_WRONG_PASSWORD; |
| | | String message = getMessage(msgID); |
| | | |
| | | setResultCode(ResultCode.INVALID_CREDENTIALS); |
| | | setAuthFailureReason(msgID, message); |
| | | |
| | | if (pwPolicyState.getPolicy().getLockoutFailureCount() > 0) |
| | | { |
| | | pwPolicyState.updateAuthFailureTimes(); |
| | | if (pwPolicyState.lockedDueToFailures()) |
| | | { |
| | | AccountStatusNotificationType notificationType; |
| | | |
| | | int lockoutDuration = pwPolicyState.getSecondsUntilUnlock(); |
| | | if (lockoutDuration > -1) |
| | | { |
| | | notificationType = AccountStatusNotificationType. |
| | | ACCOUNT_TEMPORARILY_LOCKED; |
| | | msgID = MSGID_BIND_ACCOUNT_TEMPORARILY_LOCKED; |
| | | message = getMessage(msgID, |
| | | secondsToTimeString(lockoutDuration)); |
| | | } |
| | | else |
| | | { |
| | | notificationType = AccountStatusNotificationType. |
| | | ACCOUNT_PERMANENTLY_LOCKED; |
| | | msgID = MSGID_BIND_ACCOUNT_PERMANENTLY_LOCKED; |
| | | message = getMessage(msgID); |
| | | } |
| | | |
| | | pwPolicyState.generateAccountStatusNotification( |
| | | notificationType, userEntryDN, msgID, message); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | |
| | | int msgID = MSGID_BIND_OPERATION_PASSWORD_VALIDATION_EXCEPTION; |
| | | String message = getMessage(msgID, getExceptionMessage(e)); |
| | | |
| | | setResultCode(DirectoryServer.getServerErrorResultCode()); |
| | | setAuthFailureReason(msgID, message); |
| | | break bindProcessing; |
| | | } |
| | | finally |
| | | { |
| | | // No matter what, make sure to unlock the user's entry. |
| | | LockManager.unlock(bindDN, userLock); |
| | | } |
| | | |
| | | break; |
| | | |
| | | |
| | | case SASL: |
| | | // Get the appropriate authentication handler for this request based |
| | | // on the SASL mechanism. If there is none, then fail. |
| | | SASLMechanismHandler saslHandler = |
| | | DirectoryServer.getSASLMechanismHandler(saslMechanism); |
| | | if (saslHandler == null) |
| | | { |
| | | setResultCode(ResultCode.AUTH_METHOD_NOT_SUPPORTED); |
| | | |
| | | int msgID = MSGID_BIND_OPERATION_UNKNOWN_SASL_MECHANISM; |
| | | String message = getMessage(msgID, saslMechanism); |
| | | |
| | | appendErrorMessage(message); |
| | | setAuthFailureReason(msgID, message); |
| | | break bindProcessing; |
| | | } |
| | | |
| | | |
| | | // Check to see if the client has sufficient permission to perform the |
| | | // bind. |
| | | // NYI |
| | | |
| | | |
| | | // Invoke the pre-operation bind plugins. |
| | | PreOperationPluginResult preOpResult = |
| | | pluginConfigManager.invokePreOperationBindPlugins(this); |
| | | if (preOpResult.connectionTerminated()) |
| | | { |
| | | // There's no point in continuing with anything. Log the result |
| | | // and return. |
| | | setResultCode(ResultCode.CANCELED); |
| | | |
| | | int msgID = MSGID_CANCELED_BY_PREOP_DISCONNECT; |
| | | appendErrorMessage(getMessage(msgID)); |
| | | |
| | | processingStopTime = System.currentTimeMillis(); |
| | | |
| | | logBindResponse(this); |
| | | pluginConfigManager.invokePostResponseBindPlugins(this); |
| | | return; |
| | | } |
| | | else if (preOpResult.sendResponseImmediately()) |
| | | { |
| | | skipPostOperation = true; |
| | | break bindProcessing; |
| | | } |
| | | else if (preOpResult.skipCoreProcessing()) |
| | | { |
| | | skipPostOperation = false; |
| | | break bindProcessing; |
| | | } |
| | | |
| | | |
| | | // Actually process the SASL bind. |
| | | saslHandler.processSASLBind(this); |
| | | |
| | | |
| | | // If the server is operating in lockdown mode, then we will need to |
| | | // ensure that the authentication was successful and performed as a |
| | | // root user to continue. |
| | | if (DirectoryServer.lockdownMode()) |
| | | { |
| | | ResultCode resultCode = getResultCode(); |
| | | if (resultCode != ResultCode.SASL_BIND_IN_PROGRESS) |
| | | { |
| | | if ((resultCode != ResultCode.SUCCESS) || |
| | | (saslAuthUserEntry == null) || |
| | | (! DirectoryServer.isRootDN(saslAuthUserEntry.getDN()))) |
| | | { |
| | | setResultCode(ResultCode.INVALID_CREDENTIALS); |
| | | |
| | | int msgID = MSGID_BIND_REJECTED_LOCKDOWN_MODE; |
| | | setAuthFailureReason(msgID, getMessage(msgID)); |
| | | |
| | | break bindProcessing; |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | // Create the password policy state object. |
| | | String userDNString; |
| | | if (saslAuthUserEntry == null) |
| | | { |
| | | pwPolicyState = null; |
| | | userDNString = null; |
| | | } |
| | | else |
| | | { |
| | | try |
| | | { |
| | | // FIXME -- Need to have a way to enable debugging. |
| | | pwPolicyState = new PasswordPolicyState(saslAuthUserEntry, false, |
| | | false); |
| | | userEntryDN = saslAuthUserEntry.getDN(); |
| | | userDNString = String.valueOf(userEntryDN); |
| | | } |
| | | catch (DirectoryException de) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, de); |
| | | } |
| | | |
| | | setResponseData(de); |
| | | break bindProcessing; |
| | | } |
| | | } |
| | | |
| | | |
| | | // Perform password policy checks that will need to be completed |
| | | // regardless of whether the authentication was successful. |
| | | if (pwPolicyState != null) |
| | | { |
| | | if (pwPolicyState.isDisabled()) |
| | | { |
| | | setResultCode(ResultCode.INVALID_CREDENTIALS); |
| | | |
| | | int msgID = MSGID_BIND_OPERATION_ACCOUNT_DISABLED; |
| | | String message = getMessage(msgID, userDNString); |
| | | setAuthFailureReason(msgID, message); |
| | | break bindProcessing; |
| | | } |
| | | else if (pwPolicyState.isAccountExpired()) |
| | | { |
| | | setResultCode(ResultCode.INVALID_CREDENTIALS); |
| | | |
| | | int msgID = MSGID_BIND_OPERATION_ACCOUNT_EXPIRED; |
| | | String message = getMessage(msgID, userDNString); |
| | | setAuthFailureReason(msgID, message); |
| | | |
| | | pwPolicyState.generateAccountStatusNotification( |
| | | AccountStatusNotificationType.ACCOUNT_EXPIRED, bindDN, msgID, |
| | | message); |
| | | |
| | | break bindProcessing; |
| | | } |
| | | |
| | | if (pwPolicyState.getPolicy().requireSecureAuthentication() && |
| | | (! clientConnection.isSecure()) && |
| | | (! saslHandler.isSecure(saslMechanism))) |
| | | { |
| | | setResultCode(ResultCode.INVALID_CREDENTIALS); |
| | | |
| | | int msgID = MSGID_BIND_OPERATION_INSECURE_SASL_BIND; |
| | | String message = getMessage(msgID, saslMechanism, userDNString); |
| | | setAuthFailureReason(msgID, message); |
| | | break bindProcessing; |
| | | } |
| | | |
| | | if (pwPolicyState.lockedDueToFailures()) |
| | | { |
| | | setResultCode(ResultCode.INVALID_CREDENTIALS); |
| | | |
| | | if (pwPolicyErrorType == null) |
| | | { |
| | | pwPolicyErrorType = PasswordPolicyErrorType.ACCOUNT_LOCKED; |
| | | } |
| | | |
| | | int msgID = MSGID_BIND_OPERATION_ACCOUNT_FAILURE_LOCKED; |
| | | String message = getMessage(msgID, userDNString); |
| | | setAuthFailureReason(msgID, message); |
| | | break bindProcessing; |
| | | } |
| | | |
| | | if (pwPolicyState.lockedDueToIdleInterval()) |
| | | { |
| | | setResultCode(ResultCode.INVALID_CREDENTIALS); |
| | | |
| | | if (pwPolicyErrorType == null) |
| | | { |
| | | pwPolicyErrorType = PasswordPolicyErrorType.ACCOUNT_LOCKED; |
| | | } |
| | | |
| | | int msgID = MSGID_BIND_OPERATION_ACCOUNT_IDLE_LOCKED; |
| | | String message = getMessage(msgID, userDNString); |
| | | setAuthFailureReason(msgID, message); |
| | | |
| | | pwPolicyState.generateAccountStatusNotification( |
| | | AccountStatusNotificationType.ACCOUNT_IDLE_LOCKED, bindDN, |
| | | msgID, message); |
| | | |
| | | break bindProcessing; |
| | | } |
| | | |
| | | |
| | | if (saslHandler.isPasswordBased(saslMechanism)) |
| | | { |
| | | if (pwPolicyState.lockedDueToMaximumResetAge()) |
| | | { |
| | | setResultCode(ResultCode.INVALID_CREDENTIALS); |
| | | |
| | | if (pwPolicyErrorType == null) |
| | | { |
| | | pwPolicyErrorType = PasswordPolicyErrorType.ACCOUNT_LOCKED; |
| | | } |
| | | |
| | | int msgID = MSGID_BIND_OPERATION_ACCOUNT_RESET_LOCKED; |
| | | String message = getMessage(msgID, userDNString); |
| | | setAuthFailureReason(msgID, message); |
| | | |
| | | pwPolicyState.generateAccountStatusNotification( |
| | | AccountStatusNotificationType.ACCOUNT_RESET_LOCKED, bindDN, |
| | | msgID, message); |
| | | |
| | | break bindProcessing; |
| | | } |
| | | |
| | | if (pwPolicyState.isPasswordExpired()) |
| | | { |
| | | if (pwPolicyErrorType == null) |
| | | { |
| | | pwPolicyErrorType = PasswordPolicyErrorType.PASSWORD_EXPIRED; |
| | | } |
| | | |
| | | int maxGraceLogins |
| | | = pwPolicyState.getPolicy().getGraceLoginCount(); |
| | | if ((maxGraceLogins > 0) && pwPolicyState.mayUseGraceLogin()) |
| | | { |
| | | List<Long> graceLoginTimes = |
| | | pwPolicyState.getGraceLoginTimes(); |
| | | if ((graceLoginTimes == null) || |
| | | (graceLoginTimes.size() < maxGraceLogins)) |
| | | { |
| | | isGraceLogin = true; |
| | | mustChangePassword = true; |
| | | |
| | | if (pwPolicyWarningType == null) |
| | | { |
| | | pwPolicyWarningType = |
| | | PasswordPolicyWarningType.GRACE_LOGINS_REMAINING; |
| | | pwPolicyWarningValue = |
| | | maxGraceLogins - (graceLoginTimes.size() + 1); |
| | | } |
| | | } |
| | | else |
| | | { |
| | | int msgID = MSGID_BIND_OPERATION_PASSWORD_EXPIRED; |
| | | String message = getMessage(msgID, String.valueOf(bindDN)); |
| | | |
| | | setResultCode(ResultCode.INVALID_CREDENTIALS); |
| | | setAuthFailureReason(msgID, message); |
| | | |
| | | pwPolicyState.generateAccountStatusNotification( |
| | | AccountStatusNotificationType.PASSWORD_EXPIRED, bindDN, |
| | | msgID, message); |
| | | |
| | | break bindProcessing; |
| | | } |
| | | } |
| | | else |
| | | { |
| | | int msgID = MSGID_BIND_OPERATION_PASSWORD_EXPIRED; |
| | | String message = getMessage(msgID, String.valueOf(bindDN)); |
| | | |
| | | setResultCode(ResultCode.INVALID_CREDENTIALS); |
| | | setAuthFailureReason(msgID, message); |
| | | |
| | | pwPolicyState.generateAccountStatusNotification( |
| | | AccountStatusNotificationType.PASSWORD_EXPIRED, bindDN, |
| | | msgID, message); |
| | | |
| | | break bindProcessing; |
| | | } |
| | | } |
| | | else if (pwPolicyState.shouldWarn()) |
| | | { |
| | | int numSeconds = pwPolicyState.getSecondsUntilExpiration(); |
| | | String timeToExpiration = secondsToTimeString(numSeconds); |
| | | |
| | | int msgID = MSGID_BIND_PASSWORD_EXPIRING; |
| | | String message = getMessage(msgID, timeToExpiration); |
| | | appendErrorMessage(message); |
| | | |
| | | if (pwPolicyWarningType == null) |
| | | { |
| | | pwPolicyWarningType = |
| | | PasswordPolicyWarningType.TIME_BEFORE_EXPIRATION; |
| | | pwPolicyWarningValue = numSeconds; |
| | | } |
| | | |
| | | isFirstWarning = pwPolicyState.isFirstWarning(); |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | // Determine whether the authentication was successful and perform |
| | | // any remaining password policy processing accordingly. Also check |
| | | // for a custom size/time limit. |
| | | ResultCode resultCode = getResultCode(); |
| | | if (resultCode == ResultCode.SUCCESS) |
| | | { |
| | | if (pwPolicyState != null) |
| | | { |
| | | if (saslHandler.isPasswordBased(saslMechanism) && |
| | | pwPolicyState.mustChangePassword()) |
| | | { |
| | | mustChangePassword = true; |
| | | } |
| | | |
| | | if (isFirstWarning) |
| | | { |
| | | pwPolicyState.setWarnedTime(); |
| | | |
| | | int numSeconds = pwPolicyState.getSecondsUntilExpiration(); |
| | | String timeToExpiration = secondsToTimeString(numSeconds); |
| | | |
| | | int msgID = MSGID_BIND_PASSWORD_EXPIRING; |
| | | String message = getMessage(msgID, timeToExpiration); |
| | | |
| | | pwPolicyState.generateAccountStatusNotification( |
| | | AccountStatusNotificationType.PASSWORD_EXPIRING, bindDN, |
| | | msgID, message); |
| | | } |
| | | |
| | | if (isGraceLogin) |
| | | { |
| | | pwPolicyState.updateGraceLoginTimes(); |
| | | } |
| | | |
| | | pwPolicyState.setLastLoginTime(); |
| | | |
| | | |
| | | // See if the user's entry contains a custom size limit. |
| | | AttributeType attrType = |
| | | DirectoryServer.getAttributeType(OP_ATTR_USER_SIZE_LIMIT, |
| | | true); |
| | | List<Attribute> attrList = |
| | | saslAuthUserEntry.getAttribute(attrType); |
| | | if ((attrList != null) && (attrList.size() == 1)) |
| | | { |
| | | Attribute a = attrList.get(0); |
| | | LinkedHashSet<AttributeValue> values = a.getValues(); |
| | | Iterator<AttributeValue> iterator = values.iterator(); |
| | | if (iterator.hasNext()) |
| | | { |
| | | AttributeValue v = iterator.next(); |
| | | if (iterator.hasNext()) |
| | | { |
| | | int msgID = MSGID_BIND_MULTIPLE_USER_SIZE_LIMITS; |
| | | String message = getMessage(msgID, userDNString); |
| | | logError(ErrorLogCategory.CORE_SERVER, |
| | | ErrorLogSeverity.SEVERE_WARNING, message, msgID); |
| | | } |
| | | else |
| | | { |
| | | try |
| | | { |
| | | sizeLimit = Integer.parseInt(v.getStringValue()); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | |
| | | int msgID = MSGID_BIND_CANNOT_PROCESS_USER_SIZE_LIMIT; |
| | | String message = |
| | | getMessage(msgID, v.getStringValue(), userDNString); |
| | | logError(ErrorLogCategory.CORE_SERVER, |
| | | ErrorLogSeverity.SEVERE_WARNING, message, msgID); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | // See if the user's entry contains a custom time limit. |
| | | attrType = |
| | | DirectoryServer.getAttributeType(OP_ATTR_USER_TIME_LIMIT, |
| | | true); |
| | | attrList = saslAuthUserEntry.getAttribute(attrType); |
| | | if ((attrList != null) && (attrList.size() == 1)) |
| | | { |
| | | Attribute a = attrList.get(0); |
| | | LinkedHashSet<AttributeValue> values = a.getValues(); |
| | | Iterator<AttributeValue> iterator = values.iterator(); |
| | | if (iterator.hasNext()) |
| | | { |
| | | AttributeValue v = iterator.next(); |
| | | if (iterator.hasNext()) |
| | | { |
| | | int msgID = MSGID_BIND_MULTIPLE_USER_TIME_LIMITS; |
| | | String message = getMessage(msgID, userDNString); |
| | | logError(ErrorLogCategory.CORE_SERVER, |
| | | ErrorLogSeverity.SEVERE_WARNING, message, msgID); |
| | | } |
| | | else |
| | | { |
| | | try |
| | | { |
| | | timeLimit = Integer.parseInt(v.getStringValue()); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | |
| | | int msgID = MSGID_BIND_CANNOT_PROCESS_USER_TIME_LIMIT; |
| | | String message = |
| | | getMessage(msgID, v.getStringValue(), userDNString); |
| | | logError(ErrorLogCategory.CORE_SERVER, |
| | | ErrorLogSeverity.SEVERE_WARNING, message, msgID); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | // See if the user's entry contains a custom lookthrough limit. |
| | | attrType = |
| | | DirectoryServer.getAttributeType( |
| | | OP_ATTR_USER_LOOKTHROUGH_LIMIT, true); |
| | | attrList = saslAuthUserEntry.getAttribute(attrType); |
| | | if ((attrList != null) && (attrList.size() == 1)) |
| | | { |
| | | Attribute a = attrList.get(0); |
| | | LinkedHashSet<AttributeValue> values = a.getValues(); |
| | | Iterator<AttributeValue> iterator = values.iterator(); |
| | | if (iterator.hasNext()) |
| | | { |
| | | AttributeValue v = iterator.next(); |
| | | if (iterator.hasNext()) |
| | | { |
| | | int msgID = MSGID_BIND_MULTIPLE_USER_LOOKTHROUGH_LIMITS; |
| | | String message = getMessage(msgID, userDNString); |
| | | logError(ErrorLogCategory.CORE_SERVER, |
| | | ErrorLogSeverity.SEVERE_WARNING, message, msgID); |
| | | } |
| | | else |
| | | { |
| | | try |
| | | { |
| | | lookthroughLimit = Integer.parseInt(v.getStringValue()); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | |
| | | int msgID = |
| | | MSGID_BIND_CANNOT_PROCESS_USER_LOOKTHROUGH_LIMIT; |
| | | String message = |
| | | getMessage(msgID, v.getStringValue(), userDNString); |
| | | logError(ErrorLogCategory.CORE_SERVER, |
| | | ErrorLogSeverity.SEVERE_WARNING, message, msgID); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | else if (resultCode == ResultCode.SASL_BIND_IN_PROGRESS) |
| | | { |
| | | // FIXME -- Is any special processing needed here? |
| | | } |
| | | else |
| | | { |
| | | if (pwPolicyState != null) |
| | | { |
| | | if (saslHandler.isPasswordBased(saslMechanism)) |
| | | { |
| | | |
| | | if (pwPolicyState.getPolicy().getLockoutFailureCount() > 0) |
| | | { |
| | | pwPolicyState.updateAuthFailureTimes(); |
| | | if (pwPolicyState.lockedDueToFailures()) |
| | | { |
| | | AccountStatusNotificationType notificationType; |
| | | int msgID; |
| | | String message; |
| | | |
| | | int lockoutDuration = pwPolicyState.getSecondsUntilUnlock(); |
| | | if (lockoutDuration > -1) |
| | | { |
| | | notificationType = AccountStatusNotificationType. |
| | | ACCOUNT_TEMPORARILY_LOCKED; |
| | | msgID = MSGID_BIND_ACCOUNT_TEMPORARILY_LOCKED; |
| | | message = getMessage(msgID, |
| | | secondsToTimeString(lockoutDuration)); |
| | | } |
| | | else |
| | | { |
| | | notificationType = AccountStatusNotificationType. |
| | | ACCOUNT_PERMANENTLY_LOCKED; |
| | | msgID = MSGID_BIND_ACCOUNT_PERMANENTLY_LOCKED; |
| | | message = getMessage(msgID); |
| | | } |
| | | |
| | | pwPolicyState.generateAccountStatusNotification( |
| | | notificationType, userEntryDN, msgID, message); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | break; |
| | | |
| | | |
| | | default: |
| | | // Send a protocol error response to the client and disconnect. |
| | | // NYI |
| | | return; |
| | | } |
| | | } |
| | | |
| | | |
| | | // Update the user's account with any password policy changes that may be |
| | | // required. |
| | | try |
| | | { |
| | | if (pwPolicyState != null) |
| | | { |
| | | pwPolicyState.updateUserEntry(); |
| | | } |
| | | } |
| | | catch (DirectoryException de) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, de); |
| | | } |
| | | |
| | | setResponseData(de); |
| | | } |
| | | |
| | | |
| | | // Invoke the post-operation bind plugins. |
| | | if (! skipPostOperation) |
| | | { |
| | | PostOperationPluginResult postOpResult = |
| | | pluginConfigManager.invokePostOperationBindPlugins(this); |
| | | if (postOpResult.connectionTerminated()) |
| | | { |
| | | // There's no point in continuing with anything. Log the result |
| | | // and return. |
| | | setResultCode(ResultCode.CANCELED); |
| | | |
| | | int msgID = MSGID_CANCELED_BY_PREOP_DISCONNECT; |
| | | appendErrorMessage(getMessage(msgID)); |
| | | |
| | | processingStopTime = System.currentTimeMillis(); |
| | | |
| | | logBindResponse(this); |
| | | pluginConfigManager.invokePostResponseBindPlugins(this); |
| | | return; |
| | | } |
| | | } |
| | | |
| | | |
| | | // Update the authentication information for the user. |
| | | if ((getResultCode() == ResultCode.SUCCESS) && (authInfo != null)) |
| | | { |
| | | authenticatedUserEntry = authInfo.getAuthenticationEntry(); |
| | | clientConnection.setAuthenticationInfo(authInfo); |
| | | clientConnection.setSizeLimit(sizeLimit); |
| | | clientConnection.setTimeLimit(timeLimit); |
| | | clientConnection.setLookthroughLimit(lookthroughLimit); |
| | | clientConnection.setMustChangePassword(mustChangePassword); |
| | | |
| | | if (returnAuthzID) |
| | | { |
| | | responseControls.add(new AuthorizationIdentityResponseControl( |
| | | authInfo.getAuthorizationDN())); |
| | | } |
| | | } |
| | | |
| | | |
| | | // See if we need to send a password policy control to the client. If so, |
| | | // then add it to the response. |
| | | if (getResultCode() == ResultCode.SUCCESS) |
| | | { |
| | | if (pwPolicyControlRequested) |
| | | { |
| | | PasswordPolicyResponseControl pwpControl = |
| | | new PasswordPolicyResponseControl(pwPolicyWarningType, |
| | | pwPolicyWarningValue, |
| | | pwPolicyErrorType); |
| | | responseControls.add(pwpControl); |
| | | } |
| | | else |
| | | { |
| | | if (pwPolicyErrorType == PasswordPolicyErrorType.PASSWORD_EXPIRED) |
| | | { |
| | | responseControls.add(new PasswordExpiredControl()); |
| | | } |
| | | else if (pwPolicyWarningType == |
| | | PasswordPolicyWarningType.TIME_BEFORE_EXPIRATION) |
| | | { |
| | | responseControls.add(new PasswordExpiringControl( |
| | | pwPolicyWarningValue)); |
| | | } |
| | | } |
| | | } |
| | | else |
| | | { |
| | | if (pwPolicyControlRequested) |
| | | { |
| | | PasswordPolicyResponseControl pwpControl = |
| | | new PasswordPolicyResponseControl(pwPolicyWarningType, |
| | | pwPolicyWarningValue, |
| | | pwPolicyErrorType); |
| | | responseControls.add(pwpControl); |
| | | } |
| | | else |
| | | { |
| | | if (pwPolicyErrorType == PasswordPolicyErrorType.PASSWORD_EXPIRED) |
| | | { |
| | | responseControls.add(new PasswordExpiredControl()); |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | // Unset the "bind in progress" flag to allow other operations to be |
| | | // processed. |
| | | // FIXME -- Make sure this also gets unset at every possible point at which |
| | | // the bind could fail and this method could return early. |
| | | clientConnection.setBindInProgress(false); |
| | | |
| | | |
| | | // Stop the processing timer. |
| | | processingStopTime = System.currentTimeMillis(); |
| | | |
| | | |
| | | // Send the bind response to the client. |
| | | clientConnection.sendResponse(this); |
| | | |
| | | |
| | | // Log the bind response. |
| | | logBindResponse(this); |
| | | |
| | | |
| | | // Invoke the post-response bind plugins. |
| | | pluginConfigManager.invokePostResponseBindPlugins(this); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final CancelResult cancel(CancelRequest cancelRequest) |
| | | { |
| | | cancelRequest.addResponseMessage(getMessage(MSGID_CANNOT_CANCEL_BIND)); |
| | | return CancelResult.CANNOT_CANCEL; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final CancelRequest getCancelRequest() |
| | | { |
| | | return null; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | protected boolean setCancelRequest(CancelRequest cancelRequest) |
| | | { |
| | | // Bind operations cannot be canceled. |
| | | return false; |
| | | } |
| | | |
| | | public abstract void setUserEntryDN(DN userEntryDN); |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final void toString(StringBuilder buffer) |
| | | { |
| | | buffer.append("BindOperation(connID="); |
| | | buffer.append(clientConnection.getConnectionID()); |
| | | buffer.append(", opID="); |
| | | buffer.append(operationID); |
| | | buffer.append(", protocol=\""); |
| | | buffer.append(clientConnection.getProtocol()); |
| | | buffer.append(" "); |
| | | buffer.append(protocolVersion); |
| | | buffer.append("\", dn="); |
| | | buffer.append(rawBindDN); |
| | | buffer.append(", authType="); |
| | | buffer.append(authType); |
| | | buffer.append(")"); |
| | | } |
| | | } |
| | | |
| New file |
| | |
| | | /* |
| | | * 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 |
| | | * trunk/opends/resource/legal-notices/OpenDS.LICENSE |
| | | * or https://OpenDS.dev.java.net/OpenDS.LICENSE. |
| | | * 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 |
| | | * trunk/opends/resource/legal-notices/OpenDS.LICENSE. 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 |
| | | * |
| | | * |
| | | * Portions Copyright 2007 Sun Microsystems, Inc. |
| | | */ |
| | | package org.opends.server.core; |
| | | |
| | | |
| | | |
| | | import static org.opends.server.core.CoreConstants.LOG_ELEMENT_AUTH_TYPE; |
| | | import static org.opends.server.core.CoreConstants.LOG_ELEMENT_BIND_DN; |
| | | import static org.opends.server.core.CoreConstants.LOG_ELEMENT_ERROR_MESSAGE; |
| | | import static org.opends.server.core.CoreConstants.LOG_ELEMENT_MATCHED_DN; |
| | | import static org.opends.server.core.CoreConstants.LOG_ELEMENT_PROCESSING_TIME; |
| | | import static org.opends.server.core.CoreConstants.LOG_ELEMENT_REFERRAL_URLS; |
| | | import static org.opends.server.core.CoreConstants.LOG_ELEMENT_RESULT_CODE; |
| | | import static org.opends.server.core.CoreConstants.LOG_ELEMENT_SASL_MECHANISM; |
| | | import static org.opends.server.loggers.AccessLogger.logBindRequest; |
| | | import static org.opends.server.loggers.AccessLogger.logBindResponse; |
| | | import static org.opends.server.loggers.debug.DebugLogger.debugEnabled; |
| | | import static org.opends.server.messages.CoreMessages.*; |
| | | import static org.opends.server.messages.MessageHandler.getMessage; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.Iterator; |
| | | import java.util.List; |
| | | |
| | | import org.opends.server.api.ClientConnection; |
| | | import org.opends.server.api.plugin.PreParsePluginResult; |
| | | import org.opends.server.loggers.debug.DebugLogger; |
| | | import org.opends.server.loggers.debug.DebugTracer; |
| | | import org.opends.server.protocols.asn1.ASN1OctetString; |
| | | import org.opends.server.types.AbstractOperation; |
| | | import org.opends.server.types.AuthenticationInfo; |
| | | import org.opends.server.types.AuthenticationType; |
| | | import org.opends.server.types.ByteString; |
| | | import org.opends.server.types.CancelRequest; |
| | | import org.opends.server.types.CancelResult; |
| | | import org.opends.server.types.Control; |
| | | import org.opends.server.types.DN; |
| | | import org.opends.server.types.DebugLogLevel; |
| | | import org.opends.server.types.DirectoryException; |
| | | import org.opends.server.types.DisconnectReason; |
| | | import org.opends.server.types.Entry; |
| | | import org.opends.server.types.Operation; |
| | | import org.opends.server.types.OperationType; |
| | | import org.opends.server.types.ResultCode; |
| | | import org.opends.server.types.operation.PreParseBindOperation; |
| | | import org.opends.server.workflowelement.localbackend.*; |
| | | |
| | | |
| | | |
| | | |
| | | /** |
| | | * This class defines an operation that may be used to authenticate a user to |
| | | * the Directory Server. Note that for security restrictions, response messages |
| | | * that may be returned to the client must be carefully cleaned to ensure that |
| | | * they do not provide a malicious client with information that may be useful in |
| | | * an attack. This does impact the debugability of the server, but that can |
| | | * be addressed by calling the <CODE>setAuthFailureReason</CODE> method, which |
| | | * can provide a reason for a failure in a form that will not be returned to the |
| | | * client but may be written to a log file. |
| | | */ |
| | | public class BindOperationBasis |
| | | extends AbstractOperation |
| | | implements BindOperation, PreParseBindOperation |
| | | { |
| | | /** |
| | | * The tracer object for the debug logger. |
| | | */ |
| | | private static final DebugTracer TRACER = DebugLogger.getTracer(); |
| | | |
| | | // The credentials used for SASL authentication. |
| | | private ASN1OctetString saslCredentials; |
| | | |
| | | // The server SASL credentials provided to the client in the response. |
| | | private ASN1OctetString serverSASLCredentials; |
| | | |
| | | // The authentication info for this bind operation. |
| | | private AuthenticationInfo authInfo = null; |
| | | |
| | | // The authentication type used for this bind operation. |
| | | private AuthenticationType authType; |
| | | |
| | | // The raw, unprocessed bind DN as contained in the client request. |
| | | private ByteString rawBindDN; |
| | | |
| | | // The password used for simple authentication. |
| | | private ByteString simplePassword; |
| | | |
| | | // The bind DN used for this bind operation. |
| | | private DN bindDN; |
| | | |
| | | // The DN of the user entry that is attempting to authenticate. |
| | | private DN userEntryDN; |
| | | |
| | | // The DN of the user as whom a SASL authentication was attempted (regardless |
| | | // of whether the authentication was successful) for the purpose of updating |
| | | // password policy state information. |
| | | private Entry saslAuthUserEntry; |
| | | |
| | | // The unique ID associated with the failure reason message. |
| | | private int authFailureID; |
| | | |
| | | // The set of response controls for this bind operation. |
| | | private List<Control> responseControls; |
| | | |
| | | // A message explaining the reason for the authentication failure. |
| | | private String authFailureReason; |
| | | |
| | | // The SASL mechanism used for SASL authentication. |
| | | private String saslMechanism; |
| | | |
| | | // A string representation of the protocol version for this bind operation. |
| | | private String protocolVersion; |
| | | |
| | | /** |
| | | * Creates a new simple bind operation with the provided information. |
| | | * |
| | | * @param clientConnection The client connection with which this operation |
| | | * is associated. |
| | | * @param operationID The operation ID for this operation. |
| | | * @param messageID The message ID of the request with which this |
| | | * operation is associated. |
| | | * @param requestControls The set of controls included in the request. |
| | | * @param protocolVersion The string representation of the protocol version |
| | | * associated with this bind request. |
| | | * @param rawBindDN The raw, unprocessed bind DN as provided in the |
| | | * request from the client. |
| | | * @param simplePassword The password to use for the simple |
| | | * authentication. |
| | | */ |
| | | public BindOperationBasis(ClientConnection clientConnection, long operationID, |
| | | int messageID, List<Control> requestControls, |
| | | String protocolVersion, ByteString rawBindDN, |
| | | ByteString simplePassword) |
| | | { |
| | | super(clientConnection, operationID, messageID, requestControls); |
| | | |
| | | |
| | | this.protocolVersion = protocolVersion; |
| | | this.authType = AuthenticationType.SIMPLE; |
| | | this.saslMechanism = null; |
| | | this.saslCredentials = null; |
| | | |
| | | if (rawBindDN == null) |
| | | { |
| | | this.rawBindDN = new ASN1OctetString(); |
| | | } |
| | | else |
| | | { |
| | | this.rawBindDN = rawBindDN; |
| | | } |
| | | |
| | | if (simplePassword == null) |
| | | { |
| | | this.simplePassword = new ASN1OctetString(); |
| | | } |
| | | else |
| | | { |
| | | this.simplePassword = simplePassword; |
| | | } |
| | | |
| | | bindDN = null; |
| | | userEntryDN = null; |
| | | responseControls = new ArrayList<Control>(0); |
| | | authFailureID = 0; |
| | | authFailureReason = null; |
| | | saslAuthUserEntry = null; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Creates a new SASL bind operation with the provided information. |
| | | * |
| | | * @param clientConnection The client connection with which this operation |
| | | * is associated. |
| | | * @param operationID The operation ID for this operation. |
| | | * @param messageID The message ID of the request with which this |
| | | * operation is associated. |
| | | * @param requestControls The set of controls included in the request. |
| | | * @param protocolVersion The string representation of the protocol version |
| | | * associated with this bind request. |
| | | * @param rawBindDN The raw, unprocessed bind DN as provided in the |
| | | * request from the client. |
| | | * @param saslMechanism The SASL mechanism included in the request. |
| | | * @param saslCredentials The optional SASL credentials included in the |
| | | * request. |
| | | */ |
| | | public BindOperationBasis(ClientConnection clientConnection, long operationID, |
| | | int messageID, List<Control> requestControls, |
| | | String protocolVersion, ByteString rawBindDN, |
| | | String saslMechanism, ASN1OctetString saslCredentials) |
| | | { |
| | | super(clientConnection, operationID, messageID, requestControls); |
| | | |
| | | |
| | | this.protocolVersion = protocolVersion; |
| | | this.authType = AuthenticationType.SASL; |
| | | this.saslMechanism = saslMechanism; |
| | | this.saslCredentials = saslCredentials; |
| | | this.simplePassword = null; |
| | | |
| | | if (rawBindDN == null) |
| | | { |
| | | this.rawBindDN = new ASN1OctetString(); |
| | | } |
| | | else |
| | | { |
| | | this.rawBindDN = rawBindDN; |
| | | } |
| | | |
| | | bindDN = null; |
| | | userEntryDN = null; |
| | | responseControls = new ArrayList<Control>(0); |
| | | authFailureID = 0; |
| | | authFailureReason = null; |
| | | saslAuthUserEntry = null; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Creates a new simple bind operation with the provided information. |
| | | * |
| | | * @param clientConnection The client connection with which this operation |
| | | * is associated. |
| | | * @param operationID The operation ID for this operation. |
| | | * @param messageID The message ID of the request with which this |
| | | * operation is associated. |
| | | * @param requestControls The set of controls included in the request. |
| | | * @param protocolVersion The string representation of the protocol version |
| | | * associated with this bind request. |
| | | * @param bindDN The bind DN for this bind operation. |
| | | * @param simplePassword The password to use for the simple |
| | | * authentication. |
| | | */ |
| | | public BindOperationBasis(ClientConnection clientConnection, long operationID, |
| | | int messageID, List<Control> requestControls, |
| | | String protocolVersion, DN bindDN, |
| | | ByteString simplePassword) |
| | | { |
| | | super(clientConnection, operationID, messageID, requestControls); |
| | | |
| | | |
| | | this.protocolVersion = protocolVersion; |
| | | this.authType = AuthenticationType.SIMPLE; |
| | | this.bindDN = bindDN; |
| | | this.saslMechanism = null; |
| | | this.saslCredentials = null; |
| | | |
| | | if (bindDN == null) |
| | | { |
| | | rawBindDN = new ASN1OctetString(); |
| | | } |
| | | else |
| | | { |
| | | rawBindDN = new ASN1OctetString(bindDN.toString()); |
| | | } |
| | | |
| | | if (simplePassword == null) |
| | | { |
| | | this.simplePassword = new ASN1OctetString(); |
| | | } |
| | | else |
| | | { |
| | | this.simplePassword = simplePassword; |
| | | } |
| | | |
| | | responseControls = new ArrayList<Control>(0); |
| | | authFailureID = 0; |
| | | authFailureReason = null; |
| | | saslAuthUserEntry = null; |
| | | userEntryDN = null; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Creates a new SASL bind operation with the provided information. |
| | | * |
| | | * @param clientConnection The client connection with which this operation |
| | | * is associated. |
| | | * @param operationID The operation ID for this operation. |
| | | * @param messageID The message ID of the request with which this |
| | | * operation is associated. |
| | | * @param requestControls The set of controls included in the request. |
| | | * @param protocolVersion The string representation of the protocol version |
| | | * associated with this bind request. |
| | | * @param bindDN The bind DN for this bind operation. |
| | | * @param saslMechanism The SASL mechanism included in the request. |
| | | * @param saslCredentials The optional SASL credentials included in the |
| | | * request. |
| | | */ |
| | | public BindOperationBasis(ClientConnection clientConnection, long operationID, |
| | | int messageID, List<Control> requestControls, |
| | | String protocolVersion, DN bindDN, |
| | | String saslMechanism, ASN1OctetString saslCredentials) |
| | | { |
| | | super(clientConnection, operationID, messageID, requestControls); |
| | | |
| | | |
| | | this.protocolVersion = protocolVersion; |
| | | this.authType = AuthenticationType.SASL; |
| | | this.bindDN = bindDN; |
| | | this.saslMechanism = saslMechanism; |
| | | this.saslCredentials = saslCredentials; |
| | | this.simplePassword = null; |
| | | |
| | | if (bindDN == null) |
| | | { |
| | | rawBindDN = new ASN1OctetString(); |
| | | } |
| | | else |
| | | { |
| | | rawBindDN = new ASN1OctetString(bindDN.toString()); |
| | | } |
| | | |
| | | responseControls = new ArrayList<Control>(0); |
| | | authFailureID = 0; |
| | | authFailureReason = null; |
| | | saslAuthUserEntry = null; |
| | | userEntryDN = null; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public final AuthenticationType getAuthenticationType() |
| | | { |
| | | return authType; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public final ByteString getRawBindDN() |
| | | { |
| | | return rawBindDN; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public final void setRawBindDN(ByteString rawBindDN) |
| | | { |
| | | if (rawBindDN == null) |
| | | { |
| | | this.rawBindDN = new ASN1OctetString(); |
| | | } |
| | | else |
| | | { |
| | | this.rawBindDN = rawBindDN; |
| | | } |
| | | |
| | | bindDN = null; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public final DN getBindDN() |
| | | { |
| | | try |
| | | { |
| | | if (bindDN == null) |
| | | { |
| | | bindDN = DN.decode(rawBindDN); |
| | | } |
| | | } |
| | | catch (DirectoryException de) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, de); |
| | | } |
| | | |
| | | setResultCode(ResultCode.INVALID_CREDENTIALS); |
| | | setAuthFailureReason(de.getMessageID(), de.getErrorMessage()); |
| | | } |
| | | return bindDN; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public final ByteString getSimplePassword() |
| | | { |
| | | return simplePassword; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public final void setSimplePassword(ByteString simplePassword) |
| | | { |
| | | if (simplePassword == null) |
| | | { |
| | | this.simplePassword = new ASN1OctetString(); |
| | | } |
| | | else |
| | | { |
| | | this.simplePassword = simplePassword; |
| | | } |
| | | |
| | | authType = AuthenticationType.SIMPLE; |
| | | saslMechanism = null; |
| | | saslCredentials = null; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public final String getSASLMechanism() |
| | | { |
| | | return saslMechanism; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public final ASN1OctetString getSASLCredentials() |
| | | { |
| | | return saslCredentials; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public final void setSASLCredentials(String saslMechanism, |
| | | ASN1OctetString saslCredentials) |
| | | { |
| | | this.saslMechanism = saslMechanism; |
| | | this.saslCredentials = saslCredentials; |
| | | |
| | | authType = AuthenticationType.SASL; |
| | | simplePassword = null; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public final ASN1OctetString getServerSASLCredentials() |
| | | { |
| | | return serverSASLCredentials; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public final void setServerSASLCredentials(ASN1OctetString |
| | | serverSASLCredentials) |
| | | { |
| | | this.serverSASLCredentials = serverSASLCredentials; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public final Entry getSASLAuthUserEntry() |
| | | { |
| | | return saslAuthUserEntry; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public final void setSASLAuthUserEntry(Entry saslAuthUserEntry) |
| | | { |
| | | this.saslAuthUserEntry = saslAuthUserEntry; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public final String getAuthFailureReason() |
| | | { |
| | | return authFailureReason; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public final int getAuthFailureID() |
| | | { |
| | | return authFailureID; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public final void setAuthFailureReason(int id, String reason) |
| | | { |
| | | if (id < 0) |
| | | { |
| | | authFailureID = 0; |
| | | } |
| | | else |
| | | { |
| | | authFailureID = id; |
| | | } |
| | | |
| | | authFailureReason = reason; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public final DN getUserEntryDN() |
| | | { |
| | | return userEntryDN; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public final AuthenticationInfo getAuthenticationInfo() |
| | | { |
| | | return authInfo; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public final void setAuthenticationInfo(AuthenticationInfo authInfo) |
| | | { |
| | | this.authInfo = authInfo; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final OperationType getOperationType() |
| | | { |
| | | // Note that no debugging will be done in this method because it is a likely |
| | | // candidate for being called by the logging subsystem. |
| | | |
| | | return OperationType.BIND; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final void disconnectClient(DisconnectReason disconnectReason, |
| | | boolean sendNotification, String message, |
| | | int messageID) |
| | | { |
| | | // Since bind operations can't be cancelled, we don't need to do anything |
| | | // but forward the request on to the client connection. |
| | | clientConnection.disconnect(disconnectReason, sendNotification, message, |
| | | messageID); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final String[][] getRequestLogElements() |
| | | { |
| | | // Note that no debugging will be done in this method because it is a likely |
| | | // candidate for being called by the logging subsystem. |
| | | |
| | | if (authType == AuthenticationType.SASL) |
| | | { |
| | | return new String[][] |
| | | { |
| | | new String[] { LOG_ELEMENT_BIND_DN, String.valueOf(rawBindDN) }, |
| | | new String[] { LOG_ELEMENT_AUTH_TYPE, authType.toString() }, |
| | | new String[] { LOG_ELEMENT_SASL_MECHANISM, saslMechanism } |
| | | }; |
| | | } |
| | | else |
| | | { |
| | | return new String[][] |
| | | { |
| | | new String[] { LOG_ELEMENT_BIND_DN, String.valueOf(rawBindDN) }, |
| | | new String[] { LOG_ELEMENT_AUTH_TYPE, authType.toString() } |
| | | }; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final String[][] getResponseLogElements() |
| | | { |
| | | // Note that no debugging will be done in this method because it is a likely |
| | | // candidate for being called by the logging subsystem. |
| | | |
| | | String resultCode = String.valueOf(getResultCode().getIntValue()); |
| | | |
| | | String errorMessage; |
| | | StringBuilder errorMessageBuffer = getErrorMessage(); |
| | | if (errorMessageBuffer == null) |
| | | { |
| | | errorMessage = null; |
| | | } |
| | | else |
| | | { |
| | | errorMessage = errorMessageBuffer.toString(); |
| | | } |
| | | |
| | | String matchedDNStr; |
| | | DN matchedDN = getMatchedDN(); |
| | | if (matchedDN == null) |
| | | { |
| | | matchedDNStr = null; |
| | | } |
| | | else |
| | | { |
| | | matchedDNStr = matchedDN.toString(); |
| | | } |
| | | |
| | | String referrals; |
| | | List<String> referralURLs = getReferralURLs(); |
| | | if ((referralURLs == null) || referralURLs.isEmpty()) |
| | | { |
| | | referrals = null; |
| | | } |
| | | else |
| | | { |
| | | StringBuilder buffer = new StringBuilder(); |
| | | Iterator<String> iterator = referralURLs.iterator(); |
| | | buffer.append(iterator.next()); |
| | | |
| | | while (iterator.hasNext()) |
| | | { |
| | | buffer.append(", "); |
| | | buffer.append(iterator.next()); |
| | | } |
| | | |
| | | referrals = buffer.toString(); |
| | | } |
| | | |
| | | String processingTime = |
| | | String.valueOf(getProcessingTime()); |
| | | |
| | | return new String[][] |
| | | { |
| | | new String[] { LOG_ELEMENT_RESULT_CODE, resultCode }, |
| | | new String[] { LOG_ELEMENT_ERROR_MESSAGE, errorMessage }, |
| | | new String[] { LOG_ELEMENT_MATCHED_DN, matchedDNStr }, |
| | | new String[] { LOG_ELEMENT_REFERRAL_URLS, referrals }, |
| | | new String[] { LOG_ELEMENT_PROCESSING_TIME, processingTime } |
| | | }; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final List<Control> getResponseControls() |
| | | { |
| | | return responseControls; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final void addResponseControl(Control control) |
| | | { |
| | | responseControls.add(control); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final void removeResponseControl(Control control) |
| | | { |
| | | responseControls.remove(control); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final CancelResult cancel(CancelRequest cancelRequest) |
| | | { |
| | | cancelRequest.addResponseMessage(getMessage(MSGID_CANNOT_CANCEL_BIND)); |
| | | return CancelResult.CANNOT_CANCEL; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final CancelRequest getCancelRequest() |
| | | { |
| | | return null; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public |
| | | boolean setCancelRequest(CancelRequest cancelRequest) |
| | | { |
| | | // Bind operations cannot be canceled. |
| | | return false; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final void toString(StringBuilder buffer) |
| | | { |
| | | buffer.append("BindOperation(connID="); |
| | | buffer.append(clientConnection.getConnectionID()); |
| | | buffer.append(", opID="); |
| | | buffer.append(operationID); |
| | | buffer.append(", protocol=\""); |
| | | buffer.append(clientConnection.getProtocol()); |
| | | buffer.append(" "); |
| | | buffer.append(protocolVersion); |
| | | buffer.append(", dn="); |
| | | buffer.append(rawBindDN); |
| | | buffer.append(", authType="); |
| | | buffer.append(authType); |
| | | buffer.append(")"); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setUserEntryDN(DN userEntryDN) |
| | | { |
| | | this.userEntryDN = userEntryDN; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public String getProtocolVersion() |
| | | { |
| | | return protocolVersion; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setProtocolVersion(String protocolVersion) |
| | | { |
| | | this.protocolVersion = protocolVersion; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public final void run() |
| | | { |
| | | // Start the processing timer and initially set the result to indicate that |
| | | // the result is unknown. |
| | | setProcessingStartTime(); |
| | | ClientConnection clientConnection = getClientConnection(); |
| | | |
| | | setResultCode(ResultCode.UNDEFINED); |
| | | |
| | | // Set a flag to indicate that a bind operation is in progress. This should |
| | | // ensure that no new operations will be accepted for this client until the |
| | | // bind is complete. |
| | | clientConnection.setBindInProgress(true); |
| | | |
| | | // Wipe out any existing authentication for the client connection and create |
| | | // a placeholder that will be used if the bind is successful. |
| | | clientConnection.setUnauthenticated(); |
| | | |
| | | // Abandon any operations that may be in progress for the client. |
| | | String cancelReason = getMessage(MSGID_CANCELED_BY_BIND_REQUEST); |
| | | CancelRequest cancelRequest = new CancelRequest(true, cancelReason); |
| | | clientConnection.cancelAllOperationsExcept(cancelRequest, getMessageID()); |
| | | |
| | | |
| | | // Get the plugin config manager that will be used for invoking plugins. |
| | | PluginConfigManager pluginConfigManager = |
| | | DirectoryServer.getPluginConfigManager(); |
| | | |
| | | |
| | | // Create a labeled block of code that we can break out of if a problem is |
| | | // detected. |
| | | bindProcessing: |
| | | { |
| | | // Invoke the pre-parse bind plugins. |
| | | PreParsePluginResult preParseResult = |
| | | pluginConfigManager.invokePreParseBindPlugins(this); |
| | | if (preParseResult.connectionTerminated()) |
| | | { |
| | | // There's no point in continuing with anything. Log the request and |
| | | // result and return. |
| | | setResultCode(ResultCode.CANCELED); |
| | | |
| | | int msgID = MSGID_CANCELED_BY_PREPARSE_DISCONNECT; |
| | | appendErrorMessage(getMessage(msgID)); |
| | | |
| | | setProcessingStopTime(); |
| | | |
| | | logBindRequest(this); |
| | | logBindResponse(this); |
| | | return; |
| | | } |
| | | else if (preParseResult.sendResponseImmediately()) |
| | | { |
| | | logBindRequest(this); |
| | | break bindProcessing; |
| | | } |
| | | else if (preParseResult.skipCoreProcessing()) |
| | | { |
| | | break bindProcessing; |
| | | } |
| | | |
| | | // Log the bind request message. |
| | | logBindRequest(this); |
| | | |
| | | // Process the bind DN to convert it from the raw form as provided by the |
| | | // client into the form required for the rest of the bind processing. |
| | | DN bindDN = getBindDN(); |
| | | if (bindDN == null){ |
| | | break bindProcessing; |
| | | } |
| | | |
| | | // If this is a simple bind |
| | | // Then check wether the bind DN is actually one of the alternate root DNs |
| | | // defined in the server. If so, then replace it with the actual DN |
| | | // for that user. |
| | | switch (getAuthenticationType()) |
| | | { |
| | | case SIMPLE: |
| | | DN actualRootDN = DirectoryServer.getActualRootBindDN(bindDN); |
| | | if (actualRootDN != null) |
| | | { |
| | | bindDN = actualRootDN; |
| | | } |
| | | } |
| | | |
| | | |
| | | // Retrieve the network group attached to the client connection |
| | | // and get a workflow to process the operation. |
| | | NetworkGroup ng = getClientConnection().getNetworkGroup(); |
| | | Workflow workflow = ng.getWorkflowCandidate(bindDN); |
| | | if (workflow == null) |
| | | { |
| | | // We have found no workflow for the requested base DN, just return |
| | | // a no such entry result code and stop the processing. |
| | | updateOperationErrMsgAndResCode(); |
| | | break bindProcessing; |
| | | } |
| | | workflow.execute(this); |
| | | } |
| | | |
| | | // Check for and handle a request to cancel this operation. |
| | | if (getCancelResult() == CancelResult.CANCELED) |
| | | { |
| | | setProcessingStopTime(); |
| | | logBindResponse(this); |
| | | invokePostResponsePlugins(); |
| | | return; |
| | | } |
| | | |
| | | // Unset the "bind in progress" flag to allow other operations to be |
| | | // processed. |
| | | // FIXME -- Make sure this also gets unset at every possible point at which |
| | | // the bind could fail and this method could return early. |
| | | clientConnection.setBindInProgress(false); |
| | | |
| | | // Stop the processing timer. |
| | | setProcessingStopTime(); |
| | | |
| | | // Send the bind response to the client. |
| | | clientConnection.sendResponse(this); |
| | | |
| | | // Log the bind response. |
| | | logBindResponse(this); |
| | | |
| | | // Check wether there are local operations in attachments |
| | | List localOperations = |
| | | (List)getAttachment(Operation.LOCALBACKENDOPERATIONS); |
| | | if (localOperations != null && (! localOperations.isEmpty())){ |
| | | for (Object localOp : localOperations) |
| | | { |
| | | LocalBackendBindOperation localOperation = |
| | | (LocalBackendBindOperation)localOp; |
| | | // Invoke the post-response bind plugins. |
| | | pluginConfigManager.invokePostResponseBindPlugins(localOperation); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Updates the error message and the result code of the operation. |
| | | * |
| | | * This method is called because no workflows were found to process |
| | | * the operation. |
| | | */ |
| | | private void updateOperationErrMsgAndResCode() |
| | | { |
| | | int msgID = MSGID_BIND_OPERATION_UNKNOWN_USER; |
| | | String message = getMessage(msgID, String.valueOf(getBindDN())); |
| | | |
| | | setResultCode(ResultCode.INVALID_CREDENTIALS); |
| | | setAuthFailureReason(msgID, message); |
| | | } |
| | | |
| | | /** |
| | | * Execute the postResponseBindPlugins. |
| | | */ |
| | | private void invokePostResponsePlugins() |
| | | { |
| | | // Get the plugin config manager that will be used for invoking plugins. |
| | | PluginConfigManager pluginConfigManager = |
| | | DirectoryServer.getPluginConfigManager(); |
| | | |
| | | // Check wether there are local operations in attachments |
| | | List localOperations = |
| | | (List)getAttachment(Operation.LOCALBACKENDOPERATIONS); |
| | | |
| | | if (localOperations != null && (! localOperations.isEmpty())){ |
| | | for (Object localOp : localOperations) |
| | | { |
| | | LocalBackendBindOperation localOperation = |
| | | (LocalBackendBindOperation)localOp; |
| | | // Invoke the post-response bind plugins. |
| | | pluginConfigManager.invokePostResponseBindPlugins(localOperation); |
| | | } |
| | | } |
| | | } |
| | | |
| | | } |
| | | |
| New file |
| | |
| | | /* |
| | | * 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 |
| | | * trunk/opends/resource/legal-notices/OpenDS.LICENSE |
| | | * or https://OpenDS.dev.java.net/OpenDS.LICENSE. |
| | | * 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 |
| | | * trunk/opends/resource/legal-notices/OpenDS.LICENSE. 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 |
| | | * |
| | | * |
| | | * Portions Copyright 2007 Sun Microsystems, Inc. |
| | | */ |
| | | package org.opends.server.core; |
| | | |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | | import org.opends.server.api.ClientConnection; |
| | | import org.opends.server.protocols.asn1.ASN1OctetString; |
| | | import org.opends.server.types.AuthenticationInfo; |
| | | import org.opends.server.types.AuthenticationType; |
| | | import org.opends.server.types.ByteString; |
| | | import org.opends.server.types.CancelRequest; |
| | | import org.opends.server.types.CancelResult; |
| | | import org.opends.server.types.Control; |
| | | import org.opends.server.types.DN; |
| | | import org.opends.server.types.DirectoryException; |
| | | import org.opends.server.types.DisconnectReason; |
| | | import org.opends.server.types.Entry; |
| | | import org.opends.server.types.OperationType; |
| | | import org.opends.server.types.ResultCode; |
| | | |
| | | /** |
| | | * This abstract class wraps/decorates a given bind operation. |
| | | * This class will be extended by sub-classes to enhance the |
| | | * functionnality of the BindOperationBasis. |
| | | */ |
| | | public abstract class BindOperationWrapper implements BindOperation |
| | | { |
| | | private BindOperation bind; |
| | | |
| | | /** |
| | | * Creates a new bind operation based on the provided bind operation. |
| | | * |
| | | * @param bind The bind operation to wrap |
| | | */ |
| | | protected BindOperationWrapper(BindOperation bind){ |
| | | this.bind = bind; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void addRequestControl(Control control) |
| | | { |
| | | bind.addRequestControl(control); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void addResponseControl(Control control) |
| | | { |
| | | bind.addResponseControl(control); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void appendAdditionalLogMessage(String message) |
| | | { |
| | | bind.appendAdditionalLogMessage(message); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void appendErrorMessage(String message) |
| | | { |
| | | bind.appendErrorMessage(message); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public CancelResult cancel(CancelRequest cancelRequest) |
| | | { |
| | | return bind.cancel(cancelRequest); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void disconnectClient(DisconnectReason disconnectReason, |
| | | boolean sendNotification, String message, int messageID) |
| | | { |
| | | bind.disconnectClient(disconnectReason, sendNotification, message, |
| | | messageID); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public boolean dontSynchronize() |
| | | { |
| | | return bind.dontSynchronize(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public StringBuilder getAdditionalLogMessage() |
| | | { |
| | | return bind.getAdditionalLogMessage(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public Object getAttachment(String name) |
| | | { |
| | | return bind.getAttachment(name); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public Map<String, Object> getAttachments() |
| | | { |
| | | return bind.getAttachments(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public AuthenticationInfo getAuthenticationInfo() |
| | | { |
| | | return bind.getAuthenticationInfo(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public AuthenticationType getAuthenticationType() |
| | | { |
| | | return bind.getAuthenticationType(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public int getAuthFailureID() |
| | | { |
| | | return bind.getAuthFailureID(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public String getAuthFailureReason() |
| | | { |
| | | return bind.getAuthFailureReason(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public DN getAuthorizationDN() |
| | | { |
| | | return bind.getAuthorizationDN(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public Entry getAuthorizationEntry() |
| | | { |
| | | return bind.getAuthorizationEntry(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public DN getBindDN() |
| | | { |
| | | return bind.getBindDN(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public CancelRequest getCancelRequest() |
| | | { |
| | | return bind.getCancelRequest(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public CancelResult getCancelResult() |
| | | { |
| | | return bind.getCancelResult(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public ClientConnection getClientConnection() |
| | | { |
| | | return bind.getClientConnection(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public String[][] getCommonLogElements() |
| | | { |
| | | return bind.getCommonLogElements(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public long getConnectionID() |
| | | { |
| | | return bind.getConnectionID(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public StringBuilder getErrorMessage() |
| | | { |
| | | return bind.getErrorMessage(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public DN getMatchedDN() |
| | | { |
| | | return bind.getMatchedDN(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public int getMessageID() |
| | | { |
| | | return bind.getMessageID(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public long getOperationID() |
| | | { |
| | | return bind.getOperationID(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public OperationType getOperationType() |
| | | { |
| | | return bind.getOperationType(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public long getProcessingStartTime() |
| | | { |
| | | return bind.getProcessingStartTime(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public long getProcessingStopTime() |
| | | { |
| | | return bind.getProcessingStopTime(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public long getProcessingTime() |
| | | { |
| | | return bind.getProcessingTime(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public ByteString getRawBindDN() |
| | | { |
| | | return bind.getRawBindDN(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public List<String> getReferralURLs() |
| | | { |
| | | return bind.getReferralURLs(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public List<Control> getRequestControls() |
| | | { |
| | | return bind.getRequestControls(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public String[][] getRequestLogElements() |
| | | { |
| | | return bind.getRequestLogElements(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public List<Control> getResponseControls() |
| | | { |
| | | return bind.getResponseControls(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public String[][] getResponseLogElements() |
| | | { |
| | | return bind.getResponseLogElements(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public ResultCode getResultCode() |
| | | { |
| | | return bind.getResultCode(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public Entry getSASLAuthUserEntry() |
| | | { |
| | | return bind.getSASLAuthUserEntry(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public ASN1OctetString getSASLCredentials() |
| | | { |
| | | return bind.getSASLCredentials(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public String getSASLMechanism() |
| | | { |
| | | return bind.getSASLMechanism(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public ASN1OctetString getServerSASLCredentials() |
| | | { |
| | | return bind.getServerSASLCredentials(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public ByteString getSimplePassword() |
| | | { |
| | | return bind.getSimplePassword(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public DN getUserEntryDN() |
| | | { |
| | | return bind.getUserEntryDN(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void indicateCancelled(CancelRequest cancelRequest) |
| | | { |
| | | bind.indicateCancelled(cancelRequest); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public boolean isInternalOperation() |
| | | { |
| | | return bind.isInternalOperation(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public boolean isSynchronizationOperation() |
| | | { |
| | | return bind.isSynchronizationOperation(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void operationCompleted() |
| | | { |
| | | bind.operationCompleted(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public Object removeAttachment(String name) |
| | | { |
| | | return bind.removeAttachment(name); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void removeRequestControl(Control control) |
| | | { |
| | | bind.removeRequestControl(control); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void removeResponseControl(Control control) |
| | | { |
| | | bind.removeResponseControl(control); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setAdditionalLogMessage(StringBuilder additionalLogMessage) |
| | | { |
| | | bind.setAdditionalLogMessage(additionalLogMessage); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public Object setAttachment(String name, Object value) |
| | | { |
| | | return bind.setAttachment(name, value); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setAttachments(Map<String, Object> attachments) |
| | | { |
| | | bind.setAttachments(attachments); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setAuthenticationInfo(AuthenticationInfo authInfo) |
| | | { |
| | | bind.setAuthenticationInfo(authInfo); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setAuthFailureReason(int id, String reason) |
| | | { |
| | | bind.setAuthFailureReason(id, reason); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setAuthorizationEntry(Entry authorizationEntry) |
| | | { |
| | | bind.setAuthorizationEntry(authorizationEntry); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public boolean setCancelRequest(CancelRequest cancelRequest) |
| | | { |
| | | return bind.setCancelRequest(cancelRequest); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setCancelResult(CancelResult cancelResult) |
| | | { |
| | | bind.setCancelResult(cancelResult); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setDontSynchronize(boolean dontSynchronize) |
| | | { |
| | | bind.setDontSynchronize(dontSynchronize); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setErrorMessage(StringBuilder errorMessage) |
| | | { |
| | | bind.setErrorMessage(errorMessage); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setInternalOperation(boolean isInternalOperation) |
| | | { |
| | | bind.setInternalOperation(isInternalOperation); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setMatchedDN(DN matchedDN) |
| | | { |
| | | bind.setMatchedDN(matchedDN); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setProcessingStartTime() |
| | | { |
| | | bind.setProcessingStartTime(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setProcessingStopTime() |
| | | { |
| | | bind.setProcessingStopTime(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setRawBindDN(ByteString rawBindDN) |
| | | { |
| | | bind.setRawBindDN(rawBindDN); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setReferralURLs(List<String> referralURLs) |
| | | { |
| | | bind.setReferralURLs(referralURLs); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setResponseData(DirectoryException directoryException) |
| | | { |
| | | bind.setResponseData(directoryException); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setResultCode(ResultCode resultCode) |
| | | { |
| | | bind.setResultCode(resultCode); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setSASLAuthUserEntry(Entry saslAuthUserEntry) |
| | | { |
| | | bind.setSASLAuthUserEntry(saslAuthUserEntry); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setSASLCredentials(String saslMechanism, |
| | | ASN1OctetString saslCredentials) |
| | | { |
| | | bind.setSASLCredentials(saslMechanism, saslCredentials); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setServerSASLCredentials(ASN1OctetString serverSASLCredentials) |
| | | { |
| | | bind.setServerSASLCredentials(serverSASLCredentials); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setSimplePassword(ByteString simplePassword) |
| | | { |
| | | bind.setSimplePassword(simplePassword); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setSynchronizationOperation(boolean isSynchronizationOperation) |
| | | { |
| | | bind.setSynchronizationOperation(isSynchronizationOperation); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setUserEntryDN(DN userEntryDN){ |
| | | bind.setUserEntryDN(userEntryDN); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public String toString() |
| | | { |
| | | return bind.toString(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void toString(StringBuilder buffer) |
| | | { |
| | | bind.toString(buffer); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setProtocolVersion(String protocolVersion) |
| | | { |
| | | bind.setProtocolVersion(protocolVersion); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public String getProtocolVersion() |
| | | { |
| | | return bind.getProtocolVersion(); |
| | | } |
| | | |
| | | } |
| | |
| | | import org.opends.server.controls.ProxiedAuthV1Control; |
| | | import org.opends.server.controls.ProxiedAuthV2Control; |
| | | import org.opends.server.protocols.asn1.ASN1OctetString; |
| | | import org.opends.server.types.AbstractOperation; |
| | | import org.opends.server.types.Attribute; |
| | | import org.opends.server.types.AttributeType; |
| | | import org.opends.server.types.AttributeValue; |
| | |
| | | import org.opends.server.types.Entry; |
| | | import org.opends.server.types.LDAPException; |
| | | import org.opends.server.types.LockManager; |
| | | import org.opends.server.types.Operation; |
| | | import org.opends.server.types.OperationType; |
| | | import org.opends.server.types.Privilege; |
| | | import org.opends.server.types.ResultCode; |
| | |
| | | import static org.opends.server.core.CoreConstants.*; |
| | | import static org.opends.server.loggers.AccessLogger.*; |
| | | import static org.opends.server.loggers.debug.DebugLogger.*; |
| | | |
| | | import org.opends.server.loggers.debug.DebugLogger; |
| | | import org.opends.server.loggers.debug.DebugTracer; |
| | | import org.opends.server.types.DebugLogLevel; |
| | | import static org.opends.server.messages.CoreMessages.*; |
| | |
| | | * pair. |
| | | */ |
| | | public class CompareOperation |
| | | extends Operation |
| | | extends AbstractOperation |
| | | implements PreParseCompareOperation, PreOperationCompareOperation, |
| | | PostOperationCompareOperation, PostResponseCompareOperation |
| | | { |
| | | /** |
| | | * The tracer object for the debug logger. |
| | | */ |
| | | private static final DebugTracer TRACER = getTracer(); |
| | | private static final DebugTracer TRACER = DebugLogger.getTracer(); |
| | | |
| | | // The attribute type for this compare operation. |
| | | private AttributeType attributeType; |
| | |
| | | // The set of response controls for this compare operation. |
| | | private List<Control> responseControls; |
| | | |
| | | // The time that processing started on this operation. |
| | | private long processingStartTime; |
| | | |
| | | // The time that processing ended on this operation. |
| | | private long processingStopTime; |
| | | |
| | | // The attribute type for the compare operation. |
| | | private String rawAttributeType; |
| | | |
| | |
| | | return entry; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final long getProcessingStartTime() |
| | | { |
| | | return processingStartTime; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final long getProcessingStopTime() |
| | | { |
| | | return processingStopTime; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final long getProcessingTime() |
| | | { |
| | | return (processingStopTime - processingStartTime); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | |
| | | } |
| | | |
| | | String processingTime = |
| | | String.valueOf(processingStopTime - processingStartTime); |
| | | String.valueOf(getProcessingTime()); |
| | | |
| | | return new String[][] |
| | | { |
| | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | * Performs the work of actually processing this operation. This |
| | | * should include all processing for the operation, including |
| | | * invoking plugins, logging messages, performing access control, |
| | | * managing synchronization, and any other work that might need to |
| | | * be done in the course of processing. |
| | | */ |
| | | @Override() |
| | | public final void run() |
| | | { |
| | | setResultCode(ResultCode.UNDEFINED); |
| | |
| | | |
| | | |
| | | // Start the processing timer. |
| | | processingStartTime = System.currentTimeMillis(); |
| | | setProcessingStartTime(); |
| | | |
| | | |
| | | // Check for and handle a request to cancel this operation. |
| | | if (cancelRequest != null) |
| | | { |
| | | indicateCancelled(cancelRequest); |
| | | processingStopTime = System.currentTimeMillis(); |
| | | setProcessingStopTime(); |
| | | return; |
| | | } |
| | | |
| | |
| | | int msgID = MSGID_CANCELED_BY_PREPARSE_DISCONNECT; |
| | | appendErrorMessage(getMessage(msgID)); |
| | | |
| | | processingStopTime = System.currentTimeMillis(); |
| | | setProcessingStopTime(); |
| | | |
| | | logCompareRequest(this); |
| | | logCompareResponse(this); |
| | |
| | | if (cancelRequest != null) |
| | | { |
| | | indicateCancelled(cancelRequest); |
| | | processingStopTime = System.currentTimeMillis(); |
| | | setProcessingStopTime(); |
| | | logCompareResponse(this); |
| | | pluginConfigManager.invokePostResponseComparePlugins(this); |
| | | return; |
| | |
| | | if (cancelRequest != null) |
| | | { |
| | | indicateCancelled(cancelRequest); |
| | | processingStopTime = System.currentTimeMillis(); |
| | | setProcessingStopTime(); |
| | | logCompareResponse(this); |
| | | pluginConfigManager.invokePostResponseComparePlugins(this); |
| | | return; |
| | |
| | | if (cancelRequest != null) |
| | | { |
| | | indicateCancelled(cancelRequest); |
| | | processingStopTime = System.currentTimeMillis(); |
| | | setProcessingStopTime(); |
| | | logCompareResponse(this); |
| | | pluginConfigManager.invokePostResponseComparePlugins(this); |
| | | return; |
| | |
| | | int msgID = MSGID_CANCELED_BY_PREOP_DISCONNECT; |
| | | appendErrorMessage(getMessage(msgID)); |
| | | |
| | | processingStopTime = System.currentTimeMillis(); |
| | | setProcessingStopTime(); |
| | | logCompareResponse(this); |
| | | pluginConfigManager.invokePostResponseComparePlugins(this); |
| | | return; |
| | |
| | | if (cancelRequest != null) |
| | | { |
| | | indicateCancelled(cancelRequest); |
| | | processingStopTime = System.currentTimeMillis(); |
| | | setProcessingStopTime(); |
| | | logCompareResponse(this); |
| | | pluginConfigManager.invokePostResponseComparePlugins(this); |
| | | return; |
| | |
| | | int msgID = MSGID_CANCELED_BY_POSTOP_DISCONNECT; |
| | | appendErrorMessage(getMessage(msgID)); |
| | | |
| | | processingStopTime = System.currentTimeMillis(); |
| | | setProcessingStopTime(); |
| | | logCompareResponse(this); |
| | | pluginConfigManager.invokePostResponseComparePlugins(this); |
| | | return; |
| | |
| | | |
| | | |
| | | // Stop the processing timer. |
| | | processingStopTime = System.currentTimeMillis(); |
| | | setProcessingStopTime(); |
| | | |
| | | |
| | | // Send the compare response to the client. |
| | |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | protected boolean setCancelRequest(CancelRequest cancelRequest) |
| | | public boolean setCancelRequest(CancelRequest cancelRequest) |
| | | { |
| | | this.cancelRequest = cancelRequest; |
| | | return true; |
| | |
| | | buffer.append(rawAttributeType); |
| | | buffer.append(")"); |
| | | } |
| | | |
| | | } |
| | | |
| | |
| | | import org.opends.server.api.AccessControlHandler; |
| | | import org.opends.server.api.AccessControlProvider; |
| | | import org.opends.server.config.ConfigException; |
| | | import org.opends.server.types.InitializationException; |
| | | import org.opends.server.types.Operation; |
| | | import org.opends.server.types.SearchResultEntry; |
| | | import org.opends.server.types.SearchResultReference; |
| | | import org.opends.server.types.*; |
| | | import org.opends.server.workflowelement.localbackend.*; |
| | | |
| | | /** |
| | | * This class implements a default access control provider for the |
| | |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public boolean isAllowed(AddOperation addOperation) { |
| | | public boolean isAllowed(LocalBackendAddOperation addOperation) { |
| | | |
| | | return true; |
| | | } |
| | |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public boolean isAllowed(BindOperation bindOperation) { |
| | | public boolean isAllowed(LocalBackendBindOperation bindOperation) { |
| | | |
| | | return true; |
| | | } |
| | |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public boolean isAllowed(DeleteOperation deleteOperation) { |
| | | public boolean isAllowed(LocalBackendDeleteOperation deleteOperation) { |
| | | |
| | | return true; |
| | | } |
| | |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public boolean isAllowed(ModifyOperation modifyOperation) { |
| | | public boolean isAllowed(LocalBackendModifyOperation modifyOperation) { |
| | | |
| | | return true; |
| | | } |
| | |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public boolean isAllowed(SearchOperation searchOperation) { |
| | | public boolean isAllowed(LocalBackendSearchOperation searchOperation) { |
| | | |
| | | return true; |
| | | } |
| | |
| | | */ |
| | | @Override |
| | | public SearchResultEntry filterEntry( |
| | | SearchOperation searchOperation, SearchResultEntry searchEntry) { |
| | | SearchOperation searchOperation, |
| | | SearchResultEntry searchEntry) { |
| | | |
| | | // No implementation required. |
| | | return searchEntry; |
| | |
| | | */ |
| | | package org.opends.server.core; |
| | | |
| | | |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.Iterator; |
| | | import java.util.List; |
| | | import java.util.concurrent.locks.Lock; |
| | | |
| | | import org.opends.server.api.Backend; |
| | | import org.opends.server.api.ChangeNotificationListener; |
| | | import org.opends.server.api.ClientConnection; |
| | | import org.opends.server.api.SynchronizationProvider; |
| | | import org.opends.server.api.plugin.PostOperationPluginResult; |
| | | import org.opends.server.api.plugin.PreOperationPluginResult; |
| | | import org.opends.server.api.plugin.PreParsePluginResult; |
| | | import org.opends.server.controls.LDAPAssertionRequestControl; |
| | | import org.opends.server.controls.LDAPPreReadRequestControl; |
| | | import org.opends.server.controls.LDAPPreReadResponseControl; |
| | | import org.opends.server.controls.ProxiedAuthV1Control; |
| | | import org.opends.server.controls.ProxiedAuthV2Control; |
| | | import org.opends.server.protocols.asn1.ASN1OctetString; |
| | | import org.opends.server.types.AttributeType; |
| | | import org.opends.server.types.ByteString; |
| | | import org.opends.server.types.CancelledOperationException; |
| | | import org.opends.server.types.CancelRequest; |
| | | import org.opends.server.types.CancelResult; |
| | | import org.opends.server.types.Control; |
| | | import org.opends.server.types.DirectoryException; |
| | | import org.opends.server.types.DisconnectReason; |
| | | import org.opends.server.types.DN; |
| | | import org.opends.server.types.Entry; |
| | | import org.opends.server.types.ErrorLogCategory; |
| | | import org.opends.server.types.ErrorLogSeverity; |
| | | import org.opends.server.types.LDAPException; |
| | | import org.opends.server.types.LockManager; |
| | | import org.opends.server.types.Operation; |
| | | import org.opends.server.types.OperationType; |
| | | import org.opends.server.types.Privilege; |
| | | import org.opends.server.types.ResultCode; |
| | | import org.opends.server.types.SearchFilter; |
| | | import org.opends.server.types.SearchResultEntry; |
| | | import org.opends.server.types.SynchronizationProviderResult; |
| | | import org.opends.server.types.operation.PostOperationDeleteOperation; |
| | | import org.opends.server.types.operation.PostResponseDeleteOperation; |
| | | import org.opends.server.types.operation.PreOperationDeleteOperation; |
| | | import org.opends.server.types.operation.PreParseDeleteOperation; |
| | | |
| | | import static org.opends.server.core.CoreConstants.*; |
| | | import static org.opends.server.loggers.AccessLogger.*; |
| | | import org.opends.server.types.DebugLogLevel; |
| | | import static org.opends.server.loggers.ErrorLogger.*; |
| | | import static org.opends.server.loggers.debug.DebugLogger.*; |
| | | import org.opends.server.loggers.debug.DebugTracer; |
| | | import static org.opends.server.messages.CoreMessages.*; |
| | | import static org.opends.server.messages.MessageHandler.*; |
| | | import static org.opends.server.util.ServerConstants.*; |
| | | import static org.opends.server.util.StaticUtils.*; |
| | | |
| | | |
| | | |
| | | /** |
| | | * This class defines an operation that may be used to remove an entry from the |
| | | * Directory Server. |
| | | * This interface defines an operation that may be used to remove an entry from |
| | | * the Directory Server. |
| | | */ |
| | | public class DeleteOperation |
| | | extends Operation |
| | | implements PreParseDeleteOperation, PreOperationDeleteOperation, |
| | | PostOperationDeleteOperation, PostResponseDeleteOperation |
| | | public interface DeleteOperation extends Operation |
| | | { |
| | | /** |
| | | * The tracer object for the debug logger. |
| | | */ |
| | | private static final DebugTracer TRACER = getTracer(); |
| | | |
| | | |
| | | |
| | | |
| | | // The raw, unprocessed entry DN as included in the client request. |
| | | private ByteString rawEntryDN; |
| | | |
| | | // The cancel request that has been issued for this delete operation. |
| | | private CancelRequest cancelRequest; |
| | | |
| | | // The DN of the entry for the delete operation. |
| | | private DN entryDN; |
| | | |
| | | // The proxied authorization target DN for this operation. |
| | | private DN proxiedAuthorizationDN; |
| | | |
| | | // The entry to be deleted. |
| | | private Entry entry; |
| | | |
| | | // The set of response controls for this delete operation. |
| | | private List<Control> responseControls; |
| | | |
| | | // The change number that has been assigned to this operation. |
| | | private long changeNumber; |
| | | |
| | | // The time that processing started on this operation. |
| | | private long processingStartTime; |
| | | |
| | | // The time that processing ended on this operation. |
| | | private long processingStopTime; |
| | | |
| | | |
| | | |
| | | /** |
| | | * Creates a new delete operation with the provided information. |
| | | * |
| | | * @param clientConnection The client connection with which this operation |
| | | * is associated. |
| | | * @param operationID The operation ID for this operation. |
| | | * @param messageID The message ID of the request with which this |
| | | * operation is associated. |
| | | * @param requestControls The set of controls included in the request. |
| | | * @param rawEntryDN The raw, unprocessed DN of the entry to delete, |
| | | * as included in the client request. |
| | | */ |
| | | public DeleteOperation(ClientConnection clientConnection, long operationID, |
| | | int messageID, List<Control> requestControls, |
| | | ByteString rawEntryDN) |
| | | { |
| | | super(clientConnection, operationID, messageID, requestControls); |
| | | |
| | | |
| | | this.rawEntryDN = rawEntryDN; |
| | | |
| | | entry = null; |
| | | entryDN = null; |
| | | responseControls = new ArrayList<Control>(); |
| | | cancelRequest = null; |
| | | changeNumber = -1; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Creates a new delete operation with the provided information. |
| | | * |
| | | * @param clientConnection The client connection with which this operation |
| | | * is associated. |
| | | * @param operationID The operation ID for this operation. |
| | | * @param messageID The message ID of the request with which this |
| | | * operation is associated. |
| | | * @param requestControls The set of controls included in the request. |
| | | * @param entryDN The entry DN for this delete operation. |
| | | */ |
| | | public DeleteOperation(ClientConnection clientConnection, long operationID, |
| | | int messageID, List<Control> requestControls, |
| | | DN entryDN) |
| | | { |
| | | super(clientConnection, operationID, messageID, requestControls); |
| | | |
| | | |
| | | this.entryDN = entryDN; |
| | | |
| | | rawEntryDN = new ASN1OctetString(entryDN.toString()); |
| | | responseControls = new ArrayList<Control>(); |
| | | cancelRequest = null; |
| | | changeNumber = -1; |
| | | entry = null; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Retrieves the raw, unprocessed entry DN as included in the client request. |
| | |
| | | * |
| | | * @return The raw, unprocessed entry DN as included in the client request. |
| | | */ |
| | | public final ByteString getRawEntryDN() |
| | | { |
| | | return rawEntryDN; |
| | | } |
| | | |
| | | |
| | | public abstract ByteString getRawEntryDN(); |
| | | |
| | | /** |
| | | * Specifies the raw, unprocessed entry DN as included in the client request. |
| | |
| | | * @param rawEntryDN The raw, unprocessed entry DN as included in the client |
| | | * request. |
| | | */ |
| | | public final void setRawEntryDN(ByteString rawEntryDN) |
| | | { |
| | | this.rawEntryDN = rawEntryDN; |
| | | |
| | | entryDN = null; |
| | | } |
| | | |
| | | |
| | | public abstract void setRawEntryDN(ByteString rawEntryDN); |
| | | |
| | | /** |
| | | * Retrieves the DN of the entry to delete. This should not be called by |
| | |
| | | * @return The DN of the entry to delete, or <CODE>null</CODE> if the raw |
| | | * entry DN has not yet been processed. |
| | | */ |
| | | public final DN getEntryDN() |
| | | { |
| | | return entryDN; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Retrieves the entry to be deleted. This will not be available to pre-parse |
| | | * plugins. |
| | | * |
| | | * @return The entry to be deleted, or <CODE>null</CODE> if the entry is not |
| | | * yet available. |
| | | */ |
| | | public final Entry getEntryToDelete() |
| | | { |
| | | return entry; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final long getProcessingStartTime() |
| | | { |
| | | return processingStartTime; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final long getProcessingStopTime() |
| | | { |
| | | return processingStopTime; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final long getProcessingTime() |
| | | { |
| | | return (processingStopTime - processingStartTime); |
| | | } |
| | | |
| | | |
| | | public abstract DN getEntryDN(); |
| | | |
| | | /** |
| | | * Retrieves the change number that has been assigned to this operation. |
| | |
| | | * if none has been assigned yet or if there is no applicable |
| | | * synchronization mechanism in place that uses change numbers. |
| | | */ |
| | | public final long getChangeNumber() |
| | | { |
| | | return changeNumber; |
| | | } |
| | | |
| | | |
| | | public abstract long getChangeNumber(); |
| | | |
| | | /** |
| | | * Specifies the change number that has been assigned to this operation by the |
| | |
| | | * @param changeNumber The change number that has been assigned to this |
| | | * operation by the synchronization mechanism. |
| | | */ |
| | | public final void setChangeNumber(long changeNumber) |
| | | { |
| | | this.changeNumber = changeNumber; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final OperationType getOperationType() |
| | | { |
| | | // Note that no debugging will be done in this method because it is a likely |
| | | // candidate for being called by the logging subsystem. |
| | | |
| | | return OperationType.DELETE; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final void disconnectClient(DisconnectReason disconnectReason, |
| | | boolean sendNotification, String message, |
| | | int messageID) |
| | | { |
| | | // Before calling clientConnection.disconnect, we need to mark this |
| | | // operation as cancelled so that the attempt to cancel it later won't cause |
| | | // an unnecessary delay. |
| | | setCancelResult(CancelResult.CANCELED); |
| | | |
| | | clientConnection.disconnect(disconnectReason, sendNotification, message, |
| | | messageID); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final String[][] getRequestLogElements() |
| | | { |
| | | // Note that no debugging will be done in this method because it is a likely |
| | | // candidate for being called by the logging subsystem. |
| | | |
| | | return new String[][] |
| | | { |
| | | new String[] { LOG_ELEMENT_ENTRY_DN, String.valueOf(rawEntryDN) } |
| | | }; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final String[][] getResponseLogElements() |
| | | { |
| | | // Note that no debugging will be done in this method because it is a likely |
| | | // candidate for being called by the logging subsystem. |
| | | |
| | | String resultCode = String.valueOf(getResultCode().getIntValue()); |
| | | |
| | | String errorMessage; |
| | | StringBuilder errorMessageBuffer = getErrorMessage(); |
| | | if (errorMessageBuffer == null) |
| | | { |
| | | errorMessage = null; |
| | | } |
| | | else |
| | | { |
| | | errorMessage = errorMessageBuffer.toString(); |
| | | } |
| | | |
| | | String matchedDNStr; |
| | | DN matchedDN = getMatchedDN(); |
| | | if (matchedDN == null) |
| | | { |
| | | matchedDNStr = null; |
| | | } |
| | | else |
| | | { |
| | | matchedDNStr = matchedDN.toString(); |
| | | } |
| | | |
| | | String referrals; |
| | | List<String> referralURLs = getReferralURLs(); |
| | | if ((referralURLs == null) || referralURLs.isEmpty()) |
| | | { |
| | | referrals = null; |
| | | } |
| | | else |
| | | { |
| | | StringBuilder buffer = new StringBuilder(); |
| | | Iterator<String> iterator = referralURLs.iterator(); |
| | | buffer.append(iterator.next()); |
| | | |
| | | while (iterator.hasNext()) |
| | | { |
| | | buffer.append(", "); |
| | | buffer.append(iterator.next()); |
| | | } |
| | | |
| | | referrals = buffer.toString(); |
| | | } |
| | | |
| | | String processingTime = |
| | | String.valueOf(processingStopTime - processingStartTime); |
| | | |
| | | return new String[][] |
| | | { |
| | | new String[] { LOG_ELEMENT_RESULT_CODE, resultCode }, |
| | | new String[] { LOG_ELEMENT_ERROR_MESSAGE, errorMessage }, |
| | | new String[] { LOG_ELEMENT_MATCHED_DN, matchedDNStr }, |
| | | new String[] { LOG_ELEMENT_REFERRAL_URLS, referrals }, |
| | | new String[] { LOG_ELEMENT_PROCESSING_TIME, processingTime } |
| | | }; |
| | | } |
| | | |
| | | |
| | | public abstract void setChangeNumber(long changeNumber); |
| | | |
| | | /** |
| | | * Retrieves the proxied authorization DN for this operation if proxied |
| | |
| | | * authorization has been requested, or {@code null} if proxied |
| | | * authorization has not been requested. |
| | | */ |
| | | public DN getProxiedAuthorizationDN() |
| | | { |
| | | return proxiedAuthorizationDN; |
| | | } |
| | | |
| | | |
| | | public abstract DN getProxiedAuthorizationDN(); |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | * Set the proxied authorization DN for this operation if proxied |
| | | * authorization has been requested. |
| | | * |
| | | * @param proxiedAuthorizationDN |
| | | * The proxied authorization DN for this operation if proxied |
| | | * authorization has been requested, or {@code null} if proxied |
| | | * authorization has not been requested. |
| | | */ |
| | | @Override() |
| | | public final List<Control> getResponseControls() |
| | | { |
| | | return responseControls; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final void addResponseControl(Control control) |
| | | { |
| | | responseControls.add(control); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final void removeResponseControl(Control control) |
| | | { |
| | | responseControls.remove(control); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final void run() |
| | | { |
| | | setResultCode(ResultCode.UNDEFINED); |
| | | |
| | | |
| | | // Get the plugin config manager that will be used for invoking plugins. |
| | | PluginConfigManager pluginConfigManager = |
| | | DirectoryServer.getPluginConfigManager(); |
| | | boolean skipPostOperation = false; |
| | | |
| | | |
| | | // Start the processing timer. |
| | | processingStartTime = System.currentTimeMillis(); |
| | | |
| | | |
| | | // Check for and handle a request to cancel this operation. |
| | | if (cancelRequest != null) |
| | | { |
| | | indicateCancelled(cancelRequest); |
| | | processingStopTime = System.currentTimeMillis(); |
| | | return; |
| | | } |
| | | |
| | | |
| | | // Create a labeled block of code that we can break out of if a problem is |
| | | // detected. |
| | | deleteProcessing: |
| | | { |
| | | // Invoke the pre-parse delete plugins. |
| | | PreParsePluginResult preParseResult = |
| | | pluginConfigManager.invokePreParseDeletePlugins(this); |
| | | if (preParseResult.connectionTerminated()) |
| | | { |
| | | // There's no point in continuing with anything. Log the request and |
| | | // result and return. |
| | | setResultCode(ResultCode.CANCELED); |
| | | |
| | | int msgID = MSGID_CANCELED_BY_PREPARSE_DISCONNECT; |
| | | appendErrorMessage(getMessage(msgID)); |
| | | |
| | | processingStopTime = System.currentTimeMillis(); |
| | | |
| | | logDeleteRequest(this); |
| | | logDeleteResponse(this); |
| | | pluginConfigManager.invokePostResponseDeletePlugins(this); |
| | | return; |
| | | } |
| | | else if (preParseResult.sendResponseImmediately()) |
| | | { |
| | | skipPostOperation = true; |
| | | logDeleteRequest(this); |
| | | break deleteProcessing; |
| | | } |
| | | else if (preParseResult.skipCoreProcessing()) |
| | | { |
| | | skipPostOperation = false; |
| | | break deleteProcessing; |
| | | } |
| | | |
| | | |
| | | // Log the delete request message. |
| | | logDeleteRequest(this); |
| | | |
| | | |
| | | // Check for and handle a request to cancel this operation. |
| | | if (cancelRequest != null) |
| | | { |
| | | indicateCancelled(cancelRequest); |
| | | processingStopTime = System.currentTimeMillis(); |
| | | logDeleteResponse(this); |
| | | pluginConfigManager.invokePostResponseDeletePlugins(this); |
| | | return; |
| | | } |
| | | |
| | | |
| | | // Process the entry DN to convert it from its raw form as provided by the |
| | | // client to the form required for the rest of the delete processing. |
| | | try |
| | | { |
| | | if (entryDN == null) |
| | | { |
| | | entryDN = DN.decode(rawEntryDN); |
| | | } |
| | | } |
| | | catch (DirectoryException de) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, de); |
| | | } |
| | | |
| | | setResultCode(de.getResultCode()); |
| | | appendErrorMessage(de.getErrorMessage()); |
| | | setMatchedDN(de.getMatchedDN()); |
| | | setReferralURLs(de.getReferralURLs()); |
| | | |
| | | break deleteProcessing; |
| | | } |
| | | |
| | | |
| | | // Grab a write lock on the entry. |
| | | Lock entryLock = null; |
| | | for (int i=0; i < 3; i++) |
| | | { |
| | | entryLock = LockManager.lockWrite(entryDN); |
| | | if (entryLock != null) |
| | | { |
| | | break; |
| | | } |
| | | } |
| | | |
| | | if (entryLock == null) |
| | | { |
| | | setResultCode(DirectoryServer.getServerErrorResultCode()); |
| | | appendErrorMessage(getMessage(MSGID_DELETE_CANNOT_LOCK_ENTRY, |
| | | String.valueOf(entryDN))); |
| | | break deleteProcessing; |
| | | } |
| | | |
| | | |
| | | try |
| | | { |
| | | // Get the entry to delete. If it doesn't exist, then fail. |
| | | try |
| | | { |
| | | entry = DirectoryServer.getEntry(entryDN); |
| | | if (entry == null) |
| | | { |
| | | setResultCode(ResultCode.NO_SUCH_OBJECT); |
| | | appendErrorMessage(getMessage(MSGID_DELETE_NO_SUCH_ENTRY, |
| | | String.valueOf(entryDN))); |
| | | |
| | | try |
| | | { |
| | | DN parentDN = entryDN.getParentDNInSuffix(); |
| | | while (parentDN != null) |
| | | { |
| | | if (DirectoryServer.entryExists(parentDN)) |
| | | { |
| | | setMatchedDN(parentDN); |
| | | break; |
| | | } |
| | | |
| | | parentDN = parentDN.getParentDNInSuffix(); |
| | | } |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | } |
| | | |
| | | break deleteProcessing; |
| | | } |
| | | } |
| | | catch (DirectoryException de) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, de); |
| | | } |
| | | |
| | | setResultCode(de.getResultCode()); |
| | | appendErrorMessage(de.getErrorMessage()); |
| | | setMatchedDN(de.getMatchedDN()); |
| | | setReferralURLs(de.getReferralURLs()); |
| | | break deleteProcessing; |
| | | } |
| | | |
| | | |
| | | // Invoke any conflict resolution processing that might be needed by the |
| | | // synchronization provider. |
| | | for (SynchronizationProvider provider : |
| | | DirectoryServer.getSynchronizationProviders()) |
| | | { |
| | | try |
| | | { |
| | | SynchronizationProviderResult result = |
| | | provider.handleConflictResolution(this); |
| | | if (! result.continueOperationProcessing()) |
| | | { |
| | | break deleteProcessing; |
| | | } |
| | | } |
| | | catch (DirectoryException de) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, de); |
| | | } |
| | | |
| | | logError(ErrorLogCategory.SYNCHRONIZATION, |
| | | ErrorLogSeverity.SEVERE_ERROR, |
| | | MSGID_DELETE_SYNCH_CONFLICT_RESOLUTION_FAILED, |
| | | getConnectionID(), getOperationID(), |
| | | getExceptionMessage(de)); |
| | | |
| | | setResponseData(de); |
| | | break deleteProcessing; |
| | | } |
| | | } |
| | | |
| | | // Check to see if the client has permission to perform the |
| | | // delete. |
| | | |
| | | // Check to see if there are any controls in the request. If so, then |
| | | // see if there is any special processing required. |
| | | boolean noOp = false; |
| | | LDAPPreReadRequestControl preReadRequest = null; |
| | | 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 (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); |
| | | } |
| | | |
| | | setResultCode(ResultCode.valueOf(le.getResultCode())); |
| | | appendErrorMessage(le.getMessage()); |
| | | |
| | | break deleteProcessing; |
| | | } |
| | | } |
| | | |
| | | try |
| | | { |
| | | // FIXME -- We need to determine whether the current user has |
| | | // permission to make this determination. |
| | | SearchFilter filter = assertControl.getSearchFilter(); |
| | | if (! filter.matchesEntry(entry)) |
| | | { |
| | | setResultCode(ResultCode.ASSERTION_FAILED); |
| | | |
| | | appendErrorMessage(getMessage(MSGID_DELETE_ASSERTION_FAILED, |
| | | String.valueOf(entryDN))); |
| | | |
| | | break deleteProcessing; |
| | | } |
| | | } |
| | | catch (DirectoryException de) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, de); |
| | | } |
| | | |
| | | setResultCode(ResultCode.PROTOCOL_ERROR); |
| | | |
| | | int msgID = MSGID_DELETE_CANNOT_PROCESS_ASSERTION_FILTER; |
| | | appendErrorMessage(getMessage(msgID, String.valueOf(entryDN), |
| | | de.getErrorMessage())); |
| | | |
| | | break deleteProcessing; |
| | | } |
| | | } |
| | | 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); |
| | | } |
| | | |
| | | setResultCode(ResultCode.valueOf(le.getResultCode())); |
| | | appendErrorMessage(le.getMessage()); |
| | | |
| | | break deleteProcessing; |
| | | } |
| | | } |
| | | } |
| | | else if (oid.equals(OID_PROXIED_AUTH_V1)) |
| | | { |
| | | // The requester must have the PROXIED_AUTH privilige in order to |
| | | // be able to use this control. |
| | | if (! clientConnection.hasPrivilege(Privilege.PROXIED_AUTH, this)) |
| | | { |
| | | int msgID = MSGID_PROXYAUTH_INSUFFICIENT_PRIVILEGES; |
| | | appendErrorMessage(getMessage(msgID)); |
| | | setResultCode(ResultCode.AUTHORIZATION_DENIED); |
| | | break deleteProcessing; |
| | | } |
| | | |
| | | |
| | | ProxiedAuthV1Control proxyControl; |
| | | if (c instanceof ProxiedAuthV1Control) |
| | | { |
| | | proxyControl = (ProxiedAuthV1Control) c; |
| | | } |
| | | else |
| | | { |
| | | try |
| | | { |
| | | proxyControl = ProxiedAuthV1Control.decodeControl(c); |
| | | } |
| | | catch (LDAPException le) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, le); |
| | | } |
| | | |
| | | setResultCode(ResultCode.valueOf(le.getResultCode())); |
| | | appendErrorMessage(le.getMessage()); |
| | | |
| | | break deleteProcessing; |
| | | } |
| | | } |
| | | |
| | | |
| | | Entry authorizationEntry; |
| | | try |
| | | { |
| | | authorizationEntry = proxyControl.getAuthorizationEntry(); |
| | | } |
| | | catch (DirectoryException de) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, de); |
| | | } |
| | | |
| | | setResultCode(de.getResultCode()); |
| | | appendErrorMessage(de.getErrorMessage()); |
| | | |
| | | break deleteProcessing; |
| | | } |
| | | |
| | | if (AccessControlConfigManager.getInstance() |
| | | .getAccessControlHandler().isProxiedAuthAllowed(this, |
| | | authorizationEntry) == false) { |
| | | setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS); |
| | | |
| | | int msgID = MSGID_DELETE_AUTHZ_INSUFFICIENT_ACCESS_RIGHTS; |
| | | appendErrorMessage(getMessage(msgID, String.valueOf(entryDN))); |
| | | |
| | | skipPostOperation = true; |
| | | break deleteProcessing; |
| | | } |
| | | setAuthorizationEntry(authorizationEntry); |
| | | if (authorizationEntry == null) |
| | | { |
| | | proxiedAuthorizationDN = DN.nullDN(); |
| | | } |
| | | else |
| | | { |
| | | proxiedAuthorizationDN = authorizationEntry.getDN(); |
| | | } |
| | | } |
| | | else if (oid.equals(OID_PROXIED_AUTH_V2)) |
| | | { |
| | | // The requester must have the PROXIED_AUTH privilige in order to |
| | | // be able to use this control. |
| | | if (! clientConnection.hasPrivilege(Privilege.PROXIED_AUTH, this)) |
| | | { |
| | | int msgID = MSGID_PROXYAUTH_INSUFFICIENT_PRIVILEGES; |
| | | appendErrorMessage(getMessage(msgID)); |
| | | setResultCode(ResultCode.AUTHORIZATION_DENIED); |
| | | break deleteProcessing; |
| | | } |
| | | |
| | | |
| | | ProxiedAuthV2Control proxyControl; |
| | | if (c instanceof ProxiedAuthV2Control) |
| | | { |
| | | proxyControl = (ProxiedAuthV2Control) c; |
| | | } |
| | | else |
| | | { |
| | | try |
| | | { |
| | | proxyControl = ProxiedAuthV2Control.decodeControl(c); |
| | | } |
| | | catch (LDAPException le) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, le); |
| | | } |
| | | |
| | | setResultCode(ResultCode.valueOf(le.getResultCode())); |
| | | appendErrorMessage(le.getMessage()); |
| | | |
| | | break deleteProcessing; |
| | | } |
| | | } |
| | | |
| | | |
| | | Entry authorizationEntry; |
| | | try |
| | | { |
| | | authorizationEntry = proxyControl.getAuthorizationEntry(); |
| | | } |
| | | catch (DirectoryException de) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, de); |
| | | } |
| | | |
| | | setResultCode(de.getResultCode()); |
| | | appendErrorMessage(de.getErrorMessage()); |
| | | |
| | | break deleteProcessing; |
| | | } |
| | | if (AccessControlConfigManager.getInstance() |
| | | .getAccessControlHandler().isProxiedAuthAllowed(this, |
| | | authorizationEntry) == false) { |
| | | setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS); |
| | | |
| | | int msgID = MSGID_DELETE_AUTHZ_INSUFFICIENT_ACCESS_RIGHTS; |
| | | appendErrorMessage(getMessage(msgID, String.valueOf(entryDN))); |
| | | |
| | | skipPostOperation = true; |
| | | break deleteProcessing; |
| | | } |
| | | |
| | | setAuthorizationEntry(authorizationEntry); |
| | | if (authorizationEntry == null) |
| | | { |
| | | proxiedAuthorizationDN = DN.nullDN(); |
| | | } |
| | | else |
| | | { |
| | | proxiedAuthorizationDN = authorizationEntry.getDN(); |
| | | } |
| | | } |
| | | |
| | | // NYI -- Add support for additional controls. |
| | | else if (c.isCritical()) |
| | | { |
| | | Backend backend = DirectoryServer.getBackend(entryDN); |
| | | if ((backend == null) || (! backend.supportsControl(oid))) |
| | | { |
| | | setResultCode(ResultCode.UNAVAILABLE_CRITICAL_EXTENSION); |
| | | |
| | | int msgID = MSGID_DELETE_UNSUPPORTED_CRITICAL_CONTROL; |
| | | appendErrorMessage(getMessage(msgID, String.valueOf(entryDN), |
| | | oid)); |
| | | |
| | | break deleteProcessing; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | // FIXME: for now assume that this will check all permission |
| | | // pertinent to the operation. This includes proxy authorization |
| | | // and any other controls specified. |
| | | |
| | | // FIXME: earlier checks to see if the entry already exists may |
| | | // have already exposed sensitive information to the client. |
| | | if (AccessControlConfigManager.getInstance() |
| | | .getAccessControlHandler().isAllowed(this) == false) { |
| | | setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS); |
| | | |
| | | int msgID = MSGID_DELETE_AUTHZ_INSUFFICIENT_ACCESS_RIGHTS; |
| | | appendErrorMessage(getMessage(msgID, String.valueOf(entryDN))); |
| | | |
| | | skipPostOperation = true; |
| | | break deleteProcessing; |
| | | } |
| | | |
| | | // Check for and handle a request to cancel this operation. |
| | | if (cancelRequest != null) |
| | | { |
| | | indicateCancelled(cancelRequest); |
| | | processingStopTime = System.currentTimeMillis(); |
| | | logDeleteResponse(this); |
| | | pluginConfigManager.invokePostResponseDeletePlugins(this); |
| | | return; |
| | | } |
| | | |
| | | |
| | | // If the operation is not a synchronization operation, |
| | | // invoke the pre-delete plugins. |
| | | if (!isSynchronizationOperation()) |
| | | { |
| | | PreOperationPluginResult preOpResult = |
| | | pluginConfigManager.invokePreOperationDeletePlugins(this); |
| | | if (preOpResult.connectionTerminated()) |
| | | { |
| | | // There's no point in continuing with anything. Log the request |
| | | // and result and return. |
| | | setResultCode(ResultCode.CANCELED); |
| | | |
| | | int msgID = MSGID_CANCELED_BY_PREOP_DISCONNECT; |
| | | appendErrorMessage(getMessage(msgID)); |
| | | |
| | | processingStopTime = System.currentTimeMillis(); |
| | | logDeleteResponse(this); |
| | | pluginConfigManager.invokePostResponseDeletePlugins(this); |
| | | return; |
| | | } |
| | | else if (preOpResult.sendResponseImmediately()) |
| | | { |
| | | skipPostOperation = true; |
| | | break deleteProcessing; |
| | | } |
| | | else if (preOpResult.skipCoreProcessing()) |
| | | { |
| | | skipPostOperation = false; |
| | | break deleteProcessing; |
| | | } |
| | | } |
| | | |
| | | |
| | | // Check for and handle a request to cancel this operation. |
| | | if (cancelRequest != null) |
| | | { |
| | | indicateCancelled(cancelRequest); |
| | | processingStopTime = System.currentTimeMillis(); |
| | | logDeleteResponse(this); |
| | | pluginConfigManager.invokePostResponseDeletePlugins(this); |
| | | return; |
| | | } |
| | | |
| | | |
| | | // Get the backend to use for the delete. If there is none, then fail. |
| | | Backend backend = DirectoryServer.getBackend(entryDN); |
| | | if (backend == null) |
| | | { |
| | | setResultCode(ResultCode.NO_SUCH_OBJECT); |
| | | appendErrorMessage(getMessage(MSGID_DELETE_NO_SUCH_ENTRY, |
| | | String.valueOf(entryDN))); |
| | | break deleteProcessing; |
| | | } |
| | | |
| | | |
| | | // If it is not a private backend, then check to see if the server or |
| | | // backend is operating in read-only mode. |
| | | if (! backend.isPrivateBackend()) |
| | | { |
| | | switch (DirectoryServer.getWritabilityMode()) |
| | | { |
| | | case DISABLED: |
| | | setResultCode(ResultCode.UNWILLING_TO_PERFORM); |
| | | appendErrorMessage(getMessage(MSGID_DELETE_SERVER_READONLY, |
| | | String.valueOf(entryDN))); |
| | | break deleteProcessing; |
| | | |
| | | case INTERNAL_ONLY: |
| | | if (! (isInternalOperation() || isSynchronizationOperation())) |
| | | { |
| | | setResultCode(ResultCode.UNWILLING_TO_PERFORM); |
| | | appendErrorMessage(getMessage(MSGID_DELETE_SERVER_READONLY, |
| | | String.valueOf(entryDN))); |
| | | break deleteProcessing; |
| | | } |
| | | } |
| | | |
| | | switch (backend.getWritabilityMode()) |
| | | { |
| | | case DISABLED: |
| | | setResultCode(ResultCode.UNWILLING_TO_PERFORM); |
| | | appendErrorMessage(getMessage(MSGID_DELETE_BACKEND_READONLY, |
| | | String.valueOf(entryDN))); |
| | | break deleteProcessing; |
| | | |
| | | case INTERNAL_ONLY: |
| | | if (! (isInternalOperation() || isSynchronizationOperation())) |
| | | { |
| | | setResultCode(ResultCode.UNWILLING_TO_PERFORM); |
| | | appendErrorMessage(getMessage(MSGID_DELETE_BACKEND_READONLY, |
| | | String.valueOf(entryDN))); |
| | | break deleteProcessing; |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | // The selected backend will have the responsibility of making sure that |
| | | // the entry actually exists and does not have any children (or possibly |
| | | // handling a subtree delete). But we will need to check if there are |
| | | // any subordinate backends that should stop us from attempting the |
| | | // delete. |
| | | Backend[] subBackends = backend.getSubordinateBackends(); |
| | | for (Backend b : subBackends) |
| | | { |
| | | DN[] baseDNs = b.getBaseDNs(); |
| | | for (DN dn : baseDNs) |
| | | { |
| | | if (dn.isDescendantOf(entryDN)) |
| | | { |
| | | setResultCode(ResultCode.NOT_ALLOWED_ON_NONLEAF); |
| | | appendErrorMessage(getMessage(MSGID_DELETE_HAS_SUB_BACKEND, |
| | | String.valueOf(entryDN), |
| | | String.valueOf(dn))); |
| | | break deleteProcessing; |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | // Actually perform the delete. |
| | | try |
| | | { |
| | | if (noOp) |
| | | { |
| | | appendErrorMessage(getMessage(MSGID_DELETE_NOOP)); |
| | | |
| | | // FIXME -- We must set a result code other than SUCCESS. |
| | | } |
| | | else |
| | | { |
| | | for (SynchronizationProvider provider : |
| | | DirectoryServer.getSynchronizationProviders()) |
| | | { |
| | | try |
| | | { |
| | | SynchronizationProviderResult result = |
| | | provider.doPreOperation(this); |
| | | if (! result.continueOperationProcessing()) |
| | | { |
| | | break deleteProcessing; |
| | | } |
| | | } |
| | | catch (DirectoryException de) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, de); |
| | | } |
| | | |
| | | logError(ErrorLogCategory.SYNCHRONIZATION, |
| | | ErrorLogSeverity.SEVERE_ERROR, |
| | | MSGID_DELETE_SYNCH_PREOP_FAILED, getConnectionID(), |
| | | getOperationID(), getExceptionMessage(de)); |
| | | |
| | | setResponseData(de); |
| | | break deleteProcessing; |
| | | } |
| | | } |
| | | |
| | | backend.deleteEntry(entryDN, this); |
| | | } |
| | | |
| | | if (preReadRequest != null) |
| | | { |
| | | Entry entryCopy = entry.duplicate(true); |
| | | |
| | | if (! preReadRequest.allowsAttribute( |
| | | DirectoryServer.getObjectClassAttributeType())) |
| | | { |
| | | entryCopy.removeAttribute( |
| | | DirectoryServer.getObjectClassAttributeType()); |
| | | } |
| | | |
| | | if (! preReadRequest.returnAllUserAttributes()) |
| | | { |
| | | Iterator<AttributeType> iterator = |
| | | entryCopy.getUserAttributes().keySet().iterator(); |
| | | while (iterator.hasNext()) |
| | | { |
| | | AttributeType attrType = iterator.next(); |
| | | if (! preReadRequest.allowsAttribute(attrType)) |
| | | { |
| | | iterator.remove(); |
| | | } |
| | | } |
| | | } |
| | | |
| | | if (! preReadRequest.returnAllOperationalAttributes()) |
| | | { |
| | | Iterator<AttributeType> iterator = |
| | | entryCopy.getOperationalAttributes().keySet().iterator(); |
| | | while (iterator.hasNext()) |
| | | { |
| | | AttributeType attrType = iterator.next(); |
| | | if (! preReadRequest.allowsAttribute(attrType)) |
| | | { |
| | | iterator.remove(); |
| | | } |
| | | } |
| | | } |
| | | |
| | | // FIXME -- Check access controls on the entry to see if it should |
| | | // be returned or if any attributes need to be stripped |
| | | // out.. |
| | | SearchResultEntry searchEntry = new SearchResultEntry(entryCopy); |
| | | LDAPPreReadResponseControl responseControl = |
| | | new LDAPPreReadResponseControl(preReadRequest.getOID(), |
| | | preReadRequest.isCritical(), |
| | | searchEntry); |
| | | |
| | | responseControls.add(responseControl); |
| | | } |
| | | |
| | | setResultCode(ResultCode.SUCCESS); |
| | | } |
| | | catch (DirectoryException de) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, de); |
| | | } |
| | | |
| | | setResultCode(de.getResultCode()); |
| | | appendErrorMessage(de.getErrorMessage()); |
| | | setMatchedDN(de.getMatchedDN()); |
| | | setReferralURLs(de.getReferralURLs()); |
| | | |
| | | break deleteProcessing; |
| | | } |
| | | catch (CancelledOperationException coe) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, coe); |
| | | } |
| | | |
| | | CancelResult cancelResult = coe.getCancelResult(); |
| | | |
| | | setCancelResult(cancelResult); |
| | | setResultCode(cancelResult.getResultCode()); |
| | | |
| | | String message = coe.getMessage(); |
| | | if ((message != null) && (message.length() > 0)) |
| | | { |
| | | appendErrorMessage(message); |
| | | } |
| | | |
| | | break deleteProcessing; |
| | | } |
| | | } |
| | | finally |
| | | { |
| | | LockManager.unlock(entryDN, entryLock); |
| | | |
| | | for (SynchronizationProvider provider : |
| | | DirectoryServer.getSynchronizationProviders()) |
| | | { |
| | | try |
| | | { |
| | | provider.doPostOperation(this); |
| | | } |
| | | catch (DirectoryException de) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, de); |
| | | } |
| | | |
| | | logError(ErrorLogCategory.SYNCHRONIZATION, |
| | | ErrorLogSeverity.SEVERE_ERROR, |
| | | MSGID_DELETE_SYNCH_POSTOP_FAILED, 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 delete plugins. |
| | | if (! skipPostOperation) |
| | | { |
| | | PostOperationPluginResult postOperationResult = |
| | | pluginConfigManager.invokePostOperationDeletePlugins(this); |
| | | if (postOperationResult.connectionTerminated()) |
| | | { |
| | | setResultCode(ResultCode.CANCELED); |
| | | |
| | | int msgID = MSGID_CANCELED_BY_POSTOP_DISCONNECT; |
| | | appendErrorMessage(getMessage(msgID)); |
| | | |
| | | processingStopTime = System.currentTimeMillis(); |
| | | logDeleteResponse(this); |
| | | pluginConfigManager.invokePostResponseDeletePlugins(this); |
| | | return; |
| | | } |
| | | } |
| | | |
| | | |
| | | // Notify any change notification listeners that might be registered with |
| | | // the server. |
| | | if (getResultCode() == ResultCode.SUCCESS) |
| | | { |
| | | for (ChangeNotificationListener changeListener : |
| | | DirectoryServer.getChangeNotificationListeners()) |
| | | { |
| | | try |
| | | { |
| | | changeListener.handleDeleteOperation(this, entry); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | |
| | | int msgID = MSGID_DELETE_ERROR_NOTIFYING_CHANGE_LISTENER; |
| | | String message = getMessage(msgID, getExceptionMessage(e)); |
| | | logError(ErrorLogCategory.CORE_SERVER, ErrorLogSeverity.SEVERE_ERROR, |
| | | message, msgID); |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | // Stop the processing timer. |
| | | processingStopTime = System.currentTimeMillis(); |
| | | |
| | | |
| | | // Send the delete response to the client. |
| | | getClientConnection().sendResponse(this); |
| | | |
| | | |
| | | // Log the delete response. |
| | | logDeleteResponse(this); |
| | | |
| | | |
| | | // Notify any persistent searches that might be registered with the server. |
| | | if (getResultCode() == ResultCode.SUCCESS) |
| | | { |
| | | for (PersistentSearch persistentSearch : |
| | | DirectoryServer.getPersistentSearches()) |
| | | { |
| | | try |
| | | { |
| | | persistentSearch.processDelete(this, entry); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | |
| | | int msgID = MSGID_DELETE_ERROR_NOTIFYING_PERSISTENT_SEARCH; |
| | | String message = getMessage(msgID, String.valueOf(persistentSearch), |
| | | getExceptionMessage(e)); |
| | | logError(ErrorLogCategory.CORE_SERVER, ErrorLogSeverity.SEVERE_ERROR, |
| | | message, msgID); |
| | | |
| | | DirectoryServer.deregisterPersistentSearch(persistentSearch); |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | // Invoke the post-response delete plugins. |
| | | pluginConfigManager.invokePostResponseDeletePlugins(this); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final CancelResult cancel(CancelRequest cancelRequest) |
| | | { |
| | | this.cancelRequest = cancelRequest; |
| | | |
| | | CancelResult cancelResult = getCancelResult(); |
| | | long stopWaitingTime = System.currentTimeMillis() + 5000; |
| | | while ((cancelResult == null) && |
| | | (System.currentTimeMillis() < stopWaitingTime)) |
| | | { |
| | | try |
| | | { |
| | | Thread.sleep(50); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | } |
| | | |
| | | cancelResult = getCancelResult(); |
| | | } |
| | | |
| | | if (cancelResult == null) |
| | | { |
| | | // This can happen in some rare cases (e.g., if a client disconnects and |
| | | // there is still a lot of data to send to that client), and in this case |
| | | // we'll prevent the cancel thread from blocking for a long period of |
| | | // time. |
| | | cancelResult = CancelResult.CANNOT_CANCEL; |
| | | } |
| | | |
| | | return cancelResult; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final CancelRequest getCancelRequest() |
| | | { |
| | | return cancelRequest; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | protected boolean setCancelRequest(CancelRequest cancelRequest) |
| | | { |
| | | this.cancelRequest = cancelRequest; |
| | | return true; |
| | | } |
| | | |
| | | public abstract void setProxiedAuthorizationDN(DN proxiedAuthorizationDN); |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final void toString(StringBuilder buffer) |
| | | { |
| | | buffer.append("DeleteOperation(connID="); |
| | | buffer.append(clientConnection.getConnectionID()); |
| | | buffer.append(", opID="); |
| | | buffer.append(operationID); |
| | | buffer.append(", dn="); |
| | | buffer.append(rawEntryDN); |
| | | buffer.append(")"); |
| | | } |
| | | } |
| | | |
| New file |
| | |
| | | /* |
| | | * 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 |
| | | * trunk/opends/resource/legal-notices/OpenDS.LICENSE |
| | | * or https://OpenDS.dev.java.net/OpenDS.LICENSE. |
| | | * 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 |
| | | * trunk/opends/resource/legal-notices/OpenDS.LICENSE. 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 |
| | | * |
| | | * |
| | | * Portions Copyright 2007 Sun Microsystems, Inc. |
| | | */ |
| | | package org.opends.server.core; |
| | | |
| | | |
| | | |
| | | import static org.opends.server.core.CoreConstants.LOG_ELEMENT_ENTRY_DN; |
| | | import static org.opends.server.core.CoreConstants.LOG_ELEMENT_ERROR_MESSAGE; |
| | | import static org.opends.server.core.CoreConstants.LOG_ELEMENT_MATCHED_DN; |
| | | import static org.opends.server.core.CoreConstants.LOG_ELEMENT_PROCESSING_TIME; |
| | | import static org.opends.server.core.CoreConstants.LOG_ELEMENT_REFERRAL_URLS; |
| | | import static org.opends.server.core.CoreConstants.LOG_ELEMENT_RESULT_CODE; |
| | | import static org.opends.server.loggers.AccessLogger.logDeleteRequest; |
| | | import static org.opends.server.loggers.AccessLogger.logDeleteResponse; |
| | | import static org.opends.server.loggers.ErrorLogger.logError; |
| | | import static org.opends.server.loggers.debug.DebugLogger.debugEnabled; |
| | | import static org.opends.server.messages.CoreMessages.*; |
| | | import static org.opends.server.messages.MessageHandler.getMessage; |
| | | import static org.opends.server.util.StaticUtils.getExceptionMessage; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.Iterator; |
| | | import java.util.List; |
| | | |
| | | import org.opends.server.api.ClientConnection; |
| | | import org.opends.server.api.plugin.PreParsePluginResult; |
| | | import org.opends.server.loggers.debug.DebugLogger; |
| | | import org.opends.server.loggers.debug.DebugTracer; |
| | | import org.opends.server.protocols.asn1.ASN1OctetString; |
| | | import org.opends.server.types.AbstractOperation; |
| | | import org.opends.server.types.ByteString; |
| | | import org.opends.server.types.CancelRequest; |
| | | import org.opends.server.types.CancelResult; |
| | | import org.opends.server.types.Control; |
| | | import org.opends.server.types.DN; |
| | | import org.opends.server.types.DebugLogLevel; |
| | | import org.opends.server.types.DirectoryException; |
| | | import org.opends.server.types.DisconnectReason; |
| | | import org.opends.server.types.Entry; |
| | | import org.opends.server.types.ErrorLogCategory; |
| | | import org.opends.server.types.ErrorLogSeverity; |
| | | import org.opends.server.types.Operation; |
| | | import org.opends.server.types.OperationType; |
| | | import org.opends.server.types.ResultCode; |
| | | import org.opends.server.types.operation.PostResponseDeleteOperation; |
| | | import org.opends.server.types.operation.PreParseDeleteOperation; |
| | | import org.opends.server.workflowelement.localbackend.*; |
| | | |
| | | |
| | | |
| | | /** |
| | | * This class defines an operation that may be used to remove an entry from the |
| | | * Directory Server. |
| | | */ |
| | | public class DeleteOperationBasis |
| | | extends AbstractOperation |
| | | implements PreParseDeleteOperation, |
| | | DeleteOperation, |
| | | PostResponseDeleteOperation |
| | | { |
| | | /** |
| | | * The tracer object for the debug logger. |
| | | */ |
| | | private static final DebugTracer TRACER = DebugLogger.getTracer(); |
| | | |
| | | // The raw, unprocessed entry DN as included in the client request. |
| | | private ByteString rawEntryDN; |
| | | |
| | | // The cancel request that has been issued for this delete operation. |
| | | private CancelRequest cancelRequest; |
| | | |
| | | // The DN of the entry for the delete operation. |
| | | private DN entryDN; |
| | | |
| | | // The proxied authorization target DN for this operation. |
| | | private DN proxiedAuthorizationDN; |
| | | |
| | | // The set of response controls for this delete operation. |
| | | private List<Control> responseControls; |
| | | |
| | | // The change number that has been assigned to this operation. |
| | | private long changeNumber; |
| | | |
| | | |
| | | /** |
| | | * Creates a new delete operation with the provided information. |
| | | * |
| | | * @param clientConnection The client connection with which this operation |
| | | * is associated. |
| | | * @param operationID The operation ID for this operation. |
| | | * @param messageID The message ID of the request with which this |
| | | * operation is associated. |
| | | * @param requestControls The set of controls included in the request. |
| | | * @param rawEntryDN The raw, unprocessed DN of the entry to delete, |
| | | * as included in the client request. |
| | | */ |
| | | public DeleteOperationBasis(ClientConnection clientConnection, |
| | | long operationID, |
| | | int messageID, List<Control> requestControls, |
| | | ByteString rawEntryDN) |
| | | { |
| | | super(clientConnection, operationID, messageID, requestControls); |
| | | |
| | | |
| | | this.rawEntryDN = rawEntryDN; |
| | | |
| | | entryDN = null; |
| | | responseControls = new ArrayList<Control>(); |
| | | cancelRequest = null; |
| | | changeNumber = -1; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Creates a new delete operation with the provided information. |
| | | * |
| | | * @param clientConnection The client connection with which this operation |
| | | * is associated. |
| | | * @param operationID The operation ID for this operation. |
| | | * @param messageID The message ID of the request with which this |
| | | * operation is associated. |
| | | * @param requestControls The set of controls included in the request. |
| | | * @param entryDN The entry DN for this delete operation. |
| | | */ |
| | | public DeleteOperationBasis(ClientConnection clientConnection, |
| | | long operationID, |
| | | int messageID, List<Control> requestControls, |
| | | DN entryDN) |
| | | { |
| | | super(clientConnection, operationID, messageID, requestControls); |
| | | |
| | | |
| | | this.entryDN = entryDN; |
| | | |
| | | rawEntryDN = new ASN1OctetString(entryDN.toString()); |
| | | responseControls = new ArrayList<Control>(); |
| | | cancelRequest = null; |
| | | changeNumber = -1; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public final ByteString getRawEntryDN() |
| | | { |
| | | return rawEntryDN; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public final void setRawEntryDN(ByteString rawEntryDN) |
| | | { |
| | | this.rawEntryDN = rawEntryDN; |
| | | |
| | | entryDN = null; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public final DN getEntryDN() |
| | | { |
| | | try |
| | | { |
| | | if (entryDN == null) |
| | | { |
| | | entryDN = DN.decode(rawEntryDN); |
| | | } |
| | | } |
| | | catch (DirectoryException de) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, de); |
| | | } |
| | | |
| | | setResultCode(de.getResultCode()); |
| | | appendErrorMessage(de.getErrorMessage()); |
| | | setMatchedDN(de.getMatchedDN()); |
| | | setReferralURLs(de.getReferralURLs()); |
| | | } |
| | | |
| | | return entryDN; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public final long getChangeNumber() |
| | | { |
| | | return changeNumber; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public final void setChangeNumber(long changeNumber) |
| | | { |
| | | this.changeNumber = changeNumber; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final OperationType getOperationType() |
| | | { |
| | | // Note that no debugging will be done in this method because it is a likely |
| | | // candidate for being called by the logging subsystem. |
| | | |
| | | return OperationType.DELETE; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final void disconnectClient(DisconnectReason disconnectReason, |
| | | boolean sendNotification, String message, |
| | | int messageID) |
| | | { |
| | | // Before calling clientConnection.disconnect, we need to mark this |
| | | // operation as cancelled so that the attempt to cancel it later won't cause |
| | | // an unnecessary delay. |
| | | setCancelResult(CancelResult.CANCELED); |
| | | |
| | | clientConnection.disconnect(disconnectReason, sendNotification, message, |
| | | messageID); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final String[][] getRequestLogElements() |
| | | { |
| | | // Note that no debugging will be done in this method because it is a likely |
| | | // candidate for being called by the logging subsystem. |
| | | |
| | | return new String[][] |
| | | { |
| | | new String[] { LOG_ELEMENT_ENTRY_DN, String.valueOf(rawEntryDN) } |
| | | }; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final String[][] getResponseLogElements() |
| | | { |
| | | // Note that no debugging will be done in this method because it is a likely |
| | | // candidate for being called by the logging subsystem. |
| | | |
| | | String resultCode = String.valueOf(getResultCode().getIntValue()); |
| | | |
| | | String errorMessage; |
| | | StringBuilder errorMessageBuffer = getErrorMessage(); |
| | | if (errorMessageBuffer == null) |
| | | { |
| | | errorMessage = null; |
| | | } |
| | | else |
| | | { |
| | | errorMessage = errorMessageBuffer.toString(); |
| | | } |
| | | |
| | | String matchedDNStr; |
| | | DN matchedDN = getMatchedDN(); |
| | | if (matchedDN == null) |
| | | { |
| | | matchedDNStr = null; |
| | | } |
| | | else |
| | | { |
| | | matchedDNStr = matchedDN.toString(); |
| | | } |
| | | |
| | | String referrals; |
| | | List<String> referralURLs = getReferralURLs(); |
| | | if ((referralURLs == null) || referralURLs.isEmpty()) |
| | | { |
| | | referrals = null; |
| | | } |
| | | else |
| | | { |
| | | StringBuilder buffer = new StringBuilder(); |
| | | Iterator<String> iterator = referralURLs.iterator(); |
| | | buffer.append(iterator.next()); |
| | | |
| | | while (iterator.hasNext()) |
| | | { |
| | | buffer.append(", "); |
| | | buffer.append(iterator.next()); |
| | | } |
| | | |
| | | referrals = buffer.toString(); |
| | | } |
| | | |
| | | String processingTime = |
| | | String.valueOf(getProcessingTime()); |
| | | |
| | | return new String[][] |
| | | { |
| | | new String[] { LOG_ELEMENT_RESULT_CODE, resultCode }, |
| | | new String[] { LOG_ELEMENT_ERROR_MESSAGE, errorMessage }, |
| | | new String[] { LOG_ELEMENT_MATCHED_DN, matchedDNStr }, |
| | | new String[] { LOG_ELEMENT_REFERRAL_URLS, referrals }, |
| | | new String[] { LOG_ELEMENT_PROCESSING_TIME, processingTime } |
| | | }; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public DN getProxiedAuthorizationDN() |
| | | { |
| | | return proxiedAuthorizationDN; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final List<Control> getResponseControls() |
| | | { |
| | | return responseControls; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final void addResponseControl(Control control) |
| | | { |
| | | responseControls.add(control); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final void removeResponseControl(Control control) |
| | | { |
| | | responseControls.remove(control); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final CancelResult cancel(CancelRequest cancelRequest) |
| | | { |
| | | this.cancelRequest = cancelRequest; |
| | | |
| | | CancelResult cancelResult = getCancelResult(); |
| | | long stopWaitingTime = System.currentTimeMillis() + 5000; |
| | | while ((cancelResult == null) && |
| | | (System.currentTimeMillis() < stopWaitingTime)) |
| | | { |
| | | try |
| | | { |
| | | Thread.sleep(50); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | } |
| | | |
| | | cancelResult = getCancelResult(); |
| | | } |
| | | |
| | | if (cancelResult == null) |
| | | { |
| | | // This can happen in some rare cases (e.g., if a client disconnects and |
| | | // there is still a lot of data to send to that client), and in this case |
| | | // we'll prevent the cancel thread from blocking for a long period of |
| | | // time. |
| | | cancelResult = CancelResult.CANNOT_CANCEL; |
| | | } |
| | | |
| | | return cancelResult; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final CancelRequest getCancelRequest() |
| | | { |
| | | return cancelRequest; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public |
| | | boolean setCancelRequest(CancelRequest cancelRequest) |
| | | { |
| | | this.cancelRequest = cancelRequest; |
| | | return true; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final void toString(StringBuilder buffer) |
| | | { |
| | | buffer.append("DeleteOperation(connID="); |
| | | buffer.append(clientConnection.getConnectionID()); |
| | | buffer.append(", opID="); |
| | | buffer.append(operationID); |
| | | buffer.append(", dn="); |
| | | buffer.append(rawEntryDN); |
| | | buffer.append(")"); |
| | | } |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setProxiedAuthorizationDN(DN proxiedAuthorizationDN) |
| | | { |
| | | this.proxiedAuthorizationDN = proxiedAuthorizationDN; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public final void run() |
| | | { |
| | | setResultCode(ResultCode.UNDEFINED); |
| | | |
| | | // Get the plugin config manager that will be used for invoking plugins. |
| | | PluginConfigManager pluginConfigManager = |
| | | DirectoryServer.getPluginConfigManager(); |
| | | |
| | | // Start the processing timer. |
| | | setProcessingStartTime(); |
| | | |
| | | // Check for and handle a request to cancel this operation. |
| | | if (getCancelRequest() != null) |
| | | { |
| | | indicateCancelled(getCancelRequest()); |
| | | setProcessingStopTime(); |
| | | return; |
| | | } |
| | | |
| | | // Create a labeled block of code that we can break out of if a problem is |
| | | // detected. |
| | | deleteProcessing: |
| | | { |
| | | // Invoke the pre-parse delete plugins. |
| | | PreParsePluginResult preParseResult = |
| | | pluginConfigManager.invokePreParseDeletePlugins(this); |
| | | if (preParseResult.connectionTerminated()) |
| | | { |
| | | // There's no point in continuing with anything. Log the request and |
| | | // result and return. |
| | | setResultCode(ResultCode.CANCELED); |
| | | |
| | | int msgID = MSGID_CANCELED_BY_PREPARSE_DISCONNECT; |
| | | appendErrorMessage(getMessage(msgID)); |
| | | |
| | | setProcessingStopTime(); |
| | | |
| | | logDeleteRequest(this); |
| | | logDeleteResponse(this); |
| | | pluginConfigManager.invokePostResponseDeletePlugins(this); |
| | | return; |
| | | } |
| | | else if (preParseResult.sendResponseImmediately()) |
| | | { |
| | | logDeleteRequest(this); |
| | | break deleteProcessing; |
| | | } |
| | | else if (preParseResult.skipCoreProcessing()) |
| | | { |
| | | break deleteProcessing; |
| | | } |
| | | |
| | | |
| | | // Log the delete request message. |
| | | logDeleteRequest(this); |
| | | |
| | | |
| | | // Check for and handle a request to cancel this operation. |
| | | if (getCancelRequest() != null) |
| | | { |
| | | indicateCancelled(getCancelRequest()); |
| | | setProcessingStopTime(); |
| | | logDeleteResponse(this); |
| | | pluginConfigManager.invokePostResponseDeletePlugins(this); |
| | | return; |
| | | } |
| | | |
| | | |
| | | // Process the entry DN to convert it from its raw form as provided by the |
| | | // client to the form required for the rest of the delete processing. |
| | | DN entryDN = getEntryDN(); |
| | | if (entryDN == null){ |
| | | break deleteProcessing; |
| | | } |
| | | |
| | | |
| | | // Retrieve the network group attached to the client connection |
| | | // and get a workflow to process the operation. |
| | | NetworkGroup ng = getClientConnection().getNetworkGroup(); |
| | | Workflow workflow = ng.getWorkflowCandidate(entryDN); |
| | | if (workflow == null) |
| | | { |
| | | // We have found no workflow for the requested base DN, just return |
| | | // a no such entry result code and stop the processing. |
| | | updateOperationErrMsgAndResCode(); |
| | | break deleteProcessing; |
| | | } |
| | | workflow.execute(this); |
| | | } |
| | | |
| | | // Check for and handle a request to cancel this operation. |
| | | if ((getCancelRequest() != null) || |
| | | (getCancelResult() == CancelResult.CANCELED)) |
| | | { |
| | | if (getCancelRequest() != null){ |
| | | indicateCancelled(getCancelRequest()); |
| | | } |
| | | setProcessingStopTime(); |
| | | logDeleteResponse(this); |
| | | invokePostResponsePlugins(); |
| | | return; |
| | | } |
| | | |
| | | // Indicate that it is now too late to attempt to cancel the operation. |
| | | setCancelResult(CancelResult.TOO_LATE); |
| | | |
| | | // Stop the processing timer. |
| | | setProcessingStopTime(); |
| | | |
| | | |
| | | // Send the delete response to the client. |
| | | getClientConnection().sendResponse(this); |
| | | |
| | | |
| | | // Log the delete response. |
| | | logDeleteResponse(this); |
| | | |
| | | // Check wether there are local operations in attachments |
| | | List localOperations = |
| | | (List)getAttachment(Operation.LOCALBACKENDOPERATIONS); |
| | | if (localOperations != null && (! localOperations.isEmpty())){ |
| | | for (Object localOp : localOperations) |
| | | { |
| | | LocalBackendDeleteOperation localOperation = |
| | | (LocalBackendDeleteOperation)localOp; |
| | | // Notify any persistent searches that might be registered with the |
| | | // server. |
| | | if (getResultCode() == ResultCode.SUCCESS) |
| | | { |
| | | for (PersistentSearch persistentSearch : |
| | | DirectoryServer.getPersistentSearches()) |
| | | { |
| | | try |
| | | { |
| | | persistentSearch.processDelete(localOperation, |
| | | localOperation.getEntryToDelete()); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | |
| | | int msgID = MSGID_DELETE_ERROR_NOTIFYING_PERSISTENT_SEARCH; |
| | | String message = getMessage(msgID, |
| | | String.valueOf(persistentSearch), |
| | | getExceptionMessage(e)); |
| | | logError(ErrorLogCategory.CORE_SERVER, |
| | | ErrorLogSeverity.SEVERE_ERROR, |
| | | message, msgID); |
| | | |
| | | DirectoryServer.deregisterPersistentSearch(persistentSearch); |
| | | } |
| | | } |
| | | } |
| | | |
| | | // Invoke the post-response delete plugins. |
| | | pluginConfigManager.invokePostResponseDeletePlugins(localOperation); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Updates the error message and the result code of the operation. |
| | | * |
| | | * This method is called because no workflows were found to process |
| | | * the operation. |
| | | */ |
| | | private void updateOperationErrMsgAndResCode() |
| | | { |
| | | setResultCode(ResultCode.NO_SUCH_OBJECT); |
| | | appendErrorMessage(getMessage(MSGID_DELETE_NO_SUCH_ENTRY, |
| | | String.valueOf(getEntryDN()))); |
| | | } |
| | | |
| | | /** |
| | | * Execute the postResponseDeletePlugins. |
| | | */ |
| | | private void invokePostResponsePlugins() |
| | | { |
| | | // Get the plugin config manager that will be used for invoking plugins. |
| | | PluginConfigManager pluginConfigManager = |
| | | DirectoryServer.getPluginConfigManager(); |
| | | |
| | | // Check wether there are local operations in attachments |
| | | List localOperations = |
| | | (List)getAttachment(Operation.LOCALBACKENDOPERATIONS); |
| | | |
| | | if (localOperations != null && (! localOperations.isEmpty())){ |
| | | for (Object localOp : localOperations) |
| | | { |
| | | LocalBackendDeleteOperation localOperation = |
| | | (LocalBackendDeleteOperation)localOp; |
| | | // Invoke the post-response delete plugins. |
| | | pluginConfigManager.invokePostResponseDeletePlugins(localOperation); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | * |
| | | * This method always returns null. |
| | | */ |
| | | public Entry getEntryToDelete() { |
| | | // TODO Auto-generated method stub |
| | | return null; |
| | | } |
| | | |
| | | } |
| | | |
| New file |
| | |
| | | /* |
| | | * 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 |
| | | * trunk/opends/resource/legal-notices/OpenDS.LICENSE |
| | | * or https://OpenDS.dev.java.net/OpenDS.LICENSE. |
| | | * 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 |
| | | * trunk/opends/resource/legal-notices/OpenDS.LICENSE. 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 |
| | | * |
| | | * |
| | | * Portions Copyright 2007 Sun Microsystems, Inc. |
| | | */ |
| | | package org.opends.server.core; |
| | | |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | | import org.opends.server.api.ClientConnection; |
| | | import org.opends.server.types.ByteString; |
| | | import org.opends.server.types.CancelRequest; |
| | | import org.opends.server.types.CancelResult; |
| | | import org.opends.server.types.Control; |
| | | import org.opends.server.types.DN; |
| | | import org.opends.server.types.DirectoryException; |
| | | import org.opends.server.types.DisconnectReason; |
| | | import org.opends.server.types.Entry; |
| | | import org.opends.server.types.OperationType; |
| | | import org.opends.server.types.ResultCode; |
| | | |
| | | /** |
| | | * This abstract class wraps/decorates a given delete operation. |
| | | * This class will be extended by sub-classes to enhance the |
| | | * functionnality of the DeleteOperationBasis. |
| | | */ |
| | | public abstract class DeleteOperationWrapper implements DeleteOperation |
| | | { |
| | | DeleteOperation delete; |
| | | |
| | | /** |
| | | * Creates a new delete operation based on the provided delete operation. |
| | | * |
| | | * @param delete The delete operation to wrap |
| | | */ |
| | | public DeleteOperationWrapper(DeleteOperation delete){ |
| | | this.delete = delete; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void addRequestControl(Control control) |
| | | { |
| | | delete.addRequestControl(control); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void addResponseControl(Control control) |
| | | { |
| | | delete.addResponseControl(control); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void appendAdditionalLogMessage(String message) |
| | | { |
| | | delete.appendAdditionalLogMessage(message); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void appendErrorMessage(String message) |
| | | { |
| | | delete.appendErrorMessage(message); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public CancelResult cancel(CancelRequest cancelRequest) |
| | | { |
| | | return delete.cancel(cancelRequest); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void disconnectClient(DisconnectReason disconnectReason, |
| | | boolean sendNotification, String message, int messageID) |
| | | { |
| | | delete.disconnectClient(disconnectReason, sendNotification, |
| | | message, messageID); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public boolean dontSynchronize() |
| | | { |
| | | return delete.dontSynchronize(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public StringBuilder getAdditionalLogMessage() |
| | | { |
| | | return delete.getAdditionalLogMessage(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public Object getAttachment(String name) |
| | | { |
| | | return delete.getAttachment(name); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public Map<String, Object> getAttachments() |
| | | { |
| | | return delete.getAttachments(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public DN getAuthorizationDN() |
| | | { |
| | | return delete.getAuthorizationDN(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public Entry getAuthorizationEntry() |
| | | { |
| | | return delete.getAuthorizationEntry(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public CancelRequest getCancelRequest() |
| | | { |
| | | return delete.getCancelRequest(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public CancelResult getCancelResult() |
| | | { |
| | | return delete.getCancelResult(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public ClientConnection getClientConnection() |
| | | { |
| | | return delete.getClientConnection(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public String[][] getCommonLogElements() |
| | | { |
| | | return delete.getCommonLogElements(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public long getConnectionID() |
| | | { |
| | | return delete.getConnectionID(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public DN getEntryDN() |
| | | { |
| | | return delete.getEntryDN(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public StringBuilder getErrorMessage() |
| | | { |
| | | return delete.getErrorMessage(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public DN getMatchedDN() |
| | | { |
| | | return delete.getMatchedDN(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public int getMessageID() |
| | | { |
| | | return delete.getMessageID(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public long getOperationID() |
| | | { |
| | | return delete.getOperationID(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public OperationType getOperationType() |
| | | { |
| | | return delete.getOperationType(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public long getProcessingStartTime() |
| | | { |
| | | return delete.getProcessingStartTime(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public long getProcessingStopTime() |
| | | { |
| | | return delete.getProcessingStopTime(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public long getProcessingTime() |
| | | { |
| | | return delete.getProcessingTime(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public ByteString getRawEntryDN() |
| | | { |
| | | return delete.getRawEntryDN(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public List<String> getReferralURLs() |
| | | { |
| | | return delete.getReferralURLs(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public List<Control> getRequestControls() |
| | | { |
| | | return delete.getRequestControls(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public String[][] getRequestLogElements() |
| | | { |
| | | return delete.getRequestLogElements(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public List<Control> getResponseControls() |
| | | { |
| | | return delete.getResponseControls(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public String[][] getResponseLogElements() |
| | | { |
| | | return delete.getResponseLogElements(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public ResultCode getResultCode() |
| | | { |
| | | return delete.getResultCode(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void indicateCancelled(CancelRequest cancelRequest) |
| | | { |
| | | delete.indicateCancelled(cancelRequest); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public boolean isInternalOperation() |
| | | { |
| | | return delete.isInternalOperation(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public boolean isSynchronizationOperation() |
| | | { |
| | | return delete.isSynchronizationOperation(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void operationCompleted() |
| | | { |
| | | delete.operationCompleted(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public Object removeAttachment(String name) |
| | | { |
| | | return delete.removeAttachment(name); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void removeRequestControl(Control control) |
| | | { |
| | | delete.removeRequestControl(control); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void removeResponseControl(Control control) |
| | | { |
| | | delete.removeResponseControl(control); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setAdditionalLogMessage(StringBuilder additionalLogMessage) |
| | | { |
| | | delete.setAdditionalLogMessage(additionalLogMessage); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public Object setAttachment(String name, Object value) |
| | | { |
| | | return delete.setAttachment(name, value); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setAttachments(Map<String, Object> attachments) |
| | | { |
| | | delete.setAttachments(attachments); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setAuthorizationEntry(Entry authorizationEntry) |
| | | { |
| | | delete.setAuthorizationEntry(authorizationEntry); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public boolean setCancelRequest(CancelRequest cancelRequest) |
| | | { |
| | | return delete.setCancelRequest(cancelRequest); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setCancelResult(CancelResult cancelResult) |
| | | { |
| | | delete.setCancelResult(cancelResult); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setDontSynchronize(boolean dontSynchronize) |
| | | { |
| | | delete.setDontSynchronize(dontSynchronize); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setErrorMessage(StringBuilder errorMessage) |
| | | { |
| | | delete.setErrorMessage(errorMessage); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setInternalOperation(boolean isInternalOperation) |
| | | { |
| | | delete.setInternalOperation(isInternalOperation); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setMatchedDN(DN matchedDN) |
| | | { |
| | | delete.setMatchedDN(matchedDN); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setProcessingStartTime() |
| | | { |
| | | delete.setProcessingStartTime(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setProcessingStopTime() |
| | | { |
| | | delete.setProcessingStopTime(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setRawEntryDN(ByteString rawEntryDN) |
| | | { |
| | | delete.setRawEntryDN(rawEntryDN); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setReferralURLs(List<String> referralURLs) |
| | | { |
| | | delete.setReferralURLs(referralURLs); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setResponseData(DirectoryException directoryException) |
| | | { |
| | | delete.setResponseData(directoryException); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setResultCode(ResultCode resultCode) |
| | | { |
| | | delete.setResultCode(resultCode); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setSynchronizationOperation(boolean isSynchronizationOperation) |
| | | { |
| | | delete.setSynchronizationOperation(isSynchronizationOperation); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public final long getChangeNumber() |
| | | { |
| | | return delete.getChangeNumber(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public final void setChangeNumber(long changeNumber) |
| | | { |
| | | delete.setChangeNumber(changeNumber); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public String toString() |
| | | { |
| | | return delete.toString(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void toString(StringBuilder buffer) |
| | | { |
| | | delete.toString(buffer); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public DN getProxiedAuthorizationDN() |
| | | { |
| | | return delete.getProxiedAuthorizationDN(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setProxiedAuthorizationDN(DN proxiedAuthorizationDN) |
| | | { |
| | | delete.setProxiedAuthorizationDN(proxiedAuthorizationDN); |
| | | } |
| | | |
| | | } |
| | |
| | | import static org.opends.server.util.StaticUtils.*; |
| | | import static org.opends.server.util.Validator.*; |
| | | import com.sleepycat.je.JEVersion; |
| | | import org.opends.server.workflowelement.WorkflowElement; |
| | | import org.opends.server.workflowelement.localbackend.*; |
| | | |
| | | |
| | | |
| | |
| | | // Initialize all the backends and their associated suffixes. |
| | | initializeBackends(); |
| | | |
| | | // A first set of workflows had been created in the registerBackend |
| | | // method. We now need to complete the workflow creation for the |
| | | // backends that were not registered through the registerBackend |
| | | // method (ie. cn=config and RootDSE). |
| | | createAndRegisterRemainingWorkflows(); |
| | | |
| | | // Check for and initialize user configured entry cache if any, |
| | | // if not stick with default entry cache initialized earlier. |
| | | entryCacheConfigManager.initializeEntryCache(); |
| | |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Deregister a workflow from a network group. |
| | | * |
| | | * In the first implementation, workflows are stored in the default network |
| | | * group. |
| | | * |
| | | * @param backend the backend which is handled by the workflows to |
| | | * deregister |
| | | */ |
| | | private static void deregisterWorkflows( |
| | | Backend backend |
| | | ) |
| | | { |
| | | // Get the default network group and deregister all the workflows |
| | | // being configured for the backend (reminder: there is one worklfow |
| | | // per base DN configured in the backend). |
| | | NetworkGroup defaultNetworkGroup = NetworkGroup.getDefaultNetworkGroup(); |
| | | for (DN baseDN: backend.getBaseDNs()) |
| | | { |
| | | defaultNetworkGroup.deregisterWorkflow (baseDN); |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Create a workflow for the a backend then register the workflow |
| | | * with the appropriate network group. |
| | | * |
| | | * TODO implement the registration with the appropriate network group. |
| | | * |
| | | * @param backend the backend handled by the workflow |
| | | */ |
| | | private static void createAndRegisterWorkflows( |
| | | Backend backend |
| | | ) |
| | | { |
| | | // Create a root workflow element to encapsulate the backend |
| | | LocalBackendWorkflowElement rootWE = |
| | | new LocalBackendWorkflowElement(backend); |
| | | |
| | | // Create a worklfow for each baseDN being configured |
| | | // in the backend and register the workflow with the network groups |
| | | for (DN curBaseDN: backend.getBaseDNs()) |
| | | { |
| | | WorkflowImpl workflowImpl = new WorkflowImpl( |
| | | curBaseDN, (WorkflowElement) rootWE); |
| | | registerWorkflowInNetworkGroups(workflowImpl); |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Register a workflow with the appropriate network groups. |
| | | * |
| | | * In the first implementation, the workflow is registered with the |
| | | * default network group only. |
| | | * |
| | | * TODO implement the registration with the appropriate network group. |
| | | * |
| | | * @param workflowImpl the workflow to register |
| | | */ |
| | | private static void registerWorkflowInNetworkGroups( |
| | | WorkflowImpl workflowImpl |
| | | ) |
| | | { |
| | | // Register first the workflow with the default network group |
| | | NetworkGroup defaultNetworkGroup = NetworkGroup.getDefaultNetworkGroup(); |
| | | defaultNetworkGroup.registerWorkflow(workflowImpl); |
| | | |
| | | // Now for each network group that exposes the baseDN of the workflow |
| | | // create an instance of the workflow and register it with the network |
| | | // group. |
| | | // TODO jdemendi - we need the network group configuration to configure |
| | | // the workflows per network group. |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Create the workflows for the backends that were not registered through |
| | | * registerBackend method, nemely cn=config and RootDSE. |
| | | * |
| | | * TODO jdemendi - read the Workflow config and create them accordingly |
| | | * |
| | | * For the prototype: there is no configuration for the workflows. |
| | | * So we create one workflow per local backend, and we register it |
| | | * to the pool. |
| | | */ |
| | | private void createAndRegisterRemainingWorkflows() |
| | | { |
| | | // Create a workflow for the cn=config backend |
| | | createAndRegisterWorkflows (configHandler); |
| | | |
| | | // Create a workflows for the rootDSE backend |
| | | createAndRegisterWorkflows (rootDSEBackend); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Initializes the Directory Server group manager. |
| | |
| | | monitor.initializeMonitorProvider(null); |
| | | backend.setBackendMonitor(monitor); |
| | | registerMonitorProvider(monitor); |
| | | |
| | | // FIXME jdemendi - temporary code: create one workflow for each |
| | | // base DN being configured in the backend. We should not rely on the |
| | | // backend registration to create and register workflows because |
| | | // a workflow can be created for different type of "backend", including |
| | | // remote lDAP server. Instead, we should create the workflows using |
| | | // the administration framework. We will be doing so as soon as we |
| | | // have a configuration section for the workflows. |
| | | createAndRegisterWorkflows (backend); |
| | | } |
| | | } |
| | | } |
| | |
| | | |
| | | directoryServer.backends = newBackends; |
| | | |
| | | // Delete all the workflows registered for the backend. |
| | | // FIXME jdemendi - This task should be performed in the scope of the |
| | | // administration framework. However the administration framework |
| | | // requires a configuration section for the workflows which we don't have |
| | | // as of today. |
| | | deregisterWorkflows (backend); |
| | | |
| | | BackendMonitor monitor = backend.getBackendMonitor(); |
| | | if (monitor != null) |
| | | { |
| | |
| | | * @throws DirectoryException If a problem prevents the operation from being |
| | | * added to the queue (e.g., the queue is full). |
| | | */ |
| | | public static void enqueueRequest(Operation operation) |
| | | public static void enqueueRequest(AbstractOperation operation) |
| | | throws DirectoryException |
| | | { |
| | | // See if a bind is already in progress on the associated connection. If so |
| | |
| | | import org.opends.server.api.plugin.PreOperationPluginResult; |
| | | import org.opends.server.api.plugin.PreParsePluginResult; |
| | | import org.opends.server.protocols.asn1.ASN1OctetString; |
| | | import org.opends.server.types.AbstractOperation; |
| | | import org.opends.server.types.CancelRequest; |
| | | import org.opends.server.types.CancelResult; |
| | | import org.opends.server.types.Control; |
| | | import org.opends.server.types.DisconnectReason; |
| | | import org.opends.server.types.DN; |
| | | import org.opends.server.types.Operation; |
| | | import org.opends.server.types.OperationType; |
| | | import org.opends.server.types.ResultCode; |
| | | import org.opends.server.types.operation.PostOperationExtendedOperation; |
| | |
| | | import static org.opends.server.core.CoreConstants.*; |
| | | import static org.opends.server.loggers.AccessLogger.*; |
| | | import static org.opends.server.loggers.debug.DebugLogger.*; |
| | | |
| | | import org.opends.server.loggers.debug.DebugLogger; |
| | | import org.opends.server.loggers.debug.DebugTracer; |
| | | import org.opends.server.types.DebugLogLevel; |
| | | import static org.opends.server.messages.CoreMessages.*; |
| | |
| | | * kind of task. |
| | | */ |
| | | public class ExtendedOperation |
| | | extends Operation |
| | | extends AbstractOperation |
| | | implements PreParseExtendedOperation, PreOperationExtendedOperation, |
| | | PostOperationExtendedOperation, PostResponseExtendedOperation |
| | | { |
| | | /** |
| | | * The tracer object for the debug logger. |
| | | */ |
| | | private static final DebugTracer TRACER = getTracer(); |
| | | |
| | | |
| | | |
| | | private static final DebugTracer TRACER = DebugLogger.getTracer(); |
| | | |
| | | // The value for the request associated with this extended operation. |
| | | private ASN1OctetString requestValue; |
| | |
| | | // The set of response controls for this extended operation. |
| | | private List<Control> responseControls; |
| | | |
| | | // The time that processing started on this operation. |
| | | private long processingStartTime; |
| | | |
| | | // The time that processing ended on this operation. |
| | | private long processingStopTime; |
| | | |
| | | // The OID for the request associated with this extended operation. |
| | | private String requestOID; |
| | | |
| | |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final long getProcessingStartTime() |
| | | { |
| | | return processingStartTime; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final long getProcessingStopTime() |
| | | { |
| | | return processingStopTime; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final long getProcessingTime() |
| | | { |
| | | return (processingStopTime - processingStartTime); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | |
| | | } |
| | | |
| | | String processingTime = |
| | | String.valueOf(processingStopTime - processingStartTime); |
| | | String.valueOf(getProcessingTime()); |
| | | |
| | | return new String[][] |
| | | { |
| | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | * Performs the work of actually processing this operation. This |
| | | * should include all processing for the operation, including |
| | | * invoking plugins, logging messages, performing access control, |
| | | * managing synchronization, and any other work that might need to |
| | | * be done in the course of processing. |
| | | */ |
| | | @Override() |
| | | public final void run() |
| | | { |
| | | setResultCode(ResultCode.UNDEFINED); |
| | |
| | | |
| | | |
| | | // Start the processing timer. |
| | | processingStartTime = System.currentTimeMillis(); |
| | | setProcessingStartTime(); |
| | | |
| | | |
| | | // Check for and handle a request to cancel this operation. |
| | |
| | | requestOID.equals(OID_START_TLS_REQUEST))) |
| | | { |
| | | indicateCancelled(cancelRequest); |
| | | processingStopTime = System.currentTimeMillis(); |
| | | setProcessingStopTime(); |
| | | return; |
| | | } |
| | | } |
| | |
| | | int msgID = MSGID_CANCELED_BY_PREPARSE_DISCONNECT; |
| | | appendErrorMessage(getMessage(msgID)); |
| | | |
| | | processingStopTime = System.currentTimeMillis(); |
| | | setProcessingStopTime(); |
| | | |
| | | logExtendedRequest(this); |
| | | logExtendedResponse(this); |
| | |
| | | requestOID.equals(OID_START_TLS_REQUEST))) |
| | | { |
| | | indicateCancelled(cancelRequest); |
| | | processingStopTime = System.currentTimeMillis(); |
| | | setProcessingStopTime(); |
| | | pluginConfigManager.invokePostResponseExtendedPlugins(this); |
| | | return; |
| | | } |
| | |
| | | int msgID = MSGID_CANCELED_BY_PREOP_DISCONNECT; |
| | | appendErrorMessage(getMessage(msgID)); |
| | | |
| | | processingStopTime = System.currentTimeMillis(); |
| | | setProcessingStopTime(); |
| | | |
| | | logExtendedResponse(this); |
| | | pluginConfigManager.invokePostResponseExtendedPlugins(this); |
| | |
| | | requestOID.equals(OID_START_TLS_REQUEST))) |
| | | { |
| | | indicateCancelled(cancelRequest); |
| | | processingStopTime = System.currentTimeMillis(); |
| | | setProcessingStopTime(); |
| | | pluginConfigManager.invokePostResponseExtendedPlugins(this); |
| | | return; |
| | | } |
| | |
| | | int msgID = MSGID_CANCELED_BY_PREOP_DISCONNECT; |
| | | appendErrorMessage(getMessage(msgID)); |
| | | |
| | | processingStopTime = System.currentTimeMillis(); |
| | | setProcessingStopTime(); |
| | | |
| | | logExtendedResponse(this); |
| | | pluginConfigManager.invokePostResponseExtendedPlugins(this); |
| | |
| | | |
| | | |
| | | // Stop the processing timer. |
| | | processingStopTime = System.currentTimeMillis(); |
| | | setProcessingStopTime(); |
| | | |
| | | |
| | | // Send the response to the client, if it has not already been sent. |
| | |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | protected boolean setCancelRequest(CancelRequest cancelRequest) |
| | | public boolean setCancelRequest(CancelRequest cancelRequest) |
| | | { |
| | | this.cancelRequest = cancelRequest; |
| | | return true; |
| | |
| | | buffer.append(requestOID); |
| | | buffer.append(")"); |
| | | } |
| | | |
| | | } |
| | | |
| | |
| | | import java.util.Map; |
| | | import java.util.concurrent.ConcurrentHashMap; |
| | | |
| | | |
| | | import org.opends.server.workflowelement.localbackend.*; |
| | | import org.opends.server.admin.ClassPropertyDefinition; |
| | | import org.opends.server.admin.server.ConfigurationAddListener; |
| | | import org.opends.server.admin.server.ConfigurationChangeListener; |
| | |
| | | SearchScope.WHOLE_SUBTREE, |
| | | DereferencePolicy.NEVER_DEREF_ALIASES, |
| | | 0, 0, false, filter, null, null); |
| | | LocalBackendSearchOperation localSearch = |
| | | new LocalBackendSearchOperation(internalSearch); |
| | | try |
| | | { |
| | | backend.search(internalSearch); |
| | | backend.search(localSearch); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | |
| | | import org.opends.server.controls.ProxiedAuthV1Control; |
| | | import org.opends.server.controls.ProxiedAuthV2Control; |
| | | import org.opends.server.protocols.asn1.ASN1OctetString; |
| | | import org.opends.server.types.AbstractOperation; |
| | | import org.opends.server.types.Attribute; |
| | | import org.opends.server.types.AttributeType; |
| | | import org.opends.server.types.AttributeValue; |
| | |
| | | import org.opends.server.types.LockManager; |
| | | import org.opends.server.types.Modification; |
| | | import org.opends.server.types.ModificationType; |
| | | import org.opends.server.types.Operation; |
| | | import org.opends.server.types.OperationType; |
| | | import org.opends.server.types.Privilege; |
| | | import org.opends.server.types.RDN; |
| | |
| | | import org.opends.server.types.DebugLogLevel; |
| | | import static org.opends.server.loggers.ErrorLogger.*; |
| | | import static org.opends.server.loggers.debug.DebugLogger.*; |
| | | |
| | | import org.opends.server.loggers.debug.DebugLogger; |
| | | import org.opends.server.loggers.debug.DebugTracer; |
| | | import static org.opends.server.messages.CoreMessages.*; |
| | | import static org.opends.server.messages.MessageHandler.*; |
| | |
| | | * in the Directory Server. |
| | | */ |
| | | public class ModifyDNOperation |
| | | extends Operation |
| | | extends AbstractOperation |
| | | implements PreParseModifyDNOperation, PreOperationModifyDNOperation, |
| | | PostOperationModifyDNOperation, PostResponseModifyDNOperation |
| | | { |
| | | /** |
| | | * The tracer object for the debug logger. |
| | | */ |
| | | private static final DebugTracer TRACER = getTracer(); |
| | | private static final DebugTracer TRACER = DebugLogger.getTracer(); |
| | | |
| | | // Indicates whether to delete the old RDN value from the entry. |
| | | private boolean deleteOldRDN; |
| | |
| | | // The change number that has been assigned to this operation. |
| | | private long changeNumber; |
| | | |
| | | // The time that processing started on this operation. |
| | | private long processingStartTime; |
| | | |
| | | // The time that processing ended on this operation. |
| | | private long processingStopTime; |
| | | |
| | | // The new RDN for the entry. |
| | | private RDN newRDN; |
| | | |
| | |
| | | return newEntry; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final long getProcessingStartTime() |
| | | { |
| | | return processingStartTime; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final long getProcessingStopTime() |
| | | { |
| | | return processingStopTime; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final long getProcessingTime() |
| | | { |
| | | return (processingStopTime - processingStartTime); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Retrieves the change number that has been assigned to this operation. |
| | | * |
| | |
| | | } |
| | | |
| | | String processingTime = |
| | | String.valueOf(processingStopTime - processingStartTime); |
| | | String.valueOf(getProcessingTime()); |
| | | |
| | | return new String[][] |
| | | { |
| | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | * Performs the work of actually processing this operation. This |
| | | * should include all processing for the operation, including |
| | | * invoking plugins, logging messages, performing access control, |
| | | * managing synchronization, and any other work that might need to |
| | | * be done in the course of processing. |
| | | */ |
| | | @Override() |
| | | public final void run() |
| | | { |
| | | setResultCode(ResultCode.UNDEFINED); |
| | |
| | | |
| | | |
| | | // Start the processing timer. |
| | | processingStartTime = System.currentTimeMillis(); |
| | | setProcessingStartTime(); |
| | | |
| | | |
| | | // Check for and handle a request to cancel this operation. |
| | | if (cancelRequest != null) |
| | | { |
| | | indicateCancelled(cancelRequest); |
| | | processingStopTime = System.currentTimeMillis(); |
| | | setProcessingStopTime(); |
| | | return; |
| | | } |
| | | |
| | |
| | | int msgID = MSGID_CANCELED_BY_PREPARSE_DISCONNECT; |
| | | appendErrorMessage(getMessage(msgID)); |
| | | |
| | | processingStopTime = System.currentTimeMillis(); |
| | | setProcessingStopTime(); |
| | | |
| | | logModifyDNRequest(this); |
| | | logModifyDNResponse(this); |
| | |
| | | if (cancelRequest != null) |
| | | { |
| | | indicateCancelled(cancelRequest); |
| | | processingStopTime = System.currentTimeMillis(); |
| | | setProcessingStopTime(); |
| | | logModifyDNResponse(this); |
| | | pluginConfigManager.invokePostResponseModifyDNPlugins(this); |
| | | return; |
| | |
| | | if (cancelRequest != null) |
| | | { |
| | | indicateCancelled(cancelRequest); |
| | | processingStopTime = System.currentTimeMillis(); |
| | | setProcessingStopTime(); |
| | | logModifyDNResponse(this); |
| | | pluginConfigManager.invokePostResponseModifyDNPlugins(this); |
| | | return; |
| | |
| | | if (cancelRequest != null) |
| | | { |
| | | indicateCancelled(cancelRequest); |
| | | processingStopTime = System.currentTimeMillis(); |
| | | setProcessingStopTime(); |
| | | logModifyDNResponse(this); |
| | | pluginConfigManager.invokePostResponseModifyDNPlugins(this); |
| | | return; |
| | |
| | | if (cancelRequest != null) |
| | | { |
| | | indicateCancelled(cancelRequest); |
| | | processingStopTime = System.currentTimeMillis(); |
| | | setProcessingStopTime(); |
| | | logModifyDNResponse(this); |
| | | pluginConfigManager.invokePostResponseModifyDNPlugins(this); |
| | | return; |
| | |
| | | int msgID = MSGID_CANCELED_BY_PREOP_DISCONNECT; |
| | | appendErrorMessage(getMessage(msgID)); |
| | | |
| | | processingStopTime = System.currentTimeMillis(); |
| | | setProcessingStopTime(); |
| | | logModifyDNResponse(this); |
| | | pluginConfigManager.invokePostResponseModifyDNPlugins(this); |
| | | return; |
| | |
| | | if (cancelRequest != null) |
| | | { |
| | | indicateCancelled(cancelRequest); |
| | | processingStopTime = System.currentTimeMillis(); |
| | | setProcessingStopTime(); |
| | | logModifyDNResponse(this); |
| | | pluginConfigManager.invokePostResponseModifyDNPlugins(this); |
| | | return; |
| | |
| | | int msgID = MSGID_CANCELED_BY_POSTOP_DISCONNECT; |
| | | appendErrorMessage(getMessage(msgID)); |
| | | |
| | | processingStopTime = System.currentTimeMillis(); |
| | | setProcessingStopTime(); |
| | | logModifyDNResponse(this); |
| | | pluginConfigManager.invokePostResponseModifyDNPlugins(this); |
| | | return; |
| | |
| | | |
| | | |
| | | // Stop the processing timer. |
| | | processingStopTime = System.currentTimeMillis(); |
| | | setProcessingStopTime(); |
| | | |
| | | |
| | | // Send the modify DN response to the client. |
| | |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | protected boolean setCancelRequest(CancelRequest cancelRequest) |
| | | public boolean setCancelRequest(CancelRequest cancelRequest) |
| | | { |
| | | this.cancelRequest = cancelRequest; |
| | | return true; |
| | |
| | | |
| | | buffer.append(")"); |
| | | } |
| | | |
| | | } |
| | | |
| | |
| | | */ |
| | | package org.opends.server.core; |
| | | |
| | | |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.Arrays; |
| | | import java.util.HashSet; |
| | | import java.util.Iterator; |
| | | import java.util.LinkedHashSet; |
| | | import java.util.LinkedList; |
| | | import java.util.List; |
| | | import java.util.concurrent.locks.Lock; |
| | | |
| | | import org.opends.server.api.AttributeSyntax; |
| | | import org.opends.server.api.Backend; |
| | | import org.opends.server.api.ChangeNotificationListener; |
| | | import org.opends.server.api.ClientConnection; |
| | | import org.opends.server.api.PasswordStorageScheme; |
| | | import org.opends.server.api.SynchronizationProvider; |
| | | import org.opends.server.api.plugin.PostOperationPluginResult; |
| | | import org.opends.server.api.plugin.PreOperationPluginResult; |
| | | import org.opends.server.api.plugin.PreParsePluginResult; |
| | | import org.opends.server.controls.LDAPAssertionRequestControl; |
| | | import org.opends.server.controls.LDAPPreReadRequestControl; |
| | | import org.opends.server.controls.LDAPPreReadResponseControl; |
| | | import org.opends.server.controls.LDAPPostReadRequestControl; |
| | | import org.opends.server.controls.LDAPPostReadResponseControl; |
| | | import org.opends.server.controls.ProxiedAuthV1Control; |
| | | import org.opends.server.controls.ProxiedAuthV2Control; |
| | | import org.opends.server.protocols.asn1.ASN1OctetString; |
| | | import org.opends.server.protocols.ldap.LDAPAttribute; |
| | | import org.opends.server.protocols.ldap.LDAPModification; |
| | | import org.opends.server.schema.AuthPasswordSyntax; |
| | | import org.opends.server.schema.BooleanSyntax; |
| | | import org.opends.server.schema.UserPasswordSyntax; |
| | | import org.opends.server.types.AcceptRejectWarn; |
| | | import org.opends.server.types.AccountStatusNotificationType; |
| | | import org.opends.server.types.Attribute; |
| | | import org.opends.server.types.AttributeType; |
| | | import org.opends.server.types.AttributeValue; |
| | | import org.opends.server.types.AuthenticationInfo; |
| | | import org.opends.server.types.ByteString; |
| | | import org.opends.server.types.CancelledOperationException; |
| | | import org.opends.server.types.CancelRequest; |
| | | 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.DisconnectReason; |
| | | import org.opends.server.types.DN; |
| | | import org.opends.server.types.Entry; |
| | | import org.opends.server.types.ErrorLogCategory; |
| | | import org.opends.server.types.ErrorLogSeverity; |
| | | import org.opends.server.types.LDAPException; |
| | | import org.opends.server.types.LockManager; |
| | | import org.opends.server.types.DirectoryException; |
| | | import org.opends.server.types.Modification; |
| | | import org.opends.server.types.ModificationType; |
| | | import org.opends.server.types.Operation; |
| | | import org.opends.server.types.OperationType; |
| | | import org.opends.server.types.Privilege; |
| | | import org.opends.server.types.RawModification; |
| | | 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.PreParseModifyOperation; |
| | | import org.opends.server.types.operation.PreOperationModifyOperation; |
| | | import org.opends.server.types.operation.PostOperationModifyOperation; |
| | | import org.opends.server.types.operation.PostResponseModifyOperation; |
| | | |
| | | import static org.opends.server.config.ConfigConstants.*; |
| | | import static org.opends.server.core.CoreConstants.*; |
| | | import static org.opends.server.loggers.AccessLogger.*; |
| | | import static org.opends.server.loggers.ErrorLogger.*; |
| | | import static org.opends.server.loggers.debug.DebugLogger.*; |
| | | import org.opends.server.loggers.debug.DebugTracer; |
| | | import static org.opends.server.messages.CoreMessages.*; |
| | | import static org.opends.server.messages.MessageHandler.getMessage; |
| | | import static org.opends.server.util.ServerConstants.*; |
| | | import static org.opends.server.util.StaticUtils.*; |
| | | |
| | | |
| | | |
| | | /** |
| | | * This class defines an operation that may be used to modify an entry in the |
| | | * Directory Server. |
| | | * This interface defines an operation used to modify an entry in |
| | | * the Directory Server. |
| | | */ |
| | | public class ModifyOperation |
| | | extends Operation |
| | | implements PreParseModifyOperation, PreOperationModifyOperation, |
| | | PostOperationModifyOperation, PostResponseModifyOperation |
| | | public interface ModifyOperation extends Operation |
| | | { |
| | | /** |
| | | * The tracer object for the debug logger. |
| | | */ |
| | | private static final DebugTracer TRACER = getTracer(); |
| | | |
| | | // The raw, unprocessed entry DN as included by the client request. |
| | | private ByteString rawEntryDN; |
| | | |
| | | // The cancel request that has been issued for this modify operation. |
| | | private CancelRequest cancelRequest; |
| | | |
| | | // The DN of the entry for the modify operation. |
| | | private DN entryDN; |
| | | |
| | | // The proxied authorization target DN for this operation. |
| | | private DN proxiedAuthorizationDN; |
| | | |
| | | // The current entry, before any changes are applied. |
| | | private Entry currentEntry; |
| | | |
| | | // The modified entry that will be stored in the backend. |
| | | private Entry modifiedEntry; |
| | | |
| | | // The set of clear-text current passwords (if any were provided). |
| | | private List<AttributeValue> currentPasswords; |
| | | |
| | | // The set of clear-text new passwords (if any were provided). |
| | | private List<AttributeValue> newPasswords; |
| | | |
| | | // The set of response controls for this modify operation. |
| | | private List<Control> responseControls; |
| | | |
| | | // The raw, unprocessed set of modifications as included in the client |
| | | // request. |
| | | private List<RawModification> rawModifications; |
| | | |
| | | // The set of modifications for this modify operation. |
| | | private List<Modification> modifications; |
| | | |
| | | // The change number that has been assigned to this operation. |
| | | private long changeNumber; |
| | | |
| | | // The time that processing started on this operation. |
| | | private long processingStartTime; |
| | | |
| | | // The time that processing ended on this operation. |
| | | private long processingStopTime; |
| | | |
| | | |
| | | |
| | | /** |
| | | * Creates a new modify operation with the provided information. |
| | | * |
| | | * @param clientConnection The client connection with which this operation |
| | | * is associated. |
| | | * @param operationID The operation ID for this operation. |
| | | * @param messageID The message ID of the request with which this |
| | | * operation is associated. |
| | | * @param requestControls The set of controls included in the request. |
| | | * @param rawEntryDN The raw, unprocessed DN of the entry to modify, |
| | | * as included in the client request. |
| | | * @param rawModifications The raw, unprocessed set of modifications for |
| | | * this modify operation as included in the client |
| | | * request. |
| | | */ |
| | | public ModifyOperation(ClientConnection clientConnection, long operationID, |
| | | int messageID, List<Control> requestControls, |
| | | ByteString rawEntryDN, |
| | | List<RawModification> rawModifications) |
| | | { |
| | | super(clientConnection, operationID, messageID, requestControls); |
| | | |
| | | |
| | | this.rawEntryDN = rawEntryDN; |
| | | this.rawModifications = rawModifications; |
| | | |
| | | entryDN = null; |
| | | modifications = null; |
| | | currentEntry = null; |
| | | modifiedEntry = null; |
| | | responseControls = new ArrayList<Control>(); |
| | | cancelRequest = null; |
| | | changeNumber = -1; |
| | | |
| | | currentPasswords = null; |
| | | newPasswords = null; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Creates a new modify operation with the provided information. |
| | | * |
| | | * @param clientConnection The client connection with which this operation |
| | | * is associated. |
| | | * @param operationID The operation ID for this operation. |
| | | * @param messageID The message ID of the request with which this |
| | | * operation is associated. |
| | | * @param requestControls The set of controls included in the request. |
| | | * @param entryDN The entry DN for the modify operation. |
| | | * @param modifications The set of modifications for this modify |
| | | * operation. |
| | | */ |
| | | public ModifyOperation(ClientConnection clientConnection, long operationID, |
| | | int messageID, List<Control> requestControls, |
| | | DN entryDN, List<Modification> modifications) |
| | | { |
| | | super(clientConnection, operationID, messageID, requestControls); |
| | | |
| | | |
| | | this.entryDN = entryDN; |
| | | this.modifications = modifications; |
| | | |
| | | rawEntryDN = new ASN1OctetString(entryDN.toString()); |
| | | |
| | | rawModifications = new ArrayList<RawModification>(modifications.size()); |
| | | for (Modification m : modifications) |
| | | { |
| | | rawModifications.add(new LDAPModification(m.getModificationType(), |
| | | new LDAPAttribute(m.getAttribute()))); |
| | | } |
| | | |
| | | currentEntry = null; |
| | | modifiedEntry = null; |
| | | responseControls = new ArrayList<Control>(); |
| | | cancelRequest = null; |
| | | changeNumber = -1; |
| | | |
| | | currentPasswords = null; |
| | | newPasswords = null; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Retrieves the raw, unprocessed entry DN as included in the client request. |
| | | * The DN that is returned may or may not be a valid DN, since no validation |
| | | * will have been performed upon it. |
| | | * |
| | | * @return The raw, unprocessed entry DN as included in the client request. |
| | | */ |
| | | public final ByteString getRawEntryDN() |
| | | { |
| | | return rawEntryDN; |
| | | } |
| | | |
| | | |
| | | public abstract ByteString getRawEntryDN(); |
| | | |
| | | /** |
| | | * Specifies the raw, unprocessed entry DN as included in the client request. |
| | |
| | | * @param rawEntryDN The raw, unprocessed entry DN as included in the client |
| | | * request. |
| | | */ |
| | | public final void setRawEntryDN(ByteString rawEntryDN) |
| | | { |
| | | this.rawEntryDN = rawEntryDN; |
| | | |
| | | entryDN = null; |
| | | } |
| | | |
| | | |
| | | public abstract void setRawEntryDN(ByteString rawEntryDN); |
| | | |
| | | /** |
| | | * Retrieves the DN of the entry to modify. This should not be called by |
| | |
| | | * @return The DN of the entry to modify, or <CODE>null</CODE> if the raw |
| | | * entry DN has not yet been processed. |
| | | */ |
| | | public final DN getEntryDN() |
| | | { |
| | | return entryDN; |
| | | } |
| | | |
| | | |
| | | public abstract DN getEntryDN(); |
| | | |
| | | /** |
| | | * Retrieves the set of raw, unprocessed modifications as included in the |
| | |
| | | * @return The set of raw, unprocessed modifications as included in the |
| | | * client request. |
| | | */ |
| | | public final List<RawModification> getRawModifications() |
| | | { |
| | | return rawModifications; |
| | | } |
| | | |
| | | |
| | | public abstract List<RawModification> getRawModifications(); |
| | | |
| | | /** |
| | | * Adds the provided modification to the set of raw modifications for this |
| | |
| | | * @param rawModification The modification to add to the set of raw |
| | | * modifications for this modify operation. |
| | | */ |
| | | public final void addRawModification(RawModification rawModification) |
| | | { |
| | | rawModifications.add(rawModification); |
| | | |
| | | modifications = null; |
| | | } |
| | | |
| | | |
| | | public abstract void addRawModification(RawModification rawModification); |
| | | |
| | | /** |
| | | * Specifies the raw modifications for this modify operation. |
| | | * |
| | | * @param rawModifications The raw modifications for this modify operation. |
| | | */ |
| | | public final void setRawModifications(List<RawModification> rawModifications) |
| | | { |
| | | this.rawModifications = rawModifications; |
| | | |
| | | modifications = null; |
| | | } |
| | | |
| | | |
| | | public abstract void setRawModifications( |
| | | List<RawModification> rawModifications); |
| | | |
| | | /** |
| | | * Retrieves the set of modifications for this modify operation. Its contents |
| | |
| | | * <CODE>null</CODE> if the modifications have not yet been |
| | | * processed. |
| | | */ |
| | | public final List<Modification> getModifications() |
| | | { |
| | | return modifications; |
| | | } |
| | | |
| | | |
| | | public abstract List<Modification> getModifications(); |
| | | |
| | | /** |
| | | * Adds the provided modification to the set of modifications to this modify |
| | |
| | | * @throws DirectoryException If an unexpected problem occurs while applying |
| | | * the modification to the entry. |
| | | */ |
| | | public final void addModification(Modification modification) |
| | | throws DirectoryException |
| | | { |
| | | modifiedEntry.applyModification(modification); |
| | | modifications.add(modification); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Retrieves the current entry before any modifications are applied. This |
| | | * will not be available to pre-parse plugins. |
| | | * |
| | | * @return The current entry, or <CODE>null</CODE> if it is not yet |
| | | * available. |
| | | */ |
| | | public final Entry getCurrentEntry() |
| | | { |
| | | return currentEntry; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Retrieves the modified entry that is to be written to the backend. This |
| | | * will be available to pre-operation plugins, and if such a plugin does make |
| | | * a change to this entry, then it is also necessary to add that change to |
| | | * the set of modifications to ensure that the update will be consistent. |
| | | * |
| | | * @return The modified entry that is to be written to the backend, or |
| | | * <CODE>null</CODE> if it is not yet available. |
| | | */ |
| | | public final Entry getModifiedEntry() |
| | | { |
| | | return modifiedEntry; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Retrieves the set of clear-text current passwords for the user, if |
| | | * available. This will only be available if the modify operation contains |
| | | * one or more delete elements that target the password attribute and provide |
| | | * the values to delete in the clear. It will not be available to pre-parse |
| | | * plugins. |
| | | * |
| | | * @return The set of clear-text current password values as provided in the |
| | | * modify request, or <CODE>null</CODE> if there were none or this |
| | | * information is not yet available. |
| | | */ |
| | | public final List<AttributeValue> getCurrentPasswords() |
| | | { |
| | | return currentPasswords; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Retrieves the set of clear-text new passwords for the user, if available. |
| | | * This will only be available if the modify operation contains one or more |
| | | * add or replace elements that target the password attribute and provide the |
| | | * values in the clear. It will not be available to pre-parse plugins. |
| | | * |
| | | * @return The set of clear-text new passwords as provided in the modify |
| | | * request, or <CODE>null</CODE> if there were none or this |
| | | * information is not yet available. |
| | | */ |
| | | public final List<AttributeValue> getNewPasswords() |
| | | { |
| | | return newPasswords; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final long getProcessingStartTime() |
| | | { |
| | | return processingStartTime; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final long getProcessingStopTime() |
| | | { |
| | | return processingStopTime; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final long getProcessingTime() |
| | | { |
| | | return (processingStopTime - processingStartTime); |
| | | } |
| | | |
| | | |
| | | public abstract void addModification(Modification modification) |
| | | throws DirectoryException; |
| | | |
| | | /** |
| | | * Retrieves the change number that has been assigned to this operation. |
| | |
| | | * if none has been assigned yet or if there is no applicable |
| | | * synchronization mechanism in place that uses change numbers. |
| | | */ |
| | | public final long getChangeNumber() |
| | | { |
| | | return changeNumber; |
| | | } |
| | | |
| | | |
| | | public abstract long getChangeNumber(); |
| | | |
| | | /** |
| | | * Specifies the change number that has been assigned to this operation by the |
| | |
| | | * @param changeNumber The change number that has been assigned to this |
| | | * operation by the synchronization mechanism. |
| | | */ |
| | | public final void setChangeNumber(long changeNumber) |
| | | { |
| | | this.changeNumber = changeNumber; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final void disconnectClient(DisconnectReason disconnectReason, |
| | | boolean sendNotification, String message, |
| | | int messageID) |
| | | { |
| | | // Before calling clientConnection.disconnect, we need to mark this |
| | | // operation as cancelled so that the attempt to cancel it later won't cause |
| | | // an unnecessary delay. |
| | | setCancelResult(CancelResult.CANCELED); |
| | | |
| | | clientConnection.disconnect(disconnectReason, sendNotification, message, |
| | | messageID); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final OperationType getOperationType() |
| | | { |
| | | // Note that no debugging will be done in this method because it is a likely |
| | | // candidate for being called by the logging subsystem. |
| | | |
| | | return OperationType.MODIFY; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final String[][] getRequestLogElements() |
| | | { |
| | | // Note that no debugging will be done in this method because it is a likely |
| | | // candidate for being called by the logging subsystem. |
| | | |
| | | return new String[][] |
| | | { |
| | | new String[] { LOG_ELEMENT_ENTRY_DN, String.valueOf(rawEntryDN) } |
| | | }; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final String[][] getResponseLogElements() |
| | | { |
| | | // Note that no debugging will be done in this method because it is a likely |
| | | // candidate for being called by the logging subsystem. |
| | | |
| | | String resultCode = String.valueOf(getResultCode().getIntValue()); |
| | | |
| | | String errorMessage; |
| | | StringBuilder errorMessageBuffer = getErrorMessage(); |
| | | if (errorMessageBuffer == null) |
| | | { |
| | | errorMessage = null; |
| | | } |
| | | else |
| | | { |
| | | errorMessage = errorMessageBuffer.toString(); |
| | | } |
| | | |
| | | String matchedDNStr; |
| | | DN matchedDN = getMatchedDN(); |
| | | if (matchedDN == null) |
| | | { |
| | | matchedDNStr = null; |
| | | } |
| | | else |
| | | { |
| | | matchedDNStr = matchedDN.toString(); |
| | | } |
| | | |
| | | String referrals; |
| | | List<String> referralURLs = getReferralURLs(); |
| | | if ((referralURLs == null) || referralURLs.isEmpty()) |
| | | { |
| | | referrals = null; |
| | | } |
| | | else |
| | | { |
| | | StringBuilder buffer = new StringBuilder(); |
| | | Iterator<String> iterator = referralURLs.iterator(); |
| | | buffer.append(iterator.next()); |
| | | |
| | | while (iterator.hasNext()) |
| | | { |
| | | buffer.append(", "); |
| | | buffer.append(iterator.next()); |
| | | } |
| | | |
| | | referrals = buffer.toString(); |
| | | } |
| | | |
| | | String processingTime = |
| | | String.valueOf(processingStopTime - processingStartTime); |
| | | |
| | | return new String[][] |
| | | { |
| | | new String[] { LOG_ELEMENT_RESULT_CODE, resultCode }, |
| | | new String[] { LOG_ELEMENT_ERROR_MESSAGE, errorMessage }, |
| | | new String[] { LOG_ELEMENT_MATCHED_DN, matchedDNStr }, |
| | | new String[] { LOG_ELEMENT_REFERRAL_URLS, referrals }, |
| | | new String[] { LOG_ELEMENT_PROCESSING_TIME, processingTime } |
| | | }; |
| | | } |
| | | |
| | | |
| | | public abstract void setChangeNumber(long changeNumber); |
| | | |
| | | /** |
| | | * Retrieves the proxied authorization DN for this operation if proxied |
| | |
| | | * authorization has been requested, or {@code null} if proxied |
| | | * authorization has not been requested. |
| | | */ |
| | | public DN getProxiedAuthorizationDN() |
| | | { |
| | | return proxiedAuthorizationDN; |
| | | } |
| | | |
| | | |
| | | public abstract DN getProxiedAuthorizationDN(); |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | * Set the proxied authorization DN for this operation if proxied |
| | | * authorization has been requested. |
| | | * |
| | | * @param proxiedAuthorizationDN |
| | | * The proxied authorization DN for this operation if proxied |
| | | * authorization has been requested, or {@code null} if proxied |
| | | * authorization has not been requested. |
| | | */ |
| | | @Override() |
| | | public final List<Control> getResponseControls() |
| | | { |
| | | return responseControls; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final void addResponseControl(Control control) |
| | | { |
| | | responseControls.add(control); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final void removeResponseControl(Control control) |
| | | { |
| | | responseControls.remove(control); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final void run() |
| | | { |
| | | setResultCode(ResultCode.UNDEFINED); |
| | | |
| | | |
| | | // Get the plugin config manager that will be used for invoking plugins. |
| | | PluginConfigManager pluginConfigManager = |
| | | DirectoryServer.getPluginConfigManager(); |
| | | boolean skipPostOperation = false; |
| | | |
| | | |
| | | // Start the processing timer. |
| | | processingStartTime = System.currentTimeMillis(); |
| | | |
| | | |
| | | // Check for and handle a request to cancel this operation. |
| | | if (cancelRequest != null) |
| | | { |
| | | indicateCancelled(cancelRequest); |
| | | processingStopTime = System.currentTimeMillis(); |
| | | return; |
| | | } |
| | | |
| | | |
| | | // Create a labeled block of code that we can break out of if a problem is |
| | | // detected. |
| | | modifyProcessing: |
| | | { |
| | | // Invoke the pre-parse modify plugins. |
| | | PreParsePluginResult preParseResult = |
| | | pluginConfigManager.invokePreParseModifyPlugins(this); |
| | | if (preParseResult.connectionTerminated()) |
| | | { |
| | | // There's no point in continuing with anything. Log the request and |
| | | // result and return. |
| | | setResultCode(ResultCode.CANCELED); |
| | | |
| | | int msgID = MSGID_CANCELED_BY_PREPARSE_DISCONNECT; |
| | | appendErrorMessage(getMessage(msgID)); |
| | | |
| | | processingStopTime = System.currentTimeMillis(); |
| | | |
| | | logModifyRequest(this); |
| | | logModifyResponse(this); |
| | | pluginConfigManager.invokePostResponseModifyPlugins(this); |
| | | return; |
| | | } |
| | | else if (preParseResult.sendResponseImmediately()) |
| | | { |
| | | skipPostOperation = true; |
| | | logModifyRequest(this); |
| | | break modifyProcessing; |
| | | } |
| | | else if (preParseResult.skipCoreProcessing()) |
| | | { |
| | | skipPostOperation = false; |
| | | break modifyProcessing; |
| | | } |
| | | |
| | | |
| | | // Log the modify request message. |
| | | logModifyRequest(this); |
| | | |
| | | |
| | | // Check for and handle a request to cancel this operation. |
| | | if (cancelRequest != null) |
| | | { |
| | | indicateCancelled(cancelRequest); |
| | | processingStopTime = System.currentTimeMillis(); |
| | | logModifyResponse(this); |
| | | pluginConfigManager.invokePostResponseModifyPlugins(this); |
| | | return; |
| | | } |
| | | |
| | | |
| | | // Process the entry DN to convert it from the raw form to the form |
| | | // required for the rest of the modify processing. |
| | | try |
| | | { |
| | | if (entryDN == null) |
| | | { |
| | | entryDN = DN.decode(rawEntryDN); |
| | | } |
| | | } |
| | | catch (DirectoryException de) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, de); |
| | | } |
| | | |
| | | setResultCode(de.getResultCode()); |
| | | appendErrorMessage(de.getErrorMessage()); |
| | | skipPostOperation = true; |
| | | |
| | | break modifyProcessing; |
| | | } |
| | | |
| | | |
| | | // Process the modifications to convert them from their raw form to the |
| | | // form required for the rest of the modify processing. |
| | | if (modifications == null) |
| | | { |
| | | modifications = new ArrayList<Modification>(rawModifications.size()); |
| | | for (RawModification m : rawModifications) |
| | | { |
| | | try |
| | | { |
| | | modifications.add(m.toModification()); |
| | | } |
| | | catch (LDAPException le) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, le); |
| | | } |
| | | |
| | | setResultCode(ResultCode.valueOf(le.getResultCode())); |
| | | appendErrorMessage(le.getMessage()); |
| | | |
| | | break modifyProcessing; |
| | | } |
| | | } |
| | | } |
| | | |
| | | if (modifications.isEmpty()) |
| | | { |
| | | setResultCode(ResultCode.CONSTRAINT_VIOLATION); |
| | | appendErrorMessage(getMessage(MSGID_MODIFY_NO_MODIFICATIONS, |
| | | String.valueOf(entryDN))); |
| | | break modifyProcessing; |
| | | } |
| | | |
| | | |
| | | // If the user must change their password before doing anything else, and |
| | | // if the target of the modify operation isn't the user's own entry, then |
| | | // reject the request. |
| | | if ((! isInternalOperation()) && clientConnection.mustChangePassword()) |
| | | { |
| | | DN authzDN = getAuthorizationDN(); |
| | | if ((authzDN != null) && (! authzDN.equals(entryDN))) |
| | | { |
| | | // The user will not be allowed to do anything else before |
| | | // the password gets changed. |
| | | setResultCode(ResultCode.UNWILLING_TO_PERFORM); |
| | | |
| | | int msgID = MSGID_MODIFY_MUST_CHANGE_PASSWORD; |
| | | appendErrorMessage(getMessage(msgID)); |
| | | break modifyProcessing; |
| | | } |
| | | } |
| | | |
| | | |
| | | // Check for and handle a request to cancel this operation. |
| | | if (cancelRequest != null) |
| | | { |
| | | indicateCancelled(cancelRequest); |
| | | processingStopTime = System.currentTimeMillis(); |
| | | logModifyResponse(this); |
| | | pluginConfigManager.invokePostResponseModifyPlugins(this); |
| | | return; |
| | | } |
| | | |
| | | |
| | | // Acquire a write lock on the target entry. |
| | | Lock entryLock = null; |
| | | for (int i=0; i < 3; i++) |
| | | { |
| | | entryLock = LockManager.lockWrite(entryDN); |
| | | if (entryLock != null) |
| | | { |
| | | break; |
| | | } |
| | | } |
| | | |
| | | if (entryLock == null) |
| | | { |
| | | setResultCode(DirectoryServer.getServerErrorResultCode()); |
| | | appendErrorMessage(getMessage(MSGID_MODIFY_CANNOT_LOCK_ENTRY, |
| | | String.valueOf(entryDN))); |
| | | |
| | | skipPostOperation = true; |
| | | break modifyProcessing; |
| | | } |
| | | |
| | | |
| | | try |
| | | { |
| | | // Check for and handle a request to cancel this operation. |
| | | if (cancelRequest != null) |
| | | { |
| | | indicateCancelled(cancelRequest); |
| | | processingStopTime = System.currentTimeMillis(); |
| | | logModifyResponse(this); |
| | | pluginConfigManager.invokePostResponseModifyPlugins(this); |
| | | return; |
| | | } |
| | | |
| | | |
| | | // Get the entry to modify. If it does not exist, then fail. |
| | | try |
| | | { |
| | | currentEntry = DirectoryServer.getEntry(entryDN); |
| | | } |
| | | catch (DirectoryException de) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, de); |
| | | } |
| | | |
| | | setResultCode(de.getResultCode()); |
| | | appendErrorMessage(de.getErrorMessage()); |
| | | setMatchedDN(de.getMatchedDN()); |
| | | setReferralURLs(de.getReferralURLs()); |
| | | |
| | | break modifyProcessing; |
| | | } |
| | | |
| | | if (currentEntry == null) |
| | | { |
| | | setResultCode(ResultCode.NO_SUCH_OBJECT); |
| | | appendErrorMessage(getMessage(MSGID_MODIFY_NO_SUCH_ENTRY, |
| | | String.valueOf(entryDN))); |
| | | |
| | | // See if one of the entry's ancestors exists. |
| | | DN parentDN = entryDN.getParentDNInSuffix(); |
| | | while (parentDN != null) |
| | | { |
| | | try |
| | | { |
| | | if (DirectoryServer.entryExists(parentDN)) |
| | | { |
| | | setMatchedDN(parentDN); |
| | | break; |
| | | } |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | break; |
| | | } |
| | | |
| | | parentDN = parentDN.getParentDNInSuffix(); |
| | | } |
| | | |
| | | break modifyProcessing; |
| | | } |
| | | |
| | | // Check to see if there are any controls in the request. If so, then |
| | | // see if there is any special processing required. |
| | | boolean noOp = false; |
| | | LDAPPreReadRequestControl preReadRequest = null; |
| | | LDAPPostReadRequestControl postReadRequest = null; |
| | | 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 (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); |
| | | } |
| | | |
| | | setResultCode(ResultCode.valueOf(le.getResultCode())); |
| | | appendErrorMessage(le.getMessage()); |
| | | |
| | | break modifyProcessing; |
| | | } |
| | | } |
| | | |
| | | try |
| | | { |
| | | // FIXME -- We need to determine whether the current user has |
| | | // permission to make this determination. |
| | | SearchFilter filter = assertControl.getSearchFilter(); |
| | | if (! filter.matchesEntry(currentEntry)) |
| | | { |
| | | setResultCode(ResultCode.ASSERTION_FAILED); |
| | | |
| | | appendErrorMessage(getMessage(MSGID_MODIFY_ASSERTION_FAILED, |
| | | String.valueOf(entryDN))); |
| | | |
| | | break modifyProcessing; |
| | | } |
| | | } |
| | | catch (DirectoryException de) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, de); |
| | | } |
| | | |
| | | setResultCode(ResultCode.PROTOCOL_ERROR); |
| | | |
| | | int msgID = MSGID_MODIFY_CANNOT_PROCESS_ASSERTION_FILTER; |
| | | appendErrorMessage(getMessage(msgID, String.valueOf(entryDN), |
| | | de.getErrorMessage())); |
| | | |
| | | break modifyProcessing; |
| | | } |
| | | } |
| | | 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); |
| | | } |
| | | |
| | | setResultCode(ResultCode.valueOf(le.getResultCode())); |
| | | appendErrorMessage(le.getMessage()); |
| | | |
| | | break modifyProcessing; |
| | | } |
| | | } |
| | | } |
| | | 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); |
| | | } |
| | | |
| | | setResultCode(ResultCode.valueOf(le.getResultCode())); |
| | | appendErrorMessage(le.getMessage()); |
| | | |
| | | break modifyProcessing; |
| | | } |
| | | } |
| | | } |
| | | else if (oid.equals(OID_PROXIED_AUTH_V1)) |
| | | { |
| | | // The requester must have the PROXIED_AUTH privilige in order to |
| | | // be able to use this control. |
| | | if (! clientConnection.hasPrivilege(Privilege.PROXIED_AUTH, this)) |
| | | { |
| | | int msgID = MSGID_PROXYAUTH_INSUFFICIENT_PRIVILEGES; |
| | | appendErrorMessage(getMessage(msgID)); |
| | | setResultCode(ResultCode.AUTHORIZATION_DENIED); |
| | | break modifyProcessing; |
| | | } |
| | | |
| | | |
| | | ProxiedAuthV1Control proxyControl; |
| | | if (c instanceof ProxiedAuthV1Control) |
| | | { |
| | | proxyControl = (ProxiedAuthV1Control) c; |
| | | } |
| | | else |
| | | { |
| | | try |
| | | { |
| | | proxyControl = ProxiedAuthV1Control.decodeControl(c); |
| | | } |
| | | catch (LDAPException le) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, le); |
| | | } |
| | | |
| | | setResultCode(ResultCode.valueOf(le.getResultCode())); |
| | | appendErrorMessage(le.getMessage()); |
| | | |
| | | break modifyProcessing; |
| | | } |
| | | } |
| | | |
| | | |
| | | Entry authorizationEntry; |
| | | try |
| | | { |
| | | authorizationEntry = proxyControl.getAuthorizationEntry(); |
| | | } |
| | | catch (DirectoryException de) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, de); |
| | | } |
| | | |
| | | setResultCode(de.getResultCode()); |
| | | appendErrorMessage(de.getErrorMessage()); |
| | | |
| | | break modifyProcessing; |
| | | } |
| | | if (AccessControlConfigManager.getInstance(). |
| | | getAccessControlHandler().isProxiedAuthAllowed(this, |
| | | authorizationEntry) == false) { |
| | | setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS); |
| | | |
| | | int msgID = MSGID_MODIFY_AUTHZ_INSUFFICIENT_ACCESS_RIGHTS; |
| | | appendErrorMessage(getMessage(msgID, String.valueOf(entryDN))); |
| | | |
| | | skipPostOperation = true; |
| | | break modifyProcessing; |
| | | } |
| | | |
| | | setAuthorizationEntry(authorizationEntry); |
| | | if (authorizationEntry == null) |
| | | { |
| | | proxiedAuthorizationDN = DN.nullDN(); |
| | | } |
| | | else |
| | | { |
| | | proxiedAuthorizationDN = authorizationEntry.getDN(); |
| | | } |
| | | } |
| | | else if (oid.equals(OID_PROXIED_AUTH_V2)) |
| | | { |
| | | // The requester must have the PROXIED_AUTH privilige in order to |
| | | // be able to use this control. |
| | | if (! clientConnection.hasPrivilege(Privilege.PROXIED_AUTH, this)) |
| | | { |
| | | int msgID = MSGID_PROXYAUTH_INSUFFICIENT_PRIVILEGES; |
| | | appendErrorMessage(getMessage(msgID)); |
| | | setResultCode(ResultCode.AUTHORIZATION_DENIED); |
| | | break modifyProcessing; |
| | | } |
| | | |
| | | |
| | | ProxiedAuthV2Control proxyControl; |
| | | if (c instanceof ProxiedAuthV2Control) |
| | | { |
| | | proxyControl = (ProxiedAuthV2Control) c; |
| | | } |
| | | else |
| | | { |
| | | try |
| | | { |
| | | proxyControl = ProxiedAuthV2Control.decodeControl(c); |
| | | } |
| | | catch (LDAPException le) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, le); |
| | | } |
| | | |
| | | setResultCode(ResultCode.valueOf(le.getResultCode())); |
| | | appendErrorMessage(le.getMessage()); |
| | | |
| | | break modifyProcessing; |
| | | } |
| | | } |
| | | |
| | | |
| | | Entry authorizationEntry; |
| | | try |
| | | { |
| | | authorizationEntry = proxyControl.getAuthorizationEntry(); |
| | | } |
| | | catch (DirectoryException de) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, de); |
| | | } |
| | | |
| | | setResultCode(de.getResultCode()); |
| | | appendErrorMessage(de.getErrorMessage()); |
| | | |
| | | break modifyProcessing; |
| | | } |
| | | |
| | | if (AccessControlConfigManager.getInstance(). |
| | | getAccessControlHandler().isProxiedAuthAllowed(this, |
| | | authorizationEntry) == false) { |
| | | setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS); |
| | | |
| | | int msgID = MSGID_MODIFY_AUTHZ_INSUFFICIENT_ACCESS_RIGHTS; |
| | | appendErrorMessage(getMessage(msgID, String.valueOf(entryDN))); |
| | | |
| | | skipPostOperation = true; |
| | | break modifyProcessing; |
| | | } |
| | | setAuthorizationEntry(authorizationEntry); |
| | | if (authorizationEntry == null) |
| | | { |
| | | proxiedAuthorizationDN = DN.nullDN(); |
| | | } |
| | | else |
| | | { |
| | | proxiedAuthorizationDN = authorizationEntry.getDN(); |
| | | } |
| | | } |
| | | |
| | | // NYI -- Add support for additional controls. |
| | | else if (c.isCritical()) |
| | | { |
| | | Backend backend = DirectoryServer.getBackend(entryDN); |
| | | if ((backend == null) || (! backend.supportsControl(oid))) |
| | | { |
| | | setResultCode(ResultCode.UNAVAILABLE_CRITICAL_EXTENSION); |
| | | |
| | | int msgID = MSGID_MODIFY_UNSUPPORTED_CRITICAL_CONTROL; |
| | | appendErrorMessage(getMessage(msgID, String.valueOf(entryDN), |
| | | oid)); |
| | | |
| | | break modifyProcessing; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | // Get the password policy state object for the entry that can be used |
| | | // to perform any appropriate password policy processing. Also, see if |
| | | // the entry is being updated by the end user or an administrator. |
| | | PasswordPolicyState pwPolicyState; |
| | | boolean selfChange = entryDN.equals(getAuthorizationDN()); |
| | | try |
| | | { |
| | | // FIXME -- Need a way to enable debug mode. |
| | | pwPolicyState = new PasswordPolicyState(currentEntry, false, false); |
| | | } |
| | | catch (DirectoryException de) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, de); |
| | | } |
| | | |
| | | setResultCode(de.getResultCode()); |
| | | appendErrorMessage(de.getErrorMessage()); |
| | | |
| | | break modifyProcessing; |
| | | } |
| | | |
| | | |
| | | // Create a duplicate of the entry and apply the changes to it. |
| | | modifiedEntry = currentEntry.duplicate(false); |
| | | |
| | | if (! noOp) |
| | | { |
| | | // Invoke any conflict resolution processing that might be needed by |
| | | // the synchronization provider. |
| | | for (SynchronizationProvider provider : |
| | | DirectoryServer.getSynchronizationProviders()) |
| | | { |
| | | try |
| | | { |
| | | SynchronizationProviderResult result = |
| | | provider.handleConflictResolution(this); |
| | | if (! result.continueOperationProcessing()) |
| | | { |
| | | break modifyProcessing; |
| | | } |
| | | } |
| | | catch (DirectoryException de) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, de); |
| | | } |
| | | |
| | | logError(ErrorLogCategory.SYNCHRONIZATION, |
| | | ErrorLogSeverity.SEVERE_ERROR, |
| | | MSGID_MODIFY_SYNCH_CONFLICT_RESOLUTION_FAILED, |
| | | getConnectionID(), getOperationID(), |
| | | getExceptionMessage(de)); |
| | | |
| | | setResponseData(de); |
| | | break modifyProcessing; |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | // Declare variables used for password policy state processing. |
| | | boolean passwordChanged = false; |
| | | boolean currentPasswordProvided = false; |
| | | boolean isEnabled = true; |
| | | boolean enabledStateChanged = false; |
| | | int numPasswords; |
| | | if (currentEntry.hasAttribute( |
| | | pwPolicyState.getPolicy().getPasswordAttribute())) |
| | | { |
| | | // It may actually have more than one, but we can't tell the |
| | | // difference if the values are encoded, and its enough for our |
| | | // purposes just to know that there is at least one. |
| | | numPasswords = 1; |
| | | } |
| | | else |
| | | { |
| | | numPasswords = 0; |
| | | } |
| | | |
| | | |
| | | // If it's not an internal or synchronization operation, then iterate |
| | | // through the set of modifications to see if a password is included in |
| | | // the changes. If so, then add the appropriate state changes to the |
| | | // set of modifications. |
| | | if (! (isInternalOperation() || isSynchronizationOperation())) |
| | | { |
| | | for (Modification m : modifications) |
| | | { |
| | | if (m.getAttribute().getAttributeType().equals( |
| | | pwPolicyState.getPolicy().getPasswordAttribute())) |
| | | { |
| | | passwordChanged = true; |
| | | if (! selfChange) |
| | | { |
| | | if (! clientConnection.hasPrivilege(Privilege.PASSWORD_RESET, |
| | | this)) |
| | | { |
| | | int msgID = MSGID_MODIFY_PWRESET_INSUFFICIENT_PRIVILEGES; |
| | | appendErrorMessage(getMessage(msgID)); |
| | | setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS); |
| | | break modifyProcessing; |
| | | } |
| | | } |
| | | |
| | | break; |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | for (Modification m : modifications) |
| | | { |
| | | Attribute a = m.getAttribute(); |
| | | AttributeType t = a.getAttributeType(); |
| | | |
| | | |
| | | // If the attribute type is marked "NO-USER-MODIFICATION" then fail |
| | | // unless this is an internal operation or is related to |
| | | // synchronization in some way. |
| | | if (t.isNoUserModification()) |
| | | { |
| | | if (! (isInternalOperation() || isSynchronizationOperation() || |
| | | m.isInternal())) |
| | | { |
| | | setResultCode(ResultCode.UNWILLING_TO_PERFORM); |
| | | appendErrorMessage(getMessage(MSGID_MODIFY_ATTR_IS_NO_USER_MOD, |
| | | String.valueOf(entryDN), |
| | | a.getName())); |
| | | break modifyProcessing; |
| | | } |
| | | } |
| | | |
| | | // If the attribute type is marked "OBSOLETE" and the modification |
| | | // is setting new values, then fail unless this is an internal |
| | | // operation or is related to synchronization in some way. |
| | | if (t.isObsolete()) |
| | | { |
| | | if (a.hasValue() && |
| | | (m.getModificationType() != ModificationType.DELETE)) |
| | | { |
| | | if (! (isInternalOperation() || isSynchronizationOperation() || |
| | | m.isInternal())) |
| | | { |
| | | setResultCode(ResultCode.CONSTRAINT_VIOLATION); |
| | | appendErrorMessage(getMessage(MSGID_MODIFY_ATTR_IS_OBSOLETE, |
| | | String.valueOf(entryDN), |
| | | a.getName())); |
| | | break modifyProcessing; |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | // See if the attribute is one which controls the privileges available |
| | | // for a user. If it is, then the client must have the |
| | | // PRIVILEGE_CHANGE privilege. |
| | | if (t.hasName(OP_ATTR_PRIVILEGE_NAME)) |
| | | { |
| | | if (! clientConnection.hasPrivilege(Privilege.PRIVILEGE_CHANGE, |
| | | this)) |
| | | { |
| | | int msgID = MSGID_MODIFY_CHANGE_PRIVILEGE_INSUFFICIENT_PRIVILEGES; |
| | | appendErrorMessage(getMessage(msgID)); |
| | | setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS); |
| | | break modifyProcessing; |
| | | } |
| | | } |
| | | |
| | | |
| | | // If the modification is updating the password attribute, then |
| | | // perform any necessary password policy processing. This processing |
| | | // should be skipped for synchronization operations. |
| | | boolean isPassword |
| | | = t.equals(pwPolicyState.getPolicy().getPasswordAttribute()); |
| | | if (isPassword && (!(isSynchronizationOperation()))) |
| | | { |
| | | // If the attribute contains any options, then reject it. Passwords |
| | | // will not be allowed to have options. Skipped for internal |
| | | // operations. |
| | | if(!isInternalOperation()) |
| | | { |
| | | if (a.hasOptions()) |
| | | { |
| | | setResultCode(ResultCode.UNWILLING_TO_PERFORM); |
| | | |
| | | int msgID = MSGID_MODIFY_PASSWORDS_CANNOT_HAVE_OPTIONS; |
| | | appendErrorMessage(getMessage(msgID)); |
| | | break modifyProcessing; |
| | | } |
| | | |
| | | |
| | | // If it's a self change, then see if that's allowed. |
| | | if (selfChange && |
| | | (! pwPolicyState.getPolicy().allowUserPasswordChanges())) |
| | | { |
| | | setResultCode(ResultCode.UNWILLING_TO_PERFORM); |
| | | |
| | | int msgID = MSGID_MODIFY_NO_USER_PW_CHANGES; |
| | | appendErrorMessage(getMessage(msgID)); |
| | | break modifyProcessing; |
| | | } |
| | | |
| | | |
| | | // If we require secure password changes, then makes sure it's a |
| | | // secure communication channel. |
| | | if (pwPolicyState.getPolicy().requireSecurePasswordChanges() && |
| | | (! clientConnection.isSecure())) |
| | | { |
| | | setResultCode(ResultCode.UNWILLING_TO_PERFORM); |
| | | |
| | | int msgID = MSGID_MODIFY_REQUIRE_SECURE_CHANGES; |
| | | appendErrorMessage(getMessage(msgID)); |
| | | break modifyProcessing; |
| | | } |
| | | |
| | | |
| | | // If it's a self change and it's not been long enough since the |
| | | // previous change, then reject it. |
| | | if (selfChange && pwPolicyState.isWithinMinimumAge()) |
| | | { |
| | | setResultCode(ResultCode.UNWILLING_TO_PERFORM); |
| | | |
| | | int msgID = MSGID_MODIFY_WITHIN_MINIMUM_AGE; |
| | | appendErrorMessage(getMessage(msgID)); |
| | | break modifyProcessing; |
| | | } |
| | | } |
| | | |
| | | // Check to see whether this will adding, deleting, or replacing |
| | | // password values (increment doesn't make any sense for passwords). |
| | | // Then perform the appropriate type of processing for that kind of |
| | | // modification. |
| | | boolean isAdd = false; |
| | | LinkedHashSet<AttributeValue> pwValues = a.getValues(); |
| | | LinkedHashSet<AttributeValue> encodedValues = |
| | | new LinkedHashSet<AttributeValue>(); |
| | | switch (m.getModificationType()) |
| | | { |
| | | case ADD: |
| | | case REPLACE: |
| | | int passwordsToAdd = pwValues.size(); |
| | | |
| | | if (m.getModificationType() == ModificationType.ADD) |
| | | { |
| | | numPasswords += passwordsToAdd; |
| | | isAdd = true; |
| | | } |
| | | else |
| | | { |
| | | numPasswords = passwordsToAdd; |
| | | } |
| | | // If there were multiple password values provided, then make |
| | | // sure that's OK. |
| | | |
| | | if (! isInternalOperation() && |
| | | ! pwPolicyState.getPolicy().allowExpiredPasswordChanges() && |
| | | (passwordsToAdd > 1)) |
| | | { |
| | | setResultCode(ResultCode.UNWILLING_TO_PERFORM); |
| | | |
| | | int msgID = MSGID_MODIFY_MULTIPLE_VALUES_NOT_ALLOWED; |
| | | appendErrorMessage(getMessage(msgID)); |
| | | break modifyProcessing; |
| | | } |
| | | |
| | | // Iterate through the password values and see if any of them |
| | | // are pre-encoded. If so, then check to see if we'll allow it. |
| | | // Otherwise, store the clear-text values for later validation |
| | | // and update the attribute with the encoded values. |
| | | for (AttributeValue v : pwValues) |
| | | { |
| | | if (pwPolicyState.passwordIsPreEncoded(v.getValue())) |
| | | { |
| | | if ((!isInternalOperation()) && |
| | | ! pwPolicyState.getPolicy().allowPreEncodedPasswords()) |
| | | { |
| | | setResultCode(ResultCode.UNWILLING_TO_PERFORM); |
| | | |
| | | int msgID = MSGID_MODIFY_NO_PREENCODED_PASSWORDS; |
| | | appendErrorMessage(getMessage(msgID)); |
| | | break modifyProcessing; |
| | | } |
| | | else |
| | | { |
| | | encodedValues.add(v); |
| | | } |
| | | } |
| | | else |
| | | { |
| | | if (isAdd) |
| | | { |
| | | // Make sure that the password value doesn't already |
| | | // exist. |
| | | if (pwPolicyState.passwordMatches(v.getValue())) |
| | | { |
| | | setResultCode(ResultCode.ATTRIBUTE_OR_VALUE_EXISTS); |
| | | |
| | | int msgID = MSGID_MODIFY_PASSWORD_EXISTS; |
| | | appendErrorMessage(getMessage(msgID)); |
| | | break modifyProcessing; |
| | | } |
| | | } |
| | | |
| | | if (newPasswords == null) |
| | | { |
| | | newPasswords = new LinkedList<AttributeValue>(); |
| | | } |
| | | |
| | | newPasswords.add(v); |
| | | |
| | | try |
| | | { |
| | | for (ByteString s : |
| | | pwPolicyState.encodePassword(v.getValue())) |
| | | { |
| | | encodedValues.add(new AttributeValue( |
| | | a.getAttributeType(), s)); |
| | | } |
| | | } |
| | | catch (DirectoryException de) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, de); |
| | | } |
| | | |
| | | setResultCode(de.getResultCode()); |
| | | appendErrorMessage(de.getErrorMessage()); |
| | | break modifyProcessing; |
| | | } |
| | | } |
| | | } |
| | | |
| | | a.setValues(encodedValues); |
| | | |
| | | break; |
| | | |
| | | case DELETE: |
| | | // Iterate through the password values and see if any of them |
| | | // are pre-encoded. We will never allow pre-encoded passwords |
| | | // for user password changes, but we will allow them for |
| | | // administrators. For each clear-text value, verify that at |
| | | // least one value in the entry matches and replace the |
| | | // clear-text value with the appropriate encoded forms. |
| | | for (AttributeValue v : pwValues) |
| | | { |
| | | if (pwPolicyState.passwordIsPreEncoded(v.getValue())) |
| | | { |
| | | if ((!isInternalOperation()) && selfChange) |
| | | { |
| | | setResultCode(ResultCode.UNWILLING_TO_PERFORM); |
| | | |
| | | int msgID = MSGID_MODIFY_NO_PREENCODED_PASSWORDS; |
| | | appendErrorMessage(getMessage(msgID)); |
| | | break modifyProcessing; |
| | | } |
| | | else |
| | | { |
| | | encodedValues.add(v); |
| | | } |
| | | } |
| | | else |
| | | { |
| | | List<Attribute> attrList = currentEntry.getAttribute(t); |
| | | if ((attrList == null) || (attrList.isEmpty())) |
| | | { |
| | | setResultCode(ResultCode.UNWILLING_TO_PERFORM); |
| | | |
| | | int msgID = MSGID_MODIFY_NO_EXISTING_VALUES; |
| | | appendErrorMessage(getMessage(msgID)); |
| | | break modifyProcessing; |
| | | } |
| | | boolean found = false; |
| | | for (Attribute attr : attrList) |
| | | { |
| | | for (AttributeValue av : attr.getValues()) |
| | | { |
| | | if (pwPolicyState.getPolicy().usesAuthPasswordSyntax()) |
| | | { |
| | | if (AuthPasswordSyntax.isEncoded(av.getValue())) |
| | | { |
| | | try |
| | | { |
| | | StringBuilder[] compoenents = |
| | | AuthPasswordSyntax.decodeAuthPassword( |
| | | av.getStringValue()); |
| | | PasswordStorageScheme scheme = |
| | | DirectoryServer. |
| | | getAuthPasswordStorageScheme( |
| | | compoenents[0].toString()); |
| | | if (scheme != null) |
| | | { |
| | | if (scheme.authPasswordMatches( |
| | | v.getValue(), |
| | | compoenents[1].toString(), |
| | | compoenents[2].toString())) |
| | | { |
| | | encodedValues.add(av); |
| | | found = true; |
| | | } |
| | | } |
| | | } |
| | | catch (DirectoryException de) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, de); |
| | | } |
| | | |
| | | setResultCode(de.getResultCode()); |
| | | |
| | | int msgID = MSGID_MODIFY_CANNOT_DECODE_PW; |
| | | appendErrorMessage( |
| | | getMessage(msgID, de.getErrorMessage())); |
| | | break modifyProcessing; |
| | | } |
| | | } |
| | | else |
| | | { |
| | | if (av.equals(v)) |
| | | { |
| | | encodedValues.add(v); |
| | | found = true; |
| | | } |
| | | } |
| | | } |
| | | else |
| | | { |
| | | if (UserPasswordSyntax.isEncoded(av.getValue())) |
| | | { |
| | | try |
| | | { |
| | | String[] compoenents = |
| | | UserPasswordSyntax.decodeUserPassword( |
| | | av.getStringValue()); |
| | | PasswordStorageScheme scheme = |
| | | DirectoryServer.getPasswordStorageScheme( |
| | | toLowerCase(compoenents[0])); |
| | | if (scheme != null) |
| | | { |
| | | if (scheme.passwordMatches( |
| | | v.getValue(), |
| | | new ASN1OctetString(compoenents[1]))) |
| | | { |
| | | encodedValues.add(av); |
| | | found = true; |
| | | } |
| | | } |
| | | } |
| | | catch (DirectoryException de) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, de); |
| | | } |
| | | |
| | | setResultCode(de.getResultCode()); |
| | | |
| | | int msgID = MSGID_MODIFY_CANNOT_DECODE_PW; |
| | | appendErrorMessage(getMessage(msgID, |
| | | de.getErrorMessage())); |
| | | break modifyProcessing; |
| | | } |
| | | } |
| | | else |
| | | { |
| | | if (av.equals(v)) |
| | | { |
| | | encodedValues.add(v); |
| | | found = true; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | if (found) |
| | | { |
| | | if (currentPasswords == null) |
| | | { |
| | | currentPasswords = new LinkedList<AttributeValue>(); |
| | | } |
| | | currentPasswords.add(v); |
| | | |
| | | numPasswords--; |
| | | } |
| | | else |
| | | { |
| | | setResultCode(ResultCode.UNWILLING_TO_PERFORM); |
| | | |
| | | int msgID = MSGID_MODIFY_INVALID_PASSWORD; |
| | | appendErrorMessage(getMessage(msgID)); |
| | | break modifyProcessing; |
| | | } |
| | | |
| | | currentPasswordProvided = true; |
| | | } |
| | | } |
| | | |
| | | a.setValues(encodedValues); |
| | | |
| | | break; |
| | | |
| | | default: |
| | | setResultCode(ResultCode.UNWILLING_TO_PERFORM); |
| | | |
| | | int msgID = MSGID_MODIFY_INVALID_MOD_TYPE_FOR_PASSWORD; |
| | | appendErrorMessage(getMessage(msgID, |
| | | String.valueOf(m.getModificationType()), a.getName())); |
| | | |
| | | break modifyProcessing; |
| | | } |
| | | } |
| | | else |
| | | { |
| | | // See if it's an attribute used to maintain the account |
| | | // enabled/disabled state. |
| | | AttributeType disabledAttr = |
| | | DirectoryServer.getAttributeType(OP_ATTR_ACCOUNT_DISABLED, |
| | | true); |
| | | if (t.equals(disabledAttr)) |
| | | { |
| | | enabledStateChanged = true; |
| | | for (AttributeValue v : a.getValues()) |
| | | { |
| | | try |
| | | { |
| | | isEnabled = (! BooleanSyntax.decodeBooleanValue( |
| | | v.getNormalizedValue())); |
| | | } |
| | | catch (DirectoryException de) |
| | | { |
| | | setResultCode(ResultCode.INVALID_ATTRIBUTE_SYNTAX); |
| | | |
| | | int msgID = MSGID_MODIFY_INVALID_DISABLED_VALUE; |
| | | String message = |
| | | getMessage(msgID, OP_ATTR_ACCOUNT_DISABLED, |
| | | String.valueOf(de.getErrorMessage())); |
| | | appendErrorMessage(message); |
| | | break modifyProcessing; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | switch (m.getModificationType()) |
| | | { |
| | | case ADD: |
| | | // Make sure that one or more values have been provided for the |
| | | // attribute. |
| | | LinkedHashSet<AttributeValue> newValues = a.getValues(); |
| | | if ((newValues == null) || newValues.isEmpty()) |
| | | { |
| | | setResultCode(ResultCode.PROTOCOL_ERROR); |
| | | appendErrorMessage(getMessage(MSGID_MODIFY_ADD_NO_VALUES, |
| | | String.valueOf(entryDN), |
| | | a.getName())); |
| | | break modifyProcessing; |
| | | } |
| | | |
| | | |
| | | // Make sure that all the new values are valid according to the |
| | | // associated syntax. |
| | | if (DirectoryServer.checkSchema()) |
| | | { |
| | | AcceptRejectWarn syntaxPolicy = |
| | | DirectoryServer.getSyntaxEnforcementPolicy(); |
| | | AttributeSyntax syntax = t.getSyntax(); |
| | | |
| | | if (syntaxPolicy == AcceptRejectWarn.REJECT) |
| | | { |
| | | StringBuilder invalidReason = new StringBuilder(); |
| | | |
| | | for (AttributeValue v : newValues) |
| | | { |
| | | if (! syntax.valueIsAcceptable(v.getValue(), invalidReason)) |
| | | { |
| | | setResultCode(ResultCode.INVALID_ATTRIBUTE_SYNTAX); |
| | | |
| | | int msgID = MSGID_MODIFY_ADD_INVALID_SYNTAX; |
| | | appendErrorMessage(getMessage(msgID, |
| | | String.valueOf(entryDN), |
| | | a.getName(), |
| | | v.getStringValue(), |
| | | invalidReason.toString())); |
| | | |
| | | break modifyProcessing; |
| | | } |
| | | } |
| | | } |
| | | else if (syntaxPolicy == AcceptRejectWarn.WARN) |
| | | { |
| | | StringBuilder invalidReason = new StringBuilder(); |
| | | |
| | | for (AttributeValue v : newValues) |
| | | { |
| | | if (! syntax.valueIsAcceptable(v.getValue(), invalidReason)) |
| | | { |
| | | setResultCode(ResultCode.INVALID_ATTRIBUTE_SYNTAX); |
| | | |
| | | int msgID = MSGID_MODIFY_ADD_INVALID_SYNTAX; |
| | | logError(ErrorLogCategory.SCHEMA, |
| | | ErrorLogSeverity.SEVERE_WARNING, msgID, |
| | | String.valueOf(entryDN), a.getName(), |
| | | v.getStringValue(), invalidReason.toString()); |
| | | |
| | | invalidReason = new StringBuilder(); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | // Add the provided attribute or merge an existing attribute with |
| | | // the values of the new attribute. If there are any duplicates, |
| | | // then fail. |
| | | LinkedList<AttributeValue> duplicateValues = |
| | | new LinkedList<AttributeValue>(); |
| | | if (a.getAttributeType().isObjectClassType()) |
| | | { |
| | | try |
| | | { |
| | | modifiedEntry.addObjectClasses(newValues); |
| | | break; |
| | | } |
| | | catch (DirectoryException de) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, de); |
| | | } |
| | | |
| | | setResponseData(de); |
| | | break modifyProcessing; |
| | | } |
| | | } |
| | | else |
| | | { |
| | | modifiedEntry.addAttribute(a, duplicateValues); |
| | | if (duplicateValues.isEmpty()) |
| | | { |
| | | break; |
| | | } |
| | | else |
| | | { |
| | | StringBuilder buffer = new StringBuilder(); |
| | | Iterator<AttributeValue> iterator = |
| | | duplicateValues.iterator(); |
| | | buffer.append(iterator.next().getStringValue()); |
| | | while (iterator.hasNext()) |
| | | { |
| | | buffer.append(", "); |
| | | buffer.append(iterator.next().getStringValue()); |
| | | } |
| | | |
| | | setResultCode(ResultCode.ATTRIBUTE_OR_VALUE_EXISTS); |
| | | |
| | | int msgID = MSGID_MODIFY_ADD_DUPLICATE_VALUE; |
| | | appendErrorMessage(getMessage(msgID, String.valueOf(entryDN), |
| | | a.getName(), |
| | | buffer.toString())); |
| | | |
| | | break modifyProcessing; |
| | | } |
| | | } |
| | | |
| | | |
| | | case DELETE: |
| | | // Remove the specified attribute values or the entire attribute |
| | | // from the value. If there are any specified values that were |
| | | // not present, then fail. If the RDN attribute value would be |
| | | // removed, then fail. |
| | | LinkedList<AttributeValue> missingValues = |
| | | new LinkedList<AttributeValue>(); |
| | | boolean attrExists = |
| | | modifiedEntry.removeAttribute(a, missingValues); |
| | | |
| | | if (attrExists) |
| | | { |
| | | if (missingValues.isEmpty()) |
| | | { |
| | | RDN rdn = modifiedEntry.getDN().getRDN(); |
| | | if ((rdn != null) && rdn.hasAttributeType(t) && |
| | | (! modifiedEntry.hasValue(t, a.getOptions(), |
| | | rdn.getAttributeValue(t)))) |
| | | { |
| | | setResultCode(ResultCode.NOT_ALLOWED_ON_RDN); |
| | | |
| | | int msgID = MSGID_MODIFY_DELETE_RDN_ATTR; |
| | | appendErrorMessage(getMessage(msgID, |
| | | String.valueOf(entryDN), |
| | | a.getName())); |
| | | break modifyProcessing; |
| | | } |
| | | |
| | | break; |
| | | } |
| | | else |
| | | { |
| | | StringBuilder buffer = new StringBuilder(); |
| | | Iterator<AttributeValue> iterator = missingValues.iterator(); |
| | | buffer.append(iterator.next().getStringValue()); |
| | | while (iterator.hasNext()) |
| | | { |
| | | buffer.append(", "); |
| | | buffer.append(iterator.next().getStringValue()); |
| | | } |
| | | |
| | | setResultCode(ResultCode.NO_SUCH_ATTRIBUTE); |
| | | |
| | | int msgID = MSGID_MODIFY_DELETE_MISSING_VALUES; |
| | | appendErrorMessage(getMessage(msgID, String.valueOf(entryDN), |
| | | a.getName(), |
| | | buffer.toString())); |
| | | |
| | | break modifyProcessing; |
| | | } |
| | | } |
| | | else |
| | | { |
| | | setResultCode(ResultCode.NO_SUCH_ATTRIBUTE); |
| | | |
| | | int msgID = MSGID_MODIFY_DELETE_NO_SUCH_ATTR; |
| | | appendErrorMessage(getMessage(msgID, String.valueOf(entryDN), |
| | | a.getName())); |
| | | break modifyProcessing; |
| | | } |
| | | |
| | | |
| | | case REPLACE: |
| | | // If it is the objectclass attribute, then treat that separately. |
| | | if (a.getAttributeType().isObjectClassType()) |
| | | { |
| | | try |
| | | { |
| | | modifiedEntry.setObjectClasses(a.getValues()); |
| | | break; |
| | | } |
| | | catch (DirectoryException de) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, de); |
| | | } |
| | | |
| | | setResponseData(de); |
| | | break modifyProcessing; |
| | | } |
| | | } |
| | | |
| | | |
| | | // If the provided attribute does not have any values, then we |
| | | // will simply remove the attribute from the entry (if it exists). |
| | | if (! a.hasValue()) |
| | | { |
| | | modifiedEntry.removeAttribute(t, a.getOptions()); |
| | | RDN rdn = modifiedEntry.getDN().getRDN(); |
| | | if ((rdn != null) && rdn.hasAttributeType(t) && |
| | | (! modifiedEntry.hasValue(t, a.getOptions(), |
| | | rdn.getAttributeValue(t)))) |
| | | { |
| | | setResultCode(ResultCode.NOT_ALLOWED_ON_RDN); |
| | | |
| | | int msgID = MSGID_MODIFY_DELETE_RDN_ATTR; |
| | | appendErrorMessage(getMessage(msgID, String.valueOf(entryDN), |
| | | a.getName())); |
| | | break modifyProcessing; |
| | | } |
| | | break; |
| | | } |
| | | |
| | | |
| | | // Make sure that all the new values are valid according to the |
| | | // associated syntax. |
| | | newValues = a.getValues(); |
| | | if (DirectoryServer.checkSchema()) |
| | | { |
| | | AcceptRejectWarn syntaxPolicy = |
| | | DirectoryServer.getSyntaxEnforcementPolicy(); |
| | | AttributeSyntax syntax = t.getSyntax(); |
| | | |
| | | if (syntaxPolicy == AcceptRejectWarn.REJECT) |
| | | { |
| | | StringBuilder invalidReason = new StringBuilder(); |
| | | |
| | | for (AttributeValue v : newValues) |
| | | { |
| | | if (! syntax.valueIsAcceptable(v.getValue(), invalidReason)) |
| | | { |
| | | setResultCode(ResultCode.INVALID_ATTRIBUTE_SYNTAX); |
| | | |
| | | int msgID = MSGID_MODIFY_REPLACE_INVALID_SYNTAX; |
| | | appendErrorMessage(getMessage(msgID, |
| | | String.valueOf(entryDN), |
| | | a.getName(), |
| | | v.getStringValue(), |
| | | invalidReason.toString())); |
| | | |
| | | break modifyProcessing; |
| | | } |
| | | } |
| | | } |
| | | else if (syntaxPolicy == AcceptRejectWarn.WARN) |
| | | { |
| | | StringBuilder invalidReason = new StringBuilder(); |
| | | |
| | | for (AttributeValue v : newValues) |
| | | { |
| | | if (! syntax.valueIsAcceptable(v.getValue(), invalidReason)) |
| | | { |
| | | setResultCode(ResultCode.INVALID_ATTRIBUTE_SYNTAX); |
| | | |
| | | int msgID = MSGID_MODIFY_REPLACE_INVALID_SYNTAX; |
| | | logError(ErrorLogCategory.SCHEMA, |
| | | ErrorLogSeverity.SEVERE_WARNING, msgID, |
| | | String.valueOf(entryDN), a.getName(), |
| | | v.getStringValue(), invalidReason.toString()); |
| | | |
| | | invalidReason = new StringBuilder(); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | // If the provided attribute does not have any options, then we |
| | | // will simply use it in place of any existing attribute of the |
| | | // provided type (or add it if it doesn't exist). |
| | | if (! a.hasOptions()) |
| | | { |
| | | List<Attribute> attrList = new ArrayList<Attribute>(1); |
| | | attrList.add(a); |
| | | modifiedEntry.putAttribute(t, attrList); |
| | | |
| | | RDN rdn = modifiedEntry.getDN().getRDN(); |
| | | if ((rdn != null) && rdn.hasAttributeType(t) && |
| | | (! modifiedEntry.hasValue(t, a.getOptions(), |
| | | rdn.getAttributeValue(t)))) |
| | | { |
| | | setResultCode(ResultCode.NOT_ALLOWED_ON_RDN); |
| | | |
| | | int msgID = MSGID_MODIFY_DELETE_RDN_ATTR; |
| | | appendErrorMessage(getMessage(msgID, String.valueOf(entryDN), |
| | | a.getName())); |
| | | break modifyProcessing; |
| | | } |
| | | break; |
| | | } |
| | | |
| | | |
| | | // See if there is an existing attribute of the provided type. If |
| | | // not, then we'll use the new one. |
| | | List<Attribute> attrList = modifiedEntry.getAttribute(t); |
| | | if ((attrList == null) || attrList.isEmpty()) |
| | | { |
| | | attrList = new ArrayList<Attribute>(1); |
| | | attrList.add(a); |
| | | modifiedEntry.putAttribute(t, attrList); |
| | | |
| | | RDN rdn = modifiedEntry.getDN().getRDN(); |
| | | if ((rdn != null) && rdn.hasAttributeType(t) && |
| | | (! modifiedEntry.hasValue(t, a.getOptions(), |
| | | rdn.getAttributeValue(t)))) |
| | | { |
| | | setResultCode(ResultCode.NOT_ALLOWED_ON_RDN); |
| | | |
| | | int msgID = MSGID_MODIFY_DELETE_RDN_ATTR; |
| | | appendErrorMessage(getMessage(msgID, String.valueOf(entryDN), |
| | | a.getName())); |
| | | break modifyProcessing; |
| | | } |
| | | break; |
| | | } |
| | | |
| | | |
| | | // There must be an existing occurrence of the provided attribute |
| | | // in the entry. If there is a version with exactly the set of |
| | | // options provided, then replace it. Otherwise, add a new one. |
| | | boolean found = false; |
| | | for (int i=0; i < attrList.size(); i++) |
| | | { |
| | | if (attrList.get(i).optionsEqual(a.getOptions())) |
| | | { |
| | | attrList.set(i, a); |
| | | found = true; |
| | | break; |
| | | } |
| | | } |
| | | |
| | | if (! found) |
| | | { |
| | | attrList.add(a); |
| | | } |
| | | |
| | | RDN rdn = modifiedEntry.getDN().getRDN(); |
| | | if ((rdn != null) && rdn.hasAttributeType(t) && |
| | | (! modifiedEntry.hasValue(t, a.getOptions(), |
| | | rdn.getAttributeValue(t)))) |
| | | { |
| | | setResultCode(ResultCode.NOT_ALLOWED_ON_RDN); |
| | | |
| | | int msgID = MSGID_MODIFY_DELETE_RDN_ATTR; |
| | | appendErrorMessage(getMessage(msgID, String.valueOf(entryDN), |
| | | a.getName())); |
| | | break modifyProcessing; |
| | | } |
| | | break; |
| | | |
| | | |
| | | case INCREMENT: |
| | | // The specified attribute type must not be an RDN attribute. |
| | | rdn = modifiedEntry.getDN().getRDN(); |
| | | if ((rdn != null) && rdn.hasAttributeType(t)) |
| | | { |
| | | setResultCode(ResultCode.NOT_ALLOWED_ON_RDN); |
| | | appendErrorMessage(getMessage(MSGID_MODIFY_INCREMENT_RDN, |
| | | String.valueOf(entryDN), |
| | | a.getName())); |
| | | } |
| | | |
| | | |
| | | // The provided attribute must have a single value, and it must be |
| | | // an integer. |
| | | LinkedHashSet<AttributeValue> values = a.getValues(); |
| | | if ((values == null) || values.isEmpty()) |
| | | { |
| | | setResultCode(ResultCode.PROTOCOL_ERROR); |
| | | |
| | | int msgID = MSGID_MODIFY_INCREMENT_REQUIRES_VALUE; |
| | | appendErrorMessage(getMessage(msgID, String.valueOf(entryDN), |
| | | a.getName())); |
| | | |
| | | break modifyProcessing; |
| | | } |
| | | else if (values.size() > 1) |
| | | { |
| | | setResultCode(ResultCode.PROTOCOL_ERROR); |
| | | |
| | | int msgID = MSGID_MODIFY_INCREMENT_REQUIRES_SINGLE_VALUE; |
| | | appendErrorMessage(getMessage(msgID, String.valueOf(entryDN), |
| | | a.getName())); |
| | | break modifyProcessing; |
| | | } |
| | | |
| | | AttributeValue v = values.iterator().next(); |
| | | |
| | | long incrementValue; |
| | | try |
| | | { |
| | | incrementValue = Long.parseLong(v.getNormalizedStringValue()); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | |
| | | setResultCode(ResultCode.INVALID_ATTRIBUTE_SYNTAX); |
| | | |
| | | int msgID = MSGID_MODIFY_INCREMENT_PROVIDED_VALUE_NOT_INTEGER; |
| | | appendErrorMessage(getMessage(msgID, String.valueOf(entryDN), |
| | | a.getName(), v.getStringValue())); |
| | | |
| | | break modifyProcessing; |
| | | } |
| | | |
| | | |
| | | // Get the corresponding attribute from the entry and make sure |
| | | // that it has a single integer value. |
| | | attrList = modifiedEntry.getAttribute(t, a.getOptions()); |
| | | if ((attrList == null) || attrList.isEmpty()) |
| | | { |
| | | setResultCode(ResultCode.CONSTRAINT_VIOLATION); |
| | | |
| | | int msgID = MSGID_MODIFY_INCREMENT_REQUIRES_EXISTING_VALUE; |
| | | appendErrorMessage(getMessage(msgID, String.valueOf(entryDN), |
| | | a.getName())); |
| | | |
| | | break modifyProcessing; |
| | | } |
| | | |
| | | boolean updated = false; |
| | | for (Attribute attr : attrList) |
| | | { |
| | | LinkedHashSet<AttributeValue> valueList = attr.getValues(); |
| | | if ((valueList == null) || valueList.isEmpty()) |
| | | { |
| | | continue; |
| | | } |
| | | |
| | | LinkedHashSet<AttributeValue> newValueList = |
| | | new LinkedHashSet<AttributeValue>(valueList.size()); |
| | | for (AttributeValue existingValue : valueList) |
| | | { |
| | | long newIntValue; |
| | | try |
| | | { |
| | | long existingIntValue = |
| | | Long.parseLong(existingValue.getStringValue()); |
| | | newIntValue = existingIntValue + incrementValue; |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | |
| | | setResultCode(ResultCode.INVALID_ATTRIBUTE_SYNTAX); |
| | | |
| | | int msgID = MSGID_MODIFY_INCREMENT_REQUIRES_INTEGER_VALUE; |
| | | appendErrorMessage(getMessage(msgID, |
| | | String.valueOf(entryDN), |
| | | a.getName(), |
| | | existingValue.getStringValue())); |
| | | break modifyProcessing; |
| | | } |
| | | |
| | | ByteString newValue = |
| | | new ASN1OctetString(String.valueOf(newIntValue)); |
| | | newValueList.add(new AttributeValue(t, newValue)); |
| | | } |
| | | |
| | | attr.setValues(newValueList); |
| | | updated = true; |
| | | } |
| | | |
| | | if (! updated) |
| | | { |
| | | setResultCode(ResultCode.CONSTRAINT_VIOLATION); |
| | | |
| | | int msgID = MSGID_MODIFY_INCREMENT_REQUIRES_EXISTING_VALUE; |
| | | appendErrorMessage(getMessage(msgID, String.valueOf(entryDN), |
| | | a.getName())); |
| | | |
| | | break modifyProcessing; |
| | | } |
| | | |
| | | break; |
| | | |
| | | default: |
| | | } |
| | | } |
| | | |
| | | |
| | | // If there was a password change, then perform any additional checks |
| | | // that may be necessary. |
| | | if (passwordChanged) |
| | | { |
| | | // If it was a self change, then see if the current password was |
| | | // provided and handle accordingly. |
| | | if (selfChange && |
| | | pwPolicyState.getPolicy().requireCurrentPassword() && |
| | | (! currentPasswordProvided)) |
| | | { |
| | | setResultCode(ResultCode.UNWILLING_TO_PERFORM); |
| | | |
| | | int msgID = MSGID_MODIFY_PW_CHANGE_REQUIRES_CURRENT_PW; |
| | | appendErrorMessage(getMessage(msgID)); |
| | | break modifyProcessing; |
| | | } |
| | | |
| | | |
| | | // If this change would result in multiple password values, then see |
| | | // if that's OK. |
| | | if ((numPasswords > 1) && |
| | | (! pwPolicyState.getPolicy().allowMultiplePasswordValues())) |
| | | { |
| | | setResultCode(ResultCode.UNWILLING_TO_PERFORM); |
| | | |
| | | int msgID = MSGID_MODIFY_MULTIPLE_PASSWORDS_NOT_ALLOWED; |
| | | appendErrorMessage(getMessage(msgID)); |
| | | break modifyProcessing; |
| | | } |
| | | |
| | | |
| | | // If any of the password values should be validated, then do so now. |
| | | if (selfChange || |
| | | (! pwPolicyState.getPolicy().skipValidationForAdministrators())) |
| | | { |
| | | if (newPasswords != null) |
| | | { |
| | | HashSet<ByteString> clearPasswords = new HashSet<ByteString>(); |
| | | clearPasswords.addAll(pwPolicyState.getClearPasswords()); |
| | | |
| | | if (currentPasswords != null) |
| | | { |
| | | if (clearPasswords.isEmpty()) |
| | | { |
| | | for (AttributeValue v : currentPasswords) |
| | | { |
| | | clearPasswords.add(v.getValue()); |
| | | } |
| | | } |
| | | else |
| | | { |
| | | // NOTE: We can't rely on the fact that Set doesn't allow |
| | | // duplicates because technically it's possible that the |
| | | // values aren't duplicates if they are ASN.1 elements with |
| | | // different types (like 0x04 for a standard universal octet |
| | | // string type versus 0x80 for a simple password in a bind |
| | | // operation). So we have to manually check for duplicates. |
| | | for (AttributeValue v : currentPasswords) |
| | | { |
| | | ByteString pw = v.getValue(); |
| | | |
| | | boolean found = false; |
| | | for (ByteString s : clearPasswords) |
| | | { |
| | | if (Arrays.equals(s.value(), pw.value())) |
| | | { |
| | | found = true; |
| | | break; |
| | | } |
| | | } |
| | | |
| | | if (! found) |
| | | { |
| | | clearPasswords.add(pw); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | for (AttributeValue v : newPasswords) |
| | | { |
| | | StringBuilder invalidReason = new StringBuilder(); |
| | | if (! pwPolicyState.passwordIsAcceptable(this, modifiedEntry, |
| | | v.getValue(), |
| | | clearPasswords, |
| | | invalidReason)) |
| | | { |
| | | setResultCode(ResultCode.UNWILLING_TO_PERFORM); |
| | | |
| | | int msgID = MSGID_MODIFY_PW_VALIDATION_FAILED; |
| | | appendErrorMessage(getMessage(msgID, |
| | | invalidReason.toString())); |
| | | break modifyProcessing; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | // Check to see if the client has permission to perform the |
| | | // modify. |
| | | // The access control check is not made any earlier because the |
| | | // handler needs access to the modified entry. |
| | | |
| | | // FIXME: for now assume that this will check all permission |
| | | // pertinent to the operation. This includes proxy authorization |
| | | // and any other controls specified. |
| | | |
| | | // FIXME: earlier checks to see if the entry already exists may |
| | | // have already exposed sensitive information to the client. |
| | | if (!AccessControlConfigManager.getInstance() |
| | | .getAccessControlHandler().isAllowed(this)) { |
| | | setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS); |
| | | |
| | | int msgID = MSGID_MODIFY_AUTHZ_INSUFFICIENT_ACCESS_RIGHTS; |
| | | appendErrorMessage(getMessage(msgID, String.valueOf(entryDN))); |
| | | |
| | | skipPostOperation = true; |
| | | break modifyProcessing; |
| | | } |
| | | |
| | | boolean wasLocked = false; |
| | | if (passwordChanged) |
| | | { |
| | | // See if the account was locked for any reason. |
| | | wasLocked = pwPolicyState.lockedDueToIdleInterval() || |
| | | pwPolicyState.lockedDueToMaximumResetAge() || |
| | | pwPolicyState.lockedDueToFailures(); |
| | | |
| | | // Update the password policy state attributes in the user's entry. |
| | | // If the modification fails, then these changes won't be applied. |
| | | pwPolicyState.setPasswordChangedTime(); |
| | | pwPolicyState.clearFailureLockout(); |
| | | pwPolicyState.clearGraceLoginTimes(); |
| | | pwPolicyState.clearWarnedTime(); |
| | | |
| | | if(pwPolicyState.getPolicy().forceChangeOnAdd() |
| | | || pwPolicyState.getPolicy().forceChangeOnReset()) |
| | | { |
| | | if (selfChange) |
| | | { |
| | | pwPolicyState.setMustChangePassword(false); |
| | | } |
| | | else |
| | | { |
| | | pwPolicyState.setMustChangePassword( |
| | | pwPolicyState.getPolicy().forceChangeOnReset()); |
| | | } |
| | | } |
| | | |
| | | if (pwPolicyState.getPolicy().getRequireChangeByTime() > 0) |
| | | { |
| | | pwPolicyState.setRequiredChangeTime(); |
| | | } |
| | | modifications.addAll(pwPolicyState.getModifications()); |
| | | //Apply pwd Policy modifications to modified entry. |
| | | try { |
| | | modifiedEntry.applyModifications(pwPolicyState.getModifications()); |
| | | } catch (DirectoryException e) { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | |
| | | setResponseData(e); |
| | | break modifyProcessing; |
| | | } |
| | | } |
| | | else if ((! isInternalOperation()) && |
| | | pwPolicyState.mustChangePassword()) |
| | | { |
| | | // The user will not be allowed to do anything else before |
| | | // the password gets changed. |
| | | setResultCode(ResultCode.UNWILLING_TO_PERFORM); |
| | | |
| | | int msgID = MSGID_MODIFY_MUST_CHANGE_PASSWORD; |
| | | appendErrorMessage(getMessage(msgID)); |
| | | break modifyProcessing; |
| | | } |
| | | |
| | | // Make sure that the new entry is valid per the server schema. |
| | | if (DirectoryServer.checkSchema()) |
| | | { |
| | | StringBuilder invalidReason = new StringBuilder(); |
| | | if (! modifiedEntry.conformsToSchema(null, false, false, false, |
| | | invalidReason)) |
| | | { |
| | | setResultCode(ResultCode.OBJECTCLASS_VIOLATION); |
| | | appendErrorMessage(getMessage(MSGID_MODIFY_VIOLATES_SCHEMA, |
| | | String.valueOf(entryDN), |
| | | invalidReason.toString())); |
| | | break modifyProcessing; |
| | | } |
| | | } |
| | | |
| | | // Check for and handle a request to cancel this operation. |
| | | if (cancelRequest != null) |
| | | { |
| | | indicateCancelled(cancelRequest); |
| | | processingStopTime = System.currentTimeMillis(); |
| | | logModifyResponse(this); |
| | | pluginConfigManager.invokePostResponseModifyPlugins(this); |
| | | return; |
| | | } |
| | | |
| | | // If the operation is not a synchronization operation, |
| | | // Invoke the pre-operation modify plugins. |
| | | if (!isSynchronizationOperation()) |
| | | { |
| | | PreOperationPluginResult preOpResult = |
| | | pluginConfigManager.invokePreOperationModifyPlugins(this); |
| | | if (preOpResult.connectionTerminated()) |
| | | { |
| | | // There's no point in continuing with anything. Log the result |
| | | // and return. |
| | | setResultCode(ResultCode.CANCELED); |
| | | |
| | | int msgID = MSGID_CANCELED_BY_PREOP_DISCONNECT; |
| | | appendErrorMessage(getMessage(msgID)); |
| | | |
| | | processingStopTime = System.currentTimeMillis(); |
| | | |
| | | logModifyResponse(this); |
| | | pluginConfigManager.invokePostResponseModifyPlugins(this); |
| | | return; |
| | | } |
| | | else if (preOpResult.sendResponseImmediately()) |
| | | { |
| | | skipPostOperation = true; |
| | | break modifyProcessing; |
| | | } |
| | | else if (preOpResult.skipCoreProcessing()) |
| | | { |
| | | skipPostOperation = false; |
| | | break modifyProcessing; |
| | | } |
| | | } |
| | | |
| | | |
| | | // Check for and handle a request to cancel this operation. |
| | | if (cancelRequest != null) |
| | | { |
| | | indicateCancelled(cancelRequest); |
| | | processingStopTime = System.currentTimeMillis(); |
| | | logModifyResponse(this); |
| | | pluginConfigManager.invokePostResponseModifyPlugins(this); |
| | | return; |
| | | } |
| | | |
| | | |
| | | // Actually perform the modify operation. This should also include |
| | | // taking care of any synchronization that might be needed. |
| | | Backend backend = DirectoryServer.getBackend(entryDN); |
| | | if (backend == null) |
| | | { |
| | | setResultCode(ResultCode.NO_SUCH_OBJECT); |
| | | appendErrorMessage(getMessage(MSGID_MODIFY_NO_BACKEND_FOR_ENTRY, |
| | | String.valueOf(entryDN))); |
| | | break modifyProcessing; |
| | | } |
| | | |
| | | try |
| | | { |
| | | // If it is not a private backend, then check to see if the server or |
| | | // backend is operating in read-only mode. |
| | | if (! backend.isPrivateBackend()) |
| | | { |
| | | switch (DirectoryServer.getWritabilityMode()) |
| | | { |
| | | case DISABLED: |
| | | setResultCode(ResultCode.UNWILLING_TO_PERFORM); |
| | | appendErrorMessage(getMessage(MSGID_MODIFY_SERVER_READONLY, |
| | | String.valueOf(entryDN))); |
| | | break modifyProcessing; |
| | | |
| | | case INTERNAL_ONLY: |
| | | if (! (isInternalOperation() || isSynchronizationOperation())) |
| | | { |
| | | setResultCode(ResultCode.UNWILLING_TO_PERFORM); |
| | | appendErrorMessage(getMessage(MSGID_MODIFY_SERVER_READONLY, |
| | | String.valueOf(entryDN))); |
| | | break modifyProcessing; |
| | | } |
| | | } |
| | | |
| | | switch (backend.getWritabilityMode()) |
| | | { |
| | | case DISABLED: |
| | | setResultCode(ResultCode.UNWILLING_TO_PERFORM); |
| | | appendErrorMessage(getMessage(MSGID_MODIFY_BACKEND_READONLY, |
| | | String.valueOf(entryDN))); |
| | | break modifyProcessing; |
| | | |
| | | case INTERNAL_ONLY: |
| | | if (! (isInternalOperation() || isSynchronizationOperation())) |
| | | { |
| | | setResultCode(ResultCode.UNWILLING_TO_PERFORM); |
| | | appendErrorMessage(getMessage(MSGID_MODIFY_BACKEND_READONLY, |
| | | String.valueOf(entryDN))); |
| | | break modifyProcessing; |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | if (noOp) |
| | | { |
| | | appendErrorMessage(getMessage(MSGID_MODIFY_NOOP)); |
| | | |
| | | // FIXME -- We must set a result code other than SUCCESS. |
| | | } |
| | | else |
| | | { |
| | | for (SynchronizationProvider provider : |
| | | DirectoryServer.getSynchronizationProviders()) |
| | | { |
| | | try |
| | | { |
| | | SynchronizationProviderResult result = |
| | | provider.doPreOperation(this); |
| | | if (! result.continueOperationProcessing()) |
| | | { |
| | | break modifyProcessing; |
| | | } |
| | | } |
| | | catch (DirectoryException de) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, de); |
| | | } |
| | | |
| | | logError(ErrorLogCategory.SYNCHRONIZATION, |
| | | ErrorLogSeverity.SEVERE_ERROR, |
| | | MSGID_MODIFY_SYNCH_PREOP_FAILED, getConnectionID(), |
| | | getOperationID(), getExceptionMessage(de)); |
| | | |
| | | setResponseData(de); |
| | | break modifyProcessing; |
| | | } |
| | | } |
| | | |
| | | backend.replaceEntry(modifiedEntry, this); |
| | | |
| | | |
| | | // If the modification was successful, then see if there's any other |
| | | // work that we need to do here before handing off to postop |
| | | // plugins. |
| | | if (passwordChanged) |
| | | { |
| | | if (selfChange) |
| | | { |
| | | AuthenticationInfo authInfo = |
| | | clientConnection.getAuthenticationInfo(); |
| | | if (authInfo.getAuthenticationDN().equals(entryDN)) |
| | | { |
| | | clientConnection.setMustChangePassword(false); |
| | | } |
| | | |
| | | int msgID = MSGID_MODIFY_PASSWORD_CHANGED; |
| | | String message = getMessage(msgID); |
| | | pwPolicyState.generateAccountStatusNotification( |
| | | AccountStatusNotificationType.PASSWORD_CHANGED, entryDN, |
| | | msgID, message); |
| | | } |
| | | else |
| | | { |
| | | int msgID = MSGID_MODIFY_PASSWORD_RESET; |
| | | String message = getMessage(msgID); |
| | | pwPolicyState.generateAccountStatusNotification( |
| | | AccountStatusNotificationType.PASSWORD_RESET, entryDN, |
| | | msgID, message); |
| | | } |
| | | } |
| | | |
| | | if (enabledStateChanged) |
| | | { |
| | | if (isEnabled) |
| | | { |
| | | int msgID = MSGID_MODIFY_ACCOUNT_ENABLED; |
| | | String message = getMessage(msgID); |
| | | pwPolicyState.generateAccountStatusNotification( |
| | | AccountStatusNotificationType.ACCOUNT_ENABLED, entryDN, |
| | | msgID, message); |
| | | } |
| | | else |
| | | { |
| | | int msgID = MSGID_MODIFY_ACCOUNT_DISABLED; |
| | | String message = getMessage(msgID); |
| | | pwPolicyState.generateAccountStatusNotification( |
| | | AccountStatusNotificationType.ACCOUNT_DISABLED, entryDN, |
| | | msgID, message); |
| | | } |
| | | } |
| | | |
| | | if (wasLocked) |
| | | { |
| | | int msgID = MSGID_MODIFY_ACCOUNT_UNLOCKED; |
| | | String message = getMessage(msgID); |
| | | pwPolicyState.generateAccountStatusNotification( |
| | | AccountStatusNotificationType.ACCOUNT_UNLOCKED, entryDN, |
| | | msgID, message); |
| | | } |
| | | } |
| | | |
| | | 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); |
| | | |
| | | responseControls.add(responseControl); |
| | | } |
| | | |
| | | if (postReadRequest != null) |
| | | { |
| | | Entry entry = modifiedEntry.duplicate(true); |
| | | |
| | | if (! postReadRequest.allowsAttribute( |
| | | DirectoryServer.getObjectClassAttributeType())) |
| | | { |
| | | entry.removeAttribute( |
| | | DirectoryServer.getObjectClassAttributeType()); |
| | | } |
| | | |
| | | if (! postReadRequest.returnAllUserAttributes()) |
| | | { |
| | | Iterator<AttributeType> iterator = |
| | | entry.getUserAttributes().keySet().iterator(); |
| | | while (iterator.hasNext()) |
| | | { |
| | | AttributeType attrType = iterator.next(); |
| | | if (! postReadRequest.allowsAttribute(attrType)) |
| | | { |
| | | iterator.remove(); |
| | | } |
| | | } |
| | | } |
| | | |
| | | if (! postReadRequest.returnAllOperationalAttributes()) |
| | | { |
| | | Iterator<AttributeType> iterator = |
| | | entry.getOperationalAttributes().keySet().iterator(); |
| | | while (iterator.hasNext()) |
| | | { |
| | | AttributeType attrType = iterator.next(); |
| | | if (! postReadRequest.allowsAttribute(attrType)) |
| | | { |
| | | iterator.remove(); |
| | | } |
| | | } |
| | | } |
| | | |
| | | // FIXME -- Check access controls on the entry to see if it should |
| | | // be returned or if any attributes need to be stripped |
| | | // out.. |
| | | SearchResultEntry searchEntry = new SearchResultEntry(entry); |
| | | LDAPPostReadResponseControl responseControl = |
| | | new LDAPPostReadResponseControl(postReadRequest.getOID(), |
| | | postReadRequest.isCritical(), |
| | | searchEntry); |
| | | |
| | | responseControls.add(responseControl); |
| | | } |
| | | |
| | | setResultCode(ResultCode.SUCCESS); |
| | | } |
| | | catch (DirectoryException de) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, de); |
| | | } |
| | | |
| | | setResultCode(de.getResultCode()); |
| | | appendErrorMessage(de.getErrorMessage()); |
| | | setMatchedDN(de.getMatchedDN()); |
| | | setReferralURLs(de.getReferralURLs()); |
| | | |
| | | break modifyProcessing; |
| | | } |
| | | catch (CancelledOperationException coe) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, coe); |
| | | } |
| | | |
| | | CancelResult cancelResult = coe.getCancelResult(); |
| | | |
| | | setCancelResult(cancelResult); |
| | | setResultCode(cancelResult.getResultCode()); |
| | | |
| | | String message = coe.getMessage(); |
| | | if ((message != null) && (message.length() > 0)) |
| | | { |
| | | appendErrorMessage(message); |
| | | } |
| | | |
| | | break modifyProcessing; |
| | | } |
| | | } |
| | | finally |
| | | { |
| | | LockManager.unlock(entryDN, entryLock); |
| | | |
| | | for (SynchronizationProvider provider : |
| | | DirectoryServer.getSynchronizationProviders()) |
| | | { |
| | | try |
| | | { |
| | | provider.doPostOperation(this); |
| | | } |
| | | catch (DirectoryException de) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, de); |
| | | } |
| | | |
| | | logError(ErrorLogCategory.SYNCHRONIZATION, |
| | | ErrorLogSeverity.SEVERE_ERROR, |
| | | MSGID_MODIFY_SYNCH_POSTOP_FAILED, 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 modify plugins. |
| | | if (! skipPostOperation) |
| | | { |
| | | // FIXME -- Should this also be done while holding the locks? |
| | | PostOperationPluginResult postOpResult = |
| | | pluginConfigManager.invokePostOperationModifyPlugins(this); |
| | | if (postOpResult.connectionTerminated()) |
| | | { |
| | | // There's no point in continuing with anything. Log the result and |
| | | // return. |
| | | setResultCode(ResultCode.CANCELED); |
| | | |
| | | int msgID = MSGID_CANCELED_BY_PREOP_DISCONNECT; |
| | | appendErrorMessage(getMessage(msgID)); |
| | | |
| | | processingStopTime = System.currentTimeMillis(); |
| | | |
| | | logModifyResponse(this); |
| | | pluginConfigManager.invokePostResponseModifyPlugins(this); |
| | | return; |
| | | } |
| | | } |
| | | |
| | | |
| | | // Notify any change notification listeners that might be registered with |
| | | // the server. |
| | | if (getResultCode() == ResultCode.SUCCESS) |
| | | { |
| | | for (ChangeNotificationListener changeListener : |
| | | DirectoryServer.getChangeNotificationListeners()) |
| | | { |
| | | try |
| | | { |
| | | changeListener.handleModifyOperation(this, currentEntry, |
| | | modifiedEntry); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | |
| | | int msgID = MSGID_MODIFY_ERROR_NOTIFYING_CHANGE_LISTENER; |
| | | String message = getMessage(msgID, getExceptionMessage(e)); |
| | | logError(ErrorLogCategory.CORE_SERVER, ErrorLogSeverity.SEVERE_ERROR, |
| | | message, msgID); |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | // Stop the processing timer. |
| | | processingStopTime = System.currentTimeMillis(); |
| | | |
| | | |
| | | // Send the modify response to the client. |
| | | clientConnection.sendResponse(this); |
| | | |
| | | |
| | | // Log the modify response. |
| | | logModifyResponse(this); |
| | | |
| | | |
| | | // Notify any persistent searches that might be registered with the server. |
| | | if (getResultCode() == ResultCode.SUCCESS) |
| | | { |
| | | for (PersistentSearch persistentSearch : |
| | | DirectoryServer.getPersistentSearches()) |
| | | { |
| | | try |
| | | { |
| | | persistentSearch.processModify(this, currentEntry, modifiedEntry); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | |
| | | int msgID = MSGID_MODIFY_ERROR_NOTIFYING_PERSISTENT_SEARCH; |
| | | String message = getMessage(msgID, String.valueOf(persistentSearch), |
| | | getExceptionMessage(e)); |
| | | logError(ErrorLogCategory.CORE_SERVER, ErrorLogSeverity.SEVERE_ERROR, |
| | | message, msgID); |
| | | |
| | | DirectoryServer.deregisterPersistentSearch(persistentSearch); |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | // Invoke the post-response modify plugins. |
| | | pluginConfigManager.invokePostResponseModifyPlugins(this); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final CancelResult cancel(CancelRequest cancelRequest) |
| | | { |
| | | this.cancelRequest = cancelRequest; |
| | | |
| | | CancelResult cancelResult = getCancelResult(); |
| | | long stopWaitingTime = System.currentTimeMillis() + 5000; |
| | | while ((cancelResult == null) && |
| | | (System.currentTimeMillis() < stopWaitingTime)) |
| | | { |
| | | try |
| | | { |
| | | Thread.sleep(50); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | } |
| | | |
| | | cancelResult = getCancelResult(); |
| | | } |
| | | |
| | | if (cancelResult == null) |
| | | { |
| | | // This can happen in some rare cases (e.g., if a client disconnects and |
| | | // there is still a lot of data to send to that client), and in this case |
| | | // we'll prevent the cancel thread from blocking for a long period of |
| | | // time. |
| | | cancelResult = CancelResult.CANNOT_CANCEL; |
| | | } |
| | | |
| | | return cancelResult; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final CancelRequest getCancelRequest() |
| | | { |
| | | return cancelRequest; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | protected boolean setCancelRequest(CancelRequest cancelRequest) |
| | | { |
| | | this.cancelRequest = cancelRequest; |
| | | return true; |
| | | } |
| | | |
| | | |
| | | public abstract void setProxiedAuthorizationDN(DN proxiedAuthorizationDN); |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final void toString(StringBuilder buffer) |
| | | { |
| | | buffer.append("ModifyOperation(connID="); |
| | | buffer.append(clientConnection.getConnectionID()); |
| | | buffer.append(", opID="); |
| | | buffer.append(operationID); |
| | | buffer.append(", dn="); |
| | | buffer.append(rawEntryDN); |
| | | buffer.append(")"); |
| | | } |
| | | } |
| | | |
| New file |
| | |
| | | /* |
| | | * 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 |
| | | * trunk/opends/resource/legal-notices/OpenDS.LICENSE |
| | | * or https://OpenDS.dev.java.net/OpenDS.LICENSE. |
| | | * 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 |
| | | * trunk/opends/resource/legal-notices/OpenDS.LICENSE. 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 |
| | | * |
| | | * |
| | | * Portions Copyright 2007 Sun Microsystems, Inc. |
| | | */ |
| | | package org.opends.server.core; |
| | | |
| | | |
| | | |
| | | import static org.opends.server.core.CoreConstants.LOG_ELEMENT_ENTRY_DN; |
| | | import static org.opends.server.core.CoreConstants.LOG_ELEMENT_ERROR_MESSAGE; |
| | | import static org.opends.server.core.CoreConstants.LOG_ELEMENT_MATCHED_DN; |
| | | import static org.opends.server.core.CoreConstants.LOG_ELEMENT_PROCESSING_TIME; |
| | | import static org.opends.server.core.CoreConstants.LOG_ELEMENT_REFERRAL_URLS; |
| | | import static org.opends.server.core.CoreConstants.LOG_ELEMENT_RESULT_CODE; |
| | | import static org.opends.server.loggers.AccessLogger.logModifyRequest; |
| | | import static org.opends.server.loggers.AccessLogger.logModifyResponse; |
| | | import static org.opends.server.loggers.ErrorLogger.logError; |
| | | import static org.opends.server.loggers.debug.DebugLogger.debugEnabled; |
| | | import static org.opends.server.messages.CoreMessages.*; |
| | | import static org.opends.server.messages.MessageHandler.getMessage; |
| | | import static org.opends.server.util.StaticUtils.getExceptionMessage; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.Iterator; |
| | | import java.util.List; |
| | | |
| | | import org.opends.server.api.ClientConnection; |
| | | import org.opends.server.api.plugin.PreParsePluginResult; |
| | | import org.opends.server.loggers.debug.DebugLogger; |
| | | import org.opends.server.loggers.debug.DebugTracer; |
| | | import org.opends.server.protocols.asn1.ASN1OctetString; |
| | | import org.opends.server.protocols.ldap.LDAPAttribute; |
| | | import org.opends.server.types.LDAPException; |
| | | import org.opends.server.protocols.ldap.LDAPModification; |
| | | import org.opends.server.types.AttributeValue; |
| | | import org.opends.server.types.Entry; |
| | | import org.opends.server.types.ErrorLogCategory; |
| | | import org.opends.server.types.ErrorLogSeverity; |
| | | import org.opends.server.types.Operation; |
| | | import org.opends.server.types.RawModification; |
| | | import org.opends.server.types.AbstractOperation; |
| | | import org.opends.server.types.ByteString; |
| | | import org.opends.server.types.CancelRequest; |
| | | import org.opends.server.types.CancelResult; |
| | | import org.opends.server.types.Control; |
| | | import org.opends.server.types.DN; |
| | | import org.opends.server.types.DebugLogLevel; |
| | | import org.opends.server.types.DirectoryException; |
| | | import org.opends.server.types.DisconnectReason; |
| | | import org.opends.server.types.Modification; |
| | | import org.opends.server.types.OperationType; |
| | | import org.opends.server.types.ResultCode; |
| | | import org.opends.server.types.operation.PostResponseModifyOperation; |
| | | import org.opends.server.types.operation.PreParseModifyOperation; |
| | | import org.opends.server.workflowelement.localbackend.*; |
| | | |
| | | |
| | | |
| | | /** |
| | | * This class defines an operation that may be used to modify an entry in the |
| | | * Directory Server. |
| | | */ |
| | | public class ModifyOperationBasis |
| | | extends AbstractOperation implements ModifyOperation, |
| | | PreParseModifyOperation, |
| | | PostResponseModifyOperation |
| | | { |
| | | |
| | | /** |
| | | * The tracer object for the debug logger. |
| | | */ |
| | | private static final DebugTracer TRACER = DebugLogger.getTracer(); |
| | | |
| | | // The raw, unprocessed entry DN as included by the client request. |
| | | private ByteString rawEntryDN; |
| | | |
| | | // The DN of the entry for the modify operation. |
| | | private DN entryDN; |
| | | |
| | | // The proxied authorization target DN for this operation. |
| | | private DN proxiedAuthorizationDN; |
| | | |
| | | // The set of response controls for this modify operation. |
| | | private List<Control> responseControls; |
| | | |
| | | // The raw, unprocessed set of modifications as included in the client |
| | | // request. |
| | | private List<RawModification> rawModifications; |
| | | |
| | | // The set of modifications for this modify operation. |
| | | private List<Modification> modifications; |
| | | |
| | | // The cancel request that has been issued for this modify operation. |
| | | CancelRequest cancelRequest; |
| | | |
| | | // The change number that has been assigned to this operation. |
| | | private long changeNumber; |
| | | |
| | | /** |
| | | * Creates a new modify operation with the provided information. |
| | | * |
| | | * @param clientConnection The client connection with which this operation |
| | | * is associated. |
| | | * @param operationID The operation ID for this operation. |
| | | * @param messageID The message ID of the request with which this |
| | | * operation is associated. |
| | | * @param requestControls The set of controls included in the request. |
| | | * @param rawEntryDN The raw, unprocessed DN of the entry to modify, |
| | | * as included in the client request. |
| | | * @param rawModifications The raw, unprocessed set of modifications for |
| | | * this modify operation as included in the client |
| | | * request. |
| | | */ |
| | | public ModifyOperationBasis(ClientConnection clientConnection, |
| | | long operationID, |
| | | int messageID, List<Control> requestControls, |
| | | ByteString rawEntryDN, |
| | | List<RawModification> rawModifications) |
| | | { |
| | | super(clientConnection, operationID, messageID, requestControls); |
| | | |
| | | |
| | | this.rawEntryDN = rawEntryDN; |
| | | this.rawModifications = rawModifications; |
| | | |
| | | entryDN = null; |
| | | modifications = null; |
| | | responseControls = new ArrayList<Control>(); |
| | | cancelRequest = null; |
| | | } |
| | | |
| | | /** |
| | | * Creates a new modify operation with the provided information. |
| | | * |
| | | * @param clientConnection The client connection with which this operation |
| | | * is associated. |
| | | * @param operationID The operation ID for this operation. |
| | | * @param messageID The message ID of the request with which this |
| | | * operation is associated. |
| | | * @param requestControls The set of controls included in the request. |
| | | * @param entryDN The entry DN for the modify operation. |
| | | * @param modifications The set of modifications for this modify |
| | | * operation. |
| | | */ |
| | | public ModifyOperationBasis(ClientConnection clientConnection, |
| | | long operationID, |
| | | int messageID, List<Control> requestControls, |
| | | DN entryDN, List<Modification> modifications) |
| | | { |
| | | super(clientConnection, operationID, messageID, requestControls); |
| | | |
| | | |
| | | this.entryDN = entryDN; |
| | | this.modifications = modifications; |
| | | |
| | | rawEntryDN = new ASN1OctetString(entryDN.toString()); |
| | | |
| | | rawModifications = new ArrayList<RawModification>(modifications.size()); |
| | | for (Modification m : modifications) |
| | | { |
| | | rawModifications.add(new LDAPModification(m.getModificationType(), |
| | | new LDAPAttribute(m.getAttribute()))); |
| | | } |
| | | |
| | | responseControls = new ArrayList<Control>(); |
| | | cancelRequest = null; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public final ByteString getRawEntryDN() |
| | | { |
| | | return rawEntryDN; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public final void setRawEntryDN(ByteString rawEntryDN) |
| | | { |
| | | this.rawEntryDN = rawEntryDN; |
| | | |
| | | entryDN = null; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public final DN getEntryDN() |
| | | { |
| | | if (entryDN == null){ |
| | | try { |
| | | entryDN = DN.decode(rawEntryDN); |
| | | } |
| | | catch (DirectoryException de) { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, de); |
| | | } |
| | | |
| | | setResultCode(de.getResultCode()); |
| | | appendErrorMessage(de.getErrorMessage()); |
| | | } |
| | | } |
| | | return entryDN; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public final List<RawModification> getRawModifications() |
| | | { |
| | | return rawModifications; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public final void addRawModification(RawModification rawModification) |
| | | { |
| | | rawModifications.add(rawModification); |
| | | |
| | | modifications = null; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public final void setRawModifications(List<RawModification> rawModifications) |
| | | { |
| | | this.rawModifications = rawModifications; |
| | | |
| | | modifications = null; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public final List<Modification> getModifications() |
| | | { |
| | | if (modifications == null) |
| | | { |
| | | modifications = new ArrayList<Modification>(rawModifications.size()); |
| | | try { |
| | | for (RawModification m : rawModifications) |
| | | { |
| | | modifications.add(m.toModification()); |
| | | } |
| | | } |
| | | catch (LDAPException le) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, le); |
| | | } |
| | | setResultCode(ResultCode.valueOf(le.getResultCode())); |
| | | appendErrorMessage(le.getMessage()); |
| | | modifications = null; |
| | | } |
| | | } |
| | | return modifications; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public final void addModification(Modification modification) |
| | | throws DirectoryException |
| | | { |
| | | modifications.add(modification); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public final void disconnectClient(DisconnectReason disconnectReason, |
| | | boolean sendNotification, String message, |
| | | int messageID) |
| | | { |
| | | // Before calling clientConnection.disconnect, we need to mark this |
| | | // operation as cancelled so that the attempt to cancel it later won't cause |
| | | // an unnecessary delay. |
| | | setCancelResult(CancelResult.CANCELED); |
| | | |
| | | clientConnection.disconnect(disconnectReason, sendNotification, message, |
| | | messageID); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public final OperationType getOperationType() |
| | | { |
| | | // Note that no debugging will be done in this method because it is a likely |
| | | // candidate for being called by the logging subsystem. |
| | | |
| | | return OperationType.MODIFY; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public final String[][] getRequestLogElements() |
| | | { |
| | | // Note that no debugging will be done in this method because it is a likely |
| | | // candidate for being called by the logging subsystem. |
| | | |
| | | return new String[][] |
| | | { |
| | | new String[] { LOG_ELEMENT_ENTRY_DN, String.valueOf(rawEntryDN) } |
| | | }; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public final String[][] getResponseLogElements() |
| | | { |
| | | // Note that no debugging will be done in this method because it is a likely |
| | | // candidate for being called by the logging subsystem. |
| | | |
| | | String resultCode = String.valueOf(getResultCode().getIntValue()); |
| | | |
| | | String errorMessage; |
| | | StringBuilder errorMessageBuffer = getErrorMessage(); |
| | | if (errorMessageBuffer == null) |
| | | { |
| | | errorMessage = null; |
| | | } |
| | | else |
| | | { |
| | | errorMessage = errorMessageBuffer.toString(); |
| | | } |
| | | |
| | | String matchedDNStr; |
| | | DN matchedDN = getMatchedDN(); |
| | | if (matchedDN == null) |
| | | { |
| | | matchedDNStr = null; |
| | | } |
| | | else |
| | | { |
| | | matchedDNStr = matchedDN.toString(); |
| | | } |
| | | |
| | | String referrals; |
| | | List<String> referralURLs = getReferralURLs(); |
| | | if ((referralURLs == null) || referralURLs.isEmpty()) |
| | | { |
| | | referrals = null; |
| | | } |
| | | else |
| | | { |
| | | StringBuilder buffer = new StringBuilder(); |
| | | Iterator<String> iterator = referralURLs.iterator(); |
| | | buffer.append(iterator.next()); |
| | | |
| | | while (iterator.hasNext()) |
| | | { |
| | | buffer.append(", "); |
| | | buffer.append(iterator.next()); |
| | | } |
| | | |
| | | referrals = buffer.toString(); |
| | | } |
| | | |
| | | String processingTime = |
| | | String.valueOf(getProcessingTime()); |
| | | |
| | | return new String[][] |
| | | { |
| | | new String[] { LOG_ELEMENT_RESULT_CODE, resultCode }, |
| | | new String[] { LOG_ELEMENT_ERROR_MESSAGE, errorMessage }, |
| | | new String[] { LOG_ELEMENT_MATCHED_DN, matchedDNStr }, |
| | | new String[] { LOG_ELEMENT_REFERRAL_URLS, referrals }, |
| | | new String[] { LOG_ELEMENT_PROCESSING_TIME, processingTime } |
| | | }; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public DN getProxiedAuthorizationDN() |
| | | { |
| | | return proxiedAuthorizationDN; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public final List<Control> getResponseControls() |
| | | { |
| | | return responseControls; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public final void addResponseControl(Control control) |
| | | { |
| | | responseControls.add(control); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public final void removeResponseControl(Control control) |
| | | { |
| | | responseControls.remove(control); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public final CancelResult cancel(CancelRequest cancelRequest) |
| | | { |
| | | this.cancelRequest = cancelRequest; |
| | | |
| | | CancelResult cancelResult = getCancelResult(); |
| | | long stopWaitingTime = System.currentTimeMillis() + 5000; |
| | | while ((cancelResult == null) && |
| | | (System.currentTimeMillis() < stopWaitingTime)) |
| | | { |
| | | try |
| | | { |
| | | Thread.sleep(50); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | } |
| | | |
| | | cancelResult = getCancelResult(); |
| | | } |
| | | |
| | | if (cancelResult == null) |
| | | { |
| | | // This can happen in some rare cases (e.g., if a client disconnects and |
| | | // there is still a lot of data to send to that client), and in this case |
| | | // we'll prevent the cancel thread from blocking for a long period of |
| | | // time. |
| | | cancelResult = CancelResult.CANNOT_CANCEL; |
| | | } |
| | | |
| | | return cancelResult; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public final CancelRequest getCancelRequest() |
| | | { |
| | | return cancelRequest; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public boolean setCancelRequest(CancelRequest cancelRequest) |
| | | { |
| | | this.cancelRequest = cancelRequest; |
| | | return true; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public final void toString(StringBuilder buffer) |
| | | { |
| | | buffer.append("ModifyOperation(connID="); |
| | | buffer.append(clientConnection.getConnectionID()); |
| | | buffer.append(", opID="); |
| | | buffer.append(operationID); |
| | | buffer.append(", dn="); |
| | | buffer.append(rawEntryDN); |
| | | buffer.append(")"); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public final long getChangeNumber(){ |
| | | return changeNumber; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setChangeNumber(long changeNumber) |
| | | { |
| | | this.changeNumber = changeNumber; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setProxiedAuthorizationDN(DN proxiedAuthorizationDN) |
| | | { |
| | | this.proxiedAuthorizationDN = proxiedAuthorizationDN; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public final void run() |
| | | { |
| | | setResultCode(ResultCode.UNDEFINED); |
| | | |
| | | // Get the plugin config manager that will be used for invoking plugins. |
| | | PluginConfigManager pluginConfigManager = |
| | | DirectoryServer.getPluginConfigManager(); |
| | | |
| | | // Start the processing timer. |
| | | setProcessingStartTime(); |
| | | |
| | | // Check for and handle a request to cancel this operation. |
| | | if (getCancelRequest() != null) |
| | | { |
| | | indicateCancelled(getCancelRequest()); |
| | | setProcessingStopTime(); |
| | | return; |
| | | } |
| | | |
| | | |
| | | // Create a labeled block of code that we can break out of if a problem is |
| | | // detected. |
| | | modifyProcessing: |
| | | { |
| | | // Invoke the pre-parse modify plugins. |
| | | PreParsePluginResult preParseResult = |
| | | pluginConfigManager.invokePreParseModifyPlugins(this); |
| | | if (preParseResult.connectionTerminated()) |
| | | { |
| | | // There's no point in continuing with anything. Log the request and |
| | | // result and return. |
| | | setResultCode(ResultCode.CANCELED); |
| | | |
| | | int msgID = MSGID_CANCELED_BY_PREPARSE_DISCONNECT; |
| | | appendErrorMessage(getMessage(msgID)); |
| | | |
| | | setProcessingStopTime(); |
| | | |
| | | logModifyRequest(this); |
| | | logModifyResponse(this); |
| | | pluginConfigManager.invokePostResponseModifyPlugins(this); |
| | | return; |
| | | } |
| | | else if (preParseResult.sendResponseImmediately()) |
| | | { |
| | | logModifyRequest(this); |
| | | break modifyProcessing; |
| | | } |
| | | else if (preParseResult.skipCoreProcessing()) |
| | | { |
| | | break modifyProcessing; |
| | | } |
| | | |
| | | // Log the modify request message. |
| | | logModifyRequest(this); |
| | | |
| | | // Check for and handle a request to cancel this operation. |
| | | if (getCancelRequest() != null) |
| | | { |
| | | indicateCancelled(getCancelRequest()); |
| | | setProcessingStopTime(); |
| | | pluginConfigManager.invokePostResponseModifyPlugins(this); |
| | | return; |
| | | } |
| | | |
| | | |
| | | // Process the entry DN to convert it from the raw form to the form |
| | | // required for the rest of the modify processing. |
| | | DN entryDN = getEntryDN(); |
| | | if (entryDN == null){ |
| | | break modifyProcessing; |
| | | } |
| | | |
| | | |
| | | // Retrieve the network group attached to the client connection |
| | | // and get a workflow to process the operation. |
| | | NetworkGroup ng = getClientConnection().getNetworkGroup(); |
| | | Workflow workflow = ng.getWorkflowCandidate(entryDN); |
| | | if (workflow == null) |
| | | { |
| | | // We have found no workflow for the requested base DN, just return |
| | | // a no such entry result code and stop the processing. |
| | | updateOperationErrMsgAndResCode(); |
| | | break modifyProcessing; |
| | | } |
| | | workflow.execute(this); |
| | | } |
| | | |
| | | // Check for and handle a request to cancel this operation. |
| | | if ((getCancelRequest() != null) || |
| | | (getCancelResult() == CancelResult.CANCELED)) |
| | | { |
| | | if (getCancelRequest() != null){ |
| | | indicateCancelled(getCancelRequest()); |
| | | } |
| | | setProcessingStopTime(); |
| | | logModifyResponse(this); |
| | | invokePostResponsePlugins(); |
| | | return; |
| | | } |
| | | |
| | | // Indicate that it is now too late to attempt to cancel the operation. |
| | | setCancelResult(CancelResult.TOO_LATE); |
| | | |
| | | // -- DONE AT A LOWER LEVEL -- |
| | | // Notify any change notification listeners that might be registered with |
| | | // the server. |
| | | |
| | | // Stop the processing timer. |
| | | setProcessingStopTime(); |
| | | |
| | | // Send the modify response to the client. |
| | | getClientConnection().sendResponse(this); |
| | | |
| | | // Log the modify response. |
| | | logModifyResponse(this); |
| | | |
| | | // Check wether there are local operations in attachments |
| | | List localOperations = |
| | | (List)getAttachment(Operation.LOCALBACKENDOPERATIONS); |
| | | if (localOperations != null && (! localOperations.isEmpty())){ |
| | | for (Object localOp : localOperations) |
| | | { |
| | | LocalBackendModifyOperation localOperation = |
| | | (LocalBackendModifyOperation)localOp; |
| | | // Notify any persistent searches that might be registered with |
| | | // the server. |
| | | if (localOperation.getResultCode() == ResultCode.SUCCESS) |
| | | { |
| | | for (PersistentSearch persistentSearch : |
| | | DirectoryServer.getPersistentSearches()) |
| | | { |
| | | try |
| | | { |
| | | persistentSearch.processModify(localOperation, |
| | | localOperation.getCurrentEntry(), |
| | | localOperation.getModifiedEntry()); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | |
| | | int msgID = MSGID_MODIFY_ERROR_NOTIFYING_PERSISTENT_SEARCH; |
| | | String message = getMessage(msgID, |
| | | String.valueOf(persistentSearch), |
| | | getExceptionMessage(e)); |
| | | logError(ErrorLogCategory.CORE_SERVER, |
| | | ErrorLogSeverity.SEVERE_ERROR, |
| | | message, msgID); |
| | | |
| | | DirectoryServer.deregisterPersistentSearch(persistentSearch); |
| | | } |
| | | } |
| | | } |
| | | |
| | | // Invoke the post-response modify plugins. |
| | | pluginConfigManager.invokePostResponseModifyPlugins(localOperation); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Updates the error message and the result code of the operation. |
| | | * |
| | | * This method is called because no workflows were found to process |
| | | * the operation. |
| | | */ |
| | | private void updateOperationErrMsgAndResCode() |
| | | { |
| | | setResultCode(ResultCode.NO_SUCH_OBJECT); |
| | | appendErrorMessage(getMessage(MSGID_MODIFY_NO_SUCH_ENTRY, |
| | | String.valueOf(getEntryDN()))); |
| | | } |
| | | |
| | | /** |
| | | * Execute the postResponseModifyPlugins. |
| | | */ |
| | | private void invokePostResponsePlugins() |
| | | { |
| | | // Get the plugin config manager that will be used for invoking plugins. |
| | | PluginConfigManager pluginConfigManager = |
| | | DirectoryServer.getPluginConfigManager(); |
| | | |
| | | // Check wether there are local operations in attachments |
| | | List localOperations = |
| | | (List)getAttachment(Operation.LOCALBACKENDOPERATIONS); |
| | | |
| | | if (localOperations != null && (! localOperations.isEmpty())){ |
| | | for (Object localOp : localOperations) |
| | | { |
| | | LocalBackendModifyOperation localOperation = |
| | | (LocalBackendModifyOperation)localOp; |
| | | // Invoke the post-response add plugins. |
| | | pluginConfigManager.invokePostResponseModifyPlugins(localOperation); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | * |
| | | * This method always returns null. |
| | | */ |
| | | public Entry getCurrentEntry() { |
| | | // TODO Auto-generated method stub |
| | | return null; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | * |
| | | * This method always returns null. |
| | | */ |
| | | public List<AttributeValue> getCurrentPasswords() |
| | | { |
| | | return null; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | * |
| | | * This method always returns null. |
| | | */ |
| | | public Entry getModifiedEntry() |
| | | { |
| | | return null; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | * |
| | | * This method always returns null. |
| | | */ |
| | | public List<AttributeValue> getNewPasswords() |
| | | { |
| | | return null; |
| | | } |
| | | |
| | | } |
| | | |
| New file |
| | |
| | | /* |
| | | * 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 |
| | | * trunk/opends/resource/legal-notices/OpenDS.LICENSE |
| | | * or https://OpenDS.dev.java.net/OpenDS.LICENSE. |
| | | * 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 |
| | | * trunk/opends/resource/legal-notices/OpenDS.LICENSE. 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 |
| | | * |
| | | * |
| | | * Portions Copyright 2007 Sun Microsystems, Inc. |
| | | */ |
| | | package org.opends.server.core; |
| | | |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | | import org.opends.server.api.ClientConnection; |
| | | import org.opends.server.types.ByteString; |
| | | import org.opends.server.types.CancelRequest; |
| | | import org.opends.server.types.CancelResult; |
| | | import org.opends.server.types.Control; |
| | | import org.opends.server.types.DN; |
| | | import org.opends.server.types.DirectoryException; |
| | | import org.opends.server.types.DisconnectReason; |
| | | import org.opends.server.types.Entry; |
| | | import org.opends.server.types.Modification; |
| | | import org.opends.server.types.OperationType; |
| | | import org.opends.server.types.RawModification; |
| | | import org.opends.server.types.ResultCode; |
| | | |
| | | /** |
| | | * This abstract class wraps/decorates a given modify operation. |
| | | * This class will be extended by sub-classes to enhance the |
| | | * functionnality of the ModifyOperationBasis. |
| | | */ |
| | | public abstract class ModifyOperationWrapper implements ModifyOperation |
| | | { |
| | | private ModifyOperation modify; |
| | | |
| | | /** |
| | | * Creates a new modify operation based on the provided modify operation. |
| | | * |
| | | * @param modify The modify operation to wrap |
| | | */ |
| | | protected ModifyOperationWrapper(ModifyOperation modify){ |
| | | this.modify = modify; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void addModification(Modification modification) |
| | | throws DirectoryException |
| | | { |
| | | modify.addModification(modification); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void addRawModification(RawModification rawModification) |
| | | { |
| | | modify.addRawModification(rawModification); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void addResponseControl(Control control) |
| | | { |
| | | modify.addResponseControl(control); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public CancelResult cancel(CancelRequest cancelRequest) |
| | | { |
| | | return modify.cancel(cancelRequest); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void disconnectClient(DisconnectReason disconnectReason, |
| | | boolean sendNotification, String message, int messageID) |
| | | { |
| | | modify.disconnectClient(disconnectReason, sendNotification, |
| | | message, messageID); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public boolean dontSynchronize() |
| | | { |
| | | return modify.dontSynchronize(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public boolean equals(Object obj) |
| | | { |
| | | return modify.equals(obj); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public CancelRequest getCancelRequest() |
| | | { |
| | | return modify.getCancelRequest(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public DN getEntryDN() |
| | | { |
| | | return modify.getEntryDN(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public List<Modification> getModifications() |
| | | { |
| | | return modify.getModifications(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public OperationType getOperationType() |
| | | { |
| | | return modify.getOperationType(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public long getProcessingStartTime() |
| | | { |
| | | return modify.getProcessingStartTime(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public long getProcessingStopTime() |
| | | { |
| | | return modify.getProcessingStopTime(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public long getProcessingTime() |
| | | { |
| | | return modify.getProcessingTime(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public ByteString getRawEntryDN() |
| | | { |
| | | return modify.getRawEntryDN(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public List<RawModification> getRawModifications() |
| | | { |
| | | return modify.getRawModifications(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public String[][] getRequestLogElements() |
| | | { |
| | | return modify.getRequestLogElements(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public List<Control> getResponseControls() |
| | | { |
| | | return modify.getResponseControls(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public String[][] getResponseLogElements() |
| | | { |
| | | return modify.getResponseLogElements(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public int hashCode() |
| | | { |
| | | return modify.hashCode(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void removeResponseControl(Control control) |
| | | { |
| | | modify.removeResponseControl(control); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public boolean setCancelRequest(CancelRequest cancelRequest){ |
| | | return modify.setCancelRequest(cancelRequest); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setRawEntryDN(ByteString rawEntryDN) |
| | | { |
| | | modify.setRawEntryDN(rawEntryDN); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setRawModifications(List<RawModification> rawModifications) |
| | | { |
| | | modify.setRawModifications(rawModifications); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void toString(StringBuilder buffer) |
| | | { |
| | | modify.toString(buffer); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setProcessingStopTime(){ |
| | | modify.setProcessingStopTime(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setProcessingStartTime(){ |
| | | modify.setProcessingStopTime(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void addRequestControl(Control control) |
| | | { |
| | | modify.addRequestControl(control); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void appendAdditionalLogMessage(String message) |
| | | { |
| | | modify.appendAdditionalLogMessage(message); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void appendErrorMessage(String message) |
| | | { |
| | | modify.appendErrorMessage(message); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public StringBuilder getAdditionalLogMessage() |
| | | { |
| | | return modify.getAdditionalLogMessage(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public Object getAttachment(String name) |
| | | { |
| | | return modify.getAttachment(name); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public Map<String, Object> getAttachments() |
| | | { |
| | | return modify.getAttachments(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public DN getAuthorizationDN() |
| | | { |
| | | return modify.getAuthorizationDN(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public Entry getAuthorizationEntry() |
| | | { |
| | | return modify.getAuthorizationEntry(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public CancelResult getCancelResult() |
| | | { |
| | | return modify.getCancelResult(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public ClientConnection getClientConnection() |
| | | { |
| | | return modify.getClientConnection(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public String[][] getCommonLogElements() |
| | | { |
| | | return modify.getCommonLogElements(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public long getConnectionID() |
| | | { |
| | | return modify.getConnectionID(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public StringBuilder getErrorMessage() |
| | | { |
| | | return modify.getErrorMessage(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public DN getMatchedDN() |
| | | { |
| | | return modify.getMatchedDN(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public int getMessageID() |
| | | { |
| | | return modify.getMessageID(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public long getOperationID() |
| | | { |
| | | return modify.getOperationID(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public List<String> getReferralURLs() |
| | | { |
| | | return modify.getReferralURLs(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public List<Control> getRequestControls() |
| | | { |
| | | return modify.getRequestControls(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public ResultCode getResultCode() |
| | | { |
| | | return modify.getResultCode(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void indicateCancelled(CancelRequest cancelRequest) |
| | | { |
| | | modify.indicateCancelled(cancelRequest); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public boolean isInternalOperation() |
| | | { |
| | | return modify.isInternalOperation(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public boolean isSynchronizationOperation() |
| | | { |
| | | return modify.isSynchronizationOperation(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void operationCompleted() |
| | | { |
| | | modify.operationCompleted(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public Object removeAttachment(String name) |
| | | { |
| | | return modify.removeAttachment(name); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void removeRequestControl(Control control) |
| | | { |
| | | modify.removeRequestControl(control); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setAdditionalLogMessage(StringBuilder additionalLogMessage) |
| | | { |
| | | modify.setAdditionalLogMessage(additionalLogMessage); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public Object setAttachment(String name, Object value) |
| | | { |
| | | return modify.setAttachment(name, value); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setAttachments(Map<String, Object> attachments) |
| | | { |
| | | modify.setAttachments(attachments); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setAuthorizationEntry(Entry authorizationEntry) |
| | | { |
| | | modify.setAuthorizationEntry(authorizationEntry); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setCancelResult(CancelResult cancelResult) |
| | | { |
| | | modify.setCancelResult(cancelResult); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setDontSynchronize(boolean dontSynchronize) |
| | | { |
| | | modify.setDontSynchronize(dontSynchronize); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setErrorMessage(StringBuilder errorMessage) |
| | | { |
| | | modify.setErrorMessage(errorMessage); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setInternalOperation(boolean isInternalOperation) |
| | | { |
| | | modify.setInternalOperation(isInternalOperation); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setMatchedDN(DN matchedDN) |
| | | { |
| | | modify.setMatchedDN(matchedDN); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setReferralURLs(List<String> referralURLs) |
| | | { |
| | | modify.setReferralURLs(referralURLs); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setResponseData(DirectoryException directoryException) |
| | | { |
| | | modify.setResponseData(directoryException); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setResultCode(ResultCode resultCode) |
| | | { |
| | | modify.setResultCode(resultCode); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setSynchronizationOperation(boolean isSynchronizationOperation) |
| | | { |
| | | modify.setSynchronizationOperation(isSynchronizationOperation); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public String toString() |
| | | { |
| | | return modify.toString(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public final long getChangeNumber(){ |
| | | return modify.getChangeNumber(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setChangeNumber(long changeNumber) |
| | | { |
| | | modify.setChangeNumber(changeNumber); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public DN getProxiedAuthorizationDN() |
| | | { |
| | | return modify.getProxiedAuthorizationDN(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public void setProxiedAuthorizationDN(DN proxiedAuthorizationDN){ |
| | | modify.setProxiedAuthorizationDN(proxiedAuthorizationDN); |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | /* |
| | | * 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 |
| | | * trunk/opends/resource/legal-notices/OpenDS.LICENSE |
| | | * or https://OpenDS.dev.java.net/OpenDS.LICENSE. |
| | | * 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 |
| | | * trunk/opends/resource/legal-notices/OpenDS.LICENSE. 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 |
| | | * |
| | | * |
| | | * Portions Copyright 2007 Sun Microsystems, Inc. |
| | | */ |
| | | package org.opends.server.core; |
| | | |
| | | |
| | | import java.util.ArrayList; |
| | | |
| | | import org.opends.server.types.DN; |
| | | import org.opends.server.workflowelement.WorkflowElement; |
| | | |
| | | |
| | | /** |
| | | * This class defines the network group. A network group is used to categorize |
| | | * client connections. A network group is defined by a set of criteria, a |
| | | * set of policies and a set of workflows. A client connection belongs to a |
| | | * network group whenever it satisfies all the network group criteria. As soon |
| | | * as a client connection belongs to a network group, it has to comply with |
| | | * all the network group policies and all the client operation can be routed |
| | | * to one the network group workflows. |
| | | */ |
| | | public class NetworkGroup |
| | | { |
| | | // Workflows registered with the current network group. |
| | | private ArrayList<WorkflowTopologyNode> registeredWorkflows = |
| | | new ArrayList<WorkflowTopologyNode>(); |
| | | |
| | | |
| | | // The workflow for the rootDSE entry. The RootDSE workflow |
| | | // is not stored in the list of registered workflows. |
| | | private RootDseWorkflowTopology rootDSEWorkflow = null; |
| | | |
| | | |
| | | // List of naming contexts handled by the network group. |
| | | private NetworkGroupNamingContexts namingContexts = |
| | | new NetworkGroupNamingContexts(); |
| | | |
| | | |
| | | // The default network group (singleton). |
| | | // The default network group has no criterion, no policy, and gives |
| | | // access to all the workflows. The purpose of the default network |
| | | // group is to allow new clients to perform a first operation before |
| | | // they can be attached to a specific network group. |
| | | private static NetworkGroup defaultNetworkGroup = |
| | | new NetworkGroup ("default"); |
| | | |
| | | |
| | | // The list of all network groups that have been created. |
| | | // The default network group is not stored in that pool. |
| | | private static ArrayList<NetworkGroup> networkGroupPool = |
| | | new ArrayList<NetworkGroup>(); |
| | | |
| | | |
| | | // Human readable network group name. |
| | | private String networkGroupName = null; |
| | | |
| | | |
| | | /** |
| | | * Creates a new instance of the network group. |
| | | * |
| | | * @param networkGroupName the name of the network group for debug purpose |
| | | */ |
| | | public NetworkGroup ( |
| | | String networkGroupName |
| | | ) |
| | | { |
| | | this.networkGroupName = networkGroupName; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Registers a network group with the pool of network groups. |
| | | * |
| | | * @param networkGroup the network group to register |
| | | */ |
| | | public static void registerNetworkGroup( |
| | | NetworkGroup networkGroup |
| | | ) |
| | | { |
| | | networkGroupPool.add(networkGroup); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Registers a workflow with the network group. |
| | | * |
| | | * @param workflow the workflow to register |
| | | */ |
| | | public void registerWorkflow( |
| | | WorkflowImpl workflow |
| | | ) |
| | | { |
| | | // The workflow is rgistered with no pre/post workflow element. |
| | | registerWorkflow(workflow, null, null); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Registers a workflow with the network group and the workflow may have |
| | | * pre and post workflow element. |
| | | * |
| | | * @param workflow the workflow to register |
| | | * @param preWorkflowElements the tasks to execute before the workflow |
| | | * @param postWorkflowElements the tasks to execute after the workflow |
| | | */ |
| | | private void registerWorkflow( |
| | | WorkflowImpl workflow, |
| | | WorkflowElement[] preWorkflowElements, |
| | | WorkflowElement[] postWorkflowElements |
| | | ) |
| | | { |
| | | // true as soon as the workflow has been registered |
| | | boolean registered = false; |
| | | |
| | | // Is it the rootDSE workflow? |
| | | DN baseDN = workflow.getBaseDN(); |
| | | if (baseDN.isNullDN()) |
| | | { |
| | | // NOTE - The rootDSE workflow is not stored in the pool. |
| | | rootDSEWorkflow = new RootDseWorkflowTopology(workflow, namingContexts); |
| | | registered = true; |
| | | } |
| | | else |
| | | { |
| | | // This workflow is not the rootDSE workflow. Try to insert it in the |
| | | // workflow topology. |
| | | if (! baseDNAlreadyRegistered(baseDN)) |
| | | { |
| | | WorkflowTopologyNode workflowTopology = new WorkflowTopologyNode( |
| | | workflow, preWorkflowElements, postWorkflowElements); |
| | | |
| | | // Add the workflow in the workflow topology... |
| | | for (WorkflowTopologyNode curWorkflow: registeredWorkflows) |
| | | { |
| | | // Try to insert the new workflow under an existing workflow... |
| | | if (curWorkflow.insertSubordinate(workflowTopology)) |
| | | { |
| | | // new workflow has been inserted in the topology |
| | | break; |
| | | } |
| | | |
| | | // ... or try to insert the existing workflow below the new |
| | | // workflow |
| | | if (workflowTopology.insertSubordinate(curWorkflow)) |
| | | { |
| | | // new workflow has been inserted in the topology |
| | | break; |
| | | } |
| | | } |
| | | |
| | | // ... then register the workflow with the pool. |
| | | registeredWorkflows.add(workflowTopology); |
| | | registered = true; |
| | | |
| | | // Rebuild the list of naming context handled by the network group |
| | | rebuildNamingContextList(); |
| | | } |
| | | } |
| | | |
| | | // If the workflow has been registered successfully then register it |
| | | // with the default network group |
| | | if (registered) |
| | | { |
| | | if (this != defaultNetworkGroup) |
| | | { |
| | | defaultNetworkGroup.registerWorkflow( |
| | | workflow, preWorkflowElements, postWorkflowElements); |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Deregisters a workflow with the network group. |
| | | * |
| | | * @param baseDN the baseDN of the workflow to deregister |
| | | */ |
| | | public void deregisterWorkflow ( |
| | | DN baseDN |
| | | ) |
| | | { |
| | | Workflow workflow = getWorkflowCandidate(baseDN); |
| | | if (workflow != null) |
| | | { |
| | | deregisterWorkflow(workflow); |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Deregisters a workflow with the network group. |
| | | * |
| | | * @param workflow the workflow to deregister |
| | | */ |
| | | private void deregisterWorkflow( |
| | | Workflow workflow |
| | | ) |
| | | { |
| | | // true as soon as the workflow has been deregistered |
| | | boolean deregistered = false; |
| | | |
| | | // Is it the rootDSE workflow? |
| | | if (workflow == rootDSEWorkflow) |
| | | { |
| | | rootDSEWorkflow = null; |
| | | deregistered = true; |
| | | } |
| | | else |
| | | { |
| | | // The workflow to deregister is not the root DSE workflow. |
| | | // Remove it from the workflow topology. |
| | | WorkflowTopologyNode workflowTopology = (WorkflowTopologyNode) workflow; |
| | | workflowTopology.remove(); |
| | | |
| | | // Then deregister the workflow with the network group. |
| | | registeredWorkflows.remove(workflow); |
| | | deregistered = true; |
| | | |
| | | // Rebuild the list of naming context handled by the network group |
| | | rebuildNamingContextList(); |
| | | } |
| | | |
| | | // If the workflow has been deregistered then deregister it with |
| | | // the default network group as well |
| | | if (deregistered) |
| | | { |
| | | if (this != defaultNetworkGroup) |
| | | { |
| | | defaultNetworkGroup.deregisterWorkflow(workflow); |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Gets the highest workflow in the topology that can handle the baseDN. |
| | | * |
| | | * @param baseDN the base DN of the request |
| | | * @return the highest workflow in the topology that can handle the base DN, |
| | | * <code>null</code> if none was found |
| | | */ |
| | | public Workflow getWorkflowCandidate ( |
| | | DN baseDN |
| | | ) |
| | | { |
| | | // the top workflow to return |
| | | Workflow workflowCandidate = null; |
| | | |
| | | // get the list of workflow candidates |
| | | if (baseDN.isNullDN()) |
| | | { |
| | | // The rootDSE workflow is the candidate. |
| | | workflowCandidate = rootDSEWorkflow; |
| | | } |
| | | else |
| | | { |
| | | // Search the highest workflow in the topology that can handle |
| | | // the baseDN. |
| | | for (WorkflowTopologyNode curWorkflow: namingContexts.getNamingContexts()) |
| | | { |
| | | workflowCandidate = curWorkflow.getWorkflowCandidate (baseDN); |
| | | if (workflowCandidate != null) |
| | | { |
| | | break; |
| | | } |
| | | } |
| | | } |
| | | |
| | | return workflowCandidate; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Returns the default network group. The default network group is always |
| | | * defined and has no criterion, no policy and provide full access to |
| | | * all the registered workflows. |
| | | * |
| | | * @return the default network group |
| | | */ |
| | | public static NetworkGroup getDefaultNetworkGroup() |
| | | { |
| | | return defaultNetworkGroup; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Rebuilds the list of naming contexts handled by the network group. |
| | | * This operation should be performed whenever a workflow topology |
| | | * has been updated (workflow registration or de-registration). |
| | | */ |
| | | private void rebuildNamingContextList() |
| | | { |
| | | // reset lists of naming contexts |
| | | namingContexts.resetLists(); |
| | | |
| | | // a registered workflow with no parent is a naming context |
| | | for (WorkflowTopologyNode curWorkflow: registeredWorkflows) |
| | | { |
| | | WorkflowTopologyNode parent = curWorkflow.getParent(); |
| | | if (parent == null) |
| | | { |
| | | namingContexts.addNamingContext (curWorkflow); |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Checks whether a base DN has been already registered with |
| | | * the network group. |
| | | * |
| | | * @param baseDN the base DN to check |
| | | * @return <code>false</code> if the base DN is registered with the |
| | | * network group, <code>false</code> otherwise |
| | | */ |
| | | private boolean baseDNAlreadyRegistered ( |
| | | DN baseDN |
| | | ) |
| | | { |
| | | // returned result |
| | | boolean alreadyRegistered = false; |
| | | |
| | | // go through the list of registered workflow and check whether a base DN |
| | | // has already been used in a registered workflow |
| | | for (WorkflowTopologyNode curWorkflow: registeredWorkflows) |
| | | { |
| | | DN curDN = curWorkflow.getBaseDN(); |
| | | if (baseDN.equals (curDN)) |
| | | { |
| | | alreadyRegistered = true; |
| | | break; |
| | | } |
| | | } |
| | | |
| | | // check done |
| | | return alreadyRegistered; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Returns the list of naming contexts handled by the network group. |
| | | * |
| | | * @return the list of naming contexts |
| | | */ |
| | | public NetworkGroupNamingContexts getNamingContexts() |
| | | { |
| | | return namingContexts; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Dumps info from the current network group for debug purpose. |
| | | * |
| | | * @param leftMargin white spaces used to indent traces |
| | | * @return a string buffer that contains trace information |
| | | */ |
| | | public StringBuffer toString (String leftMargin) |
| | | { |
| | | StringBuffer sb = new StringBuffer(); |
| | | String newMargin = leftMargin + " "; |
| | | |
| | | sb.append (leftMargin + "Networkgroup (" + networkGroupName+ "\n"); |
| | | sb.append (leftMargin + "List of registered workflows:\n"); |
| | | for (WorkflowTopologyNode w: registeredWorkflows) |
| | | { |
| | | sb.append (w.toString (newMargin)); |
| | | } |
| | | |
| | | namingContexts.toString (leftMargin); |
| | | |
| | | sb.append (leftMargin + "rootDSEWorkflow:\n"); |
| | | if (rootDSEWorkflow == null) |
| | | { |
| | | sb.append (newMargin + "null\n"); |
| | | } |
| | | else |
| | | { |
| | | sb.append (rootDSEWorkflow.toString (newMargin)); |
| | | } |
| | | |
| | | return sb; |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | /* |
| | | * 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 |
| | | * trunk/opends/resource/legal-notices/OpenDS.LICENSE |
| | | * or https://OpenDS.dev.java.net/OpenDS.LICENSE. |
| | | * 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 |
| | | * trunk/opends/resource/legal-notices/OpenDS.LICENSE. 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 |
| | | * |
| | | * |
| | | * Portions Copyright 2007 Sun Microsystems, Inc. |
| | | */ |
| | | package org.opends.server.core; |
| | | |
| | | |
| | | /** |
| | | * This class defines the network group criteria. A criterion is used |
| | | * by the network groups to determine whether a client connection belongs |
| | | * to the network group or not. |
| | | */ |
| | | public class NetworkGroupCriteria |
| | | { |
| | | |
| | | /** |
| | | * Creates a new instance of the network group criteria. |
| | | */ |
| | | public NetworkGroupCriteria() |
| | | { |
| | | // No implementation is required. |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | /* |
| | | * 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 |
| | | * trunk/opends/resource/legal-notices/OpenDS.LICENSE |
| | | * or https://OpenDS.dev.java.net/OpenDS.LICENSE. |
| | | * 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 |
| | | * trunk/opends/resource/legal-notices/OpenDS.LICENSE. 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 |
| | | * |
| | | * |
| | | * Portions Copyright 2007 Sun Microsystems, Inc. |
| | | */ |
| | | package org.opends.server.core; |
| | | |
| | | |
| | | import java.util.ArrayList; |
| | | |
| | | |
| | | /** |
| | | * This classes defines a list of naming contexts for a network group. |
| | | */ |
| | | public class NetworkGroupNamingContexts |
| | | { |
| | | // List of naming contexts. |
| | | private ArrayList<WorkflowTopologyNode> namingContexts = null; |
| | | |
| | | // List of public naming contexts. |
| | | private ArrayList<WorkflowTopologyNode> publicNamingContexts = null; |
| | | |
| | | // List of private naming contexts. |
| | | private ArrayList<WorkflowTopologyNode> privateNamingContexts = null; |
| | | |
| | | /** |
| | | * Create a list of naming contexts for a network group. |
| | | */ |
| | | public NetworkGroupNamingContexts() |
| | | { |
| | | // create the lists of naming contexts |
| | | resetLists(); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Reset the list of naming contexts. |
| | | */ |
| | | public void resetLists() |
| | | { |
| | | namingContexts = new ArrayList<WorkflowTopologyNode>(); |
| | | privateNamingContexts = new ArrayList<WorkflowTopologyNode>(); |
| | | publicNamingContexts = new ArrayList<WorkflowTopologyNode>(); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Add a workflow in the list of naming context. |
| | | * |
| | | * @param workflow the workflow to add in the list of naming contexts |
| | | */ |
| | | public void addNamingContext ( |
| | | WorkflowTopologyNode workflow |
| | | ) |
| | | { |
| | | // add the workflow to the list of naming context |
| | | namingContexts.add (workflow); |
| | | |
| | | // add the workflow to the private/public list of naming contexts |
| | | if (workflow.isPrivate()) |
| | | { |
| | | privateNamingContexts.add (workflow); |
| | | } |
| | | else |
| | | { |
| | | publicNamingContexts.add (workflow); |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Get the list of naming contexts. |
| | | * |
| | | * @return the list of all the naming contexts |
| | | */ |
| | | public ArrayList<WorkflowTopologyNode> getNamingContexts() |
| | | { |
| | | return namingContexts; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Get the list of private naming contexts. |
| | | * |
| | | * @return the list of private naming contexts |
| | | */ |
| | | public ArrayList<WorkflowTopologyNode> getPrivateNamingContexts() |
| | | { |
| | | return privateNamingContexts; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Get the list of public naming contexts. |
| | | * |
| | | * @return the list of public naming contexts |
| | | */ |
| | | public ArrayList<WorkflowTopologyNode> getPublicNamingContexts() |
| | | { |
| | | return publicNamingContexts; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Dumps info from the current networkk group for debug purpose. |
| | | * |
| | | * @param leftMargin white spaces used to indent traces |
| | | * @return a string buffer that contains trace information |
| | | */ |
| | | public StringBuffer toString (String leftMargin) |
| | | { |
| | | StringBuffer sb = new StringBuffer(); |
| | | String newMargin = leftMargin + " "; |
| | | |
| | | sb.append (leftMargin + "List of naming contexts:\n"); |
| | | for (WorkflowTopologyNode w: namingContexts) |
| | | { |
| | | sb.append (w.toString (newMargin)); |
| | | } |
| | | |
| | | sb.append (leftMargin + "List of PRIVATE naming contexts:\n"); |
| | | for (WorkflowTopologyNode w: privateNamingContexts) |
| | | { |
| | | sb.append (w.toString (newMargin)); |
| | | } |
| | | |
| | | sb.append (leftMargin + "List of PUBLIC naming contexts:\n"); |
| | | for (WorkflowTopologyNode w: publicNamingContexts) |
| | | { |
| | | sb.append (w.toString (newMargin)); |
| | | } |
| | | |
| | | return sb; |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | /* |
| | | * 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 |
| | | * trunk/opends/resource/legal-notices/OpenDS.LICENSE |
| | | * or https://OpenDS.dev.java.net/OpenDS.LICENSE. |
| | | * 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 |
| | | * trunk/opends/resource/legal-notices/OpenDS.LICENSE. 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 |
| | | * |
| | | * |
| | | * Portions Copyright 2007 Sun Microsystems, Inc. |
| | | */ |
| | | package org.opends.server.core; |
| | | |
| | | |
| | | /** |
| | | * This class defines the network group policy. A client connection |
| | | * that belongs to a network group has to comply with the policies |
| | | * attach to the network group. |
| | | */ |
| | | public class NetworkGroupPolicy |
| | | { |
| | | |
| | | /** |
| | | * Creates a new instance of the network group policy. |
| | | */ |
| | | public NetworkGroupPolicy() |
| | | { |
| | | // No implementation is required. |
| | | } |
| | | |
| | | } |
| | |
| | | package org.opends.server.core; |
| | | |
| | | |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.Set; |
| | | |
| | | import org.opends.server.controls.EntryChangeNotificationControl; |
| | | import org.opends.server.controls.PersistentSearchChangeType; |
| | | import org.opends.server.types.Control; |
| | | import org.opends.server.types.DN; |
| | | import org.opends.server.types.DebugLogLevel; |
| | | import org.opends.server.types.DirectoryException; |
| | | import org.opends.server.types.DN; |
| | | import org.opends.server.types.Entry; |
| | | import org.opends.server.types.SearchFilter; |
| | | import org.opends.server.types.SearchScope; |
| | | import org.opends.server.workflowelement.localbackend.*; |
| | | |
| | | import static org.opends.server.loggers.debug.DebugLogger.*; |
| | | import org.opends.server.loggers.debug.DebugTracer; |
| | |
| | | * @param addOperation The add operation that has been processed. |
| | | * @param entry The entry that was added. |
| | | */ |
| | | public void processAdd(AddOperation addOperation, Entry entry) |
| | | public void processAdd(LocalBackendAddOperation addOperation, Entry entry) |
| | | { |
| | | // See if we care about add operations. |
| | | if (! changeTypes.contains(PersistentSearchChangeType.ADD)) |
| | |
| | | * @param deleteOperation The delete operation that has been processed. |
| | | * @param entry The entry that was removed. |
| | | */ |
| | | public void processDelete(DeleteOperation deleteOperation, Entry entry) |
| | | public void processDelete(LocalBackendDeleteOperation deleteOperation, |
| | | Entry entry) |
| | | { |
| | | // See if we care about delete operations. |
| | | if (! changeTypes.contains(PersistentSearchChangeType.DELETE)) |
| | |
| | | * @param oldEntry The entry before the modification was applied. |
| | | * @param newEntry The entry after the modification was applied. |
| | | */ |
| | | public void processModify(ModifyOperation modifyOperation, Entry oldEntry, |
| | | public void processModify(LocalBackendModifyOperation modifyOperation, |
| | | Entry oldEntry, |
| | | Entry newEntry) |
| | | { |
| | | // See if we care about modify operations. |
| | |
| | | import org.opends.server.types.SearchResultReference; |
| | | |
| | | import org.opends.server.types.DebugLogLevel; |
| | | import org.opends.server.types.operation.PostOperationAddOperation; |
| | | import org.opends.server.types.operation.PostOperationBindOperation; |
| | | import org.opends.server.types.operation.PostOperationDeleteOperation; |
| | | import org.opends.server.types.operation.PostOperationModifyOperation; |
| | | import org.opends.server.types.operation.PostOperationSearchOperation; |
| | | import org.opends.server.types.operation.PostResponseAddOperation; |
| | | import org.opends.server.types.operation.PostResponseBindOperation; |
| | | import org.opends.server.types.operation.PostResponseDeleteOperation; |
| | | import org.opends.server.types.operation.PostResponseModifyOperation; |
| | | import org.opends.server.types.operation.PostResponseSearchOperation; |
| | | import org.opends.server.types.operation.PreOperationAddOperation; |
| | | import org.opends.server.types.operation.PreOperationBindOperation; |
| | | import org.opends.server.types.operation.PreOperationDeleteOperation; |
| | | import org.opends.server.types.operation.PreOperationModifyOperation; |
| | | import org.opends.server.types.operation.PreOperationSearchOperation; |
| | | import org.opends.server.types.operation.PreParseAddOperation; |
| | | import org.opends.server.types.operation.PreParseBindOperation; |
| | | import org.opends.server.types.operation.PreParseDeleteOperation; |
| | | import org.opends.server.types.operation.PreParseModifyOperation; |
| | | import org.opends.server.types.operation.PreParseSearchOperation; |
| | | import org.opends.server.workflowelement.localbackend.*; |
| | | |
| | | import static org.opends.server.loggers.ErrorLogger.*; |
| | | import static org.opends.server.loggers.debug.DebugLogger.*; |
| | | import org.opends.server.loggers.debug.DebugTracer; |
| | |
| | | * |
| | | * @return The result of processing the pre-parse add plugins. |
| | | */ |
| | | public PreParsePluginResult invokePreParseAddPlugins(AddOperation |
| | | addOperation) |
| | | public PreParsePluginResult invokePreParseAddPlugins( |
| | | PreParseAddOperation addOperation) |
| | | { |
| | | PreParsePluginResult result = null; |
| | | |
| | |
| | | * @return The result of processing the pre-parse bind plugins. |
| | | */ |
| | | public PreParsePluginResult invokePreParseBindPlugins( |
| | | BindOperation bindOperation) |
| | | PreParseBindOperation bindOperation) |
| | | { |
| | | PreParsePluginResult result = null; |
| | | |
| | |
| | | * @return The result of processing the pre-parse delete plugins. |
| | | */ |
| | | public PreParsePluginResult invokePreParseDeletePlugins( |
| | | DeleteOperation deleteOperation) |
| | | PreParseDeleteOperation deleteOperation) |
| | | { |
| | | PreParsePluginResult result = null; |
| | | |
| | |
| | | * Invokes the set of pre-parse modify plugins that have been configured in |
| | | * the Directory Server. |
| | | * |
| | | * @param modifyOperation The modify operation for which to invoke the |
| | | * @param operation The modify operation for which to invoke the |
| | | * pre-parse plugins. |
| | | * |
| | | * @return The result of processing the pre-parse modify plugins. |
| | | */ |
| | | public PreParsePluginResult invokePreParseModifyPlugins( |
| | | ModifyOperation modifyOperation) |
| | | PreParseModifyOperation operation) |
| | | { |
| | | PreParsePluginResult result = null; |
| | | |
| | |
| | | { |
| | | try |
| | | { |
| | | result = p.doPreParse(modifyOperation); |
| | | result = p.doPreParse(operation); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | |
| | | int msgID = MSGID_PLUGIN_PRE_PARSE_PLUGIN_EXCEPTION; |
| | | String message = |
| | | getMessage(msgID, |
| | | modifyOperation.getOperationType().getOperationName(), |
| | | operation.getOperationType().getOperationName(), |
| | | String.valueOf(p.getPluginEntryDN()), |
| | | modifyOperation.getConnectionID(), |
| | | modifyOperation.getOperationID(), |
| | | operation.getConnectionID(), |
| | | operation.getOperationID(), |
| | | stackTraceToSingleLineString(e)); |
| | | logError(ErrorLogCategory.PLUGIN, ErrorLogSeverity.SEVERE_ERROR, |
| | | message, msgID); |
| | | |
| | | modifyOperation.setResultCode( |
| | | operation.setResultCode( |
| | | DirectoryServer.getServerErrorResultCode()); |
| | | modifyOperation.appendErrorMessage(message); |
| | | operation.appendErrorMessage(message); |
| | | |
| | | return new PreParsePluginResult(false, false, true); |
| | | } |
| | |
| | | int msgID = MSGID_PLUGIN_PRE_PARSE_PLUGIN_RETURNED_NULL; |
| | | String message = |
| | | getMessage(msgID, |
| | | modifyOperation.getOperationType().getOperationName(), |
| | | operation.getOperationType().getOperationName(), |
| | | String.valueOf(p.getPluginEntryDN()), |
| | | modifyOperation.getConnectionID(), |
| | | modifyOperation.getOperationID()); |
| | | operation.getConnectionID(), |
| | | operation.getOperationID()); |
| | | logError(ErrorLogCategory.PLUGIN, ErrorLogSeverity.SEVERE_ERROR, |
| | | message, msgID); |
| | | |
| | | modifyOperation.setResultCode( |
| | | operation.setResultCode( |
| | | DirectoryServer.getServerErrorResultCode()); |
| | | modifyOperation.appendErrorMessage(message); |
| | | operation.appendErrorMessage(message); |
| | | |
| | | return new PreParsePluginResult(false, false, true); |
| | | } |
| | |
| | | * @return The result of processing the pre-parse search plugins. |
| | | */ |
| | | public PreParsePluginResult invokePreParseSearchPlugins( |
| | | SearchOperation searchOperation) |
| | | PreParseSearchOperation searchOperation) |
| | | { |
| | | PreParsePluginResult result = null; |
| | | |
| | |
| | | * @return The result of processing the pre-operation add plugins. |
| | | */ |
| | | public PreOperationPluginResult invokePreOperationAddPlugins( |
| | | AddOperation addOperation) |
| | | PreOperationAddOperation addOperation) |
| | | { |
| | | PreOperationPluginResult result = null; |
| | | |
| | |
| | | * @return The result of processing the pre-operation bind plugins. |
| | | */ |
| | | public PreOperationPluginResult invokePreOperationBindPlugins( |
| | | BindOperation bindOperation) |
| | | PreOperationBindOperation bindOperation) |
| | | { |
| | | PreOperationPluginResult result = null; |
| | | |
| | |
| | | * @return The result of processing the pre-operation delete plugins. |
| | | */ |
| | | public PreOperationPluginResult invokePreOperationDeletePlugins( |
| | | DeleteOperation deleteOperation) |
| | | PreOperationDeleteOperation deleteOperation) |
| | | { |
| | | PreOperationPluginResult result = null; |
| | | |
| | |
| | | * @return The result of processing the pre-operation modify plugins. |
| | | */ |
| | | public PreOperationPluginResult invokePreOperationModifyPlugins( |
| | | ModifyOperation modifyOperation) |
| | | PreOperationModifyOperation modifyOperation) |
| | | { |
| | | PreOperationPluginResult result = null; |
| | | |
| | |
| | | * @return The result of processing the pre-operation search plugins. |
| | | */ |
| | | public PreOperationPluginResult invokePreOperationSearchPlugins( |
| | | SearchOperation searchOperation) |
| | | PreOperationSearchOperation searchOperation) |
| | | { |
| | | PreOperationPluginResult result = null; |
| | | |
| | |
| | | * @return The result of processing the post-operation add plugins. |
| | | */ |
| | | public PostOperationPluginResult invokePostOperationAddPlugins( |
| | | AddOperation addOperation) |
| | | PostOperationAddOperation addOperation) |
| | | { |
| | | PostOperationPluginResult result = null; |
| | | |
| | |
| | | * @return The result of processing the post-operation bind plugins. |
| | | */ |
| | | public PostOperationPluginResult invokePostOperationBindPlugins( |
| | | BindOperation bindOperation) |
| | | PostOperationBindOperation bindOperation) |
| | | { |
| | | PostOperationPluginResult result = null; |
| | | |
| | |
| | | * @return The result of processing the post-operation delete plugins. |
| | | */ |
| | | public PostOperationPluginResult invokePostOperationDeletePlugins( |
| | | DeleteOperation deleteOperation) |
| | | PostOperationDeleteOperation deleteOperation) |
| | | { |
| | | PostOperationPluginResult result = null; |
| | | |
| | |
| | | * @return The result of processing the post-operation modify plugins. |
| | | */ |
| | | public PostOperationPluginResult invokePostOperationModifyPlugins( |
| | | ModifyOperation modifyOperation) |
| | | PostOperationModifyOperation modifyOperation) |
| | | { |
| | | PostOperationPluginResult result = null; |
| | | |
| | |
| | | * @return The result of processing the post-operation search plugins. |
| | | */ |
| | | public PostOperationPluginResult invokePostOperationSearchPlugins( |
| | | SearchOperation searchOperation) |
| | | PostOperationSearchOperation searchOperation) |
| | | { |
| | | PostOperationPluginResult result = null; |
| | | |
| | |
| | | * @return The result of processing the post-response add plugins. |
| | | */ |
| | | public PostResponsePluginResult invokePostResponseAddPlugins( |
| | | AddOperation addOperation) |
| | | PostResponseAddOperation addOperation) |
| | | { |
| | | PostResponsePluginResult result = null; |
| | | |
| | |
| | | * @return The result of processing the post-response bind plugins. |
| | | */ |
| | | public PostResponsePluginResult invokePostResponseBindPlugins( |
| | | BindOperation bindOperation) |
| | | PostResponseBindOperation bindOperation) |
| | | { |
| | | PostResponsePluginResult result = null; |
| | | |
| | |
| | | * @return The result of processing the post-response delete plugins. |
| | | */ |
| | | public PostResponsePluginResult invokePostResponseDeletePlugins( |
| | | DeleteOperation deleteOperation) |
| | | PostResponseDeleteOperation deleteOperation) |
| | | { |
| | | PostResponsePluginResult result = null; |
| | | |
| | |
| | | * @return The result of processing the post-response modify plugins. |
| | | */ |
| | | public PostResponsePluginResult invokePostResponseModifyPlugins( |
| | | ModifyOperation modifyOperation) |
| | | PostResponseModifyOperation modifyOperation) |
| | | { |
| | | PostResponsePluginResult result = null; |
| | | |
| | |
| | | * @return The result of processing the post-response search plugins. |
| | | */ |
| | | public PostResponsePluginResult invokePostResponseSearchPlugins( |
| | | SearchOperation searchOperation) |
| | | PostResponseSearchOperation searchOperation) |
| | | { |
| | | PostResponsePluginResult result = null; |
| | | |
| | |
| | | return result; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Invokes the set of search result entry plugins that have been configured |
| | | * in the Directory Server. |
| | |
| | | * @return The result of processing the search result entry plugins. |
| | | */ |
| | | public SearchEntryPluginResult invokeSearchResultEntryPlugins( |
| | | SearchOperation searchOperation, |
| | | LocalBackendSearchOperation searchOperation, |
| | | SearchResultEntry searchEntry) |
| | | { |
| | | SearchEntryPluginResult result = null; |
| | |
| | | return result; |
| | | } |
| | | |
| | | /** |
| | | * Invokes the set of search result entry plugins that have been configured |
| | | * in the Directory Server. |
| | | * |
| | | * @param searchOperation The search operation for which to invoke the |
| | | * search result entry plugins. |
| | | * @param searchEntry The search result entry to be processed. |
| | | * |
| | | * @return The result of processing the search result entry plugins. |
| | | */ |
| | | public SearchEntryPluginResult invokeSearchResultEntryPlugins( |
| | | SearchOperationBasis searchOperation, |
| | | SearchResultEntry searchEntry) |
| | | { |
| | | SearchEntryPluginResult result = null; |
| | | |
| | | for (DirectoryServerPlugin p : searchResultEntryPlugins) |
| | | { |
| | | try |
| | | { |
| | | result = p.processSearchEntry(searchOperation, searchEntry); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | |
| | | int msgID = MSGID_PLUGIN_SEARCH_ENTRY_PLUGIN_EXCEPTION; |
| | | String message = getMessage(msgID, String.valueOf(p.getPluginEntryDN()), |
| | | searchOperation.getConnectionID(), |
| | | searchOperation.getOperationID(), |
| | | String.valueOf(searchEntry.getDN()), |
| | | stackTraceToSingleLineString(e)); |
| | | logError(ErrorLogCategory.PLUGIN, ErrorLogSeverity.SEVERE_ERROR, |
| | | message, msgID); |
| | | |
| | | return new SearchEntryPluginResult(false, false, false, false); |
| | | } |
| | | |
| | | if (result == null) |
| | | { |
| | | int msgID = MSGID_PLUGIN_SEARCH_ENTRY_PLUGIN_RETURNED_NULL; |
| | | String message = getMessage(msgID, String.valueOf(p.getPluginEntryDN()), |
| | | searchOperation.getConnectionID(), |
| | | searchOperation.getOperationID(), |
| | | String.valueOf(searchEntry.getDN())); |
| | | logError(ErrorLogCategory.PLUGIN, ErrorLogSeverity.SEVERE_ERROR, |
| | | message, msgID); |
| | | |
| | | return new SearchEntryPluginResult(false, false, false, false); |
| | | } |
| | | else if (result.connectionTerminated() || |
| | | (! result.continuePluginProcessing())) |
| | | { |
| | | return result; |
| | | } |
| | | } |
| | | |
| | | if (result == null) |
| | | { |
| | | // This should only happen if there were no search result entry plugins |
| | | // registered, which is fine. |
| | | result = SearchEntryPluginResult.SUCCESS; |
| | | } |
| | | |
| | | return result; |
| | | } |
| | | |
| | | /** |
| | | * Invokes the set of search result reference plugins that have been |
| | |
| | | * @return The result of processing the search result reference plugins. |
| | | */ |
| | | public SearchReferencePluginResult invokeSearchResultReferencePlugins( |
| | | SearchOperation searchOperation, |
| | | LocalBackendSearchOperation searchOperation, |
| | | SearchResultReference searchReference) |
| | | { |
| | | SearchReferencePluginResult result = null; |
| | | |
| | | for (DirectoryServerPlugin p : searchResultReferencePlugins) |
| | | { |
| | | try |
| | | { |
| | | result = p.processSearchReference(searchOperation, searchReference); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | |
| | | int msgID = MSGID_PLUGIN_SEARCH_REFERENCE_PLUGIN_EXCEPTION; |
| | | String message = getMessage(msgID, String.valueOf(p.getPluginEntryDN()), |
| | | searchOperation.getConnectionID(), |
| | | searchOperation.getOperationID(), |
| | | searchReference.getReferralURLString(), |
| | | stackTraceToSingleLineString(e)); |
| | | logError(ErrorLogCategory.PLUGIN, ErrorLogSeverity.SEVERE_ERROR, |
| | | message, msgID); |
| | | |
| | | return new SearchReferencePluginResult(false, false, false, false); |
| | | } |
| | | |
| | | if (result == null) |
| | | { |
| | | int msgID = MSGID_PLUGIN_SEARCH_REFERENCE_PLUGIN_RETURNED_NULL; |
| | | String message = getMessage(msgID, String.valueOf(p.getPluginEntryDN()), |
| | | searchOperation.getConnectionID(), |
| | | searchOperation.getOperationID(), |
| | | searchReference.getReferralURLString()); |
| | | logError(ErrorLogCategory.PLUGIN, ErrorLogSeverity.SEVERE_ERROR, |
| | | message, msgID); |
| | | |
| | | return new SearchReferencePluginResult(false, false, false, false); |
| | | } |
| | | else if (result.connectionTerminated() || |
| | | (! result.continuePluginProcessing())) |
| | | { |
| | | return result; |
| | | } |
| | | } |
| | | |
| | | if (result == null) |
| | | { |
| | | // This should only happen if there were no search result reference |
| | | // plugins registered, which is fine. |
| | | result = SearchReferencePluginResult.SUCCESS; |
| | | } |
| | | |
| | | return result; |
| | | } |
| | | |
| | | /** |
| | | * Invokes the set of search result reference plugins that have been |
| | | * configured in the Directory Server. |
| | | * |
| | | * @param searchOperation The search operation for which to invoke the |
| | | * search result reference plugins. |
| | | * @param searchReference The search result reference to be processed. |
| | | * |
| | | * @return The result of processing the search result reference plugins. |
| | | */ |
| | | public SearchReferencePluginResult invokeSearchResultReferencePlugins( |
| | | SearchOperationBasis searchOperation, |
| | | SearchResultReference searchReference) |
| | | { |
| | | SearchReferencePluginResult result = null; |
| New file |
| | |
| | | /* |
| | | * 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 |
| | | * trunk/opends/resource/legal-notices/OpenDS.LICENSE |
| | | * or https://OpenDS.dev.java.net/OpenDS.LICENSE. |
| | | * 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 |
| | | * trunk/opends/resource/legal-notices/OpenDS.LICENSE. 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 |
| | | * |
| | | * |
| | | * Portions Copyright 2007 Sun Microsystems, Inc. |
| | | */ |
| | | package org.opends.server.core; |
| | | |
| | | |
| | | import org.opends.server.types.DN; |
| | | import org.opends.server.types.Operation; |
| | | import org.opends.server.types.OperationType; |
| | | import org.opends.server.types.SearchScope; |
| | | |
| | | |
| | | /** |
| | | * This class implements the workflow node that handles the root DSE entry. |
| | | * As opposed to the WorkflowTopologyNode class, the root DSE node has no |
| | | * parent node nor subordinate nodes. Instead, the root DSE node has a set |
| | | * of naming contexts, each of which is a WorkflowTopologyNode object with |
| | | * no parent. |
| | | */ |
| | | public class RootDseWorkflowTopology extends WorkflowTopology |
| | | { |
| | | |
| | | // The naming contexts known by the root DSE. These naming contexts |
| | | // are defined in the scope of a network group. |
| | | private NetworkGroupNamingContexts namingContexts = null; |
| | | |
| | | |
| | | /** |
| | | * Creates a workflow node to handle the root DSE entry. |
| | | * |
| | | * @param workflowImpl the workflow which contains the processing for |
| | | * the root DSE backend |
| | | * @param namingContexts the list of naming contexts being registered |
| | | * with the network group the root DSE belongs to |
| | | */ |
| | | public RootDseWorkflowTopology( |
| | | WorkflowImpl workflowImpl, |
| | | NetworkGroupNamingContexts namingContexts |
| | | ) |
| | | { |
| | | super(workflowImpl); |
| | | this.namingContexts = namingContexts; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Executes an operation on the root DSE entry. |
| | | * |
| | | * @param operation the operation to execute |
| | | */ |
| | | public void execute( |
| | | Operation operation |
| | | ) |
| | | { |
| | | // Execute the operation. |
| | | OperationType operationType = operation.getOperationType(); |
| | | if (operationType != OperationType.SEARCH) |
| | | { |
| | | // Execute the operation |
| | | getWorkflowImpl().execute(operation); |
| | | } |
| | | else |
| | | { |
| | | // Execute the SEARCH operation |
| | | executeSearch((SearchOperation) operation); |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Executes a search operation on the the root DSE entry. |
| | | * |
| | | * @param searchOp the operation to execute |
| | | */ |
| | | private void executeSearch( |
| | | SearchOperation searchOp |
| | | ) |
| | | { |
| | | // Keep a the original search scope because we will alter it in the |
| | | // operation. |
| | | SearchScope originalScope = searchOp.getScope(); |
| | | |
| | | // Search base? |
| | | // The root DSE entry itself is never returned unless the operation |
| | | // is a search base on the null suffix. |
| | | if (originalScope == SearchScope.BASE_OBJECT) |
| | | { |
| | | getWorkflowImpl().execute(searchOp); |
| | | return; |
| | | } |
| | | |
| | | // Create a workflow result code in case we need to perform search in |
| | | // subordinate workflows. |
| | | WorkflowResultCode workflowResultCode = new WorkflowResultCode( |
| | | searchOp.getResultCode(), searchOp.getErrorMessage()); |
| | | |
| | | // The search scope is not 'base', so let's do a search on all the public |
| | | // naming contexts with appropriate new search scope and new base DN. |
| | | SearchScope newScope = elaborateScopeForSearchInSubordinates(originalScope); |
| | | searchOp.setScope(newScope); |
| | | DN originalBaseDN = searchOp.getBaseDN(); |
| | | |
| | | for (WorkflowTopologyNode namingContext: |
| | | namingContexts.getPublicNamingContexts()) |
| | | { |
| | | // We have to change the operation request base DN to match the |
| | | // subordinate workflow base DN. Otherwise the workflow will |
| | | // return a no such entry result code as the operation request |
| | | // base DN is a superior of the workflow base DN! |
| | | DN ncDN = namingContext.getBaseDN(); |
| | | |
| | | // Set the new request base DN then do execute the operation |
| | | // in the naming context workflow. |
| | | searchOp.setBaseDN(ncDN); |
| | | namingContext.execute(searchOp); |
| | | boolean sendReferenceEntry = |
| | | workflowResultCode.elaborateGlobalResultCode( |
| | | searchOp.getResultCode(), searchOp.getErrorMessage()); |
| | | if (sendReferenceEntry) |
| | | { |
| | | // TODO jdemendi - turn a referral result code into a reference entry |
| | | // and send the reference entry to the client application |
| | | } |
| | | } |
| | | |
| | | // Now restore the original request base DN and original search scope |
| | | searchOp.setBaseDN(originalBaseDN); |
| | | searchOp.setScope(originalScope); |
| | | |
| | | // Set the operation result code and error message |
| | | searchOp.setResultCode(workflowResultCode.resultCode()); |
| | | searchOp.setErrorMessage(workflowResultCode.errorMessage()); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Dumps info from the current workflow for debug purpose. |
| | | * |
| | | * @param leftMargin white spaces used to indent the traces |
| | | * @return a string buffer that contains trace information |
| | | */ |
| | | public StringBuffer toString(String leftMargin) |
| | | { |
| | | StringBuffer sb = new StringBuffer(); |
| | | |
| | | // display the baseDN |
| | | sb.append(leftMargin + "Workflow baseDN:[ \"\" ]\n"); |
| | | |
| | | return sb; |
| | | } |
| | | |
| | | } |
| | |
| | | */ |
| | | package org.opends.server.core; |
| | | |
| | | |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.HashSet; |
| | | import java.util.Iterator; |
| | | import java.util.LinkedHashSet; |
| | | import java.util.List; |
| | | import java.util.concurrent.atomic.AtomicBoolean; |
| | | |
| | | import org.opends.server.api.Backend; |
| | | import org.opends.server.api.ClientConnection; |
| | | import org.opends.server.api.plugin.PostOperationPluginResult; |
| | | import org.opends.server.api.plugin.PreOperationPluginResult; |
| | | import org.opends.server.api.plugin.PreParsePluginResult; |
| | | import org.opends.server.api.plugin.SearchEntryPluginResult; |
| | | import org.opends.server.api.plugin.SearchReferencePluginResult; |
| | | import org.opends.server.controls.*; |
| | | import org.opends.server.protocols.asn1.ASN1OctetString; |
| | | import org.opends.server.protocols.ldap.LDAPFilter; |
| | | import org.opends.server.types.Attribute; |
| | | import org.opends.server.types.AttributeType; |
| | | import org.opends.server.types.AttributeValue; |
| | | import org.opends.server.controls.MatchedValuesControl; |
| | | import org.opends.server.types.ByteString; |
| | | import org.opends.server.types.CancelledOperationException; |
| | | import org.opends.server.types.CancelRequest; |
| | | import org.opends.server.types.CancelResult; |
| | | import org.opends.server.types.Control; |
| | | import org.opends.server.types.DebugLogLevel; |
| | | import org.opends.server.types.DN; |
| | | import org.opends.server.types.DereferencePolicy; |
| | | import org.opends.server.types.DirectoryException; |
| | | import org.opends.server.types.DisconnectReason; |
| | | import org.opends.server.types.DN; |
| | | import org.opends.server.types.Entry; |
| | | import org.opends.server.types.FilterType; |
| | | import org.opends.server.types.LDAPException; |
| | | import org.opends.server.types.Operation; |
| | | import org.opends.server.types.OperationType; |
| | | import org.opends.server.types.Privilege; |
| | | import org.opends.server.types.RawFilter; |
| | | import org.opends.server.types.ResultCode; |
| | | import org.opends.server.types.SearchFilter; |
| | | import org.opends.server.types.SearchResultEntry; |
| | | import org.opends.server.types.SearchResultReference; |
| | | import org.opends.server.types.SearchScope; |
| | | import org.opends.server.types.operation.PostOperationSearchOperation; |
| | | import org.opends.server.types.operation.PostResponseSearchOperation; |
| | | import org.opends.server.types.operation.PreOperationSearchOperation; |
| | | import org.opends.server.types.operation.PreParseSearchOperation; |
| | | import org.opends.server.types.operation.SearchEntrySearchOperation; |
| | | import org.opends.server.types.operation.SearchReferenceSearchOperation; |
| | | import org.opends.server.util.TimeThread; |
| | | |
| | | import static org.opends.server.core.CoreConstants.*; |
| | | import static org.opends.server.loggers.AccessLogger.*; |
| | | import static org.opends.server.loggers.debug.DebugLogger.*; |
| | | import org.opends.server.loggers.debug.DebugTracer; |
| | | import static org.opends.server.messages.CoreMessages.*; |
| | | import static org.opends.server.messages.MessageHandler.*; |
| | | import static org.opends.server.util.ServerConstants.*; |
| | | import static org.opends.server.util.StaticUtils.*; |
| | | |
| | | |
| | | |
| | | /** |
| | | * This class defines an operation that may be used to locate entries in the |
| | | * Directory Server based on a given set of criteria. |
| | | * This interface defines an operation used to search for entries |
| | | * in the Directory Server. |
| | | */ |
| | | public class SearchOperation |
| | | extends Operation |
| | | implements PreParseSearchOperation, PreOperationSearchOperation, |
| | | PostOperationSearchOperation, PostResponseSearchOperation, |
| | | SearchEntrySearchOperation, SearchReferenceSearchOperation |
| | | public interface SearchOperation extends Operation |
| | | { |
| | | /** |
| | | * The tracer object for the debug logger. |
| | | */ |
| | | private static final DebugTracer TRACER = getTracer(); |
| | | |
| | | // Indicates whether a search result done response has been sent to the |
| | | // client. |
| | | private AtomicBoolean responseSent; |
| | | |
| | | // Indicates whether the client is able to handle referrals. |
| | | private boolean clientAcceptsReferrals; |
| | | |
| | | // Indicates whether to include the account usable control with search result |
| | | // entries. |
| | | private boolean includeUsableControl; |
| | | |
| | | // Indicates whether to only real attributes should be returned. |
| | | private boolean realAttributesOnly; |
| | | |
| | | // Indicates whether LDAP subentries should be returned. |
| | | private boolean returnLDAPSubentries; |
| | | |
| | | // Indicates whether to include attribute types only or both types and values. |
| | | private boolean typesOnly; |
| | | |
| | | // Indicates whether to only virtual attributes should be returned. |
| | | private boolean virtualAttributesOnly; |
| | | |
| | | // The raw, unprocessed base DN as included in the request from the client. |
| | | private ByteString rawBaseDN; |
| | | |
| | | // The cancel request that has been issued for this search operation. |
| | | private CancelRequest cancelRequest; |
| | | |
| | | // The dereferencing policy for the search operation. |
| | | private DereferencePolicy derefPolicy; |
| | | |
| | | // The base DN for the search operation. |
| | | private DN baseDN; |
| | | |
| | | // The proxied authorization target DN for this operation. |
| | | private DN proxiedAuthorizationDN; |
| | | |
| | | // The number of entries that have been sent to the client. |
| | | private int entriesSent; |
| | | |
| | | // The number of search result references that have been sent to the client. |
| | | private int referencesSent; |
| | | |
| | | // The size limit for the search operation. |
| | | private int sizeLimit; |
| | | |
| | | // The time limit for the search operation. |
| | | private int timeLimit; |
| | | |
| | | // The set of attributes that should be returned in matching entries. |
| | | private LinkedHashSet<String> attributes; |
| | | |
| | | // The set of response controls for this search operation. |
| | | private List<Control> responseControls; |
| | | |
| | | // The time that processing started on this operation. |
| | | private long processingStartTime; |
| | | |
| | | // The time that processing ended on this operation. |
| | | private long processingStopTime; |
| | | |
| | | // The time that the search time limit has expired. |
| | | private long timeLimitExpiration; |
| | | |
| | | // The matched values control associated with this search operation. |
| | | private MatchedValuesControl matchedValuesControl; |
| | | |
| | | // The persistent search associated with this search operation. |
| | | private PersistentSearch persistentSearch; |
| | | |
| | | // The raw, unprocessed filter as included in the request from the client. |
| | | private RawFilter rawFilter; |
| | | |
| | | // The search filter for the search operation. |
| | | private SearchFilter filter; |
| | | |
| | | // The search scope for the search operation. |
| | | private SearchScope scope; |
| | | |
| | | |
| | | |
| | | /** |
| | | * Creates a new search operation with the provided information. |
| | | * |
| | | * @param clientConnection The client connection with which this operation |
| | | * is associated. |
| | | * @param operationID The operation ID for this operation. |
| | | * @param messageID The message ID of the request with which this |
| | | * operation is associated. |
| | | * @param requestControls The set of controls included in the request. |
| | | * @param rawBaseDN The raw, unprocessed base DN as included in the |
| | | * request from the client. |
| | | * @param scope The scope for this search operation. |
| | | * @param derefPolicy The alias dereferencing policy for this search |
| | | * operation. |
| | | * @param sizeLimit The size limit for this search operation. |
| | | * @param timeLimit The time limit for this search operation. |
| | | * @param typesOnly The typesOnly flag for this search operation. |
| | | * @param rawFilter the raw, unprocessed filter as included in the |
| | | * request from the client. |
| | | * @param attributes The requested attributes for this search |
| | | * operation. |
| | | */ |
| | | public SearchOperation(ClientConnection clientConnection, long operationID, |
| | | int messageID, List<Control> requestControls, |
| | | ByteString rawBaseDN, SearchScope scope, |
| | | DereferencePolicy derefPolicy, int sizeLimit, |
| | | int timeLimit, boolean typesOnly, RawFilter rawFilter, |
| | | LinkedHashSet<String> attributes) |
| | | { |
| | | super(clientConnection, operationID, messageID, requestControls); |
| | | |
| | | |
| | | this.rawBaseDN = rawBaseDN; |
| | | this.scope = scope; |
| | | this.derefPolicy = derefPolicy; |
| | | this.sizeLimit = sizeLimit; |
| | | this.timeLimit = timeLimit; |
| | | this.typesOnly = typesOnly; |
| | | this.rawFilter = rawFilter; |
| | | |
| | | if (attributes == null) |
| | | { |
| | | this.attributes = new LinkedHashSet<String>(0); |
| | | } |
| | | else |
| | | { |
| | | this.attributes = attributes; |
| | | } |
| | | |
| | | |
| | | if (clientConnection.getSizeLimit() <= 0) |
| | | { |
| | | this.sizeLimit = sizeLimit; |
| | | } |
| | | else |
| | | { |
| | | if (sizeLimit <= 0) |
| | | { |
| | | this.sizeLimit = clientConnection.getSizeLimit(); |
| | | } |
| | | else |
| | | { |
| | | this.sizeLimit = Math.min(sizeLimit, clientConnection.getSizeLimit()); |
| | | } |
| | | } |
| | | |
| | | |
| | | if (clientConnection.getTimeLimit() <= 0) |
| | | { |
| | | this.timeLimit = timeLimit; |
| | | } |
| | | else |
| | | { |
| | | if (timeLimit <= 0) |
| | | { |
| | | this.timeLimit = clientConnection.getTimeLimit(); |
| | | } |
| | | else |
| | | { |
| | | this.timeLimit = Math.min(timeLimit, clientConnection.getTimeLimit()); |
| | | } |
| | | } |
| | | |
| | | |
| | | baseDN = null; |
| | | filter = null; |
| | | entriesSent = 0; |
| | | referencesSent = 0; |
| | | responseControls = new ArrayList<Control>(); |
| | | cancelRequest = null; |
| | | clientAcceptsReferrals = true; |
| | | includeUsableControl = false; |
| | | responseSent = new AtomicBoolean(false); |
| | | persistentSearch = null; |
| | | returnLDAPSubentries = false; |
| | | matchedValuesControl = null; |
| | | realAttributesOnly = false; |
| | | virtualAttributesOnly = false; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Creates a new search operation with the provided information. |
| | | * |
| | | * @param clientConnection The client connection with which this operation |
| | | * is associated. |
| | | * @param operationID The operation ID for this operation. |
| | | * @param messageID The message ID of the request with which this |
| | | * operation is associated. |
| | | * @param requestControls The set of controls included in the request. |
| | | * @param baseDN The base DN for this search operation. |
| | | * @param scope The scope for this search operation. |
| | | * @param derefPolicy The alias dereferencing policy for this search |
| | | * operation. |
| | | * @param sizeLimit The size limit for this search operation. |
| | | * @param timeLimit The time limit for this search operation. |
| | | * @param typesOnly The typesOnly flag for this search operation. |
| | | * @param filter The filter for this search operation. |
| | | * @param attributes The attributes for this search operation. |
| | | */ |
| | | public SearchOperation(ClientConnection clientConnection, long operationID, |
| | | int messageID, List<Control> requestControls, |
| | | DN baseDN, SearchScope scope, |
| | | DereferencePolicy derefPolicy, int sizeLimit, |
| | | int timeLimit, boolean typesOnly, SearchFilter filter, |
| | | LinkedHashSet<String> attributes) |
| | | { |
| | | super(clientConnection, operationID, messageID, requestControls); |
| | | |
| | | |
| | | this.baseDN = baseDN; |
| | | this.scope = scope; |
| | | this.derefPolicy = derefPolicy; |
| | | this.sizeLimit = sizeLimit; |
| | | this.timeLimit = timeLimit; |
| | | this.typesOnly = typesOnly; |
| | | this.filter = filter; |
| | | |
| | | if (attributes == null) |
| | | { |
| | | this.attributes = new LinkedHashSet<String>(0); |
| | | } |
| | | else |
| | | { |
| | | this.attributes = attributes; |
| | | } |
| | | |
| | | rawBaseDN = new ASN1OctetString(baseDN.toString()); |
| | | rawFilter = new LDAPFilter(filter); |
| | | |
| | | |
| | | if (clientConnection.getSizeLimit() <= 0) |
| | | { |
| | | this.sizeLimit = sizeLimit; |
| | | } |
| | | else |
| | | { |
| | | if (sizeLimit <= 0) |
| | | { |
| | | this.sizeLimit = clientConnection.getSizeLimit(); |
| | | } |
| | | else |
| | | { |
| | | this.sizeLimit = Math.min(sizeLimit, clientConnection.getSizeLimit()); |
| | | } |
| | | } |
| | | |
| | | |
| | | if (clientConnection.getTimeLimit() <= 0) |
| | | { |
| | | this.timeLimit = timeLimit; |
| | | } |
| | | else |
| | | { |
| | | if (timeLimit <= 0) |
| | | { |
| | | this.timeLimit = clientConnection.getTimeLimit(); |
| | | } |
| | | else |
| | | { |
| | | this.timeLimit = Math.min(timeLimit, clientConnection.getTimeLimit()); |
| | | } |
| | | } |
| | | |
| | | |
| | | entriesSent = 0; |
| | | referencesSent = 0; |
| | | responseControls = new ArrayList<Control>(); |
| | | cancelRequest = null; |
| | | clientAcceptsReferrals = true; |
| | | includeUsableControl = false; |
| | | responseSent = new AtomicBoolean(false); |
| | | persistentSearch = null; |
| | | returnLDAPSubentries = false; |
| | | matchedValuesControl = null; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Retrieves the raw, unprocessed base DN as included in the request from the |
| | |
| | | * @return The raw, unprocessed base DN as included in the request from the |
| | | * client. |
| | | */ |
| | | public final ByteString getRawBaseDN() |
| | | { |
| | | return rawBaseDN; |
| | | } |
| | | |
| | | |
| | | public abstract ByteString getRawBaseDN(); |
| | | |
| | | /** |
| | | * Specifies the raw, unprocessed base DN as included in the request from the |
| | |
| | | * @param rawBaseDN The raw, unprocessed base DN as included in the request |
| | | * from the client. |
| | | */ |
| | | public final void setRawBaseDN(ByteString rawBaseDN) |
| | | { |
| | | this.rawBaseDN = rawBaseDN; |
| | | |
| | | baseDN = null; |
| | | } |
| | | |
| | | |
| | | public abstract void setRawBaseDN(ByteString rawBaseDN); |
| | | |
| | | /** |
| | | * Retrieves the base DN for this search operation. This should not be called |
| | |
| | | * @return The base DN for this search operation, or <CODE>null</CODE> if the |
| | | * raw base DN has not yet been processed. |
| | | */ |
| | | public final DN getBaseDN() |
| | | { |
| | | return baseDN; |
| | | } |
| | | |
| | | |
| | | public abstract DN getBaseDN(); |
| | | |
| | | /** |
| | | * Specifies the base DN for this search operation. This method is only |
| | |
| | | * |
| | | * @param baseDN The base DN for this search operation. |
| | | */ |
| | | public final void setBaseDN(DN baseDN) |
| | | { |
| | | this.baseDN = baseDN; |
| | | } |
| | | |
| | | |
| | | public abstract void setBaseDN(DN baseDN); |
| | | |
| | | /** |
| | | * Retrieves the scope for this search operation. |
| | | * |
| | | * @return The scope for this search operation. |
| | | */ |
| | | public final SearchScope getScope() |
| | | { |
| | | return scope; |
| | | } |
| | | |
| | | |
| | | public abstract SearchScope getScope(); |
| | | |
| | | /** |
| | | * Specifies the scope for this search operation. This should only be called |
| | |
| | | * |
| | | * @param scope The scope for this search operation. |
| | | */ |
| | | public final void setScope(SearchScope scope) |
| | | { |
| | | this.scope = scope; |
| | | } |
| | | |
| | | |
| | | public abstract void setScope(SearchScope scope); |
| | | |
| | | /** |
| | | * Retrieves the alias dereferencing policy for this search operation. |
| | | * |
| | | * @return The alias dereferencing policy for this search operation. |
| | | */ |
| | | public final DereferencePolicy getDerefPolicy() |
| | | { |
| | | return derefPolicy; |
| | | } |
| | | |
| | | |
| | | public abstract DereferencePolicy getDerefPolicy(); |
| | | |
| | | /** |
| | | * Specifies the alias dereferencing policy for this search operation. This |
| | |
| | | * @param derefPolicy The alias dereferencing policy for this search |
| | | * operation. |
| | | */ |
| | | public final void setDerefPolicy(DereferencePolicy derefPolicy) |
| | | { |
| | | this.derefPolicy = derefPolicy; |
| | | } |
| | | |
| | | |
| | | public abstract void setDerefPolicy(DereferencePolicy derefPolicy); |
| | | |
| | | /** |
| | | * Retrieves the size limit for this search operation. |
| | | * |
| | | * @return The size limit for this search operation. |
| | | */ |
| | | public final int getSizeLimit() |
| | | { |
| | | return sizeLimit; |
| | | } |
| | | |
| | | |
| | | public abstract int getSizeLimit(); |
| | | |
| | | /** |
| | | * Specifies the size limit for this search operation. This should only be |
| | |
| | | * |
| | | * @param sizeLimit The size limit for this search operation. |
| | | */ |
| | | public final void setSizeLimit(int sizeLimit) |
| | | { |
| | | this.sizeLimit = sizeLimit; |
| | | } |
| | | |
| | | |
| | | public abstract void setSizeLimit(int sizeLimit); |
| | | |
| | | /** |
| | | * Retrieves the time limit for this search operation. |
| | | * |
| | | * @return The time limit for this search operation. |
| | | */ |
| | | public final int getTimeLimit() |
| | | { |
| | | return timeLimit; |
| | | } |
| | | public abstract int getTimeLimit(); |
| | | |
| | | |
| | | /** |
| | | * Get the time after which the search time limit has expired. |
| | | * |
| | | * @return the timeLimitExpiration |
| | | */ |
| | | public abstract Long getTimeLimitExpiration(); |
| | | |
| | | /** |
| | | * Specifies the time limit for this search operation. This should only be |
| | |
| | | * |
| | | * @param timeLimit The time limit for this search operation. |
| | | */ |
| | | public final void setTimeLimit(int timeLimit) |
| | | { |
| | | this.timeLimit = timeLimit; |
| | | } |
| | | |
| | | |
| | | public abstract void setTimeLimit(int timeLimit); |
| | | |
| | | /** |
| | | * Retrieves the typesOnly flag for this search operation. |
| | | * |
| | | * @return The typesOnly flag for this search operation. |
| | | */ |
| | | public final boolean getTypesOnly() |
| | | { |
| | | return typesOnly; |
| | | } |
| | | |
| | | |
| | | public abstract boolean getTypesOnly(); |
| | | |
| | | /** |
| | | * Specifies the typesOnly flag for this search operation. This should only |
| | |
| | | * |
| | | * @param typesOnly The typesOnly flag for this search operation. |
| | | */ |
| | | public final void setTypesOnly(boolean typesOnly) |
| | | { |
| | | this.typesOnly = typesOnly; |
| | | } |
| | | |
| | | |
| | | public abstract void setTypesOnly(boolean typesOnly); |
| | | |
| | | /** |
| | | * Retrieves the raw, unprocessed search filter as included in the request |
| | |
| | | * @return The raw, unprocessed search filter as included in the request from |
| | | * the client. |
| | | */ |
| | | public final RawFilter getRawFilter() |
| | | { |
| | | return rawFilter; |
| | | } |
| | | |
| | | |
| | | public abstract RawFilter getRawFilter(); |
| | | |
| | | /** |
| | | * Specifies the raw, unprocessed search filter as included in the request |
| | |
| | | * @param rawFilter The raw, unprocessed search filter as included in the |
| | | * request from the client. |
| | | */ |
| | | public final void setRawFilter(RawFilter rawFilter) |
| | | { |
| | | this.rawFilter = rawFilter; |
| | | |
| | | filter = null; |
| | | } |
| | | |
| | | |
| | | public abstract void setRawFilter(RawFilter rawFilter); |
| | | |
| | | /** |
| | | * Retrieves the filter for this search operation. This should not be called |
| | |
| | | * @return The filter for this search operation, or <CODE>null</CODE> if the |
| | | * raw filter has not yet been processed. |
| | | */ |
| | | public final SearchFilter getFilter() |
| | | { |
| | | return filter; |
| | | } |
| | | |
| | | |
| | | public abstract SearchFilter getFilter(); |
| | | |
| | | /** |
| | | * Retrieves the set of requested attributes for this search operation. Its |
| | |
| | | * |
| | | * @return The set of requested attributes for this search operation. |
| | | */ |
| | | public final LinkedHashSet<String> getAttributes() |
| | | { |
| | | return attributes; |
| | | } |
| | | |
| | | |
| | | public abstract LinkedHashSet<String> getAttributes(); |
| | | |
| | | /** |
| | | * Specifies the set of requested attributes for this search operation. It |
| | |
| | | * @param attributes The set of requested attributes for this search |
| | | * operation. |
| | | */ |
| | | public final void setAttributes(LinkedHashSet<String> attributes) |
| | | { |
| | | if (attributes == null) |
| | | { |
| | | this.attributes.clear(); |
| | | } |
| | | else |
| | | { |
| | | this.attributes = attributes; |
| | | } |
| | | } |
| | | |
| | | |
| | | public abstract void setAttributes(LinkedHashSet<String> attributes); |
| | | |
| | | /** |
| | | * Retrieves the number of entries sent to the client for this search |
| | |
| | | * @return The number of entries sent to the client for this search |
| | | * operation. |
| | | */ |
| | | public final int getEntriesSent() |
| | | { |
| | | return entriesSent; |
| | | } |
| | | |
| | | |
| | | public abstract int getEntriesSent(); |
| | | |
| | | /** |
| | | * Retrieves the number of search references sent to the client for this |
| | |
| | | * @return The number of search references sent to the client for this search |
| | | * operation. |
| | | */ |
| | | public final int getReferencesSent() |
| | | { |
| | | return referencesSent; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final long getProcessingStartTime() |
| | | { |
| | | return processingStartTime; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final long getProcessingStopTime() |
| | | { |
| | | return processingStopTime; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final long getProcessingTime() |
| | | { |
| | | return (processingStopTime - processingStartTime); |
| | | } |
| | | |
| | | |
| | | public abstract int getReferencesSent(); |
| | | |
| | | /** |
| | | * Used as a callback for backends to indicate that the provided entry matches |
| | |
| | | * <CODE>false</CODE> if not for some reason (e.g., the size limit |
| | | * has been reached or the search has been abandoned). |
| | | */ |
| | | public final boolean returnEntry(Entry entry, List<Control> controls) |
| | | { |
| | | // See if the operation has been abandoned. If so, then don't send the |
| | | // entry and indicate that the search should end. |
| | | if (cancelRequest != null) |
| | | { |
| | | setResultCode(ResultCode.CANCELED); |
| | | return false; |
| | | } |
| | | |
| | | // See if the size limit has been exceeded. If so, then don't send the |
| | | // entry and indicate that the search should end. |
| | | if ((sizeLimit > 0) && (entriesSent >= sizeLimit)) |
| | | { |
| | | setResultCode(ResultCode.SIZE_LIMIT_EXCEEDED); |
| | | appendErrorMessage(getMessage(MSGID_SEARCH_SIZE_LIMIT_EXCEEDED, |
| | | sizeLimit)); |
| | | return false; |
| | | } |
| | | |
| | | // See if the time limit has expired. If so, then don't send the entry and |
| | | // indicate that the search should end. |
| | | if ((timeLimit > 0) && (TimeThread.getTime() >= timeLimitExpiration)) |
| | | { |
| | | setResultCode(ResultCode.TIME_LIMIT_EXCEEDED); |
| | | appendErrorMessage(getMessage(MSGID_SEARCH_TIME_LIMIT_EXCEEDED, |
| | | timeLimit)); |
| | | return false; |
| | | } |
| | | |
| | | // Determine whether the provided entry is a subentry and if so whether it |
| | | // should be returned. |
| | | if ((scope != SearchScope.BASE_OBJECT) && (! returnLDAPSubentries) && |
| | | entry.isLDAPSubentry()) |
| | | { |
| | | // Check to see if the filter contains an equality element with the |
| | | // objectclass attribute type and a value of "ldapSubentry". If so, then |
| | | // we'll return it anyway. Technically, this isn't part of the |
| | | // specification so we don't need to get carried away with really in-depth |
| | | // checks. |
| | | switch (filter.getFilterType()) |
| | | { |
| | | case AND: |
| | | case OR: |
| | | for (SearchFilter f : filter.getFilterComponents()) |
| | | { |
| | | if ((f.getFilterType() == FilterType.EQUALITY) && |
| | | (f.getAttributeType().isObjectClassType())) |
| | | { |
| | | AttributeValue v = f.getAssertionValue(); |
| | | if (toLowerCase(v.getStringValue()).equals("ldapsubentry")) |
| | | { |
| | | returnLDAPSubentries = true; |
| | | } |
| | | break; |
| | | } |
| | | } |
| | | break; |
| | | case EQUALITY: |
| | | AttributeType t = filter.getAttributeType(); |
| | | if (t.isObjectClassType()) |
| | | { |
| | | AttributeValue v = filter.getAssertionValue(); |
| | | if (toLowerCase(v.getStringValue()).equals("ldapsubentry")) |
| | | { |
| | | returnLDAPSubentries = true; |
| | | } |
| | | } |
| | | break; |
| | | } |
| | | |
| | | if (! returnLDAPSubentries) |
| | | { |
| | | // We still shouldn't return it even based on the filter. Just throw it |
| | | // away without doing anything. |
| | | return true; |
| | | } |
| | | } |
| | | |
| | | |
| | | // Determine whether to include the account usable control. If so, then |
| | | // create it now. |
| | | if (includeUsableControl) |
| | | { |
| | | try |
| | | { |
| | | // FIXME -- Need a way to enable PWP debugging. |
| | | PasswordPolicyState pwpState = new PasswordPolicyState(entry, false, |
| | | false); |
| | | |
| | | boolean isInactive = pwpState.isDisabled() || |
| | | pwpState.isAccountExpired(); |
| | | boolean isLocked = pwpState.lockedDueToFailures() || |
| | | pwpState.lockedDueToMaximumResetAge() || |
| | | pwpState.lockedDueToIdleInterval(); |
| | | boolean isReset = pwpState.mustChangePassword(); |
| | | boolean isExpired = pwpState.isPasswordExpired(); |
| | | |
| | | if (isInactive || isLocked || isReset || isExpired) |
| | | { |
| | | int secondsBeforeUnlock = pwpState.getSecondsUntilUnlock(); |
| | | int remainingGraceLogins = pwpState.getGraceLoginsRemaining(); |
| | | |
| | | if (controls == null) |
| | | { |
| | | controls = new ArrayList<Control>(1); |
| | | } |
| | | |
| | | controls.add(new AccountUsableResponseControl(isInactive, isReset, |
| | | isExpired, remainingGraceLogins, isLocked, |
| | | secondsBeforeUnlock)); |
| | | } |
| | | else |
| | | { |
| | | if (controls == null) |
| | | { |
| | | controls = new ArrayList<Control>(1); |
| | | } |
| | | |
| | | int secondsBeforeExpiration = pwpState.getSecondsUntilExpiration(); |
| | | controls.add(new AccountUsableResponseControl( |
| | | secondsBeforeExpiration)); |
| | | } |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | } |
| | | } |
| | | |
| | | // Check to see if the entry can be read by the client. |
| | | SearchResultEntry tmpSearchEntry = new SearchResultEntry(entry, |
| | | controls); |
| | | if (AccessControlConfigManager.getInstance() |
| | | .getAccessControlHandler().maySend(this, tmpSearchEntry) == false) { |
| | | return true; |
| | | } |
| | | |
| | | // Make a copy of the entry and pare it down to only include the set |
| | | // of |
| | | // requested attributes. |
| | | Entry entryToReturn; |
| | | if ((attributes == null) || attributes.isEmpty()) |
| | | { |
| | | entryToReturn = entry.duplicateWithoutOperationalAttributes(typesOnly, |
| | | true); |
| | | } |
| | | else |
| | | { |
| | | entryToReturn = entry.duplicateWithoutAttributes(); |
| | | |
| | | for (String attrName : attributes) |
| | | { |
| | | if (attrName.equals("*")) |
| | | { |
| | | // This is a special placeholder indicating that all user attributes |
| | | // should be returned. |
| | | if (typesOnly) |
| | | { |
| | | // First, add the placeholder for the objectclass attribute. |
| | | AttributeType ocType = |
| | | DirectoryServer.getObjectClassAttributeType(); |
| | | List<Attribute> ocList = new ArrayList<Attribute>(1); |
| | | ocList.add(new Attribute(ocType)); |
| | | entryToReturn.putAttribute(ocType, ocList); |
| | | } |
| | | else |
| | | { |
| | | // First, add the objectclass attribute. |
| | | Attribute ocAttr = entry.getObjectClassAttribute(); |
| | | try |
| | | { |
| | | entryToReturn.setObjectClasses(ocAttr.getValues()); |
| | | } |
| | | catch (DirectoryException e) |
| | | { |
| | | // We cannot get this exception because the object classes have |
| | | // already been validated in the entry they came from. |
| | | } |
| | | } |
| | | |
| | | // Next iterate through all the user attributes and include them. |
| | | for (AttributeType t : entry.getUserAttributes().keySet()) |
| | | { |
| | | List<Attribute> attrList = |
| | | entry.duplicateUserAttribute(t, null, typesOnly); |
| | | entryToReturn.putAttribute(t, attrList); |
| | | } |
| | | |
| | | continue; |
| | | } |
| | | else if (attrName.equals("+")) |
| | | { |
| | | // This is a special placeholder indicating that all operational |
| | | // attributes should be returned. |
| | | for (AttributeType t : entry.getOperationalAttributes().keySet()) |
| | | { |
| | | List<Attribute> attrList = |
| | | entry.duplicateOperationalAttribute(t, null, typesOnly); |
| | | entryToReturn.putAttribute(t, attrList); |
| | | } |
| | | |
| | | continue; |
| | | } |
| | | |
| | | String lowerName; |
| | | HashSet<String> options; |
| | | int semicolonPos = attrName.indexOf(';'); |
| | | if (semicolonPos > 0) |
| | | { |
| | | lowerName = toLowerCase(attrName.substring(0, semicolonPos)); |
| | | int nextPos = attrName.indexOf(';', semicolonPos+1); |
| | | options = new HashSet<String>(); |
| | | while (nextPos > 0) |
| | | { |
| | | options.add(attrName.substring(semicolonPos+1, nextPos)); |
| | | |
| | | semicolonPos = nextPos; |
| | | nextPos = attrName.indexOf(';', semicolonPos+1); |
| | | } |
| | | |
| | | options.add(attrName.substring(semicolonPos+1)); |
| | | } |
| | | else |
| | | { |
| | | lowerName = toLowerCase(attrName); |
| | | options = null; |
| | | } |
| | | |
| | | |
| | | AttributeType attrType = DirectoryServer.getAttributeType(lowerName); |
| | | if (attrType == null) |
| | | { |
| | | boolean added = false; |
| | | for (AttributeType t : entry.getUserAttributes().keySet()) |
| | | { |
| | | if (t.hasNameOrOID(lowerName)) |
| | | { |
| | | List<Attribute> attrList = |
| | | entry.duplicateUserAttribute(t, options, typesOnly); |
| | | if (attrList != null) |
| | | { |
| | | entryToReturn.putAttribute(t, attrList); |
| | | |
| | | added = true; |
| | | break; |
| | | } |
| | | } |
| | | } |
| | | |
| | | if (added) |
| | | { |
| | | continue; |
| | | } |
| | | |
| | | for (AttributeType t : entry.getOperationalAttributes().keySet()) |
| | | { |
| | | if (t.hasNameOrOID(lowerName)) |
| | | { |
| | | List<Attribute> attrList = |
| | | entry.duplicateOperationalAttribute(t, options, typesOnly); |
| | | if (attrList != null) |
| | | { |
| | | entryToReturn.putAttribute(t, attrList); |
| | | |
| | | break; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | else |
| | | { |
| | | if (attrType.isObjectClassType()) { |
| | | if (typesOnly) |
| | | { |
| | | AttributeType ocType = |
| | | DirectoryServer.getObjectClassAttributeType(); |
| | | List<Attribute> ocList = new ArrayList<Attribute>(1); |
| | | ocList.add(new Attribute(ocType)); |
| | | entryToReturn.putAttribute(ocType, ocList); |
| | | } |
| | | else |
| | | { |
| | | List<Attribute> attrList = new ArrayList<Attribute>(1); |
| | | attrList.add(entry.getObjectClassAttribute()); |
| | | entryToReturn.putAttribute(attrType, attrList); |
| | | } |
| | | } |
| | | else |
| | | { |
| | | List<Attribute> attrList = |
| | | entry.duplicateOperationalAttribute(attrType, options, |
| | | typesOnly); |
| | | if (attrList == null) |
| | | { |
| | | attrList = entry.duplicateUserAttribute(attrType, options, |
| | | typesOnly); |
| | | } |
| | | if (attrList != null) |
| | | { |
| | | entryToReturn.putAttribute(attrType, attrList); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | if (realAttributesOnly) |
| | | { |
| | | entryToReturn.stripVirtualAttributes(); |
| | | } |
| | | else if (virtualAttributesOnly) |
| | | { |
| | | entryToReturn.stripRealAttributes(); |
| | | } |
| | | |
| | | |
| | | // If there is a matched values control, then further pare down the entry |
| | | // based on the filters that it contains. |
| | | if ((matchedValuesControl != null) && (! typesOnly)) |
| | | { |
| | | // First, look at the set of objectclasses. |
| | | AttributeType attrType = DirectoryServer.getObjectClassAttributeType(); |
| | | Iterator<String> ocIterator = |
| | | entryToReturn.getObjectClasses().values().iterator(); |
| | | while (ocIterator.hasNext()) |
| | | { |
| | | String ocName = ocIterator.next(); |
| | | AttributeValue v = new AttributeValue(attrType, |
| | | new ASN1OctetString(ocName)); |
| | | if (! matchedValuesControl.valueMatches(attrType, v)) |
| | | { |
| | | ocIterator.remove(); |
| | | } |
| | | } |
| | | |
| | | |
| | | // Next, the set of user attributes. |
| | | for (AttributeType t : entryToReturn.getUserAttributes().keySet()) |
| | | { |
| | | for (Attribute a : entryToReturn.getUserAttribute(t)) |
| | | { |
| | | Iterator<AttributeValue> valueIterator = a.getValues().iterator(); |
| | | while (valueIterator.hasNext()) |
| | | { |
| | | AttributeValue v = valueIterator.next(); |
| | | if (! matchedValuesControl.valueMatches(t, v)) |
| | | { |
| | | valueIterator.remove(); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | // Then the set of operational attributes. |
| | | for (AttributeType t : entryToReturn.getOperationalAttributes().keySet()) |
| | | { |
| | | for (Attribute a : entryToReturn.getOperationalAttribute(t)) |
| | | { |
| | | Iterator<AttributeValue> valueIterator = a.getValues().iterator(); |
| | | while (valueIterator.hasNext()) |
| | | { |
| | | AttributeValue v = valueIterator.next(); |
| | | if (! matchedValuesControl.valueMatches(t, v)) |
| | | { |
| | | valueIterator.remove(); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | // Convert the provided entry to a search result entry. |
| | | SearchResultEntry searchEntry = new SearchResultEntry(entryToReturn, |
| | | controls); |
| | | |
| | | // Strip out any attributes that the client does not have access to. |
| | | |
| | | // FIXME: need some way to prevent plugins from adding attributes or |
| | | // values that the client is not permitted to see. |
| | | searchEntry = AccessControlConfigManager.getInstance() |
| | | .getAccessControlHandler().filterEntry(this, searchEntry); |
| | | |
| | | // Invoke any search entry plugins that may be registered with the server. |
| | | SearchEntryPluginResult pluginResult = |
| | | DirectoryServer.getPluginConfigManager(). |
| | | invokeSearchResultEntryPlugins(this, searchEntry); |
| | | if (pluginResult.connectionTerminated()) |
| | | { |
| | | // We won't attempt to send this entry, and we won't continue with |
| | | // any processing. Just update the operation to indicate that it was |
| | | // cancelled and return false. |
| | | setResultCode(ResultCode.CANCELED); |
| | | appendErrorMessage(getMessage(MSGID_CANCELED_BY_SEARCH_ENTRY_DISCONNECT, |
| | | String.valueOf(entry.getDN()))); |
| | | return false; |
| | | } |
| | | |
| | | |
| | | // Send the entry to the client. |
| | | if (pluginResult.sendEntry()) |
| | | { |
| | | try |
| | | { |
| | | clientConnection.sendSearchEntry(this, searchEntry); |
| | | |
| | | // Log the entry sent to the client. |
| | | logSearchResultEntry(this, searchEntry); |
| | | |
| | | entriesSent++; |
| | | } |
| | | catch (DirectoryException de) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, de); |
| | | } |
| | | |
| | | setResponseData(de); |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | return pluginResult.continueSearch(); |
| | | } |
| | | |
| | | |
| | | public abstract boolean returnEntry(Entry entry, List<Control> controls); |
| | | |
| | | /** |
| | | * Used as a callback for backends to indicate that the provided search |
| | |
| | | * <CODE>false</CODE> if not for some reason (e.g., the size limit |
| | | * has been reached or the search has been abandoned). |
| | | */ |
| | | public final boolean returnReference(SearchResultReference reference) |
| | | { |
| | | // See if the operation has been abandoned. If so, then don't send the |
| | | // reference and indicate that the search should end. |
| | | if (cancelRequest != null) |
| | | { |
| | | setResultCode(ResultCode.CANCELED); |
| | | return false; |
| | | } |
| | | |
| | | |
| | | // See if the time limit has expired. If so, then don't send the entry and |
| | | // indicate that the search should end. |
| | | if ((timeLimit > 0) && (TimeThread.getTime() >= timeLimitExpiration)) |
| | | { |
| | | setResultCode(ResultCode.TIME_LIMIT_EXCEEDED); |
| | | appendErrorMessage(getMessage(MSGID_SEARCH_TIME_LIMIT_EXCEEDED, |
| | | timeLimit)); |
| | | return false; |
| | | } |
| | | |
| | | |
| | | // See if we know that this client can't handle referrals. If so, then |
| | | // don't even try to send it. |
| | | if (! clientAcceptsReferrals) |
| | | { |
| | | return true; |
| | | } |
| | | |
| | | |
| | | // See if the client has permission to read this reference. |
| | | if (AccessControlConfigManager.getInstance() |
| | | .getAccessControlHandler().maySend(this, reference) == false) { |
| | | return true; |
| | | } |
| | | |
| | | |
| | | // Invoke any search reference plugins that may be registered with the |
| | | // server. |
| | | SearchReferencePluginResult pluginResult = |
| | | DirectoryServer.getPluginConfigManager(). |
| | | invokeSearchResultReferencePlugins(this, reference); |
| | | if (pluginResult.connectionTerminated()) |
| | | { |
| | | // We won't attempt to send this entry, and we won't continue with |
| | | // any processing. Just update the operation to indicate that it was |
| | | // cancelled and return false. |
| | | setResultCode(ResultCode.CANCELED); |
| | | appendErrorMessage(getMessage(MSGID_CANCELED_BY_SEARCH_REF_DISCONNECT, |
| | | String.valueOf(reference.getReferralURLString()))); |
| | | return false; |
| | | } |
| | | |
| | | |
| | | // Send the reference to the client. Note that this could throw an |
| | | // exception, which would indicate that the associated client can't handle |
| | | // referrals. If that't the case, then set a flag so we'll know not to try |
| | | // to send any more. |
| | | if (pluginResult.sendReference()) |
| | | { |
| | | try |
| | | { |
| | | if (clientConnection.sendSearchReference(this, reference)) |
| | | { |
| | | // Log the entry sent to the client. |
| | | logSearchResultReference(this, reference); |
| | | referencesSent++; |
| | | |
| | | // FIXME -- Should the size limit apply here? |
| | | } |
| | | else |
| | | { |
| | | // We know that the client can't handle referrals, so we won't try to |
| | | // send it any more. |
| | | clientAcceptsReferrals = false; |
| | | } |
| | | } |
| | | catch (DirectoryException de) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, de); |
| | | } |
| | | |
| | | setResponseData(de); |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | |
| | | return pluginResult.continueSearch(); |
| | | } |
| | | |
| | | |
| | | public abstract boolean returnReference(SearchResultReference reference); |
| | | |
| | | /** |
| | | * Sends the search result done message to the client. Note that this method |
| | |
| | | * message should have been set for this operation before this method is |
| | | * called. |
| | | */ |
| | | public final void sendSearchResultDone() |
| | | { |
| | | // Send the search result done message to the client. We want to make sure |
| | | // that this only gets sent once, and it's possible that this could be |
| | | // multithreaded in the event of a persistent search, so do it safely. |
| | | if (responseSent.compareAndSet(false, true)) |
| | | { |
| | | // Send the response to the client. |
| | | clientConnection.sendResponse(this); |
| | | |
| | | // Log the search result. |
| | | logSearchResultDone(this); |
| | | |
| | | |
| | | // Invoke the post-response search plugins. |
| | | DirectoryServer.getPluginConfigManager(). |
| | | invokePostResponseSearchPlugins(this); |
| | | } |
| | | } |
| | | |
| | | |
| | | public abstract void sendSearchResultDone(); |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | * Set the time after which the search time limit has expired. |
| | | * |
| | | * @param timeLimitExpiration - Time after which the search has expired |
| | | */ |
| | | @Override() |
| | | public final OperationType getOperationType() |
| | | { |
| | | // Note that no debugging will be done in this method because it is a likely |
| | | // candidate for being called by the logging subsystem. |
| | | |
| | | return OperationType.SEARCH; |
| | | } |
| | | |
| | | |
| | | public abstract void setTimeLimitExpiration(Long timeLimitExpiration); |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | * Indicates whether LDAP subentries should be returned or not. |
| | | * |
| | | * @return true if the LDAP subentries should be returned, false otherwise |
| | | */ |
| | | @Override() |
| | | public final void disconnectClient(DisconnectReason disconnectReason, |
| | | boolean sendNotification, String message, |
| | | int messageID) |
| | | { |
| | | // Before calling clientConnection.disconnect, we need to mark this |
| | | // operation as cancelled so that the attempt to cancel it later won't cause |
| | | // an unnecessary delay. |
| | | setCancelResult(CancelResult.CANCELED); |
| | | |
| | | clientConnection.disconnect(disconnectReason, sendNotification, message, |
| | | messageID); |
| | | } |
| | | |
| | | |
| | | public abstract boolean isReturnLDAPSubentries(); |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | * Set the flag indicating wether the LDAP subentries should be returned. |
| | | * |
| | | * @param returnLDAPSubentries - Boolean indicating wether the LDAP |
| | | * subentries should be returned or not |
| | | */ |
| | | @Override() |
| | | public final String[][] getRequestLogElements() |
| | | { |
| | | // Note that no debugging will be done in this method because it is a likely |
| | | // candidate for being called by the logging subsystem. |
| | | |
| | | String attrs; |
| | | if ((attributes == null) || attributes.isEmpty()) |
| | | { |
| | | attrs = null; |
| | | } |
| | | else |
| | | { |
| | | StringBuilder attrBuffer = new StringBuilder(); |
| | | Iterator<String> iterator = attributes.iterator(); |
| | | attrBuffer.append(iterator.next()); |
| | | |
| | | while (iterator.hasNext()) |
| | | { |
| | | attrBuffer.append(", "); |
| | | attrBuffer.append(iterator.next()); |
| | | } |
| | | |
| | | attrs = attrBuffer.toString(); |
| | | } |
| | | |
| | | return new String[][] |
| | | { |
| | | new String[] { LOG_ELEMENT_BASE_DN, String.valueOf(rawBaseDN) }, |
| | | new String[] { LOG_ELEMENT_SCOPE, String.valueOf(scope) }, |
| | | new String[] { LOG_ELEMENT_SIZE_LIMIT, String.valueOf(sizeLimit) }, |
| | | new String[] { LOG_ELEMENT_TIME_LIMIT, String.valueOf(timeLimit) }, |
| | | new String[] { LOG_ELEMENT_FILTER, String.valueOf(rawFilter) }, |
| | | new String[] { LOG_ELEMENT_REQUESTED_ATTRIBUTES, attrs } |
| | | }; |
| | | } |
| | | |
| | | |
| | | public abstract void setReturnLDAPSubentries(boolean returnLDAPSubentries); |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | * The matched values control associated with this search operation. |
| | | * |
| | | * @return the match values control |
| | | */ |
| | | @Override() |
| | | public final String[][] getResponseLogElements() |
| | | { |
| | | // Note that no debugging will be done in this method because it is a likely |
| | | // candidate for being called by the logging subsystem. |
| | | public abstract MatchedValuesControl getMatchedValuesControl(); |
| | | |
| | | String resultCode = String.valueOf(getResultCode().getIntValue()); |
| | | /** |
| | | * Set the match values control. |
| | | * |
| | | * @param controls - The matched values control |
| | | */ |
| | | public abstract void setMatchedValuesControl(MatchedValuesControl controls); |
| | | |
| | | String errorMessage; |
| | | StringBuilder errorMessageBuffer = getErrorMessage(); |
| | | if (errorMessageBuffer == null) |
| | | { |
| | | errorMessage = null; |
| | | } |
| | | else |
| | | { |
| | | errorMessage = errorMessageBuffer.toString(); |
| | | } |
| | | /** |
| | | * Indicates whether to include the account usable response control with |
| | | * search result entries or not. |
| | | * |
| | | * @return true if the usable control has to be part of the search result |
| | | * entry |
| | | */ |
| | | public abstract boolean isIncludeUsableControl(); |
| | | |
| | | String matchedDNStr; |
| | | DN matchedDN = getMatchedDN(); |
| | | if (matchedDN == null) |
| | | { |
| | | matchedDNStr = null; |
| | | } |
| | | else |
| | | { |
| | | matchedDNStr = matchedDN.toString(); |
| | | } |
| | | /** |
| | | * Specify whether to include the account usable response control within the |
| | | * search result entries. |
| | | * |
| | | * @param includeUsableControl - True if the account usable response control |
| | | * has to be included within the search result |
| | | * entries, false otherwise |
| | | */ |
| | | public abstract void setIncludeUsableControl(boolean includeUsableControl); |
| | | |
| | | String referrals; |
| | | List<String> referralURLs = getReferralURLs(); |
| | | if ((referralURLs == null) || referralURLs.isEmpty()) |
| | | { |
| | | referrals = null; |
| | | } |
| | | else |
| | | { |
| | | StringBuilder buffer = new StringBuilder(); |
| | | Iterator<String> iterator = referralURLs.iterator(); |
| | | buffer.append(iterator.next()); |
| | | /** |
| | | * Register the psearch in the search operation. |
| | | * |
| | | * @param psearch - Persistent search associated to that operation |
| | | */ |
| | | public abstract void setPersistentSearch(PersistentSearch psearch); |
| | | |
| | | while (iterator.hasNext()) |
| | | { |
| | | buffer.append(", "); |
| | | buffer.append(iterator.next()); |
| | | } |
| | | /** |
| | | * Get the psearch from the search operation. |
| | | * |
| | | * @return the psearch, or null if no psearch was registered |
| | | */ |
| | | public abstract PersistentSearch getPersistentSearch(); |
| | | |
| | | referrals = buffer.toString(); |
| | | } |
| | | /** |
| | | * Indicates whether the client is able to handle referrals. |
| | | * |
| | | * @return true, if the client is able to handle referrals |
| | | */ |
| | | public abstract boolean isClientAcceptsReferrals(); |
| | | |
| | | String processingTime = |
| | | String.valueOf(processingStopTime - processingStartTime); |
| | | /** |
| | | * Specify whether the client is able to handle referrals. |
| | | * |
| | | * @param clientAcceptReferrals - Boolean set to true if the client |
| | | * can handle referrals |
| | | */ |
| | | public abstract void setClientAcceptsReferrals(boolean clientAcceptReferrals); |
| | | |
| | | return new String[][] |
| | | { |
| | | new String[] { LOG_ELEMENT_RESULT_CODE, resultCode }, |
| | | new String[] { LOG_ELEMENT_ERROR_MESSAGE, errorMessage }, |
| | | new String[] { LOG_ELEMENT_MATCHED_DN, matchedDNStr }, |
| | | new String[] { LOG_ELEMENT_REFERRAL_URLS, referrals }, |
| | | new String[] { LOG_ELEMENT_ENTRIES_SENT, String.valueOf(entriesSent) }, |
| | | new String[] { LOG_ELEMENT_REFERENCES_SENT, |
| | | String.valueOf(referencesSent ) }, |
| | | new String[] { LOG_ELEMENT_PROCESSING_TIME, processingTime } |
| | | }; |
| | | } |
| | | /** |
| | | * Increments by 1 the number of entries sent to the client for this search |
| | | * operation. |
| | | */ |
| | | public abstract void incrementEntriesSent(); |
| | | |
| | | /** |
| | | * Increments by 1 the number of search references sent to the client for this |
| | | * search operation. |
| | | */ |
| | | public abstract void incrementReferencesSent(); |
| | | |
| | | /** |
| | | * Indicates wether the search result done message has to be sent |
| | | * to the client, or not. |
| | | * |
| | | * @return true if the search result done message is to be sent to the client |
| | | */ |
| | | public abstract boolean isSendResponse(); |
| | | |
| | | /** |
| | | * Specify wether the search result done message has to be sent |
| | | * to the client, or not. |
| | | * |
| | | * @param sendResponse - boolean indicating wether the search result done |
| | | * message is to send to the client |
| | | */ |
| | | public abstract void setSendResponse(boolean sendResponse); |
| | | |
| | | /** |
| | | * Returns true if only real attributes should be returned. |
| | | * |
| | | * @return true if only real attributes should be returned, false otherwise |
| | | */ |
| | | public abstract boolean isRealAttributesOnly(); |
| | | |
| | | /** |
| | | * Specify wether to only return real attributes. |
| | | * |
| | | * @param realAttributesOnly - boolean setup to true, if only the real |
| | | * attributes should be returned |
| | | */ |
| | | public abstract void setRealAttributesOnly(boolean realAttributesOnly); |
| | | |
| | | /** |
| | | * Returns true if only virtual attributes should be returned. |
| | | * |
| | | * @return true if only virtual attributes should be returned, false |
| | | * otherwise |
| | | */ |
| | | public abstract boolean isVirtualAttributesOnly(); |
| | | |
| | | /** |
| | | * Specify wether to only return virtual attributes. |
| | | * |
| | | * @param virtualAttributesOnly - boolean setup to true, if only the virtual |
| | | * attributes should be returned |
| | | */ |
| | | public abstract void setVirtualAttributesOnly(boolean virtualAttributesOnly); |
| | | |
| | | /** |
| | | * Sends the provided search result entry to the client. |
| | | * |
| | | * @param entry The search result entry to be sent to |
| | | * the client. |
| | | * |
| | | * @throws DirectoryException If a problem occurs while attempting |
| | | * to send the entry to the client and |
| | | * the search should be terminated. |
| | | */ |
| | | public abstract void sendSearchEntry(SearchResultEntry entry) |
| | | throws DirectoryException; |
| | | |
| | | /** |
| | | * Sends the provided search result reference to the client. |
| | | * |
| | | * @param reference The search result reference to be sent |
| | | * to the client. |
| | | * |
| | | * @return <CODE>true</CODE> if the client is able to accept |
| | | * referrals, or <CODE>false</CODE> if the client cannot |
| | | * handle referrals and no more attempts should be made to |
| | | * send them for the associated search operation. |
| | | * |
| | | * @throws DirectoryException If a problem occurs while attempting |
| | | * to send the reference to the client |
| | | * and the search should be terminated. |
| | | */ |
| | | public abstract boolean sendSearchReference(SearchResultReference reference) |
| | | throws DirectoryException; |
| | | |
| | | /** |
| | | * Retrieves the proxied authorization DN for this operation if proxied |
| | |
| | | * authorization has been requested, or {@code null} if proxied |
| | | * authorization has not been requested. |
| | | */ |
| | | public DN getProxiedAuthorizationDN() |
| | | { |
| | | return proxiedAuthorizationDN; |
| | | } |
| | | |
| | | |
| | | public abstract DN getProxiedAuthorizationDN(); |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final List<Control> getResponseControls() |
| | | { |
| | | return responseControls; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final void addResponseControl(Control control) |
| | | { |
| | | responseControls.add(control); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final void removeResponseControl(Control control) |
| | | { |
| | | responseControls.remove(control); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final void run() |
| | | { |
| | | setResultCode(ResultCode.UNDEFINED); |
| | | boolean sendResponse = true; |
| | | |
| | | |
| | | // Get the plugin config manager that will be used for invoking plugins. |
| | | PluginConfigManager pluginConfigManager = |
| | | DirectoryServer.getPluginConfigManager(); |
| | | boolean skipPostOperation = false; |
| | | |
| | | |
| | | // Start the processing timer. |
| | | processingStartTime = System.currentTimeMillis(); |
| | | if (timeLimit <= 0) |
| | | { |
| | | timeLimitExpiration = Long.MAX_VALUE; |
| | | } |
| | | else |
| | | { |
| | | // FIXME -- Factor in the user's effective time limit. |
| | | timeLimitExpiration = processingStartTime + (1000L * timeLimit); |
| | | } |
| | | |
| | | |
| | | // Check for and handle a request to cancel this operation. |
| | | if (cancelRequest != null) |
| | | { |
| | | indicateCancelled(cancelRequest); |
| | | processingStopTime = System.currentTimeMillis(); |
| | | return; |
| | | } |
| | | |
| | | |
| | | // Create a labeled block of code that we can break out of if a problem is |
| | | // detected. |
| | | searchProcessing: |
| | | { |
| | | PreParsePluginResult preParseResult = |
| | | pluginConfigManager.invokePreParseSearchPlugins(this); |
| | | if (preParseResult.connectionTerminated()) |
| | | { |
| | | // There's no point in continuing with anything. Log the request and |
| | | // result and return. |
| | | setResultCode(ResultCode.CANCELED); |
| | | |
| | | int msgID = MSGID_CANCELED_BY_PREPARSE_DISCONNECT; |
| | | appendErrorMessage(getMessage(msgID)); |
| | | |
| | | processingStopTime = System.currentTimeMillis(); |
| | | |
| | | logSearchRequest(this); |
| | | logSearchResultDone(this); |
| | | pluginConfigManager.invokePostResponseSearchPlugins(this); |
| | | return; |
| | | } |
| | | else if (preParseResult.sendResponseImmediately()) |
| | | { |
| | | skipPostOperation = true; |
| | | logSearchRequest(this); |
| | | break searchProcessing; |
| | | } |
| | | else if (preParseResult.skipCoreProcessing()) |
| | | { |
| | | skipPostOperation = false; |
| | | break searchProcessing; |
| | | } |
| | | |
| | | |
| | | // Log the search request message. |
| | | logSearchRequest(this); |
| | | |
| | | |
| | | // Check for and handle a request to cancel this operation. |
| | | if (cancelRequest != null) |
| | | { |
| | | indicateCancelled(cancelRequest); |
| | | processingStopTime = System.currentTimeMillis(); |
| | | logSearchResultDone(this); |
| | | pluginConfigManager.invokePostResponseSearchPlugins(this); |
| | | return; |
| | | } |
| | | |
| | | |
| | | // Process the search base and filter to convert them from their raw forms |
| | | // as provided by the client to the forms required for the rest of the |
| | | // search processing. |
| | | try |
| | | { |
| | | if (baseDN == null) |
| | | { |
| | | baseDN = DN.decode(rawBaseDN); |
| | | } |
| | | } |
| | | catch (DirectoryException de) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, de); |
| | | } |
| | | |
| | | setResultCode(de.getResultCode()); |
| | | appendErrorMessage(de.getErrorMessage()); |
| | | setMatchedDN(de.getMatchedDN()); |
| | | setReferralURLs(de.getReferralURLs()); |
| | | |
| | | break searchProcessing; |
| | | } |
| | | |
| | | try |
| | | { |
| | | if (filter == null) |
| | | { |
| | | filter = rawFilter.toSearchFilter(); |
| | | } |
| | | } |
| | | catch (DirectoryException de) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, de); |
| | | } |
| | | |
| | | setResultCode(de.getResultCode()); |
| | | appendErrorMessage(de.getErrorMessage()); |
| | | setMatchedDN(de.getMatchedDN()); |
| | | setReferralURLs(de.getReferralURLs()); |
| | | |
| | | break searchProcessing; |
| | | } |
| | | |
| | | // Check to see if there are any controls in the request. If so, then |
| | | // see if there is any special processing required. |
| | | boolean processSearch = true; |
| | | List<Control> requestControls = 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); |
| | | } |
| | | |
| | | setResultCode(ResultCode.valueOf(le.getResultCode())); |
| | | appendErrorMessage(le.getMessage()); |
| | | |
| | | break searchProcessing; |
| | | } |
| | | } |
| | | |
| | | try |
| | | { |
| | | // FIXME -- We need to determine whether the current user has |
| | | // permission to make this determination. |
| | | SearchFilter assertionFilter = assertControl.getSearchFilter(); |
| | | Entry entry; |
| | | try |
| | | { |
| | | entry = DirectoryServer.getEntry(baseDN); |
| | | } |
| | | catch (DirectoryException de) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, de); |
| | | } |
| | | |
| | | setResultCode(de.getResultCode()); |
| | | |
| | | int msgID = MSGID_SEARCH_CANNOT_GET_ENTRY_FOR_ASSERTION; |
| | | appendErrorMessage(getMessage(msgID, de.getErrorMessage())); |
| | | |
| | | break searchProcessing; |
| | | } |
| | | |
| | | if (entry == null) |
| | | { |
| | | setResultCode(ResultCode.NO_SUCH_OBJECT); |
| | | |
| | | int msgID = MSGID_SEARCH_NO_SUCH_ENTRY_FOR_ASSERTION; |
| | | appendErrorMessage(getMessage(msgID)); |
| | | |
| | | break searchProcessing; |
| | | } |
| | | |
| | | |
| | | if (! assertionFilter.matchesEntry(entry)) |
| | | { |
| | | setResultCode(ResultCode.ASSERTION_FAILED); |
| | | |
| | | appendErrorMessage(getMessage(MSGID_SEARCH_ASSERTION_FAILED)); |
| | | |
| | | break searchProcessing; |
| | | } |
| | | } |
| | | catch (DirectoryException de) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, de); |
| | | } |
| | | |
| | | setResultCode(ResultCode.PROTOCOL_ERROR); |
| | | |
| | | int msgID = MSGID_SEARCH_CANNOT_PROCESS_ASSERTION_FILTER; |
| | | appendErrorMessage(getMessage(msgID, de.getErrorMessage())); |
| | | |
| | | break searchProcessing; |
| | | } |
| | | } |
| | | else if (oid.equals(OID_PROXIED_AUTH_V1)) |
| | | { |
| | | // The requester must have the PROXIED_AUTH privilige in order to be |
| | | // able to use this control. |
| | | if (! clientConnection.hasPrivilege(Privilege.PROXIED_AUTH, this)) |
| | | { |
| | | int msgID = MSGID_PROXYAUTH_INSUFFICIENT_PRIVILEGES; |
| | | appendErrorMessage(getMessage(msgID)); |
| | | setResultCode(ResultCode.AUTHORIZATION_DENIED); |
| | | break searchProcessing; |
| | | } |
| | | |
| | | |
| | | ProxiedAuthV1Control proxyControl; |
| | | if (c instanceof ProxiedAuthV1Control) |
| | | { |
| | | proxyControl = (ProxiedAuthV1Control) c; |
| | | } |
| | | else |
| | | { |
| | | try |
| | | { |
| | | proxyControl = ProxiedAuthV1Control.decodeControl(c); |
| | | } |
| | | catch (LDAPException le) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, le); |
| | | } |
| | | |
| | | setResultCode(ResultCode.valueOf(le.getResultCode())); |
| | | appendErrorMessage(le.getMessage()); |
| | | |
| | | break searchProcessing; |
| | | } |
| | | } |
| | | |
| | | |
| | | Entry authorizationEntry; |
| | | try |
| | | { |
| | | authorizationEntry = proxyControl.getAuthorizationEntry(); |
| | | } |
| | | catch (DirectoryException de) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, de); |
| | | } |
| | | |
| | | setResultCode(de.getResultCode()); |
| | | appendErrorMessage(de.getErrorMessage()); |
| | | |
| | | break searchProcessing; |
| | | } |
| | | |
| | | if (AccessControlConfigManager.getInstance(). |
| | | getAccessControlHandler().isProxiedAuthAllowed(this, |
| | | authorizationEntry) == false) { |
| | | setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS); |
| | | |
| | | int msgID = MSGID_SEARCH_AUTHZ_INSUFFICIENT_ACCESS_RIGHTS; |
| | | appendErrorMessage(getMessage(msgID, String.valueOf(baseDN))); |
| | | |
| | | skipPostOperation = true; |
| | | break searchProcessing; |
| | | } |
| | | setAuthorizationEntry(authorizationEntry); |
| | | if (authorizationEntry == null) |
| | | { |
| | | proxiedAuthorizationDN = DN.nullDN(); |
| | | } |
| | | else |
| | | { |
| | | proxiedAuthorizationDN = authorizationEntry.getDN(); |
| | | } |
| | | } |
| | | else if (oid.equals(OID_PROXIED_AUTH_V2)) |
| | | { |
| | | // The requester must have the PROXIED_AUTH privilige in order to be |
| | | // able to use this control. |
| | | if (! clientConnection.hasPrivilege(Privilege.PROXIED_AUTH, this)) |
| | | { |
| | | int msgID = MSGID_PROXYAUTH_INSUFFICIENT_PRIVILEGES; |
| | | appendErrorMessage(getMessage(msgID)); |
| | | setResultCode(ResultCode.AUTHORIZATION_DENIED); |
| | | break searchProcessing; |
| | | } |
| | | |
| | | |
| | | ProxiedAuthV2Control proxyControl; |
| | | if (c instanceof ProxiedAuthV2Control) |
| | | { |
| | | proxyControl = (ProxiedAuthV2Control) c; |
| | | } |
| | | else |
| | | { |
| | | try |
| | | { |
| | | proxyControl = ProxiedAuthV2Control.decodeControl(c); |
| | | } |
| | | catch (LDAPException le) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, le); |
| | | } |
| | | |
| | | setResultCode(ResultCode.valueOf(le.getResultCode())); |
| | | appendErrorMessage(le.getMessage()); |
| | | |
| | | break searchProcessing; |
| | | } |
| | | } |
| | | |
| | | |
| | | Entry authorizationEntry; |
| | | try |
| | | { |
| | | authorizationEntry = proxyControl.getAuthorizationEntry(); |
| | | } |
| | | catch (DirectoryException de) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, de); |
| | | } |
| | | |
| | | setResultCode(de.getResultCode()); |
| | | appendErrorMessage(de.getErrorMessage()); |
| | | |
| | | break searchProcessing; |
| | | } |
| | | |
| | | if (AccessControlConfigManager.getInstance() |
| | | .getAccessControlHandler().isProxiedAuthAllowed(this, |
| | | authorizationEntry) == false) { |
| | | setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS); |
| | | |
| | | int msgID = MSGID_SEARCH_AUTHZ_INSUFFICIENT_ACCESS_RIGHTS; |
| | | appendErrorMessage(getMessage(msgID, String.valueOf(baseDN))); |
| | | |
| | | skipPostOperation = true; |
| | | break searchProcessing; |
| | | } |
| | | |
| | | setAuthorizationEntry(authorizationEntry); |
| | | if (authorizationEntry == null) |
| | | { |
| | | proxiedAuthorizationDN = DN.nullDN(); |
| | | } |
| | | else |
| | | { |
| | | proxiedAuthorizationDN = authorizationEntry.getDN(); |
| | | } |
| | | } |
| | | else if (oid.equals(OID_PERSISTENT_SEARCH)) |
| | | { |
| | | PersistentSearchControl psearchControl; |
| | | if (c instanceof PersistentSearchControl) |
| | | { |
| | | psearchControl = (PersistentSearchControl) c; |
| | | } |
| | | else |
| | | { |
| | | try |
| | | { |
| | | psearchControl = PersistentSearchControl.decodeControl(c); |
| | | } |
| | | catch (LDAPException le) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, le); |
| | | } |
| | | |
| | | setResultCode(ResultCode.valueOf(le.getResultCode())); |
| | | appendErrorMessage(le.getMessage()); |
| | | |
| | | break searchProcessing; |
| | | } |
| | | } |
| | | |
| | | persistentSearch = |
| | | new PersistentSearch(this, psearchControl.getChangeTypes(), |
| | | psearchControl.getReturnECs()); |
| | | |
| | | // If we're only interested in changes, then we don't actually want |
| | | // to process the search now. |
| | | if (psearchControl.getChangesOnly()) |
| | | { |
| | | processSearch = false; |
| | | } |
| | | } |
| | | else if (oid.equals(OID_LDAP_SUBENTRIES)) |
| | | { |
| | | returnLDAPSubentries = true; |
| | | } |
| | | else if (oid.equals(OID_MATCHED_VALUES)) |
| | | { |
| | | if (c instanceof MatchedValuesControl) |
| | | { |
| | | matchedValuesControl = (MatchedValuesControl) c; |
| | | } |
| | | else |
| | | { |
| | | try |
| | | { |
| | | matchedValuesControl = MatchedValuesControl.decodeControl(c); |
| | | } |
| | | catch (LDAPException le) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, le); |
| | | } |
| | | |
| | | setResultCode(ResultCode.valueOf(le.getResultCode())); |
| | | appendErrorMessage(le.getMessage()); |
| | | |
| | | break searchProcessing; |
| | | } |
| | | } |
| | | } |
| | | else if (oid.equals(OID_ACCOUNT_USABLE_CONTROL)) |
| | | { |
| | | includeUsableControl = true; |
| | | } |
| | | else if (oid.equals(OID_REAL_ATTRS_ONLY)) |
| | | { |
| | | realAttributesOnly = true; |
| | | } |
| | | else if (oid.equals(OID_VIRTUAL_ATTRS_ONLY)) |
| | | { |
| | | virtualAttributesOnly = true; |
| | | } else if(oid.equals(OID_GET_EFFECTIVE_RIGHTS)) { |
| | | GetEffectiveRights effectiveRightsControl; |
| | | if (c instanceof GetEffectiveRights) |
| | | { |
| | | effectiveRightsControl = (GetEffectiveRights) c; |
| | | } |
| | | else |
| | | { |
| | | try |
| | | { |
| | | effectiveRightsControl = GetEffectiveRights.decodeControl(c); |
| | | } |
| | | catch (LDAPException le) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, le); |
| | | } |
| | | |
| | | setResultCode(ResultCode.valueOf(le.getResultCode())); |
| | | appendErrorMessage(le.getMessage()); |
| | | |
| | | break searchProcessing; |
| | | } |
| | | } |
| | | |
| | | if (!AccessControlConfigManager.getInstance() |
| | | .getAccessControlHandler(). |
| | | isGetEffectiveRightsAllowed(this, effectiveRightsControl)) { |
| | | setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS); |
| | | int msgID = |
| | | MSGID_SEARCH_EFFECTIVERIGHTS_INSUFFICIENT_ACCESS_RIGHTS; |
| | | appendErrorMessage(getMessage(msgID, String.valueOf(baseDN))); |
| | | |
| | | skipPostOperation = true; |
| | | break searchProcessing; |
| | | } |
| | | } |
| | | |
| | | // NYI -- Add support for additional controls. |
| | | else if (c.isCritical()) |
| | | { |
| | | Backend backend = DirectoryServer.getBackend(baseDN); |
| | | if ((backend == null) || (! backend.supportsControl(oid))) |
| | | { |
| | | setResultCode(ResultCode.UNAVAILABLE_CRITICAL_EXTENSION); |
| | | |
| | | int msgID = MSGID_SEARCH_UNSUPPORTED_CRITICAL_CONTROL; |
| | | appendErrorMessage(getMessage(msgID, oid)); |
| | | |
| | | break searchProcessing; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | // Check to see if the client has permission to perform the |
| | | // search. |
| | | |
| | | // FIXME: for now assume that this will check all permission |
| | | // pertinent to the operation. This includes proxy authorization |
| | | // and any other controls specified. |
| | | if (AccessControlConfigManager.getInstance() |
| | | .getAccessControlHandler().isAllowed(this) == false) { |
| | | setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS); |
| | | |
| | | int msgID = MSGID_SEARCH_AUTHZ_INSUFFICIENT_ACCESS_RIGHTS; |
| | | appendErrorMessage(getMessage(msgID, String.valueOf(baseDN))); |
| | | |
| | | skipPostOperation = true; |
| | | break searchProcessing; |
| | | } |
| | | |
| | | // Check for and handle a request to cancel this operation. |
| | | if (cancelRequest != null) |
| | | { |
| | | indicateCancelled(cancelRequest); |
| | | processingStopTime = System.currentTimeMillis(); |
| | | logSearchResultDone(this); |
| | | pluginConfigManager.invokePostResponseSearchPlugins(this); |
| | | return; |
| | | } |
| | | |
| | | |
| | | // Invoke the pre-operation search plugins. |
| | | PreOperationPluginResult preOpResult = |
| | | pluginConfigManager.invokePreOperationSearchPlugins(this); |
| | | if (preOpResult.connectionTerminated()) |
| | | { |
| | | // There's no point in continuing with anything. Log the request and |
| | | // result and return. |
| | | setResultCode(ResultCode.CANCELED); |
| | | |
| | | int msgID = MSGID_CANCELED_BY_PREOP_DISCONNECT; |
| | | appendErrorMessage(getMessage(msgID)); |
| | | |
| | | processingStopTime = System.currentTimeMillis(); |
| | | logSearchResultDone(this); |
| | | pluginConfigManager.invokePostResponseSearchPlugins(this); |
| | | return; |
| | | } |
| | | else if (preOpResult.sendResponseImmediately()) |
| | | { |
| | | skipPostOperation = true; |
| | | break searchProcessing; |
| | | } |
| | | else if (preOpResult.skipCoreProcessing()) |
| | | { |
| | | skipPostOperation = false; |
| | | break searchProcessing; |
| | | } |
| | | |
| | | |
| | | // Check for and handle a request to cancel this operation. |
| | | if (cancelRequest != null) |
| | | { |
| | | indicateCancelled(cancelRequest); |
| | | processingStopTime = System.currentTimeMillis(); |
| | | logSearchResultDone(this); |
| | | pluginConfigManager.invokePostResponseSearchPlugins(this); |
| | | return; |
| | | } |
| | | |
| | | |
| | | // Get the backend that should hold the search base. If there is none, |
| | | // then fail. |
| | | Backend backend = DirectoryServer.getBackend(baseDN); |
| | | if (backend == null) |
| | | { |
| | | setResultCode(ResultCode.NO_SUCH_OBJECT); |
| | | appendErrorMessage(getMessage(MSGID_SEARCH_BASE_DOESNT_EXIST, |
| | | String.valueOf(baseDN))); |
| | | break searchProcessing; |
| | | } |
| | | |
| | | |
| | | // We'll set the result code to "success". If a problem occurs, then it |
| | | // will be overwritten. |
| | | setResultCode(ResultCode.SUCCESS); |
| | | |
| | | |
| | | // If there's a persistent search, then register it with the server. |
| | | if (persistentSearch != null) |
| | | { |
| | | DirectoryServer.registerPersistentSearch(persistentSearch); |
| | | sendResponse = false; |
| | | } |
| | | |
| | | |
| | | // Process the search in the backend and all its subordinates. |
| | | try |
| | | { |
| | | if (processSearch) |
| | | { |
| | | searchBackend(backend); |
| | | } |
| | | } |
| | | catch (DirectoryException de) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, de); |
| | | } |
| | | |
| | | setResultCode(de.getResultCode()); |
| | | appendErrorMessage(de.getErrorMessage()); |
| | | setMatchedDN(de.getMatchedDN()); |
| | | setReferralURLs(de.getReferralURLs()); |
| | | |
| | | if (persistentSearch != null) |
| | | { |
| | | DirectoryServer.deregisterPersistentSearch(persistentSearch); |
| | | sendResponse = true; |
| | | } |
| | | |
| | | break searchProcessing; |
| | | } |
| | | catch (CancelledOperationException coe) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, coe); |
| | | } |
| | | |
| | | CancelResult cancelResult = coe.getCancelResult(); |
| | | |
| | | setCancelResult(cancelResult); |
| | | setResultCode(cancelResult.getResultCode()); |
| | | |
| | | String message = coe.getMessage(); |
| | | if ((message != null) && (message.length() > 0)) |
| | | { |
| | | appendErrorMessage(message); |
| | | } |
| | | |
| | | if (persistentSearch != null) |
| | | { |
| | | DirectoryServer.deregisterPersistentSearch(persistentSearch); |
| | | sendResponse = true; |
| | | } |
| | | |
| | | skipPostOperation = true; |
| | | break searchProcessing; |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | |
| | | setResultCode(DirectoryServer.getServerErrorResultCode()); |
| | | |
| | | int msgID = MSGID_SEARCH_BACKEND_EXCEPTION; |
| | | appendErrorMessage(getMessage(msgID, getExceptionMessage(e))); |
| | | |
| | | if (persistentSearch != null) |
| | | { |
| | | DirectoryServer.deregisterPersistentSearch(persistentSearch); |
| | | sendResponse = true; |
| | | } |
| | | |
| | | skipPostOperation = true; |
| | | break searchProcessing; |
| | | } |
| | | } |
| | | |
| | | |
| | | // Check for and handle a request to cancel this operation. |
| | | if (cancelRequest != null) |
| | | { |
| | | indicateCancelled(cancelRequest); |
| | | processingStopTime = System.currentTimeMillis(); |
| | | logSearchResultDone(this); |
| | | pluginConfigManager.invokePostResponseSearchPlugins(this); |
| | | return; |
| | | } |
| | | |
| | | |
| | | // Invoke the post-operation search plugins. |
| | | if (! skipPostOperation) |
| | | { |
| | | PostOperationPluginResult postOperationResult = |
| | | pluginConfigManager.invokePostOperationSearchPlugins(this); |
| | | if (postOperationResult.connectionTerminated()) |
| | | { |
| | | setResultCode(ResultCode.CANCELED); |
| | | |
| | | int msgID = MSGID_CANCELED_BY_POSTOP_DISCONNECT; |
| | | appendErrorMessage(getMessage(msgID)); |
| | | |
| | | processingStopTime = System.currentTimeMillis(); |
| | | logSearchResultDone(this); |
| | | pluginConfigManager.invokePostResponseSearchPlugins(this); |
| | | return; |
| | | } |
| | | } |
| | | |
| | | |
| | | // Indicate that it is now too late to attempt to cancel the operation. |
| | | setCancelResult(CancelResult.TOO_LATE); |
| | | |
| | | |
| | | // Stop the processing timer. |
| | | processingStopTime = System.currentTimeMillis(); |
| | | |
| | | |
| | | // If everything is successful to this point and it is not a persistent |
| | | // search, then send the search result done message to the client. |
| | | // Otherwise, we'll want to make the size and time limit values unlimited |
| | | // to ensure that the remainder of the persistent search isn't subject to |
| | | // those restrictions. |
| | | if (sendResponse) |
| | | { |
| | | sendSearchResultDone(); |
| | | } |
| | | else |
| | | { |
| | | sizeLimit = 0; |
| | | timeLimit = 0; |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Processes the search in the provided backend and recursively through its |
| | | * subordinate backends. |
| | | * Set the proxied authorization DN for this operation if proxied |
| | | * authorization has been requested. |
| | | * |
| | | * @param backend The backend in which to process the search. |
| | | * |
| | | * @throws DirectoryException If a problem occurs while processing the |
| | | * search. |
| | | * |
| | | * @throws CancelledOperationException If the backend noticed and reacted |
| | | * to a request to cancel or abandon the |
| | | * search operation. |
| | | * @param proxiedAuthorizationDN |
| | | * The proxied authorization DN for this operation if proxied |
| | | * authorization has been requested, or {@code null} if proxied |
| | | * authorization has not been requested. |
| | | */ |
| | | private final void searchBackend(Backend backend) |
| | | throws DirectoryException, CancelledOperationException |
| | | { |
| | | // Check for and handle a request to cancel this operation. |
| | | if (cancelRequest != null) |
| | | { |
| | | setCancelResult(CancelResult.CANCELED); |
| | | processingStopTime = System.currentTimeMillis(); |
| | | return; |
| | | public abstract void setProxiedAuthorizationDN(DN proxiedAuthorizationDN); |
| | | |
| | | } |
| | | |
| | | |
| | | // Perform the search in the provided backend. |
| | | backend.search(this); |
| | | |
| | | |
| | | // If there are any subordinate backends, then process the search there as |
| | | // well. |
| | | Backend[] subBackends = backend.getSubordinateBackends(); |
| | | for (Backend b : subBackends) |
| | | { |
| | | DN[] baseDNs = b.getBaseDNs(); |
| | | for (DN dn : baseDNs) |
| | | { |
| | | if (dn.isDescendantOf(baseDN)) |
| | | { |
| | | searchBackend(b); |
| | | break; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final CancelResult cancel(CancelRequest cancelRequest) |
| | | { |
| | | this.cancelRequest = cancelRequest; |
| | | |
| | | if (persistentSearch != null) |
| | | { |
| | | DirectoryServer.deregisterPersistentSearch(persistentSearch); |
| | | persistentSearch = null; |
| | | } |
| | | |
| | | CancelResult cancelResult = getCancelResult(); |
| | | long stopWaitingTime = System.currentTimeMillis() + 5000; |
| | | while ((cancelResult == null) && |
| | | (System.currentTimeMillis() < stopWaitingTime)) |
| | | { |
| | | try |
| | | { |
| | | Thread.sleep(50); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | } |
| | | |
| | | cancelResult = getCancelResult(); |
| | | } |
| | | |
| | | if (cancelResult == null) |
| | | { |
| | | // This can happen in some rare cases (e.g., if a client disconnects and |
| | | // there is still a lot of data to send to that client), and in this case |
| | | // we'll prevent the cancel thread from blocking for a long period of |
| | | // time. |
| | | cancelResult = CancelResult.CANNOT_CANCEL; |
| | | } |
| | | |
| | | return cancelResult; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final CancelRequest getCancelRequest() |
| | | { |
| | | return cancelRequest; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | protected boolean setCancelRequest(CancelRequest cancelRequest) |
| | | { |
| | | this.cancelRequest = cancelRequest; |
| | | return true; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final void toString(StringBuilder buffer) |
| | | { |
| | | buffer.append("SearchOperation(connID="); |
| | | buffer.append(clientConnection.getConnectionID()); |
| | | buffer.append(", opID="); |
| | | buffer.append(operationID); |
| | | buffer.append(", baseDN="); |
| | | buffer.append(rawBaseDN); |
| | | buffer.append(", scope="); |
| | | buffer.append(scope.toString()); |
| | | buffer.append(", filter="); |
| | | buffer.append(rawFilter.toString()); |
| | | buffer.append(")"); |
| | | } |
| | | } |
| | | |
| opends/src/server/org/opends/server/core/SearchOperationBasis.java
opends/src/server/org/opends/server/core/SearchOperationWrapper.java
opends/src/server/org/opends/server/core/UnbindOperation.java
opends/src/server/org/opends/server/core/Workflow.java
opends/src/server/org/opends/server/core/WorkflowImpl.java
opends/src/server/org/opends/server/core/WorkflowResultCode.java
opends/src/server/org/opends/server/core/WorkflowTopology.java
opends/src/server/org/opends/server/core/WorkflowTopologyNode.java
opends/src/server/org/opends/server/extensions/StaticGroup.java
opends/src/server/org/opends/server/extensions/TraditionalWorkQueue.java
opends/src/server/org/opends/server/extensions/TraditionalWorkerThread.java
opends/src/server/org/opends/server/loggers/TextAuditLogPublisher.java
opends/src/server/org/opends/server/messages/CoreMessages.java
opends/src/server/org/opends/server/protocols/internal/InternalClientConnection.java
opends/src/server/org/opends/server/protocols/internal/InternalSearchOperation.java
opends/src/server/org/opends/server/protocols/jmx/JmxClientConnection.java
opends/src/server/org/opends/server/protocols/jmx/RmiAuthenticator.java
opends/src/server/org/opends/server/protocols/ldap/LDAPClientConnection.java
opends/src/server/org/opends/server/replication/plugin/Historical.java
opends/src/server/org/opends/server/replication/plugin/MultimasterReplication.java
opends/src/server/org/opends/server/replication/plugin/PersistentServerState.java
opends/src/server/org/opends/server/replication/plugin/ReplicationDomain.java
opends/src/server/org/opends/server/replication/protocol/AddMsg.java
opends/src/server/org/opends/server/replication/protocol/DeleteMsg.java
opends/src/server/org/opends/server/replication/protocol/ModifyMsg.java
opends/src/server/org/opends/server/types/AbstractOperation.java
opends/src/server/org/opends/server/types/Operation.java
opends/src/server/org/opends/server/workflowelement/LeafWorkflowElement.java
opends/src/server/org/opends/server/workflowelement/WorkflowElement.java
opends/tests/unit-tests-testng/src/server/org/opends/server/TestCaseUtils.java
opends/tests/unit-tests-testng/src/server/org/opends/server/backends/SchemaBackendTestCase.java
opends/tests/unit-tests-testng/src/server/org/opends/server/backends/jeb/TestBackendImpl.java
opends/tests/unit-tests-testng/src/server/org/opends/server/controls/ServerSideSortControlTestCase.java
opends/tests/unit-tests-testng/src/server/org/opends/server/controls/VLVControlTestCase.java
opends/tests/unit-tests-testng/src/server/org/opends/server/core/AbandonOperationTestCase.java
opends/tests/unit-tests-testng/src/server/org/opends/server/core/AddOperationTestCase.java
opends/tests/unit-tests-testng/src/server/org/opends/server/core/BindOperationTestCase.java
opends/tests/unit-tests-testng/src/server/org/opends/server/core/DeleteOperationTestCase.java
opends/tests/unit-tests-testng/src/server/org/opends/server/core/ModifyOperationTestCase.java
opends/tests/unit-tests-testng/src/server/org/opends/server/core/NetworkGroupTest.java
opends/tests/unit-tests-testng/src/server/org/opends/server/core/SearchOperationTestCase.java
opends/tests/unit-tests-testng/src/server/org/opends/server/core/WorkflowTopologyTest.java
opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/AnonymousSASLMechanismHandlerTestCase.java
opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/AttributeValuePasswordValidatorTestCase.java
opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/CharacterSetPasswordValidatorTestCase.java
opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/DictionaryPasswordValidatorTestCase.java
opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/EntryDNVirtualAttributeProviderTestCase.java
opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/IsMemberOfVirtualAttributeProviderTestCase.java
opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/LengthBasedPasswordValidatorTestCase.java
opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/RepeatedCharactersPasswordValidatorTestCase.java
opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/SimilarityBasedPasswordValidatorTestCase.java
opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/UniqueCharactersPasswordValidatorTestCase.java
opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/internal/InternalClientConnectionTestCase.java
opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/jmx/JmxConnectTest.java
opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/jmx/JmxTestCase.java
opends/tests/unit-tests-testng/src/server/org/opends/server/replication/InitOnLineTest.java
opends/tests/unit-tests-testng/src/server/org/opends/server/replication/ProtocolWindowTest.java
opends/tests/unit-tests-testng/src/server/org/opends/server/replication/ReSyncTest.java
opends/tests/unit-tests-testng/src/server/org/opends/server/replication/ReplicationTestCase.java
opends/tests/unit-tests-testng/src/server/org/opends/server/replication/SchemaReplicationTest.java
opends/tests/unit-tests-testng/src/server/org/opends/server/replication/StressTest.java
opends/tests/unit-tests-testng/src/server/org/opends/server/replication/UpdateOperationTest.java
opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/ModifyConflictTest.java
opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/PersistentServerStateTest.java
opends/tests/unit-tests-testng/src/server/org/opends/server/replication/plugin/PersistentStateTest.java
opends/tests/unit-tests-testng/src/server/org/opends/server/replication/protocol/SynchronizationMsgTest.java
opends/tests/unit-tests-testng/src/server/org/opends/server/replication/server/UpdateComparatorTest.java
opends/tests/unit-tests-testng/src/server/org/opends/server/types/PrivilegeTestCase.java |