/** Clean out state because of a failed authentication attempt */
  private void cleanState() {
    username = null;
    if (password != null) {
      Arrays.fill(password, ' ');
      password = null;
    }
    try {
      if (ctx != null) {
        ctx.close();
      }
    } catch (NamingException e) {
      // ignore
    }
    ctx = null;

    if (clearPass) {
      sharedState.remove(USERNAME_KEY);
      sharedState.remove(PASSWORD_KEY);
    }
  }
  /**
   * Search for the user's entry. Determine the distinguished name of the user's entry and
   * optionally an authorization identity for the user.
   *
   * @param ctx an LDAP context to use for the search
   * @return the user's distinguished name or an empty string if none was found.
   * @exception LoginException if the user's entry cannot be found.
   */
  private String findUserDN(LdapContext ctx) throws LoginException {

    String userDN = "";

    // Locate the user's LDAP entry
    if (userFilter != null) {
      if (debug) {
        System.out.println(
            "\t\t[LdapLoginModule] " + "searching for entry belonging to user: "******"\t\t[LdapLoginModule] " + "cannot search for entry belonging to user: "******"Cannot find user's LDAP entry");
    }

    try {
      NamingEnumeration results =
          ctx.search("", replaceUsernameToken(filterMatcher, userFilter), constraints);

      // Extract the distinguished name of the user's entry
      // (Use the first entry if more than one is returned)
      if (results.hasMore()) {
        SearchResult entry = (SearchResult) results.next();

        // %%% - use the SearchResult.getNameInNamespace method
        //        available in JDK 1.5 and later.
        //        (can remove call to constraints.setReturningObjFlag)
        userDN = ((Context) entry.getObject()).getNameInNamespace();

        if (debug) {
          System.out.println("\t\t[LdapLoginModule] found entry: " + userDN);
        }

        // Extract a value from user's authorization identity attribute
        if (authzIdentityAttr != null) {
          Attribute attr = entry.getAttributes().get(authzIdentityAttr);
          if (attr != null) {
            Object val = attr.get();
            if (val instanceof String) {
              authzIdentity = (String) val;
            }
          }
        }

        results.close();

      } else {
        // Bad username
        if (debug) {
          System.out.println("\t\t[LdapLoginModule] user's entry " + "not found");
        }
      }

    } catch (NamingException e) {
      // ignore
    }

    if (userDN.equals("")) {
      throw (LoginException) new FailedLoginException("Cannot find user's LDAP entry");
    } else {
      return userDN;
    }
  }
  /**
   * Attempt authentication
   *
   * @param getPasswdFromSharedState boolean that tells this method whether to retrieve the password
   *     from the sharedState.
   * @exception LoginException if the authentication attempt fails.
   */
  private void attemptAuthentication(boolean getPasswdFromSharedState) throws LoginException {

    // first get the username and password
    getUsernamePassword(getPasswdFromSharedState);

    if (password == null || password.length == 0) {
      throw (LoginException) new FailedLoginException("No password was supplied");
    }

    String dn = "";

    if (authFirst || authOnly) {

      String id = replaceUsernameToken(identityMatcher, authcIdentity);

      // Prepare to bind using user's username and password
      ldapEnvironment.put(Context.SECURITY_CREDENTIALS, password);
      ldapEnvironment.put(Context.SECURITY_PRINCIPAL, id);

      if (debug) {
        System.out.println(
            "\t\t[LdapLoginModule] " + "attempting to authenticate user: "******"Cannot bind to LDAP server").initCause(e);
      }

      // Authentication has succeeded

      // Locate the user's distinguished name
      if (userFilter != null) {
        dn = findUserDN(ctx);
      } else {
        dn = id;
      }

    } else {

      try {
        // Connect to the LDAP server (using anonymous bind)
        ctx = new InitialLdapContext(ldapEnvironment, null);

      } catch (NamingException e) {
        throw (LoginException)
            new FailedLoginException("Cannot connect to LDAP server").initCause(e);
      }

      // Locate the user's distinguished name
      dn = findUserDN(ctx);

      try {

        // Prepare to bind using user's distinguished name and password
        ctx.addToEnvironment(Context.SECURITY_AUTHENTICATION, "simple");
        ctx.addToEnvironment(Context.SECURITY_PRINCIPAL, dn);
        ctx.addToEnvironment(Context.SECURITY_CREDENTIALS, password);

        if (debug) {
          System.out.println(
              "\t\t[LdapLoginModule] " + "attempting to authenticate user: "******"Cannot bind to LDAP server").initCause(e);
      }
    }

    // Save input as shared state only if authentication succeeded
    if (storePass
        && !sharedState.containsKey(USERNAME_KEY)
        && !sharedState.containsKey(PASSWORD_KEY)) {
      sharedState.put(USERNAME_KEY, username);
      sharedState.put(PASSWORD_KEY, password);
    }

    // Create the user principals
    userPrincipal = new UserPrincipal(username);
    if (authzIdentity != null) {
      authzPrincipal = new UserPrincipal(authzIdentity);
    }

    try {

      ldapPrincipal = new LdapPrincipal(dn);

    } catch (InvalidNameException e) {
      if (debug) {
        System.out.println("\t\t[LdapLoginModule] " + "cannot create LdapPrincipal: bad DN");
      }
      throw (LoginException) new FailedLoginException("Cannot create LdapPrincipal").initCause(e);
    }
  }