0 Replies Latest reply on Aug 27, 2004 1:17 PM by Ragav Gomatam

    Custom login Module problems & issues

    Ragav Gomatam Novice

      Hi 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