001: // ========================================================================
002: // $Id: JDBCUserRealm.java 1180 2006-11-08 19:47:06Z janb $
003: // Copyright 2003-2004 Mort Bay Consulting Pty. Ltd.
004: // ------------------------------------------------------------------------
005: // Licensed under the Apache License, Version 2.0 (the "License");
006: // you may not use this file except in compliance with the License.
007: // You may obtain a copy of the License at
008: // http://www.apache.org/licenses/LICENSE-2.0
009: // Unless required by applicable law or agreed to in writing, software
010: // distributed under the License is distributed on an "AS IS" BASIS,
011: // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012: // See the License for the specific language governing permissions and
013: // limitations under the License.
014: // ========================================================================
015:
016: package org.mortbay.jetty.security;
017:
018: import java.io.IOException;
019: import java.security.Principal;
020: import java.sql.Connection;
021: import java.sql.DriverManager;
022: import java.sql.PreparedStatement;
023: import java.sql.ResultSet;
024: import java.sql.SQLException;
025: import java.util.Properties;
026:
027: import org.mortbay.jetty.Request;
028: import org.mortbay.log.Log;
029: import org.mortbay.resource.Resource;
030: import org.mortbay.util.Loader;
031:
032: /* ------------------------------------------------------------ */
033: /** HashMapped User Realm with JDBC as data source.
034: * JDBCUserRealm extends HashUserRealm and adds a method to fetch user
035: * information from database.
036: * The authenticate() method checks the inherited HashMap for the user.
037: * If the user is not found, it will fetch details from the database
038: * and populate the inherited HashMap. It then calls the HashUserRealm
039: * authenticate() method to perform the actual authentication.
040: * Periodically (controlled by configuration parameter), internal
041: * hashes are cleared. Caching can be disabled by setting cache
042: * refresh interval to zero.
043: * Uses one database connection that is initialized at startup. Reconnect
044: * on failures. authenticate() is 'synchronized'.
045: *
046: * An example properties file for configuration is in
047: * $JETTY_HOME/etc/jdbcRealm.properties
048: *
049: * @version $Id: JDBCUserRealm.java 1180 2006-11-08 19:47:06Z janb $
050: * @author Arkadi Shishlov (arkadi)
051: * @author Fredrik Borgh
052: * @author Greg Wilkins (gregw)
053: * @author Ben Alex
054: */
055:
056: public class JDBCUserRealm extends HashUserRealm implements UserRealm {
057:
058: private String _jdbcDriver;
059: private String _url;
060: private String _userName;
061: private String _password;
062: private String _userTable;
063: private String _userTableKey;
064: private String _userTableUserField;
065: private String _userTablePasswordField;
066: private String _roleTable;
067: private String _roleTableKey;
068: private String _roleTableRoleField;
069: private String _userRoleTable;
070: private String _userRoleTableUserKey;
071: private String _userRoleTableRoleKey;
072: private int _cacheTime;
073:
074: private long _lastHashPurge;
075: private Connection _con;
076: private String _userSql;
077: private String _roleSql;
078:
079: /* ------------------------------------------------------------ */
080: /** Constructor.
081: */
082: public JDBCUserRealm() {
083: super ();
084: }
085:
086: /* ------------------------------------------------------------ */
087: /** Constructor.
088: * @param name
089: */
090: public JDBCUserRealm(String name) {
091: super (name);
092: }
093:
094: /* ------------------------------------------------------------ */
095: /** Constructor.
096: * @param name Realm name
097: * @param config Filename or url of JDBC connection properties file.
098: * @exception IOException
099: * @exception ClassNotFoundException
100: */
101: public JDBCUserRealm(String name, String config)
102: throws IOException, ClassNotFoundException,
103: InstantiationException, IllegalAccessException {
104: super (name);
105: setConfig(config);
106: Loader.loadClass(this .getClass(), _jdbcDriver).newInstance();
107: connectDatabase();
108: }
109:
110: public String getName() {
111: return super .getName();
112: }
113:
114: public void setName(String name) {
115: super .setName(name);
116: }
117:
118: public String getConfig() {
119: return super .getConfig();
120: }
121:
122: /* ------------------------------------------------------------ */
123: /** Load JDBC connection configuration from properties file.
124: *
125: * @param config Filename or url of user properties file.
126: * @exception IOException
127: */
128: public void setConfig(String config) throws IOException {
129: super .setConfig(config);
130: Properties properties = new Properties();
131: Resource resource = Resource.newResource(config);
132: properties.load(resource.getInputStream());
133:
134: _jdbcDriver = properties.getProperty("jdbcdriver");
135: _url = properties.getProperty("url");
136: _userName = properties.getProperty("username");
137: _password = properties.getProperty("password");
138: _userTable = properties.getProperty("usertable");
139: _userTableKey = properties.getProperty("usertablekey");
140: _userTableUserField = properties
141: .getProperty("usertableuserfield");
142: _userTablePasswordField = properties
143: .getProperty("usertablepasswordfield");
144: _roleTable = properties.getProperty("roletable");
145: _roleTableKey = properties.getProperty("roletablekey");
146: _roleTableRoleField = properties
147: .getProperty("roletablerolefield");
148: _userRoleTable = properties.getProperty("userroletable");
149: _userRoleTableUserKey = properties
150: .getProperty("userroletableuserkey");
151: _userRoleTableRoleKey = properties
152: .getProperty("userroletablerolekey");
153: _cacheTime = new Integer(properties.getProperty("cachetime"))
154: .intValue();
155:
156: if (_jdbcDriver == null || _jdbcDriver.equals("")
157: || _url == null || _url.equals("") || _userName == null
158: || _userName.equals("") || _password == null
159: || _cacheTime < 0) {
160: if (Log.isDebugEnabled())
161: Log.debug("UserRealm " + getName()
162: + " has not been properly configured");
163: }
164: _cacheTime *= 1000;
165: _lastHashPurge = 0;
166: _userSql = "select " + _userTableKey + ","
167: + _userTablePasswordField + " from " + _userTable
168: + " where " + _userTableUserField + " = ?";
169: _roleSql = "select r." + _roleTableRoleField + " from "
170: + _roleTable + " r, " + _userRoleTable + " u where u."
171: + _userRoleTableUserKey + " = ?" + " and r."
172: + _roleTableKey + " = u." + _userRoleTableRoleKey;
173: }
174:
175: /* ------------------------------------------------------------ */
176: public void logout(Principal user) {
177: }
178:
179: /* ------------------------------------------------------------ */
180: /** (re)Connect to database with parameters setup by loadConfig()
181: */
182: public void connectDatabase() {
183: try {
184: Class.forName(_jdbcDriver);
185: _con = DriverManager.getConnection(_url, _userName,
186: _password);
187: } catch (SQLException e) {
188: Log.warn("UserRealm " + getName()
189: + " could not connect to database; will try later",
190: e);
191: } catch (ClassNotFoundException e) {
192: Log.warn("UserRealm " + getName()
193: + " could not connect to database; will try later",
194: e);
195: }
196: }
197:
198: /* ------------------------------------------------------------ */
199: public Principal authenticate(String username, Object credentials,
200: Request request) {
201: synchronized (this ) {
202: long now = System.currentTimeMillis();
203: if (now - _lastHashPurge > _cacheTime || _cacheTime == 0) {
204: _users.clear();
205: _roles.clear();
206: _lastHashPurge = now;
207: }
208: Principal user = super .getPrincipal(username);
209: if (user == null) {
210: loadUser(username);
211: user = super .getPrincipal(username);
212: }
213: }
214: return super .authenticate(username, credentials, request);
215: }
216:
217: /* ------------------------------------------------------------ */
218: private void loadUser(String username) {
219: try {
220: if (null == _con)
221: connectDatabase();
222:
223: if (null == _con)
224: throw new SQLException("Can't connect to database");
225:
226: PreparedStatement stat = _con.prepareStatement(_userSql);
227: stat.setObject(1, username);
228: ResultSet rs = stat.executeQuery();
229:
230: if (rs.next()) {
231: Object key = rs.getObject(_userTableKey);
232: put(username, rs.getString(_userTablePasswordField));
233: stat.close();
234:
235: stat = _con.prepareStatement(_roleSql);
236: stat.setObject(1, key);
237: rs = stat.executeQuery();
238:
239: while (rs.next())
240: addUserToRole(username, rs
241: .getString(_roleTableRoleField));
242:
243: stat.close();
244: }
245: } catch (SQLException e) {
246: Log.warn("UserRealm " + getName()
247: + " could not load user information from database",
248: e);
249: connectDatabase();
250: }
251: }
252: }
|