]> git.aero2k.de Git - tmp/jakarta-migration.git/blob
3a3b6de485da8d8821a1f148bc7c333fc52ecf91
[tmp/jakarta-migration.git] /
1 /**
2  *  This document is a part of the source code and related artifacts
3  *  for CollectionSpace, an open source collections management system
4  *  for museums and related institutions:
5
6  *  http://www.collectionspace.org
7  *  http://wiki.collectionspace.org
8
9  *  Copyright 2009 University of California at Berkeley
10
11  *  Licensed under the Educational Community License (ECL), Version 2.0.
12  *  You may not use this file except in compliance with this License.
13
14  *  You may obtain a copy of the ECL 2.0 License at
15
16  *  https://source.collectionspace.org/collection-space/LICENSE.txt
17
18  *  Unless required by applicable law or agreed to in writing, software
19  *  distributed under the License is distributed on an "AS IS" BASIS,
20  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21  *  See the License for the specific language governing permissions and
22  *  limitations under the License.
23  *//**
24  *  This document is a part of the source code and related artifacts
25  *  for CollectionSpace, an open source collections management system
26  *  for museums and related institutions:
27
28  *  http://www.collectionspace.org
29  *  http://wiki.collectionspace.org
30
31  *  Copyright 2009 University of California at Berkeley
32
33  *  Licensed under the Educational Community License (ECL), Version 2.0.
34  *  You may not use this file except in compliance with this License.
35
36  *  You may obtain a copy of the ECL 2.0 License at
37
38  *  https://source.collectionspace.org/collection-space/LICENSE.txt
39
40  *  Unless required by applicable law or agreed to in writing, software
41  *  distributed under the License is distributed on an "AS IS" BASIS,
42  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
43  *  See the License for the specific language governing permissions and
44  *  limitations under the License.
45  */
46 /*
47  * To change this template, choose Tools | Templates
48  * and open the template in the editor.
49  */
50 package org.collectionspace.authentication.realm.db;
51
52 import java.lang.reflect.Constructor;
53 import java.security.Principal;
54 import java.security.acl.Group;
55 import java.sql.Connection;
56 import java.sql.PreparedStatement;
57 import java.sql.ResultSet;
58 import java.sql.SQLException;
59 import java.util.Collection;
60 import java.util.HashMap;
61 import java.util.Map;
62
63 import javax.naming.Context;
64 import javax.naming.InitialContext;
65 import javax.naming.NamingException;
66 import javax.security.auth.login.FailedLoginException;
67 import javax.security.auth.login.LoginException;
68 import javax.sql.DataSource;
69
70 //import org.apache.commons.logging.Log;
71 //import org.apache.commons.logging.LogFactory;
72
73 import org.collectionspace.authentication.AuthN;
74 import org.collectionspace.authentication.CSpaceTenant;
75 import org.collectionspace.authentication.realm.CSpaceRealm;
76
77 import org.slf4j.Logger;
78 import org.slf4j.LoggerFactory;
79
80 /**
81  * CSpaceDbRealm provides access to user, password, role, tenant database
82  * @author 
83  */
84 public class CSpaceDbRealm implements CSpaceRealm {
85
86     private Logger logger = LoggerFactory.getLogger(CSpaceDbRealm.class);
87     
88     private String datasourceName;
89     private String principalsQuery;
90     private String rolesQuery;
91     private String tenantsQueryNoDisabled;
92     private String tenantsQueryWithDisabled;
93     private boolean suspendResume;
94
95     /**
96      * CSpace Database Realm
97      * @param datasourceName datasource name
98      */
99     public CSpaceDbRealm(Map options) {
100         datasourceName = (String) options.get("dsJndiName");
101         if (datasourceName == null) {
102             datasourceName = "java:/DefaultDS";
103         }
104         Object tmp = options.get("principalsQuery");
105         if (tmp != null) {
106             principalsQuery = tmp.toString();
107         }
108         tmp = options.get("rolesQuery");
109         if (tmp != null) {
110             rolesQuery = tmp.toString();
111         }
112         tmp = options.get("tenantsQueryNoDisabled");
113         if (tmp != null) {
114             tenantsQueryNoDisabled = tmp.toString();
115         }
116         tmp = options.get("tenantsQueryWithDisabled");
117         if (tmp != null) {
118                 tenantsQueryWithDisabled = tmp.toString();
119         }
120         tmp = options.get("suspendResume");
121         if (tmp != null) {
122             suspendResume = Boolean.valueOf(tmp.toString()).booleanValue();
123         }
124         if (logger.isTraceEnabled()) {
125             logger.trace("DatabaseServerLoginModule, dsJndiName=" + datasourceName);
126             logger.trace("principalsQuery=" + principalsQuery);
127             logger.trace("rolesQuery=" + rolesQuery);
128             logger.trace("suspendResume=" + suspendResume);
129         }
130
131     }
132
133     @Override
134     public String getUsersPassword(String username) throws LoginException {
135
136         String password = null;
137         Connection conn = null;
138         PreparedStatement ps = null;
139         ResultSet rs = null;
140         try {
141             conn = getConnection();
142             // Get the password
143             if (logger.isDebugEnabled()) {
144                 logger.debug("Executing query: " + principalsQuery + ", with username: " + username);
145             }
146             ps = conn.prepareStatement(principalsQuery);
147             ps.setString(1, username);
148             rs = ps.executeQuery();
149             if (rs.next() == false) {
150                 if (logger.isDebugEnabled()) {
151                     logger.debug(principalsQuery + " returned no matches from db");
152                 }
153                 throw new FailedLoginException("No matching username found");
154             }
155
156             password = rs.getString(1);
157         } catch (SQLException ex) {
158                 if (logger.isTraceEnabled() == true) {
159                         logger.error("Could not open database to read AuthN tables.", ex);
160                 }
161             LoginException le = new LoginException("Authentication query failed: " + ex.getLocalizedMessage());
162             le.initCause(ex);
163             throw le;
164         } catch (Exception ex) {
165             LoginException le = new LoginException("Unknown Exception");
166             le.initCause(ex);
167             throw le;
168         } finally {
169             if (rs != null) {
170                 try {
171                     rs.close();
172                 } catch (SQLException e) {
173                 }
174             }
175             if (ps != null) {
176                 try {
177                     ps.close();
178                 } catch (SQLException e) {
179                 }
180             }
181             if (conn != null) {
182                 try {
183                     conn.close();
184                 } catch (SQLException ex) {
185                 }
186             }
187         }
188         return password;
189     }
190
191     /**
192      * Execute the rolesQuery against the datasourceName to obtain the roles for
193      * the authenticated user.
194      * @return collection containing the roles
195      */
196     @Override
197     public Collection<Group> getRoles(String username, String principalClassName, String groupClassName) throws LoginException {
198
199         if (logger.isDebugEnabled()) {
200             logger.debug("getRoleSets using rolesQuery: " + rolesQuery + ", username: " + username);
201         }
202
203         Connection conn = null;
204         HashMap<String, Group> groupsMap = new HashMap<String, Group>();
205         PreparedStatement ps = null;
206         ResultSet rs = null;
207
208         try {
209             conn = getConnection();
210             // Get the user role names
211             if (logger.isDebugEnabled()) {
212                 logger.debug("Executing query: " + rolesQuery + ", with username: " + username);
213             }
214
215             ps = conn.prepareStatement(rolesQuery);
216             try {
217                 ps.setString(1, username);
218             } catch (ArrayIndexOutOfBoundsException ignore) {
219                 // The query may not have any parameters so just try it
220             }
221             rs = ps.executeQuery();
222             if (rs.next() == false) {
223                 if (logger.isDebugEnabled()) {
224                     logger.debug("No roles found");
225                 }
226 //                if(aslm.getUnauthenticatedIdentity() == null){
227 //                    throw new FailedLoginException("No matching username found in Roles");
228 //                }
229                 /* We are running with an unauthenticatedIdentity so create an
230                 empty Roles set and return.
231                  */
232
233                 Group g = createGroup(groupClassName, "Roles");
234                 groupsMap.put(g.getName(), g);
235                 return groupsMap.values();
236             }
237
238             do {
239                 String roleName = rs.getString(1);
240                 String groupName = rs.getString(2);
241                 if (groupName == null || groupName.length() == 0) {
242                     groupName = "Roles";
243                 }
244
245                 Group group = (Group) groupsMap.get(groupName);
246                 if (group == null) {
247                     group = createGroup(groupClassName, groupName);
248                     groupsMap.put(groupName, group);
249                 }
250
251                 try {
252                     Principal p = createPrincipal(principalClassName, roleName);
253                     if (logger.isDebugEnabled()) {
254                         logger.debug("Assign user to role " + roleName);
255                     }
256
257                     group.addMember(p);
258                 } catch (Exception e) {
259                     logger.error("Failed to create principal: " + roleName + " " + e.toString());
260                 }
261
262             } while (rs.next());
263         } catch (SQLException ex) {
264             LoginException le = new LoginException("Query failed");
265             le.initCause(ex);
266             throw le;
267         } catch (Exception e) {
268             LoginException le = new LoginException("unknown exception");
269             le.initCause(e);
270             throw le;
271         } finally {
272             if (rs != null) {
273                 try {
274                     rs.close();
275                 } catch (SQLException e) {
276                 }
277             }
278             if (ps != null) {
279                 try {
280                     ps.close();
281                 } catch (SQLException e) {
282                 }
283             }
284             if (conn != null) {
285                 try {
286                     conn.close();
287                 } catch (Exception ex) {
288                 }
289             }
290
291         }
292
293         return groupsMap.values();
294
295     }
296     @Override
297     public Collection<Group> getTenants(String username, String groupClassName) throws LoginException {
298         return getTenants(username, groupClassName, false);
299     }
300     
301     /**
302      * Execute the tenantsQuery against the datasourceName to obtain the tenants for
303      * the authenticated user.
304      * @return collection containing the roles
305      */
306     @Override
307     public Collection<Group> getTenants(String username, String groupClassName, boolean includeDisabledTenants) throws LoginException {
308
309         String tenantsQuery = getTenantQuery(includeDisabledTenants);
310         
311         if (logger.isDebugEnabled()) {
312             logger.debug("getTenants using tenantsQuery: " + tenantsQuery + ", username: " + username);
313         }
314
315         Connection conn = null;
316         HashMap<String, Group> groupsMap = new HashMap<String, Group>();
317         PreparedStatement ps = null;
318         ResultSet rs = null;
319
320         try {
321             conn = getConnection();
322
323             ps = conn.prepareStatement(tenantsQuery);
324             try {
325                 ps.setString(1, username);
326             } catch (ArrayIndexOutOfBoundsException ignore) {
327                 // The query may not have any parameters so just try it
328             }
329             rs = ps.executeQuery();
330             if (rs.next() == false) {
331                 if (logger.isDebugEnabled()) {
332                     logger.debug("No tenants found");
333                 }
334                 // We are running with an unauthenticatedIdentity so create an
335                 // empty Tenants set and return.
336                 // FIXME  should this be allowed?
337                 Group g = createGroup(groupClassName, "Tenants");
338                 groupsMap.put(g.getName(), g);
339                 return groupsMap.values();
340             }
341
342             do {
343                 String tenantId = rs.getString(1);
344                 String tenantName = rs.getString(2);
345                 String groupName = rs.getString(3);
346                 if (groupName == null || groupName.length() == 0) {
347                     groupName = "Tenants";
348                 }
349
350                 Group group = (Group) groupsMap.get(groupName);
351                 if (group == null) {
352                     group = createGroup(groupClassName, groupName);
353                     groupsMap.put(groupName, group);
354                 }
355
356                 try {
357                     Principal p = createTenant(tenantName, tenantId);
358                     if (logger.isDebugEnabled()) {
359                         logger.debug("Assign user to tenant " + tenantName);
360                     }
361
362                     group.addMember(p);
363                 } catch (Exception e) {
364                     logger.error("Failed to create tenant: " + tenantName + " " + e.toString());
365                 }
366             } while (rs.next());
367         } catch (SQLException ex) {
368             LoginException le = new LoginException("Query failed");
369             le.initCause(ex);
370             throw le;
371         } catch (Exception e) {
372             LoginException le = new LoginException("unknown exception");
373             le.initCause(e);
374             throw le;
375         } finally {
376             if (rs != null) {
377                 try {
378                     rs.close();
379                 } catch (SQLException e) {
380                 }
381             }
382             if (ps != null) {
383                 try {
384                     ps.close();
385                 } catch (SQLException e) {
386                 }
387             }
388             if (conn != null) {
389                 try {
390                     conn.close();
391                 } catch (Exception ex) {
392                 }
393             }
394
395         }
396
397         return groupsMap.values();
398     }
399
400     private CSpaceTenant createTenant(String name, String id) throws Exception {
401         return new CSpaceTenant(name, id);
402     }
403
404     private Group createGroup(String groupClassName, String name) throws Exception {
405         return (Group) createPrincipal(groupClassName, name);
406     }
407
408     private Principal createPrincipal(String principalClassName, String name) throws Exception {
409         ClassLoader loader = Thread.currentThread().getContextClassLoader();
410         Class clazz = loader.loadClass(principalClassName);
411         Class[] ctorSig = {String.class};
412         Constructor ctor = clazz.getConstructor(ctorSig);
413         Object[] ctorArgs = {name};
414         Principal p = (Principal) ctor.newInstance(ctorArgs);
415         return p;
416     }
417
418     private Connection getConnection() throws LoginException, SQLException {
419         InitialContext ctx = null;
420         Connection conn = null;
421         String dataSourceName = getDataSourceName();
422         DataSource ds = null;
423         try {
424             ctx = new InitialContext();
425             try {
426                 ds = (DataSource) ctx.lookup(dataSourceName);
427             } catch (Exception e) {}
428             
429                 try {
430                         Context envCtx = (Context) ctx.lookup("java:comp/env");
431                         ds = (DataSource) envCtx.lookup(dataSourceName);
432                 } catch (Exception e) {}
433                 
434                 try {
435                         Context envCtx = (Context) ctx.lookup("java:comp");
436                         ds = (DataSource) envCtx.lookup(dataSourceName);
437                 } catch (Exception e) {}
438                 
439                 try {
440                         Context envCtx = (Context) ctx.lookup("java:");
441                         ds = (DataSource) envCtx.lookup(dataSourceName);
442                 } catch (Exception e) {}
443                 
444                 try {
445                         Context envCtx = (Context) ctx.lookup("java");
446                         ds = (DataSource) envCtx.lookup(dataSourceName);
447                 } catch (Exception e) {}
448                 
449                 try {
450                         ds = (DataSource) ctx.lookup("java:/" + dataSourceName);
451                 } catch (Exception e) {}  
452
453                 if (ds == null) {
454                 ds = AuthN.getDataSource();
455                 }
456                 
457             if (ds == null) {
458                 throw new IllegalArgumentException("datasource not found: " + dataSourceName);
459             }
460             
461             conn = ds.getConnection();
462             if (conn == null) {
463                 conn = AuthN.getDataSource().getConnection();  //FIXME:REM - This is the result of some type of JNDI mess.  Should try to solve this problem and clean up this code.
464             }
465             return conn;
466         } catch (NamingException ex) {
467             LoginException le = new LoginException("Error looking up DataSource from: " + dataSourceName);
468             le.initCause(ex);
469             throw le;
470         } finally {
471             if (ctx != null) {
472                 try {
473                     ctx.close();
474                 } catch (Exception e) {
475                         e.printStackTrace();
476                 }
477             }
478         }
479
480     }
481
482     /**
483      * @return the datasourceName
484      */
485     public String getDataSourceName() {
486         return datasourceName;
487     }
488
489     /**
490      * @return the principalQuery
491      */
492     public String getPrincipalQuery() {
493         return principalsQuery;
494     }
495
496     /**
497      * @param principalQuery the principalQuery to set
498      */
499     public void setPrincipalQuery(String principalQuery) {
500         this.principalsQuery = principalQuery;
501     }
502
503     /**
504      * @return the roleQuery
505      */
506     public String getRoleQuery() {
507         return rolesQuery;
508     }
509
510     /**
511      * @param roleQuery the roleQuery to set
512      */
513     public void setRoleQuery(String roleQuery) {
514         this.rolesQuery = roleQuery;
515     }
516
517     /**
518      * @return the tenantQuery
519      */
520     public String getTenantQuery(boolean includeDisabledTenants) {
521         return includeDisabledTenants?tenantsQueryWithDisabled:tenantsQueryNoDisabled;
522     }
523
524     /**
525      * @param tenantQuery the tenantQuery to set
526     public void setTenantQuery(String tenantQuery) {
527         this.tenantsQueryNoDisabled = tenantQuery;
528     }
529      */
530 }