Save This Page
Home » apache-tomcat-6.0.16-src » org.apache » catalina » realm » [javadoc | source]
    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one or more
    3    * contributor license agreements.  See the NOTICE file distributed with
    4    * this work for additional information regarding copyright ownership.
    5    * The ASF licenses this file to You under the Apache License, Version 2.0
    6    * (the "License"); you may not use this file except in compliance with
    7    * the License.  You may obtain a copy of the License at
    8    * 
    9    *      http://www.apache.org/licenses/LICENSE-2.0
   10    * 
   11    * Unless required by applicable law or agreed to in writing, software
   12    * distributed under the License is distributed on an "AS IS" BASIS,
   13    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   14    * See the License for the specific language governing permissions and
   15    * limitations under the License.
   16    */
   17   
   18   
   19   package org.apache.catalina.realm;
   20   
   21   
   22   import java.security.Principal;
   23   import java.sql.Connection;
   24   import java.sql.PreparedStatement;
   25   import java.sql.ResultSet;
   26   import java.sql.SQLException;
   27   import java.util.ArrayList;
   28   
   29   import javax.naming.Context;
   30   import javax.sql.DataSource;
   31   
   32   import org.apache.naming.ContextBindings;
   33   import org.apache.catalina.LifecycleException;
   34   import org.apache.catalina.ServerFactory;
   35   import org.apache.catalina.core.StandardServer;
   36   import org.apache.catalina.util.StringManager;
   37   
   38   /**
   39   *
   40   * Implmentation of <b>Realm</b> that works with any JDBC JNDI DataSource.
   41   * See the JDBCRealm.howto for more details on how to set up the database and
   42   * for configuration options.
   43   *
   44   * @author Glenn L. Nielsen
   45   * @author Craig R. McClanahan
   46   * @author Carson McDonald
   47   * @author Ignacio Ortega
   48   * @version $Revision: 543691 $
   49   */
   50   
   51   public class DataSourceRealm
   52       extends RealmBase {
   53   
   54   
   55       // ----------------------------------------------------- Instance Variables
   56   
   57   
   58       /**
   59        * The generated string for the roles PreparedStatement
   60        */
   61       private String preparedRoles = null;
   62   
   63   
   64       /**
   65        * The generated string for the credentials PreparedStatement
   66        */
   67       private String preparedCredentials = null;
   68   
   69   
   70       /**
   71        * The name of the JNDI JDBC DataSource
   72        */
   73       protected String dataSourceName = null;
   74   
   75   
   76       /**
   77        * Descriptive information about this Realm implementation.
   78        */
   79       protected static final String info =
   80           "org.apache.catalina.realm.DataSourceRealm/1.0";
   81   
   82   
   83       /**
   84        * Context local datasource.
   85        */
   86       protected boolean localDataSource = false;
   87   
   88   
   89       /**
   90        * Descriptive information about this Realm implementation.
   91        */
   92       protected static final String name = "DataSourceRealm";
   93   
   94   
   95       /**
   96        * The column in the user role table that names a role
   97        */
   98       protected String roleNameCol = null;
   99   
  100   
  101       /**
  102        * The string manager for this package.
  103        */
  104       protected static final StringManager sm =
  105           StringManager.getManager(Constants.Package);
  106   
  107   
  108       /**
  109        * The column in the user table that holds the user's credintials
  110        */
  111       protected String userCredCol = null;
  112   
  113   
  114       /**
  115        * The column in the user table that holds the user's name
  116        */
  117       protected String userNameCol = null;
  118   
  119   
  120       /**
  121        * The table that holds the relation between user's and roles
  122        */
  123       protected String userRoleTable = null;
  124   
  125   
  126       /**
  127        * The table that holds user data.
  128        */
  129       protected String userTable = null;
  130   
  131   
  132       // ------------------------------------------------------------- Properties
  133   
  134   
  135       /**
  136        * Return the name of the JNDI JDBC DataSource.
  137        *
  138        */
  139       public String getDataSourceName() {
  140           return dataSourceName;
  141       }
  142   
  143       /**
  144        * Set the name of the JNDI JDBC DataSource.
  145        *
  146        * @param dataSourceName the name of the JNDI JDBC DataSource
  147        */
  148       public void setDataSourceName( String dataSourceName) {
  149         this.dataSourceName = dataSourceName;
  150       }
  151   
  152       /**
  153        * Return if the datasource will be looked up in the webapp JNDI Context.
  154        */
  155       public boolean getLocalDataSource() {
  156           return localDataSource;
  157       }
  158   
  159       /**
  160        * Set to true to cause the datasource to be looked up in the webapp JNDI
  161        * Context.
  162        *
  163        * @param localDataSource the new flag value
  164        */
  165       public void setLocalDataSource(boolean localDataSource) {
  166         this.localDataSource = localDataSource;
  167       }
  168   
  169       /**
  170        * Return the column in the user role table that names a role.
  171        *
  172        */
  173       public String getRoleNameCol() {
  174           return roleNameCol;
  175       }
  176   
  177       /**
  178        * Set the column in the user role table that names a role.
  179        *
  180        * @param roleNameCol The column name
  181        */
  182       public void setRoleNameCol( String roleNameCol ) {
  183           this.roleNameCol = roleNameCol;
  184       }
  185   
  186       /**
  187        * Return the column in the user table that holds the user's credentials.
  188        *
  189        */
  190       public String getUserCredCol() {
  191           return userCredCol;
  192       }
  193   
  194       /**
  195        * Set the column in the user table that holds the user's credentials.
  196        *
  197        * @param userCredCol The column name
  198        */
  199       public void setUserCredCol( String userCredCol ) {
  200          this.userCredCol = userCredCol;
  201       }
  202   
  203       /**
  204        * Return the column in the user table that holds the user's name.
  205        *
  206        */
  207       public String getUserNameCol() {
  208           return userNameCol;
  209       }
  210   
  211       /**
  212        * Set the column in the user table that holds the user's name.
  213        *
  214        * @param userNameCol The column name
  215        */
  216       public void setUserNameCol( String userNameCol ) {
  217          this.userNameCol = userNameCol;
  218       }
  219   
  220       /**
  221        * Return the table that holds the relation between user's and roles.
  222        *
  223        */
  224       public String getUserRoleTable() {
  225           return userRoleTable;
  226       }
  227   
  228       /**
  229        * Set the table that holds the relation between user's and roles.
  230        *
  231        * @param userRoleTable The table name
  232        */
  233       public void setUserRoleTable( String userRoleTable ) {
  234           this.userRoleTable = userRoleTable;
  235       }
  236   
  237       /**
  238        * Return the table that holds user data..
  239        *
  240        */
  241       public String getUserTable() {
  242           return userTable;
  243       }
  244   
  245       /**
  246        * Set the table that holds user data.
  247        *
  248        * @param userTable The table name
  249        */
  250       public void setUserTable( String userTable ) {
  251         this.userTable = userTable;
  252       }
  253   
  254   
  255       // --------------------------------------------------------- Public Methods
  256   
  257   
  258       /**
  259        * Return the Principal associated with the specified username and
  260        * credentials, if there is one; otherwise return <code>null</code>.
  261        *
  262        * If there are any errors with the JDBC connection, executing
  263        * the query or anything we return null (don't authenticate). This
  264        * event is also logged, and the connection will be closed so that
  265        * a subsequent request will automatically re-open it.
  266        *
  267        * @param username Username of the Principal to look up
  268        * @param credentials Password or other credentials to use in
  269        *  authenticating this username
  270        */
  271       public Principal authenticate(String username, String credentials) {
  272       	
  273       	// No user - can't possibly authenticate, don't bother the database then
  274       	if (username == null) {
  275       		return null;
  276       	}
  277           
  278       	Connection dbConnection = null;
  279   
  280           try {
  281   
  282               // Ensure that we have an open database connection
  283               dbConnection = open();
  284               if (dbConnection == null) {
  285                   // If the db connection open fails, return "not authenticated"
  286                   return null;
  287               }
  288               
  289               // Acquire a Principal object for this user
  290               return authenticate(dbConnection, username, credentials);
  291               
  292           } catch (SQLException e) {
  293               // Log the problem for posterity
  294               containerLog.error(sm.getString("dataSourceRealm.exception"), e);
  295   
  296               // Return "not authenticated" for this request
  297               return (null);
  298   
  299           } finally {
  300           	close(dbConnection);
  301           }
  302   
  303       }
  304   
  305   
  306       // -------------------------------------------------------- Package Methods
  307   
  308   
  309       // ------------------------------------------------------ Protected Methods
  310   
  311   
  312       /**
  313        * Return the Principal associated with the specified username and
  314        * credentials, if there is one; otherwise return <code>null</code>.
  315        *
  316        * @param dbConnection The database connection to be used
  317        * @param username Username of the Principal to look up
  318        * @param credentials Password or other credentials to use in
  319        *  authenticating this username
  320        */
  321       protected Principal authenticate(Connection dbConnection,
  322                                                  String username,
  323                                                  String credentials) throws SQLException{
  324   
  325           String dbCredentials = getPassword(dbConnection, username);
  326   
  327           // Validate the user's credentials
  328           boolean validated = false;
  329           if (hasMessageDigest()) {
  330               // Hex hashes should be compared case-insensitive
  331               validated = (digest(credentials).equalsIgnoreCase(dbCredentials));
  332           } else
  333               validated = (digest(credentials).equals(dbCredentials));
  334   
  335           if (validated) {
  336               if (containerLog.isTraceEnabled())
  337                   containerLog.trace(
  338                       sm.getString("dataSourceRealm.authenticateSuccess",
  339                                    username));
  340           } else {
  341               if (containerLog.isTraceEnabled())
  342                   containerLog.trace(
  343                       sm.getString("dataSourceRealm.authenticateFailure",
  344                                    username));
  345               return (null);
  346           }
  347   
  348           ArrayList<String> list = getRoles(dbConnection, username);
  349   
  350           // Create and return a suitable Principal for this user
  351           return (new GenericPrincipal(this, username, credentials, list));
  352   
  353       }
  354   
  355   
  356       /**
  357        * Close the specified database connection.
  358        *
  359        * @param dbConnection The connection to be closed
  360        */
  361       protected void close(Connection dbConnection) {
  362   
  363           // Do nothing if the database connection is already closed
  364           if (dbConnection == null)
  365               return;
  366   
  367           // Commit if not auto committed
  368           try {
  369               if (!dbConnection.getAutoCommit()) {
  370                   dbConnection.commit();
  371               }            
  372           } catch (SQLException e) {
  373               containerLog.error("Exception committing connection before closing:", e);
  374           }
  375   
  376           // Close this database connection, and log any errors
  377           try {
  378               dbConnection.close();
  379           } catch (SQLException e) {
  380               containerLog.error(sm.getString("dataSourceRealm.close"), e); // Just log it here
  381           }
  382   
  383       }
  384   
  385       /**
  386        * Open the specified database connection.
  387        *
  388        * @return Connection to the database
  389        */
  390       protected Connection open() {
  391   
  392           try {
  393               Context context = null;
  394               if (localDataSource) {
  395                   context = ContextBindings.getClassLoader();
  396                   context = (Context) context.lookup("comp/env");
  397               } else {
  398                   StandardServer server = 
  399                       (StandardServer) ServerFactory.getServer();
  400                   context = server.getGlobalNamingContext();
  401               }
  402               DataSource dataSource = (DataSource)context.lookup(dataSourceName);
  403   	    return dataSource.getConnection();
  404           } catch (Exception e) {
  405               // Log the problem for posterity
  406               containerLog.error(sm.getString("dataSourceRealm.exception"), e);
  407           }  
  408           return null;
  409       }
  410   
  411       /**
  412        * Return a short name for this Realm implementation.
  413        */
  414       protected String getName() {
  415   
  416           return (name);
  417   
  418       }
  419   
  420       /**
  421        * Return the password associated with the given principal's user name.
  422        */
  423       protected String getPassword(String username) {
  424   
  425           Connection dbConnection = null;
  426   
  427           // Ensure that we have an open database connection
  428           dbConnection = open();
  429           if (dbConnection == null) {
  430               return null;
  431           }
  432   
  433           try {
  434           	return getPassword(dbConnection, username);        	
  435           } finally {
  436               close(dbConnection);
  437           }
  438       }
  439       
  440       /**
  441        * Return the password associated with the given principal's user name.
  442        * @param dbConnection The database connection to be used
  443        * @param username Username for which password should be retrieved
  444        */
  445       protected String getPassword(Connection dbConnection, 
  446   								 String username) {
  447   
  448           ResultSet rs = null;
  449           PreparedStatement stmt = null;
  450           String dbCredentials = null;
  451   
  452           try {
  453               stmt = credentials(dbConnection, username);
  454               rs = stmt.executeQuery();
  455               if (rs.next()) {
  456                   dbCredentials = rs.getString(1);
  457               }
  458   
  459               return (dbCredentials != null) ? dbCredentials.trim() : null;
  460               
  461           } catch(SQLException e) {
  462               containerLog.error(
  463                       sm.getString("dataSourceRealm.getPassword.exception",
  464                                    username));
  465           } finally {
  466           	try {
  467   	            if (rs != null) {
  468   	                rs.close();
  469   	            }
  470   	            if (stmt != null) {
  471   	                stmt.close();
  472   	            }
  473           	} catch (SQLException e) {
  474                       containerLog.error(
  475                           sm.getString("dataSourceRealm.getPassword.exception",
  476           		             username));
  477           		
  478           	}
  479           }
  480           
  481           return null;
  482       }
  483   
  484   
  485       /**
  486        * Return the Principal associated with the given user name.
  487        */
  488       protected Principal getPrincipal(String username) {
  489       	Connection dbConnection = open();
  490           if (dbConnection == null) {
  491               return new GenericPrincipal(this,username, null, null);
  492           }
  493           try {
  494           	return (new GenericPrincipal(this,
  495           			username,
  496   					getPassword(dbConnection, username),
  497   					getRoles(dbConnection, username)));
  498           } finally {
  499           	close(dbConnection);
  500           }
  501   
  502       }
  503   
  504       /**
  505        * Return the roles associated with the given user name.
  506        * @param username Username for which roles should be retrieved
  507        */
  508       protected ArrayList getRoles(String username) {
  509   
  510           Connection dbConnection = null;
  511   
  512           // Ensure that we have an open database connection
  513           dbConnection = open();
  514           if (dbConnection == null) {
  515               return null;
  516           }
  517   
  518           try {
  519               return getRoles(dbConnection, username);
  520           } finally {
  521           	close(dbConnection);
  522           }
  523       }
  524       
  525       /**
  526        * Return the roles associated with the given user name
  527        * @param dbConnection The database connection to be used
  528        * @param username Username for which roles should be retrieved
  529        */
  530       protected ArrayList<String> getRoles(Connection dbConnection,
  531                                        String username) {
  532       	
  533           ResultSet rs = null;
  534           PreparedStatement stmt = null;
  535           ArrayList<String> list = null;
  536       	
  537           try {
  538       		stmt = roles(dbConnection, username);
  539       		rs = stmt.executeQuery();
  540       		list = new ArrayList<String>();
  541       		
  542       		while (rs.next()) {
  543       			String role = rs.getString(1);
  544       			if (role != null) {
  545       				list.add(role.trim());
  546       			}
  547       		}
  548       		return list;
  549       	} catch(SQLException e) {
  550               containerLog.error(
  551                   sm.getString("dataSourceRealm.getRoles.exception", username));
  552           }
  553       	finally {
  554           	try {
  555   	            if (rs != null) {
  556   	                rs.close();
  557   	            }
  558   	            if (stmt != null) {
  559   	                stmt.close();
  560   	            }
  561           	} catch (SQLException e) {
  562                       containerLog.error(
  563                           sm.getString("dataSourceRealm.getRoles.exception",
  564                                        username));
  565           	}
  566           }
  567       	
  568       	return null;
  569       }
  570   
  571       /**
  572        * Return a PreparedStatement configured to perform the SELECT required
  573        * to retrieve user credentials for the specified username.
  574        *
  575        * @param dbConnection The database connection to be used
  576        * @param username Username for which credentials should be retrieved
  577        *
  578        * @exception SQLException if a database error occurs
  579        */
  580       private PreparedStatement credentials(Connection dbConnection,
  581                                               String username)
  582           throws SQLException {
  583   
  584           PreparedStatement credentials =
  585               dbConnection.prepareStatement(preparedCredentials);
  586   
  587           credentials.setString(1, username);
  588           return (credentials);
  589   
  590       }
  591       
  592       /**
  593        * Return a PreparedStatement configured to perform the SELECT required
  594        * to retrieve user roles for the specified username.
  595        *
  596        * @param dbConnection The database connection to be used
  597        * @param username Username for which roles should be retrieved
  598        *
  599        * @exception SQLException if a database error occurs
  600        */
  601       private PreparedStatement roles(Connection dbConnection, String username)
  602           throws SQLException {
  603   
  604           PreparedStatement roles = 
  605               dbConnection.prepareStatement(preparedRoles);
  606   
  607           roles.setString(1, username);
  608           return (roles);
  609   
  610       }
  611   
  612       // ------------------------------------------------------ Lifecycle Methods
  613   
  614   
  615       /**
  616        *
  617        * Prepare for active use of the public methods of this Component.
  618        *
  619        * @exception LifecycleException if this component detects a fatal error
  620        *  that prevents it from being started
  621        */
  622       public void start() throws LifecycleException {
  623   
  624           // Perform normal superclass initialization
  625           super.start();
  626   
  627           // Create the roles PreparedStatement string
  628           StringBuffer temp = new StringBuffer("SELECT ");
  629           temp.append(roleNameCol);
  630           temp.append(" FROM ");
  631           temp.append(userRoleTable);
  632           temp.append(" WHERE ");
  633           temp.append(userNameCol);
  634           temp.append(" = ?");
  635           preparedRoles = temp.toString();
  636   
  637           // Create the credentials PreparedStatement string
  638           temp = new StringBuffer("SELECT ");
  639           temp.append(userCredCol);
  640           temp.append(" FROM ");
  641           temp.append(userTable);
  642           temp.append(" WHERE ");
  643           temp.append(userNameCol);
  644           temp.append(" = ?");
  645           preparedCredentials = temp.toString();
  646       }
  647   
  648   
  649       /**
  650        * Gracefully shut down active use of the public methods of this Component.
  651        *
  652        * @exception LifecycleException if this component detects a fatal error
  653        *  that needs to be reported
  654        */
  655       public void stop() throws LifecycleException {
  656   
  657           // Perform normal superclass finalization
  658           super.stop();
  659   
  660       }
  661   
  662   
  663   }

Save This Page
Home » apache-tomcat-6.0.16-src » org.apache » catalina » realm » [javadoc | source]