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,salt, 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 salt = UUID.randomUUID().toString();
470 String secEncPasswd = SecurityUtils.createPasswordHash(
471 adminAcctName, DEFAULT_ADMIN_PASSWORD, salt);
472 pstmt.setString(1, adminAcctName); // set username param
473 pstmt.setString(2, secEncPasswd); // set passwd param
474 pstmt.setString(3, salt);
475 if (logger.isDebugEnabled()) {
476 logger.debug("createDefaultUsersAndAccounts adding user: "
477 +adminAcctName+" for tenant: "+tName);
479 pstmt.executeUpdate();
480 } else if (logger.isDebugEnabled()) {
481 logger.debug("createDefaultUsersAndAccounts: user: "+adminAcctName
482 +" already exists - skipping.");
486 String readerAcctName = getDefaultReaderUserID(tName);
487 if(!usersInRepo.contains(readerAcctName)) {
488 String salt = UUID.randomUUID().toString();
489 String secEncPasswd = SecurityUtils.createPasswordHash(
490 readerAcctName, DEFAULT_READER_PASSWORD, salt);
491 pstmt.setString(1, readerAcctName); // set username param
492 pstmt.setString(2, secEncPasswd); // set passwd param
493 pstmt.setString(3, salt);
494 if (logger.isDebugEnabled()) {
495 logger.debug("createDefaultUsersAndAccounts adding user: "
496 +readerAcctName+" for tenant: "+tName);
498 pstmt.executeUpdate();
499 } else if (logger.isDebugEnabled()) {
500 logger.debug("createDefaultUsersAndAccounts: user: "+readerAcctName
501 +" already exists - skipping.");
505 } catch(Exception e) {
516 private static void findOrCreateDefaultAccounts(Connection conn, Hashtable<String, String> tenantInfo,
517 ArrayList<String> usersInRepo,
518 Hashtable<String, String> tenantAdminAcctCSIDs, Hashtable<String, String> tenantReaderAcctCSIDs)
519 throws SQLException, Exception {
520 // Third, create the accounts. Assume that if the users were already there,
521 // then the accounts were as well
522 PreparedStatement pstmt = null;
524 pstmt = conn.prepareStatement(INSERT_ACCOUNT_SQL); // create a statement
525 for(String tId : tenantInfo.keySet()) {
526 String tName = tenantInfo.get(tId);
527 String adminCSID = UUID.randomUUID().toString();
528 tenantAdminAcctCSIDs.put(tId, adminCSID);
529 String adminAcctName = getDefaultAdminUserID(tName);
530 if(!usersInRepo.contains(adminAcctName)) {
531 pstmt.setString(1, adminCSID); // set csid param
532 pstmt.setString(2, adminAcctName); // set email param (bogus)
533 pstmt.setString(3, adminAcctName); // set userid param
534 pstmt.setString(4, "Administrator");// set screen name param
535 if (logger.isDebugEnabled()) {
536 logger.debug("createDefaultAccounts adding account: "
537 +adminAcctName+" for tenant: "+tName);
539 pstmt.executeUpdate();
540 } else if (logger.isDebugEnabled()) {
541 logger.debug("createDefaultAccounts: user: "+adminAcctName
542 +" already exists - skipping account generation.");
545 String readerCSID = UUID.randomUUID().toString();
546 tenantReaderAcctCSIDs.put(tId, readerCSID);
547 String readerAcctName = getDefaultReaderUserID(tName);
548 if(!usersInRepo.contains(readerAcctName)) {
549 pstmt.setString(1, readerCSID); // set csid param
550 pstmt.setString(2, readerAcctName); // set email param (bogus)
551 pstmt.setString(3, readerAcctName); // set userid param
552 pstmt.setString(4, "Reader"); // set screen name param
553 if (logger.isDebugEnabled()) {
554 logger.debug("createDefaultAccounts adding account: "
555 +readerAcctName+" for tenant: "+tName);
557 pstmt.executeUpdate();
558 } else if (logger.isDebugEnabled()) {
559 logger.debug("createDefaultAccounts: user: "+readerAcctName
560 +" already exists - skipping account creation.");
564 } catch(Exception e) {
572 private static boolean findOrCreateTenantManagerUserAndAccount(Connection conn)
573 throws SQLException, Exception {
574 // Find or create the special tenant manager account.
575 // Later can make the user name for tenant manager be configurable, settable.
576 Statement stmt = null;
577 PreparedStatement pstmt = null;
578 boolean created = false;
580 boolean foundTMgrUser = false;
581 stmt = conn.createStatement();
582 ResultSet rs = stmt.executeQuery(QUERY_TENANT_MGR_USER_SQL);
583 // Should only find one - only consider it
585 String uName = rs.getString("username");
586 foundTMgrUser = uName.equals(TENANT_MANAGER_USER);
590 String salt = UUID.randomUUID().toString();
591 pstmt = conn.prepareStatement(INSERT_USER_SQL); // create a statement
592 String secEncPasswd = SecurityUtils.createPasswordHash(
593 TENANT_MANAGER_USER, DEFAULT_TENANT_MANAGER_PASSWORD, salt);
594 pstmt.setString(1, TENANT_MANAGER_USER); // set username param
595 pstmt.setString(2, secEncPasswd); // set passwd param
596 pstmt.setString(3, salt);
597 if (logger.isDebugEnabled()) {
598 logger.debug("findOrCreateTenantManagerUserAndAccount adding tenant manager user: "
599 +TENANT_MANAGER_USER);
601 pstmt.executeUpdate();
603 // Now create the account to match
604 pstmt = conn.prepareStatement(INSERT_ACCOUNT_SQL); // create a statement
605 pstmt.setString(1, AuthN.TENANT_MANAGER_ACCT_ID); // set csid param
606 pstmt.setString(2, DEFAULT_TENANT_MANAGER_EMAIL); // set email param (bogus)
607 pstmt.setString(3, TENANT_MANAGER_USER); // set userid param
608 pstmt.setString(4, TENANT_MANAGER_SCREEN_NAME);// set screen name param
609 if (logger.isDebugEnabled()) {
610 logger.debug("findOrCreateTenantManagerUserAndAccount adding tenant manager account: "
611 +TENANT_MANAGER_USER);
613 pstmt.executeUpdate();
616 } else if (logger.isDebugEnabled()) {
617 logger.debug("findOrCreateTenantManagerUserAndAccount: tenant manager: "+TENANT_MANAGER_USER
618 +" already exists.");
620 } catch(Exception e) {
631 private static void bindDefaultAccountsToTenants(Connection conn, DatabaseProductType databaseProductType,
632 Hashtable<String, String> tenantInfo, ArrayList<String> usersInRepo,
633 Hashtable<String, String> tenantAdminAcctCSIDs, Hashtable<String, String> tenantReaderAcctCSIDs)
634 throws SQLException, Exception {
635 // Fourth, bind accounts to tenants. Assume that if the users were already there,
636 // then the accounts were bound to tenants correctly
637 PreparedStatement pstmt = null;
639 String insertAccountTenantSQL;
640 if (databaseProductType == DatabaseProductType.MYSQL) {
641 insertAccountTenantSQL =
642 "INSERT INTO accounts_tenants (TENANTS_ACCOUNTS_COMMON_CSID,tenant_id) "
644 } else if (databaseProductType == DatabaseProductType.POSTGRESQL) {
645 insertAccountTenantSQL =
646 "INSERT INTO accounts_tenants (HJID, TENANTS_ACCOUNTS_COMMON_CSID,tenant_id) "
647 + " VALUES(nextval('hibernate_sequence'), ?, ?)";
649 throw new Exception("Unrecognized database system.");
651 pstmt = conn.prepareStatement(insertAccountTenantSQL); // create a statement
652 for(String tId : tenantInfo.keySet()) {
653 String tName = tenantInfo.get(tId);
654 if(!usersInRepo.contains(getDefaultAdminUserID(tName))) {
655 String adminAcct = tenantAdminAcctCSIDs.get(tId);
656 pstmt.setString(1, adminAcct); // set acct CSID param
657 pstmt.setString(2, tId); // set tenant_id param
658 if (logger.isDebugEnabled()) {
659 logger.debug("createDefaultAccounts binding account id: "
660 +adminAcct+" to tenant id: "+tId);
662 pstmt.executeUpdate();
664 if(!usersInRepo.contains(getDefaultReaderUserID(tName))) {
665 String readerAcct = tenantReaderAcctCSIDs.get(tId);
666 pstmt.setString(1, readerAcct); // set acct CSID param
667 pstmt.setString(2, tId); // set tenant_id param
668 if (logger.isDebugEnabled()) {
669 logger.debug("createDefaultAccounts binding account id: "
670 +readerAcct+" to tenant id: "+tId);
672 pstmt.executeUpdate();
676 } catch(Exception e) {
685 * Creates the default Admin and Reader roles for all the configured tenants.
687 * Returns the CSID of the Spring Admin role.
691 * @param tenantAdminRoleCSIDs
692 * @param tenantReaderRoleCSIDs
694 * @throws SQLException
697 private static String findOrCreateDefaultRoles(Connection conn, Hashtable<String, String> tenantInfo,
698 Hashtable<String, String> tenantAdminRoleCSIDs, Hashtable<String, String> tenantReaderRoleCSIDs)
699 throws SQLException, Exception {
701 String springAdminRoleCSID = null;
702 Statement stmt = null;
703 PreparedStatement pstmt = null;
706 // Look for the Spring Security admin role. If not found, create it.
708 final String querySpringRole = String.format("SELECT csid from roles WHERE rolename='%s'", AuthN.ROLE_SPRING_ADMIN_NAME);
709 stmt = conn.createStatement();
710 ResultSet rs = stmt.executeQuery(querySpringRole);
712 springAdminRoleCSID = rs.getString(1);
713 if (logger.isDebugEnabled()) {
714 logger.debug("createDefaultAccounts found Spring Admin role: " + springAdminRoleCSID);
717 final String insertSpringAdminRoleSQL = String.format(
718 "INSERT INTO roles (csid, rolename, displayName, rolegroup, created_at, tenant_id) VALUES ('%s', '%s', '%s', '%s', now(), '%s')",
719 AuthN.ROLE_SPRING_ADMIN_ID, AuthN.ROLE_SPRING_ADMIN_NAME, AuthN.SPRING_ADMIN_USER, AuthN.ROLE_SPRING_GROUP_NAME, AuthN.ADMIN_TENANT_ID);
720 stmt.executeUpdate(insertSpringAdminRoleSQL);
721 springAdminRoleCSID = AuthN.ROLE_SPRING_ADMIN_ID;
727 // Look for and save each tenants default Admin and Reader roles
729 final String getRoleCSIDSql = "SELECT csid from roles WHERE tenant_id=? and rolename=?";
730 pstmt = conn.prepareStatement(getRoleCSIDSql); // create a statement
731 for (String tenantId : tenantInfo.keySet()) {
733 // Look for the default Admin role
735 pstmt.setString(1, tenantId);
736 pstmt.setString(2, getDefaultAdminRole(tenantId));
737 rs = pstmt.executeQuery();
738 // extract data from the ResultSet
740 throw new RuntimeException("Cannot find role: " + getDefaultAdminRole(tenantId)
741 + " for tenant id: " + tenantId + " in roles!");
743 String tenantAdminRoleCSID = rs.getString(1); // First column (#1) is the CSID
744 tenantAdminRoleCSIDs.put(tenantId, tenantAdminRoleCSID);
748 // Look for the default Reader role
750 pstmt.setString(1, tenantId); // set tenant_id param
751 pstmt.setString(2, getDefaultReaderRole(tenantId)); // set rolename param
752 rs = pstmt.executeQuery();
753 // extract data from the ResultSet
755 throw new RuntimeException("Cannot find role: " + getDefaultReaderRole(tenantId)
756 + " for tenant id: " + tenantId + " in roles!");
758 String tenantReaderRoleCSID = rs.getString(1);
759 tenantReaderRoleCSIDs.put(tenantId, tenantReaderRoleCSID);
763 } catch(Exception e) {
766 if (stmt != null) stmt.close();
767 if (pstmt != null) pstmt.close();
770 return springAdminRoleCSID;
773 private static String findTenantManagerRole(Connection conn )
774 throws SQLException, RuntimeException, Exception {
775 String tenantMgrRoleCSID = null;
776 PreparedStatement pstmt = null;
778 String rolename = getQualifiedRoleName(AuthN.ALL_TENANTS_MANAGER_TENANT_ID,
779 AuthN.ROLE_ALL_TENANTS_MANAGER);
780 pstmt = conn.prepareStatement(GET_TENANT_MGR_ROLE_SQL); // create a statement
782 pstmt.setString(1, rolename); // set rolename param
783 rs = pstmt.executeQuery();
785 tenantMgrRoleCSID = rs.getString(1);
786 if (logger.isDebugEnabled()) {
787 logger.debug("findTenantManagerRole found Tenant Mgr role: "
792 } catch(Exception e) {
798 if(tenantMgrRoleCSID==null)
799 throw new RuntimeException("findTenantManagerRole: Cound not find tenant Manager Role!");
800 return tenantMgrRoleCSID;
803 private static void bindAccountsToRoles(Connection conn, DatabaseProductType databaseProductType,
804 Hashtable<String, String> tenantInfo, ArrayList<String> usersInRepo,
805 String springAdminRoleCSID,
806 Hashtable<String, String> tenantAdminRoleCSIDs, Hashtable<String, String> tenantReaderRoleCSIDs,
807 Hashtable<String, String> tenantAdminAcctCSIDs, Hashtable<String, String> tenantReaderAcctCSIDs)
808 throws SQLException, Exception {
809 // Sixth, bind the accounts to roles. If the users already existed,
810 // we'll assume they were set up correctly.
811 PreparedStatement pstmt = null;
813 String insertAccountRoleSQL;
814 if (databaseProductType == DatabaseProductType.POSTGRESQL) {
815 insertAccountRoleSQL = INSERT_ACCOUNT_ROLE_SQL_POSTGRES;
817 throw new Exception("Unrecognized database system.");
820 pstmt = conn.prepareStatement(insertAccountRoleSQL); // create a statement
821 for (String tId : tenantInfo.keySet()) {
822 String adminUserId = getDefaultAdminUserID(tenantInfo.get(tId));
823 if (!usersInRepo.contains(adminUserId)) {
824 String adminAcct = tenantAdminAcctCSIDs.get(tId);
825 String adminRoleId = tenantAdminRoleCSIDs.get(tId);
826 pstmt.setString(1, adminAcct); // set acct CSID param
827 pstmt.setString(2, adminUserId); // set user_id param
828 pstmt.setString(3, adminRoleId); // set role_id param
829 pstmt.setString(4, getDefaultAdminRole(tId)); // set rolename param
830 pstmt.executeUpdate();
832 // Now add the Spring Admin Role to the admin accounts
834 pstmt.setString(3, springAdminRoleCSID); // set role_id param
835 pstmt.setString(4, AuthN.ROLE_SPRING_ADMIN_NAME); // set rolename param
836 pstmt.executeUpdate();
838 String readerUserId = getDefaultReaderUserID(tenantInfo.get(tId));
839 if (!usersInRepo.contains(readerUserId)) {
840 String readerAcct = tenantReaderAcctCSIDs.get(tId);
841 String readerRoleId = tenantReaderRoleCSIDs.get(tId);
842 pstmt.setString(1, readerAcct); // set acct CSID param
843 pstmt.setString(2, readerUserId); // set user_id param
844 pstmt.setString(3, readerRoleId); // set role_id param
845 pstmt.setString(4, getDefaultReaderRole(tId)); // set rolename param
846 pstmt.executeUpdate();
850 } catch(Exception e) {
859 private static void bindTenantManagerAccountRole(Connection conn, DatabaseProductType databaseProductType,
860 String tenantManagerUserID, String tenantManagerAccountID, String tenantManagerRoleID, String tenantManagerRoleName )
861 throws SQLException, Exception {
862 PreparedStatement pstmt = null;
864 String insertAccountRoleSQL;
865 if (databaseProductType == DatabaseProductType.MYSQL) {
866 insertAccountRoleSQL = INSERT_ACCOUNT_ROLE_SQL_MYSQL;
867 } else if (databaseProductType == DatabaseProductType.POSTGRESQL) {
868 insertAccountRoleSQL = INSERT_ACCOUNT_ROLE_SQL_POSTGRES;
870 throw new Exception("Unrecognized database system.");
872 if (logger.isDebugEnabled()) {
873 logger.debug("bindTenantManagerAccountRole binding account to role with SQL:\n"
874 +insertAccountRoleSQL);
876 pstmt = conn.prepareStatement(insertAccountRoleSQL); // create a statement
877 pstmt.setString(1, tenantManagerAccountID); // set acct CSID param
878 pstmt.setString(2, tenantManagerUserID); // set user_id param
879 pstmt.setString(3, tenantManagerRoleID); // set role_id param
880 pstmt.setString(4, tenantManagerRoleName); // set rolename param
881 pstmt.executeUpdate();
883 /* At this point, tenant manager should not need the Spring Admin Role
884 pstmt.setString(3, springAdminRoleCSID); // set role_id param
885 pstmt.setString(4, SPRING_ADMIN_ROLE); // set rolename param
886 if (logger.isDebugEnabled()) {
887 logger.debug("createDefaultAccounts binding account: "
888 +adminUserId+" to Spring Admin role: "+springAdminRoleCSID);
890 pstmt.executeUpdate();
894 } catch(Exception e) {
903 * Using the tenant bindings, ensure there are corresponding Tenant records (db columns).
905 //FIXME: This code should be using JPA objects and JPATransactionContext, not raw SQL.
906 public static void createTenants(
907 TenantBindingConfigReaderImpl tenantBindingConfigReader,
908 DatabaseProductType databaseProductType,
909 String cspaceDatabaseName) throws Exception {
910 logger.debug("ServiceMain.createTenants starting...");
911 Hashtable<String, String> tenantInfo = getTenantNamesFromConfig(tenantBindingConfigReader);
912 Connection conn = null;
914 conn = getConnection(cspaceDatabaseName);
915 ArrayList<String> existingTenants = compileExistingTenants(conn, tenantInfo);
917 // Note that this only creates tenants not marked as "createDisabled"
918 createMissingTenants(conn, tenantInfo, existingTenants);
919 } catch (Exception e) {
920 logger.debug("Exception in createTenants: " + e.getLocalizedMessage());
927 } catch (SQLException sqle) {
928 if (logger.isDebugEnabled()) {
929 logger.debug("SQL Exception closing statement/connection: " + sqle.getLocalizedMessage());
937 * @param tenantBindingConfigReader
938 * @param databaseProductType
939 * @param cspaceDatabaseName
942 //FIXME: This code should be using the JPA objects and JPATransactionContext, not raw SQL.
943 public static void createDefaultAccounts(
944 TenantBindingConfigReaderImpl tenantBindingConfigReader,
945 DatabaseProductType databaseProductType,
946 String cspaceDatabaseName) throws Exception {
948 logger.debug("ServiceMain.createDefaultAccounts starting...");
950 Hashtable<String, String> tenantInfo = getTenantNamesFromConfig(tenantBindingConfigReader);
951 Connection conn = null;
952 // TODO - need to put in tests for existence first.
953 // We could just look for the accounts per tenant up front, and assume that
954 // the rest is there if the accounts are.
955 // Could add a sql script to remove these if need be - Spring only does roles,
956 // and we're not touching that, so we could safely toss the
957 // accounts, users, account-tenants, account-roles, and start over.
959 conn = getConnection(cspaceDatabaseName);
961 ArrayList<String> usersInRepo = findOrCreateDefaultUsers(conn, tenantInfo);
963 Hashtable<String, String> tenantAdminAcctCSIDs = new Hashtable<String, String>();
964 Hashtable<String, String> tenantReaderAcctCSIDs = new Hashtable<String, String>();
965 findOrCreateDefaultAccounts(conn, tenantInfo, usersInRepo,
966 tenantAdminAcctCSIDs, tenantReaderAcctCSIDs);
968 bindDefaultAccountsToTenants(conn, databaseProductType, tenantInfo, usersInRepo,
969 tenantAdminAcctCSIDs, tenantReaderAcctCSIDs);
971 Hashtable<String, String> tenantAdminRoleCSIDs = new Hashtable<String, String>();
972 Hashtable<String, String> tenantReaderRoleCSIDs = new Hashtable<String, String>();
973 String springAdminRoleCSID = findOrCreateDefaultRoles(conn, tenantInfo,
974 tenantAdminRoleCSIDs, tenantReaderRoleCSIDs);
976 bindAccountsToRoles(conn, databaseProductType,
977 tenantInfo, usersInRepo, springAdminRoleCSID,
978 tenantAdminRoleCSIDs, tenantReaderRoleCSIDs,
979 tenantAdminAcctCSIDs, tenantReaderAcctCSIDs);
981 boolean createdTenantMgrAccount = findOrCreateTenantManagerUserAndAccount(conn);
982 if (createdTenantMgrAccount) {
983 // If we created the account, we need to create the bindings. Otherwise, assume they
984 // are all set (from previous initialization).
985 String tenantManagerRoleCSID = findTenantManagerRole(conn);
986 bindTenantManagerAccountRole(conn, databaseProductType,
987 TENANT_MANAGER_USER, AuthN.TENANT_MANAGER_ACCT_ID,
988 tenantManagerRoleCSID, AuthN.ROLE_ALL_TENANTS_MANAGER);
990 } catch (Exception e) {
991 logger.debug("Exception in createDefaultAccounts: " + e.getLocalizedMessage());
998 } catch (SQLException sqle) {
999 if (logger.isDebugEnabled()) {
1000 logger.debug("SQL Exception closing statement/connection: " + sqle.getLocalizedMessage());
1006 private static String getDefaultAdminRole(String tenantId) {
1007 return ROLE_PREFIX + tenantId + TENANT_ADMIN_ROLE_SUFFIX;
1010 private static String getDefaultReaderRole(String tenantId) {
1011 return ROLE_PREFIX+tenantId+TENANT_READER_ROLE_SUFFIX;
1014 private static String getDefaultAdminUserID(String tenantName) {
1015 return TENANT_ADMIN_ACCT_PREFIX + tenantName;
1018 private static String getDefaultReaderUserID(String tenantName) {
1019 return TENANT_READER_ACCT_PREFIX + tenantName;
1022 static private PermissionAction createPermissionAction(Permission perm,
1023 ActionType actionType) {
1024 PermissionAction pa = new PermissionAction();
1026 CSpaceAction action = URIResourceImpl.getAction(actionType);
1027 URIResourceImpl uriRes = new URIResourceImpl(perm.getTenantId(),
1028 perm.getResourceName(), action);
1029 pa.setName(actionType);
1030 pa.setObjectIdentity(uriRes.getHashedId().toString());
1031 pa.setObjectIdentityResource(uriRes.getId());
1036 private static HashSet<String> getTransitionVerbList(TenantBindingType tenantBinding, ServiceBindingType serviceBinding) {
1037 HashSet<String> result = new HashSet<String>();
1039 TransitionDefList transitionDefList = getTransitionDefList(tenantBinding, serviceBinding);
1040 for (TransitionDef transitionDef : transitionDefList.getTransitionDef()) {
1041 String transitionVerb = transitionDef.getName();
1042 String[] tokens = transitionVerb.split("_"); // Split the verb into words. The workflow verbs are compound words combined with the '_' character.
1043 result.add(tokens[0]); // We only care about the first word.
1049 private static TransitionDefList getTransitionDefList(TenantBindingType tenantBinding, ServiceBindingType serviceBinding) {
1050 TransitionDefList result = null;
1052 String serviceObjectName = serviceBinding.getObject().getName();
1054 @SuppressWarnings("rawtypes")
1055 DocumentHandler docHandler = ServiceConfigUtils.createDocumentHandlerInstance(
1056 tenantBinding, serviceBinding);
1057 Lifecycle lifecycle = docHandler.getLifecycle(serviceObjectName);
1058 if (lifecycle != null) {
1059 result = lifecycle.getTransitionDefList();
1061 } catch (Exception e) {
1062 // Ignore this exception and return an empty non-null TransitionDefList
1065 if (result == null) {
1066 if (serviceBinding.getType().equalsIgnoreCase(ServiceBindingUtils.SERVICE_TYPE_SECURITY) == false) {
1067 logger.debug("Could not retrieve a lifecycle transition definition list from: "
1068 + serviceBinding.getName()
1069 + " with tenant ID = "
1070 + tenantBinding.getId());
1072 // return an empty list
1073 result = new TransitionDefList();
1075 logger.debug("Successfully retrieved a lifecycle transition definition list from: "
1076 + serviceBinding.getName()
1077 + " with tenant ID = "
1078 + tenantBinding.getId());
1085 * Creates the immutable workflow permission sets for the default admin and reader roles.
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
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 HashSet<String> transitionVerbList = getTransitionVerbList(tenantBinding, serviceBinding);
1126 for (String transitionVerb : transitionVerbList) {
1128 // Create the permission for the admin role
1129 Permission adminPerm = createWorkflowPermission(tenantBinding, serviceBinding, transitionVerb, ACTIONGROUP_CRUDL, true);
1130 persist(jpaTransactionContext, adminPerm, adminRole, true, ACTIONGROUP_CRUDL);
1132 // Create the permission for the read-only role
1133 Permission readonlyPerm = createWorkflowPermission(tenantBinding, serviceBinding, transitionVerb, ACTIONGROUP_RL, true);
1134 persist(jpaTransactionContext, readonlyPerm, readonlyRole, true, ACTIONGROUP_RL); // Persist/store the permission and permrole records and related Spring Security info
1136 jpaTransactionContext.commitTransaction();
1137 } catch (IllegalStateException e) {
1138 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.
1139 } catch (Exception x) {
1140 jpaTransactionContext.markForRollback();
1143 logger.warning("AuthZ refresh service binding property is set to FALSE so default permissions will NOT be refreshed for: "
1144 + serviceBinding.getName());
1148 } catch (Exception e) {
1149 jpaTransactionContext.markForRollback();
1150 logger.fine("Caught exception and rolling back permission creation: " + e.getMessage());
1155 private static void createMissingTenants(Connection conn, Hashtable<String, String> tenantInfo,
1156 ArrayList<String> existingTenants) throws SQLException, Exception {
1157 // Need to define and look for a createDisabled attribute in tenant config
1158 final String insertTenantSQL =
1159 "INSERT INTO tenants (id,name,authorities_initialized,disabled,created_at) VALUES (?,?,FALSE,FALSE,now())";
1160 PreparedStatement pstmt = null;
1162 pstmt = conn.prepareStatement(insertTenantSQL); // create a statement
1163 for(String tId : tenantInfo.keySet()) {
1164 if(existingTenants.contains(tId)) {
1165 if (logger.isDebugEnabled()) {
1166 logger.debug("createMissingTenants: tenant exists (skipping): "
1167 +tenantInfo.get(tId));
1171 pstmt.setString(1, tId); // set id param
1172 pstmt.setString(2, tenantInfo.get(tId)); // set name param
1173 if (logger.isDebugEnabled()) {
1174 logger.debug("createMissingTenants adding entry for tenant: "+tId);
1176 pstmt.executeUpdate();
1179 } catch(Exception e) {
1187 public static String getPersistedMD5Hash(String tenantId, String cspaceDatabaseName) throws Exception {
1188 String result = null;
1190 // First find or create the tenants
1191 final String queryTenantSQL = String.format("SELECT id, name, config_md5hash FROM tenants WHERE id = '%s'", tenantId);
1193 Statement stmt = null;
1197 conn = getConnection(cspaceDatabaseName);
1198 stmt = conn.createStatement();
1199 ResultSet rs = stmt.executeQuery(queryTenantSQL);
1202 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'.",
1203 tenantId, cspaceDatabaseName);
1204 throw new Exception(errMsg);
1206 String tId = rs.getString("id"); // for debugging only
1207 String tName = rs.getString("name"); // for debugging only
1208 result = rs.getString("config_md5hash");
1212 } catch(Exception e) {
1215 if (stmt != null) stmt.close();
1221 private static PermissionRoleRel findPermRoleRel(
1222 JPATransactionContext jpaTransactionContext,
1223 String permissionId,
1225 PermissionRoleRel result = null;
1228 String whereClause = "where permissionId = :id and roleId = :roleId";
1229 HashMap<String, Object> params = new HashMap<String, Object>();
1230 params.put("id", permissionId);
1231 params.put("roleId", RoleId);
1233 result = (PermissionRoleRel) JpaStorageUtils.getEntity(jpaTransactionContext,
1234 PermissionRoleRel.class.getCanonicalName(), whereClause, params);
1235 } catch (Exception e) {
1236 //Do nothing. Will return null;
1243 * Persists the Permission, PermissionRoleRel, and Spring Security table entries all in one transaction
1245 private static void persist(JPATransactionContext jpaTransactionContext, Permission permission, Role role, boolean enforceTenancy, ActionGroup actionGroup) throws Exception {
1246 AuthorizationStore authzStore = new AuthorizationStore();
1247 // First persist the Permission record
1248 authzStore.store(jpaTransactionContext, permission);
1250 // If the PermRoleRel doesn't already exists then relate the permission and the role in a new PermissionRole (the service payload)
1251 // Create a PermissionRoleRel (the database relation table for the permission and role)
1252 PermissionRoleRel permRoleRel = findPermRoleRel(jpaTransactionContext, permission.getCsid(), role.getCsid());
1253 if (permRoleRel == null) {
1254 PermissionRole permRole = createPermissionRole(permission, role, enforceTenancy);
1255 List<PermissionRoleRel> permRoleRels = new ArrayList<PermissionRoleRel>();
1256 PermissionRoleUtil.buildPermissionRoleRel(jpaTransactionContext, permRole, SubjectType.ROLE, permRoleRels,
1257 false /*not for delete*/, role.getTenantId());
1258 for (PermissionRoleRel prr : permRoleRels) {
1259 authzStore.store(jpaTransactionContext, prr);
1261 Profiler profiler = new Profiler(AuthorizationCommon.class, 2);
1263 // Add a corresponding entry in the Spring Security Tables
1264 addPermissionsForUri(jpaTransactionContext, permission, permRole);
1266 logger.debug("Finished full perm generation for "
1267 + ":" + permission.getTenantId()
1268 + ":" + permission.getResourceName()
1269 + ":" + actionGroup.getName()
1270 + ":" + profiler.getCumulativeTime());
1275 public static boolean hasTokenExpired(EmailConfig emailConfig, Token token) throws NoSuchAlgorithmException {
1276 boolean result = false;
1278 int maxConfigSeconds = emailConfig.getPasswordResetConfig().getTokenExpirationSeconds().intValue();
1279 int maxTokenSeconds = token.getExpireSeconds().intValue();
1281 long createdTime = token.getCreatedAtItem().getTime();
1282 long configExpirationTime = createdTime + maxConfigSeconds * 1000; // the current tenant config for how long a token stays valid
1283 long tokenDefinedExirationTime = createdTime + maxTokenSeconds * 1000; // the tenant config for how long a token stays valid when the token was created.
1285 if (configExpirationTime != tokenDefinedExirationTime) {
1286 String msg = String.format("The configured expiration time for the token = '%s' changed from when the token was created.",
1291 // Note: the current tenant bindings config for expiration takes precedence over the config used to create the token.
1293 if (System.currentTimeMillis() >= configExpirationTime) {
1301 * Validate that the password reset configuration is correct.
1303 private static String validatePasswordResetConfig(PasswordResetConfig passwordResetConfig) {
1304 String result = null;
1306 if (passwordResetConfig != null) {
1307 result = passwordResetConfig.getMessage();
1308 if (result == null || result.length() == 0) {
1309 result = DEFAULT_PASSWORD_RESET_EMAIL_MESSAGE;
1310 logger.warn("Could not find a password reset message in the tenant's configuration. Using the default one");
1313 if (result.contains("{{link}}") == false) {
1314 logger.warn("The tenant's password reset message does not contain a required '{{link}}' marker.");
1318 if (passwordResetConfig.getLoginpage() == null || passwordResetConfig.getLoginpage().trim().isEmpty()) {
1319 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'.");
1323 String subject = passwordResetConfig.getSubject();
1324 if (subject == null || subject.trim().isEmpty()) {
1325 passwordResetConfig.setSubject(DEFAULT_PASSWORD_RESET_EMAIL_SUBJECT);
1334 * Generate a password reset message. Embeds an authorization token to reset a user's password.
1336 public static String generatePasswordResetEmailMessage(EmailConfig emailConfig, AccountListItem accountListItem, Token token) throws Exception {
1337 String result = null;
1339 result = validatePasswordResetConfig(emailConfig.getPasswordResetConfig());
1340 if (result == null) {
1341 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.",
1342 token.getTenantId(), accountListItem.getEmail());
1343 throw new Exception(errMsg);
1346 String link = emailConfig.getBaseurl() + emailConfig.getPasswordResetConfig().getLoginpage() + "?token=" + token.getId();
1347 result = result.replaceAll("\\{\\{link\\}\\}", link);
1349 if (result.contains("{{greeting}}")) {
1350 String greeting = accountListItem.getScreenName();
1351 result = result.replaceAll("\\{\\{greeting\\}\\}", greeting);
1352 result = result.replaceAll("\\\\n", "\\\n");
1353 result = result.replaceAll("\\\\r", "\\\r");
1359 public static void persistTenantBindingsMD5Hash(TenantBindingConfigReaderImpl tenantBindingConfigReader,
1360 DatabaseProductType databaseProductType, String cspaceDatabaseName) throws Exception {
1361 // Need to define and look for a createDisabled attribute in tenant config
1362 String updateTableSQL = "UPDATE tenants SET config_md5hash = ? WHERE id = ?";
1365 PreparedStatement pstmt = null;
1367 conn = getConnection(cspaceDatabaseName);
1368 pstmt = conn.prepareStatement(updateTableSQL); // create a statement
1369 for (String tId : AuthorizationCommon.tenantConfigMD5HashTable.keySet()) {
1370 pstmt.setString(1, AuthorizationCommon.getTenantConfigMD5Hash(tId));
1371 pstmt.setString(2, tId);
1372 if (logger.isDebugEnabled()) {
1373 logger.debug("createMissingTenants adding entry for tenant: " + tId);
1375 pstmt.executeUpdate();
1378 } catch(Exception e) {
1381 if (pstmt!=null) pstmt.close();