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