1 package org.collectionspace.services.common.authorization_mgt;
3 import java.security.NoSuchAlgorithmException;
4 import java.sql.Connection;
5 import java.sql.PreparedStatement;
6 import java.sql.ResultSet;
7 import java.sql.SQLException;
8 import java.sql.Statement;
9 import java.util.ArrayList;
10 import java.util.Date;
11 import java.util.HashMap;
12 import java.util.HashSet;
13 import java.util.Hashtable;
14 import java.util.List;
16 import java.util.UUID;
17 import javax.naming.NamingException;
18 import javax.ws.rs.core.UriBuilder;
20 import org.collectionspace.authentication.AuthN;
21 import org.collectionspace.services.account.AccountListItem;
23 import org.collectionspace.services.authentication.Token;
24 import org.collectionspace.services.authorization.AuthZ;
25 import org.collectionspace.services.authorization.CSpaceAction;
26 import org.collectionspace.services.authorization.CSpaceResource;
27 import org.collectionspace.services.authorization.PermissionException;
28 import org.collectionspace.services.authorization.PermissionRole;
29 import org.collectionspace.services.authorization.PermissionRoleRel;
30 import org.collectionspace.services.authorization.PermissionValue;
31 import org.collectionspace.services.authorization.Role;
32 import org.collectionspace.services.authorization.RoleValue;
33 import org.collectionspace.services.authorization.SubjectType;
34 import org.collectionspace.services.authorization.URIResourceImpl;
35 import org.collectionspace.services.authorization.perms.ActionType;
36 import org.collectionspace.services.authorization.perms.EffectType;
37 import org.collectionspace.services.authorization.perms.Permission;
38 import org.collectionspace.services.authorization.perms.PermissionAction;
39 import org.collectionspace.services.client.AccountClient;
40 import org.collectionspace.services.client.PermissionClient;
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.DocumentException;
49 import org.collectionspace.services.common.document.DocumentHandler;
50 import org.collectionspace.services.common.security.SecurityUtils;
51 import org.collectionspace.services.common.storage.DatabaseProductType;
52 import org.collectionspace.services.common.storage.JDBCTools;
53 import org.collectionspace.services.common.storage.jpa.JPATransactionContext;
54 import org.collectionspace.services.common.storage.jpa.JpaStorageUtils;
56 import org.collectionspace.services.config.service.ServiceBindingType;
57 import org.collectionspace.services.config.tenant.EmailConfig;
58 import org.collectionspace.services.config.tenant.PasswordResetConfig;
59 import org.collectionspace.services.config.tenant.TenantBindingType;
61 import org.collectionspace.services.lifecycle.Lifecycle;
62 import org.collectionspace.services.lifecycle.TransitionDef;
63 import org.collectionspace.services.lifecycle.TransitionDefList;
65 //import org.mortbay.log.Log;
66 import org.slf4j.Logger;
67 import org.slf4j.LoggerFactory;
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 private static final String DEFAULT_PASSWORD_RESET_EMAIL_SUBJECT = "Password reset for CollectionSpace account";
81 // Keep track of the MD5 hash value for the tenant bindings
83 private static final Map<String, String> tenantConfigMD5HashTable = new HashMap<String, String>();
86 // ActionGroup labels/constants
89 // for READ-WRITE-DELETE
90 final public static String ACTIONGROUP_CRUDL_NAME = "CRUDL";
91 final public static ActionType[] ACTIONSET_CRUDL = {ActionType.CREATE, ActionType.READ, ActionType.UPDATE, ActionType.DELETE, ActionType.SEARCH};
93 final public static String ACTIONGROUP_CRUL_NAME = "CRUL";
94 final public static ActionType[] ACTIONSET_CRUL = {ActionType.CREATE, ActionType.READ, ActionType.UPDATE, ActionType.SEARCH};
96 final public static String ACTIONGROUP_RL_NAME = "RL";
97 final public static ActionType[] ACTIONSET_RL = {ActionType.READ, ActionType.SEARCH};
99 static ActionGroup ACTIONGROUP_CRUDL;
100 static ActionGroup ACTIONGROUP_CRUL;
101 static ActionGroup ACTIONGROUP_RL;
103 // A static block to initialize the predefined action groups
106 ACTIONGROUP_CRUDL = new ActionGroup();
107 ACTIONGROUP_CRUDL.name = ACTIONGROUP_CRUDL_NAME;
108 ACTIONGROUP_CRUDL.actions = ACTIONSET_CRUDL;
110 ACTIONGROUP_RL = new ActionGroup();
111 ACTIONGROUP_RL.name = ACTIONGROUP_RL_NAME;
112 ACTIONGROUP_RL.actions = ACTIONSET_RL;
114 ACTIONGROUP_CRUL = new ActionGroup();
115 ACTIONGROUP_CRUL.name = ACTIONGROUP_CRUL_NAME;
116 ACTIONGROUP_CRUL.actions = ACTIONSET_CRUL;
119 final static Logger logger = LoggerFactory.getLogger(AuthorizationCommon.class);
121 final public static String ROLE_TENANT_ADMINISTRATOR = "TENANT_ADMINISTRATOR";
122 final public static String ROLE_TENANT_READER = "TENANT_READER";
124 public static final String TENANT_MANAGER_USER = "tenantManager";
125 public static final String TENANT_MANAGER_SCREEN_NAME = TENANT_MANAGER_USER;
126 public static final String DEFAULT_TENANT_MANAGER_PASSWORD = "manage";
127 public static final String DEFAULT_TENANT_MANAGER_EMAIL = "tenantManager@collectionspace.org";
129 public static final String TENANT_ADMIN_ACCT_PREFIX = "admin@";
130 public static final String TENANT_READER_ACCT_PREFIX = "reader@";
131 public static final String ROLE_PREFIX = "ROLE_";
132 public static final String TENANT_ADMIN_ROLE_SUFFIX = "_TENANT_ADMINISTRATOR";
133 public static final String TENANT_READER_ROLE_SUFFIX = "_TENANT_READER";
134 public static final String DEFAULT_ADMIN_PASSWORD = "Administrator";
135 public static final String DEFAULT_READER_PASSWORD = "reader";
137 // SQL for init tasks
138 final private static String INSERT_ACCOUNT_ROLE_SQL_MYSQL =
139 "INSERT INTO accounts_roles(account_id, user_id, role_id, role_name, created_at)"
140 +" VALUES(?, ?, ?, ?, now())";
141 final private static String INSERT_ACCOUNT_ROLE_SQL_POSTGRES =
142 "INSERT INTO accounts_roles(HJID, account_id, user_id, role_id, role_name, created_at)"
143 +" VALUES(nextval('hibernate_sequence'), ?, ?, ?, ?, now())";
144 final private static String QUERY_USERS_SQL =
145 "SELECT username FROM users WHERE username LIKE '"
146 +TENANT_ADMIN_ACCT_PREFIX+"%' OR username LIKE '"+TENANT_READER_ACCT_PREFIX+"%'";
147 final private static String INSERT_USER_SQL =
148 "INSERT INTO users (username,passwd,created_at) VALUES (?,?,now())";
149 final private static String INSERT_ACCOUNT_SQL =
150 "INSERT INTO accounts_common "
151 + "(csid, email, userid, status, screen_name, metadata_protection, roles_protection, created_at) "
152 + "VALUES (?,?,?,'ACTIVE',?, 'immutable', 'immutable', now())";
154 // TENANT MANAGER specific SQL
155 final private static String QUERY_TENANT_MGR_USER_SQL =
156 "SELECT username FROM users WHERE username = '"+TENANT_MANAGER_USER+"'";
157 final private static String GET_TENANT_MGR_ROLE_SQL =
158 "SELECT csid from roles WHERE tenant_id='" + AuthN.ALL_TENANTS_MANAGER_TENANT_ID + "' and rolename=?";
160 public static final String IGNORE_TENANT_ID = null; // A null constant to indicate an empty/unused value for the tenant ID
163 public static String getTenantConfigMD5Hash(String tenantId) {
164 return tenantConfigMD5HashTable.get(tenantId);
167 public static String setTenantConfigMD5Hash(String tenantId, String md5hash) {
168 return tenantConfigMD5HashTable.put(tenantId, md5hash);
171 public static Role getRole(JPATransactionContext jpaTransactionContext, String tenantId, String displayName) {
174 String roleName = AuthorizationCommon.getQualifiedRoleName(tenantId, displayName);
175 role = AuthorizationStore.getRoleByName(jpaTransactionContext, roleName, tenantId);
181 * Create a new role instance to be persisted later.
189 public static Role createRole(String tenantId, String name, String description, boolean immutable) {
190 Role role = new Role();
192 role.setCreatedAtItem(new Date());
193 role.setDisplayName(name);
194 String roleName = AuthorizationCommon.getQualifiedRoleName(tenantId, name);
195 role.setRoleName(roleName);
196 String id = UUID.randomUUID().toString(); //FIXME: The qualified role name should be unique enough to use as an ID/key
198 role.setDescription(description);
199 role.setTenantId(tenantId);
200 if (immutable == true) {
201 role.setMetadataProtection(RoleClient.IMMUTABLE);
202 role.setPermsProtection(RoleClient.IMMUTABLE);
209 * Add permission to the Spring Security tables
210 * with assumption that resource is of type URI
211 * @param permission configuration
213 public static void addPermissionsForUri(JPATransactionContext jpaTransactionContext,
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());
231 boolean grant = perm.getEffect().equals(EffectType.PERMIT) ? true : false;
232 List<PermissionAction> permActions = perm.getAction();
233 ArrayList<CSpaceResource> resources = new ArrayList<CSpaceResource>();
234 for (PermissionAction permAction : permActions) {
235 CSpaceAction action = URIResourceImpl.getAction(permAction.getName());
236 URIResourceImpl uriRes = new URIResourceImpl(perm.getTenantId(), perm.getResourceName(), action);
237 resources.add(uriRes);
239 AuthZ.get().addPermissions(resources.toArray(new CSpaceResource[0]), principals.toArray(new String[0]), grant); // CSPACE-4967
240 jpaTransactionContext.setAclTablesUpdateFlag(true); // Tell the containing JPA transaction that we've committed changes to the Spring Tables
243 private static Connection getConnection(String databaseName) throws NamingException, SQLException {
244 return JDBCTools.getConnection(JDBCTools.CSPACE_DATASOURCE_NAME,
249 * Spring security seems to require that all of our role names start
250 * with the ROLE_PREFIX string.
252 public static String getQualifiedRoleName(String tenantId, String name) {
253 String result = name;
255 String qualifiedName = ROLE_PREFIX + tenantId.toUpperCase() + "_" + name.toUpperCase();
256 if (name.equals(qualifiedName) == false) {
257 result = qualifiedName;
263 private static ActionGroup getActionGroup(String actionGroupStr) {
264 ActionGroup result = null;
266 if (actionGroupStr.equalsIgnoreCase(ACTIONGROUP_CRUDL_NAME)) {
267 result = ACTIONGROUP_CRUDL;
268 } else if (actionGroupStr.equalsIgnoreCase(ACTIONGROUP_RL_NAME)) {
269 result = ACTIONGROUP_RL;
270 } else if (actionGroupStr.equalsIgnoreCase(ACTIONGROUP_CRUL_NAME)) {
271 result = ACTIONGROUP_CRUL;
277 public static Permission createPermission(String tenantId,
280 String actionGroupStr,
282 Permission result = null;
284 ActionGroup actionGroup = getActionGroup(actionGroupStr);
285 result = createPermission(tenantId, resourceName, description, actionGroup, immutable);
290 private static Permission createPermission(String tenantId,
293 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);
315 perm.setMetadataProtection(PermissionClient.IMMUTABLE);
316 perm.setActionsProtection(PermissionClient.IMMUTABLE);
322 private static Permission createWorkflowPermission(TenantBindingType tenantBinding,
323 ServiceBindingType serviceBinding,
324 String transitionVerb,
325 ActionGroup actionGroup,
328 Permission result = null;
329 String workFlowServiceSuffix;
330 String transitionName;
331 if (transitionVerb != null) {
332 transitionName = transitionVerb;
333 workFlowServiceSuffix = WorkflowClient.SERVICE_AUTHZ_SUFFIX;
335 transitionName = ""; //since the transitionDef was null, we're assuming that this is the base workflow permission to be created
336 workFlowServiceSuffix = WorkflowClient.SERVICE_PATH;
339 String tenantId = tenantBinding.getId();
340 String resourceName = "/"
341 + serviceBinding.getName().toLowerCase().trim()
342 + workFlowServiceSuffix
344 String description = "A generated workflow permission for actiongroup " + actionGroup.name;
345 result = createPermission(tenantId, resourceName, description, actionGroup, immutable);
347 if (logger.isDebugEnabled() == true) {
348 logger.debug("Generated a workflow permission: "
349 + result.getResourceName()
350 + ":" + transitionName
351 + ":" + "tenant id=" + result.getTenantId()
352 + ":" + actionGroup.name);
358 private static PermissionRole createPermissionRole(
359 Permission permission,
361 boolean enforceTenancy) throws DocumentException
363 PermissionRole permRole = new PermissionRole();
366 // Check to see if the tenant ID of the permission and the tenant ID of the role match
368 boolean tenantIdsMatch = role.getTenantId().equalsIgnoreCase(permission.getTenantId());
369 if (tenantIdsMatch == false && enforceTenancy == false) {
370 tenantIdsMatch = true; // If we don't need to enforce tenancy then we'll just consider them matched.
373 if (tenantIdsMatch == true) {
374 permRole.setSubject(SubjectType.ROLE);
376 // Set of the permission value list of the permrole
378 List<PermissionValue> permValues = new ArrayList<PermissionValue>();
379 PermissionValue permValue = new PermissionValue();
380 permValue.setPermissionId(permission.getCsid());
381 permValue.setResourceName(permission.getResourceName().toLowerCase());
382 permValue.setActionGroup(permission.getActionGroup());
383 permValues.add(permValue);
384 permRole.setPermission(permValues);
386 // Set of the role value list of the permrole
388 List<RoleValue> roleValues = new ArrayList<RoleValue>();
389 RoleValue rv = new RoleValue();
390 // This needs to use the qualified name, not the display name
391 rv.setRoleName(role.getRoleName());
392 rv.setRoleId(role.getCsid());
394 permRole.setRole(roleValues);
396 String errMsg = "The tenant ID of the role: " + role.getTenantId()
397 + " did not match the tenant ID of the permission: " + permission.getTenantId();
398 throw new DocumentException(errMsg);
404 private static Hashtable<String, String> getTenantNamesFromConfig(TenantBindingConfigReaderImpl tenantBindingConfigReader) {
406 // Note that this only handles tenants not marked as "createDisabled"
407 Hashtable<String, TenantBindingType> tenantBindings =
408 tenantBindingConfigReader.getTenantBindings();
409 Hashtable<String, String> tenantInfo = new Hashtable<String, String>();
410 for (TenantBindingType tenantBinding : tenantBindings.values()) {
411 String tId = tenantBinding.getId();
412 String tName = tenantBinding.getName();
413 tenantInfo.put(tId, tName);
414 if (logger.isDebugEnabled()) {
415 logger.debug("getTenantNamesFromConfig found configured tenant id: "+tId+" name: "+tName);
421 private static ArrayList<String> compileExistingTenants(Connection conn, Hashtable<String, String> tenantInfo)
422 throws SQLException, Exception {
423 Statement stmt = null;
424 ArrayList<String> existingTenants = new ArrayList<String>();
425 // First find or create the tenants
426 final String queryTenantSQL = "SELECT id,name FROM tenants";
428 stmt = conn.createStatement();
429 ResultSet rs = stmt.executeQuery(queryTenantSQL);
431 String tId = rs.getString("id");
432 String tName = rs.getString("name");
433 if(tenantInfo.containsKey(tId)) {
434 existingTenants.add(tId);
435 if(!tenantInfo.get(tId).equalsIgnoreCase(tName)) {
436 logger.warn("Configured name for tenant: "
437 +tId+" in repository: "+tName
438 +" does not match config'd name: "+ tenantInfo.get(tId));
443 } catch(Exception e) {
450 return existingTenants;
453 private static ArrayList<String> findOrCreateDefaultUsers(Connection conn, Hashtable<String, String> tenantInfo)
454 throws SQLException, Exception {
455 // Second find or create the users
456 Statement stmt = null;
457 PreparedStatement pstmt = null;
458 ArrayList<String> usersInRepo = new ArrayList<String>();
460 stmt = conn.createStatement();
461 ResultSet rs = stmt.executeQuery(QUERY_USERS_SQL);
463 String uName = rs.getString("username");
464 usersInRepo.add(uName);
467 pstmt = conn.prepareStatement(INSERT_USER_SQL); // create a statement
468 for(String tName : tenantInfo.values()) {
469 String adminAcctName = getDefaultAdminUserID(tName);
470 if(!usersInRepo.contains(adminAcctName)) {
471 String secEncPasswd = SecurityUtils.createPasswordHash(DEFAULT_ADMIN_PASSWORD);
472 pstmt.setString(1, adminAcctName); // set username param
473 pstmt.setString(2, secEncPasswd); // set passwd param
474 if (logger.isDebugEnabled()) {
475 logger.debug("createDefaultUsersAndAccounts adding user: "
476 +adminAcctName+" for tenant: "+tName);
478 pstmt.executeUpdate();
479 } else if (logger.isDebugEnabled()) {
480 logger.debug("createDefaultUsersAndAccounts: user: "+adminAcctName
481 +" already exists - skipping.");
485 String readerAcctName = getDefaultReaderUserID(tName);
486 if(!usersInRepo.contains(readerAcctName)) {
487 String secEncPasswd = SecurityUtils.createPasswordHash(DEFAULT_READER_PASSWORD);
488 pstmt.setString(1, readerAcctName); // set username param
489 pstmt.setString(2, secEncPasswd); // set passwd param
490 if (logger.isDebugEnabled()) {
491 logger.debug("createDefaultUsersAndAccounts adding user: "
492 +readerAcctName+" for tenant: "+tName);
494 pstmt.executeUpdate();
495 } else if (logger.isDebugEnabled()) {
496 logger.debug("createDefaultUsersAndAccounts: user: "+readerAcctName
497 +" already exists - skipping.");
501 } catch(Exception e) {
512 private static void findOrCreateDefaultAccounts(Connection conn, Hashtable<String, String> tenantInfo,
513 ArrayList<String> usersInRepo,
514 Hashtable<String, String> tenantAdminAcctCSIDs, Hashtable<String, String> tenantReaderAcctCSIDs)
515 throws SQLException, Exception {
516 // Third, create the accounts. Assume that if the users were already there,
517 // then the accounts were as well
518 PreparedStatement pstmt = null;
520 pstmt = conn.prepareStatement(INSERT_ACCOUNT_SQL); // create a statement
521 for(String tId : tenantInfo.keySet()) {
522 String tName = tenantInfo.get(tId);
523 String adminCSID = UUID.randomUUID().toString();
524 tenantAdminAcctCSIDs.put(tId, adminCSID);
525 String adminAcctName = getDefaultAdminUserID(tName);
526 if(!usersInRepo.contains(adminAcctName)) {
527 pstmt.setString(1, adminCSID); // set csid param
528 pstmt.setString(2, adminAcctName); // set email param (bogus)
529 pstmt.setString(3, adminAcctName); // set userid param
530 pstmt.setString(4, "Administrator");// set screen name param
531 if (logger.isDebugEnabled()) {
532 logger.debug("createDefaultAccounts adding account: "
533 +adminAcctName+" for tenant: "+tName);
535 pstmt.executeUpdate();
536 } else if (logger.isDebugEnabled()) {
537 logger.debug("createDefaultAccounts: user: "+adminAcctName
538 +" already exists - skipping account generation.");
541 String readerCSID = UUID.randomUUID().toString();
542 tenantReaderAcctCSIDs.put(tId, readerCSID);
543 String readerAcctName = getDefaultReaderUserID(tName);
544 if(!usersInRepo.contains(readerAcctName)) {
545 pstmt.setString(1, readerCSID); // set csid param
546 pstmt.setString(2, readerAcctName); // set email param (bogus)
547 pstmt.setString(3, readerAcctName); // set userid param
548 pstmt.setString(4, "Reader"); // set screen name param
549 if (logger.isDebugEnabled()) {
550 logger.debug("createDefaultAccounts adding account: "
551 +readerAcctName+" for tenant: "+tName);
553 pstmt.executeUpdate();
554 } else if (logger.isDebugEnabled()) {
555 logger.debug("createDefaultAccounts: user: "+readerAcctName
556 +" already exists - skipping account creation.");
560 } catch(Exception e) {
568 private static boolean findOrCreateTenantManagerUserAndAccount(Connection conn)
569 throws SQLException, Exception {
570 // Find or create the special tenant manager account.
571 // Later can make the user name for tenant manager be configurable, settable.
572 Statement stmt = null;
573 PreparedStatement pstmt = null;
574 boolean created = false;
576 boolean foundTMgrUser = false;
577 stmt = conn.createStatement();
578 ResultSet rs = stmt.executeQuery(QUERY_TENANT_MGR_USER_SQL);
579 // Should only find one - only consider it
581 String uName = rs.getString("username");
582 foundTMgrUser = uName.equals(TENANT_MANAGER_USER);
586 pstmt = conn.prepareStatement(INSERT_USER_SQL); // create a statement
587 String secEncPasswd = SecurityUtils.createPasswordHash(DEFAULT_TENANT_MANAGER_PASSWORD);
588 pstmt.setString(1, TENANT_MANAGER_USER); // set username param
589 pstmt.setString(2, secEncPasswd); // set passwd param
590 if (logger.isDebugEnabled()) {
591 logger.debug("findOrCreateTenantManagerUserAndAccount adding tenant manager user: "
592 +TENANT_MANAGER_USER);
594 pstmt.executeUpdate();
596 // Now create the account to match
597 pstmt = conn.prepareStatement(INSERT_ACCOUNT_SQL); // create a statement
598 pstmt.setString(1, AuthN.TENANT_MANAGER_ACCT_ID); // set csid param
599 pstmt.setString(2, DEFAULT_TENANT_MANAGER_EMAIL); // set email param (bogus)
600 pstmt.setString(3, TENANT_MANAGER_USER); // set userid param
601 pstmt.setString(4, TENANT_MANAGER_SCREEN_NAME);// set screen name param
602 if (logger.isDebugEnabled()) {
603 logger.debug("findOrCreateTenantManagerUserAndAccount adding tenant manager account: "
604 +TENANT_MANAGER_USER);
606 pstmt.executeUpdate();
609 } else if (logger.isDebugEnabled()) {
610 logger.debug("findOrCreateTenantManagerUserAndAccount: tenant manager: "+TENANT_MANAGER_USER
611 +" already exists.");
613 } catch(Exception e) {
624 private static void bindDefaultAccountsToTenants(Connection conn, DatabaseProductType databaseProductType,
625 Hashtable<String, String> tenantInfo, ArrayList<String> usersInRepo,
626 Hashtable<String, String> tenantAdminAcctCSIDs, Hashtable<String, String> tenantReaderAcctCSIDs)
627 throws SQLException, Exception {
628 // Fourth, bind accounts to tenants. Assume that if the users were already there,
629 // then the accounts were bound to tenants correctly
630 PreparedStatement pstmt = null;
632 String insertAccountTenantSQL;
633 if (databaseProductType == DatabaseProductType.MYSQL) {
634 insertAccountTenantSQL =
635 "INSERT INTO accounts_tenants (TENANTS_ACCOUNTS_COMMON_CSID,tenant_id) "
637 } else if (databaseProductType == DatabaseProductType.POSTGRESQL) {
638 insertAccountTenantSQL =
639 "INSERT INTO accounts_tenants (HJID, TENANTS_ACCOUNTS_COMMON_CSID,tenant_id) "
640 + " VALUES(nextval('hibernate_sequence'), ?, ?)";
642 throw new Exception("Unrecognized database system.");
644 pstmt = conn.prepareStatement(insertAccountTenantSQL); // create a statement
645 for(String tId : tenantInfo.keySet()) {
646 String tName = tenantInfo.get(tId);
647 if(!usersInRepo.contains(getDefaultAdminUserID(tName))) {
648 String adminAcct = tenantAdminAcctCSIDs.get(tId);
649 pstmt.setString(1, adminAcct); // set acct CSID param
650 pstmt.setString(2, tId); // set tenant_id param
651 if (logger.isDebugEnabled()) {
652 logger.debug("createDefaultAccounts binding account id: "
653 +adminAcct+" to tenant id: "+tId);
655 pstmt.executeUpdate();
657 if(!usersInRepo.contains(getDefaultReaderUserID(tName))) {
658 String readerAcct = tenantReaderAcctCSIDs.get(tId);
659 pstmt.setString(1, readerAcct); // set acct CSID param
660 pstmt.setString(2, tId); // set tenant_id param
661 if (logger.isDebugEnabled()) {
662 logger.debug("createDefaultAccounts binding account id: "
663 +readerAcct+" to tenant id: "+tId);
665 pstmt.executeUpdate();
669 } catch(Exception e) {
678 * Creates the default Admin and Reader roles for all the configured tenants.
680 * Returns the CSID of the Spring Admin role.
684 * @param tenantAdminRoleCSIDs
685 * @param tenantReaderRoleCSIDs
687 * @throws SQLException
690 private static String findOrCreateDefaultRoles(Connection conn, Hashtable<String, String> tenantInfo,
691 Hashtable<String, String> tenantAdminRoleCSIDs, Hashtable<String, String> tenantReaderRoleCSIDs)
692 throws SQLException, Exception {
694 String springAdminRoleCSID = null;
695 Statement stmt = null;
696 PreparedStatement pstmt = null;
699 // Look for the Spring Security admin role. If not found, create it.
701 final String querySpringRole = String.format("SELECT csid from roles WHERE rolename='%s'", AuthN.ROLE_SPRING_ADMIN_NAME);
702 stmt = conn.createStatement();
703 ResultSet rs = stmt.executeQuery(querySpringRole);
705 springAdminRoleCSID = rs.getString(1);
706 if (logger.isDebugEnabled()) {
707 logger.debug("createDefaultAccounts found Spring Admin role: " + springAdminRoleCSID);
710 final String insertSpringAdminRoleSQL = String.format(
711 "INSERT INTO roles (csid, rolename, displayName, rolegroup, created_at, tenant_id) VALUES ('%s', '%s', '%s', '%s', now(), '%s')",
712 AuthN.ROLE_SPRING_ADMIN_ID, AuthN.ROLE_SPRING_ADMIN_NAME, AuthN.SPRING_ADMIN_USER, AuthN.ROLE_SPRING_GROUP_NAME, AuthN.ADMIN_TENANT_ID);
713 stmt.executeUpdate(insertSpringAdminRoleSQL);
714 springAdminRoleCSID = AuthN.ROLE_SPRING_ADMIN_ID;
720 // Look for and save each tenants default Admin and Reader roles
722 final String getRoleCSIDSql = "SELECT csid from roles WHERE tenant_id=? and rolename=?";
723 pstmt = conn.prepareStatement(getRoleCSIDSql); // create a statement
724 for (String tenantId : tenantInfo.keySet()) {
726 // Look for the default Admin role
728 pstmt.setString(1, tenantId);
729 pstmt.setString(2, getDefaultAdminRole(tenantId));
730 rs = pstmt.executeQuery();
731 // extract data from the ResultSet
733 throw new RuntimeException("Cannot find role: " + getDefaultAdminRole(tenantId)
734 + " for tenant id: " + tenantId + " in roles!");
736 String tenantAdminRoleCSID = rs.getString(1); // First column (#1) is the CSID
737 tenantAdminRoleCSIDs.put(tenantId, tenantAdminRoleCSID);
741 // Look for the default Reader role
743 pstmt.setString(1, tenantId); // set tenant_id param
744 pstmt.setString(2, getDefaultReaderRole(tenantId)); // set rolename param
745 rs = pstmt.executeQuery();
746 // extract data from the ResultSet
748 throw new RuntimeException("Cannot find role: " + getDefaultReaderRole(tenantId)
749 + " for tenant id: " + tenantId + " in roles!");
751 String tenantReaderRoleCSID = rs.getString(1);
752 tenantReaderRoleCSIDs.put(tenantId, tenantReaderRoleCSID);
756 } catch(Exception e) {
759 if (stmt != null) stmt.close();
760 if (pstmt != null) pstmt.close();
763 return springAdminRoleCSID;
766 private static String findTenantManagerRole(Connection conn )
767 throws SQLException, RuntimeException, Exception {
768 String tenantMgrRoleCSID = null;
769 PreparedStatement pstmt = null;
771 String rolename = getQualifiedRoleName(AuthN.ALL_TENANTS_MANAGER_TENANT_ID,
772 AuthN.ROLE_ALL_TENANTS_MANAGER);
773 pstmt = conn.prepareStatement(GET_TENANT_MGR_ROLE_SQL); // create a statement
775 pstmt.setString(1, rolename); // set rolename param
776 rs = pstmt.executeQuery();
778 tenantMgrRoleCSID = rs.getString(1);
779 if (logger.isDebugEnabled()) {
780 logger.debug("findTenantManagerRole found Tenant Mgr role: "
785 } catch(Exception e) {
791 if(tenantMgrRoleCSID==null)
792 throw new RuntimeException("findTenantManagerRole: Cound not find tenant Manager Role!");
793 return tenantMgrRoleCSID;
796 private static void bindAccountsToRoles(Connection conn, DatabaseProductType databaseProductType,
797 Hashtable<String, String> tenantInfo, ArrayList<String> usersInRepo,
798 String springAdminRoleCSID,
799 Hashtable<String, String> tenantAdminRoleCSIDs, Hashtable<String, String> tenantReaderRoleCSIDs,
800 Hashtable<String, String> tenantAdminAcctCSIDs, Hashtable<String, String> tenantReaderAcctCSIDs)
801 throws SQLException, Exception {
802 // Sixth, bind the accounts to roles. If the users already existed,
803 // we'll assume they were set up correctly.
804 PreparedStatement pstmt = null;
806 String insertAccountRoleSQL;
807 if (databaseProductType == DatabaseProductType.POSTGRESQL) {
808 insertAccountRoleSQL = INSERT_ACCOUNT_ROLE_SQL_POSTGRES;
810 throw new Exception("Unrecognized database system.");
813 pstmt = conn.prepareStatement(insertAccountRoleSQL); // create a statement
814 for (String tId : tenantInfo.keySet()) {
815 String adminUserId = getDefaultAdminUserID(tenantInfo.get(tId));
816 if (!usersInRepo.contains(adminUserId)) {
817 String adminAcct = tenantAdminAcctCSIDs.get(tId);
818 String adminRoleId = tenantAdminRoleCSIDs.get(tId);
819 pstmt.setString(1, adminAcct); // set acct CSID param
820 pstmt.setString(2, adminUserId); // set user_id param
821 pstmt.setString(3, adminRoleId); // set role_id param
822 pstmt.setString(4, getDefaultAdminRole(tId)); // set rolename param
823 pstmt.executeUpdate();
825 // Now add the Spring Admin Role to the admin accounts
827 pstmt.setString(3, springAdminRoleCSID); // set role_id param
828 pstmt.setString(4, AuthN.ROLE_SPRING_ADMIN_NAME); // set rolename param
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 pstmt.executeUpdate();
843 } catch(Exception e) {
852 private static void bindTenantManagerAccountRole(Connection conn, DatabaseProductType databaseProductType,
853 String tenantManagerUserID, String tenantManagerAccountID, String tenantManagerRoleID, String tenantManagerRoleName )
854 throws SQLException, Exception {
855 PreparedStatement pstmt = null;
857 String insertAccountRoleSQL;
858 if (databaseProductType == DatabaseProductType.MYSQL) {
859 insertAccountRoleSQL = INSERT_ACCOUNT_ROLE_SQL_MYSQL;
860 } else if (databaseProductType == DatabaseProductType.POSTGRESQL) {
861 insertAccountRoleSQL = INSERT_ACCOUNT_ROLE_SQL_POSTGRES;
863 throw new Exception("Unrecognized database system.");
865 if (logger.isDebugEnabled()) {
866 logger.debug("bindTenantManagerAccountRole binding account to role with SQL:\n"
867 +insertAccountRoleSQL);
869 pstmt = conn.prepareStatement(insertAccountRoleSQL); // create a statement
870 pstmt.setString(1, tenantManagerAccountID); // set acct CSID param
871 pstmt.setString(2, tenantManagerUserID); // set user_id param
872 pstmt.setString(3, tenantManagerRoleID); // set role_id param
873 pstmt.setString(4, tenantManagerRoleName); // set rolename param
874 pstmt.executeUpdate();
876 /* At this point, tenant manager should not need the Spring Admin Role
877 pstmt.setString(3, springAdminRoleCSID); // set role_id param
878 pstmt.setString(4, SPRING_ADMIN_ROLE); // set rolename param
879 if (logger.isDebugEnabled()) {
880 logger.debug("createDefaultAccounts binding account: "
881 +adminUserId+" to Spring Admin role: "+springAdminRoleCSID);
883 pstmt.executeUpdate();
887 } catch(Exception e) {
896 * Using the tenant bindings, ensure there are corresponding Tenant records (db columns).
898 //FIXME: This code should be using JPA objects and JPATransactionContext, not raw SQL.
899 public static void createTenants(
900 TenantBindingConfigReaderImpl tenantBindingConfigReader,
901 DatabaseProductType databaseProductType,
902 String cspaceDatabaseName) throws Exception {
903 logger.debug("ServiceMain.createTenants starting...");
904 Hashtable<String, String> tenantInfo = getTenantNamesFromConfig(tenantBindingConfigReader);
905 Connection conn = null;
907 conn = getConnection(cspaceDatabaseName);
908 ArrayList<String> existingTenants = compileExistingTenants(conn, tenantInfo);
910 // Note that this only creates tenants not marked as "createDisabled"
911 createMissingTenants(conn, tenantInfo, existingTenants);
912 } catch (Exception e) {
913 logger.debug("Exception in createTenants: " + e.getLocalizedMessage());
920 } catch (SQLException sqle) {
921 if (logger.isDebugEnabled()) {
922 logger.debug("SQL Exception closing statement/connection: " + sqle.getLocalizedMessage());
930 * @param tenantBindingConfigReader
931 * @param databaseProductType
932 * @param cspaceDatabaseName
935 //FIXME: This code should be using the JPA objects and JPATransactionContext, not raw SQL.
936 public static void createDefaultAccounts(
937 TenantBindingConfigReaderImpl tenantBindingConfigReader,
938 DatabaseProductType databaseProductType,
939 String cspaceDatabaseName) throws Exception {
941 logger.debug("ServiceMain.createDefaultAccounts starting...");
943 Hashtable<String, String> tenantInfo = getTenantNamesFromConfig(tenantBindingConfigReader);
944 Connection conn = null;
945 // TODO - need to put in tests for existence first.
946 // We could just look for the accounts per tenant up front, and assume that
947 // the rest is there if the accounts are.
948 // Could add a sql script to remove these if need be - Spring only does roles,
949 // and we're not touching that, so we could safely toss the
950 // accounts, users, account-tenants, account-roles, and start over.
952 conn = getConnection(cspaceDatabaseName);
954 ArrayList<String> usersInRepo = findOrCreateDefaultUsers(conn, tenantInfo);
956 Hashtable<String, String> tenantAdminAcctCSIDs = new Hashtable<String, String>();
957 Hashtable<String, String> tenantReaderAcctCSIDs = new Hashtable<String, String>();
958 findOrCreateDefaultAccounts(conn, tenantInfo, usersInRepo,
959 tenantAdminAcctCSIDs, tenantReaderAcctCSIDs);
961 bindDefaultAccountsToTenants(conn, databaseProductType, tenantInfo, usersInRepo,
962 tenantAdminAcctCSIDs, tenantReaderAcctCSIDs);
964 Hashtable<String, String> tenantAdminRoleCSIDs = new Hashtable<String, String>();
965 Hashtable<String, String> tenantReaderRoleCSIDs = new Hashtable<String, String>();
966 String springAdminRoleCSID = findOrCreateDefaultRoles(conn, tenantInfo,
967 tenantAdminRoleCSIDs, tenantReaderRoleCSIDs);
969 bindAccountsToRoles(conn, databaseProductType,
970 tenantInfo, usersInRepo, springAdminRoleCSID,
971 tenantAdminRoleCSIDs, tenantReaderRoleCSIDs,
972 tenantAdminAcctCSIDs, tenantReaderAcctCSIDs);
974 boolean createdTenantMgrAccount = findOrCreateTenantManagerUserAndAccount(conn);
975 if (createdTenantMgrAccount) {
976 // If we created the account, we need to create the bindings. Otherwise, assume they
977 // are all set (from previous initialization).
978 String tenantManagerRoleCSID = findTenantManagerRole(conn);
979 bindTenantManagerAccountRole(conn, databaseProductType,
980 TENANT_MANAGER_USER, AuthN.TENANT_MANAGER_ACCT_ID,
981 tenantManagerRoleCSID, AuthN.ROLE_ALL_TENANTS_MANAGER);
983 } catch (Exception e) {
984 logger.debug("Exception in createDefaultAccounts: " + e.getLocalizedMessage());
991 } catch (SQLException sqle) {
992 if (logger.isDebugEnabled()) {
993 logger.debug("SQL Exception closing statement/connection: " + sqle.getLocalizedMessage());
999 private static String getDefaultAdminRole(String tenantId) {
1000 return ROLE_PREFIX + tenantId + TENANT_ADMIN_ROLE_SUFFIX;
1003 private static String getDefaultReaderRole(String tenantId) {
1004 return ROLE_PREFIX+tenantId+TENANT_READER_ROLE_SUFFIX;
1007 private static String getDefaultAdminUserID(String tenantName) {
1008 return TENANT_ADMIN_ACCT_PREFIX + tenantName;
1011 private static String getDefaultReaderUserID(String tenantName) {
1012 return TENANT_READER_ACCT_PREFIX + tenantName;
1015 static private PermissionAction createPermissionAction(Permission perm,
1016 ActionType actionType) {
1017 PermissionAction pa = new PermissionAction();
1019 CSpaceAction action = URIResourceImpl.getAction(actionType);
1020 URIResourceImpl uriRes = new URIResourceImpl(perm.getTenantId(),
1021 perm.getResourceName(), action);
1022 pa.setName(actionType);
1023 pa.setObjectIdentity(uriRes.getHashedId().toString());
1024 pa.setObjectIdentityResource(uriRes.getId());
1029 private static HashSet<String> getTransitionVerbList(TenantBindingType tenantBinding, ServiceBindingType serviceBinding) {
1030 HashSet<String> result = new HashSet<String>();
1032 TransitionDefList transitionDefList = getTransitionDefList(tenantBinding, serviceBinding);
1033 for (TransitionDef transitionDef : transitionDefList.getTransitionDef()) {
1034 String transitionVerb = transitionDef.getName();
1035 String[] tokens = transitionVerb.split("_"); // Split the verb into words. The workflow verbs are compound words combined with the '_' character.
1036 result.add(tokens[0]); // We only care about the first word.
1042 private static TransitionDefList getTransitionDefList(TenantBindingType tenantBinding, ServiceBindingType serviceBinding) {
1043 TransitionDefList result = null;
1045 String serviceObjectName = serviceBinding.getObject().getName();
1047 @SuppressWarnings("rawtypes")
1048 DocumentHandler docHandler = ServiceConfigUtils.createDocumentHandlerInstance(
1049 tenantBinding, serviceBinding);
1050 Lifecycle lifecycle = docHandler.getLifecycle(serviceObjectName);
1051 if (lifecycle != null) {
1052 result = lifecycle.getTransitionDefList();
1054 } catch (Exception e) {
1055 // Ignore this exception and return an empty non-null TransitionDefList
1058 if (result == null) {
1059 if (serviceBinding.getType().equalsIgnoreCase(ServiceBindingUtils.SERVICE_TYPE_SECURITY) == false) {
1060 logger.debug("Could not retrieve a lifecycle transition definition list from: "
1061 + serviceBinding.getName()
1062 + " with tenant ID = "
1063 + tenantBinding.getId());
1065 // return an empty list
1066 result = new TransitionDefList();
1068 logger.debug("Successfully retrieved a lifecycle transition definition list from: "
1069 + serviceBinding.getName()
1070 + " with tenant ID = "
1071 + tenantBinding.getId());
1078 * Creates the immutable workflow permission sets for the default admin and reader roles.
1080 * @param tenantBindingConfigReader
1081 * @param databaseProductType
1082 * @param cspaceDatabaseName
1085 public static void createDefaultWorkflowPermissions(
1086 JPATransactionContext jpaTransactionContext,
1087 TenantBindingConfigReaderImpl tenantBindingConfigReader,
1088 DatabaseProductType databaseProductType,
1089 String cspaceDatabaseName) throws Exception
1091 java.util.logging.Logger logger = java.util.logging.Logger.getAnonymousLogger();
1093 AuthZ.get().login(); //login to Spring Security manager
1096 Hashtable<String, TenantBindingType> tenantBindings = tenantBindingConfigReader.getTenantBindings();
1097 for (String tenantId : tenantBindings.keySet()) {
1098 logger.info(String.format("Creating/verifying workflow permissions for tenant ID=%s.", tenantId));
1099 TenantBindingType tenantBinding = tenantBindings.get(tenantId);
1100 if (tenantBinding.isConfigChangedSinceLastStart() == false) {
1101 continue; // skip the rest of the loop and go to the next tenant
1104 Role adminRole = AuthorizationCommon.getRole(jpaTransactionContext, tenantBinding.getId(), ROLE_TENANT_ADMINISTRATOR);
1105 Role readonlyRole = AuthorizationCommon.getRole(jpaTransactionContext, tenantBinding.getId(), ROLE_TENANT_READER);
1107 if (adminRole == null || readonlyRole == null) {
1108 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.");
1110 throw new RuntimeException("One or more of the required default CollectionSpace administrator roles is missing or was never created.");
1113 for (ServiceBindingType serviceBinding : tenantBinding.getServiceBindings()) {
1114 String prop = ServiceBindingUtils.getPropertyValue(serviceBinding, REFRESH_AUTHZ_PROP);
1115 if (prop == null ? true : Boolean.parseBoolean(prop)) {
1117 jpaTransactionContext.beginTransaction();
1118 HashSet<String> transitionVerbList = getTransitionVerbList(tenantBinding, serviceBinding);
1119 for (String transitionVerb : transitionVerbList) {
1121 // Create the permission for the admin role
1122 Permission adminPerm = createWorkflowPermission(tenantBinding, serviceBinding, transitionVerb, ACTIONGROUP_CRUDL, true);
1123 persist(jpaTransactionContext, adminPerm, adminRole, true, ACTIONGROUP_CRUDL);
1125 // Create the permission for the read-only role
1126 Permission readonlyPerm = createWorkflowPermission(tenantBinding, serviceBinding, transitionVerb, ACTIONGROUP_RL, true);
1127 persist(jpaTransactionContext, readonlyPerm, readonlyRole, true, ACTIONGROUP_RL); // Persist/store the permission and permrole records and related Spring Security info
1129 jpaTransactionContext.commitTransaction();
1130 } catch (IllegalStateException e) {
1131 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.
1132 } catch (Exception x) {
1133 jpaTransactionContext.markForRollback();
1136 logger.warning("AuthZ refresh service binding property is set to FALSE so default permissions will NOT be refreshed for: "
1137 + serviceBinding.getName());
1141 } catch (Exception e) {
1142 jpaTransactionContext.markForRollback();
1143 logger.fine("Caught exception and rolling back permission creation: " + e.getMessage());
1148 private static void createMissingTenants(Connection conn, Hashtable<String, String> tenantInfo,
1149 ArrayList<String> existingTenants) throws SQLException, Exception {
1150 // Need to define and look for a createDisabled attribute in tenant config
1151 final String insertTenantSQL =
1152 "INSERT INTO tenants (id,name,authorities_initialized,disabled,created_at) VALUES (?,?,FALSE,FALSE,now())";
1153 PreparedStatement pstmt = null;
1155 pstmt = conn.prepareStatement(insertTenantSQL); // create a statement
1156 for(String tId : tenantInfo.keySet()) {
1157 if(existingTenants.contains(tId)) {
1158 if (logger.isDebugEnabled()) {
1159 logger.debug("createMissingTenants: tenant exists (skipping): "
1160 +tenantInfo.get(tId));
1164 pstmt.setString(1, tId); // set id param
1165 pstmt.setString(2, tenantInfo.get(tId)); // set name param
1166 if (logger.isDebugEnabled()) {
1167 logger.debug("createMissingTenants adding entry for tenant: "+tId);
1169 pstmt.executeUpdate();
1172 } catch(Exception e) {
1180 public static String getPersistedMD5Hash(String tenantId, String cspaceDatabaseName) throws Exception {
1181 String result = null;
1183 // First find or create the tenants
1184 final String queryTenantSQL = String.format("SELECT id, name, config_md5hash FROM tenants WHERE id = '%s'", tenantId);
1186 Statement stmt = null;
1190 conn = getConnection(cspaceDatabaseName);
1191 stmt = conn.createStatement();
1192 ResultSet rs = stmt.executeQuery(queryTenantSQL);
1195 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'.",
1196 tenantId, cspaceDatabaseName);
1197 throw new Exception(errMsg);
1199 String tId = rs.getString("id"); // for debugging only
1200 String tName = rs.getString("name"); // for debugging only
1201 result = rs.getString("config_md5hash");
1205 } catch(Exception e) {
1208 if (stmt != null) stmt.close();
1214 private static PermissionRoleRel findPermRoleRel(
1215 JPATransactionContext jpaTransactionContext,
1216 String permissionId,
1218 PermissionRoleRel result = null;
1221 String whereClause = "where permissionId = :id and roleId = :roleId";
1222 HashMap<String, Object> params = new HashMap<String, Object>();
1223 params.put("id", permissionId);
1224 params.put("roleId", RoleId);
1226 result = (PermissionRoleRel) JpaStorageUtils.getEntity(jpaTransactionContext,
1227 PermissionRoleRel.class.getCanonicalName(), whereClause, params);
1228 } catch (Exception e) {
1229 //Do nothing. Will return null;
1236 * Persists the Permission, PermissionRoleRel, and Spring Security table entries all in one transaction
1238 private static void persist(JPATransactionContext jpaTransactionContext, Permission permission, Role role, boolean enforceTenancy, ActionGroup actionGroup) throws Exception {
1239 AuthorizationStore authzStore = new AuthorizationStore();
1240 // First persist the Permission record
1241 authzStore.store(jpaTransactionContext, permission);
1243 // If the PermRoleRel doesn't already exists then relate the permission and the role in a new PermissionRole (the service payload)
1244 // Create a PermissionRoleRel (the database relation table for the permission and role)
1245 PermissionRoleRel permRoleRel = findPermRoleRel(jpaTransactionContext, permission.getCsid(), role.getCsid());
1246 if (permRoleRel == null) {
1247 PermissionRole permRole = createPermissionRole(permission, role, enforceTenancy);
1248 List<PermissionRoleRel> permRoleRels = new ArrayList<PermissionRoleRel>();
1249 PermissionRoleUtil.buildPermissionRoleRel(jpaTransactionContext, permRole, SubjectType.ROLE, permRoleRels,
1250 false /*not for delete*/, role.getTenantId());
1251 for (PermissionRoleRel prr : permRoleRels) {
1252 authzStore.store(jpaTransactionContext, prr);
1254 Profiler profiler = new Profiler(AuthorizationCommon.class, 2);
1256 // Add a corresponding entry in the Spring Security Tables
1257 addPermissionsForUri(jpaTransactionContext, permission, permRole);
1259 logger.debug("Finished full perm generation for "
1260 + ":" + permission.getTenantId()
1261 + ":" + permission.getResourceName()
1262 + ":" + actionGroup.getName()
1263 + ":" + profiler.getCumulativeTime());
1268 public static boolean hasTokenExpired(EmailConfig emailConfig, Token token) throws NoSuchAlgorithmException {
1269 boolean result = false;
1271 int maxConfigSeconds = emailConfig.getPasswordResetConfig().getTokenExpirationSeconds().intValue();
1272 int maxTokenSeconds = token.getExpireSeconds().intValue();
1274 long createdTime = token.getCreatedAtItem().getTime();
1275 long configExpirationTime = createdTime + maxConfigSeconds * 1000; // the current tenant config for how long a token stays valid
1276 long tokenDefinedExirationTime = createdTime + maxTokenSeconds * 1000; // the tenant config for how long a token stays valid when the token was created.
1278 if (configExpirationTime != tokenDefinedExirationTime) {
1279 String msg = String.format("The configured expiration time for the token = '%s' changed from when the token was created.",
1284 // Note: the current tenant bindings config for expiration takes precedence over the config used to create the token.
1286 if (System.currentTimeMillis() >= configExpirationTime) {
1294 * Validate that the password reset configuration is correct.
1296 private static String validatePasswordResetConfig(PasswordResetConfig passwordResetConfig) {
1297 String result = null;
1299 if (passwordResetConfig != null) {
1300 result = passwordResetConfig.getMessage();
1301 if (result == null || result.length() == 0) {
1302 result = DEFAULT_PASSWORD_RESET_EMAIL_MESSAGE;
1303 logger.warn("Could not find a password reset message in the tenant's configuration. Using the default one");
1306 if (result.contains("{{link}}") == false) {
1307 logger.warn("The tenant's password reset message does not contain a required '{{link}}' marker.");
1311 String subject = passwordResetConfig.getSubject();
1313 if (subject == null || subject.trim().isEmpty()) {
1314 passwordResetConfig.setSubject(DEFAULT_PASSWORD_RESET_EMAIL_SUBJECT);
1322 * Generate a password reset message. Embeds an authorization token to reset a user's password.
1324 public static String generatePasswordResetEmailMessage(EmailConfig emailConfig, AccountListItem accountListItem, Token token) throws Exception {
1325 String result = null;
1327 result = validatePasswordResetConfig(emailConfig.getPasswordResetConfig());
1328 if (result == null) {
1329 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.",
1330 token.getTenantId(), accountListItem.getEmail());
1331 throw new Exception(errMsg);
1334 String link = UriBuilder.fromUri(emailConfig.getBaseurl())
1335 .path(AccountClient.PROCESS_PASSWORD_RESET_PATH)
1336 .replaceQuery("token=" + token.getId())
1340 result = result.replaceAll("\\{\\{link\\}\\}", link);
1342 if (result.contains("{{greeting}}")) {
1343 String greeting = accountListItem.getScreenName();
1344 result = result.replaceAll("\\{\\{greeting\\}\\}", greeting);
1345 result = result.replaceAll("\\\\n", "\\\n");
1346 result = result.replaceAll("\\\\r", "\\\r");
1352 public static void persistTenantBindingsMD5Hash(TenantBindingConfigReaderImpl tenantBindingConfigReader,
1353 DatabaseProductType databaseProductType, String cspaceDatabaseName) throws Exception {
1354 // Need to define and look for a createDisabled attribute in tenant config
1355 String updateTableSQL = "UPDATE tenants SET config_md5hash = ? WHERE id = ?";
1358 PreparedStatement pstmt = null;
1360 conn = getConnection(cspaceDatabaseName);
1361 pstmt = conn.prepareStatement(updateTableSQL); // create a statement
1362 for (String tId : AuthorizationCommon.tenantConfigMD5HashTable.keySet()) {
1363 pstmt.setString(1, AuthorizationCommon.getTenantConfigMD5Hash(tId));
1364 pstmt.setString(2, tId);
1365 if (logger.isDebugEnabled()) {
1366 logger.debug("createMissingTenants adding entry for tenant: " + tId);
1368 pstmt.executeUpdate();
1371 } catch(Exception e) {
1374 if (pstmt!=null) pstmt.close();