Search code examples
javaactive-directoryldapapache-directory

Force password change on next login with Active Directory using Apache LDAP API


We use Active Directory (AD) and when users are added they get a password set and a flag to enforce "User must change password at next logon" which results in an AD attribute pwdLastSet=0

I have a Java application using Apache LDAP API to authenticate but when I am doing that I get error code 49 INVALID_CREDENTIALS and no indication to change password.

How can I with Apache LDAP API detect that user has to change password first?

My simple authenticator:

    public void authenticate(String uid, String password) {
    String status = "";
    try {
        LdapConnectionConfig config = new LdapConnectionConfig();
        config.setUseSsl(true);
        config.setLdapHost("activedirectory.domain.net");
        config.setLdapPort(636);
        config.setTrustManagers(new NoVerificationTrustManager());
        config.setName(_ldapMgmtUser);
        config.setCredentials(_ldapMgmtPassword);

        final DefaultPoolableLdapConnectionFactory factory = new DefaultPoolableLdapConnectionFactory(config);
        final LdapConnectionPool pool = new LdapConnectionPool(factory);
        pool.setTestOnBorrow(true);
        final LdapConnectionTemplate ldapConnectionTemplate = new LdapConnectionTemplate(pool);

        final PasswordWarning warning = ldapConnectionTemplate.authenticate(_rootDn, "(sAMAccountName=" + uid + ")",
                SearchScope.SUBTREE, password.toCharArray());

        status = "User credentials authenticated";
        if (warning != null) {
            status = status + " \n Warning!!" + warning.toString();
        }
        System.out.println(status);
    } catch (final PasswordException e) {
        System.err.println("############# PasswordException #############");
        status = e.toString();
        e.printStackTrace();
    } catch (Exception e) {
        System.err.println("############# Exception #############");
        e.printStackTrace();

    } finally {
    }
    return;
}

Solution

  • I noted that when using LdapConnectionTemplate and authenticate(...) (as above) it doesn't return any useful error codes in the exception and no PasswordWarning. Shouldn't this scenario return a PasswordWarning? https://nightlies.apache.org/directory/api/2.0.1/apidocs/org/apache/directory/ldap/client/template/PasswordWarning.html

    If I use: LdapNetworkConnection and connection.bind(...) it returns an LdapException with message 80090308: LdapErr: DSID-0C090453, comment: AcceptSecurityContext error, data 773, v3839 where 773 is what is expected.

    It feels like LdapConnectionTemplate has a (few) bug(s).

    The full code:

        public void authenticateWithBind(String uid, String password) {
            String status = "";
            LdapConnection connection = null;
            try {
                LdapConnectionConfig config = new LdapConnectionConfig();
                config.setUseSsl(true);
                config.setLdapHost("activedirectory.domain.net");
                config.setLdapPort(636);
                config.setTrustManagers(new NoVerificationTrustManager());
                config.setName(_ldapMgmtUser);
                config.setCredentials(_ldapMgmtPassword);
    
                connection = new LdapNetworkConnection(config);
                connection.bind(_ldapMgmtUser, _ldapMgmtPassword);
    
                EntryCursor cursor = connection.search(_rootDn, "(sAMAccountName=" + uid + ")", SearchScope.SUBTREE, "*");
                
                while (cursor.next()) {
                    Entry entry = cursor.get();
                    System.out.println("DN: " + entry.getDn());
                    System.out.println("Attribute: " + entry.get("pwdLastSet"));
    
                    connection.bind(entry.getDn(), password);
                    status = "User credentials authenticated";
                    System.out.println(status);
                }
            } catch (LdapException e) {
                System.err.println("############# LdapException #############");
                e.printStackTrace();
                System.err.println("Error message: " + e.getMessage());
    
            } catch (Exception e) {
                System.err.println("############# Exception #############");
                e.printStackTrace();
                System.err.println("Error message: " + e.getMessage());
            } finally {
                closeConnection(connection);
            }
            return;
        }