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;
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;
70 import org.collectionspace.authentication.CSpaceTenant;
73 * CSpaceDbRealm provides access to user, password, role, tenant database
76 public class CSpaceDbRealm implements CSpaceRealm {
78 private static Log log = LogFactory.getLog(CSpaceDbRealm.class);
79 private String datasourceName;
80 private String principalsQuery;
81 private String rolesQuery;
82 private String tenantsQuery;
83 private boolean suspendResume;
86 * CSpace Database Realm
87 * @param datasourceName datasource name
89 public CSpaceDbRealm(Map options) {
90 datasourceName = (String) options.get("dsJndiName");
91 if (datasourceName == null) {
92 datasourceName = "java:/DefaultDS";
94 Object tmp = options.get("principalsQuery");
96 principalsQuery = tmp.toString();
98 tmp = options.get("rolesQuery");
100 rolesQuery = tmp.toString();
102 tmp = options.get("tenantsQuery");
104 tenantsQuery = tmp.toString();
106 tmp = options.get("suspendResume");
108 suspendResume = Boolean.valueOf(tmp.toString()).booleanValue();
110 if (log.isTraceEnabled()) {
111 log.trace("DatabaseServerLoginModule, dsJndiName=" + datasourceName);
112 log.trace("principalsQuery=" + principalsQuery);
113 log.trace("rolesQuery=" + rolesQuery);
114 log.trace("suspendResume=" + suspendResume);
120 public String getUsersPassword(String username) throws LoginException {
122 String password = null;
123 Connection conn = null;
124 PreparedStatement ps = null;
127 conn = getConnection();
129 if (log.isDebugEnabled()) {
130 log.debug("Executing query: " + principalsQuery + ", with username: " + username);
132 ps = conn.prepareStatement(principalsQuery);
133 ps.setString(1, username);
134 rs = ps.executeQuery();
135 if (rs.next() == false) {
136 if (log.isDebugEnabled()) {
137 log.debug(principalsQuery + " returned no matches from db");
139 throw new FailedLoginException("No matching username found");
142 password = rs.getString(1);
143 } catch (SQLException ex) {
144 LoginException le = new LoginException("Query failed");
147 } catch (Exception ex) {
148 LoginException le = new LoginException("Unknown Exception");
155 } catch (SQLException e) {
161 } catch (SQLException e) {
167 } catch (SQLException ex) {
175 * Execute the rolesQuery against the datasourceName to obtain the roles for
176 * the authenticated user.
177 * @return collection containing the roles
180 public Collection<Group> getRoles(String username, String principalClassName, String groupClassName) throws LoginException {
182 if (log.isDebugEnabled()) {
183 log.debug("getRoleSets using rolesQuery: " + rolesQuery + ", username: " + username);
186 Connection conn = null;
187 HashMap<String, Group> groupsMap = new HashMap<String, Group>();
188 PreparedStatement ps = null;
192 conn = getConnection();
193 // Get the user role names
194 if (log.isDebugEnabled()) {
195 log.debug("Executing query: " + rolesQuery + ", with username: " + username);
198 ps = conn.prepareStatement(rolesQuery);
200 ps.setString(1, username);
201 } catch (ArrayIndexOutOfBoundsException ignore) {
202 // The query may not have any parameters so just try it
204 rs = ps.executeQuery();
205 if (rs.next() == false) {
206 if (log.isDebugEnabled()) {
207 log.debug("No roles found");
209 // if(aslm.getUnauthenticatedIdentity() == null){
210 // throw new FailedLoginException("No matching username found in Roles");
212 /* We are running with an unauthenticatedIdentity so create an
213 empty Roles set and return.
216 Group g = createGroup(groupClassName, "Roles");
217 groupsMap.put(g.getName(), g);
218 return groupsMap.values();
222 String roleName = rs.getString(1);
223 String groupName = rs.getString(2);
224 if (groupName == null || groupName.length() == 0) {
228 Group group = (Group) groupsMap.get(groupName);
230 group = createGroup(groupClassName, groupName);
231 groupsMap.put(groupName, group);
235 Principal p = createPrincipal(principalClassName, roleName);
236 if (log.isDebugEnabled()) {
237 log.debug("Assign user to role " + roleName);
241 } catch (Exception e) {
242 log.error("Failed to create principal: " + roleName + " " + e.toString());
246 } catch (SQLException ex) {
247 LoginException le = new LoginException("Query failed");
250 } catch (Exception e) {
251 LoginException le = new LoginException("unknown exception");
258 } catch (SQLException e) {
264 } catch (SQLException e) {
270 } catch (Exception ex) {
276 return groupsMap.values();
281 * Execute the tenantsQuery against the datasourceName to obtain the tenants for
282 * the authenticated user.
283 * @return collection containing the roles
286 public Collection<Group> getTenants(String username, String groupClassName) throws LoginException {
288 if (log.isDebugEnabled()) {
289 log.debug("getTenants using tenantsQuery: " + tenantsQuery + ", username: " + username);
292 Connection conn = null;
293 HashMap<String, Group> groupsMap = new HashMap<String, Group>();
294 PreparedStatement ps = null;
298 conn = getConnection();
299 // Get the user role names
300 if (log.isDebugEnabled()) {
301 log.debug("Executing query: " + tenantsQuery + ", with username: " + username);
304 ps = conn.prepareStatement(tenantsQuery);
306 ps.setString(1, username);
307 } catch (ArrayIndexOutOfBoundsException ignore) {
308 // The query may not have any parameters so just try it
310 rs = ps.executeQuery();
311 if (rs.next() == false) {
312 if (log.isDebugEnabled()) {
313 log.debug("No tenants found");
315 // We are running with an unauthenticatedIdentity so create an
316 // empty Tenants set and return.
317 // FIXME should this be allowed?
318 Group g = createGroup(groupClassName, "Tenants");
319 groupsMap.put(g.getName(), g);
320 return groupsMap.values();
324 String tenantId = rs.getString(1);
325 String tenantName = rs.getString(2);
326 String groupName = rs.getString(3);
327 if (groupName == null || groupName.length() == 0) {
328 groupName = "Tenants";
331 Group group = (Group) groupsMap.get(groupName);
333 group = createGroup(groupClassName, groupName);
334 groupsMap.put(groupName, group);
338 Principal p = createTenant(tenantName, tenantId);
339 if (log.isDebugEnabled()) {
340 log.debug("Assign user to tenant " + tenantName);
344 } catch (Exception e) {
345 log.error("Failed to create tenant: " + tenantName + " " + e.toString());
348 } catch (SQLException ex) {
349 LoginException le = new LoginException("Query failed");
352 } catch (Exception e) {
353 LoginException le = new LoginException("unknown exception");
360 } catch (SQLException e) {
366 } catch (SQLException e) {
372 } catch (Exception ex) {
378 return groupsMap.values();
381 private CSpaceTenant createTenant(String name, String id) throws Exception {
382 return new CSpaceTenant(name, id);
385 private Group createGroup(String groupClassName, String name) throws Exception {
386 return (Group) createPrincipal(groupClassName, name);
389 private Principal createPrincipal(String principalClassName, String name) throws Exception {
390 ClassLoader loader = Thread.currentThread().getContextClassLoader();
391 Class clazz = loader.loadClass(principalClassName);
392 Class[] ctorSig = {String.class};
393 Constructor ctor = clazz.getConstructor(ctorSig);
394 Object[] ctorArgs = {name};
395 Principal p = (Principal) ctor.newInstance(ctorArgs);
399 private Connection getConnection() throws LoginException, SQLException {
400 InitialContext ctx = null;
401 Connection conn = null;
403 ctx = new InitialContext();
404 DataSource ds = (DataSource) ctx.lookup(getDataSourceName());
406 throw new IllegalArgumentException("datasource not found: " + getDataSourceName());
408 conn = ds.getConnection();
410 } catch (NamingException ex) {
411 LoginException le = new LoginException("Error looking up DataSource from: " + getDataSourceName());
418 } catch (Exception e) {
426 * @return the datasourceName
428 public String getDataSourceName() {
429 return datasourceName;
433 * @return the principalQuery
435 public String getPrincipalQuery() {
436 return principalsQuery;
440 * @param principalQuery the principalQuery to set
442 public void setPrincipalQuery(String principalQuery) {
443 this.principalsQuery = principalQuery;
447 * @return the roleQuery
449 public String getRoleQuery() {
454 * @param roleQuery the roleQuery to set
456 public void setRoleQuery(String roleQuery) {
457 this.rolesQuery = roleQuery;
461 * @return the tenantQuery
463 public String getTenantQuery() {
468 * @param tenantQuery the tenantQuery to set
470 public void setTenantQuery(String tenantQuery) {
471 this.tenantsQuery = tenantQuery;