]> git.aero2k.de Git - tmp/jakarta-migration.git/blob
aa67924f390ca432158b717e38e45944b682e13d
[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     private boolean userIsTenantManager(Connection conn, String username) {
302         String acctQuery = "SELECT csid FROM accounts_common WHERE userid=?";
303         PreparedStatement ps = null;
304         ResultSet rs = null;
305         boolean accountIsTenantManager = false;
306         try {
307             ps = conn.prepareStatement(acctQuery);
308             ps.setString(1, username);
309             rs = ps.executeQuery();
310             if (rs.next()) {
311                 String acctCSID = rs.getString(1);
312                 if(AuthN.TENANT_MANAGER_ACCT_ID.equals(acctCSID)) {
313                         accountIsTenantManager = true;
314                 }
315             }
316         } catch (SQLException ex) {
317             if(logger.isDebugEnabled()) {
318                 logger.debug("userIsTenantManager query failed on SQL error: " + ex.getLocalizedMessage());
319             }
320         } catch (Exception e) {
321             if(logger.isDebugEnabled()) {
322                 logger.debug("userIsTenantManager unknown error: " + e.getLocalizedMessage());
323             }
324         } finally {
325             if (rs != null) {
326                 try {
327                     rs.close();
328                 } catch (SQLException e) {
329                 }
330             }
331             if (ps != null) {
332                 try {
333                     ps.close();
334                 } catch (SQLException e) {
335                 }
336             }
337         }
338         return accountIsTenantManager;
339     }
340     
341     /**
342      * Execute the tenantsQuery against the datasourceName to obtain the tenants for
343      * the authenticated user.
344      * @return collection containing the roles
345      */
346     @Override
347     public Collection<Group> getTenants(String username, String groupClassName, boolean includeDisabledTenants) throws LoginException {
348
349         String tenantsQuery = getTenantQuery(includeDisabledTenants);
350         
351         if (logger.isDebugEnabled()) {
352             logger.debug("getTenants using tenantsQuery: " + tenantsQuery + ", username: " + username);
353         }
354
355         Connection conn = null;
356         HashMap<String, Group> groupsMap = new HashMap<String, Group>();
357         PreparedStatement ps = null;
358         ResultSet rs = null;
359         final String defaultGroupName = "Tenants";
360
361         try {
362             conn = getConnection();
363
364             ps = conn.prepareStatement(tenantsQuery);
365             try {
366                 ps.setString(1, username);
367             } catch (ArrayIndexOutOfBoundsException ignore) {
368                 // The query may not have any parameters so just try it
369             }
370             rs = ps.executeQuery();
371             if (rs.next() == false) {
372                         Group group = (Group) groupsMap.get(defaultGroupName);
373                         if (group == null) {
374                                 group = createGroup(groupClassName, defaultGroupName);
375                                 groupsMap.put(defaultGroupName, group);
376                         }
377                 // Check for the tenantManager
378                 if(userIsTenantManager(conn, username)) {
379                         if (logger.isDebugEnabled()) {
380                                 logger.debug("GetTenants called with tenantManager - synthesizing the pseudo-tenant");
381                         }
382                         try {
383                                 Principal p = createTenant("PseudoTenant", AuthN.TENANT_MANAGER_ACCT_ID);
384                                 if (logger.isDebugEnabled()) {
385                                         logger.debug("Assign tenantManager to tenant " + AuthN.TENANT_MANAGER_ACCT_ID);
386                                 }
387                                 group.addMember(p);
388                         } catch (Exception e) {
389                                 logger.error("Failed to create pseudo-tenant: " + e.toString());
390                         }
391                 } else {
392                         if (logger.isDebugEnabled()) {
393                                 logger.debug("No tenants found");
394                         }
395                         // We are running with an unauthenticatedIdentity so return an
396                         // empty Tenants set.
397                         // FIXME  should this be allowed?
398                 }
399                         return groupsMap.values();
400             }
401
402             do {
403                 String tenantId = rs.getString(1);
404                 String tenantName = rs.getString(2);
405                 String groupName = rs.getString(3);
406                 if (groupName == null || groupName.length() == 0) {
407                     groupName = defaultGroupName;
408                 }
409
410                 Group group = (Group) groupsMap.get(groupName);
411                 if (group == null) {
412                     group = createGroup(groupClassName, groupName);
413                     groupsMap.put(groupName, group);
414                 }
415
416                 try {
417                     Principal p = createTenant(tenantName, tenantId);
418                     if (logger.isDebugEnabled()) {
419                         logger.debug("Assign user to tenant " + tenantName);
420                     }
421
422                     group.addMember(p);
423                 } catch (Exception e) {
424                     logger.error("Failed to create tenant: " + tenantName + " " + e.toString());
425                 }
426             } while (rs.next());
427         } catch (SQLException ex) {
428             LoginException le = new LoginException("Query failed");
429             le.initCause(ex);
430             throw le;
431         } catch (Exception e) {
432             LoginException le = new LoginException("unknown exception");
433             le.initCause(e);
434             throw le;
435         } finally {
436             if (rs != null) {
437                 try {
438                     rs.close();
439                 } catch (SQLException e) {
440                 }
441             }
442             if (ps != null) {
443                 try {
444                     ps.close();
445                 } catch (SQLException e) {
446                 }
447             }
448             if (conn != null) {
449                 try {
450                     conn.close();
451                 } catch (Exception ex) {
452                 }
453             }
454
455         }
456
457         return groupsMap.values();
458     }
459
460     private CSpaceTenant createTenant(String name, String id) throws Exception {
461         return new CSpaceTenant(name, id);
462     }
463
464     private Group createGroup(String groupClassName, String name) throws Exception {
465         return (Group) createPrincipal(groupClassName, name);
466     }
467
468     private Principal createPrincipal(String principalClassName, String name) throws Exception {
469         ClassLoader loader = Thread.currentThread().getContextClassLoader();
470         Class clazz = loader.loadClass(principalClassName);
471         Class[] ctorSig = {String.class};
472         Constructor ctor = clazz.getConstructor(ctorSig);
473         Object[] ctorArgs = {name};
474         Principal p = (Principal) ctor.newInstance(ctorArgs);
475         return p;
476     }
477
478     private Connection getConnection() throws LoginException, SQLException {
479         InitialContext ctx = null;
480         Connection conn = null;
481         String dataSourceName = getDataSourceName();
482         DataSource ds = null;
483         try {
484             ctx = new InitialContext();
485             try {
486                 ds = (DataSource) ctx.lookup(dataSourceName);
487             } catch (Exception e) {}
488             
489                 try {
490                         Context envCtx = (Context) ctx.lookup("java:comp/env");
491                         ds = (DataSource) envCtx.lookup(dataSourceName);
492                 } catch (Exception e) {}
493                 
494                 try {
495                         Context envCtx = (Context) ctx.lookup("java:comp");
496                         ds = (DataSource) envCtx.lookup(dataSourceName);
497                 } catch (Exception e) {}
498                 
499                 try {
500                         Context envCtx = (Context) ctx.lookup("java:");
501                         ds = (DataSource) envCtx.lookup(dataSourceName);
502                 } catch (Exception e) {}
503                 
504                 try {
505                         Context envCtx = (Context) ctx.lookup("java");
506                         ds = (DataSource) envCtx.lookup(dataSourceName);
507                 } catch (Exception e) {}
508                 
509                 try {
510                         ds = (DataSource) ctx.lookup("java:/" + dataSourceName);
511                 } catch (Exception e) {}  
512
513                 if (ds == null) {
514                 ds = AuthN.getDataSource();
515                 }
516                 
517             if (ds == null) {
518                 throw new IllegalArgumentException("datasource not found: " + dataSourceName);
519             }
520             
521             conn = ds.getConnection();
522             if (conn == null) {
523                 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.
524             }
525             return conn;
526         } catch (NamingException ex) {
527             LoginException le = new LoginException("Error looking up DataSource from: " + dataSourceName);
528             le.initCause(ex);
529             throw le;
530         } finally {
531             if (ctx != null) {
532                 try {
533                     ctx.close();
534                 } catch (Exception e) {
535                         e.printStackTrace();
536                 }
537             }
538         }
539
540     }
541
542     /**
543      * @return the datasourceName
544      */
545     public String getDataSourceName() {
546         return datasourceName;
547     }
548
549     /**
550      * @return the principalQuery
551      */
552     public String getPrincipalQuery() {
553         return principalsQuery;
554     }
555
556     /**
557      * @param principalQuery the principalQuery to set
558      */
559     public void setPrincipalQuery(String principalQuery) {
560         this.principalsQuery = principalQuery;
561     }
562
563     /**
564      * @return the roleQuery
565      */
566     public String getRoleQuery() {
567         return rolesQuery;
568     }
569
570     /**
571      * @param roleQuery the roleQuery to set
572      */
573     public void setRoleQuery(String roleQuery) {
574         this.rolesQuery = roleQuery;
575     }
576
577     /**
578      * @return the tenantQuery
579      */
580     public String getTenantQuery(boolean includeDisabledTenants) {
581         return includeDisabledTenants?tenantsQueryWithDisabled:tenantsQueryNoDisabled;
582     }
583
584     /**
585      * @param tenantQuery the tenantQuery to set
586     public void setTenantQuery(String tenantQuery) {
587         this.tenantsQueryNoDisabled = tenantQuery;
588     }
589      */
590 }