1 package org.collectionspace.services.common.authorization_mgt;
3 import java.security.NoSuchAlgorithmException;
4 import java.sql.Connection;
5 import java.sql.PreparedStatement;
6 import java.sql.ResultSet;
7 import java.sql.SQLException;
8 import java.sql.Statement;
9 import java.util.ArrayList;
10 import java.util.Date;
11 import java.util.HashMap;
12 import java.util.HashSet;
13 import java.util.Hashtable;
14 import java.util.List;
16 import java.util.UUID;
18 import javax.naming.NamingException;
19 import javax.persistence.EntityManager;
20 import javax.persistence.EntityManagerFactory;
22 import org.collectionspace.authentication.AuthN;
23 import org.collectionspace.services.account.AccountListItem;
25 import org.collectionspace.services.authentication.Token;
26 import org.collectionspace.services.authorization.AuthZ;
27 import org.collectionspace.services.authorization.CSpaceAction;
28 import org.collectionspace.services.authorization.CSpaceResource;
29 import org.collectionspace.services.authorization.PermissionException;
30 import org.collectionspace.services.authorization.PermissionRole;
31 import org.collectionspace.services.authorization.PermissionRoleRel;
32 import org.collectionspace.services.authorization.PermissionValue;
33 import org.collectionspace.services.authorization.Role;
34 import org.collectionspace.services.authorization.RoleValue;
35 import org.collectionspace.services.authorization.SubjectType;
36 import org.collectionspace.services.authorization.URIResourceImpl;
37 import org.collectionspace.services.authorization.perms.ActionType;
38 import org.collectionspace.services.authorization.perms.EffectType;
39 import org.collectionspace.services.authorization.perms.Permission;
40 import org.collectionspace.services.authorization.perms.PermissionAction;
42 import org.collectionspace.services.client.Profiler;
43 import org.collectionspace.services.client.RoleClient;
44 import org.collectionspace.services.client.workflow.WorkflowClient;
46 import org.collectionspace.services.common.config.ServiceConfigUtils;
47 import org.collectionspace.services.common.config.TenantBindingConfigReaderImpl;
48 import org.collectionspace.services.common.context.ServiceBindingUtils;
49 import org.collectionspace.services.common.document.DocumentHandler;
50 import org.collectionspace.services.common.security.SecurityUtils;
51 import org.collectionspace.services.common.storage.DatabaseProductType;
52 import org.collectionspace.services.common.storage.JDBCTools;
53 import org.collectionspace.services.common.storage.jpa.JPATransactionContext;
54 import org.collectionspace.services.common.storage.jpa.JpaStorageUtils;
56 import org.collectionspace.services.config.service.ServiceBindingType;
57 import org.collectionspace.services.config.tenant.EmailConfig;
58 import org.collectionspace.services.config.tenant.PasswordResetConfig;
59 import org.collectionspace.services.config.tenant.TenantBindingType;
61 import org.collectionspace.services.lifecycle.Lifecycle;
62 import org.collectionspace.services.lifecycle.TransitionDef;
63 import org.collectionspace.services.lifecycle.TransitionDefList;
65 //import org.mortbay.log.Log;
66 import org.slf4j.Logger;
67 import org.slf4j.LoggerFactory;
68 import org.springframework.security.acls.model.AlreadyExistsException;
71 public class AuthorizationCommon {
73 final public static String REFRESH_AUTHZ_PROP = "refreshAuthZOnStartup";
76 // For token generation and password reset
78 final private static String DEFAULT_PASSWORD_RESET_EMAIL_MESSAGE = "Hello {{greeting}},\n\r\n\rYou've started the process to reset your CollectionSpace account password. To finish resetting your password, go to the Reset Password page {{link}} on CollectionSpace.\n\r\n\rIf clicking the link doesn't work, copy and paste the following link into your browser address bar and click Go.\n\r\n\r{{link}}\n\r Thanks,\n\r\n\r CollectionSpace Administrator\n\r\n\rPlease do not reply to this email. This mailbox is not monitored and you will not receive a response. For assistance, contact your CollectionSpace Administrator directly.";
79 final private static String tokensalt = "74102328UserDetailsReset";
80 final private static int TIME_SCALAR = 100000;
81 private static final String DEFAULT_PASSWORD_RESET_EMAIL_SUBJECT = "Password reset for CollectionSpace account";
84 // Keep track of the MD5 hash value for the tenant bindings
86 private static final Map<String, String> tenantConfigMD5HashTable = new HashMap<String, String>();
89 // ActionGroup labels/constants
93 final public static String ACTIONGROUP_CRUDL_NAME = "CRUDL";
94 final public static ActionType[] ACTIONSET_CRUDL = {ActionType.CREATE, ActionType.READ, ActionType.UPDATE, ActionType.DELETE, ActionType.SEARCH};
96 final public static String ACTIONGROUP_RL_NAME = "RL";
97 final public static ActionType[] ACTIONSET_RL = {ActionType.READ, ActionType.SEARCH};
99 static ActionGroup ACTIONGROUP_CRUDL;
100 static ActionGroup ACTIONGROUP_RL;
102 // A static block to initialize the predefined action groups
105 ACTIONGROUP_CRUDL = new ActionGroup();
106 ACTIONGROUP_CRUDL.name = ACTIONGROUP_CRUDL_NAME;
107 ACTIONGROUP_CRUDL.actions = ACTIONSET_CRUDL;
109 ACTIONGROUP_RL = new ActionGroup();
110 ACTIONGROUP_RL.name = ACTIONGROUP_RL_NAME;
111 ACTIONGROUP_RL.actions = ACTIONSET_RL;
115 final static Logger logger = LoggerFactory.getLogger(AuthorizationCommon.class);
117 final public static String ROLE_TENANT_ADMINISTRATOR = "TENANT_ADMINISTRATOR";
118 final public static String ROLE_TENANT_READER = "TENANT_READER";
120 public static final String TENANT_MANAGER_USER = "tenantManager";
121 public static final String TENANT_MANAGER_SCREEN_NAME = TENANT_MANAGER_USER;
122 public static final String DEFAULT_TENANT_MANAGER_PASSWORD = "manage";
123 public static final String DEFAULT_TENANT_MANAGER_EMAIL = "tenantManager@collectionspace.org";
125 public static final String TENANT_ADMIN_ACCT_PREFIX = "admin@";
126 public static final String TENANT_READER_ACCT_PREFIX = "reader@";
127 public static final String ROLE_PREFIX = "ROLE_";
128 public static final String TENANT_ADMIN_ROLE_SUFFIX = "_TENANT_ADMINISTRATOR";
129 public static final String TENANT_READER_ROLE_SUFFIX = "_TENANT_READER";
130 public static final String DEFAULT_ADMIN_PASSWORD = "Administrator";
131 public static final String DEFAULT_READER_PASSWORD = "reader";
133 // SQL for init tasks
134 final private static String INSERT_ACCOUNT_ROLE_SQL_MYSQL =
135 "INSERT INTO accounts_roles(account_id, user_id, role_id, role_name, created_at)"
136 +" VALUES(?, ?, ?, ?, now())";
137 final private static String INSERT_ACCOUNT_ROLE_SQL_POSTGRES =
138 "INSERT INTO accounts_roles(HJID, account_id, user_id, role_id, role_name, created_at)"
139 +" VALUES(nextval('hibernate_sequence'), ?, ?, ?, ?, now())";
140 final private static String QUERY_USERS_SQL =
141 "SELECT username FROM users WHERE username LIKE '"
142 +TENANT_ADMIN_ACCT_PREFIX+"%' OR username LIKE '"+TENANT_READER_ACCT_PREFIX+"%'";
143 final private static String INSERT_USER_SQL =
144 "INSERT INTO users (username,passwd, created_at) VALUES (?,?, now())";
145 final private static String INSERT_ACCOUNT_SQL =
146 "INSERT INTO accounts_common "
147 + "(csid, email, userid, status, screen_name, metadata_protection, roles_protection, created_at) "
148 + "VALUES (?,?,?,'ACTIVE',?, 'immutable', 'immutable', now())";
150 // TENANT MANAGER specific SQL
151 final private static String QUERY_TENANT_MGR_USER_SQL =
152 "SELECT username FROM users WHERE username = '"+TENANT_MANAGER_USER+"'";
153 final private static String GET_TENANT_MGR_ROLE_SQL =
154 "SELECT csid from roles WHERE tenant_id='" + AuthN.ALL_TENANTS_MANAGER_TENANT_ID + "' and rolename=?";
156 public static final String IGNORE_TENANT_ID = null; // A null constant to indicate an empty/unused value for the tenant ID
159 public static String getTenantConfigMD5Hash(String tenantId) {
160 return tenantConfigMD5HashTable.get(tenantId);
163 public static String setTenantConfigMD5Hash(String tenantId, String md5hash) {
164 return tenantConfigMD5HashTable.put(tenantId, md5hash);
168 public static Role xgetRole(String tenantId, String displayName) {
171 String roleName = AuthorizationCommon.getQualifiedRoleName(tenantId, displayName);
172 //role = AuthorizationStore.getRoleByName(roleName, tenantId);
177 public static Role getRole(JPATransactionContext jpaTransactionContext, String tenantId, String displayName) {
180 String roleName = AuthorizationCommon.getQualifiedRoleName(tenantId, displayName);
181 role = AuthorizationStore.getRoleByName(jpaTransactionContext, roleName, tenantId);
187 public static Role createRole(String tenantId, String name, String description) {
188 return createRole(tenantId, name, description, false /* mutable by default */);
191 public static Role createRole(String tenantId, String name, String description, boolean immutable) {
192 Role role = new Role();
194 role.setCreatedAtItem(new Date());
195 role.setDisplayName(name);
196 String roleName = AuthorizationCommon.getQualifiedRoleName(tenantId, name);
197 role.setRoleName(roleName);
198 String id = UUID.randomUUID().toString(); //FIXME: The qualified role name should be unique enough to use as an ID/key
200 role.setDescription(description);
201 role.setTenantId(tenantId);
202 if (immutable == true) {
203 role.setMetadataProtection(RoleClient.IMMUTABLE);
204 role.setPermsProtection(RoleClient.IMMUTABLE);
211 * Add permission to the Spring Security tables
212 * with assumption that resource is of type URI
213 * @param permission configuration
215 public static void addPermissionsForUri(Permission perm,
216 PermissionRole permRole) throws PermissionException {
218 // First check the integrity of the incoming arguments.
220 if (!perm.getCsid().equals(permRole.getPermission().get(0).getPermissionId())) {
221 throw new IllegalArgumentException("permission ids do not"
222 + " match for role=" + permRole.getRole().get(0).getRoleName()
223 + " with permissionId=" + permRole.getPermission().get(0).getPermissionId()
224 + " for permission with csid=" + perm.getCsid());
227 List<String> principals = new ArrayList<String>();
228 for (RoleValue roleValue : permRole.getRole()) {
229 principals.add(roleValue.getRoleName());
232 boolean grant = perm.getEffect().equals(EffectType.PERMIT) ? true : false;
233 List<PermissionAction> permActions = perm.getAction();
234 ArrayList<CSpaceResource> resources = new ArrayList<CSpaceResource>();
235 for (PermissionAction permAction : permActions) {
236 CSpaceAction action = URIResourceImpl.getAction(permAction.getName());
237 URIResourceImpl uriRes = new URIResourceImpl(perm.getTenantId(), perm.getResourceName(), action);
238 resources.add(uriRes);
240 AuthZ.get().addPermissions(resources.toArray(new CSpaceResource[0]), principals.toArray(new String[0]), grant); // CSPACE-4967
243 private static Connection getConnection(String databaseName) throws NamingException, SQLException {
244 return JDBCTools.getConnection(JDBCTools.CSPACE_DATASOURCE_NAME,
249 * Spring security seems to require that all of our role names start
250 * with the ROLE_PREFIX string.
252 public static String getQualifiedRoleName(String tenantId, String name) {
253 String result = name;
255 String qualifiedName = ROLE_PREFIX + tenantId.toUpperCase() + "_" + name.toUpperCase();
256 if (name.equals(qualifiedName) == false) {
257 result = qualifiedName;
263 private static ActionGroup getActionGroup(String actionGroupStr) {
264 ActionGroup result = null;
266 if (actionGroupStr.equalsIgnoreCase(ACTIONGROUP_CRUDL_NAME)) {
267 result = ACTIONGROUP_CRUDL;
268 } else if (actionGroupStr.equalsIgnoreCase(ACTIONGROUP_RL_NAME)) {
269 result = ACTIONGROUP_RL;
275 public static Permission createPermission(String tenantId,
278 String actionGroupStr) {
279 Permission result = null;
281 ActionGroup actionGroup = getActionGroup(actionGroupStr);
282 result = createPermission(tenantId, resourceName, description, actionGroup);
287 private static Permission createPermission(String tenantId,
290 ActionGroup actionGroup) {
292 + "-" + resourceName.replace('/', '_') // Remove the slashes so the ID can be used in a URI/URL
293 + "-" + actionGroup.name;
294 Permission perm = new Permission();
296 perm.setDescription(description);
297 perm.setCreatedAtItem(new Date());
298 perm.setResourceName(resourceName.toLowerCase().trim());
299 perm.setEffect(EffectType.PERMIT);
300 perm.setTenantId(tenantId);
302 perm.setActionGroup(actionGroup.name);
303 ArrayList<PermissionAction> pas = new ArrayList<PermissionAction>();
305 for (ActionType actionType : actionGroup.actions) {
306 PermissionAction permAction = createPermissionAction(perm, actionType);
313 private static Permission createWorkflowPermission(TenantBindingType tenantBinding,
314 ServiceBindingType serviceBinding,
315 String transitionVerb,
316 ActionGroup actionGroup)
318 Permission result = null;
319 String workFlowServiceSuffix;
320 String transitionName;
321 if (transitionVerb != null) {
322 transitionName = transitionVerb;
323 workFlowServiceSuffix = WorkflowClient.SERVICE_AUTHZ_SUFFIX;
325 transitionName = ""; //since the transitionDef was null, we're assuming that this is the base workflow permission to be created
326 workFlowServiceSuffix = WorkflowClient.SERVICE_PATH;
329 String tenantId = tenantBinding.getId();
330 String resourceName = "/"
331 + serviceBinding.getName().toLowerCase().trim()
332 + workFlowServiceSuffix
334 String description = "A generated workflow permission for actiongroup " + actionGroup.name;
335 result = createPermission(tenantId, resourceName, description, actionGroup);
337 if (logger.isDebugEnabled() == true) {
338 logger.debug("Generated a workflow permission: "
339 + result.getResourceName()
340 + ":" + transitionName
341 + ":" + "tenant id=" + result.getTenantId()
342 + ":" + actionGroup.name);
348 private static PermissionRole createPermissionRole(
349 Permission permission,
351 boolean enforceTenancy) throws Exception
353 PermissionRole permRole = new PermissionRole();
354 // Check to see if the tenant ID of the permission and the tenant ID of the role match
355 boolean tenantIdsMatch = role.getTenantId().equalsIgnoreCase(permission.getTenantId());
356 if (tenantIdsMatch == false && enforceTenancy == false) {
357 tenantIdsMatch = true; // If we don't need to enforce tenancy then we'll just consider them matched.
360 if (tenantIdsMatch == true) {
361 permRole.setSubject(SubjectType.ROLE);
363 // Set of the permission value list of the permrole
365 List<PermissionValue> permValues = new ArrayList<PermissionValue>();
366 PermissionValue permValue = new PermissionValue();
367 permValue.setPermissionId(permission.getCsid());
368 permValue.setResourceName(permission.getResourceName().toLowerCase());
369 permValue.setActionGroup(permission.getActionGroup());
370 permValues.add(permValue);
371 permRole.setPermission(permValues);
373 // Set of the role value list of the permrole
375 List<RoleValue> roleValues = new ArrayList<RoleValue>();
376 RoleValue rv = new RoleValue();
377 // This needs to use the qualified name, not the display name
378 rv.setRoleName(role.getRoleName());
379 rv.setRoleId(role.getCsid());
381 permRole.setRole(roleValues);
383 String errMsg = "The tenant ID of the role: " + role.getTenantId()
384 + " did not match the tenant ID of the permission: " + permission.getTenantId();
385 throw new Exception(errMsg);
391 private static Hashtable<String, String> getTenantNamesFromConfig(TenantBindingConfigReaderImpl tenantBindingConfigReader) {
393 // Note that this only handles tenants not marked as "createDisabled"
394 Hashtable<String, TenantBindingType> tenantBindings =
395 tenantBindingConfigReader.getTenantBindings();
396 Hashtable<String, String> tenantInfo = new Hashtable<String, String>();
397 for (TenantBindingType tenantBinding : tenantBindings.values()) {
398 String tId = tenantBinding.getId();
399 String tName = tenantBinding.getName();
400 tenantInfo.put(tId, tName);
401 if (logger.isDebugEnabled()) {
402 logger.debug("getTenantNamesFromConfig found configured tenant id: "+tId+" name: "+tName);
408 private static ArrayList<String> compileExistingTenants(Connection conn, Hashtable<String, String> tenantInfo)
409 throws SQLException, Exception {
410 Statement stmt = null;
411 ArrayList<String> existingTenants = new ArrayList<String>();
412 // First find or create the tenants
413 final String queryTenantSQL = "SELECT id,name FROM tenants";
415 stmt = conn.createStatement();
416 ResultSet rs = stmt.executeQuery(queryTenantSQL);
418 String tId = rs.getString("id");
419 String tName = rs.getString("name");
420 if(tenantInfo.containsKey(tId)) {
421 existingTenants.add(tId);
422 if(!tenantInfo.get(tId).equalsIgnoreCase(tName)) {
423 logger.warn("Configured name for tenant: "
424 +tId+" in repository: "+tName
425 +" does not match config'd name: "+ tenantInfo.get(tId));
430 } catch(Exception e) {
437 return existingTenants;
440 private static ArrayList<String> findOrCreateDefaultUsers(Connection conn, Hashtable<String, String> tenantInfo)
441 throws SQLException, Exception {
442 // Second find or create the users
443 Statement stmt = null;
444 PreparedStatement pstmt = null;
445 ArrayList<String> usersInRepo = new ArrayList<String>();
447 stmt = conn.createStatement();
448 ResultSet rs = stmt.executeQuery(QUERY_USERS_SQL);
450 String uName = rs.getString("username");
451 usersInRepo.add(uName);
454 pstmt = conn.prepareStatement(INSERT_USER_SQL); // create a statement
455 for(String tName : tenantInfo.values()) {
456 String adminAcctName = getDefaultAdminUserID(tName);
457 if(!usersInRepo.contains(adminAcctName)) {
458 String secEncPasswd = SecurityUtils.createPasswordHash(
459 adminAcctName, DEFAULT_ADMIN_PASSWORD);
460 pstmt.setString(1, adminAcctName); // set username param
461 pstmt.setString(2, secEncPasswd); // set passwd param
462 if (logger.isDebugEnabled()) {
463 logger.debug("createDefaultUsersAndAccounts adding user: "
464 +adminAcctName+" for tenant: "+tName);
466 pstmt.executeUpdate();
467 } else if (logger.isDebugEnabled()) {
468 logger.debug("createDefaultUsersAndAccounts: user: "+adminAcctName
469 +" already exists - skipping.");
473 String readerAcctName = getDefaultReaderUserID(tName);
474 if(!usersInRepo.contains(readerAcctName)) {
475 String secEncPasswd = SecurityUtils.createPasswordHash(
476 readerAcctName, DEFAULT_READER_PASSWORD);
477 pstmt.setString(1, readerAcctName); // set username param
478 pstmt.setString(2, secEncPasswd); // set passwd param
479 if (logger.isDebugEnabled()) {
480 logger.debug("createDefaultUsersAndAccounts adding user: "
481 +readerAcctName+" for tenant: "+tName);
483 pstmt.executeUpdate();
484 } else if (logger.isDebugEnabled()) {
485 logger.debug("createDefaultUsersAndAccounts: user: "+readerAcctName
486 +" already exists - skipping.");
490 } catch(Exception e) {
501 private static void findOrCreateDefaultAccounts(Connection conn, Hashtable<String, String> tenantInfo,
502 ArrayList<String> usersInRepo,
503 Hashtable<String, String> tenantAdminAcctCSIDs, Hashtable<String, String> tenantReaderAcctCSIDs)
504 throws SQLException, Exception {
505 // Third, create the accounts. Assume that if the users were already there,
506 // then the accounts were as well
507 PreparedStatement pstmt = null;
509 pstmt = conn.prepareStatement(INSERT_ACCOUNT_SQL); // create a statement
510 for(String tId : tenantInfo.keySet()) {
511 String tName = tenantInfo.get(tId);
512 String adminCSID = UUID.randomUUID().toString();
513 tenantAdminAcctCSIDs.put(tId, adminCSID);
514 String adminAcctName = getDefaultAdminUserID(tName);
515 if(!usersInRepo.contains(adminAcctName)) {
516 pstmt.setString(1, adminCSID); // set csid param
517 pstmt.setString(2, adminAcctName); // set email param (bogus)
518 pstmt.setString(3, adminAcctName); // set userid param
519 pstmt.setString(4, "Administrator");// set screen name param
520 if (logger.isDebugEnabled()) {
521 logger.debug("createDefaultAccounts adding account: "
522 +adminAcctName+" for tenant: "+tName);
524 pstmt.executeUpdate();
525 } else if (logger.isDebugEnabled()) {
526 logger.debug("createDefaultAccounts: user: "+adminAcctName
527 +" already exists - skipping account generation.");
530 String readerCSID = UUID.randomUUID().toString();
531 tenantReaderAcctCSIDs.put(tId, readerCSID);
532 String readerAcctName = getDefaultReaderUserID(tName);
533 if(!usersInRepo.contains(readerAcctName)) {
534 pstmt.setString(1, readerCSID); // set csid param
535 pstmt.setString(2, readerAcctName); // set email param (bogus)
536 pstmt.setString(3, readerAcctName); // set userid param
537 pstmt.setString(4, "Reader"); // set screen name param
538 if (logger.isDebugEnabled()) {
539 logger.debug("createDefaultAccounts adding account: "
540 +readerAcctName+" for tenant: "+tName);
542 pstmt.executeUpdate();
543 } else if (logger.isDebugEnabled()) {
544 logger.debug("createDefaultAccounts: user: "+readerAcctName
545 +" already exists - skipping account creation.");
549 } catch(Exception e) {
557 private static boolean findOrCreateTenantManagerUserAndAccount(Connection conn)
558 throws SQLException, Exception {
559 // Find or create the special tenant manager account.
560 // Later can make the user name for tenant manager be configurable, settable.
561 Statement stmt = null;
562 PreparedStatement pstmt = null;
563 boolean created = false;
565 boolean foundTMgrUser = false;
566 stmt = conn.createStatement();
567 ResultSet rs = stmt.executeQuery(QUERY_TENANT_MGR_USER_SQL);
568 // Should only find one - only consider it
570 String uName = rs.getString("username");
571 foundTMgrUser = uName.equals(TENANT_MANAGER_USER);
575 pstmt = conn.prepareStatement(INSERT_USER_SQL); // create a statement
576 String secEncPasswd = SecurityUtils.createPasswordHash(
577 TENANT_MANAGER_USER, DEFAULT_TENANT_MANAGER_PASSWORD);
578 pstmt.setString(1, TENANT_MANAGER_USER); // set username param
579 pstmt.setString(2, secEncPasswd); // set passwd param
580 if (logger.isDebugEnabled()) {
581 logger.debug("findOrCreateTenantManagerUserAndAccount adding tenant manager user: "
582 +TENANT_MANAGER_USER);
584 pstmt.executeUpdate();
586 // Now create the account to match
587 pstmt = conn.prepareStatement(INSERT_ACCOUNT_SQL); // create a statement
588 pstmt.setString(1, AuthN.TENANT_MANAGER_ACCT_ID); // set csid param
589 pstmt.setString(2, DEFAULT_TENANT_MANAGER_EMAIL); // set email param (bogus)
590 pstmt.setString(3, TENANT_MANAGER_USER); // set userid param
591 pstmt.setString(4, TENANT_MANAGER_SCREEN_NAME);// set screen name param
592 if (logger.isDebugEnabled()) {
593 logger.debug("findOrCreateTenantManagerUserAndAccount adding tenant manager account: "
594 +TENANT_MANAGER_USER);
596 pstmt.executeUpdate();
599 } else if (logger.isDebugEnabled()) {
600 logger.debug("findOrCreateTenantManagerUserAndAccount: tenant manager: "+TENANT_MANAGER_USER
601 +" already exists.");
603 } catch(Exception e) {
614 private static void bindDefaultAccountsToTenants(Connection conn, DatabaseProductType databaseProductType,
615 Hashtable<String, String> tenantInfo, ArrayList<String> usersInRepo,
616 Hashtable<String, String> tenantAdminAcctCSIDs, Hashtable<String, String> tenantReaderAcctCSIDs)
617 throws SQLException, Exception {
618 // Fourth, bind accounts to tenants. Assume that if the users were already there,
619 // then the accounts were bound to tenants correctly
620 PreparedStatement pstmt = null;
622 String insertAccountTenantSQL;
623 if (databaseProductType == DatabaseProductType.MYSQL) {
624 insertAccountTenantSQL =
625 "INSERT INTO accounts_tenants (TENANTS_ACCOUNTS_COMMON_CSID,tenant_id) "
627 } else if (databaseProductType == DatabaseProductType.POSTGRESQL) {
628 insertAccountTenantSQL =
629 "INSERT INTO accounts_tenants (HJID, TENANTS_ACCOUNTS_COMMON_CSID,tenant_id) "
630 + " VALUES(nextval('hibernate_sequence'), ?, ?)";
632 throw new Exception("Unrecognized database system.");
634 pstmt = conn.prepareStatement(insertAccountTenantSQL); // create a statement
635 for(String tId : tenantInfo.keySet()) {
636 String tName = tenantInfo.get(tId);
637 if(!usersInRepo.contains(getDefaultAdminUserID(tName))) {
638 String adminAcct = tenantAdminAcctCSIDs.get(tId);
639 pstmt.setString(1, adminAcct); // set acct CSID param
640 pstmt.setString(2, tId); // set tenant_id param
641 if (logger.isDebugEnabled()) {
642 logger.debug("createDefaultAccounts binding account id: "
643 +adminAcct+" to tenant id: "+tId);
645 pstmt.executeUpdate();
647 if(!usersInRepo.contains(getDefaultReaderUserID(tName))) {
648 String readerAcct = tenantReaderAcctCSIDs.get(tId);
649 pstmt.setString(1, readerAcct); // set acct CSID param
650 pstmt.setString(2, tId); // set tenant_id param
651 if (logger.isDebugEnabled()) {
652 logger.debug("createDefaultAccounts binding account id: "
653 +readerAcct+" to tenant id: "+tId);
655 pstmt.executeUpdate();
659 } catch(Exception e) {
668 private static String findOrCreateDefaultRoles(Connection conn, Hashtable<String, String> tenantInfo,
669 Hashtable<String, String> tenantAdminRoleCSIDs, Hashtable<String, String> tenantReaderRoleCSIDs)
670 throws SQLException, Exception {
671 // Fifth, fetch and save the default roles
672 String springAdminRoleCSID = null;
673 Statement stmt = null;
674 PreparedStatement pstmt = null;
676 final String querySpringRole =
677 "SELECT csid from roles WHERE rolename='"+AuthN.ROLE_SPRING_ADMIN_NAME+"'";
678 stmt = conn.createStatement();
679 ResultSet rs = stmt.executeQuery(querySpringRole);
681 springAdminRoleCSID = rs.getString(1);
682 if (logger.isDebugEnabled()) {
683 logger.debug("createDefaultAccounts found Spring Admin role: "
684 +springAdminRoleCSID);
687 final String insertSpringAdminRoleSQL =
688 "INSERT INTO roles (csid, rolename, displayName, rolegroup, created_at, tenant_id) "
689 + "VALUES ('-1', 'ROLE_SPRING_ADMIN', 'SPRING_ADMIN', 'Spring Security Administrator', now(), '0')";
690 stmt.executeUpdate(insertSpringAdminRoleSQL);
691 springAdminRoleCSID = "-1";
692 if (logger.isDebugEnabled()) {
693 logger.debug("createDefaultAccounts CREATED Spring Admin role: "
694 +springAdminRoleCSID);
698 final String getRoleCSIDSql =
699 "SELECT csid from roles WHERE tenant_id=? and rolename=?";
700 pstmt = conn.prepareStatement(getRoleCSIDSql); // create a statement
702 for(String tId : tenantInfo.keySet()) {
703 pstmt.setString(1, tId); // set tenant_id param
704 pstmt.setString(2, getDefaultAdminRole(tId)); // set rolename param
705 rs = pstmt.executeQuery();
706 // extract data from the ResultSet
708 throw new RuntimeException("Cannot find role: "+getDefaultAdminRole(tId)
709 +" for tenant id: "+tId+" in roles!");
711 String tenantAdminRoleCSID = rs.getString(1);
712 if (logger.isDebugEnabled()) {
713 logger.debug("createDefaultAccounts found role: "
714 +getDefaultAdminRole(tId)+"("+tenantAdminRoleCSID
715 +") for tenant id: "+tId);
717 tenantAdminRoleCSIDs.put(tId, tenantAdminRoleCSID);
718 pstmt.setString(1, tId); // set tenant_id param
719 pstmt.setString(2, getDefaultReaderRole(tId)); // set rolename param
721 rs = pstmt.executeQuery();
722 // extract data from the ResultSet
724 throw new RuntimeException("Cannot find role: "+getDefaultReaderRole(tId)
725 +" for tenant id: "+tId+" in roles!");
727 String tenantReaderRoleCSID = rs.getString(1);
728 if (logger.isDebugEnabled()) {
729 logger.debug("createDefaultAccounts found role: "
730 +getDefaultReaderRole(tId)+"("+tenantReaderRoleCSID
731 +") for tenant id: "+tId);
733 tenantReaderRoleCSIDs.put(tId, tenantReaderRoleCSID);
737 } catch(Exception e) {
745 return springAdminRoleCSID;
748 private static String findTenantManagerRole(Connection conn )
749 throws SQLException, RuntimeException, Exception {
750 String tenantMgrRoleCSID = null;
751 PreparedStatement pstmt = null;
753 String rolename = getQualifiedRoleName(AuthN.ALL_TENANTS_MANAGER_TENANT_ID,
754 AuthN.ROLE_ALL_TENANTS_MANAGER);
755 pstmt = conn.prepareStatement(GET_TENANT_MGR_ROLE_SQL); // create a statement
757 pstmt.setString(1, rolename); // set rolename param
758 rs = pstmt.executeQuery();
760 tenantMgrRoleCSID = rs.getString(1);
761 if (logger.isDebugEnabled()) {
762 logger.debug("findTenantManagerRole found Tenant Mgr role: "
767 } catch(Exception e) {
773 if(tenantMgrRoleCSID==null)
774 throw new RuntimeException("findTenantManagerRole: Cound not find tenant Manager Role!");
775 return tenantMgrRoleCSID;
778 private static void bindAccountsToRoles(Connection conn, DatabaseProductType databaseProductType,
779 Hashtable<String, String> tenantInfo, ArrayList<String> usersInRepo,
780 String springAdminRoleCSID,
781 Hashtable<String, String> tenantAdminRoleCSIDs, Hashtable<String, String> tenantReaderRoleCSIDs,
782 Hashtable<String, String> tenantAdminAcctCSIDs, Hashtable<String, String> tenantReaderAcctCSIDs)
783 throws SQLException, Exception {
784 // Sixth, bind the accounts to roles. If the users already existed,
785 // we'll assume they were set up correctly.
786 PreparedStatement pstmt = null;
788 String insertAccountRoleSQL;
789 if (databaseProductType == DatabaseProductType.MYSQL) {
790 insertAccountRoleSQL = INSERT_ACCOUNT_ROLE_SQL_MYSQL;
791 } else if (databaseProductType == DatabaseProductType.POSTGRESQL) {
792 insertAccountRoleSQL = INSERT_ACCOUNT_ROLE_SQL_POSTGRES;
794 throw new Exception("Unrecognized database system.");
796 if (logger.isDebugEnabled()) {
797 logger.debug("createDefaultAccounts binding accounts to roles with SQL:\n"
798 +insertAccountRoleSQL);
800 pstmt = conn.prepareStatement(insertAccountRoleSQL); // create a statement
801 for(String tId : tenantInfo.keySet()) {
802 String adminUserId = getDefaultAdminUserID(tenantInfo.get(tId));
803 if(!usersInRepo.contains(adminUserId)) {
804 String adminAcct = tenantAdminAcctCSIDs.get(tId);
805 String adminRoleId = tenantAdminRoleCSIDs.get(tId);
806 pstmt.setString(1, adminAcct); // set acct CSID param
807 pstmt.setString(2, adminUserId); // set user_id param
808 pstmt.setString(3, adminRoleId); // set role_id param
809 pstmt.setString(4, getDefaultAdminRole(tId)); // set rolename param
810 if (logger.isDebugEnabled()) {
811 logger.debug("createDefaultAccounts binding account: "
812 +adminUserId+" to Admin role("+adminRoleId
813 +") for tenant id: "+tId);
815 pstmt.executeUpdate();
816 // Now add the Spring Admin Role to the admin accounts
817 pstmt.setString(3, springAdminRoleCSID); // set role_id param
818 pstmt.setString(4, AuthN.ROLE_SPRING_ADMIN_NAME); // set rolename param
819 if (logger.isDebugEnabled()) {
820 logger.debug("createDefaultAccounts binding account: "
821 +adminUserId+" to Spring Admin role: "+springAdminRoleCSID);
823 pstmt.executeUpdate();
825 String readerUserId = getDefaultReaderUserID(tenantInfo.get(tId));
826 if(!usersInRepo.contains(readerUserId)) {
827 String readerAcct = tenantReaderAcctCSIDs.get(tId);
828 String readerRoleId = tenantReaderRoleCSIDs.get(tId);
829 pstmt.setString(1, readerAcct); // set acct CSID param
830 pstmt.setString(2, readerUserId); // set user_id param
831 pstmt.setString(3, readerRoleId); // set role_id param
832 pstmt.setString(4, getDefaultReaderRole(tId)); // set rolename param
833 if (logger.isDebugEnabled()) {
834 logger.debug("createDefaultAccounts binding account: "
835 +readerUserId+" to Reader role("+readerRoleId
836 +") for tenant id: "+tId);
838 pstmt.executeUpdate();
842 } catch(Exception e) {
850 private static void bindTenantManagerAccountRole(Connection conn, DatabaseProductType databaseProductType,
851 String tenantManagerUserID, String tenantManagerAccountID, String tenantManagerRoleID, String tenantManagerRoleName )
852 throws SQLException, Exception {
853 PreparedStatement pstmt = null;
855 String insertAccountRoleSQL;
856 if (databaseProductType == DatabaseProductType.MYSQL) {
857 insertAccountRoleSQL = INSERT_ACCOUNT_ROLE_SQL_MYSQL;
858 } else if (databaseProductType == DatabaseProductType.POSTGRESQL) {
859 insertAccountRoleSQL = INSERT_ACCOUNT_ROLE_SQL_POSTGRES;
861 throw new Exception("Unrecognized database system.");
863 if (logger.isDebugEnabled()) {
864 logger.debug("bindTenantManagerAccountRole binding account to role with SQL:\n"
865 +insertAccountRoleSQL);
867 pstmt = conn.prepareStatement(insertAccountRoleSQL); // create a statement
868 pstmt.setString(1, tenantManagerAccountID); // set acct CSID param
869 pstmt.setString(2, tenantManagerUserID); // set user_id param
870 pstmt.setString(3, tenantManagerRoleID); // set role_id param
871 pstmt.setString(4, tenantManagerRoleName); // set rolename param
872 if (logger.isDebugEnabled()) {
873 logger.debug("bindTenantManagerAccountRole binding user: "
874 +tenantManagerUserID+" to Admin role("+tenantManagerRoleName+")");
876 pstmt.executeUpdate();
877 /* At this point, tenant manager should not need the Spring Admin Role
878 pstmt.setString(3, springAdminRoleCSID); // set role_id param
879 pstmt.setString(4, SPRING_ADMIN_ROLE); // set rolename param
880 if (logger.isDebugEnabled()) {
881 logger.debug("createDefaultAccounts binding account: "
882 +adminUserId+" to Spring Admin role: "+springAdminRoleCSID);
884 pstmt.executeUpdate();
887 } catch(Exception e) {
896 * Using the tenant bindings, ensure there are corresponding Tenant records (db columns).
898 //FIXME: This code should be using JPA objects and JPATransactionContext, not raw SQL.
899 public static void createTenants(
900 TenantBindingConfigReaderImpl tenantBindingConfigReader,
901 DatabaseProductType databaseProductType,
902 String cspaceDatabaseName) throws Exception {
903 logger.debug("ServiceMain.createTenants starting...");
904 Hashtable<String, String> tenantInfo = getTenantNamesFromConfig(tenantBindingConfigReader);
905 Connection conn = null;
907 conn = getConnection(cspaceDatabaseName);
908 ArrayList<String> existingTenants = compileExistingTenants(conn, tenantInfo);
910 // Note that this only creates tenants not marked as "createDisabled"
911 createMissingTenants(conn, tenantInfo, existingTenants);
912 } catch (Exception e) {
913 logger.debug("Exception in createTenants: " + e.getLocalizedMessage());
919 } catch (SQLException sqle) {
920 if (logger.isDebugEnabled()) {
921 logger.debug("SQL Exception closing statement/connection: " + sqle.getLocalizedMessage());
929 * @param tenantBindingConfigReader
930 * @param databaseProductType
931 * @param cspaceDatabaseName
934 //FIXME: This code should be using the JPA objects and JPATransactionContext, not raw SQL.
935 public static void createDefaultAccounts(
936 TenantBindingConfigReaderImpl tenantBindingConfigReader,
937 DatabaseProductType databaseProductType,
938 String cspaceDatabaseName) throws Exception {
940 logger.debug("ServiceMain.createDefaultAccounts starting...");
942 Hashtable<String, String> tenantInfo = getTenantNamesFromConfig(tenantBindingConfigReader);
943 Connection conn = null;
944 // TODO - need to put in tests for existence first.
945 // We could just look for the accounts per tenant up front, and assume that
946 // the rest is there if the accounts are.
947 // Could add a sql script to remove these if need be - Spring only does roles,
948 // and we're not touching that, so we could safely toss the
949 // accounts, users, account-tenants, account-roles, and start over.
951 conn = getConnection(cspaceDatabaseName);
953 ArrayList<String> usersInRepo = findOrCreateDefaultUsers(conn, tenantInfo);
955 Hashtable<String, String> tenantAdminAcctCSIDs = new Hashtable<String, String>();
956 Hashtable<String, String> tenantReaderAcctCSIDs = new Hashtable<String, String>();
957 findOrCreateDefaultAccounts(conn, tenantInfo, usersInRepo,
958 tenantAdminAcctCSIDs, tenantReaderAcctCSIDs);
960 bindDefaultAccountsToTenants(conn, databaseProductType, tenantInfo, usersInRepo,
961 tenantAdminAcctCSIDs, tenantReaderAcctCSIDs);
963 Hashtable<String, String> tenantAdminRoleCSIDs = new Hashtable<String, String>();
964 Hashtable<String, String> tenantReaderRoleCSIDs = new Hashtable<String, String>();
965 String springAdminRoleCSID = findOrCreateDefaultRoles(conn, tenantInfo,
966 tenantAdminRoleCSIDs, tenantReaderRoleCSIDs);
968 bindAccountsToRoles(conn, databaseProductType,
969 tenantInfo, usersInRepo, springAdminRoleCSID,
970 tenantAdminRoleCSIDs, tenantReaderRoleCSIDs,
971 tenantAdminAcctCSIDs, tenantReaderAcctCSIDs);
973 boolean createdTenantMgrAccount = findOrCreateTenantManagerUserAndAccount(conn);
974 if(createdTenantMgrAccount) {
975 // If we created the account, we need to create the bindings. Otherwise, assume they
976 // are all set (from previous initialization).
977 String tenantManagerRoleCSID = findTenantManagerRole(conn);
978 bindTenantManagerAccountRole(conn, databaseProductType,
979 TENANT_MANAGER_USER, AuthN.TENANT_MANAGER_ACCT_ID,
980 tenantManagerRoleCSID, AuthN.ROLE_ALL_TENANTS_MANAGER);
982 } catch (Exception e) {
983 logger.debug("Exception in createDefaultAccounts: " + e.getLocalizedMessage());
989 } catch (SQLException sqle) {
990 if (logger.isDebugEnabled()) {
991 logger.debug("SQL Exception closing statement/connection: " + sqle.getLocalizedMessage());
997 private static String getDefaultAdminRole(String tenantId) {
998 return ROLE_PREFIX+tenantId+TENANT_ADMIN_ROLE_SUFFIX;
1001 private static String getDefaultReaderRole(String tenantId) {
1002 return ROLE_PREFIX+tenantId+TENANT_READER_ROLE_SUFFIX;
1005 private static String getDefaultAdminUserID(String tenantName) {
1006 return TENANT_ADMIN_ACCT_PREFIX+tenantName;
1009 private static String getDefaultReaderUserID(String tenantName) {
1010 return TENANT_READER_ACCT_PREFIX+tenantName;
1013 static public PermissionAction createPermissionAction(Permission perm,
1014 ActionType actionType) {
1015 PermissionAction pa = new PermissionAction();
1017 CSpaceAction action = URIResourceImpl.getAction(actionType);
1018 URIResourceImpl uriRes = new URIResourceImpl(perm.getTenantId(),
1019 perm.getResourceName(), action);
1020 pa.setName(actionType);
1021 pa.setObjectIdentity(uriRes.getHashedId().toString());
1022 pa.setObjectIdentityResource(uriRes.getId());
1027 static public PermissionAction update(Permission perm, PermissionAction permAction) {
1028 PermissionAction pa = new PermissionAction();
1030 CSpaceAction action = URIResourceImpl.getAction(permAction.getName());
1031 URIResourceImpl uriRes = new URIResourceImpl(perm.getTenantId(),
1032 perm.getResourceName(), action);
1033 pa.setObjectIdentity(uriRes.getHashedId().toString());
1034 pa.setObjectIdentityResource(uriRes.getId());
1039 private static HashSet<String> getTransitionVerbList(TenantBindingType tenantBinding, ServiceBindingType serviceBinding) {
1040 HashSet<String> result = new HashSet<String>();
1042 TransitionDefList transitionDefList = getTransitionDefList(tenantBinding, serviceBinding);
1043 for (TransitionDef transitionDef : transitionDefList.getTransitionDef()) {
1044 String transitionVerb = transitionDef.getName();
1045 String[] tokens = transitionVerb.split("_"); // Split the verb into words. The workflow verbs are compound words combined with the '_' character.
1046 result.add(tokens[0]); // We only care about the first word.
1052 private static TransitionDefList getTransitionDefList(TenantBindingType tenantBinding, ServiceBindingType serviceBinding) {
1053 TransitionDefList result = null;
1055 String serviceObjectName = serviceBinding.getObject().getName();
1056 DocumentHandler docHandler = ServiceConfigUtils.createDocumentHandlerInstance(
1057 tenantBinding, serviceBinding);
1058 Lifecycle lifecycle = docHandler.getLifecycle(serviceObjectName);
1059 if (lifecycle != null) {
1060 result = lifecycle.getTransitionDefList();
1062 } catch (Exception e) {
1063 // Ignore this exception and return an empty non-null TransitionDefList
1066 if (result == null) {
1067 if (serviceBinding.getType().equalsIgnoreCase(ServiceBindingUtils.SERVICE_TYPE_SECURITY) == false) {
1068 logger.debug("Could not retrieve a lifecycle transition definition list from: "
1069 + serviceBinding.getName()
1070 + " with tenant ID = "
1071 + tenantBinding.getId());
1073 // return an empty list
1074 result = new TransitionDefList();
1076 logger.debug("Successfully retrieved a lifecycle transition definition list from: "
1077 + serviceBinding.getName()
1078 + " with tenant ID = "
1079 + tenantBinding.getId());
1087 * @param tenantBindingConfigReader
1088 * @param databaseProductType
1089 * @param cspaceDatabaseName
1092 public static void createDefaultWorkflowPermissions(
1093 JPATransactionContext jpaTransactionContext,
1094 TenantBindingConfigReaderImpl tenantBindingConfigReader,
1095 DatabaseProductType databaseProductType,
1096 String cspaceDatabaseName) throws Exception //FIXME: REM - 4/11/2012 - Rename to createWorkflowPermissions
1098 java.util.logging.Logger logger = java.util.logging.Logger.getAnonymousLogger();
1100 AuthZ.get().login(); //login to Spring Security manager
1103 Hashtable<String, TenantBindingType> tenantBindings = tenantBindingConfigReader.getTenantBindings();
1104 for (String tenantId : tenantBindings.keySet()) {
1105 logger.info(String.format("Creating/verifying workflow permissions for tenant ID=%s.", tenantId));
1106 TenantBindingType tenantBinding = tenantBindings.get(tenantId);
1107 if (tenantBinding.isConfigChangedSinceLastStart() == false) {
1108 continue; // skip the rest of the loop and go to the next tenant
1111 Role adminRole = AuthorizationCommon.getRole(jpaTransactionContext, tenantBinding.getId(), ROLE_TENANT_ADMINISTRATOR);
1112 Role readonlyRole = AuthorizationCommon.getRole(jpaTransactionContext, tenantBinding.getId(), ROLE_TENANT_READER);
1114 if (adminRole == null || readonlyRole == null) {
1115 String msg = String.format("One or more of the required default CollectionSpace administrator roles is missing or was never created. If you're setting up a new instance of CollectionSpace, shutdown the Tomcat server and run the 'ant import' command from the root/top level CollectionSpace 'Services' source directory. Then try restarting Tomcat.");
1117 throw new RuntimeException("One or more of the required default CollectionSpace administrator roles is missing or was never created.");
1120 for (ServiceBindingType serviceBinding : tenantBinding.getServiceBindings()) {
1121 String prop = ServiceBindingUtils.getPropertyValue(serviceBinding, REFRESH_AUTHZ_PROP);
1122 if (prop == null ? true : Boolean.parseBoolean(prop)) {
1124 jpaTransactionContext.beginTransaction();
1125 TransitionDefList transitionDefList = getTransitionDefList(tenantBinding, serviceBinding);
1126 HashSet<String> transitionVerbList = getTransitionVerbList(tenantBinding, serviceBinding);
1127 for (String transitionVerb : transitionVerbList) {
1129 // Create the permission for the admin role
1130 Permission adminPerm = createWorkflowPermission(tenantBinding, serviceBinding, transitionVerb, ACTIONGROUP_CRUDL);
1131 persist(jpaTransactionContext, adminPerm, adminRole, true, ACTIONGROUP_CRUDL);
1133 // Create the permission for the read-only role
1134 Permission readonlyPerm = createWorkflowPermission(tenantBinding, serviceBinding, transitionVerb, ACTIONGROUP_RL);
1135 persist(jpaTransactionContext, readonlyPerm, readonlyRole, true, ACTIONGROUP_RL); // Persist/store the permission and permrole records and related Spring Security info
1137 jpaTransactionContext.commitTransaction();
1138 } catch (IllegalStateException e) {
1139 logger.fine(e.getLocalizedMessage()); //We end up here if there is no document handler for the service -this is ok for some of the services.
1140 } catch (Exception x) {
1141 jpaTransactionContext.markForRollback();
1144 logger.warning("AuthZ refresh service binding property is set to FALSE so default permissions will NOT be refreshed for: "
1145 + serviceBinding.getName());
1149 } catch (Exception e) {
1150 jpaTransactionContext.markForRollback();
1151 logger.fine("Caught exception and rolling back permission creation: " + e.getMessage());
1156 private static void createMissingTenants(Connection conn, Hashtable<String, String> tenantInfo,
1157 ArrayList<String> existingTenants) throws SQLException, Exception {
1158 // Need to define and look for a createDisabled attribute in tenant config
1159 final String insertTenantSQL =
1160 "INSERT INTO tenants (id,name,authorities_initialized,disabled,created_at) VALUES (?,?,FALSE,FALSE,now())";
1161 PreparedStatement pstmt = null;
1163 pstmt = conn.prepareStatement(insertTenantSQL); // create a statement
1164 for(String tId : tenantInfo.keySet()) {
1165 if(existingTenants.contains(tId)) {
1166 if (logger.isDebugEnabled()) {
1167 logger.debug("createMissingTenants: tenant exists (skipping): "
1168 +tenantInfo.get(tId));
1172 pstmt.setString(1, tId); // set id param
1173 pstmt.setString(2, tenantInfo.get(tId)); // set name param
1174 if (logger.isDebugEnabled()) {
1175 logger.debug("createMissingTenants adding entry for tenant: "+tId);
1177 pstmt.executeUpdate();
1180 } catch(Exception e) {
1188 public static String getPersistedMD5Hash(String tenantId, String cspaceDatabaseName) throws Exception {
1189 String result = null;
1191 ArrayList<String> existingTenants = new ArrayList<String>();
1192 // First find or create the tenants
1193 final String queryTenantSQL = String.format("SELECT id, name, config_md5hash FROM tenants WHERE id = '%s'", tenantId);
1195 Statement stmt = null;
1199 conn = getConnection(cspaceDatabaseName);
1200 stmt = conn.createStatement();
1201 ResultSet rs = stmt.executeQuery(queryTenantSQL);
1204 String errMsg = String.format("Unable to configure tenant ID='%s'. There appears to be more than one tenant with that ID in the AuthN/AuthZ database named '%s'.",
1205 tenantId, cspaceDatabaseName);
1206 throw new Exception(errMsg);
1208 String tId = rs.getString("id"); // for debugging only
1209 String tName = rs.getString("name"); // for debugging only
1210 result = rs.getString("config_md5hash");
1214 } catch(Exception e) {
1217 if (stmt != null) stmt.close();
1223 private static PermissionRoleRel findPermRoleRel(
1224 JPATransactionContext jpaTransactionContext,
1225 String permissionId,
1227 PermissionRoleRel result = null;
1230 String whereClause = "where permissionId = :id and roleId = :roleId";
1231 HashMap<String, Object> params = new HashMap<String, Object>();
1232 params.put("id", permissionId);
1233 params.put("roleId", RoleId);
1235 result = (PermissionRoleRel) JpaStorageUtils.getEntity(jpaTransactionContext,
1236 PermissionRoleRel.class.getCanonicalName(), whereClause, params);
1237 } catch (Exception e) {
1238 //Do nothing. Will return null;
1245 * Persists the Permission, PermissionRoleRel, and Spring Security table entries all in one transaction
1247 private static void persist(JPATransactionContext jpaTransactionContext, Permission permission, Role role, boolean enforceTenancy, ActionGroup actionGroup) throws Exception {
1248 AuthorizationStore authzStore = new AuthorizationStore();
1249 // First persist the Permission record
1250 authzStore.store(jpaTransactionContext, permission);
1252 // If the PermRoleRel doesn't already exists then relate the permission and the role in a new PermissionRole (the service payload)
1253 // Create a PermissionRoleRel (the database relation table for the permission and role)
1254 PermissionRoleRel permRoleRel = findPermRoleRel(jpaTransactionContext, permission.getCsid(), role.getCsid());
1255 if (permRoleRel == null) {
1256 PermissionRole permRole = createPermissionRole(permission, role, enforceTenancy);
1257 List<PermissionRoleRel> permRoleRels = new ArrayList<PermissionRoleRel>();
1258 PermissionRoleUtil.buildPermissionRoleRel(jpaTransactionContext, permRole, SubjectType.ROLE, permRoleRels,
1259 false /*not for delete*/, role.getTenantId());
1260 for (PermissionRoleRel prr : permRoleRels) {
1261 authzStore.store(jpaTransactionContext, prr);
1263 Profiler profiler = new Profiler(AuthorizationCommon.class, 2);
1265 // Add a corresponding entry in the Spring Security Tables
1266 addPermissionsForUri(permission, permRole);
1268 logger.debug("Finished full perm generation for "
1269 + ":" + permission.getTenantId()
1270 + ":" + permission.getResourceName()
1271 + ":" + actionGroup.getName()
1272 + ":" + profiler.getCumulativeTime());
1277 public static boolean hasTokenExpired(EmailConfig emailConfig, Token token) throws NoSuchAlgorithmException {
1278 boolean result = false;
1280 int maxConfigSeconds = emailConfig.getPasswordResetConfig().getTokenExpirationSeconds().intValue();
1281 int maxTokenSeconds = token.getExpireSeconds().intValue();
1283 long createdTime = token.getCreatedAtItem().getTime();
1284 long configExpirationTime = createdTime + maxConfigSeconds * 1000; // the current tenant config for how long a token stays valid
1285 long tokenDefinedExirationTime = createdTime + maxTokenSeconds * 1000; // the tenant config for how long a token stays valid when the token was created.
1287 if (configExpirationTime != tokenDefinedExirationTime) {
1288 String msg = String.format("The configured expiration time for the token = '%s' changed from when the token was created.",
1293 // Note: the current tenant bindings config for expiration takes precedence over the config used to create the token.
1295 if (System.currentTimeMillis() >= configExpirationTime) {
1303 * Validate that the password reset configuration is correct.
1305 private static String validatePasswordResetConfig(PasswordResetConfig passwordResetConfig) {
1306 String result = null;
1308 if (passwordResetConfig != null) {
1309 result = passwordResetConfig.getMessage();
1310 if (result == null || result.length() == 0) {
1311 result = DEFAULT_PASSWORD_RESET_EMAIL_MESSAGE;
1312 logger.warn("Could not find a password reset message in the tenant's configuration. Using the default one");
1315 if (result.contains("{{link}}") == false) {
1316 logger.warn("The tenant's password reset message does not contain a required '{{link}}' marker.");
1320 if (passwordResetConfig.getLoginpage() == null || passwordResetConfig.getLoginpage().trim().isEmpty()) {
1321 logger.warn("The tenant's password reset configuration is missing a 'loginpage' value. It should be set to something like '/collectionspace/ui/core/html/index.html'.");
1325 String subject = passwordResetConfig.getSubject();
1326 if (subject == null || subject.trim().isEmpty()) {
1327 passwordResetConfig.setSubject(DEFAULT_PASSWORD_RESET_EMAIL_SUBJECT);
1336 * Generate a password reset message. Embeds an authorization token to reset a user's password.
1338 public static String generatePasswordResetEmailMessage(EmailConfig emailConfig, AccountListItem accountListItem, Token token) throws Exception {
1339 String result = null;
1341 result = validatePasswordResetConfig(emailConfig.getPasswordResetConfig());
1342 if (result == null) {
1343 String errMsg = String.format("The password reset configuration for the tenant ID='%s' is missing or malformed. Could not initiate a password reset for user ID='%s. See the log files for more details.",
1344 token.getTenantId(), accountListItem.getEmail());
1345 throw new Exception(errMsg);
1348 String link = emailConfig.getBaseurl() + emailConfig.getPasswordResetConfig().getLoginpage() + "?token=" + token.getId();
1349 result = result.replaceAll("\\{\\{link\\}\\}", link);
1351 if (result.contains("{{greeting}}")) {
1352 String greeting = accountListItem.getScreenName();
1353 result = result.replaceAll("\\{\\{greeting\\}\\}", greeting);
1354 result = result.replaceAll("\\\\n", "\\\n");
1355 result = result.replaceAll("\\\\r", "\\\r");
1361 public static void persistTenantBindingsMD5Hash(TenantBindingConfigReaderImpl tenantBindingConfigReader,
1362 DatabaseProductType databaseProductType, String cspaceDatabaseName) throws Exception {
1363 // Need to define and look for a createDisabled attribute in tenant config
1364 String updateTableSQL = "UPDATE tenants SET config_md5hash = ? WHERE id = ?";
1367 PreparedStatement pstmt = null;
1369 conn = getConnection(cspaceDatabaseName);
1370 pstmt = conn.prepareStatement(updateTableSQL); // create a statement
1371 for (String tId : AuthorizationCommon.tenantConfigMD5HashTable.keySet()) {
1372 pstmt.setString(1, AuthorizationCommon.getTenantConfigMD5Hash(tId));
1373 pstmt.setString(2, tId);
1374 if (logger.isDebugEnabled()) {
1375 logger.debug("createMissingTenants adding entry for tenant: " + tId);
1377 pstmt.executeUpdate();
1380 } catch(Exception e) {
1383 if (pstmt!=null) pstmt.close();