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