Custom login Module problems & issues
ragavgomatam Aug 27, 2004 1:17 PMHi all,
I am quite exasperated with trying to do a customized database login module jboss jaas module.
I tried with the jboss Databaselogin Module & they work fine against the principalQuery and the Roles query with example data base schema.
My environment is as follows :-
jdk 1.4.2
jboss 3.2.5
OS - Windows XP
I have an ejb that I am trying to run from a stand alone java client...It works against the jboss Database login module, but fails when I go against my schema.....
The exception is :-
==============================================
java.lang.SecurityException: Insufficient method permissions, principal=df, method=create, interface=HOME, requiredRoles=[DF_Operations], principalRoles=null
at org.jboss.ejb.plugins.SecurityInterceptor.checkSecurityAssociation(SecurityInterceptor.java:229)
===============================================
My ejb-jar.xml is as follows :-
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE ejb-jar PUBLIC "-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN" "http://java.sun.com/j2ee/dtds/ejb-jar_2_0.dtd">
<ejb-jar>
<enterprise-beans>
<session>
<ejb-name>PortfolioManager</ejb-name>
<home>com.standardandpoors.apps.defaultfilter.portfolio.PortfolioManagerHome</home>
<remote>com.standardandpoors.apps.defaultfilter.portfolio.PortfolioManager</remote>
<ejb-class>com.standardandpoors.apps.defaultfilter.portfolio.PortfolioManagerBean</ejb-class>
<session-type>Stateless</session-type>
<transaction-type>Container</transaction-type>
</session>
</enterprise-beans>
<assembly-descriptor>
<security-role>
<role-name>df</role-name>
</security-role>
<security-role>
<role-name>DF_QuickSearch</role-name>
</security-role>
<security-role>
<role-name>DF_AdvancedSearch</role-name>
</security-role>
<security-role>
<role-name>DF_Scenarios</role-name>
</security-role>
<security-role>
<role-name>DF_SavedWork</role-name>
</security-role>
<security-role>
<role-name>DF_DataCollection</role-name>
</security-role>
<security-role>
<role-name>DF_Operations</role-name>
</security-role>
<security-role>
<role-name>DF_RunReport</role-name>
</security-role>
<security-role>
<role-name>DF_RunCreditvar</role-name>
</security-role>
<security-role>
<role-name>DF_Views</role-name>
</security-role>
<method-permission>
<role-name>DF_Operations</role-name>
<method>
<ejb-name>PortfolioManager</ejb-name>
<method-name>*</method-name>
</method>
</method-permission>
<container-transaction>
<method>
<ejb-name>PortfolioManager</ejb-name>
<method-name>*</method-name>
</method>
<trans-attribute>Required</trans-attribute>
</container-transaction>
</assembly-descriptor>
</ejb-jar>
===============================================
my jboss.xml is as follows :-
<jboss>
<security-domain>java:/jaas/dfJaasModule</security-domain>
<unauthenticated-principal>nobody</unauthenticated-principal>
<enterprise-beans>
<session>
<ejb-name>PortfolioManager</ejb-name>
<jndi-name>ejb/PortfolioManagerBean</jndi-name>
</session>
</enterprise-beans>
</jboss>
===============================================
My auth.conf is as follows :-
srp-client {
org.jboss.security.srp.jaas.SRPLoginModule required
password-stacking="useFirstPass"
principalClassName="org.jboss.security.SimplePrincipal"
srpServerJndiName="SRPServerInterface"
debug=true
;
org.jboss.security.ClientLoginModule required
password-stacking="useFirstPass"
;
};
other {
org.jboss.security.ClientLoginModule required
;
};
client-login {
org.jboss.security.ClientLoginModule required
;
};
dfJaasModule {
com.mycompany.security.login.DFLoginModule required
;
};
dbTest {
org.jboss.security.auth.spi.DatabaseServerLoginModule required
unauthenticatedIdentity="nobody"
dsJndiName="java:/jdbc/myDB"
principalsQuery="select Password from Principals where PrincipalID=?"
rolesQuery="select Role, RoleGroup from Roles where PrincipalID=?"
;
};
ejbProps {
org.jboss.security.auth.spi.UsersRolesLoginModule required
usersProperties=users.properties
rolesProperties=roles.properties
unauthenticatedIdentity="nobody"
;
};
===============================================
My login module is as follows :-
package com.mycompany.security.login;
import java.util.Set;
import java.util.Iterator;
import java.util.Properties;
import java.io.IOException;
import java.util.Map;
import java.util.HashMap;
import java.util.Collection;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.security.Principal;
import java.security.acl.Group;
import javax.sql.DataSource;
import javax.naming.InitialContext;
import javax.naming.Context;
import javax.naming.NamingException;
import javax.security.auth.Subject;
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.jboss.logging.Logger;
import org.jboss.security.SimpleGroup;
import org.jboss.security.SimplePrincipal;
import org.jboss.security.auth.spi.AbstractServerLoginModule;
public class DFLoginModule extends AbstractServerLoginModule {
protected Subject subject = null;
protected CallbackHandler callbackHandler = null;
protected Map sharedState=null;
protected Map options=null;
protected boolean loginOK = false;
private Principal userPrincipal = null;
private final int NOT = 0;
private final int OK = 1;
private final int COMMIT = 2;
private final String dsJndiName ="java:/jdbc/Sp/df/spcrm/RSKDEV";
private DataSource datasource;
private static int status;
private int clientId;
private String name = null;
private String password = null;
//hard coded for temp testing
private String[] rolesArray = {"DF_QuickSearch", "DF_AdvancedSearch", "DF_Scenarios",
"DF_SavedWork","DF_DataCollection","DF_Operations",
"DF_RunReport", "DF_RunCreditvar","DF_Views"};
//==============SQL FOR AUTHENTICATION====================
private final String authenticationQuery =
"select p.user_id, u.org_id, p.start_date, p.end_date " +
" from subscriber_products p, subscriber_users u " +
" where p.user_code = ? " +
" and p.user_password = ? " +
" and p.user_id = u.user_id";
private static final Logger log = Logger.getLogger(DFLoginModule.class);
/**
* This is the initilaize(). Does nothing specfic
* other than calling the super class method and setting the
* instance variables. You may do some thing extra but so far
* nothing is needed here except perhaps initializing the datasource.
*/
public void initialize(Subject subject, CallbackHandler callbackHandler,
Map sharedState, Map options) {
super.initialize(subject,callbackHandler,sharedState,options);
this.subject = subject;
this.callbackHandler = callbackHandler;
this.sharedState = sharedState;
this.options = options;
status = NOT;
datasource = this.getDataSource(this.dsJndiName);
log.debug("initialized! caller = "+callbackHandler.getClass().getName());
log.debug("subject = "+subject.getClass().getName());
}
/**
* This is the login() of this module that is
* invoked by the LoginContext.login(). It is here that we
* check the user id and password of the user and authenticate
* him/her. In this case we check to see if the user id & passwords
* are valid also if a user is within a specified start and end date.
* Here we set a jboss specific variable called loginOK(booelan) and we
* return this.This is a requirement for jboss AbstractServer login Module
*
* @returns boolean
* @throws LoginException
*
*/
public boolean login() throws LoginException {
Connection dbConn = null;
PreparedStatement pstmt = null;
if(callbackHandler == null) {
throw new LoginException("No callback handler is available");
}
System.out.println("=====entering login=====");
Callback callbacks[] = new Callback[2];
callbacks[0] = new NameCallback(" "); //create a constructor with an empty string
callbacks[1] = new PasswordCallback(" ",false);
try {
callbackHandler.handle(callbacks);
name = (((NameCallback)callbacks[0]).getName()).trim();
password = new String(((PasswordCallback)callbacks[1]).getPassword()).trim();
dbConn = datasource.getConnection();
pstmt = dbConn.prepareStatement(this.authenticationQuery);
pstmt.setString(1, name.toUpperCase());
pstmt.setString(2,password);
ResultSet rs = pstmt.executeQuery();
if(rs.next()) {
clientId = rs.getInt("ORG_ID");
userPrincipal = new SimplePrincipal(name);
loginOK = true;
status = OK;
} else {
loginOK = false;
status = NOT ;
}
rs.close();
pstmt.close();
dbConn.close();
} catch(java.io.IOException ioe) {
throw new LoginException(ioe.toString());
} catch(UnsupportedCallbackException ce) {
throw new LoginException("Error: "+ce.getCallback().toString());
} catch(SQLException sqe) {
sqe.printStackTrace();
throw new LoginException(sqe.toString());
}
System.out.println("=====completed login=====>" + loginOK );
return loginOK;
}
/**
* This method is also a part of the login process that must be
* implemented.It is used to disassociate any principal from the
* given jaas Subject. Clear up all the stuff here
*
* @returns boolean
* @throws LoginException
*
*/
public boolean logout() throws LoginException {
System.out.println("=====entering logout=====");
this.subject.getPrincipals().remove(userPrincipal);
this.clientId = 0;
this.name = null;
this.password = null;
status = NOT;
subject = null;
System.out.println("=====completed logout=====");
return true;
}
/**
*
* This method is called by the javax.security.auth.login.LoginContext
* login() as a part of the jaas login . As a part of the jaas login this
* method is executed after a successful jaas login. In jboss success of the
* jaas login is determined by the loginOK variable which is set to true
* in the above login(). loginOK is a protected field in the AbstractServerLogin Module
* of jboss. jboss requires that we explicitly set this variable &
* return in the above login(). In this method we associate the Principal to
* the jaas Subject if the loginOK is true.
*
* @returns boolean
* @throws LoginException
*/
public boolean commit() throws LoginException {
System.out.println("=====entering commit=====");
boolean flag = false;
if(subject == null || status == NOT ) return flag ;
if(loginOK) {
Set setOfPrincipals = subject.getPrincipals();
if(!setOfPrincipals.contains(userPrincipal)) {
setOfPrincipals.add(userPrincipal);
}
status = COMMIT;
flag = true;
} else {
flag = false;
}
System.out.println("=====completeing commit=====>" + flag + "===username is==" + name + "==password is==" + password);
return flag;
}
/**
*
* This method is called by the javax.security.auth.login.LoginContext
* login() . As a part of the jaas login, this method is
* executed if the above login fails. We should ensure that Principal
* object is cleared from the jaas Subject and is restored to nulls.
* Always returns true after fulfilling the above action.
*
* @throws LoginException
* @returns boolean
*
*/
public boolean abort() throws LoginException {
System.out.println("=====entering abort=====");
if((subject != null) && (userPrincipal != null)) {
Set setOfPrincipals = subject.getPrincipals();
if(setOfPrincipals.contains(userPrincipal)) {
setOfPrincipals.remove(userPrincipal);
}
}
subject = null;
userPrincipal = null;
status = NOT;
System.out.println("=====completeing logout=====");
return true;
}
/**
* Called by the jboss container
*
* This method returns the roles played by a particular user.
* back to the ejb container. The jboss conatiner expects these
* roles in the form of java.security.acl.Group interfaces.
* jboss has an implementation of the Group interface called SimpleGroup.
* However jboss container expects the returned 'roles' under a group called
* 'Roles'. Note this is specific to jboss ejb container.
* This method has to be overridden as the AbstractServerLogin Module of jboss
* insists that this should be overridden & we should provide the roles to the
* container.
*
* @throws LoginException
* @returns java.security.acl.Group[]
*
*/
protected Group[] getRoleSets() throws LoginException {
String groupName = "Roles";
Map groups = new HashMap();
Group roleGroup = new SimpleGroup("Roles");
groups.put("Roles", roleGroup);
Group group = (Group) groups.get(groupName);
if(group == null) {
group = new SimpleGroup(groupName);
groups.put(groupName, group);
}
group.addMember(new SimplePrincipal("DF_Operations"));
group.addMember(new SimplePrincipal("DF_Scenarios"));
Group[] roleSets = new Group[groups.size()];
groups.values().toArray(roleSets);
return roleSets;
}
/**
* Called by the jboss container
*
* This method returns the principal to the container after
* authenticating it in the login() of this class. This method also
* has to be overridden as the AbstractServerLogin Module only provides a
* abstarct implementation. In this case we have used the SimplePrincipal class
* which is a jboss implementation of java.security.Principal Interface.
*
* @returns java.security.Principal
*
*/
protected Principal getIdentity() {
System.out.println("=====entering getIdentity=====");
return this.userPrincipal;
}
/**
* This method is used to fetch a
* datasource so that a connection may be obtained from
* it and sql can be executed.
*
* @returns javax.sql.DataSource
* @param dataSourceName String
*/
private DataSource getDataSource (String dataSourceName) {
DataSource dataSource = null;
Properties p = new Properties();
p.put(Context.INITIAL_CONTEXT_FACTORY,"org.jnp.interfaces.NamingContextFactory");
p.put(Context.PROVIDER_URL,"jnp://localhost:2099");
p.put("java.naming.factory.url.pkgs","org.jboss.naming:org.jnp.interfaces");
try {
Context initContext = new InitialContext(p);
dataSource = (DataSource)initContext.lookup(dataSourceName);
} catch (NamingException ne) {
ne.printStackTrace();
}
return dataSource;
}
}
===============================================
The test client programme is :-
import java.io.IOException;
import javax.naming.InitialContext;
import javax.naming.Context;
import javax.rmi.PortableRemoteObject;
import com.mycompany.apps.defaultfilter.portfolio.*;
import com.mycompany.defaultfilter.exception.*;
import javax.security.auth.callback.*;
import javax.security.auth.login.*;
import java.util.*;
public class TestClient {
static class AppCallbackHandler implements CallbackHandler {
private String username;
private char[] password;
public AppCallbackHandler(String username, char[] password) {
this.username = username;
this.password = password;
}
public void handle(Callback[] callbacks) throws
IOException, UnsupportedCallbackException {
for (int i = 0; i < callbacks.length; i++) {
if (callbacks instanceof NameCallback) {
NameCallback nc = (NameCallback)callbacks;
nc.setName(username);
} else if (callbacks instanceof PasswordCallback) {
PasswordCallback pc = (PasswordCallback)callbacks;
pc.setPassword(password);
} else {
throw new UnsupportedCallbackException(callbacks, "Unrecognized Callback");
}
}
} //end of handle method
}
public static void main(String args[]) throws Exception {
if( args.length != 3 )
throw new IllegalArgumentException("Usage: username password example");
String name = args[0].trim();
char[] password = (args[1].trim()).toCharArray();
String example = args[2];
System.out.println("+++ Running TestClient with username="+name+", password="+args[1]+", example="+example);
try {
AppCallbackHandler handler = new AppCallbackHandler(name, password);
LoginContext lc = new LoginContext("client-login", handler);
lc.login();
System.out.println("Created LoginContext");
} catch (LoginException le) {
System.out.println("Login failed");
le.printStackTrace();
}
try {
Properties p = new Properties();
p.put(Context.INITIAL_CONTEXT_FACTORY,"org.jnp.interfaces.NamingContextFactory");
p.put(Context.PROVIDER_URL,"jnp://localhost:2099");
p.put("java.naming.factory.url.pkgs","org.jboss.naming:org.jnp.interfaces");
p.put(Context.SECURITY_PRINCIPAL, args[0].trim());
p.put(Context.SECURITY_CREDENTIALS, args[1].trim());
InitialContext iniContext = new InitialContext(p);
Object ref = iniContext.lookup("ejb/PortfolioManagerBean");
PortfolioManagerHome home = (PortfolioManagerHome)PortableRemoteObject.narrow(ref, PortfolioManagerHome.class);
PortfolioManager manager = home.create();
String portfolioName = manager.getPortfolioName(601);
System.out.println("=====portfolioName is =====" + portfolioName);
} catch(Exception e) {
e.printStackTrace();
}
}
}
===============================================
The ejb bean is :-
/*
* File: PortfolioManagerBean.java
*
* Copyright (c) 2003 Standard and Poor's
* This software is the confidential and proprietary information of Standard and Poor's.
* All Rights Reserved.
*
* File Change Log:
* Date Author Changes
* Jun 8, 2004 Shelly Gupta Created
* Aug 9, 2004 Ragav Enhanced
*/
//package
package com.mycompany.apps.defaultfilter.portfolio;
//app imports
import java.sql.BatchUpdateException;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Vector;
import javax.ejb.CreateException;
import javax.ejb.SessionBean;
import javax.ejb.SessionContext;
import javax.sql.DataSource;
import org.apache.log4j.Logger;
public class PortfolioManagerBean implements SessionBean{
private DataSource datasource;
SessionContext sessionContext;
final Logger logger = Logger.getLogger(PortfolioManagerBean.class);
private final String getPortfolioNameQuery =
"Select portfolio_name from portfolio where portfolio_id = ?";
public void ejbCreate() throws CreateException, DefaultFilterException{
datasource = DefaultFilterServiceLocator.getInstance().getDataSource();
}
public void ejbRemove(){
}
public void ejbActivate(){
}
public void ejbPassivate(){
}
public void setSessionContext(SessionContext context) {
this.sessionContext = context;
}
public String getPortfolioName(int portfolioId) throws DFGetException{
String portfolioName = "";
Connection dbConn = null;
PreparedStatement pstmt = null;
try{
dbConn = datasource.getConnection();
pstmt = dbConn.prepareStatement(getPortfolioNameQuery);
pstmt.setInt(1, portfolioId);
ResultSet rs = pstmt.executeQuery();
while (rs.next()) {
portfolioName = rs.getString(1);
}
rs.close();
}
catch (Exception e) {
logger.error(e);
throw new DFGetException(e);
}
finally {
try{
if (pstmt != null) {
pstmt.close();
}
if (dbConn != null) {
dbConn.close();
}
} catch (SQLException e) {
logger.error(e);
}
}
return portfolioName;
}
}
===============================================
I tried every thing in the custom module but I always get this exception :-
12:32:16,402 INFO [STDOUT] login.DFLoginModule - initialized! caller = javax.security.auth.login.LoginContext$SecureCallbackHandler
12:32:16,402 INFO [STDOUT] login.DFLoginModule - subject = javax.security.auth.Subject
12:32:16,402 INFO [STDOUT] =====entering login=====
12:32:17,105 INFO [STDOUT] =====completed login=====>true
12:32:17,105 INFO [STDOUT] =====entering commit=====
12:32:17,105 INFO [STDOUT] =====completeing commit=====>true===username is==df==password is==df
12:32:17,105 ERROR [SecurityInterceptor] Insufficient method permissions, principal=df, method=create, interface=HOME, requiredRoles=[DF_Operations], principalRoles=null
12:32:17,105 ERROR [LogInterceptor] EJBException, causedBy:
java.lang.SecurityException: Insufficient method permissions, principal=df, method=create, interface=HOME, requiredRoles=[DF_Operations], principalRoles=null
at org.jboss.ejb.plugins.SecurityInterceptor.checkSecurityAssociation(SecurityInterceptor.java:229)
at org.jboss.ejb.plugins.SecurityInterceptor.invokeHome(SecurityInterceptor.java:83)
at org.jboss.ejb.plugins.LogInterceptor.invokeHome(LogInterceptor.java:120)
at org.jboss.ejb.plugins.ProxyFactoryFinderInterceptor.invokeHome(ProxyFactoryFinderInterceptor.java:93)
at org.jboss.ejb.StatelessSessionContainer.internalInvokeHome(StatelessSessionContainer.java:319)
at org.jboss.ejb.Container.invoke(Container.java:743)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:324)
at org.jboss.mx.server.ReflectedDispatcher.dispatch(ReflectedDispatcher.java:60)
at org.jboss.mx.server.Invocation.dispatch(Invocation.java:61)
at org.jboss.mx.server.Invocation.dispatch(Invocation.java:53)
at org.jboss.mx.server.Invocation.invoke(Invocation.java:86)
at org.jboss.mx.server.AbstractMBeanInvoker.invoke(AbstractMBeanInvoker.java:185)
at org.jboss.mx.server.MBeanServerImpl.invoke(MBeanServerImpl.java:473)
at org.jboss.invocation.jrmp.server.JRMPInvoker.invoke(JRMPInvoker.java:360)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:324)
at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:261)
at sun.rmi.transport.Transport$1.run(Transport.java:148)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.Transport.serviceCall(Transport.java:144)
at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:460)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:701)
at java.lang.Thread.run(Thread.java:534)
===============================================
Any help will be gratefully acknowledged