/**
  * Verifies that the server will reject a CRAM-MD5 bind in which the first message contains SASL
  * credentials (which isn't allowed).
  *
  * @throws Exception If an unexpected problem occurs.
  */
 @Test()
 public void testOutOfSequenceBind() throws Exception {
   InternalClientConnection conn = new InternalClientConnection(new AuthenticationInfo());
   BindOperation bindOperation =
       conn.processSASLBind(DN.nullDN(), SASL_MECHANISM_CRAM_MD5, ByteString.valueOf("invalid"));
   assertFalse(bindOperation.getResultCode() == ResultCode.SUCCESS);
 }
  /**
   * Verifies that the server will reject a CRAM-MD5 bind with credentials containing a malformed
   * digest.
   *
   * @throws Exception If an unexpected problem occurs.
   */
  @Test()
  public void testMalformedDigest() throws Exception {
    InternalClientConnection conn = new InternalClientConnection(new AuthenticationInfo());
    BindOperation bindOperation = conn.processSASLBind(DN.nullDN(), SASL_MECHANISM_CRAM_MD5, null);
    assertEquals(bindOperation.getResultCode(), ResultCode.SASL_BIND_IN_PROGRESS);

    ByteString creds = ByteString.valueOf("dn:cn=Directory Manager malformeddigest");
    bindOperation = conn.processSASLBind(DN.nullDN(), SASL_MECHANISM_CRAM_MD5, creds);
    assertFalse(bindOperation.getResultCode() == ResultCode.SUCCESS);
  }
  /** {@inheritDoc} */
  @Override
  public void processSASLBind(BindOperation bindOperation) {
    ExternalSASLMechanismHandlerCfg config = currentConfig;
    AttributeType certificateAttributeType = this.certificateAttributeType;
    CertificateValidationPolicy validationPolicy = this.validationPolicy;

    // Get the client connection used for the bind request, and get the
    // security manager for that connection.  If either are null, then fail.
    ClientConnection clientConnection = bindOperation.getClientConnection();
    if (clientConnection == null) {
      bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
      LocalizableMessage message = ERR_SASLEXTERNAL_NO_CLIENT_CONNECTION.get();
      bindOperation.setAuthFailureReason(message);
      return;
    }

    if (!(clientConnection instanceof LDAPClientConnection)) {
      bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
      LocalizableMessage message = ERR_SASLEXTERNAL_NOT_LDAP_CLIENT_INSTANCE.get();
      bindOperation.setAuthFailureReason(message);
      return;
    }
    LDAPClientConnection lc = (LDAPClientConnection) clientConnection;
    Certificate[] clientCertChain = lc.getClientCertificateChain();
    if ((clientCertChain == null) || (clientCertChain.length == 0)) {
      bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
      LocalizableMessage message = ERR_SASLEXTERNAL_NO_CLIENT_CERT.get();
      bindOperation.setAuthFailureReason(message);
      return;
    }

    // Get the certificate mapper to use to map the certificate to a user entry.
    DN certificateMapperDN = config.getCertificateMapperDN();
    CertificateMapper<?> certificateMapper =
        DirectoryServer.getCertificateMapper(certificateMapperDN);

    // Use the Directory Server certificate mapper to map the client certificate
    // chain to a single user DN.
    Entry userEntry;
    try {
      userEntry = certificateMapper.mapCertificateToUser(clientCertChain);
    } catch (DirectoryException de) {
      logger.traceException(de);

      bindOperation.setResponseData(de);
      return;
    }

    // If the user DN is null, then we couldn't establish a mapping and
    // therefore the authentication failed.
    if (userEntry == null) {
      bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);

      LocalizableMessage message = ERR_SASLEXTERNAL_NO_MAPPING.get();
      bindOperation.setAuthFailureReason(message);
      return;
    } else {
      bindOperation.setSASLAuthUserEntry(userEntry);
    }

    // Get the userCertificate attribute from the user's entry for use in the
    // validation process.
    List<Attribute> certAttrList = userEntry.getAttribute(certificateAttributeType);
    switch (validationPolicy) {
      case ALWAYS:
        if (certAttrList == null) {
          if (validationPolicy == CertificateValidationPolicy.ALWAYS) {
            bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);

            LocalizableMessage message = ERR_SASLEXTERNAL_NO_CERT_IN_ENTRY.get(userEntry.getName());
            bindOperation.setAuthFailureReason(message);
            return;
          }
        } else {
          try {
            ByteString certBytes = ByteString.wrap(clientCertChain[0].getEncoded());
            if (!find(certAttrList, certBytes)) {
              bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);

              LocalizableMessage message =
                  ERR_SASLEXTERNAL_PEER_CERT_NOT_FOUND.get(userEntry.getName());
              bindOperation.setAuthFailureReason(message);
              return;
            }
          } catch (Exception e) {
            logger.traceException(e);

            bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);

            LocalizableMessage message =
                ERR_SASLEXTERNAL_CANNOT_VALIDATE_CERT.get(
                    userEntry.getName(), getExceptionMessage(e));
            bindOperation.setAuthFailureReason(message);
            return;
          }
        }
        break;

      case IFPRESENT:
        if (certAttrList != null) {
          try {
            ByteString certBytes = ByteString.wrap(clientCertChain[0].getEncoded());
            if (!find(certAttrList, certBytes)) {
              bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);

              LocalizableMessage message =
                  ERR_SASLEXTERNAL_PEER_CERT_NOT_FOUND.get(userEntry.getName());
              bindOperation.setAuthFailureReason(message);
              return;
            }
          } catch (Exception e) {
            logger.traceException(e);

            bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);

            LocalizableMessage message =
                ERR_SASLEXTERNAL_CANNOT_VALIDATE_CERT.get(
                    userEntry.getName(), getExceptionMessage(e));
            bindOperation.setAuthFailureReason(message);
            return;
          }
        }
    }

    AuthenticationInfo authInfo =
        new AuthenticationInfo(
            userEntry, SASL_MECHANISM_EXTERNAL, DirectoryServer.isRootDN(userEntry.getName()));
    bindOperation.setAuthenticationInfo(authInfo);
    bindOperation.setResultCode(ResultCode.SUCCESS);
  }