001: /**
002: * JOnAS: Java(TM) Open Application Server
003: * Copyright (C) 1999-2004 Bull S.A.
004: * Contact: jonas-team@objectweb.org
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation; either
009: * version 2.1 of the License, or any later version.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: *
016: * You should have received a copy of the GNU Lesser General Public
017: * License along with this library; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
019: * USA
020: *
021: * Initial developer(s): Florent BENOIT & Ludovic BERT
022: * --------------------------------------------------------------------------
023: * $Id: JResourceDS.java 7977 2006-02-07 08:58:26Z benoitf $
024: * --------------------------------------------------------------------------
025: */package org.objectweb.jonas.security.realm.factory;
026:
027: import java.security.NoSuchAlgorithmException;
028: import java.sql.Connection;
029: import java.sql.DriverManager;
030: import java.sql.PreparedStatement;
031: import java.sql.ResultSet;
032: import java.sql.SQLException;
033: import java.util.ArrayList;
034:
035: import javax.naming.Context;
036: import javax.naming.InitialContext;
037: import javax.naming.NamingException;
038: import javax.naming.Reference;
039: import javax.naming.StringRefAddr;
040: import javax.sql.DataSource;
041:
042: import org.objectweb.jonas.dbm.ConnectionManager;
043: import org.objectweb.jonas.security.realm.lib.HashHelper;
044: import org.objectweb.jonas.security.realm.principals.User;
045:
046: import org.objectweb.util.monolog.api.BasicLevel;
047:
048: /**
049: * This class extends the JResource class for the Datasource implementation.
050: * @author Florent Benoit
051: */
052: public class JResourceDS extends JResource implements JResourceDSMBean {
053:
054: /**
055: * Type of the factory
056: */
057: private static final String FACTORY_TYPE = "org.objectweb.jonas.security.realm.factory.JResourceDS";
058:
059: /**
060: * Name of the factory
061: */
062: private static final String FACTORY_NAME = "org.objectweb.jonas.security.realm.factory.JResourceDSFactory";
063:
064: /**
065: * Name of the datasource resource to use.
066: */
067: private String dsName = null;
068:
069: /**
070: * Name of table which have the username/password
071: */
072: private String userTable = null;
073:
074: /**
075: * Column of the username of the user table
076: */
077: private String userTableUsernameCol = null;
078:
079: /**
080: * Column of the password of the user table
081: */
082: private String userTablePasswordCol = null;
083:
084: /**
085: * Name of table which have the username/role
086: */
087: private String roleTable = null;
088:
089: /**
090: * Column of the username of the role table
091: */
092: private String roleTableUsernameCol = null;
093:
094: /**
095: * Column of the role of the role table
096: */
097: private String roleTableRolenameCol = null;
098:
099: /**
100: * Default algorithm. If specified, the default is not 'clear' password
101: */
102: private String algorithm = null;
103:
104: /**
105: * Datasource to use
106: */
107: private DataSource dataSource = null;
108:
109: /**
110: * User defined query for retrieving principals
111: */
112: private String userPrincipalsQuery = null;
113:
114: /**
115: * User defined query for retrieving roles
116: */
117: private String userRolesQuery = null;
118:
119: /**
120: * Constructor. Use the super constructor
121: * @throws Exception if super constructor fail
122: */
123: public JResourceDS() throws Exception {
124: super ();
125:
126: }
127:
128: /**
129: * Set the name of the resource to use
130: * @param dsName name of the resource
131: */
132: public void setDsName(String dsName) {
133: this .dsName = dsName;
134: }
135:
136: /**
137: * Set the name of the table which have the username/password
138: * @param userTable name of the table which have the username/password
139: */
140: public void setUserTable(String userTable) {
141: this .userTable = userTable;
142: }
143:
144: /**
145: * Set the name of the column of the username of the user table
146: * @param userTableUsernameCol name of the column of the username of the
147: * user table
148: */
149: public void setUserTableUsernameCol(String userTableUsernameCol) {
150: this .userTableUsernameCol = userTableUsernameCol;
151: }
152:
153: /**
154: * Set the name of column of the password of the user table
155: * @param userTablePasswordCol name of column of the password of the user
156: * table
157: */
158: public void setUserTablePasswordCol(String userTablePasswordCol) {
159: this .userTablePasswordCol = userTablePasswordCol;
160: }
161:
162: /**
163: * Set the name of table which have the username/role
164: * @param roleTable name of table which have the username/role
165: */
166: public void setRoleTable(String roleTable) {
167: this .roleTable = roleTable;
168: }
169:
170: /**
171: * Set the name of the column of the username of the role table
172: * @param roleTableUsernameCol name of the column of the username of the
173: * role table
174: */
175: public void setRoleTableUsernameCol(String roleTableUsernameCol) {
176: this .roleTableUsernameCol = roleTableUsernameCol;
177: }
178:
179: /**
180: * Set the name of the column of the role of the role table
181: * @param roleTableRolenameCol name of the column of the role of the role
182: * table
183: */
184: public void setRoleTableRolenameCol(String roleTableRolenameCol) {
185: this .roleTableRolenameCol = roleTableRolenameCol;
186: }
187:
188: /**
189: * Set the default algorithm to use
190: * @param algorithm algorithm to be used
191: */
192: public void setAlgorithm(String algorithm) {
193: this .algorithm = algorithm;
194: }
195:
196: /**
197: * Set the user defined query for retrieving principals
198: * @param userPrincipalsQuery the user defined query for retrieving
199: * principals
200: */
201: public void setUserPrincipalsQuery(String userPrincipalsQuery) {
202: this .userPrincipalsQuery = userPrincipalsQuery;
203: }
204:
205: /**
206: * Set the user defined query for retrieving roles
207: * @param userRolesQuery the user defined query for retrieving roles
208: */
209: public void setUserRolesQuery(String userRolesQuery) {
210: this .userRolesQuery = userRolesQuery;
211: }
212:
213: /**
214: * Get the name of the resource to use
215: * @return name of the resource
216: */
217: public String getDsName() {
218: return dsName;
219: }
220:
221: /**
222: * Get the name of the table which have the username/password
223: * @return name of the table which have the username/password
224: */
225: public String getUserTable() {
226: return userTable;
227: }
228:
229: /**
230: * Get the name of the column of the username of the user table
231: * @return name of the column of the username of the user table
232: */
233: public String getUserTableUsernameCol() {
234: return userTableUsernameCol;
235: }
236:
237: /**
238: * Get the name of column of the password of the user table
239: * @return name of column of the password of the user table
240: */
241: public String getUserTablePasswordCol() {
242: return userTablePasswordCol;
243: }
244:
245: /**
246: * Get the name of table which have the username/role
247: * @return name of table which have the username/role
248: */
249: public String getRoleTable() {
250: return roleTable;
251: }
252:
253: /**
254: * Get the name of the column of the username of the role table
255: * @return name of the column of the username of the role table
256: */
257: public String getRoleTableUsernameCol() {
258: return roleTableUsernameCol;
259: }
260:
261: /**
262: * Get the name of the column of the role of the role table
263: * @return name of the column of the role of the role table
264: */
265: public String getRoleTableRolenameCol() {
266: return roleTableRolenameCol;
267: }
268:
269: /**
270: * Get the default algorithm
271: * @return the default algorithm
272: */
273: public String getAlgorithm() {
274: return algorithm;
275: }
276:
277: /**
278: * Gets the user defined query for retrieving principals
279: * @return the user defined query for retrieving principals
280: */
281: public String setUserPrincipalsQuery() {
282: return userPrincipalsQuery;
283: }
284:
285: /**
286: * Gets the user defined query for retrieving roles
287: * @return the user defined query for retrieving roles
288: */
289: public String setUserRolesQuery() {
290: return userRolesQuery;
291: }
292:
293: /**
294: * Check if a user is found and return it
295: * @param username the wanted user name
296: * @return the user found or null
297: * @throws JResourceException if there is a SQLException
298: */
299: public User findUser(String username) throws JResourceException {
300:
301: if (username == null) {
302: return null;
303: }
304:
305: // Build new user
306: User user = new User();
307:
308: Connection connection = getConnection();
309:
310: user.setName(username);
311:
312: // Get the password of the user
313: ResultSet rs = null;
314: String password = null;
315: try {
316: PreparedStatement usrStmt = userStatement(connection,
317: username);
318: rs = usrStmt.executeQuery();
319: if (rs == null || !rs.next()) {
320: throw new JResourceException(
321: "No user found with username '" + username
322: + "'.");
323: }
324:
325: int records = rs.getRow();
326: if (records > 1) {
327: getLogger().log(
328: BasicLevel.ERROR,
329: "There are more than one user with the name"
330: + username);
331: }
332: password = rs.getString(1).trim();
333: rs.close();
334: usrStmt.close();
335: } catch (SQLException sqle) {
336: closeConnection(connection);
337: throw new JResourceException(sqle.getMessage(), sqle);
338: }
339:
340: if (password == null) {
341: closeConnection(connection);
342: return null;
343: }
344: user.setPassword(password);
345:
346: // Get the roles of the user
347: try {
348: PreparedStatement rlStmt = roleStatement(connection,
349: username);
350: rs = rlStmt.executeQuery();
351:
352: while (rs.next()) {
353: String role = rs.getString(1).trim();
354: user.addRole(role);
355: }
356: rs.close();
357: rlStmt.close();
358: } catch (SQLException sqle) {
359: closeConnection(connection);
360: throw new JResourceException(sqle.getMessage(), sqle);
361: }
362:
363: // Commit the connection if it is not automatic
364: try {
365: if (!connection.getAutoCommit()) {
366: connection.commit();
367: }
368: } catch (SQLException sqle) {
369: if (getLogger().isLoggable(BasicLevel.DEBUG)) {
370: getLogger().log(
371: BasicLevel.DEBUG,
372: "Cannot commit on the current connection : : '"
373: + sqle.getMessage() + "'");
374: }
375: }
376: closeConnection(connection);
377: return user;
378: }
379:
380: /**
381: * Check if the given credential is the right credential for the given user
382: * @param user user to check its credentials
383: * @param credentials the given credentials
384: * @return true if the credential is valid for this user
385: */
386: public boolean isValidUser(User user, String credentials) {
387:
388: boolean validated = false;
389:
390: //Get algorithm and hashpassword
391: String pass = user.getHashPassword().getPassword();
392: String algo = user.getHashPassword().getAlgorithm();
393:
394: // Crypt password ?
395: if (algo != null) {
396: try {
397: validated = HashHelper.hashPassword(credentials, algo)
398: .equalsIgnoreCase(pass);
399: } catch (NoSuchAlgorithmException nsae) {
400: getLogger().log(
401: BasicLevel.ERROR,
402: "Can't make a password with the algorithm "
403: + algo + ". " + nsae.getMessage());
404: }
405: } else if ((algorithm != null) && (!algorithm.equals(""))) {
406: // Encode password with the specified algorithm (no clear)
407: try {
408: validated = HashHelper.hashPassword(credentials,
409: algorithm).equalsIgnoreCase(pass);
410: } catch (NoSuchAlgorithmException nsae) {
411: getLogger().log(
412: BasicLevel.ERROR,
413: "Can't make a password with the algorithm "
414: + algorithm + ". " + nsae.getMessage());
415: }
416: } else {
417: // clear password
418: validated = credentials.equals(pass);
419: }
420: return validated;
421: }
422:
423: /**
424: * Get all the roles (from the roles and from the groups) of the given user
425: * @param user the given user
426: * @return the array list of all the roles for a given user
427: * @throws JResourceException if it fails
428: */
429: public ArrayList getArrayListCombinedRoles(User user)
430: throws JResourceException {
431:
432: ArrayList allCombinedRoles = new ArrayList();
433:
434: // Return empty array if user null
435: if (user == null) {
436: return allCombinedRoles;
437: }
438: // Add all user roles
439: String[] userRoles = user.getArrayRoles();
440: for (int r = 0; r < userRoles.length; r++) {
441: String roleName = userRoles[r];
442: if (!allCombinedRoles.contains(roleName)) {
443: allCombinedRoles.add(roleName);
444: }
445: }
446: user.setCombinedRoles(allCombinedRoles);
447:
448: return allCombinedRoles;
449: }
450:
451: /**
452: * String representation of the MemoryRealm
453: * @return the xml representation of the MemoryRealm
454: */
455: public String toXML() {
456: StringBuffer xml = new StringBuffer(" <dsrealm name=\"");
457: xml.append(getName());
458: xml.append("\"\n dsName=\"");
459: if (dsName != null) {
460: xml.append(dsName);
461: }
462: xml.append("\"\n userTable=\"");
463: if (userTable != null) {
464: xml.append(userTable);
465: }
466: xml.append("\" userTableUsernameCol=\"");
467: if (userTableUsernameCol != null) {
468: xml.append(userTableUsernameCol);
469: }
470: xml.append("\" userTablePasswordCol=\"");
471: if (userTablePasswordCol != null) {
472: xml.append(userTablePasswordCol);
473: }
474: xml.append("\"\n roleTable=\"");
475: if (roleTable != null) {
476: xml.append(roleTable);
477: }
478: xml.append("\" roleTableUsernameCol=\"");
479: if (roleTableUsernameCol != null) {
480: xml.append(roleTableUsernameCol);
481: }
482: xml.append("\" roleTableRolenameCol=\"");
483: if (roleTableRolenameCol != null) {
484: xml.append(roleTableRolenameCol);
485: }
486:
487: if ((userPrincipalsQuery != null)
488: && (!userPrincipalsQuery.equals(""))) {
489: xml.append("\"\n userPrincipalsQuery=\"");
490: xml.append(userPrincipalsQuery);
491: }
492:
493: if ((userRolesQuery != null) && (!userRolesQuery.equals(""))) {
494: xml.append("\"\n userRolesQuery=\"");
495: xml.append(userRolesQuery);
496: }
497:
498: if ((algorithm != null) && (!algorithm.equals(""))) {
499: xml.append("\"\n algorithm=\"");
500: xml.append(algorithm);
501: }
502:
503: xml.append("\" />");
504: return xml.toString();
505: }
506:
507: /**
508: * The string representation of this realm is the XML
509: * @return XML representation
510: */
511: public String toString() {
512: return this .toXML();
513: }
514:
515: /**
516: * Retrieves the Reference of the object. The Reference contains the factory
517: * used to create this object and the optional parameters used to configure
518: * the factory.
519: * @return the non-null Reference of the object.
520: * @throws NamingException if a naming exception was encountered while
521: * retrieving the reference.
522: */
523: public Reference getReference() throws NamingException {
524:
525: // Build the reference to the factory FACTORY_TYPE
526: Reference reference = new Reference(FACTORY_TYPE, FACTORY_NAME,
527: null);
528:
529: // Add ref addr
530: reference.add(new StringRefAddr("name", getName()));
531: reference.add(new StringRefAddr("dsName", dsName));
532: reference.add(new StringRefAddr("userTable", userTable));
533: reference.add(new StringRefAddr("userTableUsernameCol",
534: userTableUsernameCol));
535: reference.add(new StringRefAddr("userTablePasswordCol",
536: userTablePasswordCol));
537: reference.add(new StringRefAddr("roleTable", roleTable));
538: reference.add(new StringRefAddr("roleTableUsernameCol",
539: roleTableUsernameCol));
540: reference.add(new StringRefAddr("roleTableRolenameCol",
541: roleTableRolenameCol));
542: reference.add(new StringRefAddr("userPrincipalsQuery",
543: userPrincipalsQuery));
544: reference.add(new StringRefAddr("userRolesQuery",
545: userRolesQuery));
546: reference.add(new StringRefAddr("algorithm", algorithm));
547:
548: return reference;
549: }
550:
551: /**
552: * Try to close the given connection
553: * @param c the connection to close
554: */
555: private void closeConnection(Connection c) {
556: if (c == null) {
557: return;
558: }
559: try {
560: c.close();
561: } catch (Exception e) {
562: if (getLogger().isLoggable(BasicLevel.DEBUG)) {
563: getLogger().log(BasicLevel.DEBUG,
564: "Can not close the connection");
565: }
566: }
567:
568: }
569:
570: /**
571: * Get a connection from the dataSource
572: * @return the connection from the dataSource
573: * @throws JResourceException if an SQLException is thrown by
574: * dataSource.getConnection()
575: */
576: private Connection getConnection() throws JResourceException {
577:
578: // If no dataSource, get an instance
579: if (dataSource == null) {
580: // Finds DataSource from JNDI
581: Context initialContext = null;
582: try {
583: initialContext = new InitialContext();
584: dataSource = (DataSource) initialContext.lookup(dsName);
585: } catch (Exception e) {
586: String err = "Cannot find resource " + dsName
587: + " in the registry " + e.getMessage();
588: getLogger().log(BasicLevel.ERROR, err);
589: throw new JResourceException(err, e);
590: }
591: }
592:
593: Connection c = null;
594:
595: // Check if the request come from a client or inside the server
596: // Required until a dbm datasource can be called from a client
597: if (dataSource instanceof ConnectionManager) {
598: ConnectionManager cm = (ConnectionManager) dataSource;
599: // Client or server case ?
600: if (cm.isClientCase()) {
601: // Build a JDBC connection with getting parameters from the object
602: // ConnectionManager
603: try {
604: // Load driver
605: Class.forName(cm.getClassName());
606: // create the connection
607: c = DriverManager.getConnection(cm.getUrl(), cm
608: .getUserName(), cm.getPassword());
609: } catch (Exception de) {
610: getLogger().log(BasicLevel.ERROR, de.getMessage());
611: throw new JResourceException(
612: "Cannot build a connection using the jdbc parameters : "
613: + de.getMessage(), de);
614: }
615: }
616: }
617:
618: if (c == null) {
619: //Use the datasource
620: // Retrieve connection from the datasource
621: try {
622: c = dataSource.getConnection();
623: } catch (SQLException sqle) {
624: getLogger().log(BasicLevel.ERROR, sqle.getMessage());
625: throw new JResourceException(sqle.getMessage(), sqle);
626: }
627:
628: }
629:
630: return c;
631: }
632:
633: /**
634: * Return the user query. It select the password for a specific user
635: * @return the user query
636: */
637: private String userQuery() {
638:
639: if (userPrincipalsQuery != null) {
640: if (getLogger().isLoggable(BasicLevel.DEBUG)) {
641: getLogger().log(
642: BasicLevel.DEBUG,
643: "Return user defined SQL query for user"
644: + userPrincipalsQuery);
645: }
646: return userPrincipalsQuery;
647: } else {
648:
649: /*
650: * SELECT userTablePasswordCol FROM userTable WHERE
651: * userTableUsernameCol = ?
652: */
653: StringBuffer stringBuffer = new StringBuffer("SELECT ");
654: stringBuffer.append(userTablePasswordCol);
655: stringBuffer.append(" FROM ");
656: stringBuffer.append(userTable);
657: stringBuffer.append(" WHERE ");
658: stringBuffer.append(userTableUsernameCol);
659: stringBuffer.append(" = ?");
660: return (stringBuffer.toString());
661: }
662: }
663:
664: /**
665: * Return the roles query. It select the roles for a specific user
666: * @return the roles query
667: */
668: private String rolesOfUserQuery() {
669:
670: if (userRolesQuery != null) {
671: if (getLogger().isLoggable(BasicLevel.DEBUG)) {
672: getLogger().log(
673: BasicLevel.DEBUG,
674: "Return user defined SQL query for roles"
675: + userRolesQuery);
676: }
677: return userRolesQuery;
678: } else {
679:
680: /*
681: * SELECT r.roleTableRolenameCol FROM userTable u, roleTable r WHERE
682: * u.userTableUsernameCol = r.roleTableUsernameCol AND
683: * u.userTableUsernameCol = ?
684: */
685:
686: StringBuffer stringBuffer = new StringBuffer("SELECT r.");
687: stringBuffer.append(roleTableRolenameCol);
688: stringBuffer.append(" FROM ");
689: stringBuffer.append(userTable);
690: stringBuffer.append(" u, ");
691: stringBuffer.append(roleTable);
692: stringBuffer.append(" r WHERE u.");
693: stringBuffer.append(userTableUsernameCol);
694: stringBuffer.append(" = r.");
695: stringBuffer.append(roleTableUsernameCol);
696: stringBuffer.append(" AND u.");
697: stringBuffer.append(userTableUsernameCol);
698: stringBuffer.append(" = ?");
699: return stringBuffer.toString();
700: }
701: }
702:
703: /**
704: * Return a statement for the given username by using the userQuery query
705: * @param connection connection to use
706: * @param username the given user
707: * @return a statement for the given user
708: * @throws SQLException if the SQL statement fails
709: */
710: private PreparedStatement userStatement(Connection connection,
711: String username) throws SQLException {
712:
713: if (getLogger().isLoggable(BasicLevel.DEBUG)) {
714: getLogger().log(
715: BasicLevel.DEBUG,
716: "Creating user statement for the user '" + username
717: + "'");
718: }
719:
720: PreparedStatement userStatement = connection
721: .prepareStatement(userQuery());
722:
723: userStatement.setString(1, username);
724: return userStatement;
725: }
726:
727: /**
728: * Return a statement for the given username by using the rolesOfUserQuery
729: * query
730: * @param connection connection to use
731: * @param username the given user
732: * @return a roles of user statement for the given user
733: * @throws SQLException if the SQL statement fails
734: */
735: private PreparedStatement roleStatement(Connection connection,
736: String username) throws SQLException {
737:
738: if (getLogger().isLoggable(BasicLevel.DEBUG)) {
739: getLogger().log(
740: BasicLevel.DEBUG,
741: "Creating role statement for the user " + username
742: + "'");
743: }
744: PreparedStatement roleStatement = connection
745: .prepareStatement(rolesOfUserQuery());
746:
747: roleStatement.setString(1, username);
748:
749: return roleStatement;
750: }
751:
752: /**
753: * Remove all the Mbeans used by this resource
754: * @throws JResourceException if the MBeans can not be removed
755: */
756: public void removeMBeans() throws JResourceException {
757: //no MBeans
758: }
759:
760: }
|