I wrote a login module specific for Oracle DataBase, that try to authenticate a user on Oracle DataSource and map Oracle Role on J2ee Role. I think it could be very interesting for guys that have jboss and Oracle together, because you could manage roles and user just in one enviroment.
I tried it on my enviroment and seems to work only if I set in Oracle-xa-service.xml
<attribute name="Criteria">ByApplication</attribute>
If I set it to ByContainer I get this error:
15:14:02,562 WARN [JBossManagedConnectionPool] Destroying connection that could not be successfully matched: org.jboss.resource.adapter.jdbc.xa.oracle.XAOracleManagedConnection@462631
And connection still Opened on the DB.
Could you please explain me what's happen. Thank you in advance
BTW I attached my source, what about it? Couldn't it be useful for anyone?
--------------- all work and no play makes Jack a dull boy --------------- bye Stefano [EMAIL PROTECTED] |
/* * JBoss, the OpenSource WebOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.security.auth.spi;
import java.security.acl.Group; import java.util.HashMap; import java.util.Map; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import javax.naming.InitialContext; import javax.naming.NamingException; import javax.sql.DataSource; import javax.security.auth.Subject; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.login.LoginException; import javax.security.auth.login.FailedLoginException; import org.jboss.security.SimpleGroup; import org.jboss.security.SimplePrincipal; import org.jboss.security.auth.spi.UsernamePasswordLoginModule; /** * A JDBC based login module that supports authentication and role mapping. * It is based on two logical tables: * <ul> * <li>Principals(PrincipalID text, Password text) * <li>Roles(PrincipalID text, Role text, RoleGroup text) * </ul> * <p> * LoginModule options: * <ul> * <li><em>dsJndiName</em>: The name of the DataSource of the database containing the Principals, Roles tables * <li><em>principalsQuery</em>: The prepared statement query, equivalent to: * <pre> * "select Password from Principals where PrincipalID=?" * </pre> * Use it if you don't want to use the Oracle user/password to check permission * <li><em>rolesQuery</em>: The prepared statement query, equivalent to: * <pre> * "select Role, RoleGroup from Roles where PrincipalID=?" * </pre> * Use it if you want to read roles from a table instead or together Oracle Roles * <li><em>useOracleRoles</em> true/false to use Oracle Roles mapping to J2ee Roles * </ul> * @author <a href="www.javalinux.it">Stefano Maestri <maeste> </A> * Special thanks to Paolo Vigano my DBA for support and tests * @version $Revision: 1.6 $ */ public class OracleServerLoginModule extends UsernamePasswordLoginModule { private String dsJndiName; private String principalsQuery = null; private boolean useOracleRoles = false; private String rolesQuery = null; private String oracleRolesQuery = "select granted_role from user_role_privs"; private String username; private String password; /** * Initialize this LoginModule. */ public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options) { super.initialize(subject, callbackHandler, sharedState, options); dsJndiName = (String) options.get("dsJndiName"); if( dsJndiName == null ) dsJndiName = "java:/DefaultDS"; Object tmp = options.get("principalsQuery"); if( tmp != null ) principalsQuery = tmp.toString(); tmp = options.get("rolesQuery"); if( tmp != null ) rolesQuery = tmp.toString(); tmp = options.get("useOracleRoles"); if( tmp != null && tmp.toString().equalsIgnoreCase("true") && principalsQuery==null ) useOracleRoles = true; log.trace("OracleServerLoginModule, dsJndiName="+dsJndiName); log.trace("principalsQuery="+principalsQuery); log.trace("rolesQuery="+rolesQuery); log.trace("useOracleRoles="+rolesQuery); } /** Get the expected password for the current username available via * the getUsername() method. This is called from within the login() * method after the CallbackHandler has returned the username and * candidate password. * @return the valid password String */ protected String getUsersPassword() throws LoginException { String password = null; Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; try { username = getUsername(); if (principalsQuery != null) { InitialContext ctx = new InitialContext(); DataSource ds = (DataSource) ctx.lookup(dsJndiName); conn = ds.getConnection(); // Get the password ps = conn.prepareStatement(principalsQuery); ps.setString(1, username); rs = ps.executeQuery(); if( rs.next() == false ) throw new FailedLoginException("No matching username found in Principals"); password = rs.getString(1); password = convertRawPassword(password); rs.close(); } else { password = null; } } catch(NamingException ex) { throw new LoginException(ex.toString(true)); } catch(SQLException ex) { log.error("Query failed", ex); throw new LoginException(ex.toString()); } finally { if( rs != null ) { try { rs.close(); } catch(SQLException e) {} } if( ps != null ) { try { ps.close(); } catch(SQLException e) {} } if( conn != null ) { try { conn.close(); } catch (SQLException ex) {} } } return password; } /** Overriden by subclasses to return the Groups that correspond to the to the role sets assigned to the user. Subclasses should create at least a Group named "Roles" that contains the roles assigned to the user. A second common group is "CallerPrincipal" that provides the application identity of the user rather than the security domain identity. @return Group[] containing the sets of roles */ protected Group[] getRoleSets() throws LoginException { String username = getUsername(); Connection conn = null; HashMap setsMap = new HashMap(); PreparedStatement ps = null; ResultSet rs = null; try { InitialContext ctx = new InitialContext(); DataSource ds = (DataSource) ctx.lookup(dsJndiName); if (!useOracleRoles) { conn = ds.getConnection(); } else { conn = ds.getConnection(username,password); ps = conn.prepareStatement(oracleRolesQuery); rs = ps.executeQuery(); if( rs.next() == false ) { if( getUnauthenticatedIdentity() == null ) throw new FailedLoginException("No matching username found in Roles"); /* We are running with an unauthenticatedIdentity so create an empty Roles set and return. */ Group[] roleSets = { new SimpleGroup("Roles") }; return roleSets; } do { String name = rs.getString(1); //String groupName = rs.getString(2); String groupName = null; if( groupName == null || groupName.length() == 0 ) groupName = "Roles"; Group group = (Group) setsMap.get(groupName); if( group == null ) { group = new SimpleGroup(groupName); setsMap.put(groupName, group); } group.addMember(new SimplePrincipal(name)); } while( rs.next() ); rs.close(); ps.close(); } if (rolesQuery != null ){ // Get the users role names ps = conn.prepareStatement(rolesQuery); ps.setString(1, username); rs = ps.executeQuery(); if( rs.next() == false ) { if( getUnauthenticatedIdentity() == null ) throw new FailedLoginException("No matching username found in Roles"); /* We are running with an unauthenticatedIdentity so create an empty Roles set and return. */ Group[] roleSets = { new SimpleGroup("Roles") }; return roleSets; } do { String name = rs.getString(1); String groupName = rs.getString(2); if( groupName == null || groupName.length() == 0 ) groupName = "Roles"; Group group = (Group) setsMap.get(groupName); if( group == null ) { group = new SimpleGroup(groupName); setsMap.put(groupName, group); } group.addMember(new SimplePrincipal(name)); } while( rs.next() ); rs.close(); } } catch(NamingException ex) { throw new LoginException(ex.toString(true)); } catch(SQLException ex) { super.log.error("SQL failure", ex); throw new LoginException(ex.toString()); } finally { if( rs != null ) { try { rs.close(); } catch(SQLException e) {} } if( ps != null ) { try { ps.close(); } catch(SQLException e) {} } if( conn != null ) { try { conn.close(); } catch (Exception ex) {} } } Group[] roleSets = new Group[setsMap.size()]; setsMap.values().toArray(roleSets); return roleSets; } /** A hook to allow subclasses to convert a password from the database into a plain text string or whatever form is used for matching against the user input. It is called from within the getUsersPassword() method. @param rawPassword, the password as obtained from the database @return the argument rawPassword */ protected String convertRawPassword(String rawPassword) { return rawPassword; } protected boolean validatePassword(String inputPassword, String expectedPassword) { if( inputPassword == null || (expectedPassword == null && principalsQuery!=null) ) return false; if( expectedPassword == null && principalsQuery == null) { Connection conn = null; try { password = inputPassword; InitialContext ctx = new InitialContext(); DataSource ds = (DataSource) ctx.lookup(dsJndiName); log.debug(username+"/"+password); conn = ds.getConnection(username,password); return true; } catch(Exception ex) { log.error("Login failure", ex); } finally { if( conn != null ) { try { log.debug("closing"); conn.close(); } catch (Exception ex) { } } } } return inputPassword.equals(expectedPassword); } }