/*
* 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
* docs/licenses/cddl.txt
* or http://www.opensource.org/licenses/cddl1.php.
* 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
* docs/licenses/cddl.txt. If applicable,
* add the following below this CDDL HEADER, with the fields enclosed
* by brackets "[]" replaced with your own identifying information:
* Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*
*
* Portions Copyright 2019-2024 Ping Identity Corporation
*/
package com.unboundid.directory.sdk.examples;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import com.unboundid.asn1.ASN1OctetString;
import com.unboundid.directory.sdk.common.operation.SASLBindRequest;
import com.unboundid.directory.sdk.common.operation.UpdatableSASLBindRequest;
import com.unboundid.directory.sdk.common.operation.UpdatableSimpleBindRequest;
import com.unboundid.directory.sdk.common.types.LogSeverity;
import com.unboundid.directory.sdk.common.types.OperationContext;
import com.unboundid.directory.sdk.common.types.ServerContext;
import com.unboundid.directory.sdk.ds.api.SASLMechanismHandler;
import com.unboundid.directory.sdk.ds.config.SASLMechanismHandlerConfig;
import com.unboundid.directory.sdk.ds.types.DirectoryServerContext;
import com.unboundid.directory.sdk.ds.types.SASLBindResult;
import com.unboundid.directory.sdk.ds.types.SASLBindResultFactory;
import com.unboundid.ldap.sdk.Control;
import com.unboundid.ldap.sdk.DN;
import com.unboundid.ldap.sdk.LDAPException;
import com.unboundid.ldap.sdk.ResultCode;
import com.unboundid.util.ByteString;
import com.unboundid.util.StaticUtils;
import com.unboundid.util.Validator;
import com.unboundid.util.args.ArgumentParser;
/**
* This class provides a Server SDK SASL mechanism handler implementation that
* uses attachments on the associated bind operation to specify the behavior
* that the server should exhibit when processing the bind operation. This is
* only intended to be used internally within the server and should not be
* directly requested by an external client. It is primarily expected to be
* used in conjunction with a pre-parse bind plugin that performs all of the
* authentication processing itself, but then converts the bind request to this
* mechanism to use the information provided in attachments to define the result
* of the operation processing.
* <BR><BR>
* This SASL mechanism handler actually implements support for four related SASL
* mechanisms:
* <BR>
* <UL>
* <LI>
* PING-SECURE-PASSWORD-BASED-INTERNAL-OPERATION-ATTACHMENT: Indicates
* that the authentication used a password provided by the client, and that
* the password and any other associated credentials were provided in a
* secure manner (that is, in a manner that is not vulnerable to
* eavesdropping, replay, or alteration attacks).
* </LI>
* <LI>
* PING-NON-SECURE-PASSWORD-BASED-INTERNAL-OPERATION-ATTACHMENT: Indicates
* that the authentication used a password provided by the client, and that
* the password or any other associated credentials were provided in a
* non-secure manner (that is, in a manner that may be vulnerable to
* eavesdropping, replay, or alteration attacks).
* </LI>
* <LI>
* PING-SECURE-NON-PASSWORD-BASED-INTERNAL-OPERATION-ATTACHMENT: Indicates
* that the authentication did not use a password provided by the client,
* and that any credentials used were provided in a secure manner (that is,
* in a manner that is not vulnerable to eavesdropping, replay, or
* alteration attacks).
* </LI>
* <LI>
* PING-NON-SECURE-NON-PASSWORD-BASED-INTERNAL-OPERATION-ATTACHMENT:
* Indicates that the authentication did not use a password provided by the
* client, but that it did use credentials that were provided in a
* non-secure manner (that is, in a manner that may be vulnerable to
* eavesdropping, replay, or alteration attacks).
* </LI>
* </UL>
* <BR><BR>
* All four SASL mechanisms implemented by this handler implement support for
* the same set of operation attachments. Supported attachments include:
* <BR>
* <UL>
* <LI>
* {@code internal-op-sasl-handler-authentication-succeeded} -- indicates
* whether the authentication attempt was successful. This attachment is
* required, and its value must be a {@code java.lang.Boolean}.
* </LI>
* <LI>
* {@code internal-op-sasl-handler-diagnostic-message} -- a diagnostic
* message that should be included in the bind response to the client. This
* attachment is optional and may be included regardless of whether the
* authentication attempt succeeded or failed. If it is provided, then its
* value must be an instance of {@code java.lang.CharSequence}.
* </LI>
* <LI>
* {@code internal-op-sasl-handler-matched-dn} -- the matched DN value that
* should be included in the bind response to the client to indicate that a
* provided bind DN did not refer to an entry that exists in the server, and
* to provide the DN of its closest ancestor that does exist. This
* attachment is optional and should only be used if the authentication
* attempt failed. If it is provided, then its value must be an instance of
* {@code com.unboundid.ldap.sdk.DN}.
* </LI>
* <LI>
* {@code internal-op-sasl-handler-server-sasl-credentials} -- an encoded
* set of server SASL credentials that should be included in the bind
* response to the client. This attachment is optional and should only be
* set if the original bind request was itself a SASL bind request and
* server SASL credentials are appropriate for that request. If provided,
* the value of the attachment must be an instance of
* {@code com.unboundid.asn1.ASN1OctetString}.
* </LI>
* <LI>
* {@code internal-op-sasl-handler-response-controls} -- a set of controls
* that should be included in the bind response to the client. This
* attachment is optional, and if it is not set, then the SASL mechanism
* handler will not add any controls to the response (although other
* components of the server may add response controls if appropriate for the
* operation). If provided, the value of the attachment must be an instance
* of {@code com.unboundid.ldap.sdk.Control[]}.
* </LI>
* <LI>
* {@code internal-op-sasl-handler-bind-dn} -- the DN of the user
* attempting to authenticate. This attachment must be set if the
* authentication is successful, and it will be set as the authentication
* identity for the associated connection (unless the bind request also
* included the retain identity request control). Even if the bind attempt
* failed, this attachment should be set if it is possible to determine the
* identity of the user that was trying to authenticate so that any
* appropriate password policy state updates can be applied to that account.
* If provided, the value of the attachment must be an instance of
* {@code com.unboundid.ldap.sdk.DN}.
* </LI>
* <LI>
* {@code internal-op-sasl-handler-authentication-failure-reason} -- a
* message that will not appear in the bind response to the client, but will
* be included in the server's access log to provide additional information
* about the reason that the authentication attempt failed. This attachment
* is optional and should only be used if the authentication attempt failed.
* If it is provided, then its value must be an instance of
* {@code java.lang.CharSequence}.
* </LI>
* <LI>
* {@code internal-op-sasl-handler-authorization-dn} -- the DN of the user
* that is the alternate authorization identity for the bind operation.
* This should only be set if the bind attempt succeeded and requested an
* alternate authorization identity. If provided, the value of the
* attachment must be an instance of {@code com.unboundid.ldap.sdk.DN}.
* </LI>
* <LI>
* {@code internal-op-sasl-handler-password-used} -- the password that the
* client provided during the authentication attempt. This attachment is
* optional and should only be used if the authentication attempt was
* successful and used a password. If provided, the value of the attachment
* must be an instance of {@code com.unboundid.asn1.ASN1OctetString}.
* </LI>
* </UL>
*/
public final class InternalOperationAttachmentSASLMechanismHandler
extends SASLMechanismHandler
{
/**
* The name for the SASL mechanism that indicates that the authentication used
* a password provided by the client, and that the password and any other
* associated credentials were provided in a secure manner (that is, in a
* manner that is not vulnerable to eavesdropping, replay, or alteration
* attacks).
*/
public static final String SECURE_PASSWORD_BASED_SASL_MECHANISM_NAME =
"PING-SECURE-PASSWORD-BASED-INTERNAL-OPERATION-ATTACHMENT";
/**
* The name for the SASL mechanism that indicates that the authentication used
* a password provided by the client, and that the password or any other
* associated credentials were provided in a non-secure manner (that is, in a
* manner that may be vulnerable to eavesdropping, replay, or alteration
* attacks).
*/
public static final String NON_SECURE_PASSWORD_BASED_SASL_MECHANISM_NAME =
"PING-NON-SECURE-PASSWORD-BASED-INTERNAL-OPERATION-ATTACHMENT";
/**
* The name for the SASL mechanism that indicates that the authentication did
* not use a password provided by the client, and that any credentials used
* were provided in a secure manner (that is, in a manner that is not
* vulnerable to eavesdropping, replay, or alteration attacks).
*/
public static final String SECURE_NON_PASSWORD_BASED_SASL_MECHANISM_NAME =
"PING-SECURE-NON-PASSWORD-BASED-INTERNAL-OPERATION-ATTACHMENT";
/**
* The name for the SASL mechanism that indicates that the authentication did
* not use a password provided by the client, but that it did use credentials
* that were provided in a non-secure manner (that is, in a manner that may be
* vulnerable to eavesdropping, replay, or alteration attacks).
*/
public static final String NON_SECURE_NON_PASSWORD_BASED_SASL_MECHANISM_NAME =
"PING-NON_SECURE-NON-PASSWORD-BASED-INTERNAL-OPERATION-ATTACHMENT";
/**
* The name of the attachment used to indicate whether the authentication
* attempt was successful. This attachment is required, and its value must be
* a {@code java.lang.Boolean}.
*/
public static final String ATTACHMENT_NAME_AUTHENTICATION_SUCCEEDED =
"internal-op-sasl-handler-authentication-succeeded";
/**
* The name of the attachment used to hold a diagnostic message that should be
* included in the bind response to the client. This attachment is optional
* and may be included regardless of whether the authentication attempt
* succeeded or failed. If it is provided, then its value must be an instance
* of {@code java.lang.CharSequence}.
*/
public static final String ATTACHMENT_NAME_DIAGNOSTIC_MESSAGE =
"internal-op-sasl-handler-diagnostic-message";
/**
* The name of the attachment used to hold the matched DN value that should be
* included in the bind response to the client to indicate that a provided
* bind DN did not refer to an entry that exists in the server, and to provide
* the DN of its closest ancestor that does exist. This attachment is
* optional and should only be used if the authentication attempt failed. If
* it is provided, then its value must be an instance of
* {@code com.unboundid.ldap.sdk.DN}.
*/
public static final String ATTACHMENT_NAME_MATCHED_DN =
"internal-op-sasl-handler-matched-dn";
/**
* The name of the attachment used to hold an encoded set of server SASL
* credentials that should be included in the bind response to the client.
* This attachment is optional and should only be set if the original bind
* request was itself a SASL bind request and server SASL credentials are
* appropriate for that request. If provided, the value of the attachment
* must be an instance of {@code com.unboundid.asn1.ASN1OctetString}.
*/
public static final String ATTACHMENT_NAME_SERVER_SASL_CREDENTIALS =
"internal-op-sasl-handler-server-sasl-credentials";
/**
* The name of the attachment used to hold a set of controls that should be
* included in the bind response to the client. This attachment is optional,
* and if it is not set, then the SASL mechanism handler will not add any
* controls to the response (although other components of the server may add
* response controls if appropriate for the operation). If provided, the
* value of the attachment must be an instance of
* {@code com.unboundid.ldap.sdk.Control[]}.
*/
public static final String ATTACHMENT_NAME_RESPONSE_CONTROLS =
"internal-op-sasl-handler-response-controls";
/**
* The name of the attachment used to hold the DN of the user attempting to
* authenticate. This attachment must be set if the authentication is
* successful, and it will be set as the authentication identity for the
* associated connection (unless the bind request also included the retain
* identity request control). Even if the bind attempt failed, this
* attachment should be set if it is possible to determine the identity of the
* user that was trying to authenticate so that any appropriate password
* policy state updates can be applied to that account. If provided, the
* value of the attachment must be an instance of
* {@code com.unboundid.ldap.sdk.DN}.
*/
public static final String ATTACHMENT_NAME_BIND_DN =
"internal-op-sasl-handler-bind-dn";
/**
* The name of the attachment used to hold a message that will not appear in
* the bind response to the client, but will be included in the server's
* access log to provide additional information about the reason that the
* authentication attempt failed. This attachment is optional and should only
* be used if the authentication attempt failed. If it is provided, then its
* value must be an instance of {@code java.lang.CharSequence}.
*/
public static final String ATTACHMENT_NAME_AUTHENTICATION_FAILURE_REASON =
"internal-op-sasl-handler-authentication-failure-reason";
/**
* The name of the attachment used to hold the DN of the user that is the
* alternate authorization identity for the bind operation. This should only
* be set if the bind attempt succeeded and requested an alternate
* authorization identity. If provided, the value of the attachment must be
* an instance of {@code com.unboundid.ldap.sdk.DN}.
*/
public static final String ATTACHMENT_NAME_AUTHORIZATION_DN =
"internal-op-sasl-handler-authorization-dn";
/**
* The name of the attachment used to hold the password that the client
* provided during the authentication attempt. This attachment is optional
* and should only be used if the authentication attempt was successful and
* used a password. If provided, the value of the attachment must be an
* instance of {@code com.unboundid.asn1.ASN1OctetString}.
*/
public static final String ATTACHMENT_NAME_PASSWORD_USED =
"internal-op-sasl-handler-password-used";
// A handle to the server context in which this extension is running.
private volatile DirectoryServerContext serverContext = null;
/**
* Creates a new instance of this SASL mechanism handler. All SASL
* mechanism handler implementations must include a default constructor, but
* any initialization
* should generally be done in the {@code initializeSASLMechanismHandler}
* method.
*/
public InternalOperationAttachmentSASLMechanismHandler()
{
// No implementation is required.
}
/**
* Retrieves a human-readable name for this extension.
*
* @return A human-readable name for this extension.
*/
@Override()
public String getExtensionName()
{
return "Internal Operation Attachment SASL Mechanism Handler";
}
/**
* Retrieves a human-readable description for this extension. Each element
* of the array that is returned will be considered a separate paragraph in
* generated documentation.
*
* @return A human-readable description for this extension, or {@code null}
* or an empty array if no description should be available.
*/
@Override()
public String[] getExtensionDescription()
{
return new String[]
{
"Implements support for a number of related SASL mechanisms that use " +
"operation attachments to define the behavior the server should " +
"exhibit when processing the bind operation. This is only " +
"intended to be used internally within the server and should not " +
"be directly requested by an external client. It is primarily " +
"expected to be used in conjunction with a pre-parse bind plugin " +
"that performs all of the authentication processing itself, but " +
"then converts the bind request to this mechanism to use the " +
"information provided in attachments to define the result of the " +
"operation processing.",
"The " + SECURE_PASSWORD_BASED_SASL_MECHANISM_NAME + " mechanism " +
"should be used if the authentication used a password provided by " +
"the client, and that the password and any other associated " +
"credentials were provided in a secure manner (that is, in a " +
"manner that is not vulnerable to eavesdropping, replay, or " +
"alteration attacks).",
"The " + NON_SECURE_PASSWORD_BASED_SASL_MECHANISM_NAME + " mechanism " +
"should be used if the authentication used a password provided by " +
"the client, and that the password or any other associated " +
"credentials were provided in a non-secure manner (that is, in a " +
"manner that may be vulnerable to eavesdropping, replay, or " +
"alteration attacks).",
"The " + SECURE_NON_PASSWORD_BASED_SASL_MECHANISM_NAME + " mechanism " +
"should be used if the authentication did not use a password " +
"provided by the client, and that any credentials used were " +
"provided in a secure manner (that is, in a manner that is not " +
"vulnerable to eavesdropping, replay, or alteration attacks).",
"The " + NON_SECURE_NON_PASSWORD_BASED_SASL_MECHANISM_NAME +
" mechanism should be used if the authentication did not use a " +
"password provided by the client, but that it did use credentials " +
"that were provided in a non-secure manner (that is, in a manner " +
"that may be vulnerable to eavesdropping, replay, or alteration " +
"attacks).",
"All four SASL mechanisms implemented by this handler implement " +
"support for the same set of operation attachments. Supported " +
"attachments include:",
"internal-op-sasl-handler-authentication-succeeded -- indicates " +
"whether the authentication attempt was successful. This " +
"attachment is required, and its value must be an instance of " +
Boolean.class.getName() + '.',
"internal-op-sasl-handler-diagnostic-message -- a diagnostic message " +
"that should be included in the bind response to the client. " +
"This attachment is optional and may be included regardless of " +
"whether the authentication attempt succeeded or failed. If it " +
"is provided, then its value must be an instance of " +
CharSequence.class.getName() + '.',
"internal-op-sasl-handler-matched-dn -- the matched DN value that " +
"should be included in the bind response to the client to " +
"indicate that a provided bind DN did not refer to an entry that " +
"exists in the server, and to provide the DN of its closest " +
"ancestor that does exist. This attachment is optional and " +
"should only be used if the authentication attempt failed. If it " +
"is provided, then its value must be an instance of " +
DN.class.getName() + '.',
"internal-op-sasl-handler-server-sasl-credentials -- an encoded set " +
"of server SASL credentials that should be included in the bind " +
"response to the client. This attachment is optional and should " +
"only be set if the original bind request was itself a SASL bind " +
"request and server SASL credentials are appropriate for that " +
"request. If provided, the value of the attachment must be an " +
"instance of " + ASN1OctetString.class.getName() + '.',
"internal-op-sasl-handler-response-controls -- a set of controls that " +
"should be included in the bind response to the client. This " +
"attachment is optional, and if it is not set, then the SASL " +
"mechanism handler will not add any controls to the response " +
"(although other components of the server may add response " +
"controls if appropriate for the operation). If provided, the " +
"value of the attachment must be an instance of " +
Control.class.getName() + "[].",
"internal-op-sasl-handler-bind-dn -- the DN of the user attempting to " +
"authenticate. This attachment must be set if the " +
"authentication is successful, and it will be set as the " +
"authentication identity for the associated connection (unless " +
"the bind request also included the retain identity request " +
"control). Even if the bind attempt failed, this attachment " +
"should be set if it is possible to determine the identity of the " +
"user that was trying to authenticate so that any appropriate " +
"password policy state updates can be applied to that account. " +
"If provided, the value of the attachment must be an instance of " +
DN.class.getName() + '.',
"internal-op-sasl-handler-authentication-failure-reason -- a message " +
"that will not appear in the bind response to the client, but " +
"will be included in the server's access log to provide " +
"additional information about the reason that the authentication " +
"attempt failed. This attachment is optional and should only be " +
"used if the authentication attempt failed. If it is provided, " +
"then its value must be an instance of " +
CharSequence.class.getName() + '.',
"internal-op-sasl-handler-authorization-dn -- the DN of the user that " +
"is the alternate authorization identity for the bind operation. " +
"This should only be set if the bind attempt succeeded and " +
"requested an alternate authorization identity. If provided, the " +
"value of the attachment must be an instance of " +
DN.class.getName() + '.',
"internal-op-sasl-handler-password-used -- the password that the " +
"client provided during the authentication attempt. This " +
"attachment is optional and should only be used if the " +
"authentication attempt was successful and used a password. If " +
"provided, the value of the attachment must be an instance of " +
ASN1OctetString.class.getName() + '.',
};
}
/**
* Updates the provided argument parser to define any configuration arguments
* which may be used by this extension. The argument parser may also be
* updated to define relationships between arguments (e.g., to specify
* required, exclusive, or dependent argument sets).
*
* @param parser The argument parser to be updated with the configuration
* arguments which may be used by this extension.
*/
@Override()
public void defineConfigArguments(final ArgumentParser parser)
{
// No arguments will be allowed by default.
}
/**
* Initializes this SASL mechanism handler.
*
* @param serverContext A handle to the server context for the server in
* which this extension is running.
* @param config The general configuration for this SASL mechanism
* handler.
* @param parser The argument parser which has been initialized from
* the configuration for this SASL mechanism handler.
*/
@Override()
public void initializeSASLMechanismHandler(
final DirectoryServerContext serverContext,
final SASLMechanismHandlerConfig config,
final ArgumentParser parser)
{
if (this.serverContext == null)
{
this.serverContext = serverContext;
}
}
/**
* Indicates whether the configuration represented by the provided argument
* parser is acceptable for use by this extension. The parser will have been
* used to parse any configuration available for this extension, and any
* automatic validation will have been performed. This method may be used to
* perform any more complex validation which cannot be performed automatically
* by the argument parser.
*
* @param config The general configuration for this extension.
* @param parser The argument parser that has been used to
* parse the proposed configuration for this
* extension.
* @param unacceptableReasons A list to which messages may be added to
* provide additional information about why the
* provided configuration is not acceptable.
*
* @return {@code true} if the configuration in the provided argument parser
* appears to be acceptable, or {@code false} if not.
*/
@Override()
public boolean isConfigurationAcceptable(
final SASLMechanismHandlerConfig config,
final ArgumentParser parser,
final List<String> unacceptableReasons)
{
if (serverContext == null)
{
serverContext = config.getServerContext();
}
// No validation is needed.
return true;
}
/**
* Attempts to apply the configuration from the provided argument parser to
* this extension.
*
* @param config The general configuration for this extension.
* @param parser The argument parser that has been used to
* parse the new configuration for this
* extension.
* @param adminActionsRequired A list to which messages may be added to
* provide additional information about any
* additional administrative actions that may
* be required to apply some of the
* configuration changes.
* @param messages A list to which messages may be added to
* provide additional information about the
* processing performed by this method.
*
* @return A result code providing information about the result of applying
* the configuration change. A result of {@code SUCCESS} should be
* used to indicate that all processing completed successfully. Any
* other result will indicate that a problem occurred during
* processing.
*/
@Override()
public ResultCode applyConfiguration(final SASLMechanismHandlerConfig config,
final ArgumentParser parser,
final List<String> adminActionsRequired,
final List<String> messages)
{
if (serverContext == null)
{
serverContext = config.getServerContext();
}
// There is no configuration to apply.
return ResultCode.SUCCESS;
}
/**
* Performs any cleanup which may be necessary when this SASL mechanism
* handler is to be taken out of service.
*/
@Override()
public void finalizeSASLMechanismHandler()
{
// No finalization is needed.
}
/**
* Retrieves a list of the names of the SASL mechanisms supported by this
* SASL mechanism handler. This method will be invoked only immediately after
* the {@link #initializeSASLMechanismHandler} method is called.
*
* @return A list of the names of the SASL mechanisms supported by this SASL
* mechanism handler.
*/
@Override()
public List<String> getSASLMechanismNames()
{
return Collections.unmodifiableList(Arrays.asList(
SECURE_PASSWORD_BASED_SASL_MECHANISM_NAME,
NON_SECURE_PASSWORD_BASED_SASL_MECHANISM_NAME,
SECURE_NON_PASSWORD_BASED_SASL_MECHANISM_NAME,
NON_SECURE_NON_PASSWORD_BASED_SASL_MECHANISM_NAME));
}
/**
* Indicates whether the SASL authentication process using the specified
* mechanism may be considered secure (i.e., that a third party able to
* observe the communication, potentially over an insecure communication
* channel, would not be able to reproduce the authentication process).
*
* @param mechanism The name of the mechanism for which to make the
* determination. This will only be invoked with names of
* mechanisms returned by the
* {@link #getSASLMechanismNames} method.
*
* @return {@code true} if the specified SASL mechanism should be considered
* secure, or {@code false} if not.
*/
@Override()
public boolean isSecure(final String mechanism)
{
switch (mechanism)
{
case SECURE_PASSWORD_BASED_SASL_MECHANISM_NAME:
case SECURE_NON_PASSWORD_BASED_SASL_MECHANISM_NAME:
// These mechanisms are considered secure.
return true;
case NON_SECURE_PASSWORD_BASED_SASL_MECHANISM_NAME:
case NON_SECURE_NON_PASSWORD_BASED_SASL_MECHANISM_NAME:
// These mechanisms are considered non-secure.
return false;
default:
// This should never happen. Throw a RuntimeException to ensure that
// this is more visible than other types of non-successful operations.
final String message = getClass().getName() +
".isSecure called with an unsupported SASL mechanism of '" +
mechanism + "'.";
if (serverContext != null)
{
serverContext.logMessage(LogSeverity.SEVERE_WARNING, message);
}
throw new RuntimeException(message);
}
}
/**
* Indicates whether the SASL authentication process using the specified
* mechanism involves the use of a password stored locally in the server
* (optionally in combination with other forms of credentials).
*
* @param mechanism The name of the mechanism for which to make the
* determination. This will only be invoked with names of
* mechanisms returned by the
* {@link #getSASLMechanismNames} method.
*
* @return {@code true} if the specified SASL mechanism makes use of a local
* password, or {@code false} if not.
*/
@Override()
public boolean isPasswordBased(final String mechanism)
{
switch (mechanism)
{
case SECURE_PASSWORD_BASED_SASL_MECHANISM_NAME:
case NON_SECURE_PASSWORD_BASED_SASL_MECHANISM_NAME:
// These mechanisms are password-based.
return true;
case SECURE_NON_PASSWORD_BASED_SASL_MECHANISM_NAME:
case NON_SECURE_NON_PASSWORD_BASED_SASL_MECHANISM_NAME:
// These mechanisms are not password-based.
return false;
default:
// This should never happen. Throw a RuntimeException to ensure that
// this is more visible than other types of non-successful operations.
final String message = getClass().getName() +
".isPasswordBased called with an unsupported SASL mechanism " +
"of '" + mechanism + "'.";
if (serverContext != null)
{
serverContext.logMessage(LogSeverity.SEVERE_WARNING, message);
}
throw new RuntimeException(message);
}
}
/**
* Performs the appropriate processing for the provided SASL bind request.
*
* @param operationContext The context for the bind operation.
* @param bindRequest The SASL bind request to be processed.
* @param resultFactory A factory object that will be used to construct
* the result to return.
*
* @return An object with information about the result of the SASL bind
* processing.
*/
@Override()
public SASLBindResult processSASLBind(final OperationContext operationContext,
final SASLBindRequest bindRequest,
final SASLBindResultFactory resultFactory)
{
final String mechanism = bindRequest.getSASLMechanism();
boolean authenticationSucceeded;
final Object authenticationSucceededObject = operationContext.getAttachment(
ATTACHMENT_NAME_AUTHENTICATION_SUCCEEDED);
if (authenticationSucceededObject == null)
{
authenticationSucceeded = false;
operationContext.appendAdditionalLogMessage("Indicating that the " +
"authentication was unsuccessful because the operation was " +
"missing the " + ATTACHMENT_NAME_AUTHENTICATION_SUCCEEDED +
" attachment required for use with the " + mechanism +
" mechanism.");
}
else if (authenticationSucceededObject instanceof Boolean)
{
authenticationSucceeded =
((Boolean) authenticationSucceededObject).booleanValue();
}
else
{
authenticationSucceeded = false;
operationContext.appendAdditionalLogMessage("Indicating that the " +
"authentication was unsuccessful because the " +
ATTACHMENT_NAME_AUTHENTICATION_SUCCEEDED +
" attachment was an instance of " +
authenticationSucceededObject.getClass().getName() +
" instead of the required " + Boolean.class.getName() + '.');
}
final String diagnosticMessage;
final Object diagnosticMessageObject =
operationContext.getAttachment(ATTACHMENT_NAME_DIAGNOSTIC_MESSAGE);
if (diagnosticMessageObject == null)
{
diagnosticMessage = null;
}
else if (diagnosticMessageObject instanceof CharSequence)
{
diagnosticMessage = String.valueOf(diagnosticMessageObject);
}
else
{
diagnosticMessage = null;
operationContext.appendAdditionalLogMessage("Ignoring the value of the " +
ATTACHMENT_NAME_DIAGNOSTIC_MESSAGE +
" attachment because it was an instance of " +
diagnosticMessageObject.getClass().getName() +
" instead of the required " + CharSequence.class.getName() + '.');
}
final String matchedDN;
final Object matchedDNObject =
operationContext.getAttachment(ATTACHMENT_NAME_MATCHED_DN);
if (matchedDNObject == null)
{
matchedDN = null;
}
else if (matchedDNObject instanceof DN)
{
matchedDN = ((DN) matchedDNObject).toString();
}
else
{
matchedDN = null;
operationContext.appendAdditionalLogMessage("Ignoring the value of the " +
ATTACHMENT_NAME_MATCHED_DN +
" attachment because it was an instance of " +
matchedDNObject.getClass().getName() +
" instead of the required " + DN.class.getName() + '.');
}
final ASN1OctetString serverSASLCredentials;
final Object serverSASLCredentialsObject = operationContext.getAttachment(
ATTACHMENT_NAME_SERVER_SASL_CREDENTIALS);
if (serverSASLCredentialsObject == null)
{
serverSASLCredentials = null;
}
else if (serverSASLCredentialsObject instanceof ASN1OctetString)
{
serverSASLCredentials = (ASN1OctetString) serverSASLCredentialsObject;
}
else
{
serverSASLCredentials = null;
operationContext.appendAdditionalLogMessage("Ignoring the value of the " +
ATTACHMENT_NAME_SERVER_SASL_CREDENTIALS +
" attachment because it was an instance of " +
serverSASLCredentialsObject.getClass().getName() +
" instead of the required " + ASN1OctetString.class.getName() + '.');
}
final List<Control> responseControls;
final Object responseControlsObject =
operationContext.getAttachment(ATTACHMENT_NAME_RESPONSE_CONTROLS);
if (responseControlsObject == null)
{
responseControls = null;
}
else if (responseControlsObject instanceof Control[])
{
final Control[] responseControlArray = (Control[]) responseControlsObject;
responseControls =
Collections.unmodifiableList(Arrays.asList(responseControlArray));
}
else
{
responseControls = null;
operationContext.appendAdditionalLogMessage("Ignoring the value of the " +
ATTACHMENT_NAME_RESPONSE_CONTROLS +
" attachment because it was an instance of " +
responseControlsObject.getClass().getName() +
" instead of the required " + Control.class.getName() + "[].");
}
String authenticationFailureReason;
final Object authenticationFailureReasonObject =
operationContext.getAttachment(
ATTACHMENT_NAME_AUTHENTICATION_FAILURE_REASON);
if (authenticationFailureReasonObject == null)
{
authenticationFailureReason = null;
}
else if (authenticationFailureReasonObject instanceof CharSequence)
{
authenticationFailureReason =
String.valueOf(authenticationFailureReasonObject);
}
else
{
authenticationFailureReason = null;
operationContext.appendAdditionalLogMessage("Ignoring the value of the " +
ATTACHMENT_NAME_AUTHENTICATION_FAILURE_REASON +
" attachment because it was an instance of " +
authenticationFailureReasonObject.getClass().getName() +
" instead of the required " + CharSequence.class.getName() + '.');
}
final String bindDN;
final Object bindDNObject =
operationContext.getAttachment(ATTACHMENT_NAME_BIND_DN);
if (bindDNObject == null)
{
if (authenticationSucceeded)
{
bindDN = null;
authenticationSucceeded = false;
authenticationFailureReason = "The " + mechanism +
" bind operation did not have the " +
ATTACHMENT_NAME_BIND_DN + " attachment set, which is required " +
"for a successful authentication.";
}
else
{
bindDN = null;
}
}
else if (bindDNObject instanceof DN)
{
bindDN = ((DN) bindDNObject).toString();
}
else
{
bindDN = null;
if (authenticationSucceeded)
{
authenticationSucceeded = false;
authenticationFailureReason = "The " + ATTACHMENT_NAME_BIND_DN +
" attachment was set in the " + mechanism +
" bind operation, but its value was of type " +
bindDNObject.getClass().getName() + " instead of the required " +
"type of " + DN.class.getName() + '.';
}
else
{
operationContext.appendAdditionalLogMessage("Ignoring the value of " +
"the " + ATTACHMENT_NAME_BIND_DN +
" attachment because it was an instance of " +
bindDNObject.getClass().getName() +
" instead of the required " + DN.class.getName() + '.');
}
}
final String authorizationDN;
final Object authorizationDNObject =
operationContext.getAttachment(ATTACHMENT_NAME_AUTHORIZATION_DN);
if (authorizationDNObject == null)
{
authorizationDN = bindDN;
}
else if (authorizationDNObject instanceof DN)
{
authorizationDN = ((DN) authorizationDNObject).toString();
}
else
{
authorizationDN = null;
if (authenticationSucceeded)
{
authenticationSucceeded = false;
authenticationFailureReason = "The " +
ATTACHMENT_NAME_AUTHORIZATION_DN + " attachment was set in the " +
mechanism + " bind operation, but its value was of type " +
bindDNObject.getClass().getName() + " instead of the required " +
"type of " + DN.class.getName() + '.';
}
else
{
operationContext.appendAdditionalLogMessage("Ignoring the value of " +
"the " + ATTACHMENT_NAME_AUTHORIZATION_DN +
" attachment because it was an instance of " +
authorizationDNObject.getClass().getName() +
" instead of the required " + DN.class.getName() + '.');
}
}
final ASN1OctetString passwordUsed;
final Object passwordUsedObject =
operationContext.getAttachment(ATTACHMENT_NAME_PASSWORD_USED);
if (passwordUsedObject == null)
{
passwordUsed = null;
}
else if (passwordUsedObject instanceof ASN1OctetString)
{
passwordUsed = (ASN1OctetString) passwordUsedObject;
}
else
{
passwordUsed = null;
operationContext.appendAdditionalLogMessage("Ignoring the value of the " +
ATTACHMENT_NAME_PASSWORD_USED +
" attachment because it was an instance of " +
passwordUsedObject.getClass().getName() +
" instead of the required " + ASN1OctetString.class.getName() + '.');
}
if (authenticationSucceeded)
{
return resultFactory.createSuccessResult(bindDN, authorizationDN,
diagnosticMessage, responseControls, serverSASLCredentials,
passwordUsed);
}
else
{
return resultFactory.createFailureResult(authenticationFailureReason,
diagnosticMessage, matchedDN, responseControls,
serverSASLCredentials, bindDN);
}
}
/**
* Indicates whether the specified SASL mechanism may require multiple stages
* to process.
*
* @param mechanism The mechanism for which to make the determination.
*
* @return {@code true} if the specified SASL mechanism may require multiple
* stages to process, {@code false} if not, or {@code null} if the
* answer is not known for the specified mechanism.
*/
@Override()
public Boolean isMultiStageMechanism(final String mechanism)
{
switch (mechanism)
{
case SECURE_PASSWORD_BASED_SASL_MECHANISM_NAME:
case NON_SECURE_PASSWORD_BASED_SASL_MECHANISM_NAME:
case SECURE_NON_PASSWORD_BASED_SASL_MECHANISM_NAME:
case NON_SECURE_NON_PASSWORD_BASED_SASL_MECHANISM_NAME:
// None of these mechanisms involve multiple stages of authentication
// processing.
return false;
default:
// This should never happen. Throw a RuntimeException to ensure that
// this is more visible than other types of non-successful operations.
final String message = getClass().getName() +
".isMultiStageMechanism called with an unsupported SASL " +
"mechanism of '" + mechanism + "'.";
if (serverContext != null)
{
serverContext.logMessage(LogSeverity.SEVERE_WARNING, message);
}
throw new RuntimeException(message);
}
}
/**
* Retrieves a map containing examples of configurations that may be used for
* this extension. The map key should be a list of sample arguments, and the
* corresponding value should be a description of the behavior that will be
* exhibited by the extension when used with that configuration.
*
* @return A map containing examples of configurations that may be used for
* this extension. It may be {@code null} or empty if there should
* not be any example argument sets.
*/
@Override()
public Map<List<String>,String> getExamplesArgumentSets()
{
return Collections.singletonMap(
Collections.emptyList(),
"This SASL mechanism handler does not take any arguments.");
}
/**
* Converts the provided simple bind request to an
* {@code UpdatableSASLBindRequest} object that can be used to cause this SASL
* mechanism handler to process a successful authentication as the specified
* user. All of the appropriate attachments for this SASL mechanism handler
* will be set on the associated operation. It will use the following
* settings:
* <UL>
* <LI>
* The authentication attempt will be considered successful.
* </LI>
* <LI>
* There will not be any diagnostic message, matched DN, response
* controls, or server SASL credentials.
* </LI>
* <LI>
* The bind DN from the simple bind request will be used as both the
* authentication and authorization identity for the SASL bind.
* </LI>
* <LI>
* The password used will be the password from the simple bind request.
* </LI>
* <LI>
* The SASL bind will be considered password-based.
* </LI>
* <LI>
* The SASL bind will be considered secure if and only if the simple bind
* request was received over a secure connection.
* </LI>
* </UL>
*
* @param simpleBindRequest The simple bind request to use to create the
* corresponding SASL bind request. It must not
* be {@code null}.
* @param operationContext The operation context with which the provided
* simple bind request is associated. It must not
* be {@code null}.
*
* @return The {@code UpdatableSASLBindRequest} object created from the
* provided {@code UpdatableSimpleBindRequest} object.
*
* @throws LDAPException If a problem is encountered while trying to create
* the SASL bind request.
*/
public static UpdatableSASLBindRequest convertToSuccessfulSASLBindRequest(
final UpdatableSimpleBindRequest simpleBindRequest,
final OperationContext operationContext)
throws LDAPException
{
Validator.ensureNotNullWithMessage(simpleBindRequest,
InternalOperationAttachmentSASLMechanismHandler.class.getName() +
".createSuccessfulSASLBindRequest.simpleBindRequest must not " +
"be null.");
Validator.ensureNotNullWithMessage(operationContext,
InternalOperationAttachmentSASLMechanismHandler.class.getName() +
".createSuccessfulSASLBindRequest.operationContext must not be " +
"null.");
final String diagnosticMessage = null;
final List<Control> responseControls = Collections.emptyList();
return convertToSuccessfulSASLBindRequest(simpleBindRequest,
operationContext, diagnosticMessage, responseControls);
}
/**
* Converts the provided simple bind request to an
* {@code UpdatableSASLBindRequest} object that can be used to cause this SASL
* mechanism handler to process a successful authentication as the specified
* user. All of the appropriate attachments for this SASL mechanism handler
* will be set on the associated operation. It will use the following
* settings:
* <UL>
* <LI>
* The authentication attempt will be considered successful.
* </LI>
* <LI>
* There will not be any matched DN or server SASL credentials.
* </LI>
* <LI>
* The bind DN from the simple bind request will be used as both the
* authentication and authorization identity for the SASL bind.
* </LI>
* <LI>
* The password used will be the password from the simple bind request.
* </LI>
* <LI>
* The SASL bind will be considered password-based.
* </LI>
* <LI>
* The SASL bind will be considered secure if and only if the simple bind
* request was received over a secure connection.
* </LI>
* </UL>
*
* @param simpleBindRequest The simple bind request to use to create the
* corresponding SASL bind request. It must not
* be {@code null}.
* @param operationContext The operation context with which the provided
* simple bind request is associated. It must not
* be {@code null}.
* @param diagnosticMessage A diagnostic message to include in the response
* to the client. It may be {@code null} if no
* diagnostic message should be returned.
* @param responseControls A set of controls to include in the response to
* the client. It may be {@code null} or empty if
* no response controls should be returned.
*
* @return The {@code UpdatableSASLBindRequest} object created from the
* provided information.
*
* @throws LDAPException If a problem is encountered while trying to create
* the SASL bind request.
*/
public static UpdatableSASLBindRequest convertToSuccessfulSASLBindRequest(
final UpdatableSimpleBindRequest simpleBindRequest,
final OperationContext operationContext,
final String diagnosticMessage,
final List<Control> responseControls)
throws LDAPException
{
Validator.ensureNotNullWithMessage(simpleBindRequest,
InternalOperationAttachmentSASLMechanismHandler.class.getName() +
".createSuccessfulSASLBindRequest.simpleBindRequest must not " +
"be null.");
Validator.ensureNotNullWithMessage(operationContext,
InternalOperationAttachmentSASLMechanismHandler.class.getName() +
".createSuccessfulSASLBindRequest.operationContext must not be " +
"null.");
final ServerContext serverContext = operationContext.getServerContext();
final boolean isPasswordBased = true;
final boolean isSecure = operationContext.isSecure();
final DN authenticationDN;
try
{
authenticationDN = new DN(simpleBindRequest.getDN());
}
catch (final LDAPException e)
{
serverContext.debugCaught(e);
throw new LDAPException(ResultCode.INVALID_DN_SYNTAX,
"Unable to parse the bind DN from the provided simple bind " +
"request: " + StaticUtils.getExceptionMessage(e),
e);
}
final DN authorizationDN = authenticationDN;
final ByteString passwordUsed = simpleBindRequest.getPassword();
return convertToSuccessfulSASLBindRequest(simpleBindRequest,
operationContext, isPasswordBased, isSecure, authenticationDN,
authorizationDN, diagnosticMessage, responseControls, passwordUsed);
}
/**
* Converts the provided simple bind request to an
* {@code UpdatableSASLBindRequest} object that can be used to cause this SASL
* mechanism handler to process a successful authentication using the provided
* information. All of the appropriate attachments for this SASL mechanism
* will be set on the associated operation.
*
* @param simpleBindRequest The simple bind request to use to create the
* corresponding SASL bind request. It must not
* be {@code null}.
* @param operationContext The operation context with which the provided
* simple bind request is associated. It must not
* be {@code null}.
* @param isPasswordBased Indicates whether the resulting SASL bind should
* be considered password based.
* @param isSecure Indicates whether the resulting SASL bind should
* be considered secure.
* @param authenticationDN The DN of the authentication identity for the
* resulting SASL bind operation. It must not be
* {@code null}, but may be {@code DN.NULL_DN} to
* indicate an anonymous authentication identity.
* @param authorizationDN The DN of the authorization identity for the
* resulting SASL bind operation. It must not be
* {@code null}, but may be {@code DN.NULL_DN} to
* indicate an anonymous authentication identity.
* In most cases, this will be the same as the
* authentication DN, but they may differ if
* operations should be processed under the
* authority of a different user than the
* authentication identity.
* @param diagnosticMessage A diagnostic message to include in the response
* to the client. It may be {@code null} if no
* diagnostic message should be returned.
* @param responseControls A set of controls to include in the response to
* the client. It may be {@code null} or empty if
* no response controls should be returned.
* @param passwordUsed The password used to authenticate the client.
* It may be {@code null} if the authentication
* was not password-based, or if the password used
* is not known.
*
* @return The {@code UpdatableSASLBindRequest} object created from the
* provided information.
*/
public static UpdatableSASLBindRequest convertToSuccessfulSASLBindRequest(
final UpdatableSimpleBindRequest simpleBindRequest,
final OperationContext operationContext,
final boolean isPasswordBased, final boolean isSecure,
final DN authenticationDN, final DN authorizationDN,
final String diagnosticMessage,
final List<Control> responseControls,
final ByteString passwordUsed)
{
Validator.ensureNotNullWithMessage(simpleBindRequest,
InternalOperationAttachmentSASLMechanismHandler.class.getName() +
".createSuccessfulSASLBindRequest.simpleBindRequest must not " +
"be null.");
Validator.ensureNotNullWithMessage(operationContext,
InternalOperationAttachmentSASLMechanismHandler.class.getName() +
".createSuccessfulSASLBindRequest.operationContext must not be " +
"null.");
Validator.ensureNotNullWithMessage(authenticationDN,
InternalOperationAttachmentSASLMechanismHandler.class.getName() +
".createSuccessfulSASLBindRequest.authenticationDN must not be " +
"null.");
Validator.ensureNotNullWithMessage(authorizationDN,
InternalOperationAttachmentSASLMechanismHandler.class.getName() +
".createSuccessfulSASLBindRequest.authorizationDN must not be " +
"null.");
final String saslMechanism;
if (isPasswordBased)
{
if (isSecure)
{
saslMechanism = SECURE_PASSWORD_BASED_SASL_MECHANISM_NAME;
}
else
{
saslMechanism = NON_SECURE_PASSWORD_BASED_SASL_MECHANISM_NAME;
}
}
else
{
if (isSecure)
{
saslMechanism = SECURE_NON_PASSWORD_BASED_SASL_MECHANISM_NAME;
}
else
{
saslMechanism = NON_SECURE_NON_PASSWORD_BASED_SASL_MECHANISM_NAME;
}
}
operationContext.setAttachment(ATTACHMENT_NAME_AUTHENTICATION_SUCCEEDED,
Boolean.TRUE);
operationContext.setAttachment(ATTACHMENT_NAME_BIND_DN, authenticationDN);
if (! authenticationDN.equals(authorizationDN))
{
operationContext.setAttachment(ATTACHMENT_NAME_AUTHORIZATION_DN,
authorizationDN);
}
if (diagnosticMessage != null)
{
operationContext.setAttachment(ATTACHMENT_NAME_DIAGNOSTIC_MESSAGE,
diagnosticMessage);
}
if ((responseControls != null) && (! responseControls.isEmpty()))
{
final Control[] responseControlArray =
new Control[responseControls.size()];
responseControls.toArray(responseControlArray);
operationContext.setAttachment(ATTACHMENT_NAME_RESPONSE_CONTROLS,
responseControlArray);
}
if (passwordUsed != null)
{
operationContext.setAttachment(ATTACHMENT_NAME_PASSWORD_USED,
passwordUsed);
}
return simpleBindRequest.convertToSASLBindRequest(saslMechanism, null);
}
/**
* Converts the provided simple bind request to an
* {@code UpdatableSASLBindRequest} object that can be used to cause this SASL
* mechanism handler to process a failed authentication attempt . All of the
* appropriate attachments for this SASL mechanism handler will be set on the
* associated operation. It will use the following settings:
* <UL>
* <LI>
* The authentication attempt will be considered a failure.
* </LI>
* <LI>
* There will not be any diagnostic message, authentication failure
* reason, matched DN, response controls, or server SASL credentials.
* </LI>
* <LI>
* The bind DN from the simple bind request will be used as both the
* target user DN for the SASL bind.
* </LI>
* <LI>
* The SASL bind will be considered password-based.
* </LI>
* <LI>
* The SASL bind will be considered secure if and only if the simple bind
* request was received over a secure connection.
* </LI>
* </UL>
*
* @param simpleBindRequest The simple bind request to use to create the
* corresponding SASL bind request. It must not
* be {@code null}.
* @param operationContext The operation context with which the provided
* simple bind request is associated. It must not
* be {@code null}.
*
* @return The {@code UpdatableSASLBindRequest} object created from the
* provided information.
*
* @throws LDAPException If a problem is encountered while trying to create
* the SASL bind request.
*/
public static UpdatableSASLBindRequest convertToFailedSASLBindRequest(
final UpdatableSimpleBindRequest simpleBindRequest,
final OperationContext operationContext)
throws LDAPException
{
Validator.ensureNotNullWithMessage(simpleBindRequest,
InternalOperationAttachmentSASLMechanismHandler.class.getName() +
".createFailedSASLBindRequest.simpleBindRequest must not " +
"be null.");
Validator.ensureNotNullWithMessage(operationContext,
InternalOperationAttachmentSASLMechanismHandler.class.getName() +
".createFailedSASLBindRequest.operationContext must not be " +
"null.");
final boolean isPasswordBased = true;
final boolean isSecure = operationContext.isSecure();
final String diagnosticMessage = null;
final String authenticationFailureReason = null;
final DN matchedDN = null;
final DN targetUserDN = null;
final List<Control> responseControls = Collections.emptyList();
return convertToFailedSASLBindRequest(simpleBindRequest, operationContext,
isPasswordBased, isSecure, diagnosticMessage,
authenticationFailureReason, matchedDN, targetUserDN,
responseControls);
}
/**
* Converts the provided simple bind request to an
* {@code UpdatableSASLBindRequest} object that can be used to cause this SASL
* mechanism handler to process a failed authentication attempt using the
* provided information. All of the appropriate attachments for this SASL
* mechanism will be set on the associated operation.
*
* @param simpleBindRequest The simple bind request to use to
* create the corresponding SASL bind
* request. It must not be {@code null}.
* @param operationContext The operation context with which the
* provided simple bind request is
* associated. It must not be
* {@code null}.
* @param isPasswordBased Indicates whether the resulting SASL
* bind should be considered password
* based.
* @param isSecure Indicates whether the resulting SASL
* bind should be considered secure.
* @param diagnosticMessage A diagnostic message to include in the
* response to the client. It may be
* {@code null} if no diagnostic message
* should be returned.
* @param authenticationFailureReason A message that describes the reason
* for the authentication failure. This
* message will not be returned to the
* client, but will be included in the
* access log message for the operation
* so that administrators may better
* diagnose the failure. It may be
* {@code null} if no authentication
* failure reason is available.
* @param matchedDN The matched DN to include in the
* response to the client. It may be
* {@code null} if no matched DN should
* be included.
* @param targetUserDN The DN of the user that attempted to
* authenticate. It may be {@code null}
* if the target user DN is not known.
* @param responseControls A set of controls to include in the
* response to the client. It may be
* {@code null} or empty if no response
* controls should be returned.
*
* @return The {@code UpdatableSASLBindRequest} object created from the
* provided information.
*/
public static UpdatableSASLBindRequest convertToFailedSASLBindRequest(
final UpdatableSimpleBindRequest simpleBindRequest,
final OperationContext operationContext,
final boolean isPasswordBased, final boolean isSecure,
final String diagnosticMessage,
final String authenticationFailureReason,
final DN matchedDN, final DN targetUserDN,
final List<Control> responseControls)
{
Validator.ensureNotNullWithMessage(simpleBindRequest,
InternalOperationAttachmentSASLMechanismHandler.class.getName() +
".createFailedSASLBindRequest.simpleBindRequest must not " +
"be null.");
Validator.ensureNotNullWithMessage(operationContext,
InternalOperationAttachmentSASLMechanismHandler.class.getName() +
".createFailedSASLBindRequest.operationContext must not be " +
"null.");
final String saslMechanism;
if (isPasswordBased)
{
if (isSecure)
{
saslMechanism = SECURE_PASSWORD_BASED_SASL_MECHANISM_NAME;
}
else
{
saslMechanism = NON_SECURE_PASSWORD_BASED_SASL_MECHANISM_NAME;
}
}
else
{
if (isSecure)
{
saslMechanism = SECURE_NON_PASSWORD_BASED_SASL_MECHANISM_NAME;
}
else
{
saslMechanism = NON_SECURE_NON_PASSWORD_BASED_SASL_MECHANISM_NAME;
}
}
operationContext.setAttachment(ATTACHMENT_NAME_AUTHENTICATION_SUCCEEDED,
Boolean.FALSE);
if (diagnosticMessage != null)
{
operationContext.setAttachment(ATTACHMENT_NAME_DIAGNOSTIC_MESSAGE,
diagnosticMessage);
}
if (authenticationFailureReason != null)
{
operationContext.setAttachment(
ATTACHMENT_NAME_AUTHENTICATION_FAILURE_REASON,
authenticationFailureReason);
}
if (matchedDN != null)
{
operationContext.setAttachment(ATTACHMENT_NAME_MATCHED_DN, matchedDN);
}
if (targetUserDN != null)
{
operationContext.setAttachment(ATTACHMENT_NAME_BIND_DN, targetUserDN);
}
if ((responseControls != null) && (! responseControls.isEmpty()))
{
final Control[] responseControlArray =
new Control[responseControls.size()];
responseControls.toArray(responseControlArray);
operationContext.setAttachment(ATTACHMENT_NAME_RESPONSE_CONTROLS,
responseControlArray);
}
return simpleBindRequest.convertToSASLBindRequest(saslMechanism, null);
}
}