View Javadoc

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  
20  package org.eclipse.jetty.plus.security;
21  
22  import java.sql.Connection;
23  import java.sql.DatabaseMetaData;
24  import java.sql.PreparedStatement;
25  import java.sql.ResultSet;
26  import java.sql.SQLException;
27  import java.sql.Statement;
28  import java.util.ArrayList;
29  import java.util.List;
30  import java.util.Locale;
31  
32  import javax.naming.InitialContext;
33  import javax.naming.NameNotFoundException;
34  import javax.naming.NamingException;
35  import javax.sql.DataSource;
36  
37  import org.eclipse.jetty.plus.jndi.NamingEntryUtil;
38  import org.eclipse.jetty.security.IdentityService;
39  import org.eclipse.jetty.security.MappedLoginService;
40  import org.eclipse.jetty.server.Server;
41  import org.eclipse.jetty.server.UserIdentity;
42  import org.eclipse.jetty.util.log.Log;
43  import org.eclipse.jetty.util.log.Logger;
44  import org.eclipse.jetty.util.security.Credential;
45  
46  
47  /**
48   *
49   * DataSourceUserRealm
50   *
51   * Obtain user/password/role information from a database
52   * via jndi DataSource.
53   */
54  public class DataSourceLoginService extends MappedLoginService
55  {
56      private static final Logger LOG = Log.getLogger(DataSourceLoginService.class);
57  
58      private String _jndiName = "javax.sql.DataSource/default";
59      private DataSource _datasource;
60      private Server _server;
61      private String _userTableName = "users";
62      private String _userTableKey = "id";
63      private String _userTableUserField = "username";
64      private String _userTablePasswordField = "pwd";
65      private String _roleTableName = "roles";
66      private String _roleTableKey = "id";
67      private String _roleTableRoleField = "role";
68      private String _userRoleTableName = "user_roles";
69      private String _userRoleTableUserKey = "user_id";
70      private String _userRoleTableRoleKey = "role_id";
71      private int _cacheMs = 30000;
72      private long _lastPurge = 0;
73      private String _userSql;
74      private String _roleSql;
75      private boolean _createTables = false;
76  
77      /* ------------------------------------------------------------ */
78      public DataSourceLoginService()
79      {
80      }
81  
82      /* ------------------------------------------------------------ */
83      public DataSourceLoginService(String name)
84      {
85          setName(name);
86      }
87  
88      /* ------------------------------------------------------------ */
89      public DataSourceLoginService(String name, IdentityService identityService)
90      {
91          setName(name);
92          setIdentityService(identityService);
93      }
94  
95      /* ------------------------------------------------------------ */
96      public void setJndiName (String jndi)
97      {
98          _jndiName = jndi;
99      }
100 
101     /* ------------------------------------------------------------ */
102     public String getJndiName ()
103     {
104         return _jndiName;
105     }
106 
107     /* ------------------------------------------------------------ */
108     public void setServer (Server server)
109     {
110         _server=server;
111     }
112 
113     /* ------------------------------------------------------------ */
114     public Server getServer()
115     {
116         return _server;
117     }
118 
119     /* ------------------------------------------------------------ */
120     public void setCreateTables(boolean createTables)
121     {
122         _createTables = createTables;
123     }
124 
125     /* ------------------------------------------------------------ */
126     public boolean getCreateTables()
127     {
128         return _createTables;
129     }
130 
131     /* ------------------------------------------------------------ */
132     public void setUserTableName (String name)
133     {
134         _userTableName=name;
135     }
136 
137     /* ------------------------------------------------------------ */
138     public String getUserTableName()
139     {
140         return _userTableName;
141     }
142 
143     /* ------------------------------------------------------------ */
144     public String getUserTableKey()
145     {
146         return _userTableKey;
147     }
148 
149 
150     /* ------------------------------------------------------------ */
151     public void setUserTableKey(String tableKey)
152     {
153         _userTableKey = tableKey;
154     }
155 
156 
157     /* ------------------------------------------------------------ */
158     public String getUserTableUserField()
159     {
160         return _userTableUserField;
161     }
162 
163 
164     /* ------------------------------------------------------------ */
165     public void setUserTableUserField(String tableUserField)
166     {
167         _userTableUserField = tableUserField;
168     }
169 
170 
171     /* ------------------------------------------------------------ */
172     public String getUserTablePasswordField()
173     {
174         return _userTablePasswordField;
175     }
176 
177 
178     /* ------------------------------------------------------------ */
179     public void setUserTablePasswordField(String tablePasswordField)
180     {
181         _userTablePasswordField = tablePasswordField;
182     }
183 
184 
185     /* ------------------------------------------------------------ */
186     public String getRoleTableName()
187     {
188         return _roleTableName;
189     }
190 
191 
192     /* ------------------------------------------------------------ */
193     public void setRoleTableName(String tableName)
194     {
195         _roleTableName = tableName;
196     }
197 
198 
199     /* ------------------------------------------------------------ */
200     public String getRoleTableKey()
201     {
202         return _roleTableKey;
203     }
204 
205 
206     /* ------------------------------------------------------------ */
207     public void setRoleTableKey(String tableKey)
208     {
209         _roleTableKey = tableKey;
210     }
211 
212 
213     /* ------------------------------------------------------------ */
214     public String getRoleTableRoleField()
215     {
216         return _roleTableRoleField;
217     }
218 
219 
220     /* ------------------------------------------------------------ */
221     public void setRoleTableRoleField(String tableRoleField)
222     {
223         _roleTableRoleField = tableRoleField;
224     }
225 
226 
227     /* ------------------------------------------------------------ */
228     public String getUserRoleTableName()
229     {
230         return _userRoleTableName;
231     }
232 
233 
234     /* ------------------------------------------------------------ */
235     public void setUserRoleTableName(String roleTableName)
236     {
237         _userRoleTableName = roleTableName;
238     }
239 
240 
241     /* ------------------------------------------------------------ */
242     public String getUserRoleTableUserKey()
243     {
244         return _userRoleTableUserKey;
245     }
246 
247 
248     /* ------------------------------------------------------------ */
249     public void setUserRoleTableUserKey(String roleTableUserKey)
250     {
251         _userRoleTableUserKey = roleTableUserKey;
252     }
253 
254 
255     /* ------------------------------------------------------------ */
256     public String getUserRoleTableRoleKey()
257     {
258         return _userRoleTableRoleKey;
259     }
260 
261 
262     /* ------------------------------------------------------------ */
263     public void setUserRoleTableRoleKey(String roleTableRoleKey)
264     {
265         _userRoleTableRoleKey = roleTableRoleKey;
266     }
267 
268     /* ------------------------------------------------------------ */
269     public void setCacheMs (int ms)
270     {
271         _cacheMs=ms;
272     }
273 
274     /* ------------------------------------------------------------ */
275     public int getCacheMs ()
276     {
277         return _cacheMs;
278     }
279 
280     /* ------------------------------------------------------------ */
281     @Override
282     protected void loadUsers()
283     {
284     }
285     
286  
287     
288     /* ------------------------------------------------------------ */
289     /** Load user's info from database.
290      *
291      * @param userName
292      */
293     @Override
294     protected UserIdentity loadUser (String userName)
295     {
296         try
297         {
298             try (Connection connection = getConnection();
299                  PreparedStatement statement1 = connection.prepareStatement(_userSql))
300             {
301                 statement1.setObject(1, userName);
302                 try (ResultSet rs1 = statement1.executeQuery())
303                 {
304                     if (rs1.next())
305                     {
306                         int key = rs1.getInt(_userTableKey);
307                         String credentials = rs1.getString(_userTablePasswordField);
308                         List<String> roles = new ArrayList<String>();
309                         try (PreparedStatement statement2 = connection.prepareStatement(_roleSql))
310                         {
311                             statement2.setInt(1, key);
312                             try (ResultSet rs2 = statement2.executeQuery())
313                             {
314                                 while (rs2.next())
315                                 {
316                                     roles.add(rs2.getString(_roleTableRoleField));
317                                 }
318                             }
319                         }
320                         return putUser(userName, Credential.getCredential(credentials), roles.toArray(new String[roles.size()]));
321                     }
322                 }
323             }
324         }
325         catch (NamingException e)
326         {
327             LOG.warn("No datasource for "+_jndiName, e);
328         }
329         catch (SQLException e)
330         {
331             LOG.warn("Problem loading user info for "+userName, e);
332         }
333         return null;
334     }
335     
336     
337     
338     /* ------------------------------------------------------------ */
339     @Override
340     public UserIdentity login(String username, Object credentials)
341     {
342         long now = System.currentTimeMillis();
343         if (now - _lastPurge > _cacheMs || _cacheMs == 0)
344         {
345             _users.clear();
346             _lastPurge = now;
347         }
348  
349         return super.login(username,credentials);
350     }
351 
352     /* ------------------------------------------------------------ */
353     /**
354      * Lookup the datasource for the jndiName and formulate the
355      * necessary sql query strings based on the configured table
356      * and column names.
357      *
358      * @throws NamingException
359      */
360     public void initDb() throws NamingException, SQLException
361     {
362         if (_datasource != null)
363             return;
364 
365         @SuppressWarnings("unused")
366         InitialContext ic = new InitialContext();
367         assert ic!=null;
368 
369         //TODO Should we try webapp scope too?
370 
371         //try finding the datasource in the Server scope
372         if (_server != null)
373         {
374             try
375             {
376                 _datasource = (DataSource)NamingEntryUtil.lookup(_server, _jndiName);
377             }
378             catch (NameNotFoundException e)
379             {
380                 //next try the jvm scope
381             }
382         }
383 
384 
385         //try finding the datasource in the jvm scope
386         if (_datasource==null)
387         {
388             _datasource = (DataSource)NamingEntryUtil.lookup(null, _jndiName);
389         }
390 
391         // set up the select statements based on the table and column names configured
392         _userSql = "select " + _userTableKey + "," + _userTablePasswordField
393                   + " from " + _userTableName
394                   + " where "+ _userTableUserField + " = ?";
395 
396         _roleSql = "select r." + _roleTableRoleField
397                   + " from " + _roleTableName + " r, " + _userRoleTableName
398                   + " u where u."+ _userRoleTableUserKey + " = ?"
399                   + " and r." + _roleTableKey + " = u." + _userRoleTableRoleKey;
400 
401         prepareTables();
402     }
403 
404 
405 
406     private void prepareTables()
407     throws NamingException, SQLException
408     {
409         if (_createTables)
410         {
411             boolean autocommit = true;
412             Connection connection = getConnection();
413             try (Statement stmt = connection.createStatement())
414             {
415                 autocommit = connection.getAutoCommit();
416                 connection.setAutoCommit(false);
417                 DatabaseMetaData metaData = connection.getMetaData();
418 
419                 //check if tables exist
420                 String tableName = (metaData.storesLowerCaseIdentifiers()? _userTableName.toLowerCase(Locale.ENGLISH): (metaData.storesUpperCaseIdentifiers()?_userTableName.toUpperCase(Locale.ENGLISH): _userTableName));
421                 try (ResultSet result = metaData.getTables(null, null, tableName, null))
422                 {
423                     if (!result.next())
424                     {
425                         //user table default
426                         /*
427                          * create table _userTableName (_userTableKey integer,
428                          * _userTableUserField varchar(100) not null unique,
429                          * _userTablePasswordField varchar(20) not null, primary key(_userTableKey));
430                          */
431                         stmt.executeUpdate("create table "+_userTableName+ "("+_userTableKey+" integer,"+
432                                 _userTableUserField+" varchar(100) not null unique,"+
433                                 _userTablePasswordField+" varchar(20) not null, primary key("+_userTableKey+"))");
434                         if (LOG.isDebugEnabled()) LOG.debug("Created table "+_userTableName);
435                     }
436                 }
437 
438                 tableName = (metaData.storesLowerCaseIdentifiers()? _roleTableName.toLowerCase(Locale.ENGLISH): (metaData.storesUpperCaseIdentifiers()?_roleTableName.toUpperCase(Locale.ENGLISH): _roleTableName));
439                 try (ResultSet result = metaData.getTables(null, null, tableName, null))
440                 {
441                     if (!result.next())
442                     {
443                         //role table default
444                         /*
445                          * create table _roleTableName (_roleTableKey integer,
446                          * _roleTableRoleField varchar(100) not null unique, primary key(_roleTableKey));
447                          */
448                         String str = "create table "+_roleTableName+" ("+_roleTableKey+" integer, "+
449                         _roleTableRoleField+" varchar(100) not null unique, primary key("+_roleTableKey+"))";
450                         stmt.executeUpdate(str);
451                         if (LOG.isDebugEnabled()) LOG.debug("Created table "+_roleTableName);
452                     }
453                 }
454 
455                 tableName = (metaData.storesLowerCaseIdentifiers()? _userRoleTableName.toLowerCase(Locale.ENGLISH): (metaData.storesUpperCaseIdentifiers()?_userRoleTableName.toUpperCase(Locale.ENGLISH): _userRoleTableName));
456                 try (ResultSet result = metaData.getTables(null, null, tableName, null))
457                 {
458                     if (!result.next())
459                     {
460                         //user-role table
461                         /*
462                          * create table _userRoleTableName (_userRoleTableUserKey integer,
463                          * _userRoleTableRoleKey integer,
464                          * primary key (_userRoleTableUserKey, _userRoleTableRoleKey));
465                          *
466                          * create index idx_user_role on _userRoleTableName (_userRoleTableUserKey);
467                          */
468                         stmt.executeUpdate("create table "+_userRoleTableName+" ("+_userRoleTableUserKey+" integer, "+
469                                 _userRoleTableRoleKey+" integer, "+
470                                 "primary key ("+_userRoleTableUserKey+", "+_userRoleTableRoleKey+"))");
471                         stmt.executeUpdate("create index indx_user_role on "+_userRoleTableName+"("+_userRoleTableUserKey+")");
472                         if (LOG.isDebugEnabled()) LOG.debug("Created table "+_userRoleTableName +" and index");
473                     }
474                 }
475                 connection.commit();
476             }
477             finally
478             {
479                 try
480                 {
481                     connection.setAutoCommit(autocommit);
482                 }
483                 catch (SQLException e)
484                 {
485                     if (LOG.isDebugEnabled()) LOG.debug("Prepare tables", e);
486                 }
487                 finally
488                 {
489                     try
490                     {
491                         connection.close();
492                     }
493                     catch (SQLException e)
494                     {
495                         if (LOG.isDebugEnabled()) LOG.debug("Prepare tables", e);
496                     }
497                 }
498             }
499         }
500         else if (LOG.isDebugEnabled())
501         {
502             LOG.debug("createTables false");
503         }
504     }
505 
506 
507     private Connection getConnection ()
508     throws NamingException, SQLException
509     {
510         initDb();
511         return _datasource.getConnection();
512     }
513 
514 }