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);
}
}
