KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > catalina > realm > DataSourceRealm


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 JavaDoc;
23 import java.sql.Connection JavaDoc;
24 import java.sql.PreparedStatement JavaDoc;
25 import java.sql.ResultSet JavaDoc;
26 import java.sql.SQLException JavaDoc;
27 import java.util.ArrayList JavaDoc;
28
29 import javax.naming.Context JavaDoc;
30 import javax.sql.DataSource JavaDoc;
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: 467222 $
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 JavaDoc preparedRoles = null;
62
63
64     /**
65      * The generated string for the credentials PreparedStatement
66      */

67     private String JavaDoc preparedCredentials = null;
68
69
70     /**
71      * The name of the JNDI JDBC DataSource
72      */

73     protected String JavaDoc dataSourceName = null;
74
75
76     /**
77      * Descriptive information about this Realm implementation.
78      */

79     protected static final String JavaDoc 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 JavaDoc name = "DataSourceRealm";
93
94
95     /**
96      * The column in the user role table that names a role
97      */

98     protected String JavaDoc 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 JavaDoc userCredCol = null;
112
113
114     /**
115      * The column in the user table that holds the user's name
116      */

117     protected String JavaDoc userNameCol = null;
118
119
120     /**
121      * The table that holds the relation between user's and roles
122      */

123     protected String JavaDoc userRoleTable = null;
124
125
126     /**
127      * The table that holds user data.
128      */

129     protected String JavaDoc userTable = null;
130
131
132     // ------------------------------------------------------------- Properties
133

134
135     /**
136      * Return the name of the JNDI JDBC DataSource.
137      *
138      */

139     public String JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc userRoleTable ) {
234         this.userRoleTable = userRoleTable;
235     }
236
237     /**
238      * Return the table that holds user data..
239      *
240      */

241     public String JavaDoc 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 JavaDoc 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 JavaDoc authenticate(String JavaDoc username, String JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc authenticate(Connection JavaDoc dbConnection,
322                                                String JavaDoc username,
323                                                String JavaDoc credentials) throws SQLException JavaDoc{
324
325         String JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc open() {
391
392         try {
393             Context JavaDoc context = null;
394             if (localDataSource) {
395                 context = ContextBindings.getClassLoader();
396                 context = (Context JavaDoc) context.lookup("comp/env");
397             } else {
398                 StandardServer server =
399                     (StandardServer) ServerFactory.getServer();
400                 context = server.getGlobalNamingContext();
401             }
402             DataSource JavaDoc dataSource = (DataSource JavaDoc)context.lookup(dataSourceName);
403         return dataSource.getConnection();
404         } catch (Exception JavaDoc 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 JavaDoc 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 JavaDoc getPassword(String JavaDoc username) {
424
425         Connection JavaDoc 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 JavaDoc getPassword(Connection JavaDoc dbConnection,
446                                  String JavaDoc username) {
447
448         ResultSet JavaDoc rs = null;
449         PreparedStatement JavaDoc stmt = null;
450         String JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc getPrincipal(String JavaDoc username) {
489         Connection JavaDoc 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 JavaDoc getRoles(String JavaDoc username) {
509
510         Connection JavaDoc 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 JavaDoc getRoles(Connection JavaDoc dbConnection,
531                                      String JavaDoc username) {
532         
533         ResultSet JavaDoc rs = null;
534         PreparedStatement JavaDoc stmt = null;
535         ArrayList JavaDoc list = null;
536         
537         try {
538             stmt = roles(dbConnection, username);
539             rs = stmt.executeQuery();
540             list = new ArrayList JavaDoc();
541             
542             while (rs.next()) {
543                 String JavaDoc role = rs.getString(1);
544                 if (role != null) {
545                     list.add(role.trim());
546                 }
547             }
548             return list;
549         } catch(SQLException JavaDoc 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 JavaDoc 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 JavaDoc credentials(Connection JavaDoc dbConnection,
581                                             String JavaDoc username)
582         throws SQLException JavaDoc {
583
584         PreparedStatement JavaDoc 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 JavaDoc roles(Connection JavaDoc dbConnection, String JavaDoc username)
602         throws SQLException JavaDoc {
603
604         PreparedStatement JavaDoc 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 JavaDoc temp = new StringBuffer JavaDoc("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 JavaDoc("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 }
664
Popular Tags