1 package org.collectionspace.services.common.authorization_mgt;
3 import java.security.NoSuchAlgorithmException;
4 import java.sql.Connection;
5 import java.sql.PreparedStatement;
6 import java.sql.ResultSet;
7 import java.sql.SQLException;
8 import java.sql.Statement;
9 import java.util.ArrayList;
10 import java.util.Date;
11 import java.util.HashMap;
12 import java.util.HashSet;
13 import java.util.Hashtable;
14 import java.util.List;
16 import java.util.UUID;
18 import javax.naming.NamingException;
19 import javax.persistence.EntityManager;
20 import javax.persistence.EntityManagerFactory;
22 import org.collectionspace.authentication.AuthN;
23 import org.collectionspace.services.account.AccountListItem;
25 import org.collectionspace.services.authentication.Token;
26 import org.collectionspace.services.authorization.AuthZ;
27 import org.collectionspace.services.authorization.CSpaceAction;
28 import org.collectionspace.services.authorization.PermissionException;
29 import org.collectionspace.services.authorization.PermissionRole;
30 import org.collectionspace.services.authorization.PermissionRoleRel;
31 import org.collectionspace.services.authorization.PermissionValue;
32 import org.collectionspace.services.authorization.Role;
33 import org.collectionspace.services.authorization.RoleValue;
34 import org.collectionspace.services.authorization.SubjectType;
35 import org.collectionspace.services.authorization.URIResourceImpl;
36 import org.collectionspace.services.authorization.perms.ActionType;
37 import org.collectionspace.services.authorization.perms.EffectType;
38 import org.collectionspace.services.authorization.perms.Permission;
39 import org.collectionspace.services.authorization.perms.PermissionAction;
41 import org.collectionspace.services.client.Profiler;
42 import org.collectionspace.services.client.RoleClient;
43 import org.collectionspace.services.client.workflow.WorkflowClient;
45 import org.collectionspace.services.common.config.ServiceConfigUtils;
46 import org.collectionspace.services.common.config.TenantBindingConfigReaderImpl;
47 import org.collectionspace.services.common.context.ServiceBindingUtils;
48 import org.collectionspace.services.common.document.DocumentHandler;
49 import org.collectionspace.services.common.security.SecurityUtils;
50 import org.collectionspace.services.common.storage.DatabaseProductType;
51 import org.collectionspace.services.common.storage.JDBCTools;
52 import org.collectionspace.services.common.storage.jpa.JPATransactionContext;
53 import org.collectionspace.services.common.storage.jpa.JpaStorageUtils;
55 import org.collectionspace.services.config.service.ServiceBindingType;
56 import org.collectionspace.services.config.tenant.EmailConfig;
57 import org.collectionspace.services.config.tenant.PasswordResetConfig;
58 import org.collectionspace.services.config.tenant.TenantBindingType;
60 import org.collectionspace.services.lifecycle.Lifecycle;
61 import org.collectionspace.services.lifecycle.TransitionDef;
62 import org.collectionspace.services.lifecycle.TransitionDefList;
64 //import org.mortbay.log.Log;
65 import org.slf4j.Logger;
66 import org.slf4j.LoggerFactory;
67 import org.springframework.security.acls.model.AlreadyExistsException;
70 public class AuthorizationCommon {
72 final public static String REFRESH_AUTHZ_PROP = "refreshAuthZOnStartup";
75 // For token generation and password reset
77 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.";
78 final private static String tokensalt = "74102328UserDetailsReset";
79 final private static int TIME_SCALAR = 100000;
80 private static final String DEFAULT_PASSWORD_RESET_EMAIL_SUBJECT = "Password reset for CollectionSpace account";
83 // Keep track of the MD5 hash value for the tenant bindings
85 private static final Map<String, String> tenantConfigMD5HashTable = new HashMap<String, String>();
88 // ActionGroup labels/constants
92 final public static String ACTIONGROUP_CRUDL_NAME = "CRUDL";
93 final public static ActionType[] ACTIONSET_CRUDL = {ActionType.CREATE, ActionType.READ, ActionType.UPDATE, ActionType.DELETE, ActionType.SEARCH};
95 final public static String ACTIONGROUP_RL_NAME = "RL";
96 final public static ActionType[] ACTIONSET_RL = {ActionType.READ, ActionType.SEARCH};
98 static ActionGroup ACTIONGROUP_CRUDL;
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;
114 final static Logger logger = LoggerFactory.getLogger(AuthorizationCommon.class);
116 final public static String ROLE_TENANT_ADMINISTRATOR = "TENANT_ADMINISTRATOR";
117 final public static String ROLE_TENANT_READER = "TENANT_READER";
119 public static final String TENANT_MANAGER_USER = "tenantManager";
120 public static final String TENANT_MANAGER_SCREEN_NAME = TENANT_MANAGER_USER;
121 public static final String DEFAULT_TENANT_MANAGER_PASSWORD = "manage";
122 public static final String DEFAULT_TENANT_MANAGER_EMAIL = "tenantManager@collectionspace.org";
124 public static final String TENANT_ADMIN_ACCT_PREFIX = "admin@";
125 public static final String TENANT_READER_ACCT_PREFIX = "reader@";
126 public static final String ROLE_PREFIX = "ROLE_";
127 public static final String TENANT_ADMIN_ROLE_SUFFIX = "_TENANT_ADMINISTRATOR";
128 public static final String TENANT_READER_ROLE_SUFFIX = "_TENANT_READER";
129 public static final String DEFAULT_ADMIN_PASSWORD = "Administrator";
130 public static final String DEFAULT_READER_PASSWORD = "reader";
132 // SQL for init tasks
133 final private static String INSERT_ACCOUNT_ROLE_SQL_MYSQL =
134 "INSERT INTO accounts_roles(account_id, user_id, role_id, role_name, created_at)"
135 +" VALUES(?, ?, ?, ?, now())";
136 final private static String INSERT_ACCOUNT_ROLE_SQL_POSTGRES =
137 "INSERT INTO accounts_roles(HJID, account_id, user_id, role_id, role_name, created_at)"
138 +" VALUES(nextval('hibernate_sequence'), ?, ?, ?, ?, now())";
139 final private static String QUERY_USERS_SQL =
140 "SELECT username FROM users WHERE username LIKE '"
141 +TENANT_ADMIN_ACCT_PREFIX+"%' OR username LIKE '"+TENANT_READER_ACCT_PREFIX+"%'";
142 final private static String INSERT_USER_SQL =
143 "INSERT INTO users (username,passwd, created_at) VALUES (?,?, now())";
144 final private static String INSERT_ACCOUNT_SQL =
145 "INSERT INTO accounts_common "
146 + "(csid, email, userid, status, screen_name, metadata_protection, roles_protection, created_at) "
147 + "VALUES (?,?,?,'ACTIVE',?, 'immutable', 'immutable', now())";
149 // TENANT MANAGER specific SQL
150 final private static String QUERY_TENANT_MGR_USER_SQL =
151 "SELECT username FROM users WHERE username = '"+TENANT_MANAGER_USER+"'";
152 final private static String GET_TENANT_MGR_ROLE_SQL =
153 "SELECT csid from roles WHERE tenant_id='" + AuthN.ALL_TENANTS_MANAGER_TENANT_ID + "' and rolename=?";
155 public static final String IGNORE_TENANT_ID = null; // A null constant to indicate an empty/unused value for the tenant ID
158 public static String getTenantConfigMD5Hash(String tenantId) {
159 return tenantConfigMD5HashTable.get(tenantId);
162 public static String setTenantConfigMD5Hash(String tenantId, String md5hash) {
163 return tenantConfigMD5HashTable.put(tenantId, md5hash);
167 public static Role xgetRole(String tenantId, String displayName) {
170 String roleName = AuthorizationCommon.getQualifiedRoleName(tenantId, displayName);
171 //role = AuthorizationStore.getRoleByName(roleName, tenantId);
176 public static Role getRole(JPATransactionContext jpaTransactionContext, String tenantId, String displayName) {
179 String roleName = AuthorizationCommon.getQualifiedRoleName(tenantId, displayName);
180 role = AuthorizationStore.getRoleByName(jpaTransactionContext, roleName, tenantId);
186 public static Role createRole(String tenantId, String name, String description) {
187 return createRole(tenantId, name, description, false /* mutable by default */);
190 public static Role createRole(String tenantId, String name, String description, boolean immutable) {
191 Role role = new Role();
193 role.setCreatedAtItem(new Date());
194 role.setDisplayName(name);
195 String roleName = AuthorizationCommon.getQualifiedRoleName(tenantId, name);
196 role.setRoleName(roleName);
197 String id = UUID.randomUUID().toString(); //FIXME: The qualified role name should be unique enough to use as an ID/key
199 role.setDescription(description);
200 role.setTenantId(tenantId);
201 if (immutable == true) {
202 role.setMetadataProtection(RoleClient.IMMUTABLE);
203 role.setPermsProtection(RoleClient.IMMUTABLE);
210 * Add permission to the Spring Security tables
211 * with assumption that resource is of type URI
212 * @param permission configuration
214 public static void addPermissionsForUri(Permission perm,
215 PermissionRole permRole) throws PermissionException {
217 // First check the integrity of the incoming arguments.
219 if (!perm.getCsid().equals(permRole.getPermission().get(0).getPermissionId())) {
220 throw new IllegalArgumentException("permission ids do not"
221 + " match for role=" + permRole.getRole().get(0).getRoleName()
222 + " with permissionId=" + permRole.getPermission().get(0).getPermissionId()
223 + " for permission with csid=" + perm.getCsid());
226 List<String> principals = new ArrayList<String>();
227 for (RoleValue roleValue : permRole.getRole()) {
228 principals.add(roleValue.getRoleName());
230 List<PermissionAction> permActions = perm.getAction();
231 for (PermissionAction permAction : permActions) {
233 CSpaceAction action = URIResourceImpl.getAction(permAction.getName());
234 URIResourceImpl uriRes = new URIResourceImpl(perm.getTenantId(),
235 perm.getResourceName(), action);
236 boolean grant = perm.getEffect().equals(EffectType.PERMIT) ? true : false;
237 AuthZ.get().addPermissions(uriRes, principals.toArray(new String[0]), grant);//CSPACE-4967
238 } catch (PermissionException e) {
240 // Only throw the exception if it is *not* an already-exists exception
242 if (e.getCause() instanceof AlreadyExistsException == false) {
249 private static Connection getConnection(String databaseName) throws NamingException, SQLException {
250 return JDBCTools.getConnection(JDBCTools.CSPACE_DATASOURCE_NAME,
255 * Spring security seems to require that all of our role names start
256 * with the ROLE_PREFIX string.
258 public static String getQualifiedRoleName(String tenantId, String name) {
259 String result = name;
261 String qualifiedName = ROLE_PREFIX + tenantId.toUpperCase() + "_" + name.toUpperCase();
262 if (name.equals(qualifiedName) == false) {
263 result = qualifiedName;
269 private static ActionGroup getActionGroup(String actionGroupStr) {
270 ActionGroup result = null;
272 if (actionGroupStr.equalsIgnoreCase(ACTIONGROUP_CRUDL_NAME)) {
273 result = ACTIONGROUP_CRUDL;
274 } else if (actionGroupStr.equalsIgnoreCase(ACTIONGROUP_RL_NAME)) {
275 result = ACTIONGROUP_RL;
281 public static Permission createPermission(String tenantId,
284 String actionGroupStr) {
285 Permission result = null;
287 ActionGroup actionGroup = getActionGroup(actionGroupStr);
288 result = createPermission(tenantId, resourceName, description, actionGroup);
293 private static Permission createPermission(String tenantId,
296 ActionGroup actionGroup) {
298 + "-" + resourceName.replace('/', '_') // Remove the slashes so the ID can be used in a URI/URL
299 + "-" + actionGroup.name;
300 Permission perm = new Permission();
302 perm.setDescription(description);
303 perm.setCreatedAtItem(new Date());
304 perm.setResourceName(resourceName.toLowerCase().trim());
305 perm.setEffect(EffectType.PERMIT);
306 perm.setTenantId(tenantId);
308 perm.setActionGroup(actionGroup.name);
309 ArrayList<PermissionAction> pas = new ArrayList<PermissionAction>();
311 for (ActionType actionType : actionGroup.actions) {
312 PermissionAction permAction = createPermissionAction(perm, actionType);
319 private static Permission createWorkflowPermission(TenantBindingType tenantBinding,
320 ServiceBindingType serviceBinding,
321 String transitionVerb,
322 ActionGroup actionGroup)
324 Permission result = null;
325 String workFlowServiceSuffix;
326 String transitionName;
327 if (transitionVerb != null) {
328 transitionName = transitionVerb;
329 workFlowServiceSuffix = WorkflowClient.SERVICE_AUTHZ_SUFFIX;
331 transitionName = ""; //since the transitionDef was null, we're assuming that this is the base workflow permission to be created
332 workFlowServiceSuffix = WorkflowClient.SERVICE_PATH;
335 String tenantId = tenantBinding.getId();
336 String resourceName = "/"
337 + serviceBinding.getName().toLowerCase().trim()
338 + workFlowServiceSuffix
340 String description = "A generated workflow permission for actiongroup " + actionGroup.name;
341 result = createPermission(tenantId, resourceName, description, actionGroup);
343 if (logger.isDebugEnabled() == true) {
344 logger.debug("Generated a workflow permission: "
345 + result.getResourceName()
346 + ":" + transitionName
347 + ":" + "tenant id=" + result.getTenantId()
348 + ":" + actionGroup.name);
354 private static PermissionRole createPermissionRole(
355 Permission permission,
357 boolean enforceTenancy) throws Exception
359 PermissionRole permRole = new PermissionRole();
360 // Check to see if the tenant ID of the permission and the tenant ID of the role match
361 boolean tenantIdsMatch = role.getTenantId().equalsIgnoreCase(permission.getTenantId());
362 if (tenantIdsMatch == false && enforceTenancy == false) {
363 tenantIdsMatch = true; // If we don't need to enforce tenancy then we'll just consider them matched.
366 if (tenantIdsMatch == true) {
367 permRole.setSubject(SubjectType.ROLE);
369 // Set of the permission value list of the permrole
371 List<PermissionValue> permValues = new ArrayList<PermissionValue>();
372 PermissionValue permValue = new PermissionValue();
373 permValue.setPermissionId(permission.getCsid());
374 permValue.setResourceName(permission.getResourceName().toLowerCase());
375 permValue.setActionGroup(permission.getActionGroup());
376 permValues.add(permValue);
377 permRole.setPermission(permValues);
379 // Set of the role value list of the permrole
381 List<RoleValue> roleValues = new ArrayList<RoleValue>();
382 RoleValue rv = new RoleValue();
383 // This needs to use the qualified name, not the display name
384 rv.setRoleName(role.getRoleName());
385 rv.setRoleId(role.getCsid());
387 permRole.setRole(roleValues);
389 String errMsg = "The tenant ID of the role: " + role.getTenantId()
390 + " did not match the tenant ID of the permission: " + permission.getTenantId();
391 throw new Exception(errMsg);
397 private static Hashtable<String, String> getTenantNamesFromConfig(TenantBindingConfigReaderImpl tenantBindingConfigReader) {
399 // Note that this only handles tenants not marked as "createDisabled"
400 Hashtable<String, TenantBindingType> tenantBindings =
401 tenantBindingConfigReader.getTenantBindings();
402 Hashtable<String, String> tenantInfo = new Hashtable<String, String>();
403 for (TenantBindingType tenantBinding : tenantBindings.values()) {
404 String tId = tenantBinding.getId();
405 String tName = tenantBinding.getName();
406 tenantInfo.put(tId, tName);
407 if (logger.isDebugEnabled()) {
408 logger.debug("getTenantNamesFromConfig found configured tenant id: "+tId+" name: "+tName);
414 private static ArrayList<String> compileExistingTenants(Connection conn, Hashtable<String, String> tenantInfo)
415 throws SQLException, Exception {
416 Statement stmt = null;
417 ArrayList<String> existingTenants = new ArrayList<String>();
418 // First find or create the tenants
419 final String queryTenantSQL = "SELECT id,name FROM tenants";
421 stmt = conn.createStatement();
422 ResultSet rs = stmt.executeQuery(queryTenantSQL);
424 String tId = rs.getString("id");
425 String tName = rs.getString("name");
426 if(tenantInfo.containsKey(tId)) {
427 existingTenants.add(tId);
428 if(!tenantInfo.get(tId).equalsIgnoreCase(tName)) {
429 logger.warn("Configured name for tenant: "
430 +tId+" in repository: "+tName
431 +" does not match config'd name: "+ tenantInfo.get(tId));
436 } catch(Exception e) {
443 return existingTenants;
446 private static ArrayList<String> findOrCreateDefaultUsers(Connection conn, Hashtable<String, String> tenantInfo)
447 throws SQLException, Exception {
448 // Second find or create the users
449 Statement stmt = null;
450 PreparedStatement pstmt = null;
451 ArrayList<String> usersInRepo = new ArrayList<String>();
453 stmt = conn.createStatement();
454 ResultSet rs = stmt.executeQuery(QUERY_USERS_SQL);
456 String uName = rs.getString("username");
457 usersInRepo.add(uName);
460 pstmt = conn.prepareStatement(INSERT_USER_SQL); // create a statement
461 for(String tName : tenantInfo.values()) {
462 String adminAcctName = getDefaultAdminUserID(tName);
463 if(!usersInRepo.contains(adminAcctName)) {
464 String secEncPasswd = SecurityUtils.createPasswordHash(
465 adminAcctName, DEFAULT_ADMIN_PASSWORD);
466 pstmt.setString(1, adminAcctName); // set username param
467 pstmt.setString(2, secEncPasswd); // set passwd param
468 if (logger.isDebugEnabled()) {
469 logger.debug("createDefaultUsersAndAccounts adding user: "
470 +adminAcctName+" for tenant: "+tName);
472 pstmt.executeUpdate();
473 } else if (logger.isDebugEnabled()) {
474 logger.debug("createDefaultUsersAndAccounts: user: "+adminAcctName
475 +" already exists - skipping.");
479 String readerAcctName = getDefaultReaderUserID(tName);
480 if(!usersInRepo.contains(readerAcctName)) {
481 String secEncPasswd = SecurityUtils.createPasswordHash(
482 readerAcctName, DEFAULT_READER_PASSWORD);
483 pstmt.setString(1, readerAcctName); // set username param
484 pstmt.setString(2, secEncPasswd); // set passwd param
485 if (logger.isDebugEnabled()) {
486 logger.debug("createDefaultUsersAndAccounts adding user: "
487 +readerAcctName+" for tenant: "+tName);
489 pstmt.executeUpdate();
490 } else if (logger.isDebugEnabled()) {
491 logger.debug("createDefaultUsersAndAccounts: user: "+readerAcctName
492 +" already exists - skipping.");
496 } catch(Exception e) {
507 private static void findOrCreateDefaultAccounts(Connection conn, Hashtable<String, String> tenantInfo,
508 ArrayList<String> usersInRepo,
509 Hashtable<String, String> tenantAdminAcctCSIDs, Hashtable<String, String> tenantReaderAcctCSIDs)
510 throws SQLException, Exception {
511 // Third, create the accounts. Assume that if the users were already there,
512 // then the accounts were as well
513 PreparedStatement pstmt = null;
515 pstmt = conn.prepareStatement(INSERT_ACCOUNT_SQL); // create a statement
516 for(String tId : tenantInfo.keySet()) {
517 String tName = tenantInfo.get(tId);
518 String adminCSID = UUID.randomUUID().toString();
519 tenantAdminAcctCSIDs.put(tId, adminCSID);
520 String adminAcctName = getDefaultAdminUserID(tName);
521 if(!usersInRepo.contains(adminAcctName)) {
522 pstmt.setString(1, adminCSID); // set csid param
523 pstmt.setString(2, adminAcctName); // set email param (bogus)
524 pstmt.setString(3, adminAcctName); // set userid param
525 pstmt.setString(4, "Administrator");// set screen name param
526 if (logger.isDebugEnabled()) {
527 logger.debug("createDefaultAccounts adding account: "
528 +adminAcctName+" for tenant: "+tName);
530 pstmt.executeUpdate();
531 } else if (logger.isDebugEnabled()) {
532 logger.debug("createDefaultAccounts: user: "+adminAcctName
533 +" already exists - skipping account generation.");
536 String readerCSID = UUID.randomUUID().toString();
537 tenantReaderAcctCSIDs.put(tId, readerCSID);
538 String readerAcctName = getDefaultReaderUserID(tName);
539 if(!usersInRepo.contains(readerAcctName)) {
540 pstmt.setString(1, readerCSID); // set csid param
541 pstmt.setString(2, readerAcctName); // set email param (bogus)
542 pstmt.setString(3, readerAcctName); // set userid param
543 pstmt.setString(4, "Reader"); // set screen name param
544 if (logger.isDebugEnabled()) {
545 logger.debug("createDefaultAccounts adding account: "
546 +readerAcctName+" for tenant: "+tName);
548 pstmt.executeUpdate();
549 } else if (logger.isDebugEnabled()) {
550 logger.debug("createDefaultAccounts: user: "+readerAcctName
551 +" already exists - skipping account creation.");
555 } catch(Exception e) {
563 private static boolean findOrCreateTenantManagerUserAndAccount(Connection conn)
564 throws SQLException, Exception {
565 // Find or create the special tenant manager account.
566 // Later can make the user name for tenant manager be configurable, settable.
567 Statement stmt = null;
568 PreparedStatement pstmt = null;
569 boolean created = false;
571 boolean foundTMgrUser = false;
572 stmt = conn.createStatement();
573 ResultSet rs = stmt.executeQuery(QUERY_TENANT_MGR_USER_SQL);
574 // Should only find one - only consider it
576 String uName = rs.getString("username");
577 foundTMgrUser = uName.equals(TENANT_MANAGER_USER);
581 pstmt = conn.prepareStatement(INSERT_USER_SQL); // create a statement
582 String secEncPasswd = SecurityUtils.createPasswordHash(
583 TENANT_MANAGER_USER, DEFAULT_TENANT_MANAGER_PASSWORD);
584 pstmt.setString(1, TENANT_MANAGER_USER); // set username param
585 pstmt.setString(2, secEncPasswd); // set passwd param
586 if (logger.isDebugEnabled()) {
587 logger.debug("findOrCreateTenantManagerUserAndAccount adding tenant manager user: "
588 +TENANT_MANAGER_USER);
590 pstmt.executeUpdate();
592 // Now create the account to match
593 pstmt = conn.prepareStatement(INSERT_ACCOUNT_SQL); // create a statement
594 pstmt.setString(1, AuthN.TENANT_MANAGER_ACCT_ID); // set csid param
595 pstmt.setString(2, DEFAULT_TENANT_MANAGER_EMAIL); // set email param (bogus)
596 pstmt.setString(3, TENANT_MANAGER_USER); // set userid param
597 pstmt.setString(4, TENANT_MANAGER_SCREEN_NAME);// set screen name param
598 if (logger.isDebugEnabled()) {
599 logger.debug("findOrCreateTenantManagerUserAndAccount adding tenant manager account: "
600 +TENANT_MANAGER_USER);
602 pstmt.executeUpdate();
605 } else if (logger.isDebugEnabled()) {
606 logger.debug("findOrCreateTenantManagerUserAndAccount: tenant manager: "+TENANT_MANAGER_USER
607 +" already exists.");
609 } catch(Exception e) {
620 private static void bindDefaultAccountsToTenants(Connection conn, DatabaseProductType databaseProductType,
621 Hashtable<String, String> tenantInfo, ArrayList<String> usersInRepo,
622 Hashtable<String, String> tenantAdminAcctCSIDs, Hashtable<String, String> tenantReaderAcctCSIDs)
623 throws SQLException, Exception {
624 // Fourth, bind accounts to tenants. Assume that if the users were already there,
625 // then the accounts were bound to tenants correctly
626 PreparedStatement pstmt = null;
628 String insertAccountTenantSQL;
629 if (databaseProductType == DatabaseProductType.MYSQL) {
630 insertAccountTenantSQL =
631 "INSERT INTO accounts_tenants (TENANTS_ACCOUNTS_COMMON_CSID,tenant_id) "
633 } else if (databaseProductType == DatabaseProductType.POSTGRESQL) {
634 insertAccountTenantSQL =
635 "INSERT INTO accounts_tenants (HJID, TENANTS_ACCOUNTS_COMMON_CSID,tenant_id) "
636 + " VALUES(nextval('hibernate_sequence'), ?, ?)";
638 throw new Exception("Unrecognized database system.");
640 pstmt = conn.prepareStatement(insertAccountTenantSQL); // create a statement
641 for(String tId : tenantInfo.keySet()) {
642 String tName = tenantInfo.get(tId);
643 if(!usersInRepo.contains(getDefaultAdminUserID(tName))) {
644 String adminAcct = tenantAdminAcctCSIDs.get(tId);
645 pstmt.setString(1, adminAcct); // set acct CSID param
646 pstmt.setString(2, tId); // set tenant_id param
647 if (logger.isDebugEnabled()) {
648 logger.debug("createDefaultAccounts binding account id: "
649 +adminAcct+" to tenant id: "+tId);
651 pstmt.executeUpdate();
653 if(!usersInRepo.contains(getDefaultReaderUserID(tName))) {
654 String readerAcct = tenantReaderAcctCSIDs.get(tId);
655 pstmt.setString(1, readerAcct); // set acct CSID param
656 pstmt.setString(2, tId); // set tenant_id param
657 if (logger.isDebugEnabled()) {
658 logger.debug("createDefaultAccounts binding account id: "
659 +readerAcct+" to tenant id: "+tId);
661 pstmt.executeUpdate();
665 } catch(Exception e) {
674 private static String findOrCreateDefaultRoles(Connection conn, Hashtable<String, String> tenantInfo,
675 Hashtable<String, String> tenantAdminRoleCSIDs, Hashtable<String, String> tenantReaderRoleCSIDs)
676 throws SQLException, Exception {
677 // Fifth, fetch and save the default roles
678 String springAdminRoleCSID = null;
679 Statement stmt = null;
680 PreparedStatement pstmt = null;
682 final String querySpringRole =
683 "SELECT csid from roles WHERE rolename='"+AuthN.ROLE_SPRING_ADMIN_NAME+"'";
684 stmt = conn.createStatement();
685 ResultSet rs = stmt.executeQuery(querySpringRole);
687 springAdminRoleCSID = rs.getString(1);
688 if (logger.isDebugEnabled()) {
689 logger.debug("createDefaultAccounts found Spring Admin role: "
690 +springAdminRoleCSID);
693 final String insertSpringAdminRoleSQL =
694 "INSERT INTO roles (csid, rolename, displayName, rolegroup, created_at, tenant_id) "
695 + "VALUES ('-1', 'ROLE_SPRING_ADMIN', 'SPRING_ADMIN', 'Spring Security Administrator', now(), '0')";
696 stmt.executeUpdate(insertSpringAdminRoleSQL);
697 springAdminRoleCSID = "-1";
698 if (logger.isDebugEnabled()) {
699 logger.debug("createDefaultAccounts CREATED Spring Admin role: "
700 +springAdminRoleCSID);
704 final String getRoleCSIDSql =
705 "SELECT csid from roles WHERE tenant_id=? and rolename=?";
706 pstmt = conn.prepareStatement(getRoleCSIDSql); // create a statement
708 for(String tId : tenantInfo.keySet()) {
709 pstmt.setString(1, tId); // set tenant_id param
710 pstmt.setString(2, getDefaultAdminRole(tId)); // set rolename param
711 rs = pstmt.executeQuery();
712 // extract data from the ResultSet
714 throw new RuntimeException("Cannot find role: "+getDefaultAdminRole(tId)
715 +" for tenant id: "+tId+" in roles!");
717 String tenantAdminRoleCSID = rs.getString(1);
718 if (logger.isDebugEnabled()) {
719 logger.debug("createDefaultAccounts found role: "
720 +getDefaultAdminRole(tId)+"("+tenantAdminRoleCSID
721 +") for tenant id: "+tId);
723 tenantAdminRoleCSIDs.put(tId, tenantAdminRoleCSID);
724 pstmt.setString(1, tId); // set tenant_id param
725 pstmt.setString(2, getDefaultReaderRole(tId)); // set rolename param
727 rs = pstmt.executeQuery();
728 // extract data from the ResultSet
730 throw new RuntimeException("Cannot find role: "+getDefaultReaderRole(tId)
731 +" for tenant id: "+tId+" in roles!");
733 String tenantReaderRoleCSID = rs.getString(1);
734 if (logger.isDebugEnabled()) {
735 logger.debug("createDefaultAccounts found role: "
736 +getDefaultReaderRole(tId)+"("+tenantReaderRoleCSID
737 +") for tenant id: "+tId);
739 tenantReaderRoleCSIDs.put(tId, tenantReaderRoleCSID);
743 } catch(Exception e) {
751 return springAdminRoleCSID;
754 private static String findTenantManagerRole(Connection conn )
755 throws SQLException, RuntimeException, Exception {
756 String tenantMgrRoleCSID = null;
757 PreparedStatement pstmt = null;
759 String rolename = getQualifiedRoleName(AuthN.ALL_TENANTS_MANAGER_TENANT_ID,
760 AuthN.ROLE_ALL_TENANTS_MANAGER);
761 pstmt = conn.prepareStatement(GET_TENANT_MGR_ROLE_SQL); // create a statement
763 pstmt.setString(1, rolename); // set rolename param
764 rs = pstmt.executeQuery();
766 tenantMgrRoleCSID = rs.getString(1);
767 if (logger.isDebugEnabled()) {
768 logger.debug("findTenantManagerRole found Tenant Mgr role: "
773 } catch(Exception e) {
779 if(tenantMgrRoleCSID==null)
780 throw new RuntimeException("findTenantManagerRole: Cound not find tenant Manager Role!");
781 return tenantMgrRoleCSID;
784 private static void bindAccountsToRoles(Connection conn, DatabaseProductType databaseProductType,
785 Hashtable<String, String> tenantInfo, ArrayList<String> usersInRepo,
786 String springAdminRoleCSID,
787 Hashtable<String, String> tenantAdminRoleCSIDs, Hashtable<String, String> tenantReaderRoleCSIDs,
788 Hashtable<String, String> tenantAdminAcctCSIDs, Hashtable<String, String> tenantReaderAcctCSIDs)
789 throws SQLException, Exception {
790 // Sixth, bind the accounts to roles. If the users already existed,
791 // we'll assume they were set up correctly.
792 PreparedStatement pstmt = null;
794 String insertAccountRoleSQL;
795 if (databaseProductType == DatabaseProductType.MYSQL) {
796 insertAccountRoleSQL = INSERT_ACCOUNT_ROLE_SQL_MYSQL;
797 } else if (databaseProductType == DatabaseProductType.POSTGRESQL) {
798 insertAccountRoleSQL = INSERT_ACCOUNT_ROLE_SQL_POSTGRES;
800 throw new Exception("Unrecognized database system.");
802 if (logger.isDebugEnabled()) {
803 logger.debug("createDefaultAccounts binding accounts to roles with SQL:\n"
804 +insertAccountRoleSQL);
806 pstmt = conn.prepareStatement(insertAccountRoleSQL); // create a statement
807 for(String tId : tenantInfo.keySet()) {
808 String adminUserId = getDefaultAdminUserID(tenantInfo.get(tId));
809 if(!usersInRepo.contains(adminUserId)) {
810 String adminAcct = tenantAdminAcctCSIDs.get(tId);
811 String adminRoleId = tenantAdminRoleCSIDs.get(tId);
812 pstmt.setString(1, adminAcct); // set acct CSID param
813 pstmt.setString(2, adminUserId); // set user_id param
814 pstmt.setString(3, adminRoleId); // set role_id param
815 pstmt.setString(4, getDefaultAdminRole(tId)); // set rolename param
816 if (logger.isDebugEnabled()) {
817 logger.debug("createDefaultAccounts binding account: "
818 +adminUserId+" to Admin role("+adminRoleId
819 +") for tenant id: "+tId);
821 pstmt.executeUpdate();
822 // Now add the Spring Admin Role to the admin accounts
823 pstmt.setString(3, springAdminRoleCSID); // set role_id param
824 pstmt.setString(4, AuthN.ROLE_SPRING_ADMIN_NAME); // set rolename param
825 if (logger.isDebugEnabled()) {
826 logger.debug("createDefaultAccounts binding account: "
827 +adminUserId+" to Spring Admin role: "+springAdminRoleCSID);
829 pstmt.executeUpdate();
831 String readerUserId = getDefaultReaderUserID(tenantInfo.get(tId));
832 if(!usersInRepo.contains(readerUserId)) {
833 String readerAcct = tenantReaderAcctCSIDs.get(tId);
834 String readerRoleId = tenantReaderRoleCSIDs.get(tId);
835 pstmt.setString(1, readerAcct); // set acct CSID param
836 pstmt.setString(2, readerUserId); // set user_id param
837 pstmt.setString(3, readerRoleId); // set role_id param
838 pstmt.setString(4, getDefaultReaderRole(tId)); // set rolename param
839 if (logger.isDebugEnabled()) {
840 logger.debug("createDefaultAccounts binding account: "
841 +readerUserId+" to Reader role("+readerRoleId
842 +") for tenant id: "+tId);
844 pstmt.executeUpdate();
848 } catch(Exception e) {
856 private static void bindTenantManagerAccountRole(Connection conn, DatabaseProductType databaseProductType,
857 String tenantManagerUserID, String tenantManagerAccountID, String tenantManagerRoleID, String tenantManagerRoleName )
858 throws SQLException, Exception {
859 PreparedStatement pstmt = null;
861 String insertAccountRoleSQL;
862 if (databaseProductType == DatabaseProductType.MYSQL) {
863 insertAccountRoleSQL = INSERT_ACCOUNT_ROLE_SQL_MYSQL;
864 } else if (databaseProductType == DatabaseProductType.POSTGRESQL) {
865 insertAccountRoleSQL = INSERT_ACCOUNT_ROLE_SQL_POSTGRES;
867 throw new Exception("Unrecognized database system.");
869 if (logger.isDebugEnabled()) {
870 logger.debug("bindTenantManagerAccountRole binding account to role with SQL:\n"
871 +insertAccountRoleSQL);
873 pstmt = conn.prepareStatement(insertAccountRoleSQL); // create a statement
874 pstmt.setString(1, tenantManagerAccountID); // set acct CSID param
875 pstmt.setString(2, tenantManagerUserID); // set user_id param
876 pstmt.setString(3, tenantManagerRoleID); // set role_id param
877 pstmt.setString(4, tenantManagerRoleName); // set rolename param
878 if (logger.isDebugEnabled()) {
879 logger.debug("bindTenantManagerAccountRole binding user: "
880 +tenantManagerUserID+" to Admin role("+tenantManagerRoleName+")");
882 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();
893 } catch(Exception e) {
902 * Using the tenant bindings, ensure there are corresponding Tenant records (db columns).
904 //FIXME: This code should be using JPA objects and JPATransactionContext, not raw SQL.
905 public static void createTenants(
906 TenantBindingConfigReaderImpl tenantBindingConfigReader,
907 DatabaseProductType databaseProductType,
908 String cspaceDatabaseName) throws Exception {
909 logger.debug("ServiceMain.createTenants starting...");
910 Hashtable<String, String> tenantInfo = getTenantNamesFromConfig(tenantBindingConfigReader);
911 Connection conn = null;
913 conn = getConnection(cspaceDatabaseName);
914 ArrayList<String> existingTenants = compileExistingTenants(conn, tenantInfo);
916 // Note that this only creates tenants not marked as "createDisabled"
917 createMissingTenants(conn, tenantInfo, existingTenants);
918 } catch (Exception e) {
919 logger.debug("Exception in createTenants: " + e.getLocalizedMessage());
925 } catch (SQLException sqle) {
926 if (logger.isDebugEnabled()) {
927 logger.debug("SQL Exception closing statement/connection: " + sqle.getLocalizedMessage());
935 * @param tenantBindingConfigReader
936 * @param databaseProductType
937 * @param cspaceDatabaseName
940 //FIXME: This code should be using the JPA objects and JPATransactionContext, not raw SQL.
941 public static void createDefaultAccounts(
942 TenantBindingConfigReaderImpl tenantBindingConfigReader,
943 DatabaseProductType databaseProductType,
944 String cspaceDatabaseName) throws Exception {
946 logger.debug("ServiceMain.createDefaultAccounts starting...");
948 Hashtable<String, String> tenantInfo = getTenantNamesFromConfig(tenantBindingConfigReader);
949 Connection conn = null;
950 // TODO - need to put in tests for existence first.
951 // We could just look for the accounts per tenant up front, and assume that
952 // the rest is there if the accounts are.
953 // Could add a sql script to remove these if need be - Spring only does roles,
954 // and we're not touching that, so we could safely toss the
955 // accounts, users, account-tenants, account-roles, and start over.
957 conn = getConnection(cspaceDatabaseName);
959 ArrayList<String> usersInRepo = findOrCreateDefaultUsers(conn, tenantInfo);
961 Hashtable<String, String> tenantAdminAcctCSIDs = new Hashtable<String, String>();
962 Hashtable<String, String> tenantReaderAcctCSIDs = new Hashtable<String, String>();
963 findOrCreateDefaultAccounts(conn, tenantInfo, usersInRepo,
964 tenantAdminAcctCSIDs, tenantReaderAcctCSIDs);
966 bindDefaultAccountsToTenants(conn, databaseProductType, tenantInfo, usersInRepo,
967 tenantAdminAcctCSIDs, tenantReaderAcctCSIDs);
969 Hashtable<String, String> tenantAdminRoleCSIDs = new Hashtable<String, String>();
970 Hashtable<String, String> tenantReaderRoleCSIDs = new Hashtable<String, String>();
971 String springAdminRoleCSID = findOrCreateDefaultRoles(conn, tenantInfo,
972 tenantAdminRoleCSIDs, tenantReaderRoleCSIDs);
974 bindAccountsToRoles(conn, databaseProductType,
975 tenantInfo, usersInRepo, springAdminRoleCSID,
976 tenantAdminRoleCSIDs, tenantReaderRoleCSIDs,
977 tenantAdminAcctCSIDs, tenantReaderAcctCSIDs);
979 boolean createdTenantMgrAccount = findOrCreateTenantManagerUserAndAccount(conn);
980 if(createdTenantMgrAccount) {
981 // If we created the account, we need to create the bindings. Otherwise, assume they
982 // are all set (from previous initialization).
983 String tenantManagerRoleCSID = findTenantManagerRole(conn);
984 bindTenantManagerAccountRole(conn, databaseProductType,
985 TENANT_MANAGER_USER, AuthN.TENANT_MANAGER_ACCT_ID,
986 tenantManagerRoleCSID, AuthN.ROLE_ALL_TENANTS_MANAGER);
988 } catch (Exception e) {
989 logger.debug("Exception in createDefaultAccounts: " + e.getLocalizedMessage());
995 } catch (SQLException sqle) {
996 if (logger.isDebugEnabled()) {
997 logger.debug("SQL Exception closing statement/connection: " + sqle.getLocalizedMessage());
1003 private static String getDefaultAdminRole(String tenantId) {
1004 return ROLE_PREFIX+tenantId+TENANT_ADMIN_ROLE_SUFFIX;
1007 private static String getDefaultReaderRole(String tenantId) {
1008 return ROLE_PREFIX+tenantId+TENANT_READER_ROLE_SUFFIX;
1011 private static String getDefaultAdminUserID(String tenantName) {
1012 return TENANT_ADMIN_ACCT_PREFIX+tenantName;
1015 private static String getDefaultReaderUserID(String tenantName) {
1016 return TENANT_READER_ACCT_PREFIX+tenantName;
1019 static public PermissionAction createPermissionAction(Permission perm,
1020 ActionType actionType) {
1021 PermissionAction pa = new PermissionAction();
1023 CSpaceAction action = URIResourceImpl.getAction(actionType);
1024 URIResourceImpl uriRes = new URIResourceImpl(perm.getTenantId(),
1025 perm.getResourceName(), action);
1026 pa.setName(actionType);
1027 pa.setObjectIdentity(uriRes.getHashedId().toString());
1028 pa.setObjectIdentityResource(uriRes.getId());
1033 static public PermissionAction update(Permission perm, PermissionAction permAction) {
1034 PermissionAction pa = new PermissionAction();
1036 CSpaceAction action = URIResourceImpl.getAction(permAction.getName());
1037 URIResourceImpl uriRes = new URIResourceImpl(perm.getTenantId(),
1038 perm.getResourceName(), action);
1039 pa.setObjectIdentity(uriRes.getHashedId().toString());
1040 pa.setObjectIdentityResource(uriRes.getId());
1045 private static HashSet<String> getTransitionVerbList(TenantBindingType tenantBinding, ServiceBindingType serviceBinding) {
1046 HashSet<String> result = new HashSet<String>();
1048 TransitionDefList transitionDefList = getTransitionDefList(tenantBinding, serviceBinding);
1049 for (TransitionDef transitionDef : transitionDefList.getTransitionDef()) {
1050 String transitionVerb = transitionDef.getName();
1051 String[] tokens = transitionVerb.split("_"); // Split the verb into words. The workflow verbs are compound words combined with the '_' character.
1052 result.add(tokens[0]); // We only care about the first word.
1058 private static TransitionDefList getTransitionDefList(TenantBindingType tenantBinding, ServiceBindingType serviceBinding) {
1059 TransitionDefList result = null;
1061 String serviceObjectName = serviceBinding.getObject().getName();
1062 DocumentHandler docHandler = ServiceConfigUtils.createDocumentHandlerInstance(
1063 tenantBinding, serviceBinding);
1064 Lifecycle lifecycle = docHandler.getLifecycle(serviceObjectName);
1065 if (lifecycle != null) {
1066 result = lifecycle.getTransitionDefList();
1068 } catch (Exception e) {
1069 // Ignore this exception and return an empty non-null TransitionDefList
1072 if (result == null) {
1073 if (serviceBinding.getType().equalsIgnoreCase(ServiceBindingUtils.SERVICE_TYPE_SECURITY) == false) {
1074 logger.debug("Could not retrieve a lifecycle transition definition list from: "
1075 + serviceBinding.getName()
1076 + " with tenant ID = "
1077 + tenantBinding.getId());
1079 // return an empty list
1080 result = new TransitionDefList();
1082 logger.debug("Successfully retrieved a lifecycle transition definition list from: "
1083 + serviceBinding.getName()
1084 + " with tenant ID = "
1085 + tenantBinding.getId());
1093 * @param tenantBindingConfigReader
1094 * @param databaseProductType
1095 * @param cspaceDatabaseName
1098 public static void createDefaultWorkflowPermissions(
1099 JPATransactionContext jpaTransactionContext,
1100 TenantBindingConfigReaderImpl tenantBindingConfigReader,
1101 DatabaseProductType databaseProductType,
1102 String cspaceDatabaseName) throws Exception //FIXME: REM - 4/11/2012 - Rename to createWorkflowPermissions
1104 java.util.logging.Logger logger = java.util.logging.Logger.getAnonymousLogger();
1106 AuthZ.get().login(); //login to Spring Security manager
1109 Hashtable<String, TenantBindingType> tenantBindings = tenantBindingConfigReader.getTenantBindings();
1110 for (String tenantId : tenantBindings.keySet()) {
1111 logger.info(String.format("Creating/verifying workflow permissions for tenant ID=%s.", tenantId));
1112 TenantBindingType tenantBinding = tenantBindings.get(tenantId);
1113 if (tenantBinding.isConfigChangedSinceLastStart() == false) {
1114 continue; // skip the rest of the loop and go to the next tenant
1117 Role adminRole = AuthorizationCommon.getRole(jpaTransactionContext, tenantBinding.getId(), ROLE_TENANT_ADMINISTRATOR);
1118 Role readonlyRole = AuthorizationCommon.getRole(jpaTransactionContext, tenantBinding.getId(), ROLE_TENANT_READER);
1120 if (adminRole == null || readonlyRole == null) {
1121 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.");
1123 throw new RuntimeException("One or more of the required default CollectionSpace administrator roles is missing or was never created.");
1126 for (ServiceBindingType serviceBinding : tenantBinding.getServiceBindings()) {
1127 String prop = ServiceBindingUtils.getPropertyValue(serviceBinding, REFRESH_AUTHZ_PROP);
1128 if (prop == null ? true : Boolean.parseBoolean(prop)) {
1130 jpaTransactionContext.beginTransaction();
1131 TransitionDefList transitionDefList = getTransitionDefList(tenantBinding, serviceBinding);
1132 HashSet<String> transitionVerbList = getTransitionVerbList(tenantBinding, serviceBinding);
1133 for (String transitionVerb : transitionVerbList) {
1135 // Create the permission for the admin role
1136 Permission adminPerm = createWorkflowPermission(tenantBinding, serviceBinding, transitionVerb, ACTIONGROUP_CRUDL);
1137 persist(jpaTransactionContext, adminPerm, adminRole, true, ACTIONGROUP_CRUDL);
1139 // Create the permission for the read-only role
1140 Permission readonlyPerm = createWorkflowPermission(tenantBinding, serviceBinding, transitionVerb, ACTIONGROUP_RL);
1141 persist(jpaTransactionContext, readonlyPerm, readonlyRole, true, ACTIONGROUP_RL); // Persist/store the permission and permrole records and related Spring Security info
1143 jpaTransactionContext.commitTransaction();
1144 } catch (IllegalStateException e) {
1145 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.
1146 } catch (Exception x) {
1147 jpaTransactionContext.markForRollback();
1150 logger.warning("AuthZ refresh service binding property is set to FALSE so default permissions will NOT be refreshed for: "
1151 + serviceBinding.getName());
1155 } catch (Exception e) {
1156 jpaTransactionContext.markForRollback();
1157 logger.fine("Caught exception and rolling back permission creation: " + e.getMessage());
1162 private static void createMissingTenants(Connection conn, Hashtable<String, String> tenantInfo,
1163 ArrayList<String> existingTenants) throws SQLException, Exception {
1164 // Need to define and look for a createDisabled attribute in tenant config
1165 final String insertTenantSQL =
1166 "INSERT INTO tenants (id,name,authorities_initialized,disabled,created_at) VALUES (?,?,FALSE,FALSE,now())";
1167 PreparedStatement pstmt = null;
1169 pstmt = conn.prepareStatement(insertTenantSQL); // create a statement
1170 for(String tId : tenantInfo.keySet()) {
1171 if(existingTenants.contains(tId)) {
1172 if (logger.isDebugEnabled()) {
1173 logger.debug("createMissingTenants: tenant exists (skipping): "
1174 +tenantInfo.get(tId));
1178 pstmt.setString(1, tId); // set id param
1179 pstmt.setString(2, tenantInfo.get(tId)); // set name param
1180 if (logger.isDebugEnabled()) {
1181 logger.debug("createMissingTenants adding entry for tenant: "+tId);
1183 pstmt.executeUpdate();
1186 } catch(Exception e) {
1194 public static String getPersistedMD5Hash(String tenantId, String cspaceDatabaseName) throws Exception {
1195 String result = null;
1197 ArrayList<String> existingTenants = new ArrayList<String>();
1198 // First find or create the tenants
1199 final String queryTenantSQL = String.format("SELECT id, name, config_md5hash FROM tenants WHERE id = '%s'", tenantId);
1201 Statement stmt = null;
1205 conn = getConnection(cspaceDatabaseName);
1206 stmt = conn.createStatement();
1207 ResultSet rs = stmt.executeQuery(queryTenantSQL);
1210 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'.",
1211 tenantId, cspaceDatabaseName);
1212 throw new Exception(errMsg);
1214 String tId = rs.getString("id"); // for debugging only
1215 String tName = rs.getString("name"); // for debugging only
1216 result = rs.getString("config_md5hash");
1220 } catch(Exception e) {
1223 if (stmt != null) stmt.close();
1229 private static PermissionRoleRel findPermRoleRel(
1230 JPATransactionContext jpaTransactionContext,
1231 String permissionId,
1233 PermissionRoleRel result = null;
1236 String whereClause = "where permissionId = :id and roleId = :roleId";
1237 HashMap<String, Object> params = new HashMap<String, Object>();
1238 params.put("id", permissionId);
1239 params.put("roleId", RoleId);
1241 result = (PermissionRoleRel) JpaStorageUtils.getEntity(jpaTransactionContext,
1242 PermissionRoleRel.class.getCanonicalName(), whereClause, params);
1243 } catch (Exception e) {
1244 //Do nothing. Will return null;
1251 * Persists the Permission, PermissionRoleRel, and Spring Security table entries all in one transaction
1253 private static void persist(JPATransactionContext jpaTransactionContext, Permission permission, Role role, boolean enforceTenancy, ActionGroup actionGroup) throws Exception {
1254 AuthorizationStore authzStore = new AuthorizationStore();
1255 // First persist the Permission record
1256 authzStore.store(jpaTransactionContext, permission);
1258 // If the PermRoleRel doesn't already exists then relate the permission and the role in a new PermissionRole (the service payload)
1259 // Create a PermissionRoleRel (the database relation table for the permission and role)
1260 PermissionRoleRel permRoleRel = findPermRoleRel(jpaTransactionContext, permission.getCsid(), role.getCsid());
1261 if (permRoleRel == null) {
1262 PermissionRole permRole = createPermissionRole(permission, role, enforceTenancy);
1263 List<PermissionRoleRel> permRoleRels = new ArrayList<PermissionRoleRel>();
1264 PermissionRoleUtil.buildPermissionRoleRel(jpaTransactionContext, permRole, SubjectType.ROLE, permRoleRels,
1265 false /*not for delete*/, role.getTenantId());
1266 for (PermissionRoleRel prr : permRoleRels) {
1267 authzStore.store(jpaTransactionContext, prr);
1269 Profiler profiler = new Profiler(AuthorizationCommon.class, 2);
1271 // Add a corresponding entry in the Spring Security Tables
1272 addPermissionsForUri(permission, permRole);
1274 logger.debug("Finished full perm generation for "
1275 + ":" + permission.getTenantId()
1276 + ":" + permission.getResourceName()
1277 + ":" + actionGroup.getName()
1278 + ":" + profiler.getCumulativeTime());
1283 public static boolean hasTokenExpired(EmailConfig emailConfig, Token token) throws NoSuchAlgorithmException {
1284 boolean result = false;
1286 int maxConfigSeconds = emailConfig.getPasswordResetConfig().getTokenExpirationSeconds().intValue();
1287 int maxTokenSeconds = token.getExpireSeconds().intValue();
1289 long createdTime = token.getCreatedAtItem().getTime();
1290 long configExpirationTime = createdTime + maxConfigSeconds * 1000; // the current tenant config for how long a token stays valid
1291 long tokenDefinedExirationTime = createdTime + maxTokenSeconds * 1000; // the tenant config for how long a token stays valid when the token was created.
1293 if (configExpirationTime != tokenDefinedExirationTime) {
1294 String msg = String.format("The configured expiration time for the token = '%s' changed from when the token was created.",
1299 // Note: the current tenant bindings config for expiration takes precedence over the config used to create the token.
1301 if (System.currentTimeMillis() >= configExpirationTime) {
1309 * Validate that the password reset configuration is correct.
1311 private static String validatePasswordResetConfig(PasswordResetConfig passwordResetConfig) {
1312 String result = null;
1314 if (passwordResetConfig != null) {
1315 result = passwordResetConfig.getMessage();
1316 if (result == null || result.length() == 0) {
1317 result = DEFAULT_PASSWORD_RESET_EMAIL_MESSAGE;
1318 logger.warn("Could not find a password reset message in the tenant's configuration. Using the default one");
1321 if (result.contains("{{link}}") == false) {
1322 logger.warn("The tenant's password reset message does not contain a required '{{link}}' marker.");
1326 if (passwordResetConfig.getLoginpage() == null || passwordResetConfig.getLoginpage().trim().isEmpty()) {
1327 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'.");
1331 String subject = passwordResetConfig.getSubject();
1332 if (subject == null || subject.trim().isEmpty()) {
1333 passwordResetConfig.setSubject(DEFAULT_PASSWORD_RESET_EMAIL_SUBJECT);
1342 * Generate a password reset message. Embeds an authorization token to reset a user's password.
1344 public static String generatePasswordResetEmailMessage(EmailConfig emailConfig, AccountListItem accountListItem, Token token) throws Exception {
1345 String result = null;
1347 result = validatePasswordResetConfig(emailConfig.getPasswordResetConfig());
1348 if (result == null) {
1349 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.",
1350 token.getTenantId(), accountListItem.getEmail());
1351 throw new Exception(errMsg);
1354 String link = emailConfig.getBaseurl() + emailConfig.getPasswordResetConfig().getLoginpage() + "?token=" + token.getId();
1355 result = result.replaceAll("\\{\\{link\\}\\}", link);
1357 if (result.contains("{{greeting}}")) {
1358 String greeting = accountListItem.getScreenName();
1359 result = result.replaceAll("\\{\\{greeting\\}\\}", greeting);
1360 result = result.replaceAll("\\\\n", "\\\n");
1361 result = result.replaceAll("\\\\r", "\\\r");
1367 public static void persistTenantBindingsMD5Hash(TenantBindingConfigReaderImpl tenantBindingConfigReader,
1368 DatabaseProductType databaseProductType, String cspaceDatabaseName) throws Exception {
1369 // Need to define and look for a createDisabled attribute in tenant config
1370 String updateTableSQL = "UPDATE tenants SET config_md5hash = ? WHERE id = ?";
1373 PreparedStatement pstmt = null;
1375 conn = getConnection(cspaceDatabaseName);
1376 pstmt = conn.prepareStatement(updateTableSQL); // create a statement
1377 for (String tId : AuthorizationCommon.tenantConfigMD5HashTable.keySet()) {
1378 pstmt.setString(1, AuthorizationCommon.getTenantConfigMD5Hash(tId));
1379 pstmt.setString(2, tId);
1380 if (logger.isDebugEnabled()) {
1381 logger.debug("createMissingTenants adding entry for tenant: " + tId);
1383 pstmt.executeUpdate();
1386 } catch(Exception e) {
1389 if (pstmt!=null) pstmt.close();