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;
17 import javax.naming.NamingException;
19 import org.collectionspace.authentication.AuthN;
20 import org.collectionspace.services.account.AccountListItem;
22 import org.collectionspace.services.authentication.Token;
23 import org.collectionspace.services.authorization.AuthZ;
24 import org.collectionspace.services.authorization.CSpaceAction;
25 import org.collectionspace.services.authorization.CSpaceResource;
26 import org.collectionspace.services.authorization.PermissionException;
27 import org.collectionspace.services.authorization.PermissionRole;
28 import org.collectionspace.services.authorization.PermissionRoleRel;
29 import org.collectionspace.services.authorization.PermissionValue;
30 import org.collectionspace.services.authorization.Role;
31 import org.collectionspace.services.authorization.RoleValue;
32 import org.collectionspace.services.authorization.SubjectType;
33 import org.collectionspace.services.authorization.URIResourceImpl;
34 import org.collectionspace.services.authorization.perms.ActionType;
35 import org.collectionspace.services.authorization.perms.EffectType;
36 import org.collectionspace.services.authorization.perms.Permission;
37 import org.collectionspace.services.authorization.perms.PermissionAction;
38 import org.collectionspace.services.client.PermissionClient;
39 import org.collectionspace.services.client.Profiler;
40 import org.collectionspace.services.client.RoleClient;
41 import org.collectionspace.services.client.workflow.WorkflowClient;
43 import org.collectionspace.services.common.config.ServiceConfigUtils;
44 import org.collectionspace.services.common.config.TenantBindingConfigReaderImpl;
45 import org.collectionspace.services.common.context.ServiceBindingUtils;
46 import org.collectionspace.services.common.document.DocumentException;
47 import org.collectionspace.services.common.document.DocumentHandler;
48 import org.collectionspace.services.common.security.SecurityUtils;
49 import org.collectionspace.services.common.storage.DatabaseProductType;
50 import org.collectionspace.services.common.storage.JDBCTools;
51 import org.collectionspace.services.common.storage.jpa.JPATransactionContext;
52 import org.collectionspace.services.common.storage.jpa.JpaStorageUtils;
54 import org.collectionspace.services.config.service.ServiceBindingType;
55 import org.collectionspace.services.config.tenant.EmailConfig;
56 import org.collectionspace.services.config.tenant.PasswordResetConfig;
57 import org.collectionspace.services.config.tenant.TenantBindingType;
59 import org.collectionspace.services.lifecycle.Lifecycle;
60 import org.collectionspace.services.lifecycle.TransitionDef;
61 import org.collectionspace.services.lifecycle.TransitionDefList;
63 //import org.mortbay.log.Log;
64 import org.slf4j.Logger;
65 import org.slf4j.LoggerFactory;
68 public class AuthorizationCommon {
70 final public static String REFRESH_AUTHZ_PROP = "refreshAuthZOnStartup";
73 // For token generation and password reset
75 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.";
76 private static final String DEFAULT_PASSWORD_RESET_EMAIL_SUBJECT = "Password reset for CollectionSpace account";
79 // Keep track of the MD5 hash value for the tenant bindings
81 private static final Map<String, String> tenantConfigMD5HashTable = new HashMap<String, String>();
84 // ActionGroup labels/constants
87 // for READ-WRITE-DELETE
88 final public static String ACTIONGROUP_CRUDL_NAME = "CRUDL";
89 final public static ActionType[] ACTIONSET_CRUDL = {ActionType.CREATE, ActionType.READ, ActionType.UPDATE, ActionType.DELETE, ActionType.SEARCH};
91 final public static String ACTIONGROUP_CRUL_NAME = "CRUL";
92 final public static ActionType[] ACTIONSET_CRUL = {ActionType.CREATE, ActionType.READ, ActionType.UPDATE, ActionType.SEARCH};
94 final public static String ACTIONGROUP_RL_NAME = "RL";
95 final public static ActionType[] ACTIONSET_RL = {ActionType.READ, ActionType.SEARCH};
97 static ActionGroup ACTIONGROUP_CRUDL;
98 static ActionGroup ACTIONGROUP_CRUL;
99 static ActionGroup ACTIONGROUP_RL;
101 // A static block to initialize the predefined action groups
104 ACTIONGROUP_CRUDL = new ActionGroup();
105 ACTIONGROUP_CRUDL.name = ACTIONGROUP_CRUDL_NAME;
106 ACTIONGROUP_CRUDL.actions = ACTIONSET_CRUDL;
108 ACTIONGROUP_RL = new ActionGroup();
109 ACTIONGROUP_RL.name = ACTIONGROUP_RL_NAME;
110 ACTIONGROUP_RL.actions = ACTIONSET_RL;
112 ACTIONGROUP_CRUL = new ActionGroup();
113 ACTIONGROUP_CRUL.name = ACTIONGROUP_CRUL_NAME;
114 ACTIONGROUP_CRUL.actions = ACTIONSET_CRUL;
117 final static Logger logger = LoggerFactory.getLogger(AuthorizationCommon.class);
119 final public static String ROLE_TENANT_ADMINISTRATOR = "TENANT_ADMINISTRATOR";
120 final public static String ROLE_TENANT_READER = "TENANT_READER";
122 public static final String TENANT_MANAGER_USER = "tenantManager";
123 public static final String TENANT_MANAGER_SCREEN_NAME = TENANT_MANAGER_USER;
124 public static final String DEFAULT_TENANT_MANAGER_PASSWORD = "manage";
125 public static final String DEFAULT_TENANT_MANAGER_EMAIL = "tenantManager@collectionspace.org";
127 public static final String TENANT_ADMIN_ACCT_PREFIX = "admin@";
128 public static final String TENANT_READER_ACCT_PREFIX = "reader@";
129 public static final String ROLE_PREFIX = "ROLE_";
130 public static final String TENANT_ADMIN_ROLE_SUFFIX = "_TENANT_ADMINISTRATOR";
131 public static final String TENANT_READER_ROLE_SUFFIX = "_TENANT_READER";
132 public static final String DEFAULT_ADMIN_PASSWORD = "Administrator";
133 public static final String DEFAULT_READER_PASSWORD = "reader";
135 // SQL for init tasks
136 final private static String INSERT_ACCOUNT_ROLE_SQL_MYSQL =
137 "INSERT INTO accounts_roles(account_id, user_id, role_id, role_name, created_at)"
138 +" VALUES(?, ?, ?, ?, now())";
139 final private static String INSERT_ACCOUNT_ROLE_SQL_POSTGRES =
140 "INSERT INTO accounts_roles(HJID, account_id, user_id, role_id, role_name, created_at)"
141 +" VALUES(nextval('hibernate_sequence'), ?, ?, ?, ?, now())";
142 final private static String QUERY_USERS_SQL =
143 "SELECT username FROM users WHERE username LIKE '"
144 +TENANT_ADMIN_ACCT_PREFIX+"%' OR username LIKE '"+TENANT_READER_ACCT_PREFIX+"%'";
145 final private static String INSERT_USER_SQL =
146 "INSERT INTO users (username,passwd, created_at) VALUES (?,?, now())";
147 final private static String INSERT_ACCOUNT_SQL =
148 "INSERT INTO accounts_common "
149 + "(csid, email, userid, status, screen_name, metadata_protection, roles_protection, created_at) "
150 + "VALUES (?,?,?,'ACTIVE',?, 'immutable', 'immutable', now())";
152 // TENANT MANAGER specific SQL
153 final private static String QUERY_TENANT_MGR_USER_SQL =
154 "SELECT username FROM users WHERE username = '"+TENANT_MANAGER_USER+"'";
155 final private static String GET_TENANT_MGR_ROLE_SQL =
156 "SELECT csid from roles WHERE tenant_id='" + AuthN.ALL_TENANTS_MANAGER_TENANT_ID + "' and rolename=?";
158 public static final String IGNORE_TENANT_ID = null; // A null constant to indicate an empty/unused value for the tenant ID
161 public static String getTenantConfigMD5Hash(String tenantId) {
162 return tenantConfigMD5HashTable.get(tenantId);
165 public static String setTenantConfigMD5Hash(String tenantId, String md5hash) {
166 return tenantConfigMD5HashTable.put(tenantId, md5hash);
169 public static Role getRole(JPATransactionContext jpaTransactionContext, String tenantId, String displayName) {
172 String roleName = AuthorizationCommon.getQualifiedRoleName(tenantId, displayName);
173 role = AuthorizationStore.getRoleByName(jpaTransactionContext, roleName, tenantId);
179 * Create a new role instance to be persisted later.
187 public static Role createRole(String tenantId, String name, String description, boolean immutable) {
188 Role role = new Role();
190 role.setCreatedAtItem(new Date());
191 role.setDisplayName(name);
192 String roleName = AuthorizationCommon.getQualifiedRoleName(tenantId, name);
193 role.setRoleName(roleName);
194 String id = UUID.randomUUID().toString(); //FIXME: The qualified role name should be unique enough to use as an ID/key
196 role.setDescription(description);
197 role.setTenantId(tenantId);
198 if (immutable == true) {
199 role.setMetadataProtection(RoleClient.IMMUTABLE);
200 role.setPermsProtection(RoleClient.IMMUTABLE);
207 * Add permission to the Spring Security tables
208 * with assumption that resource is of type URI
209 * @param permission configuration
211 public static void addPermissionsForUri(JPATransactionContext jpaTransactionContext,
213 PermissionRole permRole) throws PermissionException {
215 // First check the integrity of the incoming arguments.
217 if (!perm.getCsid().equals(permRole.getPermission().get(0).getPermissionId())) {
218 throw new IllegalArgumentException("permission ids do not"
219 + " match for role=" + permRole.getRole().get(0).getRoleName()
220 + " with permissionId=" + permRole.getPermission().get(0).getPermissionId()
221 + " for permission with csid=" + perm.getCsid());
224 List<String> principals = new ArrayList<String>();
225 for (RoleValue roleValue : permRole.getRole()) {
226 principals.add(roleValue.getRoleName());
229 boolean grant = perm.getEffect().equals(EffectType.PERMIT) ? true : false;
230 List<PermissionAction> permActions = perm.getAction();
231 ArrayList<CSpaceResource> resources = new ArrayList<CSpaceResource>();
232 for (PermissionAction permAction : permActions) {
233 CSpaceAction action = URIResourceImpl.getAction(permAction.getName());
234 URIResourceImpl uriRes = new URIResourceImpl(perm.getTenantId(), perm.getResourceName(), action);
235 resources.add(uriRes);
237 AuthZ.get().addPermissions(resources.toArray(new CSpaceResource[0]), principals.toArray(new String[0]), grant); // CSPACE-4967
238 jpaTransactionContext.setAclTablesUpdateFlag(true); // Tell the containing JPA transaction that we've committed changes to the Spring Tables
241 private static Connection getConnection(String databaseName) throws NamingException, SQLException {
242 return JDBCTools.getConnection(JDBCTools.CSPACE_DATASOURCE_NAME,
247 * Spring security seems to require that all of our role names start
248 * with the ROLE_PREFIX string.
250 public static String getQualifiedRoleName(String tenantId, String name) {
251 String result = name;
253 String qualifiedName = ROLE_PREFIX + tenantId.toUpperCase() + "_" + name.toUpperCase();
254 if (name.equals(qualifiedName) == false) {
255 result = qualifiedName;
261 private static ActionGroup getActionGroup(String actionGroupStr) {
262 ActionGroup result = null;
264 if (actionGroupStr.equalsIgnoreCase(ACTIONGROUP_CRUDL_NAME)) {
265 result = ACTIONGROUP_CRUDL;
266 } else if (actionGroupStr.equalsIgnoreCase(ACTIONGROUP_RL_NAME)) {
267 result = ACTIONGROUP_RL;
268 } else if (actionGroupStr.equalsIgnoreCase(ACTIONGROUP_CRUL_NAME)) {
269 result = ACTIONGROUP_CRUL;
275 public static Permission createPermission(String tenantId,
278 String actionGroupStr,
280 Permission result = null;
282 ActionGroup actionGroup = getActionGroup(actionGroupStr);
283 result = createPermission(tenantId, resourceName, description, actionGroup, immutable);
288 private static Permission createPermission(String tenantId,
291 ActionGroup actionGroup,
294 + "-" + resourceName.replace('/', '_') // Remove the slashes so the ID can be used in a URI/URL
295 + "-" + actionGroup.name;
296 Permission perm = new Permission();
298 perm.setDescription(description);
299 perm.setCreatedAtItem(new Date());
300 perm.setResourceName(resourceName.toLowerCase().trim());
301 perm.setEffect(EffectType.PERMIT);
302 perm.setTenantId(tenantId);
304 perm.setActionGroup(actionGroup.name);
305 ArrayList<PermissionAction> pas = new ArrayList<PermissionAction>();
307 for (ActionType actionType : actionGroup.actions) {
308 PermissionAction permAction = createPermissionAction(perm, actionType);
313 perm.setMetadataProtection(PermissionClient.IMMUTABLE);
314 perm.setActionsProtection(PermissionClient.IMMUTABLE);
320 private static Permission createWorkflowPermission(TenantBindingType tenantBinding,
321 ServiceBindingType serviceBinding,
322 String transitionVerb,
323 ActionGroup actionGroup,
326 Permission result = null;
327 String workFlowServiceSuffix;
328 String transitionName;
329 if (transitionVerb != null) {
330 transitionName = transitionVerb;
331 workFlowServiceSuffix = WorkflowClient.SERVICE_AUTHZ_SUFFIX;
333 transitionName = ""; //since the transitionDef was null, we're assuming that this is the base workflow permission to be created
334 workFlowServiceSuffix = WorkflowClient.SERVICE_PATH;
337 String tenantId = tenantBinding.getId();
338 String resourceName = "/"
339 + serviceBinding.getName().toLowerCase().trim()
340 + workFlowServiceSuffix
342 String description = "A generated workflow permission for actiongroup " + actionGroup.name;
343 result = createPermission(tenantId, resourceName, description, actionGroup, immutable);
345 if (logger.isDebugEnabled() == true) {
346 logger.debug("Generated a workflow permission: "
347 + result.getResourceName()
348 + ":" + transitionName
349 + ":" + "tenant id=" + result.getTenantId()
350 + ":" + actionGroup.name);
356 private static PermissionRole createPermissionRole(
357 Permission permission,
359 boolean enforceTenancy) throws DocumentException
361 PermissionRole permRole = new PermissionRole();
364 // Check to see if the tenant ID of the permission and the tenant ID of the role match
366 boolean tenantIdsMatch = role.getTenantId().equalsIgnoreCase(permission.getTenantId());
367 if (tenantIdsMatch == false && enforceTenancy == false) {
368 tenantIdsMatch = true; // If we don't need to enforce tenancy then we'll just consider them matched.
371 if (tenantIdsMatch == true) {
372 permRole.setSubject(SubjectType.ROLE);
374 // Set of the permission value list of the permrole
376 List<PermissionValue> permValues = new ArrayList<PermissionValue>();
377 PermissionValue permValue = new PermissionValue();
378 permValue.setPermissionId(permission.getCsid());
379 permValue.setResourceName(permission.getResourceName().toLowerCase());
380 permValue.setActionGroup(permission.getActionGroup());
381 permValues.add(permValue);
382 permRole.setPermission(permValues);
384 // Set of the role value list of the permrole
386 List<RoleValue> roleValues = new ArrayList<RoleValue>();
387 RoleValue rv = new RoleValue();
388 // This needs to use the qualified name, not the display name
389 rv.setRoleName(role.getRoleName());
390 rv.setRoleId(role.getCsid());
392 permRole.setRole(roleValues);
394 String errMsg = "The tenant ID of the role: " + role.getTenantId()
395 + " did not match the tenant ID of the permission: " + permission.getTenantId();
396 throw new DocumentException(errMsg);
402 private static Hashtable<String, String> getTenantNamesFromConfig(TenantBindingConfigReaderImpl tenantBindingConfigReader) {
404 // Note that this only handles tenants not marked as "createDisabled"
405 Hashtable<String, TenantBindingType> tenantBindings =
406 tenantBindingConfigReader.getTenantBindings();
407 Hashtable<String, String> tenantInfo = new Hashtable<String, String>();
408 for (TenantBindingType tenantBinding : tenantBindings.values()) {
409 String tId = tenantBinding.getId();
410 String tName = tenantBinding.getName();
411 tenantInfo.put(tId, tName);
412 if (logger.isDebugEnabled()) {
413 logger.debug("getTenantNamesFromConfig found configured tenant id: "+tId+" name: "+tName);
419 private static ArrayList<String> compileExistingTenants(Connection conn, Hashtable<String, String> tenantInfo)
420 throws SQLException, Exception {
421 Statement stmt = null;
422 ArrayList<String> existingTenants = new ArrayList<String>();
423 // First find or create the tenants
424 final String queryTenantSQL = "SELECT id,name FROM tenants";
426 stmt = conn.createStatement();
427 ResultSet rs = stmt.executeQuery(queryTenantSQL);
429 String tId = rs.getString("id");
430 String tName = rs.getString("name");
431 if(tenantInfo.containsKey(tId)) {
432 existingTenants.add(tId);
433 if(!tenantInfo.get(tId).equalsIgnoreCase(tName)) {
434 logger.warn("Configured name for tenant: "
435 +tId+" in repository: "+tName
436 +" does not match config'd name: "+ tenantInfo.get(tId));
441 } catch(Exception e) {
448 return existingTenants;
451 private static ArrayList<String> findOrCreateDefaultUsers(Connection conn, Hashtable<String, String> tenantInfo)
452 throws SQLException, Exception {
453 // Second find or create the users
454 Statement stmt = null;
455 PreparedStatement pstmt = null;
456 ArrayList<String> usersInRepo = new ArrayList<String>();
458 stmt = conn.createStatement();
459 ResultSet rs = stmt.executeQuery(QUERY_USERS_SQL);
461 String uName = rs.getString("username");
462 usersInRepo.add(uName);
465 pstmt = conn.prepareStatement(INSERT_USER_SQL); // create a statement
466 for(String tName : tenantInfo.values()) {
467 String adminAcctName = getDefaultAdminUserID(tName);
468 if(!usersInRepo.contains(adminAcctName)) {
469 String secEncPasswd = SecurityUtils.createPasswordHash(
470 adminAcctName, DEFAULT_ADMIN_PASSWORD);
471 pstmt.setString(1, adminAcctName); // set username param
472 pstmt.setString(2, secEncPasswd); // set passwd param
473 if (logger.isDebugEnabled()) {
474 logger.debug("createDefaultUsersAndAccounts adding user: "
475 +adminAcctName+" for tenant: "+tName);
477 pstmt.executeUpdate();
478 } else if (logger.isDebugEnabled()) {
479 logger.debug("createDefaultUsersAndAccounts: user: "+adminAcctName
480 +" already exists - skipping.");
484 String readerAcctName = getDefaultReaderUserID(tName);
485 if(!usersInRepo.contains(readerAcctName)) {
486 String secEncPasswd = SecurityUtils.createPasswordHash(
487 readerAcctName, DEFAULT_READER_PASSWORD);
488 pstmt.setString(1, readerAcctName); // set username param
489 pstmt.setString(2, secEncPasswd); // set passwd param
490 if (logger.isDebugEnabled()) {
491 logger.debug("createDefaultUsersAndAccounts adding user: "
492 +readerAcctName+" for tenant: "+tName);
494 pstmt.executeUpdate();
495 } else if (logger.isDebugEnabled()) {
496 logger.debug("createDefaultUsersAndAccounts: user: "+readerAcctName
497 +" already exists - skipping.");
501 } catch(Exception e) {
512 private static void findOrCreateDefaultAccounts(Connection conn, Hashtable<String, String> tenantInfo,
513 ArrayList<String> usersInRepo,
514 Hashtable<String, String> tenantAdminAcctCSIDs, Hashtable<String, String> tenantReaderAcctCSIDs)
515 throws SQLException, Exception {
516 // Third, create the accounts. Assume that if the users were already there,
517 // then the accounts were as well
518 PreparedStatement pstmt = null;
520 pstmt = conn.prepareStatement(INSERT_ACCOUNT_SQL); // create a statement
521 for(String tId : tenantInfo.keySet()) {
522 String tName = tenantInfo.get(tId);
523 String adminCSID = UUID.randomUUID().toString();
524 tenantAdminAcctCSIDs.put(tId, adminCSID);
525 String adminAcctName = getDefaultAdminUserID(tName);
526 if(!usersInRepo.contains(adminAcctName)) {
527 pstmt.setString(1, adminCSID); // set csid param
528 pstmt.setString(2, adminAcctName); // set email param (bogus)
529 pstmt.setString(3, adminAcctName); // set userid param
530 pstmt.setString(4, "Administrator");// set screen name param
531 if (logger.isDebugEnabled()) {
532 logger.debug("createDefaultAccounts adding account: "
533 +adminAcctName+" for tenant: "+tName);
535 pstmt.executeUpdate();
536 } else if (logger.isDebugEnabled()) {
537 logger.debug("createDefaultAccounts: user: "+adminAcctName
538 +" already exists - skipping account generation.");
541 String readerCSID = UUID.randomUUID().toString();
542 tenantReaderAcctCSIDs.put(tId, readerCSID);
543 String readerAcctName = getDefaultReaderUserID(tName);
544 if(!usersInRepo.contains(readerAcctName)) {
545 pstmt.setString(1, readerCSID); // set csid param
546 pstmt.setString(2, readerAcctName); // set email param (bogus)
547 pstmt.setString(3, readerAcctName); // set userid param
548 pstmt.setString(4, "Reader"); // set screen name param
549 if (logger.isDebugEnabled()) {
550 logger.debug("createDefaultAccounts adding account: "
551 +readerAcctName+" for tenant: "+tName);
553 pstmt.executeUpdate();
554 } else if (logger.isDebugEnabled()) {
555 logger.debug("createDefaultAccounts: user: "+readerAcctName
556 +" already exists - skipping account creation.");
560 } catch(Exception e) {
568 private static boolean findOrCreateTenantManagerUserAndAccount(Connection conn)
569 throws SQLException, Exception {
570 // Find or create the special tenant manager account.
571 // Later can make the user name for tenant manager be configurable, settable.
572 Statement stmt = null;
573 PreparedStatement pstmt = null;
574 boolean created = false;
576 boolean foundTMgrUser = false;
577 stmt = conn.createStatement();
578 ResultSet rs = stmt.executeQuery(QUERY_TENANT_MGR_USER_SQL);
579 // Should only find one - only consider it
581 String uName = rs.getString("username");
582 foundTMgrUser = uName.equals(TENANT_MANAGER_USER);
586 pstmt = conn.prepareStatement(INSERT_USER_SQL); // create a statement
587 String secEncPasswd = SecurityUtils.createPasswordHash(
588 TENANT_MANAGER_USER, DEFAULT_TENANT_MANAGER_PASSWORD);
589 pstmt.setString(1, TENANT_MANAGER_USER); // set username param
590 pstmt.setString(2, secEncPasswd); // set passwd param
591 if (logger.isDebugEnabled()) {
592 logger.debug("findOrCreateTenantManagerUserAndAccount adding tenant manager user: "
593 +TENANT_MANAGER_USER);
595 pstmt.executeUpdate();
597 // Now create the account to match
598 pstmt = conn.prepareStatement(INSERT_ACCOUNT_SQL); // create a statement
599 pstmt.setString(1, AuthN.TENANT_MANAGER_ACCT_ID); // set csid param
600 pstmt.setString(2, DEFAULT_TENANT_MANAGER_EMAIL); // set email param (bogus)
601 pstmt.setString(3, TENANT_MANAGER_USER); // set userid param
602 pstmt.setString(4, TENANT_MANAGER_SCREEN_NAME);// set screen name param
603 if (logger.isDebugEnabled()) {
604 logger.debug("findOrCreateTenantManagerUserAndAccount adding tenant manager account: "
605 +TENANT_MANAGER_USER);
607 pstmt.executeUpdate();
610 } else if (logger.isDebugEnabled()) {
611 logger.debug("findOrCreateTenantManagerUserAndAccount: tenant manager: "+TENANT_MANAGER_USER
612 +" already exists.");
614 } catch(Exception e) {
625 private static void bindDefaultAccountsToTenants(Connection conn, DatabaseProductType databaseProductType,
626 Hashtable<String, String> tenantInfo, ArrayList<String> usersInRepo,
627 Hashtable<String, String> tenantAdminAcctCSIDs, Hashtable<String, String> tenantReaderAcctCSIDs)
628 throws SQLException, Exception {
629 // Fourth, bind accounts to tenants. Assume that if the users were already there,
630 // then the accounts were bound to tenants correctly
631 PreparedStatement pstmt = null;
633 String insertAccountTenantSQL;
634 if (databaseProductType == DatabaseProductType.MYSQL) {
635 insertAccountTenantSQL =
636 "INSERT INTO accounts_tenants (TENANTS_ACCOUNTS_COMMON_CSID,tenant_id) "
638 } else if (databaseProductType == DatabaseProductType.POSTGRESQL) {
639 insertAccountTenantSQL =
640 "INSERT INTO accounts_tenants (HJID, TENANTS_ACCOUNTS_COMMON_CSID,tenant_id) "
641 + " VALUES(nextval('hibernate_sequence'), ?, ?)";
643 throw new Exception("Unrecognized database system.");
645 pstmt = conn.prepareStatement(insertAccountTenantSQL); // create a statement
646 for(String tId : tenantInfo.keySet()) {
647 String tName = tenantInfo.get(tId);
648 if(!usersInRepo.contains(getDefaultAdminUserID(tName))) {
649 String adminAcct = tenantAdminAcctCSIDs.get(tId);
650 pstmt.setString(1, adminAcct); // set acct CSID param
651 pstmt.setString(2, tId); // set tenant_id param
652 if (logger.isDebugEnabled()) {
653 logger.debug("createDefaultAccounts binding account id: "
654 +adminAcct+" to tenant id: "+tId);
656 pstmt.executeUpdate();
658 if(!usersInRepo.contains(getDefaultReaderUserID(tName))) {
659 String readerAcct = tenantReaderAcctCSIDs.get(tId);
660 pstmt.setString(1, readerAcct); // set acct CSID param
661 pstmt.setString(2, tId); // set tenant_id param
662 if (logger.isDebugEnabled()) {
663 logger.debug("createDefaultAccounts binding account id: "
664 +readerAcct+" to tenant id: "+tId);
666 pstmt.executeUpdate();
670 } catch(Exception e) {
679 * Creates the default Admin and Reader roles for all the configured tenants.
681 * Returns the CSID of the Spring Admin role.
685 * @param tenantAdminRoleCSIDs
686 * @param tenantReaderRoleCSIDs
688 * @throws SQLException
691 private static String findOrCreateDefaultRoles(Connection conn, Hashtable<String, String> tenantInfo,
692 Hashtable<String, String> tenantAdminRoleCSIDs, Hashtable<String, String> tenantReaderRoleCSIDs)
693 throws SQLException, Exception {
695 String springAdminRoleCSID = null;
696 Statement stmt = null;
697 PreparedStatement pstmt = null;
700 // Look for the Spring Security admin role. If not found, create it.
702 final String querySpringRole = String.format("SELECT csid from roles WHERE rolename='%s'", AuthN.ROLE_SPRING_ADMIN_NAME);
703 stmt = conn.createStatement();
704 ResultSet rs = stmt.executeQuery(querySpringRole);
706 springAdminRoleCSID = rs.getString(1);
707 if (logger.isDebugEnabled()) {
708 logger.debug("createDefaultAccounts found Spring Admin role: " + springAdminRoleCSID);
711 final String insertSpringAdminRoleSQL = String.format(
712 "INSERT INTO roles (csid, rolename, displayName, rolegroup, created_at, tenant_id) VALUES ('%s', '%s', '%s', '%s', now(), '%s')",
713 AuthN.ROLE_SPRING_ADMIN_ID, AuthN.ROLE_SPRING_ADMIN_NAME, AuthN.SPRING_ADMIN_USER, AuthN.ROLE_SPRING_GROUP_NAME, AuthN.ADMIN_TENANT_ID);
714 stmt.executeUpdate(insertSpringAdminRoleSQL);
715 springAdminRoleCSID = AuthN.ROLE_SPRING_ADMIN_ID;
721 // Look for and save each tenants default Admin and Reader roles
723 final String getRoleCSIDSql = "SELECT csid from roles WHERE tenant_id=? and rolename=?";
724 pstmt = conn.prepareStatement(getRoleCSIDSql); // create a statement
725 for (String tenantId : tenantInfo.keySet()) {
727 // Look for the default Admin role
729 pstmt.setString(1, tenantId);
730 pstmt.setString(2, getDefaultAdminRole(tenantId));
731 rs = pstmt.executeQuery();
732 // extract data from the ResultSet
734 throw new RuntimeException("Cannot find role: " + getDefaultAdminRole(tenantId)
735 + " for tenant id: " + tenantId + " in roles!");
737 String tenantAdminRoleCSID = rs.getString(1); // First column (#1) is the CSID
738 tenantAdminRoleCSIDs.put(tenantId, tenantAdminRoleCSID);
742 // Look for the default Reader role
744 pstmt.setString(1, tenantId); // set tenant_id param
745 pstmt.setString(2, getDefaultReaderRole(tenantId)); // set rolename param
746 rs = pstmt.executeQuery();
747 // extract data from the ResultSet
749 throw new RuntimeException("Cannot find role: " + getDefaultReaderRole(tenantId)
750 + " for tenant id: " + tenantId + " in roles!");
752 String tenantReaderRoleCSID = rs.getString(1);
753 tenantReaderRoleCSIDs.put(tenantId, tenantReaderRoleCSID);
757 } catch(Exception e) {
760 if (stmt != null) stmt.close();
761 if (pstmt != null) pstmt.close();
764 return springAdminRoleCSID;
767 private static String findTenantManagerRole(Connection conn )
768 throws SQLException, RuntimeException, Exception {
769 String tenantMgrRoleCSID = null;
770 PreparedStatement pstmt = null;
772 String rolename = getQualifiedRoleName(AuthN.ALL_TENANTS_MANAGER_TENANT_ID,
773 AuthN.ROLE_ALL_TENANTS_MANAGER);
774 pstmt = conn.prepareStatement(GET_TENANT_MGR_ROLE_SQL); // create a statement
776 pstmt.setString(1, rolename); // set rolename param
777 rs = pstmt.executeQuery();
779 tenantMgrRoleCSID = rs.getString(1);
780 if (logger.isDebugEnabled()) {
781 logger.debug("findTenantManagerRole found Tenant Mgr role: "
786 } catch(Exception e) {
792 if(tenantMgrRoleCSID==null)
793 throw new RuntimeException("findTenantManagerRole: Cound not find tenant Manager Role!");
794 return tenantMgrRoleCSID;
797 private static void bindAccountsToRoles(Connection conn, DatabaseProductType databaseProductType,
798 Hashtable<String, String> tenantInfo, ArrayList<String> usersInRepo,
799 String springAdminRoleCSID,
800 Hashtable<String, String> tenantAdminRoleCSIDs, Hashtable<String, String> tenantReaderRoleCSIDs,
801 Hashtable<String, String> tenantAdminAcctCSIDs, Hashtable<String, String> tenantReaderAcctCSIDs)
802 throws SQLException, Exception {
803 // Sixth, bind the accounts to roles. If the users already existed,
804 // we'll assume they were set up correctly.
805 PreparedStatement pstmt = null;
807 String insertAccountRoleSQL;
808 if (databaseProductType == DatabaseProductType.POSTGRESQL) {
809 insertAccountRoleSQL = INSERT_ACCOUNT_ROLE_SQL_POSTGRES;
811 throw new Exception("Unrecognized database system.");
814 pstmt = conn.prepareStatement(insertAccountRoleSQL); // create a statement
815 for (String tId : tenantInfo.keySet()) {
816 String adminUserId = getDefaultAdminUserID(tenantInfo.get(tId));
817 if (!usersInRepo.contains(adminUserId)) {
818 String adminAcct = tenantAdminAcctCSIDs.get(tId);
819 String adminRoleId = tenantAdminRoleCSIDs.get(tId);
820 pstmt.setString(1, adminAcct); // set acct CSID param
821 pstmt.setString(2, adminUserId); // set user_id param
822 pstmt.setString(3, adminRoleId); // set role_id param
823 pstmt.setString(4, getDefaultAdminRole(tId)); // set rolename param
824 pstmt.executeUpdate();
826 // Now add the Spring Admin Role to the admin accounts
828 pstmt.setString(3, springAdminRoleCSID); // set role_id param
829 pstmt.setString(4, AuthN.ROLE_SPRING_ADMIN_NAME); // set rolename param
830 pstmt.executeUpdate();
832 String readerUserId = getDefaultReaderUserID(tenantInfo.get(tId));
833 if (!usersInRepo.contains(readerUserId)) {
834 String readerAcct = tenantReaderAcctCSIDs.get(tId);
835 String readerRoleId = tenantReaderRoleCSIDs.get(tId);
836 pstmt.setString(1, readerAcct); // set acct CSID param
837 pstmt.setString(2, readerUserId); // set user_id param
838 pstmt.setString(3, readerRoleId); // set role_id param
839 pstmt.setString(4, getDefaultReaderRole(tId)); // set rolename param
840 pstmt.executeUpdate();
844 } catch(Exception e) {
853 private static void bindTenantManagerAccountRole(Connection conn, DatabaseProductType databaseProductType,
854 String tenantManagerUserID, String tenantManagerAccountID, String tenantManagerRoleID, String tenantManagerRoleName )
855 throws SQLException, Exception {
856 PreparedStatement pstmt = null;
858 String insertAccountRoleSQL;
859 if (databaseProductType == DatabaseProductType.MYSQL) {
860 insertAccountRoleSQL = INSERT_ACCOUNT_ROLE_SQL_MYSQL;
861 } else if (databaseProductType == DatabaseProductType.POSTGRESQL) {
862 insertAccountRoleSQL = INSERT_ACCOUNT_ROLE_SQL_POSTGRES;
864 throw new Exception("Unrecognized database system.");
866 if (logger.isDebugEnabled()) {
867 logger.debug("bindTenantManagerAccountRole binding account to role with SQL:\n"
868 +insertAccountRoleSQL);
870 pstmt = conn.prepareStatement(insertAccountRoleSQL); // create a statement
871 pstmt.setString(1, tenantManagerAccountID); // set acct CSID param
872 pstmt.setString(2, tenantManagerUserID); // set user_id param
873 pstmt.setString(3, tenantManagerRoleID); // set role_id param
874 pstmt.setString(4, tenantManagerRoleName); // set rolename param
875 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();
888 } catch(Exception e) {
897 * Using the tenant bindings, ensure there are corresponding Tenant records (db columns).
899 //FIXME: This code should be using JPA objects and JPATransactionContext, not raw SQL.
900 public static void createTenants(
901 TenantBindingConfigReaderImpl tenantBindingConfigReader,
902 DatabaseProductType databaseProductType,
903 String cspaceDatabaseName) throws Exception {
904 logger.debug("ServiceMain.createTenants starting...");
905 Hashtable<String, String> tenantInfo = getTenantNamesFromConfig(tenantBindingConfigReader);
906 Connection conn = null;
908 conn = getConnection(cspaceDatabaseName);
909 ArrayList<String> existingTenants = compileExistingTenants(conn, tenantInfo);
911 // Note that this only creates tenants not marked as "createDisabled"
912 createMissingTenants(conn, tenantInfo, existingTenants);
913 } catch (Exception e) {
914 logger.debug("Exception in createTenants: " + e.getLocalizedMessage());
921 } catch (SQLException sqle) {
922 if (logger.isDebugEnabled()) {
923 logger.debug("SQL Exception closing statement/connection: " + sqle.getLocalizedMessage());
931 * @param tenantBindingConfigReader
932 * @param databaseProductType
933 * @param cspaceDatabaseName
936 //FIXME: This code should be using the JPA objects and JPATransactionContext, not raw SQL.
937 public static void createDefaultAccounts(
938 TenantBindingConfigReaderImpl tenantBindingConfigReader,
939 DatabaseProductType databaseProductType,
940 String cspaceDatabaseName) throws Exception {
942 logger.debug("ServiceMain.createDefaultAccounts starting...");
944 Hashtable<String, String> tenantInfo = getTenantNamesFromConfig(tenantBindingConfigReader);
945 Connection conn = null;
946 // TODO - need to put in tests for existence first.
947 // We could just look for the accounts per tenant up front, and assume that
948 // the rest is there if the accounts are.
949 // Could add a sql script to remove these if need be - Spring only does roles,
950 // and we're not touching that, so we could safely toss the
951 // accounts, users, account-tenants, account-roles, and start over.
953 conn = getConnection(cspaceDatabaseName);
955 ArrayList<String> usersInRepo = findOrCreateDefaultUsers(conn, tenantInfo);
957 Hashtable<String, String> tenantAdminAcctCSIDs = new Hashtable<String, String>();
958 Hashtable<String, String> tenantReaderAcctCSIDs = new Hashtable<String, String>();
959 findOrCreateDefaultAccounts(conn, tenantInfo, usersInRepo,
960 tenantAdminAcctCSIDs, tenantReaderAcctCSIDs);
962 bindDefaultAccountsToTenants(conn, databaseProductType, tenantInfo, usersInRepo,
963 tenantAdminAcctCSIDs, tenantReaderAcctCSIDs);
965 Hashtable<String, String> tenantAdminRoleCSIDs = new Hashtable<String, String>();
966 Hashtable<String, String> tenantReaderRoleCSIDs = new Hashtable<String, String>();
967 String springAdminRoleCSID = findOrCreateDefaultRoles(conn, tenantInfo,
968 tenantAdminRoleCSIDs, tenantReaderRoleCSIDs);
970 bindAccountsToRoles(conn, databaseProductType,
971 tenantInfo, usersInRepo, springAdminRoleCSID,
972 tenantAdminRoleCSIDs, tenantReaderRoleCSIDs,
973 tenantAdminAcctCSIDs, tenantReaderAcctCSIDs);
975 boolean createdTenantMgrAccount = findOrCreateTenantManagerUserAndAccount(conn);
976 if (createdTenantMgrAccount) {
977 // If we created the account, we need to create the bindings. Otherwise, assume they
978 // are all set (from previous initialization).
979 String tenantManagerRoleCSID = findTenantManagerRole(conn);
980 bindTenantManagerAccountRole(conn, databaseProductType,
981 TENANT_MANAGER_USER, AuthN.TENANT_MANAGER_ACCT_ID,
982 tenantManagerRoleCSID, AuthN.ROLE_ALL_TENANTS_MANAGER);
984 } catch (Exception e) {
985 logger.debug("Exception in createDefaultAccounts: " + e.getLocalizedMessage());
992 } catch (SQLException sqle) {
993 if (logger.isDebugEnabled()) {
994 logger.debug("SQL Exception closing statement/connection: " + sqle.getLocalizedMessage());
1000 private static String getDefaultAdminRole(String tenantId) {
1001 return ROLE_PREFIX + tenantId + TENANT_ADMIN_ROLE_SUFFIX;
1004 private static String getDefaultReaderRole(String tenantId) {
1005 return ROLE_PREFIX+tenantId+TENANT_READER_ROLE_SUFFIX;
1008 private static String getDefaultAdminUserID(String tenantName) {
1009 return TENANT_ADMIN_ACCT_PREFIX + tenantName;
1012 private static String getDefaultReaderUserID(String tenantName) {
1013 return TENANT_READER_ACCT_PREFIX + tenantName;
1016 static private PermissionAction createPermissionAction(Permission perm,
1017 ActionType actionType) {
1018 PermissionAction pa = new PermissionAction();
1020 CSpaceAction action = URIResourceImpl.getAction(actionType);
1021 URIResourceImpl uriRes = new URIResourceImpl(perm.getTenantId(),
1022 perm.getResourceName(), action);
1023 pa.setName(actionType);
1024 pa.setObjectIdentity(uriRes.getHashedId().toString());
1025 pa.setObjectIdentityResource(uriRes.getId());
1030 private static HashSet<String> getTransitionVerbList(TenantBindingType tenantBinding, ServiceBindingType serviceBinding) {
1031 HashSet<String> result = new HashSet<String>();
1033 TransitionDefList transitionDefList = getTransitionDefList(tenantBinding, serviceBinding);
1034 for (TransitionDef transitionDef : transitionDefList.getTransitionDef()) {
1035 String transitionVerb = transitionDef.getName();
1036 String[] tokens = transitionVerb.split("_"); // Split the verb into words. The workflow verbs are compound words combined with the '_' character.
1037 result.add(tokens[0]); // We only care about the first word.
1043 private static TransitionDefList getTransitionDefList(TenantBindingType tenantBinding, ServiceBindingType serviceBinding) {
1044 TransitionDefList result = null;
1046 String serviceObjectName = serviceBinding.getObject().getName();
1048 @SuppressWarnings("rawtypes")
1049 DocumentHandler docHandler = ServiceConfigUtils.createDocumentHandlerInstance(
1050 tenantBinding, serviceBinding);
1051 Lifecycle lifecycle = docHandler.getLifecycle(serviceObjectName);
1052 if (lifecycle != null) {
1053 result = lifecycle.getTransitionDefList();
1055 } catch (Exception e) {
1056 // Ignore this exception and return an empty non-null TransitionDefList
1059 if (result == null) {
1060 if (serviceBinding.getType().equalsIgnoreCase(ServiceBindingUtils.SERVICE_TYPE_SECURITY) == false) {
1061 logger.debug("Could not retrieve a lifecycle transition definition list from: "
1062 + serviceBinding.getName()
1063 + " with tenant ID = "
1064 + tenantBinding.getId());
1066 // return an empty list
1067 result = new TransitionDefList();
1069 logger.debug("Successfully retrieved a lifecycle transition definition list from: "
1070 + serviceBinding.getName()
1071 + " with tenant ID = "
1072 + tenantBinding.getId());
1079 * Creates the immutable workflow permission sets for the default admin and reader roles.
1081 * @param tenantBindingConfigReader
1082 * @param databaseProductType
1083 * @param cspaceDatabaseName
1086 public static void createDefaultWorkflowPermissions(
1087 JPATransactionContext jpaTransactionContext,
1088 TenantBindingConfigReaderImpl tenantBindingConfigReader,
1089 DatabaseProductType databaseProductType,
1090 String cspaceDatabaseName) throws Exception
1092 java.util.logging.Logger logger = java.util.logging.Logger.getAnonymousLogger();
1094 AuthZ.get().login(); //login to Spring Security manager
1097 Hashtable<String, TenantBindingType> tenantBindings = tenantBindingConfigReader.getTenantBindings();
1098 for (String tenantId : tenantBindings.keySet()) {
1099 logger.info(String.format("Creating/verifying workflow permissions for tenant ID=%s.", tenantId));
1100 TenantBindingType tenantBinding = tenantBindings.get(tenantId);
1101 if (tenantBinding.isConfigChangedSinceLastStart() == false) {
1102 continue; // skip the rest of the loop and go to the next tenant
1105 Role adminRole = AuthorizationCommon.getRole(jpaTransactionContext, tenantBinding.getId(), ROLE_TENANT_ADMINISTRATOR);
1106 Role readonlyRole = AuthorizationCommon.getRole(jpaTransactionContext, tenantBinding.getId(), ROLE_TENANT_READER);
1108 if (adminRole == null || readonlyRole == null) {
1109 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.");
1111 throw new RuntimeException("One or more of the required default CollectionSpace administrator roles is missing or was never created.");
1114 for (ServiceBindingType serviceBinding : tenantBinding.getServiceBindings()) {
1115 String prop = ServiceBindingUtils.getPropertyValue(serviceBinding, REFRESH_AUTHZ_PROP);
1116 if (prop == null ? true : Boolean.parseBoolean(prop)) {
1118 jpaTransactionContext.beginTransaction();
1119 HashSet<String> transitionVerbList = getTransitionVerbList(tenantBinding, serviceBinding);
1120 for (String transitionVerb : transitionVerbList) {
1122 // Create the permission for the admin role
1123 Permission adminPerm = createWorkflowPermission(tenantBinding, serviceBinding, transitionVerb, ACTIONGROUP_CRUDL, true);
1124 persist(jpaTransactionContext, adminPerm, adminRole, true, ACTIONGROUP_CRUDL);
1126 // Create the permission for the read-only role
1127 Permission readonlyPerm = createWorkflowPermission(tenantBinding, serviceBinding, transitionVerb, ACTIONGROUP_RL, true);
1128 persist(jpaTransactionContext, readonlyPerm, readonlyRole, true, ACTIONGROUP_RL); // Persist/store the permission and permrole records and related Spring Security info
1130 jpaTransactionContext.commitTransaction();
1131 } catch (IllegalStateException e) {
1132 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.
1133 } catch (Exception x) {
1134 jpaTransactionContext.markForRollback();
1137 logger.warning("AuthZ refresh service binding property is set to FALSE so default permissions will NOT be refreshed for: "
1138 + serviceBinding.getName());
1142 } catch (Exception e) {
1143 jpaTransactionContext.markForRollback();
1144 logger.fine("Caught exception and rolling back permission creation: " + e.getMessage());
1149 private static void createMissingTenants(Connection conn, Hashtable<String, String> tenantInfo,
1150 ArrayList<String> existingTenants) throws SQLException, Exception {
1151 // Need to define and look for a createDisabled attribute in tenant config
1152 final String insertTenantSQL =
1153 "INSERT INTO tenants (id,name,authorities_initialized,disabled,created_at) VALUES (?,?,FALSE,FALSE,now())";
1154 PreparedStatement pstmt = null;
1156 pstmt = conn.prepareStatement(insertTenantSQL); // create a statement
1157 for(String tId : tenantInfo.keySet()) {
1158 if(existingTenants.contains(tId)) {
1159 if (logger.isDebugEnabled()) {
1160 logger.debug("createMissingTenants: tenant exists (skipping): "
1161 +tenantInfo.get(tId));
1165 pstmt.setString(1, tId); // set id param
1166 pstmt.setString(2, tenantInfo.get(tId)); // set name param
1167 if (logger.isDebugEnabled()) {
1168 logger.debug("createMissingTenants adding entry for tenant: "+tId);
1170 pstmt.executeUpdate();
1173 } catch(Exception e) {
1181 public static String getPersistedMD5Hash(String tenantId, String cspaceDatabaseName) throws Exception {
1182 String result = null;
1184 // First find or create the tenants
1185 final String queryTenantSQL = String.format("SELECT id, name, config_md5hash FROM tenants WHERE id = '%s'", tenantId);
1187 Statement stmt = null;
1191 conn = getConnection(cspaceDatabaseName);
1192 stmt = conn.createStatement();
1193 ResultSet rs = stmt.executeQuery(queryTenantSQL);
1196 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'.",
1197 tenantId, cspaceDatabaseName);
1198 throw new Exception(errMsg);
1200 String tId = rs.getString("id"); // for debugging only
1201 String tName = rs.getString("name"); // for debugging only
1202 result = rs.getString("config_md5hash");
1206 } catch(Exception e) {
1209 if (stmt != null) stmt.close();
1215 private static PermissionRoleRel findPermRoleRel(
1216 JPATransactionContext jpaTransactionContext,
1217 String permissionId,
1219 PermissionRoleRel result = null;
1222 String whereClause = "where permissionId = :id and roleId = :roleId";
1223 HashMap<String, Object> params = new HashMap<String, Object>();
1224 params.put("id", permissionId);
1225 params.put("roleId", RoleId);
1227 result = (PermissionRoleRel) JpaStorageUtils.getEntity(jpaTransactionContext,
1228 PermissionRoleRel.class.getCanonicalName(), whereClause, params);
1229 } catch (Exception e) {
1230 //Do nothing. Will return null;
1237 * Persists the Permission, PermissionRoleRel, and Spring Security table entries all in one transaction
1239 private static void persist(JPATransactionContext jpaTransactionContext, Permission permission, Role role, boolean enforceTenancy, ActionGroup actionGroup) throws Exception {
1240 AuthorizationStore authzStore = new AuthorizationStore();
1241 // First persist the Permission record
1242 authzStore.store(jpaTransactionContext, permission);
1244 // If the PermRoleRel doesn't already exists then relate the permission and the role in a new PermissionRole (the service payload)
1245 // Create a PermissionRoleRel (the database relation table for the permission and role)
1246 PermissionRoleRel permRoleRel = findPermRoleRel(jpaTransactionContext, permission.getCsid(), role.getCsid());
1247 if (permRoleRel == null) {
1248 PermissionRole permRole = createPermissionRole(permission, role, enforceTenancy);
1249 List<PermissionRoleRel> permRoleRels = new ArrayList<PermissionRoleRel>();
1250 PermissionRoleUtil.buildPermissionRoleRel(jpaTransactionContext, permRole, SubjectType.ROLE, permRoleRels,
1251 false /*not for delete*/, role.getTenantId());
1252 for (PermissionRoleRel prr : permRoleRels) {
1253 authzStore.store(jpaTransactionContext, prr);
1255 Profiler profiler = new Profiler(AuthorizationCommon.class, 2);
1257 // Add a corresponding entry in the Spring Security Tables
1258 addPermissionsForUri(jpaTransactionContext, permission, permRole);
1260 logger.debug("Finished full perm generation for "
1261 + ":" + permission.getTenantId()
1262 + ":" + permission.getResourceName()
1263 + ":" + actionGroup.getName()
1264 + ":" + profiler.getCumulativeTime());
1269 public static boolean hasTokenExpired(EmailConfig emailConfig, Token token) throws NoSuchAlgorithmException {
1270 boolean result = false;
1272 int maxConfigSeconds = emailConfig.getPasswordResetConfig().getTokenExpirationSeconds().intValue();
1273 int maxTokenSeconds = token.getExpireSeconds().intValue();
1275 long createdTime = token.getCreatedAtItem().getTime();
1276 long configExpirationTime = createdTime + maxConfigSeconds * 1000; // the current tenant config for how long a token stays valid
1277 long tokenDefinedExirationTime = createdTime + maxTokenSeconds * 1000; // the tenant config for how long a token stays valid when the token was created.
1279 if (configExpirationTime != tokenDefinedExirationTime) {
1280 String msg = String.format("The configured expiration time for the token = '%s' changed from when the token was created.",
1285 // Note: the current tenant bindings config for expiration takes precedence over the config used to create the token.
1287 if (System.currentTimeMillis() >= configExpirationTime) {
1295 * Validate that the password reset configuration is correct.
1297 private static String validatePasswordResetConfig(PasswordResetConfig passwordResetConfig) {
1298 String result = null;
1300 if (passwordResetConfig != null) {
1301 result = passwordResetConfig.getMessage();
1302 if (result == null || result.length() == 0) {
1303 result = DEFAULT_PASSWORD_RESET_EMAIL_MESSAGE;
1304 logger.warn("Could not find a password reset message in the tenant's configuration. Using the default one");
1307 if (result.contains("{{link}}") == false) {
1308 logger.warn("The tenant's password reset message does not contain a required '{{link}}' marker.");
1312 if (passwordResetConfig.getLoginpage() == null || passwordResetConfig.getLoginpage().trim().isEmpty()) {
1313 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'.");
1317 String subject = passwordResetConfig.getSubject();
1318 if (subject == null || subject.trim().isEmpty()) {
1319 passwordResetConfig.setSubject(DEFAULT_PASSWORD_RESET_EMAIL_SUBJECT);
1328 * Generate a password reset message. Embeds an authorization token to reset a user's password.
1330 public static String generatePasswordResetEmailMessage(EmailConfig emailConfig, AccountListItem accountListItem, Token token) throws Exception {
1331 String result = null;
1333 result = validatePasswordResetConfig(emailConfig.getPasswordResetConfig());
1334 if (result == null) {
1335 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.",
1336 token.getTenantId(), accountListItem.getEmail());
1337 throw new Exception(errMsg);
1340 String link = emailConfig.getBaseurl() + emailConfig.getPasswordResetConfig().getLoginpage() + "?token=" + token.getId();
1341 result = result.replaceAll("\\{\\{link\\}\\}", link);
1343 if (result.contains("{{greeting}}")) {
1344 String greeting = accountListItem.getScreenName();
1345 result = result.replaceAll("\\{\\{greeting\\}\\}", greeting);
1346 result = result.replaceAll("\\\\n", "\\\n");
1347 result = result.replaceAll("\\\\r", "\\\r");
1353 public static void persistTenantBindingsMD5Hash(TenantBindingConfigReaderImpl tenantBindingConfigReader,
1354 DatabaseProductType databaseProductType, String cspaceDatabaseName) throws Exception {
1355 // Need to define and look for a createDisabled attribute in tenant config
1356 String updateTableSQL = "UPDATE tenants SET config_md5hash = ? WHERE id = ?";
1359 PreparedStatement pstmt = null;
1361 conn = getConnection(cspaceDatabaseName);
1362 pstmt = conn.prepareStatement(updateTableSQL); // create a statement
1363 for (String tId : AuthorizationCommon.tenantConfigMD5HashTable.keySet()) {
1364 pstmt.setString(1, AuthorizationCommon.getTenantConfigMD5Hash(tId));
1365 pstmt.setString(2, tId);
1366 if (logger.isDebugEnabled()) {
1367 logger.debug("createMissingTenants adding entry for tenant: " + tId);
1369 pstmt.executeUpdate();
1372 } catch(Exception e) {
1375 if (pstmt!=null) pstmt.close();