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:
6 * http://www.collectionspace.org
7 * http://wiki.collectionspace.org
9 * Copyright 2009 University of California at Berkeley
11 * Licensed under the Educational Community License (ECL), Version 2.0.
12 * You may not use this file except in compliance with this License.
14 * You may obtain a copy of the ECL 2.0 License at
16 * https://source.collectionspace.org/collection-space/LICENSE.txt
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.
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:
28 * http://www.collectionspace.org
29 * http://wiki.collectionspace.org
31 * Copyright 2009 University of California at Berkeley
33 * Licensed under the Educational Community License (ECL), Version 2.0.
34 * You may not use this file except in compliance with this License.
36 * You may obtain a copy of the ECL 2.0 License at
38 * https://source.collectionspace.org/collection-space/LICENSE.txt
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.
47 * To change this template, choose Tools | Templates
48 * and open the template in the editor.
50 package org.collectionspace.authentication.realm.db;
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;
62 import javax.naming.InitialContext;
63 import javax.naming.NamingException;
64 import javax.security.auth.login.FailedLoginException;
65 import javax.security.auth.login.LoginException;
66 import javax.sql.DataSource;
68 //import org.apache.commons.logging.Log;
69 //import org.apache.commons.logging.LogFactory;
71 import org.collectionspace.authentication.CSpaceTenant;
72 import org.collectionspace.authentication.realm.CSpaceRealm;
74 import org.slf4j.Logger;
75 import org.slf4j.LoggerFactory;
78 * CSpaceDbRealm provides access to user, password, role, tenant database
81 public class CSpaceDbRealm implements CSpaceRealm {
83 private Logger logger = LoggerFactory.getLogger(CSpaceDbRealm.class);
85 private String datasourceName;
86 private String principalsQuery;
87 private String rolesQuery;
88 private String tenantsQuery;
89 private boolean suspendResume;
92 * CSpace Database Realm
93 * @param datasourceName datasource name
95 public CSpaceDbRealm(Map options) {
96 datasourceName = (String) options.get("dsJndiName");
97 if (datasourceName == null) {
98 datasourceName = "java:/DefaultDS";
100 Object tmp = options.get("principalsQuery");
102 principalsQuery = tmp.toString();
104 tmp = options.get("rolesQuery");
106 rolesQuery = tmp.toString();
108 tmp = options.get("tenantsQuery");
110 tenantsQuery = tmp.toString();
112 tmp = options.get("suspendResume");
114 suspendResume = Boolean.valueOf(tmp.toString()).booleanValue();
116 if (logger.isTraceEnabled()) {
117 logger.trace("DatabaseServerLoginModule, dsJndiName=" + datasourceName);
118 logger.trace("principalsQuery=" + principalsQuery);
119 logger.trace("rolesQuery=" + rolesQuery);
120 logger.trace("suspendResume=" + suspendResume);
126 public String getUsersPassword(String username) throws LoginException {
128 String password = null;
129 Connection conn = null;
130 PreparedStatement ps = null;
133 conn = getConnection();
135 if (logger.isDebugEnabled()) {
136 logger.debug("Executing query: " + principalsQuery + ", with username: " + username);
138 ps = conn.prepareStatement(principalsQuery);
139 ps.setString(1, username);
140 rs = ps.executeQuery();
141 if (rs.next() == false) {
142 if (logger.isDebugEnabled()) {
143 logger.debug(principalsQuery + " returned no matches from db");
145 throw new FailedLoginException("No matching username found");
148 password = rs.getString(1);
149 } catch (SQLException ex) {
150 LoginException le = new LoginException("Query failed");
153 } catch (Exception ex) {
154 LoginException le = new LoginException("Unknown Exception");
161 } catch (SQLException e) {
167 } catch (SQLException e) {
173 } catch (SQLException ex) {
181 * Execute the rolesQuery against the datasourceName to obtain the roles for
182 * the authenticated user.
183 * @return collection containing the roles
186 public Collection<Group> getRoles(String username, String principalClassName, String groupClassName) throws LoginException {
188 if (logger.isDebugEnabled()) {
189 logger.debug("getRoleSets using rolesQuery: " + rolesQuery + ", username: " + username);
192 Connection conn = null;
193 HashMap<String, Group> groupsMap = new HashMap<String, Group>();
194 PreparedStatement ps = null;
198 conn = getConnection();
199 // Get the user role names
200 if (logger.isDebugEnabled()) {
201 logger.debug("Executing query: " + rolesQuery + ", with username: " + username);
204 ps = conn.prepareStatement(rolesQuery);
206 ps.setString(1, username);
207 } catch (ArrayIndexOutOfBoundsException ignore) {
208 // The query may not have any parameters so just try it
210 rs = ps.executeQuery();
211 if (rs.next() == false) {
212 if (logger.isDebugEnabled()) {
213 logger.debug("No roles found");
215 // if(aslm.getUnauthenticatedIdentity() == null){
216 // throw new FailedLoginException("No matching username found in Roles");
218 /* We are running with an unauthenticatedIdentity so create an
219 empty Roles set and return.
222 Group g = createGroup(groupClassName, "Roles");
223 groupsMap.put(g.getName(), g);
224 return groupsMap.values();
228 String roleName = rs.getString(1);
229 String groupName = rs.getString(2);
230 if (groupName == null || groupName.length() == 0) {
234 Group group = (Group) groupsMap.get(groupName);
236 group = createGroup(groupClassName, groupName);
237 groupsMap.put(groupName, group);
241 Principal p = createPrincipal(principalClassName, roleName);
242 if (logger.isDebugEnabled()) {
243 logger.debug("Assign user to role " + roleName);
247 } catch (Exception e) {
248 logger.error("Failed to create principal: " + roleName + " " + e.toString());
252 } catch (SQLException ex) {
253 LoginException le = new LoginException("Query failed");
256 } catch (Exception e) {
257 LoginException le = new LoginException("unknown exception");
264 } catch (SQLException e) {
270 } catch (SQLException e) {
276 } catch (Exception ex) {
282 return groupsMap.values();
287 * Execute the tenantsQuery against the datasourceName to obtain the tenants for
288 * the authenticated user.
289 * @return collection containing the roles
292 public Collection<Group> getTenants(String username, String groupClassName) throws LoginException {
294 if (logger.isDebugEnabled()) {
295 logger.debug("getTenants using tenantsQuery: " + tenantsQuery + ", username: " + username);
298 Connection conn = null;
299 HashMap<String, Group> groupsMap = new HashMap<String, Group>();
300 PreparedStatement ps = null;
304 conn = getConnection();
305 // Get the user role names
306 if (logger.isDebugEnabled()) {
307 logger.debug("Executing query: " + tenantsQuery + ", with username: " + username);
310 ps = conn.prepareStatement(tenantsQuery);
312 ps.setString(1, username);
313 } catch (ArrayIndexOutOfBoundsException ignore) {
314 // The query may not have any parameters so just try it
316 rs = ps.executeQuery();
317 if (rs.next() == false) {
318 if (logger.isDebugEnabled()) {
319 logger.debug("No tenants found");
321 // We are running with an unauthenticatedIdentity so create an
322 // empty Tenants set and return.
323 // FIXME should this be allowed?
324 Group g = createGroup(groupClassName, "Tenants");
325 groupsMap.put(g.getName(), g);
326 return groupsMap.values();
330 String tenantId = rs.getString(1);
331 String tenantName = rs.getString(2);
332 String groupName = rs.getString(3);
333 if (groupName == null || groupName.length() == 0) {
334 groupName = "Tenants";
337 Group group = (Group) groupsMap.get(groupName);
339 group = createGroup(groupClassName, groupName);
340 groupsMap.put(groupName, group);
344 Principal p = createTenant(tenantName, tenantId);
345 if (logger.isDebugEnabled()) {
346 logger.debug("Assign user to tenant " + tenantName);
350 } catch (Exception e) {
351 logger.error("Failed to create tenant: " + tenantName + " " + e.toString());
354 } catch (SQLException ex) {
355 LoginException le = new LoginException("Query failed");
358 } catch (Exception e) {
359 LoginException le = new LoginException("unknown exception");
366 } catch (SQLException e) {
372 } catch (SQLException e) {
378 } catch (Exception ex) {
384 return groupsMap.values();
387 private CSpaceTenant createTenant(String name, String id) throws Exception {
388 return new CSpaceTenant(name, id);
391 private Group createGroup(String groupClassName, String name) throws Exception {
392 return (Group) createPrincipal(groupClassName, name);
395 private Principal createPrincipal(String principalClassName, String name) throws Exception {
396 ClassLoader loader = Thread.currentThread().getContextClassLoader();
397 Class clazz = loader.loadClass(principalClassName);
398 Class[] ctorSig = {String.class};
399 Constructor ctor = clazz.getConstructor(ctorSig);
400 Object[] ctorArgs = {name};
401 Principal p = (Principal) ctor.newInstance(ctorArgs);
405 private Connection getConnection() throws LoginException, SQLException {
406 InitialContext ctx = null;
407 Connection conn = null;
409 ctx = new InitialContext();
410 DataSource ds = (DataSource) ctx.lookup(getDataSourceName());
412 throw new IllegalArgumentException("datasource not found: " + getDataSourceName());
414 conn = ds.getConnection();
416 } catch (NamingException ex) {
417 LoginException le = new LoginException("Error looking up DataSource from: " + getDataSourceName());
424 } catch (Exception e) {
432 * @return the datasourceName
434 public String getDataSourceName() {
435 return datasourceName;
439 * @return the principalQuery
441 public String getPrincipalQuery() {
442 return principalsQuery;
446 * @param principalQuery the principalQuery to set
448 public void setPrincipalQuery(String principalQuery) {
449 this.principalsQuery = principalQuery;
453 * @return the roleQuery
455 public String getRoleQuery() {
460 * @param roleQuery the roleQuery to set
462 public void setRoleQuery(String roleQuery) {
463 this.rolesQuery = roleQuery;
467 * @return the tenantQuery
469 public String getTenantQuery() {
474 * @param tenantQuery the tenantQuery to set
476 public void setTenantQuery(String tenantQuery) {
477 this.tenantsQuery = tenantQuery;