UnboundID Server SDK

Ping Identity
UnboundID Server SDK Documentation

InternalOperationAttachmentSASLMechanismHandler.java

/*
 * 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
 *
 *
 *      Copyright 2019 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);
    }
  }



  /**
   * 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.<String>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);
  }
}