You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by na...@apache.org on 2001/02/04 23:08:24 UTC
cvs commit: jakarta-tomcat/src/share/org/apache/tomcat/modules/aaa JDBCRealm.java
nacho 01/02/04 14:08:24
Modified: src/share/org/apache/tomcat/modules/aaa JDBCRealm.java
Log:
Cosmetic changes and the ability to select
when the JDBC connection is started at
Tomcat init or in first realm access.
Revision Changes Path
1.2 +190 -268 jakarta-tomcat/src/share/org/apache/tomcat/modules/aaa/JDBCRealm.java
Index: JDBCRealm.java
===================================================================
RCS file: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/modules/aaa/JDBCRealm.java,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- JDBCRealm.java 2001/01/01 02:01:29 1.1
+++ JDBCRealm.java 2001/02/04 22:08:24 1.2
@@ -1,11 +1,11 @@
/*
- * $Header: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/modules/aaa/JDBCRealm.java,v 1.1 2001/01/01 02:01:29 costin Exp $
- * $Revision: 1.1 $
- * $Date: 2001/01/01 02:01:29 $
+ * $Header: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/modules/aaa/JDBCRealm.java,v 1.2 2001/02/04 22:08:24 nacho Exp $
+ * $Revision: 1.2 $
+ * $Date: 2001/02/04 22:08:24 $
*
* The Apache Software License, Version 1.1
*
- * Copyright (c) 1999 The Apache Software Foundation. All rights
+ * Copyright (c) 1999 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -13,7 +13,7 @@
* are met:
*
* 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
+ * notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
@@ -21,7 +21,7 @@
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
- * any, must include the following acknowlegement:
+ * any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
@@ -64,152 +64,103 @@
import org.apache.tomcat.core.*;
import org.apache.tomcat.util.*;
import java.security.*;
-import java.security.Principal;
-import java.io.File;
-import java.util.Enumeration;
-import java.util.Hashtable;
+//import java.security.Principal;
+//import java.io.File;
+//import java.util.Enumeration;
+//import java.util.Hashtable;
import java.util.Vector;
import java.io.*;
import java.net.*;
import java.util.*;
import java.sql.*;
-
/**
- *
* Implmentation of <b>Realm</b> that works with any JDBC supported database.
- * See the JDBCRealm.howto for more details on how to set up the database and
- * for configuration options.
- *
- * TODO:
- * - Work on authentication with non-plaintext passwords
- *
+ * See the JDBCRealm.howto for more details on how to set up the database and for configuration options. TODO:
+ * - Work on authentication with non-plaintext passwords
* @author Craig R. McClanahan
* @author Carson McDonald
* @author Ignacio J. Ortega
* @author Bip Thelin
- *
*/
-
public final class JDBCRealm extends BaseInterceptor {
-
-
- ContextManager cm;
int reqRolesNote;
int userNote;
int passwordNote;
// ----------------------------------------------------- Instance Variables
- /**
- * The connection to the database.
- */
+ /** The connection to the database. */
private Connection dbConnection = null;
- /**
- * The PreparedStatement to use for authenticating users.
- */
+ /** The PreparedStatement to use for authenticating users. */
private PreparedStatement preparedAuthenticate = null;
-
- /**
- * The PreparedStatement to use for identifying the roles for
- * a specified user.
- */
+ /** The PreparedStatement to use for identifying the roles for a specified user. */
private PreparedStatement preparedRoles = null;
-
- /**
- * The connection URL to use when trying to connect to the databse
- */
+ /** The connection URL to use when trying to connect to the databse */
private String connectionURL = null;
- /**
- * The connection URL to use when trying to connect to the databse
- */
+ /** The connection URL to use when trying to connect to the databse */
private String connectionName = null;
- /**
- * The connection URL to use when trying to connect to the databse
- */
+ /** The connection URL to use when trying to connect to the databse */
private String connectionPassword = null;
- /**
- * The table that holds user data.
- */
+ /** The table that holds user data. */
private String userTable = null;
- /**
- * The column in the user table that holds the user's name
- */
+ /** The column in the user table that holds the user's name */
private String userNameCol = null;
- /**
- * The column in the user table that holds the user's credintials
- */
+ /** The column in the user table that holds the user's credintials */
private String userCredCol = null;
- /**
- * The table that holds the relation between user's and roles
- */
+ /** The table that holds the relation between user's and roles */
private String userRoleTable = null;
- /**
- * The column in the user role table that names a role
- */
+ /** The column in the user role table that names a role */
private String roleNameCol = null;
- /**
- * The JDBC driver to use.
- */
+ /** The JDBC driver to use. */
private String driverName = null;
-
- /**
- * The string manager for this package.
- */
- private static StringManager sm =
- StringManager.getManager("org.apache.tomcat.resources");
+ /** The string manager for this package. */
+ private static StringManager sm = StringManager.getManager("org.apache.tomcat.resources");
- /**
- * Has this component been started?
- */
+ /** Has this component been started? */
private boolean started = false;
+ /** Has the JDBC connection been started? */
+ private boolean JDBCstarted = false;
+
/**
- *
- * Digest algorithm used in passwords thit is same values
- * accepted by MessageDigest for algorithm
+ * Digest algorithm used in passwords thit is same values accepted by MessageDigest for algorithm
* plus "No" ( no encode ) that is the default
- *
*/
-
- private String digest="No";
+ private String digest = "No";
-
+ boolean connectOnInit = false;
// ------------------------------------------------------------- Properties
-
/**
* Set the JDBC driver that will be used.
- *
* @param driverName The driver name
*/
- public void setDriverName( String driverName ) {
- this.driverName = driverName;
+ public void setDriverName(String driverName) {
+ this.driverName = driverName;
}
/**
* Set the URL to use to connect to the database.
- *
* @param connectionURL The new connection URL
*/
- public void setConnectionURL( String connectionURL ) {
- this.connectionURL = connectionURL;
+ public void setConnectionURL(String connectionURL) {
+ this.connectionURL = connectionURL;
}
/**
* Set the name to use to connect to the database.
- *
* @param connectionName User name
*/
public void setConnectionName(String connectionName) {
@@ -218,7 +169,6 @@
/**
* Set the password to use to connect to the database.
- *
* @param connectionPassword User password
*/
public void setConnectionPassword(String connectionPassword) {
@@ -227,229 +177,214 @@
/**
* Set the table that holds user data.
- *
* @param userTable The table name
*/
- public void setUserTable( String userTable ) {
- this.userTable = userTable;
+ public void setUserTable(String userTable) {
+ this.userTable = userTable;
}
/**
* Set the column in the user table that holds the user's name
- *
* @param userNameCol The column name
*/
- public void setUserNameCol( String userNameCol ) {
- this.userNameCol = userNameCol;
+ public void setUserNameCol(String userNameCol) {
+ this.userNameCol = userNameCol;
}
/**
* Set the column in the user table that holds the user's credintials
- *
* @param userCredCol The column name
*/
- public void setUserCredCol( String userCredCol ) {
- this.userCredCol = userCredCol;
+ public void setUserCredCol(String userCredCol) {
+ this.userCredCol = userCredCol;
}
/**
* Set the table that holds the relation between user's and roles
- *
* @param userRoleTable The table name
*/
- public void setUserRoleTable( String userRoleTable ) {
+ public void setUserRoleTable(String userRoleTable) {
this.userRoleTable = userRoleTable;
}
/**
* Set the column in the user role table that names a role
- *
- * @param userRoleNameCol The column name
- */
- public void setRoleNameCol( String roleNameCol ) {
+ * @param roleNameCol The column name
+ */
+ public void setRoleNameCol(String roleNameCol) {
this.roleNameCol = roleNameCol;
}
+
/**
* Gets the digest algorithm used for credentials in the database
- * could be the same that MessageDigest accepts vor algorithm
- * and "No" that is the Default
- *
- */
-
+ * could be the same that MessageDigest accepts vor algorithm and "No" that is the Default
+ * @return
+ */
public String getDigest() {
return digest;
}
/**
- * Gets the digest algorithm used for credentials in the database
- * could be the same that MessageDigest accepts vor algorithm
- * and "No" that is the Default
- *
+ * Sets the digest algorithm used for credentials in the database
+ * could be the same that MessageDigest accepts vor algorithm and "No" that is the Default
* @param algorithm the Encode type
- */
-
+ */
public void setDigest(String algorithm) {
digest = algorithm;
}
+
+/**
+ * When connectOnInit is setted to "true" the JDBC connection is started at tomcat init
+ * if false the connection is started in the first times is needed.
+ * @param s "true" or "false"
+ */
+ public void setConnectOnInit(String s) {
+ connectOnInit = Boolean.valueOf(s).booleanValue();
+ }
/**
* If there are any errors with the JDBC connection, executing
- * the query or anything we return false (don't authenticate). This
- * event is also logged.
- *
+ * the query or anything we return false (don't authenticate). This event is also logged.
* If there is some SQL exception the connection is set to null.
* This will allow a retry on the next auth attempt. This might not
- * be the best thing to do but it will keep tomcat from needing a
- * restart if the database goes down.
+ * be the best thing to do but it will keep tomcat from needing a restart if the database goes down.
*
* @param username Username of the Principal to look up
- * @param credentials Password or other credentials to use in
- * authenticating this username
+ * @param credentials Password or other credentials to use in authenticating this username
*/
- public synchronized boolean checkPassword(String username
- , String credentials) {
+ private synchronized boolean checkPassword(String username,String credentials) {
try {
if (!checkConnection())
return false;
// Create the authentication search prepared statement if necessary
if (preparedAuthenticate == null) {
- String sql = "SELECT " + userCredCol + " FROM " + userTable +
- " WHERE " + userNameCol + " = ?";
+ String sql = "SELECT " + userCredCol
+ + " FROM " + userTable
+ + " WHERE " + userNameCol + " = ?";
if (debug >= 1)
log("JDBCRealm.authenticate: " + sql);
preparedAuthenticate = dbConnection.prepareStatement(sql);
}
-
// Perform the authentication search
preparedAuthenticate.setString(1, username);
ResultSet rs1 = preparedAuthenticate.executeQuery();
- boolean found = false;
if (rs1.next()) {
if (digest.equalsIgnoreCase("No")) {
- if (credentials.equals(rs1.getString(1))) {
+ if (credentials.equals(rs1.getString(1).trim())) {
if (debug >= 2)
- log(sm.getString("jdbcRealm.authenticateSuccess",
- username));
+ log(sm.getString("jdbcRealm.authenticateSuccess", username));
return true;
}
} else {
- if (credentials.equals(Digest(rs1.getString(1),digest))) {
+ if (credentials.equals(digest(rs1.getString(1), digest))) {
if (debug >= 2)
- log(sm.getString("jdbcRealm.authenticateSuccess",
- username));
+ log(sm.getString("jdbcRealm.authenticateSuccess", username));
return true;
}
}
}
rs1.close();
if (debug >= 2)
- log(sm.getString("jdbcRealm.authenticateFailure",
- username));
-
+ log(sm.getString("jdbcRealm.authenticateFailure", username));
return false;
- } catch( SQLException ex ) {
-
+ } catch (SQLException ex) {
// Log the problem for posterity
- log(sm.getString("jdbcRealm.checkPasswordSQLException",
- username),ex);
-
+ log(sm.getString("jdbcRealm.checkPasswordSQLException", username), ex);
// Clean up the JDBC objects so that they get recreated next time
if (preparedAuthenticate != null) {
- try {
- preparedAuthenticate.close();
- } catch (Throwable t) {
- ;
- }
- preparedAuthenticate = null;
+ try {
+ preparedAuthenticate.close();
+ } catch (Throwable t) {
+ ;
+ }
+ preparedAuthenticate = null;
}
if (dbConnection != null) {
- try {
- dbConnection.close();
- } catch (Throwable t) {
- ;
- }
- dbConnection = null;
+ try {
+ dbConnection.close();
+ } catch (Throwable t) {
+ ;
+ }
+ dbConnection = null;
}
-
// Return "not authenticated" for this request
return false;
}
}
- private boolean checkConnection(){
+ private boolean checkConnection() {
try {
- if( (dbConnection == null) || dbConnection.isClosed() ) {
+ if ((dbConnection == null) || dbConnection.isClosed()) {
Class.forName(driverName);
- log(sm.getString("jdbcRealm.checkConnectionDBClosed"));
+ if( JDBCstarted )
+ log(sm.getString("jdbcRealm.checkConnectionDBClosed"));
if ((connectionName == null || connectionName.equals("")) ||
- (connectionPassword == null || connectionPassword.equals(""))) {
+ (connectionPassword == null || connectionPassword.equals(""))) {
dbConnection = DriverManager.getConnection(connectionURL);
} else {
- dbConnection = DriverManager.getConnection(connectionURL,
- connectionName,
- connectionPassword);
+ dbConnection = DriverManager.getConnection(connectionURL, connectionName, connectionPassword);
}
- if( dbConnection == null || dbConnection.isClosed() ) {
- log(sm.getString("jdbcRealm.checkConnectionDBReOpenFail"));
- return false;
+ JDBCstarted=true;
+ if (dbConnection == null || dbConnection.isClosed()) {
+ log(sm.getString("jdbcRealm.checkConnectionDBReOpenFail"));
+ return false;
}
}
return true;
- }catch (SQLException ex){
- log(sm.getString("jdbcRealm.checkConnectionSQLException"),ex);
+ } catch (SQLException ex) {
+ log(sm.getString("jdbcRealm.checkConnectionSQLException"), ex);
return false;
}
- catch( ClassNotFoundException ex ) {
+ catch (ClassNotFoundException ex) {
throw new RuntimeException("JDBCRealm.checkConnection: " + ex);
}
}
+/**
+ * returns all the roles for a given user.
+ *
+ * @param username the user name
+ * @return the roles array
+ */
public synchronized String[] getUserRoles(String username) {
try {
- if ( !checkConnection() )
+ if (!checkConnection())
return null;
if (preparedRoles == null) {
- String sql = "SELECT " + roleNameCol + " FROM " +
- userRoleTable + " WHERE " + userNameCol + " = ?";
+ String sql = "SELECT " + roleNameCol + " FROM " + userRoleTable + " WHERE " + userNameCol + " = ?";
if (debug >= 1)
log("JDBCRealm.roles: " + sql);
preparedRoles = dbConnection.prepareStatement(sql);
}
preparedRoles.clearParameters();
preparedRoles.setString(1, username);
-
ResultSet rs = preparedRoles.executeQuery();
-
// Next we convert the resultset into a String[]
- Vector vrol=new Vector();
-
+ Vector vrol = new Vector();
while (rs.next()) {
- vrol.addElement(rs.getString(1));
+ vrol.addElement(rs.getString(1).trim());
}
- String[] res=new String[vrol.size() > 0 ? vrol.size() : 1 ];
-
+ String[] res = new String[vrol.size() > 0 ? vrol.size() : 1];
// no roles case
- if( vrol.size() == 0 ){
- res[0]="";
+ if (vrol.size() == 0) {
+ res[0] = "";
return res;
}
-
- for(int i=0 ; i<vrol.size() ; i++ )
- res[i]=(String)vrol.elementAt(i);
+ for (int i = 0; i < vrol.size(); i++)
+ res[i] = (String)vrol.elementAt(i);
return res;
}
- catch( SQLException ex ) {
+ catch (SQLException ex) {
// Set the connection to null.
// Next time we will try to get a new connection.
- log(sm.getString("jdbcRealm.getUserRolesSQLException",
- username));
+ log(sm.getString("jdbcRealm.getUserRolesSQLException", username));
if (preparedRoles != null) {
try {
preparedRoles.close();
} catch (Throwable t) {
;
- }
- preparedRoles = null;
+ }
+ preparedRoles = null;
}
if (dbConnection != null) {
try {
@@ -464,134 +399,121 @@
}
// -------------------- Tomcat hooks --------------------
-
- public void contextInit(Context ctx)
- throws org.apache.tomcat.core.TomcatException {
+ public void contextInit(Context ctx) throws org.apache.tomcat.core.TomcatException {
super.contextInit(ctx);
init(ctx.getContextManager());
- // Validate and update our current component state
+ // Validate and update our current component state
}
- public void contextShutdown(Context ctx)
- throws org.apache.tomcat.core.TomcatException {
+ public void contextShutdown(Context ctx) throws org.apache.tomcat.core.TomcatException {
shutdown();
}
- public void shutdown()
- throws org.apache.tomcat.core.TomcatException {
- // Validate and update our current component state
- if (started) {
- started=false;
- try {
- if( dbConnection != null && !dbConnection.isClosed())
- dbConnection.close();
- }catch( SQLException ex ) {
- log("dbConnection.close Exception!!!",ex);
+ public void shutdown() throws org.apache.tomcat.core.TomcatException {
+ // Validate and update our current component state
+ if (started) {
+ started = false;
+ try {
+ if (dbConnection != null && !dbConnection.isClosed())
+ dbConnection.close();
+ } catch (SQLException ex) {
+ log("dbConnection.close Exception!!!", ex);
+ }
}
- }
}
-
- public int authenticate( Request req, Response response ) {
- String user=(String)req.getNote( userNote );
- String password=(String)req.getNote( passwordNote );
- if( user==null) return DECLINED;
-
- if( checkPassword( user, password ) ) {
- if( debug > 0 ) log( "Auth ok, user=" + user );
+/**
+ * Hook implementation
+ * @param req
+ * @param response
+ * @return
+ */
+ public int authenticate(Request req, Response response) {
+ String user = (String)req.getNote(userNote);
+ String password = (String)req.getNote(passwordNote);
+ if (user == null) return DECLINED;
+ if (checkPassword(user, password)) {
+ if (debug > 0) log("Auth ok, user=" + user);
Context ctx = req.getContext();
if (ctx != null)
req.setAuthType(ctx.getAuthMethod());
- if( user!=null) {
- req.setRemoteUser( user );
-// req.setNote(reqRealmSignNote,this);
- String userRoles[] = getUserRoles( user );
- req.setUserRoles( userRoles );
- return OK;
- }
- }
- return DECLINED;
+ if (user != null) {
+ req.setRemoteUser(user);
+ // req.setNote(reqRealmSignNote,this);
+ String userRoles[] = getUserRoles(user);
+ req.setUserRoles(userRoles);
+ return OK;
+ }
+ }
+ return DECLINED;
}
-
/**
- * Digest password using the algorithm especificied and
- * convert the result to a corresponding hex string.
+ * Digest password using the algorithm especificied and convert the result to a corresponding hex string.
* If exception, the plain credentials string is returned
- *
- * @param credentials Password or other credentials to use in
- * authenticating this username
- *
+ * @param credentials Password or other credentials to use in authenticating this username
* @param algorithm Algorithm used to do th digest
- *
*/
- final public static String Digest(String credentials,String algorithm) {
+ public final static String digest(String credentials, String algorithm) {
try {
// Obtain a new message digest with MD5 encryption
MessageDigest md = (MessageDigest)MessageDigest.getInstance(algorithm).clone();
// encode the credentials
- md.update( credentials.getBytes() );
+ md.update(credentials.getBytes());
// obtain the byte array from the digest
byte[] dig = md.digest();
// convert the byte array to hex string
-// Base64 enc=new Base64();
-// return new String(enc.encode(HexUtils.convert(dig).getBytes()));
+ // Base64 enc=new Base64();
+ // return new String(enc.encode(HexUtils.convert(dig).getBytes()));
return HexUtils.convert(dig);
-
- } catch( Exception ex ) {
- ex.printStackTrace();
- return credentials;
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ return credentials;
}
}
- public static void main(String args[] ) {
+/**
+ * JDBCRealm can be used as a standalone tool for offline password digest
+ * @param args
+ */
+ public static void main(String args[]) {
if (args.length >= 2) {
- if( args[0].equalsIgnoreCase("-a")){
- for( int i=2; i < args.length ; i++){
- System.out.print(args[i]+":");
- System.out.println(Digest(args[i],args[1]));
+ if (args[0].equalsIgnoreCase("-a")) {
+ for (int i = 2; i < args.length; i++) {
+ System.out.print(args[i] + ":");
+ System.out.println(digest(args[i], args[1]));
}
}
}
-
}
- /** Called when the ContextManger is started
- */
+ /** Called when the ContextManger is started */
public void engineInit(ContextManager cm) throws TomcatException {
super.engineInit(cm);
init(cm);
}
void init(ContextManager cm) {
- if (!started) {
- started = true;
- // set-up a per/container note for maps
- try {
- // XXX make the name a "global" static - after everything is stable!
- reqRolesNote = cm.getNoteId( ContextManager.REQUEST_NOTE
- , "required.roles");
- userNote=cm.getNoteId( ContextManager.REQUEST_NOTE,
- "credentials.user");
- passwordNote=cm.getNoteId( ContextManager.REQUEST_NOTE,
- "credentials.password");
- }
- catch( TomcatException ex ) {
- log("setting up note for " + cm, ex);
- throw new RuntimeException( "Invalid state ");
- }
- }
+ if (!started) {
+ started = true;
+ // set-up a per/container note for maps
+ try {
+ // XXX make the name a "global" static - after everything is stable!
+ reqRolesNote = cm.getNoteId(ContextManager.REQUEST_NOTE, "required.roles");
+ userNote = cm.getNoteId(ContextManager.REQUEST_NOTE, "credentials.user");
+ passwordNote = cm.getNoteId(ContextManager.REQUEST_NOTE, "credentials.password");
+ if (connectOnInit && !checkConnection())
+ throw new RuntimeException("JDBCRealm cannot be started");
+ }
+ catch (TomcatException ex) {
+ log("setting up note for " + cm, ex);
+ throw new RuntimeException("Invalid state ");
+ }
+ }
}
- /** Called before the ContextManager is stoped.
- * You need to stop any threads and remove any resources.
- */
public void engineShutdown(ContextManager cm) throws TomcatException {
- //TODO: Override this org.apache.tomcat.core.BaseInterceptor method
shutdown();
}
-
}
-
-