Custom JAAS login module subject association to the container
dvsridhar May 9, 2013 9:44 AMHi,
We have a legacy financial application that works well with weblogic and websphere containers, currently we are trying to add support for Jboss 7.1. We have our own JAAS based common security framework works perfectly fine with the mentioned vendors (ofcourse with vendor specific JAAS integration API's).
Since this is a legacy JAAS module that's been used across applications, I can't really start from extending Jboss SX AbstractServerLoginModule.
Added our custom login module to jboss domain.xml security domain (other). The JbossCSFBaseLoginModule gets called and Subject gets created however the authenticated subject is not getting associated to the container. I need this to avoid multiple authentications. So I tried couple of way to get the authenticated Subject from the container both didn't work.
Approach 1:
Subject subject=null; | ||
SecurityContext sc = SecurityContextAssociation.getSecurityContext(); | ||
subject=sc.getUtil().getSubject(); |
Approach 2:
Subject subject=null; | |||
try { | |||
subject = (Subject) PolicyContext.getContext("javax.security.auth.Subject.container"); | |||
return subject; | |||
} catch (PolicyContextException e) { | |||
// TODO Auto-generated catch block | |||
e.printStackTrace(); | |||
} |
So, I dig deeper into JBoss SubjectPolicyContextHandler code which essentially gets associated during the bootup.
During the debug I noticed
SubjectInfo si = sc.getSubjectInfo();
if(si != null)
{
Subject activeSubject = si.getAuthenticatedSubject();
RunAsIdentity callerRunAsIdentity = (RunAsIdentity)sc.getIncomingRunAs();
both activeSubject and callerRunAsIdentity are resulting in null value's that's why I was getting the null subject.
My question is, after succefull LoginModule commit, how can I update/create JbossSecurityContext subjectinfo with our authenticated subject that has custom principals?
Do I need do to anything on the following lines to push the authenticated subject in the SubjectInfo.
I tried couple way to do this you will find the same the following java code, both approaches didn't yield positve results.
Approach 1:
if( mboolCommitSucceeded){
// ctx.getSubjectInfo().setRoles(roles)
// ctx.getSubjectInfo().setIdentities(moSubject.getPrincipals());
// JBossSecurityContextUtil securityContextUtil= new JBossSecurityContextUtil(ctx);
// try {
// securityContextUtil.createSubjectInfo(identity, identity, moSubject);
// } catch (Exception e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
Approach 2:
//Associating authenticated subject to the JbossSecurityContext
JBossSecurityContext ctx=(JBossSecurityContext)SecurityContextAssociation.getSecurityContext();
ctx.getSubjectInfo().setAuthenticatedSubject(moSubject);
I have the following the artifacts. Please let me know if I am missing anything.
I am struck here for a while. Any help really appreciated.
Thanks,
SD
JbossCSFBaseLoginModule.java
package com.ams.core.appserver.enterprise;
import java.security.Principal;
import java.security.acl.Group;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import javax.security.auth.Subject;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.login.LoginException;
import org.jboss.security.NestableGroup;
import org.jboss.security.SecurityConstants;
import org.jboss.security.SecurityContextAssociation;
import org.jboss.security.SimpleGroup;
import org.jboss.security.SimplePrincipal;
import org.jboss.security.plugins.JBossSecurityContext;
import com.ams.core.security2.CorePrincipalImpl;
import com.ams.csf.auth.CSFBaseLoginModule;
import com.ams.csf.common.CSFUtil;
public class JbossCSFBaseLoginModule extends CSFBaseLoginModule {
/** The login identity */
private Principal identity;
@Override
public void initialize(Subject foSubject, CallbackHandler callbackHandler, Map sharedState, Map options) {
super.initialize(foSubject, callbackHandler, sharedState, options);
}
@Override
public boolean login() throws LoginException {
super.login();
if(mboolLoginSucceeded){
try {
identity=createIdentity(CSFUtil.getUsername(moSharedState));
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return true;
}
return false;
}
@Override
public boolean abort() throws LoginException {
boolean flag=super.abort();
if(flag){
}
return flag;
}
@Override
public boolean commit() throws LoginException {
super.commit();
if( mboolCommitSucceeded){
Set<Principal> principals = moSubject.getPrincipals();
Principal identity = getIdentity();
principals.add(identity);
// add the CallerPrincipal group
Group callerGroup = getCallerPrincipalGroup(principals);
if (callerGroup == null)
{
callerGroup = new SimpleGroup(SecurityConstants.CALLER_PRINCIPAL_GROUP);
callerGroup.addMember(identity);
principals.add(callerGroup);
}
// add other role groups
Group[] roleSets = getRoleSets();
for(int g = 0; g < roleSets.length; g ++)
{
Group group = roleSets[g];
String name = group.getName();
Group subjectGroup = createGroup(name, principals);
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");
subjectGroup.addMember(tmp);
subjectGroup = tmp;
}
// Copy the group members to the Subject group
Enumeration<? extends Principal> members = group.members();
while( members.hasMoreElements() )
{
Principal role = (Principal) members.nextElement();
subjectGroup.addMember(role);
}
}
// ctx.getSubjectInfo().setRoles(roles)
// ctx.getSubjectInfo().setIdentities(moSubject.getPrincipals());
// JBossSecurityContextUtil securityContextUtil= new JBossSecurityContextUtil(ctx);
// try {
// securityContextUtil.createSubjectInfo(identity, identity, moSubject);
// } catch (Exception e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
//
//Associating authenticated subject to the JbossSecurityContext
JBossSecurityContext ctx=(JBossSecurityContext)SecurityContextAssociation.getSecurityContext();
ctx.getSubjectInfo().setAuthenticatedSubject(moSubject);
//Not sure if we need this to make the association work
// RoleGroup roleGroup= new RoleGroup();
// ctx.getSubjectInfo().setRoles(roles);
return true;
}
return false;
}
private Group[] getRoleSets() {
Group[] roleSets = {new SimpleGroup("Roles")};
roleSets[0].addMember(new SimplePrincipal("user"));
roleSets[0].addMember(new SimplePrincipal("guest"));
return roleSets;
}
private Principal getIdentity() {
// TODO Auto-generated method stub
return identity;
}
@Override
public boolean logout() throws LoginException {
boolean flag=super.logout();
Principal identity = getIdentity();
Set<Principal> principals = moSubject.getPrincipals();
principals.remove(identity);
Group callerGroup = getCallerPrincipalGroup(principals);
if (callerGroup != null)
principals.remove(callerGroup);
return flag;
}
protected Group createGroup(String name, Set<Principal> principals)
{
Group roles = null;
Iterator<Principal> iter = principals.iterator();
while( iter.hasNext() )
{
Object next = iter.next();
if( (next instanceof Group) == false )
continue;
Group grp = (Group) next;
if( grp.getName().equals(name) )
{
roles = grp;
break;
}
}
// If we did not find a group create one
if( roles == null )
{
roles = new SimpleGroup(name);
principals.add(roles);
}
return roles;
}
/** Utility method to create a Principal for the given username. This
* creates an instance of the principalClassName type if this option was
* specified using the class constructor matching: ctor(String). If
* principalClassName was not specified, a SimplePrincipal is created.
*
* @param username the name of the principal
* @return the principal instance
* @throws java.lang.Exception thrown if the custom principal type cannot be created.
*/
@SuppressWarnings("unchecked")
protected Principal createIdentity(String username)
throws Exception
{
CorePrincipalImpl p= new CorePrincipalImpl();
p.setName(username);
return p;
}
protected Group getCallerPrincipalGroup(Set<Principal> principals)
{
Group callerGroup = null;
for (Principal principal : principals)
{
if (principal instanceof Group)
{
Group group = Group.class.cast(principal);
if (group.getName().equals(SecurityConstants.CALLER_PRINCIPAL_GROUP))
{
callerGroup = group;
break;
}
}
}
return callerGroup;
}
}
domain.xml
-------------------------
<subsystem xmlns="urn:jboss:domain:security:1.1">
<security-domains>
<security-domain name="other" cache-type="default">
<security-domain name="other" cache-type="default">
<authentication>
<login-module code="com.ams.core.appserver.enterprise.JbossCSFBaseLoginModule" flag="sufficient"/>
<login-module code="org.jboss.security.ClientLoginModule" flag="required">
<module-option name="password-stacking" value="useFirstPass"/>
</login-module>
</authentication>
</security-domain>
...........
jboss-web.xml
<jboss-web>
<security-domain>java:/jaas/other</security-domain>
<security-role id="fdx">
<role-name>*</role-name>
</security-role>
</jboss-web>
web.xml has the following.. security related info..
<security-role> | |||
<description /> | |||
<role-name>fdx</role-name> | |||
</security-role> | |||
<security-role> | |||
<description /> | |||
<role-name>fdx-admin</role-name> | |||
</security-role> | |||
<security-role> | |||
<description /> | |||
<role-name>fdx-user</role-name> | |||
</security-role> | |||
<resource-ref id="ResourceRef_fdx_security"> | |||
<res-ref-name>fdx.security</res-ref-name> | |||
<res-type>javax.sql.DataSource</res-type> | |||
<res-auth>Container</res-auth> | |||
<res-sharing-scope>Shareable</res-sharing-scope> | |||
</resource-ref> | |||
<security-constraint> | |||
<web-resource-collection> | |||
<web-resource-name>restricted methods</web-resource-name> | |||
<url-pattern>/*</url-pattern> | |||
<http-method>TRACE</http-method> | |||
<http-method>OPTIONS</http-method> | |||
<http-method>DELETE</http-method> | |||
</web-resource-collection> | |||
<auth-constraint> | |||
<role-name>fdx-admin</role-name> | |||
<role-name>fdx-user</role-name> | |||
<role-name>fdx</role-name> | |||
</auth-constraint> | |||
</security-constraint> |