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.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;
66 import org.springframework.security.acls.model.AlreadyExistsException;
69 public class AuthorizationCommon {
71 final public static String REFRESH_AUTZ_PROP = "refreshAuthZOnStartup";
74 // For token generation and password reset
76 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.";
77 final private static String tokensalt = "74102328UserDetailsReset";
78 final private static int TIME_SCALAR = 100000;
79 private static final String DEFAULT_PASSWORD_RESET_EMAIL_SUBJECT = "Password reset for CollectionSpace account";
82 // Keep track of the MD5 hash value for the tenant bindings
84 private static final Map<String, String> tenantConfigMD5HashTable = new HashMap<String, String>();
87 // ActionGroup labels/constants
91 final public static String ACTIONGROUP_CRUDL_NAME = "CRUDL";
92 final public static ActionType[] ACTIONSET_CRUDL = {ActionType.CREATE, ActionType.READ, ActionType.UPDATE, ActionType.DELETE, 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_RL;
100 // A static block to initialize the predefined action groups
103 ACTIONGROUP_CRUDL = new ActionGroup();
104 ACTIONGROUP_CRUDL.name = ACTIONGROUP_CRUDL_NAME;
105 ACTIONGROUP_CRUDL.actions = ACTIONSET_CRUDL;
107 ACTIONGROUP_RL = new ActionGroup();
108 ACTIONGROUP_RL.name = ACTIONGROUP_RL_NAME;
109 ACTIONGROUP_RL.actions = ACTIONSET_RL;
113 final static Logger logger = LoggerFactory.getLogger(AuthorizationCommon.class);
115 final public static String ROLE_TENANT_ADMINISTRATOR = "TENANT_ADMINISTRATOR";
116 final public static String ROLE_TENANT_READER = "TENANT_READER";
118 public static final String TENANT_MANAGER_USER = "tenantManager";
119 public static final String TENANT_MANAGER_SCREEN_NAME = TENANT_MANAGER_USER;
120 public static final String DEFAULT_TENANT_MANAGER_PASSWORD = "manage";
121 public static final String DEFAULT_TENANT_MANAGER_EMAIL = "tenantManager@collectionspace.org";
123 public static final String TENANT_ADMIN_ACCT_PREFIX = "admin@";
124 public static final String TENANT_READER_ACCT_PREFIX = "reader@";
125 public static final String ROLE_PREFIX = "ROLE_";
126 public static final String TENANT_ADMIN_ROLE_SUFFIX = "_TENANT_ADMINISTRATOR";
127 public static final String TENANT_READER_ROLE_SUFFIX = "_TENANT_READER";
128 public static final String DEFAULT_ADMIN_PASSWORD = "Administrator";
129 public static final String DEFAULT_READER_PASSWORD = "reader";
131 // SQL for init tasks
132 final private static String INSERT_ACCOUNT_ROLE_SQL_MYSQL =
133 "INSERT INTO accounts_roles(account_id, user_id, role_id, role_name, created_at)"
134 +" VALUES(?, ?, ?, ?, now())";
135 final private static String INSERT_ACCOUNT_ROLE_SQL_POSTGRES =
136 "INSERT INTO accounts_roles(HJID, account_id, user_id, role_id, role_name, created_at)"
137 +" VALUES(nextval('hibernate_sequence'), ?, ?, ?, ?, now())";
138 final private static String QUERY_USERS_SQL =
139 "SELECT username FROM users WHERE username LIKE '"
140 +TENANT_ADMIN_ACCT_PREFIX+"%' OR username LIKE '"+TENANT_READER_ACCT_PREFIX+"%'";
141 final private static String INSERT_USER_SQL =
142 "INSERT INTO users (username,passwd, created_at) VALUES (?,?, now())";
143 final private static String INSERT_ACCOUNT_SQL =
144 "INSERT INTO accounts_common "
145 + "(csid, email, userid, status, screen_name, metadata_protection, roles_protection, created_at) "
146 + "VALUES (?,?,?,'ACTIVE',?, 'immutable', 'immutable', now())";
148 // TENANT MANAGER specific SQL
149 final private static String QUERY_TENANT_MGR_USER_SQL =
150 "SELECT username FROM users WHERE username = '"+TENANT_MANAGER_USER+"'";
151 final private static String GET_TENANT_MGR_ROLE_SQL =
152 "SELECT csid from roles WHERE tenant_id='" + AuthN.ALL_TENANTS_MANAGER_TENANT_ID + "' and rolename=?";
154 public static final String IGNORE_TENANT_ID = null; // A null constant to indicate an empty/unused value for the tenant ID
157 public static String getTenantConfigMD5Hash(String tenantId) {
158 return tenantConfigMD5HashTable.get(tenantId);
161 public static String setTenantConfigMD5Hash(String tenantId, String md5hash) {
162 return tenantConfigMD5HashTable.put(tenantId, md5hash);
165 public static Role getRole(String tenantId, String displayName) {
168 String roleName = AuthorizationCommon.getQualifiedRoleName(tenantId, displayName);
169 role = AuthorizationStore.getRoleByName(roleName, tenantId);
174 public static Role getRole(EntityManager em, String tenantId, String displayName) {
177 String roleName = AuthorizationCommon.getQualifiedRoleName(tenantId, displayName);
178 role = AuthorizationStore.getRoleByName(em, roleName, tenantId);
184 public static Role createRole(String tenantId, String name, String description) {
185 return createRole(tenantId, name, description, false /* mutable by default */);
188 public static Role createRole(String tenantId, String name, String description, boolean immutable) {
189 Role role = new Role();
191 role.setCreatedAtItem(new Date());
192 role.setDisplayName(name);
193 String roleName = AuthorizationCommon.getQualifiedRoleName(tenantId, name);
194 role.setRoleName(roleName);
195 String id = UUID.randomUUID().toString(); //FIXME: The qualified role name should be unique enough to use as an ID/key
197 role.setDescription(description);
198 role.setTenantId(tenantId);
199 if (immutable == true) {
200 role.setMetadataProtection(RoleClient.IMMUTABLE);
201 role.setPermsProtection(RoleClient.IMMUTABLE);
208 * Add permission to the Spring Security tables
209 * with assumption that resource is of type URI
210 * @param permission configuration
212 public static void addPermissionsForUri(Permission perm,
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());
228 List<PermissionAction> permActions = perm.getAction();
229 for (PermissionAction permAction : permActions) {
231 CSpaceAction action = URIResourceImpl.getAction(permAction.getName());
232 URIResourceImpl uriRes = new URIResourceImpl(perm.getTenantId(),
233 perm.getResourceName(), action);
234 boolean grant = perm.getEffect().equals(EffectType.PERMIT) ? true : false;
235 AuthZ.get().addPermissions(uriRes, principals.toArray(new String[0]), grant);//CSPACE-4967
236 } catch (PermissionException e) {
238 // Only throw the exception if it is *not* an already-exists exception
240 if (e.getCause() instanceof AlreadyExistsException == false) {
247 private static Connection getConnection(String databaseName) throws NamingException, SQLException {
248 return JDBCTools.getConnection(JDBCTools.CSPACE_DATASOURCE_NAME,
253 * Spring security seems to require that all of our role names start
254 * with the ROLE_PREFIX string.
256 public static String getQualifiedRoleName(String tenantId, String name) {
257 String result = name;
259 String qualifiedName = ROLE_PREFIX + tenantId.toUpperCase() + "_" + name.toUpperCase();
260 if (name.equals(qualifiedName) == false) {
261 result = qualifiedName;
267 private static ActionGroup getActionGroup(String actionGroupStr) {
268 ActionGroup result = null;
270 if (actionGroupStr.equalsIgnoreCase(ACTIONGROUP_CRUDL_NAME)) {
271 result = ACTIONGROUP_CRUDL;
272 } else if (actionGroupStr.equalsIgnoreCase(ACTIONGROUP_RL_NAME)) {
273 result = ACTIONGROUP_RL;
279 public static Permission createPermission(String tenantId,
282 String actionGroupStr) {
283 Permission result = null;
285 ActionGroup actionGroup = getActionGroup(actionGroupStr);
286 result = createPermission(tenantId, resourceName, description, actionGroup);
291 private static Permission createPermission(String tenantId,
294 ActionGroup actionGroup) {
296 + "-" + resourceName.replace('/', '_') // Remove the slashes so the ID can be used in a URI/URL
297 + "-" + actionGroup.name;
298 Permission perm = new Permission();
300 perm.setDescription(description);
301 perm.setCreatedAtItem(new Date());
302 perm.setResourceName(resourceName.toLowerCase().trim());
303 perm.setEffect(EffectType.PERMIT);
304 perm.setTenantId(tenantId);
306 perm.setActionGroup(actionGroup.name);
307 ArrayList<PermissionAction> pas = new ArrayList<PermissionAction>();
309 for (ActionType actionType : actionGroup.actions) {
310 PermissionAction permAction = createPermissionAction(perm, actionType);
317 private static Permission createWorkflowPermission(TenantBindingType tenantBinding,
318 ServiceBindingType serviceBinding,
319 String transitionVerb,
320 ActionGroup actionGroup)
322 Permission result = null;
323 String workFlowServiceSuffix;
324 String transitionName;
325 if (transitionVerb != null) {
326 transitionName = transitionVerb;
327 workFlowServiceSuffix = WorkflowClient.SERVICE_AUTHZ_SUFFIX;
329 transitionName = ""; //since the transitionDef was null, we're assuming that this is the base workflow permission to be created
330 workFlowServiceSuffix = WorkflowClient.SERVICE_PATH;
333 String tenantId = tenantBinding.getId();
334 String resourceName = "/"
335 + serviceBinding.getName().toLowerCase().trim()
336 + workFlowServiceSuffix
338 String description = "A generated workflow permission for actiongroup " + actionGroup.name;
339 result = createPermission(tenantId, resourceName, description, actionGroup);
341 if (logger.isDebugEnabled() == true) {
342 logger.debug("Generated a workflow permission: "
343 + result.getResourceName()
344 + ":" + transitionName
345 + ":" + "tenant id=" + result.getTenantId()
346 + ":" + actionGroup.name);
352 private static PermissionRole createPermissionRole(EntityManager em,
353 Permission permission,
355 boolean enforceTenancy) throws Exception
357 PermissionRole permRole = new PermissionRole();
358 // Check to see if the tenant ID of the permission and the tenant ID of the role match
359 boolean tenantIdsMatch = role.getTenantId().equalsIgnoreCase(permission.getTenantId());
360 if (tenantIdsMatch == false && enforceTenancy == false) {
361 tenantIdsMatch = true; // If we don't need to enforce tenancy then we'll just consider them matched.
364 if (tenantIdsMatch == true) {
365 permRole.setSubject(SubjectType.ROLE);
367 // Set of the permission value list of the permrole
369 List<PermissionValue> permValues = new ArrayList<PermissionValue>();
370 PermissionValue permValue = new PermissionValue();
371 permValue.setPermissionId(permission.getCsid());
372 permValue.setResourceName(permission.getResourceName().toLowerCase());
373 permValue.setActionGroup(permission.getActionGroup());
374 permValues.add(permValue);
375 permRole.setPermission(permValues);
377 // Set of the role value list of the permrole
379 List<RoleValue> roleValues = new ArrayList<RoleValue>();
380 RoleValue rv = new RoleValue();
381 // This needs to use the qualified name, not the display name
382 rv.setRoleName(role.getRoleName());
383 rv.setRoleId(role.getCsid());
385 permRole.setRole(roleValues);
387 String errMsg = "The tenant ID of the role: " + role.getTenantId()
388 + " did not match the tenant ID of the permission: " + permission.getTenantId();
389 throw new Exception(errMsg);
395 private static Hashtable<String, String> getTenantNamesFromConfig(TenantBindingConfigReaderImpl tenantBindingConfigReader) {
397 // Note that this only handles tenants not marked as "createDisabled"
398 Hashtable<String, TenantBindingType> tenantBindings =
399 tenantBindingConfigReader.getTenantBindings();
400 Hashtable<String, String> tenantInfo = new Hashtable<String, String>();
401 for (TenantBindingType tenantBinding : tenantBindings.values()) {
402 String tId = tenantBinding.getId();
403 String tName = tenantBinding.getName();
404 tenantInfo.put(tId, tName);
405 if (logger.isDebugEnabled()) {
406 logger.debug("getTenantNamesFromConfig found configured tenant id: "+tId+" name: "+tName);
412 private static ArrayList<String> compileExistingTenants(Connection conn, Hashtable<String, String> tenantInfo)
413 throws SQLException, Exception {
414 Statement stmt = null;
415 ArrayList<String> existingTenants = new ArrayList<String>();
416 // First find or create the tenants
417 final String queryTenantSQL = "SELECT id,name FROM tenants";
419 stmt = conn.createStatement();
420 ResultSet rs = stmt.executeQuery(queryTenantSQL);
422 String tId = rs.getString("id");
423 String tName = rs.getString("name");
424 if(tenantInfo.containsKey(tId)) {
425 existingTenants.add(tId);
426 if(!tenantInfo.get(tId).equalsIgnoreCase(tName)) {
427 logger.warn("Configured name for tenant: "
428 +tId+" in repository: "+tName
429 +" does not match config'd name: "+ tenantInfo.get(tId));
434 } catch(Exception e) {
441 return existingTenants;
444 private static ArrayList<String> findOrCreateDefaultUsers(Connection conn, Hashtable<String, String> tenantInfo)
445 throws SQLException, Exception {
446 // Second find or create the users
447 Statement stmt = null;
448 PreparedStatement pstmt = null;
449 ArrayList<String> usersInRepo = new ArrayList<String>();
451 stmt = conn.createStatement();
452 ResultSet rs = stmt.executeQuery(QUERY_USERS_SQL);
454 String uName = rs.getString("username");
455 usersInRepo.add(uName);
458 pstmt = conn.prepareStatement(INSERT_USER_SQL); // create a statement
459 for(String tName : tenantInfo.values()) {
460 String adminAcctName = getDefaultAdminUserID(tName);
461 if(!usersInRepo.contains(adminAcctName)) {
462 String secEncPasswd = SecurityUtils.createPasswordHash(
463 adminAcctName, DEFAULT_ADMIN_PASSWORD);
464 pstmt.setString(1, adminAcctName); // set username param
465 pstmt.setString(2, secEncPasswd); // set passwd param
466 if (logger.isDebugEnabled()) {
467 logger.debug("createDefaultUsersAndAccounts adding user: "
468 +adminAcctName+" for tenant: "+tName);
470 pstmt.executeUpdate();
471 } else if (logger.isDebugEnabled()) {
472 logger.debug("createDefaultUsersAndAccounts: user: "+adminAcctName
473 +" already exists - skipping.");
477 String readerAcctName = getDefaultReaderUserID(tName);
478 if(!usersInRepo.contains(readerAcctName)) {
479 String secEncPasswd = SecurityUtils.createPasswordHash(
480 readerAcctName, DEFAULT_READER_PASSWORD);
481 pstmt.setString(1, readerAcctName); // set username param
482 pstmt.setString(2, secEncPasswd); // set passwd param
483 if (logger.isDebugEnabled()) {
484 logger.debug("createDefaultUsersAndAccounts adding user: "
485 +readerAcctName+" for tenant: "+tName);
487 pstmt.executeUpdate();
488 } else if (logger.isDebugEnabled()) {
489 logger.debug("createDefaultUsersAndAccounts: user: "+readerAcctName
490 +" already exists - skipping.");
494 } catch(Exception e) {
505 private static void findOrCreateDefaultAccounts(Connection conn, Hashtable<String, String> tenantInfo,
506 ArrayList<String> usersInRepo,
507 Hashtable<String, String> tenantAdminAcctCSIDs, Hashtable<String, String> tenantReaderAcctCSIDs)
508 throws SQLException, Exception {
509 // Third, create the accounts. Assume that if the users were already there,
510 // then the accounts were as well
511 PreparedStatement pstmt = null;
513 pstmt = conn.prepareStatement(INSERT_ACCOUNT_SQL); // create a statement
514 for(String tId : tenantInfo.keySet()) {
515 String tName = tenantInfo.get(tId);
516 String adminCSID = UUID.randomUUID().toString();
517 tenantAdminAcctCSIDs.put(tId, adminCSID);
518 String adminAcctName = getDefaultAdminUserID(tName);
519 if(!usersInRepo.contains(adminAcctName)) {
520 pstmt.setString(1, adminCSID); // set csid param
521 pstmt.setString(2, adminAcctName); // set email param (bogus)
522 pstmt.setString(3, adminAcctName); // set userid param
523 pstmt.setString(4, "Administrator");// set screen name param
524 if (logger.isDebugEnabled()) {
525 logger.debug("createDefaultAccounts adding account: "
526 +adminAcctName+" for tenant: "+tName);
528 pstmt.executeUpdate();
529 } else if (logger.isDebugEnabled()) {
530 logger.debug("createDefaultAccounts: user: "+adminAcctName
531 +" already exists - skipping account generation.");
534 String readerCSID = UUID.randomUUID().toString();
535 tenantReaderAcctCSIDs.put(tId, readerCSID);
536 String readerAcctName = getDefaultReaderUserID(tName);
537 if(!usersInRepo.contains(readerAcctName)) {
538 pstmt.setString(1, readerCSID); // set csid param
539 pstmt.setString(2, readerAcctName); // set email param (bogus)
540 pstmt.setString(3, readerAcctName); // set userid param
541 pstmt.setString(4, "Reader"); // set screen name param
542 if (logger.isDebugEnabled()) {
543 logger.debug("createDefaultAccounts adding account: "
544 +readerAcctName+" for tenant: "+tName);
546 pstmt.executeUpdate();
547 } else if (logger.isDebugEnabled()) {
548 logger.debug("createDefaultAccounts: user: "+readerAcctName
549 +" already exists - skipping account creation.");
553 } catch(Exception e) {
561 private static boolean findOrCreateTenantManagerUserAndAccount(Connection conn)
562 throws SQLException, Exception {
563 // Find or create the special tenant manager account.
564 // Later can make the user name for tenant manager be configurable, settable.
565 Statement stmt = null;
566 PreparedStatement pstmt = null;
567 boolean created = false;
569 boolean foundTMgrUser = false;
570 stmt = conn.createStatement();
571 ResultSet rs = stmt.executeQuery(QUERY_TENANT_MGR_USER_SQL);
572 // Should only find one - only consider it
574 String uName = rs.getString("username");
575 foundTMgrUser = uName.equals(TENANT_MANAGER_USER);
579 pstmt = conn.prepareStatement(INSERT_USER_SQL); // create a statement
580 String secEncPasswd = SecurityUtils.createPasswordHash(
581 TENANT_MANAGER_USER, DEFAULT_TENANT_MANAGER_PASSWORD);
582 pstmt.setString(1, TENANT_MANAGER_USER); // set username param
583 pstmt.setString(2, secEncPasswd); // set passwd param
584 if (logger.isDebugEnabled()) {
585 logger.debug("findOrCreateTenantManagerUserAndAccount adding tenant manager user: "
586 +TENANT_MANAGER_USER);
588 pstmt.executeUpdate();
590 // Now create the account to match
591 pstmt = conn.prepareStatement(INSERT_ACCOUNT_SQL); // create a statement
592 pstmt.setString(1, AuthN.TENANT_MANAGER_ACCT_ID); // set csid param
593 pstmt.setString(2, DEFAULT_TENANT_MANAGER_EMAIL); // set email param (bogus)
594 pstmt.setString(3, TENANT_MANAGER_USER); // set userid param
595 pstmt.setString(4, TENANT_MANAGER_SCREEN_NAME);// set screen name param
596 if (logger.isDebugEnabled()) {
597 logger.debug("findOrCreateTenantManagerUserAndAccount adding tenant manager account: "
598 +TENANT_MANAGER_USER);
600 pstmt.executeUpdate();
603 } else if (logger.isDebugEnabled()) {
604 logger.debug("findOrCreateTenantManagerUserAndAccount: tenant manager: "+TENANT_MANAGER_USER
605 +" already exists.");
607 } catch(Exception e) {
618 private static void bindDefaultAccountsToTenants(Connection conn, DatabaseProductType databaseProductType,
619 Hashtable<String, String> tenantInfo, ArrayList<String> usersInRepo,
620 Hashtable<String, String> tenantAdminAcctCSIDs, Hashtable<String, String> tenantReaderAcctCSIDs)
621 throws SQLException, Exception {
622 // Fourth, bind accounts to tenants. Assume that if the users were already there,
623 // then the accounts were bound to tenants correctly
624 PreparedStatement pstmt = null;
626 String insertAccountTenantSQL;
627 if (databaseProductType == DatabaseProductType.MYSQL) {
628 insertAccountTenantSQL =
629 "INSERT INTO accounts_tenants (TENANTS_ACCOUNTS_COMMON_CSID,tenant_id) "
631 } else if (databaseProductType == DatabaseProductType.POSTGRESQL) {
632 insertAccountTenantSQL =
633 "INSERT INTO accounts_tenants (HJID, TENANTS_ACCOUNTS_COMMON_CSID,tenant_id) "
634 + " VALUES(nextval('hibernate_sequence'), ?, ?)";
636 throw new Exception("Unrecognized database system.");
638 pstmt = conn.prepareStatement(insertAccountTenantSQL); // create a statement
639 for(String tId : tenantInfo.keySet()) {
640 String tName = tenantInfo.get(tId);
641 if(!usersInRepo.contains(getDefaultAdminUserID(tName))) {
642 String adminAcct = tenantAdminAcctCSIDs.get(tId);
643 pstmt.setString(1, adminAcct); // set acct CSID param
644 pstmt.setString(2, tId); // set tenant_id param
645 if (logger.isDebugEnabled()) {
646 logger.debug("createDefaultAccounts binding account id: "
647 +adminAcct+" to tenant id: "+tId);
649 pstmt.executeUpdate();
651 if(!usersInRepo.contains(getDefaultReaderUserID(tName))) {
652 String readerAcct = tenantReaderAcctCSIDs.get(tId);
653 pstmt.setString(1, readerAcct); // set acct CSID param
654 pstmt.setString(2, tId); // set tenant_id param
655 if (logger.isDebugEnabled()) {
656 logger.debug("createDefaultAccounts binding account id: "
657 +readerAcct+" to tenant id: "+tId);
659 pstmt.executeUpdate();
663 } catch(Exception e) {
672 private static String findOrCreateDefaultRoles(Connection conn, Hashtable<String, String> tenantInfo,
673 Hashtable<String, String> tenantAdminRoleCSIDs, Hashtable<String, String> tenantReaderRoleCSIDs)
674 throws SQLException, Exception {
675 // Fifth, fetch and save the default roles
676 String springAdminRoleCSID = null;
677 Statement stmt = null;
678 PreparedStatement pstmt = null;
680 final String querySpringRole =
681 "SELECT csid from roles WHERE rolename='"+AuthN.ROLE_SPRING_ADMIN_NAME+"'";
682 stmt = conn.createStatement();
683 ResultSet rs = stmt.executeQuery(querySpringRole);
685 springAdminRoleCSID = rs.getString(1);
686 if (logger.isDebugEnabled()) {
687 logger.debug("createDefaultAccounts found Spring Admin role: "
688 +springAdminRoleCSID);
691 final String insertSpringAdminRoleSQL =
692 "INSERT INTO roles (csid, rolename, displayName, rolegroup, created_at, tenant_id) "
693 + "VALUES ('-1', 'ROLE_SPRING_ADMIN', 'SPRING_ADMIN', 'Spring Security Administrator', now(), '0')";
694 stmt.executeUpdate(insertSpringAdminRoleSQL);
695 springAdminRoleCSID = "-1";
696 if (logger.isDebugEnabled()) {
697 logger.debug("createDefaultAccounts CREATED Spring Admin role: "
698 +springAdminRoleCSID);
702 final String getRoleCSIDSql =
703 "SELECT csid from roles WHERE tenant_id=? and rolename=?";
704 pstmt = conn.prepareStatement(getRoleCSIDSql); // create a statement
706 for(String tId : tenantInfo.keySet()) {
707 pstmt.setString(1, tId); // set tenant_id param
708 pstmt.setString(2, getDefaultAdminRole(tId)); // set rolename param
709 rs = pstmt.executeQuery();
710 // extract data from the ResultSet
712 throw new RuntimeException("Cannot find role: "+getDefaultAdminRole(tId)
713 +" for tenant id: "+tId+" in roles!");
715 String tenantAdminRoleCSID = rs.getString(1);
716 if (logger.isDebugEnabled()) {
717 logger.debug("createDefaultAccounts found role: "
718 +getDefaultAdminRole(tId)+"("+tenantAdminRoleCSID
719 +") for tenant id: "+tId);
721 tenantAdminRoleCSIDs.put(tId, tenantAdminRoleCSID);
722 pstmt.setString(1, tId); // set tenant_id param
723 pstmt.setString(2, getDefaultReaderRole(tId)); // set rolename param
725 rs = pstmt.executeQuery();
726 // extract data from the ResultSet
728 throw new RuntimeException("Cannot find role: "+getDefaultReaderRole(tId)
729 +" for tenant id: "+tId+" in roles!");
731 String tenantReaderRoleCSID = rs.getString(1);
732 if (logger.isDebugEnabled()) {
733 logger.debug("createDefaultAccounts found role: "
734 +getDefaultReaderRole(tId)+"("+tenantReaderRoleCSID
735 +") for tenant id: "+tId);
737 tenantReaderRoleCSIDs.put(tId, tenantReaderRoleCSID);
741 } catch(Exception e) {
749 return springAdminRoleCSID;
752 private static String findTenantManagerRole(Connection conn )
753 throws SQLException, RuntimeException, Exception {
754 String tenantMgrRoleCSID = null;
755 PreparedStatement pstmt = null;
757 String rolename = getQualifiedRoleName(AuthN.ALL_TENANTS_MANAGER_TENANT_ID,
758 AuthN.ROLE_ALL_TENANTS_MANAGER);
759 pstmt = conn.prepareStatement(GET_TENANT_MGR_ROLE_SQL); // create a statement
761 pstmt.setString(1, rolename); // set rolename param
762 rs = pstmt.executeQuery();
764 tenantMgrRoleCSID = rs.getString(1);
765 if (logger.isDebugEnabled()) {
766 logger.debug("findTenantManagerRole found Tenant Mgr role: "
771 } catch(Exception e) {
777 if(tenantMgrRoleCSID==null)
778 throw new RuntimeException("findTenantManagerRole: Cound not find tenant Manager Role!");
779 return tenantMgrRoleCSID;
782 private static void bindAccountsToRoles(Connection conn, DatabaseProductType databaseProductType,
783 Hashtable<String, String> tenantInfo, ArrayList<String> usersInRepo,
784 String springAdminRoleCSID,
785 Hashtable<String, String> tenantAdminRoleCSIDs, Hashtable<String, String> tenantReaderRoleCSIDs,
786 Hashtable<String, String> tenantAdminAcctCSIDs, Hashtable<String, String> tenantReaderAcctCSIDs)
787 throws SQLException, Exception {
788 // Sixth, bind the accounts to roles. If the users already existed,
789 // we'll assume they were set up correctly.
790 PreparedStatement pstmt = null;
792 String insertAccountRoleSQL;
793 if (databaseProductType == DatabaseProductType.MYSQL) {
794 insertAccountRoleSQL = INSERT_ACCOUNT_ROLE_SQL_MYSQL;
795 } else if (databaseProductType == DatabaseProductType.POSTGRESQL) {
796 insertAccountRoleSQL = INSERT_ACCOUNT_ROLE_SQL_POSTGRES;
798 throw new Exception("Unrecognized database system.");
800 if (logger.isDebugEnabled()) {
801 logger.debug("createDefaultAccounts binding accounts to roles with SQL:\n"
802 +insertAccountRoleSQL);
804 pstmt = conn.prepareStatement(insertAccountRoleSQL); // create a statement
805 for(String tId : tenantInfo.keySet()) {
806 String adminUserId = getDefaultAdminUserID(tenantInfo.get(tId));
807 if(!usersInRepo.contains(adminUserId)) {
808 String adminAcct = tenantAdminAcctCSIDs.get(tId);
809 String adminRoleId = tenantAdminRoleCSIDs.get(tId);
810 pstmt.setString(1, adminAcct); // set acct CSID param
811 pstmt.setString(2, adminUserId); // set user_id param
812 pstmt.setString(3, adminRoleId); // set role_id param
813 pstmt.setString(4, getDefaultAdminRole(tId)); // set rolename param
814 if (logger.isDebugEnabled()) {
815 logger.debug("createDefaultAccounts binding account: "
816 +adminUserId+" to Admin role("+adminRoleId
817 +") for tenant id: "+tId);
819 pstmt.executeUpdate();
820 // Now add the Spring Admin Role to the admin accounts
821 pstmt.setString(3, springAdminRoleCSID); // set role_id param
822 pstmt.setString(4, AuthN.ROLE_SPRING_ADMIN_NAME); // set rolename param
823 if (logger.isDebugEnabled()) {
824 logger.debug("createDefaultAccounts binding account: "
825 +adminUserId+" to Spring Admin role: "+springAdminRoleCSID);
827 pstmt.executeUpdate();
829 String readerUserId = getDefaultReaderUserID(tenantInfo.get(tId));
830 if(!usersInRepo.contains(readerUserId)) {
831 String readerAcct = tenantReaderAcctCSIDs.get(tId);
832 String readerRoleId = tenantReaderRoleCSIDs.get(tId);
833 pstmt.setString(1, readerAcct); // set acct CSID param
834 pstmt.setString(2, readerUserId); // set user_id param
835 pstmt.setString(3, readerRoleId); // set role_id param
836 pstmt.setString(4, getDefaultReaderRole(tId)); // set rolename param
837 if (logger.isDebugEnabled()) {
838 logger.debug("createDefaultAccounts binding account: "
839 +readerUserId+" to Reader role("+readerRoleId
840 +") for tenant id: "+tId);
842 pstmt.executeUpdate();
846 } catch(Exception e) {
854 private static void bindTenantManagerAccountRole(Connection conn, DatabaseProductType databaseProductType,
855 String tenantManagerUserID, String tenantManagerAccountID, String tenantManagerRoleID, String tenantManagerRoleName )
856 throws SQLException, Exception {
857 PreparedStatement pstmt = null;
859 String insertAccountRoleSQL;
860 if (databaseProductType == DatabaseProductType.MYSQL) {
861 insertAccountRoleSQL = INSERT_ACCOUNT_ROLE_SQL_MYSQL;
862 } else if (databaseProductType == DatabaseProductType.POSTGRESQL) {
863 insertAccountRoleSQL = INSERT_ACCOUNT_ROLE_SQL_POSTGRES;
865 throw new Exception("Unrecognized database system.");
867 if (logger.isDebugEnabled()) {
868 logger.debug("bindTenantManagerAccountRole binding account to role with SQL:\n"
869 +insertAccountRoleSQL);
871 pstmt = conn.prepareStatement(insertAccountRoleSQL); // create a statement
872 pstmt.setString(1, tenantManagerAccountID); // set acct CSID param
873 pstmt.setString(2, tenantManagerUserID); // set user_id param
874 pstmt.setString(3, tenantManagerRoleID); // set role_id param
875 pstmt.setString(4, tenantManagerRoleName); // set rolename param
876 if (logger.isDebugEnabled()) {
877 logger.debug("bindTenantManagerAccountRole binding user: "
878 +tenantManagerUserID+" to Admin role("+tenantManagerRoleName+")");
880 pstmt.executeUpdate();
881 /* At this point, tenant manager should not need the Spring Admin Role
882 pstmt.setString(3, springAdminRoleCSID); // set role_id param
883 pstmt.setString(4, SPRING_ADMIN_ROLE); // set rolename param
884 if (logger.isDebugEnabled()) {
885 logger.debug("createDefaultAccounts binding account: "
886 +adminUserId+" to Spring Admin role: "+springAdminRoleCSID);
888 pstmt.executeUpdate();
891 } catch(Exception e) {
900 * Using the tenant bindings, ensure there are corresponding Tenant records (db columns).
902 public static void createTenants(
903 TenantBindingConfigReaderImpl tenantBindingConfigReader,
904 DatabaseProductType databaseProductType,
905 String cspaceDatabaseName) throws Exception {
906 logger.debug("ServiceMain.createTenants starting...");
907 Hashtable<String, String> tenantInfo = getTenantNamesFromConfig(tenantBindingConfigReader);
908 Connection conn = null;
910 conn = getConnection(cspaceDatabaseName);
911 ArrayList<String> existingTenants = compileExistingTenants(conn, tenantInfo);
913 // Note that this only creates tenants not marked as "createDisabled"
914 createMissingTenants(conn, tenantInfo, existingTenants);
915 } catch (Exception e) {
916 logger.debug("Exception in createTenants: " + e.getLocalizedMessage());
922 } catch (SQLException sqle) {
923 if (logger.isDebugEnabled()) {
924 logger.debug("SQL Exception closing statement/connection: " + sqle.getLocalizedMessage());
930 public static void createDefaultAccounts(
931 TenantBindingConfigReaderImpl tenantBindingConfigReader,
932 DatabaseProductType databaseProductType,
933 String cspaceDatabaseName) throws Exception {
935 logger.debug("ServiceMain.createDefaultAccounts starting...");
937 Hashtable<String, String> tenantInfo = getTenantNamesFromConfig(tenantBindingConfigReader);
938 Connection conn = null;
939 // TODO - need to put in tests for existence first.
940 // We could just look for the accounts per tenant up front, and assume that
941 // the rest is there if the accounts are.
942 // Could add a sql script to remove these if need be - Spring only does roles,
943 // and we're not touching that, so we could safely toss the
944 // accounts, users, account-tenants, account-roles, and start over.
946 conn = getConnection(cspaceDatabaseName);
948 ArrayList<String> usersInRepo = findOrCreateDefaultUsers(conn, tenantInfo);
950 Hashtable<String, String> tenantAdminAcctCSIDs = new Hashtable<String, String>();
951 Hashtable<String, String> tenantReaderAcctCSIDs = new Hashtable<String, String>();
952 findOrCreateDefaultAccounts(conn, tenantInfo, usersInRepo,
953 tenantAdminAcctCSIDs, tenantReaderAcctCSIDs);
955 bindDefaultAccountsToTenants(conn, databaseProductType, tenantInfo, usersInRepo,
956 tenantAdminAcctCSIDs, tenantReaderAcctCSIDs);
958 Hashtable<String, String> tenantAdminRoleCSIDs = new Hashtable<String, String>();
959 Hashtable<String, String> tenantReaderRoleCSIDs = new Hashtable<String, String>();
960 String springAdminRoleCSID = findOrCreateDefaultRoles(conn, tenantInfo,
961 tenantAdminRoleCSIDs, tenantReaderRoleCSIDs);
963 bindAccountsToRoles(conn, databaseProductType,
964 tenantInfo, usersInRepo, springAdminRoleCSID,
965 tenantAdminRoleCSIDs, tenantReaderRoleCSIDs,
966 tenantAdminAcctCSIDs, tenantReaderAcctCSIDs);
968 boolean createdTenantMgrAccount = findOrCreateTenantManagerUserAndAccount(conn);
969 if(createdTenantMgrAccount) {
970 // If we created the account, we need to create the bindings. Otherwise, assume they
971 // are all set (from previous initialization).
972 String tenantManagerRoleCSID = findTenantManagerRole(conn);
973 bindTenantManagerAccountRole(conn, databaseProductType,
974 TENANT_MANAGER_USER, AuthN.TENANT_MANAGER_ACCT_ID,
975 tenantManagerRoleCSID, AuthN.ROLE_ALL_TENANTS_MANAGER);
977 } catch (Exception e) {
978 logger.debug("Exception in createDefaultAccounts: " + e.getLocalizedMessage());
984 } catch (SQLException sqle) {
985 if (logger.isDebugEnabled()) {
986 logger.debug("SQL Exception closing statement/connection: " + sqle.getLocalizedMessage());
992 private static String getDefaultAdminRole(String tenantId) {
993 return ROLE_PREFIX+tenantId+TENANT_ADMIN_ROLE_SUFFIX;
996 private static String getDefaultReaderRole(String tenantId) {
997 return ROLE_PREFIX+tenantId+TENANT_READER_ROLE_SUFFIX;
1000 private static String getDefaultAdminUserID(String tenantName) {
1001 return TENANT_ADMIN_ACCT_PREFIX+tenantName;
1004 private static String getDefaultReaderUserID(String tenantName) {
1005 return TENANT_READER_ACCT_PREFIX+tenantName;
1008 static public PermissionAction createPermissionAction(Permission perm,
1009 ActionType actionType) {
1010 PermissionAction pa = new PermissionAction();
1012 CSpaceAction action = URIResourceImpl.getAction(actionType);
1013 URIResourceImpl uriRes = new URIResourceImpl(perm.getTenantId(),
1014 perm.getResourceName(), action);
1015 pa.setName(actionType);
1016 pa.setObjectIdentity(uriRes.getHashedId().toString());
1017 pa.setObjectIdentityResource(uriRes.getId());
1022 static public PermissionAction update(Permission perm, PermissionAction permAction) {
1023 PermissionAction pa = new PermissionAction();
1025 CSpaceAction action = URIResourceImpl.getAction(permAction.getName());
1026 URIResourceImpl uriRes = new URIResourceImpl(perm.getTenantId(),
1027 perm.getResourceName(), action);
1028 pa.setObjectIdentity(uriRes.getHashedId().toString());
1029 pa.setObjectIdentityResource(uriRes.getId());
1034 private static HashSet<String> getTransitionVerbList(TenantBindingType tenantBinding, ServiceBindingType serviceBinding) {
1035 HashSet<String> result = new HashSet<String>();
1037 TransitionDefList transitionDefList = getTransitionDefList(tenantBinding, serviceBinding);
1038 for (TransitionDef transitionDef : transitionDefList.getTransitionDef()) {
1039 String transitionVerb = transitionDef.getName();
1040 String[] tokens = transitionVerb.split("_"); // Split the verb into words. The workflow verbs are compound words combined with the '_' character.
1041 result.add(tokens[0]); // We only care about the first word.
1047 private static TransitionDefList getTransitionDefList(TenantBindingType tenantBinding, ServiceBindingType serviceBinding) {
1048 TransitionDefList result = null;
1050 String serviceObjectName = serviceBinding.getObject().getName();
1051 DocumentHandler docHandler = ServiceConfigUtils.createDocumentHandlerInstance(
1052 tenantBinding, serviceBinding);
1053 Lifecycle lifecycle = docHandler.getLifecycle(serviceObjectName);
1054 if (lifecycle != null) {
1055 result = lifecycle.getTransitionDefList();
1057 } catch (Exception e) {
1058 // Ignore this exception and return an empty non-null TransitionDefList
1061 if (result == null) {
1062 if (serviceBinding.getType().equalsIgnoreCase(ServiceBindingUtils.SERVICE_TYPE_SECURITY) == false) {
1063 logger.debug("Could not retrieve a lifecycle transition definition list from: "
1064 + serviceBinding.getName()
1065 + " with tenant ID = "
1066 + tenantBinding.getId());
1068 // return an empty list
1069 result = new TransitionDefList();
1071 logger.debug("Successfully retrieved a lifecycle transition definition list from: "
1072 + serviceBinding.getName()
1073 + " with tenant ID = "
1074 + tenantBinding.getId());
1082 * @param tenantBindingConfigReader
1083 * @param databaseProductType
1084 * @param cspaceDatabaseName
1087 public static void createDefaultWorkflowPermissions(TenantBindingConfigReaderImpl tenantBindingConfigReader,
1088 DatabaseProductType databaseProductType,
1089 String cspaceDatabaseName) throws Exception //FIXME: REM - 4/11/2012 - Rename to createWorkflowPermissions
1091 java.util.logging.Logger logger = java.util.logging.Logger.getAnonymousLogger();
1093 AuthZ.get().login(); //login to Spring Security manager
1095 EntityManagerFactory emf = JpaStorageUtils.getEntityManagerFactory(JpaStorageUtils.CS_PERSISTENCE_UNIT);
1096 EntityManager em = null;
1099 em = emf.createEntityManager();
1101 Hashtable<String, TenantBindingType> tenantBindings = tenantBindingConfigReader.getTenantBindings();
1102 for (String tenantId : tenantBindings.keySet()) {
1103 logger.info(String.format("Creating/verifying workflow permissions for tenant ID=%s.", tenantId));
1104 TenantBindingType tenantBinding = tenantBindings.get(tenantId);
1105 if (tenantBinding.isConfigChangedSinceLastStart() == false) {
1106 continue; // skip the rest of the loop and go to the next tenant
1109 Role adminRole = AuthorizationCommon.getRole(em, tenantBinding.getId(), ROLE_TENANT_ADMINISTRATOR);
1110 Role readonlyRole = AuthorizationCommon.getRole(em, tenantBinding.getId(), ROLE_TENANT_READER);
1112 if (adminRole == null || readonlyRole == null) {
1113 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.");
1115 throw new RuntimeException("One or more of the required default CollectionSpace administrator roles is missing or was never created.");
1118 for (ServiceBindingType serviceBinding : tenantBinding.getServiceBindings()) {
1119 String prop = ServiceBindingUtils.getPropertyValue(serviceBinding, REFRESH_AUTZ_PROP);
1120 if (prop == null ? true : Boolean.parseBoolean(prop)) {
1122 em.getTransaction().begin();
1123 TransitionDefList transitionDefList = getTransitionDefList(tenantBinding, serviceBinding);
1124 HashSet<String> transitionVerbList = getTransitionVerbList(tenantBinding, serviceBinding);
1125 for (String transitionVerb : transitionVerbList) {
1127 // Create the permission for the admin role
1128 Permission adminPerm = createWorkflowPermission(tenantBinding, serviceBinding, transitionVerb, ACTIONGROUP_CRUDL);
1129 persist(em, adminPerm, adminRole, true, ACTIONGROUP_CRUDL);
1131 // Create the permission for the read-only role
1132 Permission readonlyPerm = createWorkflowPermission(tenantBinding, serviceBinding, transitionVerb, ACTIONGROUP_RL);
1133 persist(em, readonlyPerm, readonlyRole, true, ACTIONGROUP_RL); // Persist/store the permission and permrole records and related Spring Security info
1135 em.getTransaction().commit();
1136 } catch (IllegalStateException e) {
1137 logger.fine(e.getLocalizedMessage()); //We end up here if there is no document handler for the service -this is ok for some of the services.
1140 logger.warning("AuthZ refresh service binding property is set to FALSE so default permissions will NOT be refreshed for: "
1141 + serviceBinding.getName());
1146 } catch (Exception e) {
1147 if (em != null && em.getTransaction().isActive()) {
1148 em.getTransaction().rollback();
1150 logger.fine("Caught exception and rolling back permission creation: " + e.getMessage());
1154 JpaStorageUtils.releaseEntityManagerFactory(emf);
1159 private static void createMissingTenants(Connection conn, Hashtable<String, String> tenantInfo,
1160 ArrayList<String> existingTenants) throws SQLException, Exception {
1161 // Need to define and look for a createDisabled attribute in tenant config
1162 final String insertTenantSQL =
1163 "INSERT INTO tenants (id,name,authorities_initialized,disabled,created_at) VALUES (?,?,FALSE,FALSE,now())";
1164 PreparedStatement pstmt = null;
1166 pstmt = conn.prepareStatement(insertTenantSQL); // create a statement
1167 for(String tId : tenantInfo.keySet()) {
1168 if(existingTenants.contains(tId)) {
1169 if (logger.isDebugEnabled()) {
1170 logger.debug("createMissingTenants: tenant exists (skipping): "
1171 +tenantInfo.get(tId));
1175 pstmt.setString(1, tId); // set id param
1176 pstmt.setString(2, tenantInfo.get(tId)); // set name param
1177 if (logger.isDebugEnabled()) {
1178 logger.debug("createMissingTenants adding entry for tenant: "+tId);
1180 pstmt.executeUpdate();
1183 } catch(Exception e) {
1191 public static String getPersistedMD5Hash(String tenantId, String cspaceDatabaseName) throws Exception {
1192 String result = null;
1194 ArrayList<String> existingTenants = new ArrayList<String>();
1195 // First find or create the tenants
1196 final String queryTenantSQL = String.format("SELECT id, name, config_md5hash FROM tenants WHERE id = '%s'", tenantId);
1198 Statement stmt = null;
1202 conn = getConnection(cspaceDatabaseName);
1203 stmt = conn.createStatement();
1204 ResultSet rs = stmt.executeQuery(queryTenantSQL);
1207 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'.",
1208 tenantId, cspaceDatabaseName);
1209 throw new Exception(errMsg);
1211 String tId = rs.getString("id"); // for debugging only
1212 String tName = rs.getString("name"); // for debugging only
1213 result = rs.getString("config_md5hash");
1217 } catch(Exception e) {
1220 if (stmt != null) stmt.close();
1226 private static PermissionRoleRel findPermRoleRel(EntityManager em, String permissionId, String RoleId) {
1227 PermissionRoleRel result = null;
1230 String whereClause = "where permissionId = :id and roleId = :roleId";
1231 HashMap<String, Object> params = new HashMap<String, Object>();
1232 params.put("id", permissionId);
1233 params.put("roleId", RoleId);
1235 result = (PermissionRoleRel) JpaStorageUtils.getEntity(em,
1236 PermissionRoleRel.class.getCanonicalName(), whereClause, params);
1237 } catch (Exception e) {
1238 //Do nothing. Will return null;
1245 * Persists the Permission, PermissionRoleRel, and Spring Security table entries all in one transaction
1247 private static void persist(EntityManager em, Permission permission, Role role, boolean enforceTenancy, ActionGroup actionGroup) throws Exception {
1248 AuthorizationStore authzStore = new AuthorizationStore();
1249 // First persist the Permission record
1250 authzStore.store(em, permission);
1252 // If the PermRoleRel doesn't already exists then relate the permission and the role in a new PermissionRole (the service payload)
1253 // Create a PermissionRoleRel (the database relation table for the permission and role)
1254 PermissionRoleRel permRoleRel = findPermRoleRel(em, permission.getCsid(), role.getCsid());
1255 if (permRoleRel == null) {
1256 PermissionRole permRole = createPermissionRole(em, permission, role, enforceTenancy);
1257 List<PermissionRoleRel> permRoleRels = new ArrayList<PermissionRoleRel>();
1258 PermissionRoleUtil.buildPermissionRoleRel(em, permRole, SubjectType.ROLE, permRoleRels,
1259 false /*not for delete*/, role.getTenantId());
1260 for (PermissionRoleRel prr : permRoleRels) {
1261 authzStore.store(em, prr);
1263 Profiler profiler = new Profiler(AuthorizationCommon.class, 2);
1265 // Add a corresponding entry in the Spring Security Tables
1266 addPermissionsForUri(permission, permRole);
1268 logger.debug("Finished full perm generation for "
1269 + ":" + permission.getTenantId()
1270 + ":" + permission.getResourceName()
1271 + ":" + actionGroup.getName()
1272 + ":" + profiler.getCumulativeTime());
1277 public static boolean hasTokenExpired(EmailConfig emailConfig, Token token) throws NoSuchAlgorithmException {
1278 boolean result = false;
1280 int maxConfigSeconds = emailConfig.getPasswordResetConfig().getTokenExpirationSeconds().intValue();
1281 int maxTokenSeconds = token.getExpireSeconds().intValue();
1283 long createdTime = token.getCreatedAtItem().getTime();
1284 long configExpirationTime = createdTime + maxConfigSeconds * 1000; // the current tenant config for how long a token stays valid
1285 long tokenDefinedExirationTime = createdTime + maxTokenSeconds * 1000; // the tenant config for how long a token stays valid when the token was created.
1287 if (configExpirationTime != tokenDefinedExirationTime) {
1288 String msg = String.format("The configured expiration time for the token = '%s' changed from when the token was created.",
1293 // Note: the current tenant bindings config for expiration takes precedence over the config used to create the token.
1295 if (System.currentTimeMillis() >= configExpirationTime) {
1303 * Validate that the password reset configuration is correct.
1305 private static String validatePasswordResetConfig(PasswordResetConfig passwordResetConfig) {
1306 String result = null;
1308 if (passwordResetConfig != null) {
1309 result = passwordResetConfig.getMessage();
1310 if (result == null || result.length() == 0) {
1311 result = DEFAULT_PASSWORD_RESET_EMAIL_MESSAGE;
1312 logger.warn("Could not find a password reset message in the tenant's configuration. Using the default one");
1315 if (result.contains("{{link}}") == false) {
1316 logger.warn("The tenant's password reset message does not contain a required '{{link}}' marker.");
1320 if (passwordResetConfig.getLoginpage() == null || passwordResetConfig.getLoginpage().trim().isEmpty()) {
1321 logger.warn("The tenant's password reset configuration is missing a 'loginpage' value. It should be set to something like '/collectionspace/ui/core/html/index.html'.");
1325 String subject = passwordResetConfig.getSubject();
1326 if (subject == null || subject.trim().isEmpty()) {
1327 passwordResetConfig.setSubject(DEFAULT_PASSWORD_RESET_EMAIL_SUBJECT);
1336 * Generate a password reset message. Embeds an authorization token to reset a user's password.
1338 public static String generatePasswordResetEmailMessage(EmailConfig emailConfig, AccountListItem accountListItem, Token token) throws Exception {
1339 String result = null;
1341 result = validatePasswordResetConfig(emailConfig.getPasswordResetConfig());
1342 if (result == null) {
1343 String errMsg = String.format("The password reset configuration for the tenant ID='%s' is missing or malformed. Could not initiate a password reset for user ID='%s. See the log files for more details.",
1344 token.getTenantId(), accountListItem.getEmail());
1345 throw new Exception(errMsg);
1348 String link = emailConfig.getBaseurl() + emailConfig.getPasswordResetConfig().getLoginpage() + "?token=" + token.getId();
1349 result = result.replaceAll("\\{\\{link\\}\\}", link);
1351 if (result.contains("{{greeting}}")) {
1352 String greeting = accountListItem.getScreenName();
1353 result = result.replaceAll("\\{\\{greeting\\}\\}", greeting);
1354 result = result.replaceAll("\\\\n", "\\\n");
1355 result = result.replaceAll("\\\\r", "\\\r");
1361 public static void persistTenantBindingsMD5Hash(TenantBindingConfigReaderImpl tenantBindingConfigReader,
1362 DatabaseProductType databaseProductType, String cspaceDatabaseName) throws Exception {
1363 // Need to define and look for a createDisabled attribute in tenant config
1364 String updateTableSQL = "UPDATE tenants SET config_md5hash = ? WHERE id = ?";
1367 PreparedStatement pstmt = null;
1369 conn = getConnection(cspaceDatabaseName);
1370 pstmt = conn.prepareStatement(updateTableSQL); // create a statement
1371 for (String tId : AuthorizationCommon.tenantConfigMD5HashTable.keySet()) {
1372 pstmt.setString(1, AuthorizationCommon.getTenantConfigMD5Hash(tId));
1373 pstmt.setString(2, tId);
1374 if (logger.isDebugEnabled()) {
1375 logger.debug("createMissingTenants adding entry for tenant: " + tId);
1377 pstmt.executeUpdate();
1380 } catch(Exception e) {
1383 if (pstmt!=null) pstmt.close();