]> git.aero2k.de Git - tmp/jakarta-migration.git/blob
759de6a2b2483248733e90da8f8814803b74dfcb
[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 tenantsQuery;
92     private boolean suspendResume;
93
94     /**
95      * CSpace Database Realm
96      * @param datasourceName datasource name
97      */
98     public CSpaceDbRealm(Map options) {
99         datasourceName = (String) options.get("dsJndiName");
100         if (datasourceName == null) {
101             datasourceName = "java:/DefaultDS";
102         }
103         Object tmp = options.get("principalsQuery");
104         if (tmp != null) {
105             principalsQuery = tmp.toString();
106         }
107         tmp = options.get("rolesQuery");
108         if (tmp != null) {
109             rolesQuery = tmp.toString();
110         }
111         tmp = options.get("tenantsQuery");
112         if (tmp != null) {
113             tenantsQuery = tmp.toString();
114         }
115         tmp = options.get("suspendResume");
116         if (tmp != null) {
117             suspendResume = Boolean.valueOf(tmp.toString()).booleanValue();
118         }
119         if (logger.isTraceEnabled()) {
120             logger.trace("DatabaseServerLoginModule, dsJndiName=" + datasourceName);
121             logger.trace("principalsQuery=" + principalsQuery);
122             logger.trace("rolesQuery=" + rolesQuery);
123             logger.trace("suspendResume=" + suspendResume);
124         }
125
126     }
127
128     @Override
129     public String getUsersPassword(String username) throws LoginException {
130
131         String password = null;
132         Connection conn = null;
133         PreparedStatement ps = null;
134         ResultSet rs = null;
135         try {
136             conn = getConnection();
137             // Get the password
138             if (logger.isDebugEnabled()) {
139                 logger.debug("Executing query: " + principalsQuery + ", with username: " + username);
140             }
141             ps = conn.prepareStatement(principalsQuery);
142             ps.setString(1, username);
143             rs = ps.executeQuery();
144             if (rs.next() == false) {
145                 if (logger.isDebugEnabled()) {
146                     logger.debug(principalsQuery + " returned no matches from db");
147                 }
148                 throw new FailedLoginException("No matching username found");
149             }
150
151             password = rs.getString(1);
152         } catch (SQLException ex) {
153                 if (logger.isTraceEnabled() == true) {
154                         logger.error("Could not open database to read AuthN tables.", ex);
155                 }
156             LoginException le = new LoginException("Authentication query failed: " + ex.getLocalizedMessage());
157             le.initCause(ex);
158             throw le;
159         } catch (Exception ex) {
160             LoginException le = new LoginException("Unknown Exception");
161             le.initCause(ex);
162             throw le;
163         } finally {
164             if (rs != null) {
165                 try {
166                     rs.close();
167                 } catch (SQLException e) {
168                 }
169             }
170             if (ps != null) {
171                 try {
172                     ps.close();
173                 } catch (SQLException e) {
174                 }
175             }
176             if (conn != null) {
177                 try {
178                     conn.close();
179                 } catch (SQLException ex) {
180                 }
181             }
182         }
183         return password;
184     }
185
186     /**
187      * Execute the rolesQuery against the datasourceName to obtain the roles for
188      * the authenticated user.
189      * @return collection containing the roles
190      */
191     @Override
192     public Collection<Group> getRoles(String username, String principalClassName, String groupClassName) throws LoginException {
193
194         if (logger.isDebugEnabled()) {
195             logger.debug("getRoleSets using rolesQuery: " + rolesQuery + ", username: " + username);
196         }
197
198         Connection conn = null;
199         HashMap<String, Group> groupsMap = new HashMap<String, Group>();
200         PreparedStatement ps = null;
201         ResultSet rs = null;
202
203         try {
204             conn = getConnection();
205             // Get the user role names
206             if (logger.isDebugEnabled()) {
207                 logger.debug("Executing query: " + rolesQuery + ", with username: " + username);
208             }
209
210             ps = conn.prepareStatement(rolesQuery);
211             try {
212                 ps.setString(1, username);
213             } catch (ArrayIndexOutOfBoundsException ignore) {
214                 // The query may not have any parameters so just try it
215             }
216             rs = ps.executeQuery();
217             if (rs.next() == false) {
218                 if (logger.isDebugEnabled()) {
219                     logger.debug("No roles found");
220                 }
221 //                if(aslm.getUnauthenticatedIdentity() == null){
222 //                    throw new FailedLoginException("No matching username found in Roles");
223 //                }
224                 /* We are running with an unauthenticatedIdentity so create an
225                 empty Roles set and return.
226                  */
227
228                 Group g = createGroup(groupClassName, "Roles");
229                 groupsMap.put(g.getName(), g);
230                 return groupsMap.values();
231             }
232
233             do {
234                 String roleName = rs.getString(1);
235                 String groupName = rs.getString(2);
236                 if (groupName == null || groupName.length() == 0) {
237                     groupName = "Roles";
238                 }
239
240                 Group group = (Group) groupsMap.get(groupName);
241                 if (group == null) {
242                     group = createGroup(groupClassName, groupName);
243                     groupsMap.put(groupName, group);
244                 }
245
246                 try {
247                     Principal p = createPrincipal(principalClassName, roleName);
248                     if (logger.isDebugEnabled()) {
249                         logger.debug("Assign user to role " + roleName);
250                     }
251
252                     group.addMember(p);
253                 } catch (Exception e) {
254                     logger.error("Failed to create principal: " + roleName + " " + e.toString());
255                 }
256
257             } while (rs.next());
258         } catch (SQLException ex) {
259             LoginException le = new LoginException("Query failed");
260             le.initCause(ex);
261             throw le;
262         } catch (Exception e) {
263             LoginException le = new LoginException("unknown exception");
264             le.initCause(e);
265             throw le;
266         } finally {
267             if (rs != null) {
268                 try {
269                     rs.close();
270                 } catch (SQLException e) {
271                 }
272             }
273             if (ps != null) {
274                 try {
275                     ps.close();
276                 } catch (SQLException e) {
277                 }
278             }
279             if (conn != null) {
280                 try {
281                     conn.close();
282                 } catch (Exception ex) {
283                 }
284             }
285
286         }
287
288         return groupsMap.values();
289
290     }
291
292     /**
293      * Execute the tenantsQuery against the datasourceName to obtain the tenants for
294      * the authenticated user.
295      * @return collection containing the roles
296      */
297     @Override
298     public Collection<Group> getTenants(String username, String groupClassName) throws LoginException {
299
300         if (logger.isDebugEnabled()) {
301             logger.debug("getTenants using tenantsQuery: " + tenantsQuery + ", username: " + username);
302         }
303
304         Connection conn = null;
305         HashMap<String, Group> groupsMap = new HashMap<String, Group>();
306         PreparedStatement ps = null;
307         ResultSet rs = null;
308
309         try {
310             conn = getConnection();
311             // Get the user role names
312             if (logger.isDebugEnabled()) {
313                 logger.debug("Executing query: " + tenantsQuery + ", with username: " + username);
314             }
315
316             ps = conn.prepareStatement(tenantsQuery);
317             try {
318                 ps.setString(1, username);
319             } catch (ArrayIndexOutOfBoundsException ignore) {
320                 // The query may not have any parameters so just try it
321             }
322             rs = ps.executeQuery();
323             if (rs.next() == false) {
324                 if (logger.isDebugEnabled()) {
325                     logger.debug("No tenants found");
326                 }
327                 // We are running with an unauthenticatedIdentity so create an
328                 // empty Tenants set and return.
329                 // FIXME  should this be allowed?
330                 Group g = createGroup(groupClassName, "Tenants");
331                 groupsMap.put(g.getName(), g);
332                 return groupsMap.values();
333             }
334
335             do {
336                 String tenantId = rs.getString(1);
337                 String tenantName = rs.getString(2);
338                 String groupName = rs.getString(3);
339                 if (groupName == null || groupName.length() == 0) {
340                     groupName = "Tenants";
341                 }
342
343                 Group group = (Group) groupsMap.get(groupName);
344                 if (group == null) {
345                     group = createGroup(groupClassName, groupName);
346                     groupsMap.put(groupName, group);
347                 }
348
349                 try {
350                     Principal p = createTenant(tenantName, tenantId);
351                     if (logger.isDebugEnabled()) {
352                         logger.debug("Assign user to tenant " + tenantName);
353                     }
354
355                     group.addMember(p);
356                 } catch (Exception e) {
357                     logger.error("Failed to create tenant: " + tenantName + " " + e.toString());
358                 }
359             } while (rs.next());
360         } catch (SQLException ex) {
361             LoginException le = new LoginException("Query failed");
362             le.initCause(ex);
363             throw le;
364         } catch (Exception e) {
365             LoginException le = new LoginException("unknown exception");
366             le.initCause(e);
367             throw le;
368         } finally {
369             if (rs != null) {
370                 try {
371                     rs.close();
372                 } catch (SQLException e) {
373                 }
374             }
375             if (ps != null) {
376                 try {
377                     ps.close();
378                 } catch (SQLException e) {
379                 }
380             }
381             if (conn != null) {
382                 try {
383                     conn.close();
384                 } catch (Exception ex) {
385                 }
386             }
387
388         }
389
390         return groupsMap.values();
391     }
392
393     private CSpaceTenant createTenant(String name, String id) throws Exception {
394         return new CSpaceTenant(name, id);
395     }
396
397     private Group createGroup(String groupClassName, String name) throws Exception {
398         return (Group) createPrincipal(groupClassName, name);
399     }
400
401     private Principal createPrincipal(String principalClassName, String name) throws Exception {
402         ClassLoader loader = Thread.currentThread().getContextClassLoader();
403         Class clazz = loader.loadClass(principalClassName);
404         Class[] ctorSig = {String.class};
405         Constructor ctor = clazz.getConstructor(ctorSig);
406         Object[] ctorArgs = {name};
407         Principal p = (Principal) ctor.newInstance(ctorArgs);
408         return p;
409     }
410
411     private Connection getConnection() throws LoginException, SQLException {
412         InitialContext ctx = null;
413         Connection conn = null;
414         String dataSourceName = getDataSourceName();
415         DataSource ds = null;
416         try {
417             ctx = new InitialContext();
418             try {
419                 ds = (DataSource) ctx.lookup(dataSourceName);
420             } catch (Exception e) {}
421             
422                 try {
423                         Context envCtx = (Context) ctx.lookup("java:comp/env");
424                         ds = (DataSource) envCtx.lookup(dataSourceName);
425                 } catch (Exception e) {}
426                 
427                 try {
428                         Context envCtx = (Context) ctx.lookup("java:comp");
429                         ds = (DataSource) envCtx.lookup(dataSourceName);
430                 } catch (Exception e) {}
431                 
432                 try {
433                         Context envCtx = (Context) ctx.lookup("java:");
434                         ds = (DataSource) envCtx.lookup(dataSourceName);
435                 } catch (Exception e) {}
436                 
437                 try {
438                         Context envCtx = (Context) ctx.lookup("java");
439                         ds = (DataSource) envCtx.lookup(dataSourceName);
440                 } catch (Exception e) {}
441                 
442                 try {
443                         ds = (DataSource) ctx.lookup("java:/" + dataSourceName);
444                 } catch (Exception e) {}  
445
446                 if (ds == null) {
447                 ds = AuthN.getDataSource();
448                 }
449                 
450             if (ds == null) {
451                 throw new IllegalArgumentException("datasource not found: " + dataSourceName);
452             }
453             
454             conn = ds.getConnection();
455             if (conn == null) {
456                 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.
457             }
458             return conn;
459         } catch (NamingException ex) {
460             LoginException le = new LoginException("Error looking up DataSource from: " + dataSourceName);
461             le.initCause(ex);
462             throw le;
463         } finally {
464             if (ctx != null) {
465                 try {
466                     ctx.close();
467                 } catch (Exception e) {
468                         e.printStackTrace();
469                 }
470             }
471         }
472
473     }
474
475     /**
476      * @return the datasourceName
477      */
478     public String getDataSourceName() {
479         return datasourceName;
480     }
481
482     /**
483      * @return the principalQuery
484      */
485     public String getPrincipalQuery() {
486         return principalsQuery;
487     }
488
489     /**
490      * @param principalQuery the principalQuery to set
491      */
492     public void setPrincipalQuery(String principalQuery) {
493         this.principalsQuery = principalQuery;
494     }
495
496     /**
497      * @return the roleQuery
498      */
499     public String getRoleQuery() {
500         return rolesQuery;
501     }
502
503     /**
504      * @param roleQuery the roleQuery to set
505      */
506     public void setRoleQuery(String roleQuery) {
507         this.rolesQuery = roleQuery;
508     }
509
510     /**
511      * @return the tenantQuery
512      */
513     public String getTenantQuery() {
514         return tenantsQuery;
515     }
516
517     /**
518      * @param tenantQuery the tenantQuery to set
519      */
520     public void setTenantQuery(String tenantQuery) {
521         this.tenantsQuery = tenantQuery;
522     }
523 }