]> WPIA git - gigi.git/blob - lib/jetty/org/eclipse/jetty/security/JDBCLoginService.java
Importing upstream Jetty jetty-9.2.1.v20140609
[gigi.git] / lib / jetty / org / eclipse / jetty / security / JDBCLoginService.java
1 //
2 //  ========================================================================
3 //  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
4 //  ------------------------------------------------------------------------
5 //  All rights reserved. This program and the accompanying materials
6 //  are made available under the terms of the Eclipse Public License v1.0
7 //  and Apache License v2.0 which accompanies this distribution.
8 //
9 //      The Eclipse Public License is available at
10 //      http://www.eclipse.org/legal/epl-v10.html
11 //
12 //      The Apache License v2.0 is available at
13 //      http://www.opensource.org/licenses/apache2.0.php
14 //
15 //  You may elect to redistribute this code under either of these licenses.
16 //  ========================================================================
17 //
18
19 package org.eclipse.jetty.security;
20
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.sql.Connection;
24 import java.sql.DriverManager;
25 import java.sql.PreparedStatement;
26 import java.sql.ResultSet;
27 import java.sql.SQLException;
28 import java.util.ArrayList;
29 import java.util.List;
30 import java.util.Properties;
31
32 import org.eclipse.jetty.server.UserIdentity;
33 import org.eclipse.jetty.util.Loader;
34 import org.eclipse.jetty.util.log.Log;
35 import org.eclipse.jetty.util.log.Logger;
36 import org.eclipse.jetty.util.resource.Resource;
37 import org.eclipse.jetty.util.security.Credential;
38
39 /* ------------------------------------------------------------ */
40 /**
41  * HashMapped User Realm with JDBC as data source. JDBCLoginService extends
42  * HashULoginService and adds a method to fetch user information from database.
43  * The login() method checks the inherited Map for the user. If the user is not
44  * found, it will fetch details from the database and populate the inherited
45  * Map. It then calls the superclass login() method to perform the actual
46  * authentication. Periodically (controlled by configuration parameter),
47  * internal hashes are cleared. Caching can be disabled by setting cache refresh
48  * interval to zero. Uses one database connection that is initialized at
49  * startup. Reconnect on failures. authenticate() is 'synchronized'.
50  * 
51  * An example properties file for configuration is in
52  * $JETTY_HOME/etc/jdbcRealm.properties
53  * 
54  * @version $Id: JDBCLoginService.java 4792 2009-03-18 21:55:52Z gregw $
55  * 
56  * 
57  * 
58  * 
59  */
60
61 public class JDBCLoginService extends MappedLoginService
62 {
63     private static final Logger LOG = Log.getLogger(JDBCLoginService.class);
64
65     protected String _config;
66     protected String _jdbcDriver;
67     protected String _url;
68     protected String _userName;
69     protected String _password;
70     protected String _userTableKey;
71     protected String _userTablePasswordField;
72     protected String _roleTableRoleField;
73     protected int _cacheTime;
74     protected long _lastHashPurge;
75     protected Connection _con;
76     protected String _userSql;
77     protected String _roleSql;
78
79
80     /* ------------------------------------------------------------ */
81     public JDBCLoginService()
82         throws IOException
83     {
84     }
85     
86     /* ------------------------------------------------------------ */
87     public JDBCLoginService(String name)
88         throws IOException
89     {
90         setName(name);
91     }
92     
93     /* ------------------------------------------------------------ */
94     public JDBCLoginService(String name, String config)
95         throws IOException
96     {
97         setName(name);
98         setConfig(config);
99     }
100     
101     /* ------------------------------------------------------------ */
102     public JDBCLoginService(String name, IdentityService identityService, String config)
103         throws IOException
104     {
105         setName(name);
106         setIdentityService(identityService);
107         setConfig(config);
108     }
109
110
111     /* ------------------------------------------------------------ */
112     /**
113      * @see org.eclipse.jetty.security.MappedLoginService#doStart()
114      */
115     @Override
116     protected void doStart() throws Exception
117     {
118         Properties properties = new Properties();
119         Resource resource = Resource.newResource(_config);
120         try (InputStream in = resource.getInputStream())
121         {
122             properties.load(in);
123         }
124         _jdbcDriver = properties.getProperty("jdbcdriver");
125         _url = properties.getProperty("url");
126         _userName = properties.getProperty("username");
127         _password = properties.getProperty("password");
128         String _userTable = properties.getProperty("usertable");
129         _userTableKey = properties.getProperty("usertablekey");
130         String _userTableUserField = properties.getProperty("usertableuserfield");
131         _userTablePasswordField = properties.getProperty("usertablepasswordfield");
132         String _roleTable = properties.getProperty("roletable");
133         String _roleTableKey = properties.getProperty("roletablekey");
134         _roleTableRoleField = properties.getProperty("roletablerolefield");
135         String _userRoleTable = properties.getProperty("userroletable");
136         String _userRoleTableUserKey = properties.getProperty("userroletableuserkey");
137         String _userRoleTableRoleKey = properties.getProperty("userroletablerolekey");
138         _cacheTime = new Integer(properties.getProperty("cachetime"));
139
140         if (_jdbcDriver == null || _jdbcDriver.equals("")
141             || _url == null
142             || _url.equals("")
143             || _userName == null
144             || _userName.equals("")
145             || _password == null
146             || _cacheTime < 0)
147         {
148             LOG.warn("UserRealm " + getName() + " has not been properly configured");
149         }
150         _cacheTime *= 1000;
151         _lastHashPurge = 0;
152         _userSql = "select " + _userTableKey + "," + _userTablePasswordField + " from " + _userTable + " where " + _userTableUserField + " = ?";
153         _roleSql = "select r." + _roleTableRoleField
154                    + " from "
155                    + _roleTable
156                    + " r, "
157                    + _userRoleTable
158                    + " u where u."
159                    + _userRoleTableUserKey
160                    + " = ?"
161                    + " and r."
162                    + _roleTableKey
163                    + " = u."
164                    + _userRoleTableRoleKey;
165         
166         Loader.loadClass(this.getClass(), _jdbcDriver).newInstance();
167         super.doStart();
168     }
169
170
171     /* ------------------------------------------------------------ */
172     public String getConfig()
173     {
174         return _config;
175     }
176
177     /* ------------------------------------------------------------ */
178     /**
179      * Load JDBC connection configuration from properties file.
180      * 
181      * @param config Filename or url of user properties file.
182      */
183     public void setConfig(String config)
184     {        
185         if (isRunning())
186             throw new IllegalStateException("Running");
187         _config=config;
188     }
189
190     /* ------------------------------------------------------------ */
191     /**
192      * (re)Connect to database with parameters setup by loadConfig()
193      */
194     public void connectDatabase()
195     {
196         try
197         {
198             Class.forName(_jdbcDriver);
199             _con = DriverManager.getConnection(_url, _userName, _password);
200         }
201         catch (SQLException e)
202         {
203             LOG.warn("UserRealm " + getName() + " could not connect to database; will try later", e);
204         }
205         catch (ClassNotFoundException e)
206         {
207             LOG.warn("UserRealm " + getName() + " could not connect to database; will try later", e);
208         }
209     }
210
211     /* ------------------------------------------------------------ */
212     @Override
213     public UserIdentity login(String username, Object credentials)
214     {
215         long now = System.currentTimeMillis();
216         if (now - _lastHashPurge > _cacheTime || _cacheTime == 0)
217         {
218             _users.clear();
219             _lastHashPurge = now;
220             closeConnection();
221         }
222         
223         return super.login(username,credentials);
224     }
225
226     /* ------------------------------------------------------------ */
227     @Override
228     protected void loadUsers()
229     {   
230     }
231     
232     /* ------------------------------------------------------------ */
233     @Override
234     protected UserIdentity loadUser(String username)
235     {
236         try
237         {
238             if (null == _con) 
239                 connectDatabase();
240
241             if (null == _con) 
242                 throw new SQLException("Can't connect to database");
243
244             try (PreparedStatement stat1 = _con.prepareStatement(_userSql))
245             {
246                 stat1.setObject(1, username);
247                 try (ResultSet rs1 = stat1.executeQuery())
248                 {
249                     if (rs1.next())
250                     {
251                         int key = rs1.getInt(_userTableKey);
252                         String credentials = rs1.getString(_userTablePasswordField);
253                         List<String> roles = new ArrayList<String>();
254
255                         try (PreparedStatement stat2 = _con.prepareStatement(_roleSql))
256                         {
257                             stat2.setInt(1, key);
258                             try (ResultSet rs2 = stat2.executeQuery())
259                             {
260                                 while (rs2.next())
261                                     roles.add(rs2.getString(_roleTableRoleField));
262                             }
263                         }
264                         return putUser(username, credentials, roles.toArray(new String[roles.size()]));
265                     }
266                 }
267             }
268         }
269         catch (SQLException e)
270         {
271             LOG.warn("UserRealm " + getName() + " could not load user information from database", e);
272             closeConnection();
273         }
274         return null;
275     }
276     
277     /* ------------------------------------------------------------ */
278     protected UserIdentity putUser (String username, String credentials, String[] roles)
279     {
280         return putUser(username, Credential.getCredential(credentials),roles);
281     }
282     
283
284     /**
285      * Close an existing connection
286      */
287     private void closeConnection ()
288     {
289         if (_con != null)
290         {
291             if (LOG.isDebugEnabled()) LOG.debug("Closing db connection for JDBCUserRealm");
292             try { _con.close(); }catch (Exception e) {LOG.ignore(e);}
293         }
294         _con = null;
295     }
296
297 }