Finally the web security roles are also being associated to the JAAS authenticated user. What I have done is created a JBoss specific LoginModule (MyUserLoginModule.java) which extends the org.jboss.security.auth.spi.UsernamePasswordLoginModule which uses my applications LDAPLoginModule to authenticate the user. If this is successful it associates roles (APPUser, and Public) by invoking a getRoleSets() method which is specific to JBoss. This is the class which has been written is given below
package com.sample.security.authentication;
import java.security.acl.Group;
import java.util.*;
import java.util.HashMap;
import java.util.Map;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.security.auth.Subject;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.Subject;
import java.security.Principal;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.login.FailedLoginException;
import javax.security.auth.login.LoginException;
import javax.security.auth.spi.LoginModule;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jboss.security.SimpleGroup;
import org.jboss.security.SimplePrincipal;
import org.jboss.security.NestableGroup;
import org.jboss.security.auth.spi.UsernamePasswordLoginModule;
import com.sample.security.authentication.LDAPLoginModule;
public class MyUserLoginModule extends UsernamePasswordLoginModule
{
private static Log log = LogFactory.getLog(MyUserLoginModule.class);
private
String username;
private char[] password;
private Principal identity;
private char[] credential;
private String hashAlgorithm = null;
private com.sample.security.LDAPLoginModule lm = null;
private boolean succeeded = false;
private Subject subject;
public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options)
{
super.initialize(subject, callbackHandler, sharedState, options);
this.subject=subject;
lm = new com.sample.security..LDAPLoginModule();
lm.initialize(subject, callbackHandler, sharedState, options);
}
public boolean login() throws LoginException
{
super.loginOk = false;
String[] info = getUsernameAndPassword();
String username = info[0];
String password = info[1];
if( username == null && password == null )
{
identity = unauthenticatedIdentity;
if(log.isDebugEnabled())
{
log.debug("Authenticating as unauthenticatedIdentity="+identity);
}
}
if( identity == null )
{
identity = new SimplePrincipal(username);
if(log.isDebugEnabled())
{
log.debug("Before LDAP login");
}
succeeded = lm.login();
if(log.isDebugEnabled())
{
log.debug("LDAP login status "+succeeded);
}
if( hashAlgorithm != null )
password = createPasswordHash(username, password);
String expectedPassword = getUsersPassword();
}
if( getUseFirstPass() == true )
{
sharedState.put("javax.security.auth.login.name", username);
sharedState.put("javax.security.auth.login.password", credential);
}
super.loginOk = succeeded;
if(log.isDebugEnabled())
{
log.debug("User '" + identity + "' authenticated, loginOk="+loginOk);
}
return super.loginOk;
}
public boolean commit() throws LoginException
{
try{
if(log.isDebugEnabled())
{
log.debug("commit, loginOk="+loginOk);
}
if( loginOk == false )
return false;
if(log.isDebugEnabled())
{
log.debug("before principals");
}
Set principals = subject.getPrincipals();
if(log.isDebugEnabled())
{
log.debug("principals="+principals);
}
if(log.isDebugEnabled())
{
log.debug("identity="+identity);
}
principals.add(identity);
if(log.isDebugEnabled())
{
log.debug("Add identity to principal");
}
Group[] roleSets = getRoleSets();
if(log.isDebugEnabled())
{
log.debug("Got rolesets");
}
for(int g = 0; g < roleSets.length; g ++)
{
Group group = roleSets[g];
if(log.isDebugEnabled())
{
log.debug("Got rolesets = "+group);
}
String name = group.getName();
if(log.isDebugEnabled())
{
log.debug("Got group name = "+name);
}
Group subjectGroup = createGroup(name, principals);
if(log.isDebugEnabled())
{
log.debug("Got subjectGroup = "+subjectGroup);
}
if( subjectGroup instanceof NestableGroup )
{
/* A NestableGroup only allows Groups to be added to it so we
need to add a SimpleGroup to subjectRoles to contain the roles
*/
SimpleGroup tmp = new SimpleGroup("Roles");
if(log.isDebugEnabled())
{
log.debug("Got SimpleGroup = "+tmp);
}
subjectGroup.addMember(tmp);
if(log.isDebugEnabled())
{
log.debug("Adding member to subjectGroup");
}
subjectGroup = tmp;
if(log.isDebugEnabled())
{
log.debug("Got subjectGroup = "+subjectGroup);
}
}
// Copy the group members to the Subject group
Enumeration members = group.members();
while( members.hasMoreElements() )
{
Principal role = (Principal) members.nextElement();
if(log.isDebugEnabled())
{
log.debug("Got Principal = "+role);
}
subjectGroup.addMember(role);
if(log.isDebugEnabled())
{
log.debug("Adding role to subjectGroup");
}
}
}
}
catch(Exception le){
if(log.isDebugEnabled())
{
log.debug("LoginException encountered, "+ le);
}
}
return true;
}
public boolean abort() throws LoginException
{
lm.abort();
if(log.isDebugEnabled())
{
log.debug("abort");
}
return true;
}
public boolean logout() throws LoginException
{
lm.logout();
if(log.isDebugEnabled())
{
log.debug("logout");
}
// Remove the user identity
Set principals = subject.getPrincipals();
principals.remove(identity);
// Remove any added Groups...
return true;
}
protected String getUsersPassword() throws LoginException
{
if (callbackHandler == null)
throw new LoginException(
"Error: no CallbackHandler available "
+ "to gather authentication information from the user");
Callback[] callbacks = new Callback[2];
callbacks[0] = new NameCallback("Dummy Prompt:");
callbacks[1] = new PasswordCallback("Dummy Prompt:", false);
try
{
callbackHandler.handle(callbacks);
username = ((NameCallback) callbacks[0]).getName();
char[] tmpPassword =
((PasswordCallback) callbacks[1]).getPassword();
password = new char[tmpPassword.length];
System.arraycopy(tmpPassword, 0, password, 0, tmpPassword.length);
((PasswordCallback) callbacks[1]).clearPassword();
if(log.isDebugEnabled())
{
log.debug("UserName = "+ username + ", Password = " + String.valueOf(password));
}
return String.valueOf(password);
}
catch (java.io.IOException ioe)
{
throw new LoginException(ioe.toString());
}
catch (UnsupportedCallbackException uce)
{
throw new LoginException(
"Error: "
+ uce.getCallback().toString()
+ " not available to gather authentication information "
+ "from the user");
}
}
protected Group[] getRoleSets() throws LoginException
{
String username = getUsername();
HashMap setsMap = new HashMap();
//Adding Web security roles for now hard coded
String name1 = "APPUser";
String name2 = "Public";
String groupName = "Roles";
Group group = (Group) setsMap.get(groupName);
if( group == null )
{
group = new SimpleGroup(groupName);
setsMap.put(groupName, group);
}
group.addMember(new SimplePrincipal(name1));
if(log.isDebugEnabled())
{
log.debug("Adding Roles"+ name1);
}
group.addMember(new SimplePrincipal(name2));
if(log.isDebugEnabled())
{
log.debug("Adding Roles"+ name2);
}
Group[] roleSets = new Group[setsMap.size()];
setsMap.values().toArray(roleSets);
return roleSets;
}
}
In the jboss-web.xml, I declare the a security domain as below.....
<security-domain>
java:/jaas/MyApps</security-domain>
JBoss finds the appropriate LoginModule in the login-config.xml file. The entry for the same is as below.....
<application-policy name = "MyApps">
<authentication>
<login-module code="com.sample.security.authentication.MyUserLoginModule"
flag="required" />
</authentication>
</application-policy>
With this configuration whenever I am trying to login to the application it shows me the login page defined in the web.xml file as below......
<form-login-page>/jsp/login.jsp</form-login-page>
The above page has to be constructed observing the following.
1. This login.jsp needs to have form elements for accepting user name, and password with names 'j_username', and 'j_password' respectively, and
2. The form action should be '/j_security_check'.
I supply my valid LDAP userid, password. Gets authenticated and gets the relevant web-security roles through my custom LoignModule.
I have a question now. Does the JBoss App Server have an option to specify that all authenticated users be given the web-security roles to access the web resources (which I suppose is checked by the web-container to show the resource - lack of role mappings for the user result in 403- forbidden exception)? In Websphere and Weblogic we have that option. Also is there any way to bypass the login page declared by web.xml (<form-login-page>/jsp/login.jsp</form-login-page>
so that we can use our application specific login functionality and at the same time associate web roles as what I have done.
Thanks for reading this long prose.....
Any help on this is most welcome.
Seji