Monday, November 10, 2008

UserNameToken authentication based on Active Directory

This post explains how you can authenticate users at the service end against an Active Directory[AD] - in the CallbackHandler.

This assumes that you know how to deploy a service and configure it to accept a UserNameToken for authentication.[If not please refer this.]

If you are curious to know more on CallbackHandlers - then this may help.

To get started, follow the first part in my previous post - that is to configiure AD with the user tomcat.

Now let's get started with password CallbackHandler at the service end.

package org.apache.ws.axis2;

import java.io.IOException;

import org.apache.ws.security.WSPasswordCallback;

import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;

public class PWCBHandler implements CallbackHandler {

private static String SECURITY_PRINCIPAL = "cn=tomcat,cn=users,dc=home,dc=com";
private static String SECURITY_CREDENTIALS = "1qaz2wsx@";
private static String PROVIDER_URL = "ldap://192.168.1.2:389";
private static String USER_PATTERN = "cn={0},cn=Users,dc=home,dc=com";

public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {

ADAuthenticator authenticator = null;

authenticator = new ADAuthenticator(SECURITY_PRINCIPAL, SECURITY_CREDENTIALS, PROVIDER_URL,USER_PATTERN);

for (int i = 0; i < callbacks.length; i++) {

// When the server side need to authenticate the user
WSPasswordCallback pwcb = (WSPasswordCallback) callbacks[i];

// Usage value is set to USERNAME_TOKEN_UNKNOWN when the Rampart
// Engine wants the password callback handler to validate the
// username and password in the Username Token
if (pwcb.getUsage() == WSPasswordCallback.USERNAME_TOKEN_UNKNOWN) {
if (authenticator.authenticate(pwcb.getIdentifer(), pwcb.getPassword())) {
return;
} else {
throw new UnsupportedCallbackException(callbacks[i], "check failed");
}
}
}
}
}
Now lets look at the ADAuthenticator - which actually does the authentication against the AD - user base.

package org.apache.ws.axis2;

import java.text.MessageFormat;
import java.util.Hashtable;

import javax.naming.Context;
import javax.naming.NamingException;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;

public class ADAuthenticator {

/**
* Holds the name of the environment property for specifying the initial
* context factory to use. The value of the property should be the fully
* qualified class name of the factory class that will create an initial
* context.
*/
private static String INITIAL_CONTEXT_FACTORY = "com.sun.jndi.ldap.LdapCtxFactory";

private DirContext context = null;
private String userPattern = null;

/**
*
* @param conName
* Holds the name of the environment property for specifying the
* identity of the principal for authenticating the caller to the
* service.
* @param conPassword
* Holds the name of the environment property for specifying the
* credentials of the principal for authenticating the caller to
* the service.
* @param connectionUrl
* Holds the name of the environment property for specifying
* configuration information for the service provider to use.
* @param userPattern
* Search pattern.
*/
public ADAuthenticator(String conName, String conPassword, String connectionUrl,
String userPattern) {

Hashtable environment = null;

try {
environment = new Hashtable();
environment.put(Context.INITIAL_CONTEXT_FACTORY, INITIAL_CONTEXT_FACTORY);
environment.put(Context.SECURITY_PRINCIPAL, conName);
environment.put(Context.SECURITY_CREDENTIALS, conPassword);
environment.put(Context.PROVIDER_URL, connectionUrl);
this.userPattern = userPattern;

context = new InitialDirContext(environment);
} catch (NamingException ex) {
throw new RuntimeException();
}
}

/**
* Authenticates a given user against Active Directory user store.
*
* @param userName
* User to be authenticated.
* @param password
* Password of the user.
* @return true if authenticated.
*/
public boolean authenticate(String userName, String password) {

String dn = null;

try {
dn = MessageFormat.format(userPattern, new String[] { userName });
return this.bindAsUser(this.context, dn, (String) password);
} catch (NamingException ex) {
return false;
}

}

private boolean bindAsUser(DirContext context, String dn, String credentials)
throws NamingException {

if (credentials == null || dn == null)
return false;

context.addToEnvironment(Context.SECURITY_PRINCIPAL, dn);
context.addToEnvironment(Context.SECURITY_CREDENTIALS, credentials);

context.getAttributes("", null);

return true;
}

}

1 comments:

5-star IT books said...

Hi Prabath,

Does the client needs to send its ldap credentials for each invokation of a method.
Does your example include any session management technique?
thanks.