1 package org.collectionspace.services.common.authorization_mgt;
3 import java.sql.Connection;
4 import java.sql.PreparedStatement;
5 import java.sql.ResultSet;
6 import java.sql.SQLException;
7 import java.sql.Statement;
8 import java.util.ArrayList;
10 import java.util.HashMap;
11 import java.util.HashSet;
12 import java.util.Hashtable;
13 import java.util.List;
14 import java.util.UUID;
16 import javax.naming.NamingException;
17 import javax.persistence.EntityManager;
18 import javax.persistence.EntityManagerFactory;
20 import org.collectionspace.authentication.AuthN;
21 import org.collectionspace.services.authorization.AuthZ;
22 import org.collectionspace.services.authorization.CSpaceAction;
23 import org.collectionspace.services.authorization.PermissionException;
24 import org.collectionspace.services.authorization.PermissionRole;
25 import org.collectionspace.services.authorization.PermissionRoleRel;
26 import org.collectionspace.services.authorization.PermissionValue;
27 import org.collectionspace.services.authorization.Role;
28 import org.collectionspace.services.authorization.RoleValue;
29 import org.collectionspace.services.authorization.SubjectType;
30 import org.collectionspace.services.authorization.URIResourceImpl;
31 import org.collectionspace.services.authorization.perms.ActionType;
32 import org.collectionspace.services.authorization.perms.EffectType;
33 import org.collectionspace.services.authorization.perms.Permission;
34 import org.collectionspace.services.authorization.perms.PermissionAction;
35 import org.collectionspace.services.client.Profiler;
36 import org.collectionspace.services.client.RoleClient;
37 import org.collectionspace.services.client.workflow.WorkflowClient;
38 import org.collectionspace.services.common.config.ServiceConfigUtils;
39 import org.collectionspace.services.common.config.TenantBindingConfigReaderImpl;
40 import org.collectionspace.services.common.context.ServiceBindingUtils;
41 import org.collectionspace.services.common.document.DocumentHandler;
42 import org.collectionspace.services.common.security.SecurityUtils;
43 import org.collectionspace.services.common.storage.DatabaseProductType;
44 import org.collectionspace.services.common.storage.JDBCTools;
45 import org.collectionspace.services.common.storage.jpa.JpaStorageUtils;
46 import org.collectionspace.services.config.service.ServiceBindingType;
47 import org.collectionspace.services.config.tenant.TenantBindingType;
48 import org.collectionspace.services.lifecycle.Lifecycle;
49 import org.collectionspace.services.lifecycle.TransitionDef;
50 import org.collectionspace.services.lifecycle.TransitionDefList;
52 //import org.mortbay.log.Log;
53 import org.slf4j.Logger;
54 import org.slf4j.LoggerFactory;
55 import org.springframework.security.acls.model.AlreadyExistsException;
58 public class AuthorizationCommon {
60 final public static String REFRESH_AUTZ_PROP = "refreshAuthZOnStartup";
62 // ActionGroup labels/constants
66 final public static String ACTIONGROUP_CRUDL_NAME = "CRUDL";
67 final public static ActionType[] ACTIONSET_CRUDL = {ActionType.CREATE, ActionType.READ, ActionType.UPDATE, ActionType.DELETE, ActionType.SEARCH};
69 final public static String ACTIONGROUP_RL_NAME = "RL";
70 final public static ActionType[] ACTIONSET_RL = {ActionType.READ, ActionType.SEARCH};
72 static ActionGroup ACTIONGROUP_CRUDL;
73 static ActionGroup ACTIONGROUP_RL;
75 // A static block to initialize the predefined action groups
78 ACTIONGROUP_CRUDL = new ActionGroup();
79 ACTIONGROUP_CRUDL.name = ACTIONGROUP_CRUDL_NAME;
80 ACTIONGROUP_CRUDL.actions = ACTIONSET_CRUDL;
82 ACTIONGROUP_RL = new ActionGroup();
83 ACTIONGROUP_RL.name = ACTIONGROUP_RL_NAME;
84 ACTIONGROUP_RL.actions = ACTIONSET_RL;
88 final static Logger logger = LoggerFactory.getLogger(AuthorizationCommon.class);
91 // The "super" role has a predefined ID of "0" and a tenant ID of "0";
93 final public static String ROLE_ALL_TENANTS_MANAGER = "ALL_TENANTS_MANAGER";
94 final public static String ROLE_ALL_TENANTS_MANAGER_ID = "0";
95 final public static String ALL_TENANTS_MANAGER_TENANT_ID = "0";
97 final public static String ROLE_TENANT_ADMINISTRATOR = "TENANT_ADMINISTRATOR";
98 final public static String ROLE_TENANT_READER = "TENANT_READER";
100 public static final String TENANT_MANAGER_USER = "tenantManager";
101 public static final String TENANT_MANAGER_SCREEN_NAME = TENANT_MANAGER_USER;
102 public static final String DEFAULT_TENANT_MANAGER_PASSWORD = "manage";
103 public static final String DEFAULT_TENANT_MANAGER_EMAIL = "tenantManager@collectionspace.org";
105 public static final String TENANT_ADMIN_ACCT_PREFIX = "admin@";
106 public static final String TENANT_READER_ACCT_PREFIX = "reader@";
107 public static final String ROLE_PREFIX = "ROLE_";
108 public static final String SPRING_ADMIN_ROLE = "ROLE_SPRING_ADMIN";
109 public static final String TENANT_ADMIN_ROLE_SUFFIX = "_TENANT_ADMINISTRATOR";
110 public static final String TENANT_READER_ROLE_SUFFIX = "_TENANT_READER";
111 public static final String DEFAULT_ADMIN_PASSWORD = "Administrator";
112 public static final String DEFAULT_READER_PASSWORD = "reader";
114 public static final String ROLE_SPRING_ADMIN_ID = "-1";
115 public static final String ROLE_SPRING_ADMIN_NAME = "ROLE_SPRING_ADMIN";
117 // SQL for init tasks
118 final private static String INSERT_ACCOUNT_ROLE_SQL_MYSQL =
119 "INSERT INTO accounts_roles(account_id, user_id, role_id, role_name, created_at)"
120 +" VALUES(?, ?, ?, ?, now())";
121 final private static String INSERT_ACCOUNT_ROLE_SQL_POSTGRES =
122 "INSERT INTO accounts_roles(HJID, account_id, user_id, role_id, role_name, created_at)"
123 +" VALUES(nextval('hibernate_sequence'), ?, ?, ?, ?, now())";
124 final private static String QUERY_USERS_SQL =
125 "SELECT username FROM users WHERE username LIKE '"
126 +TENANT_ADMIN_ACCT_PREFIX+"%' OR username LIKE '"+TENANT_READER_ACCT_PREFIX+"%'";
127 final private static String INSERT_USER_SQL =
128 "INSERT INTO users (username,passwd, created_at) VALUES (?,?, now())";
129 final private static String INSERT_ACCOUNT_SQL =
130 "INSERT INTO accounts_common "
131 + "(csid, email, userid, status, screen_name, metadata_protection, roles_protection, created_at) "
132 + "VALUES (?,?,?,'ACTIVE',?, 'immutable', 'immutable', now())";
134 // TENANT MANAGER specific SQL
135 final private static String QUERY_TENANT_MGR_USER_SQL =
136 "SELECT username FROM users WHERE username = '"+TENANT_MANAGER_USER+"'";
137 final private static String GET_TENANT_MGR_ROLE_SQL =
138 "SELECT csid from roles WHERE tenant_id='"+ALL_TENANTS_MANAGER_TENANT_ID+"' and rolename=?";
140 public static Role getRole(String tenantId, String displayName) {
143 String roleName = AuthorizationCommon.getQualifiedRoleName(tenantId, displayName);
144 role = AuthorizationStore.getRoleByName(roleName, tenantId);
149 public static Role getRole(EntityManager em, String tenantId, String displayName) {
152 String roleName = AuthorizationCommon.getQualifiedRoleName(tenantId, displayName);
153 role = AuthorizationStore.getRoleByName(em, roleName, tenantId);
159 public static Role createRole(String tenantId, String name, String description) {
160 return createRole(tenantId, name, description, false /* mutable by default */);
163 public static Role createRole(String tenantId, String name, String description, boolean immutable) {
164 Role role = new Role();
166 role.setCreatedAtItem(new Date());
167 role.setDisplayName(name);
168 String roleName = AuthorizationCommon.getQualifiedRoleName(tenantId, name);
169 role.setRoleName(roleName);
170 String id = UUID.randomUUID().toString(); //FIXME: The qualified role name should be unique enough to use as an ID/key
172 role.setDescription(description);
173 role.setTenantId(tenantId);
174 if (immutable == true) {
175 role.setMetadataProtection(RoleClient.IMMUTABLE);
176 role.setPermsProtection(RoleClient.IMMUTABLE);
183 * Add permission to the Spring Security tables
184 * with assumption that resource is of type URI
185 * @param permission configuration
187 public static void addPermissionsForUri(Permission perm,
188 PermissionRole permRole) throws PermissionException {
190 // First check the integrity of the incoming arguments.
192 if (!perm.getCsid().equals(permRole.getPermission().get(0).getPermissionId())) {
193 throw new IllegalArgumentException("permission ids do not"
194 + " match for role=" + permRole.getRole().get(0).getRoleName()
195 + " with permissionId=" + permRole.getPermission().get(0).getPermissionId()
196 + " for permission with csid=" + perm.getCsid());
199 List<String> principals = new ArrayList<String>();
200 for (RoleValue roleValue : permRole.getRole()) {
201 principals.add(roleValue.getRoleName());
203 List<PermissionAction> permActions = perm.getAction();
204 for (PermissionAction permAction : permActions) {
206 CSpaceAction action = URIResourceImpl.getAction(permAction.getName());
207 URIResourceImpl uriRes = new URIResourceImpl(perm.getTenantId(),
208 perm.getResourceName(), action);
209 boolean grant = perm.getEffect().equals(EffectType.PERMIT) ? true : false;
210 AuthZ.get().addPermissions(uriRes, principals.toArray(new String[0]), grant);//CSPACE-4967
211 } catch (PermissionException e) {
213 // Only throw the exception if it is *not* an already-exists exception
215 if (e.getCause() instanceof AlreadyExistsException == false) {
222 private static Connection getConnection(String databaseName) throws NamingException, SQLException {
223 return JDBCTools.getConnection(JDBCTools.CSPACE_DATASOURCE_NAME,
228 * Spring security seems to require that all of our role names start
229 * with the ROLE_PREFIX string.
231 public static String getQualifiedRoleName(String tenantId, String name) {
232 String result = name;
234 String qualifiedName = ROLE_PREFIX + tenantId.toUpperCase() + "_" + name.toUpperCase();
235 if (name.equals(qualifiedName) == false) {
236 result = qualifiedName;
242 private static ActionGroup getActionGroup(String actionGroupStr) {
243 ActionGroup result = null;
245 if (actionGroupStr.equalsIgnoreCase(ACTIONGROUP_CRUDL_NAME)) {
246 result = ACTIONGROUP_CRUDL;
247 } else if (actionGroupStr.equalsIgnoreCase(ACTIONGROUP_RL_NAME)) {
248 result = ACTIONGROUP_RL;
254 public static Permission createPermission(String tenantId,
257 String actionGroupStr) {
258 Permission result = null;
260 ActionGroup actionGroup = getActionGroup(actionGroupStr);
261 result = createPermission(tenantId, resourceName, description, actionGroup);
266 private static Permission createPermission(String tenantId,
269 ActionGroup actionGroup) {
271 + "-" + resourceName.replace('/', '_') // Remove the slashes so the ID can be used in a URI/URL
272 + "-" + actionGroup.name;
273 Permission perm = new Permission();
275 perm.setDescription(description);
276 perm.setCreatedAtItem(new Date());
277 perm.setResourceName(resourceName.toLowerCase().trim());
278 perm.setEffect(EffectType.PERMIT);
279 perm.setTenantId(tenantId);
281 perm.setActionGroup(actionGroup.name);
282 ArrayList<PermissionAction> pas = new ArrayList<PermissionAction>();
284 for (ActionType actionType : actionGroup.actions) {
285 PermissionAction permAction = createPermissionAction(perm, actionType);
292 private static Permission createWorkflowPermission(TenantBindingType tenantBinding,
293 ServiceBindingType serviceBinding,
294 String transitionVerb,
295 ActionGroup actionGroup)
297 Permission result = null;
298 String workFlowServiceSuffix;
299 String transitionName;
300 if (transitionVerb != null) {
301 transitionName = transitionVerb;
302 workFlowServiceSuffix = WorkflowClient.SERVICE_AUTHZ_SUFFIX;
304 transitionName = ""; //since the transitionDef was null, we're assuming that this is the base workflow permission to be created
305 workFlowServiceSuffix = WorkflowClient.SERVICE_PATH;
308 String tenantId = tenantBinding.getId();
309 String resourceName = "/"
310 + serviceBinding.getName().toLowerCase().trim()
311 + workFlowServiceSuffix
313 String description = "A generated workflow permission for actiongroup " + actionGroup.name;
314 result = createPermission(tenantId, resourceName, description, actionGroup);
316 if (logger.isDebugEnabled() == true) {
317 logger.debug("Generated a workflow permission: "
318 + result.getResourceName()
319 + ":" + transitionName
320 + ":" + "tenant id=" + result.getTenantId()
321 + ":" + actionGroup.name);
327 private static PermissionRole createPermissionRole(EntityManager em,
328 Permission permission,
330 boolean enforceTenancy) throws Exception
332 PermissionRole permRole = new PermissionRole();
333 // Check to see if the tenant ID of the permission and the tenant ID of the role match
334 boolean tenantIdsMatch = role.getTenantId().equalsIgnoreCase(permission.getTenantId());
335 if (tenantIdsMatch == false && enforceTenancy == false) {
336 tenantIdsMatch = true; // If we don't need to enforce tenancy then we'll just consider them matched.
339 if (tenantIdsMatch == true) {
340 permRole.setSubject(SubjectType.ROLE);
342 // Set of the permission value list of the permrole
344 List<PermissionValue> permValues = new ArrayList<PermissionValue>();
345 PermissionValue permValue = new PermissionValue();
346 permValue.setPermissionId(permission.getCsid());
347 permValue.setResourceName(permission.getResourceName().toLowerCase());
348 permValue.setActionGroup(permission.getActionGroup());
349 permValues.add(permValue);
350 permRole.setPermission(permValues);
352 // Set of the role value list of the permrole
354 List<RoleValue> roleValues = new ArrayList<RoleValue>();
355 RoleValue rv = new RoleValue();
356 // This needs to use the qualified name, not the display name
357 rv.setRoleName(role.getRoleName());
358 rv.setRoleId(role.getCsid());
360 permRole.setRole(roleValues);
362 String errMsg = "The tenant ID of the role: " + role.getTenantId()
363 + " did not match the tenant ID of the permission: " + permission.getTenantId();
364 throw new Exception(errMsg);
370 private static Hashtable<String, String> getTenantNamesFromConfig(TenantBindingConfigReaderImpl tenantBindingConfigReader) {
372 // Note that this only handles tenants not marked as "createDisabled"
373 Hashtable<String, TenantBindingType> tenantBindings =
374 tenantBindingConfigReader.getTenantBindings();
375 Hashtable<String, String> tenantInfo = new Hashtable<String, String>();
376 for (TenantBindingType tenantBinding : tenantBindings.values()) {
377 String tId = tenantBinding.getId();
378 String tName = tenantBinding.getName();
379 tenantInfo.put(tId, tName);
380 if (logger.isDebugEnabled()) {
381 logger.debug("getTenantNamesFromConfig found configured tenant id: "+tId+" name: "+tName);
387 private static ArrayList<String> compileExistingTenants(Connection conn, Hashtable<String, String> tenantInfo)
388 throws SQLException, Exception {
389 Statement stmt = null;
390 ArrayList<String> existingTenants = new ArrayList<String>();
391 // First find or create the tenants
392 final String queryTenantSQL = "SELECT id,name FROM tenants";
394 stmt = conn.createStatement();
395 ResultSet rs = stmt.executeQuery(queryTenantSQL);
397 String tId = rs.getString("id");
398 String tName = rs.getString("name");
399 if(tenantInfo.containsKey(tId)) {
400 existingTenants.add(tId);
401 if(!tenantInfo.get(tId).equalsIgnoreCase(tName)) {
402 logger.warn("Configured name for tenant: "
403 +tId+" in repository: "+tName
404 +" does not match config'd name: "+ tenantInfo.get(tId));
409 } catch(Exception e) {
416 return existingTenants;
419 private static void createMissingTenants(Connection conn, Hashtable<String, String> tenantInfo,
420 ArrayList<String> existingTenants) throws SQLException, Exception {
421 // Need to define and look for a createDisabled attribute in tenant config
422 final String insertTenantSQL =
423 "INSERT INTO tenants (id,name,disabled,created_at) VALUES (?,?,FALSE,now())";
424 PreparedStatement pstmt = null;
426 pstmt = conn.prepareStatement(insertTenantSQL); // create a statement
427 for(String tId : tenantInfo.keySet()) {
428 if(existingTenants.contains(tId)) {
429 if (logger.isDebugEnabled()) {
430 logger.debug("createMissingTenants: tenant exists (skipping): "
431 +tenantInfo.get(tId));
435 pstmt.setString(1, tId); // set id param
436 pstmt.setString(2, tenantInfo.get(tId)); // set name param
437 if (logger.isDebugEnabled()) {
438 logger.debug("createMissingTenants adding entry for tenant: "+tId);
440 pstmt.executeUpdate();
443 } catch(Exception e) {
451 private static ArrayList<String> findOrCreateDefaultUsers(Connection conn, Hashtable<String, String> tenantInfo)
452 throws SQLException, Exception {
453 // Second find or create the users
454 Statement stmt = null;
455 PreparedStatement pstmt = null;
456 ArrayList<String> usersInRepo = new ArrayList<String>();
458 stmt = conn.createStatement();
459 ResultSet rs = stmt.executeQuery(QUERY_USERS_SQL);
461 String uName = rs.getString("username");
462 usersInRepo.add(uName);
465 pstmt = conn.prepareStatement(INSERT_USER_SQL); // create a statement
466 for(String tName : tenantInfo.values()) {
467 String adminAcctName = getDefaultAdminUserID(tName);
468 if(!usersInRepo.contains(adminAcctName)) {
469 String secEncPasswd = SecurityUtils.createPasswordHash(
470 adminAcctName, DEFAULT_ADMIN_PASSWORD);
471 pstmt.setString(1, adminAcctName); // set username param
472 pstmt.setString(2, secEncPasswd); // set passwd param
473 if (logger.isDebugEnabled()) {
474 logger.debug("createDefaultUsersAndAccounts adding user: "
475 +adminAcctName+" for tenant: "+tName);
477 pstmt.executeUpdate();
478 } else if (logger.isDebugEnabled()) {
479 logger.debug("createDefaultUsersAndAccounts: user: "+adminAcctName
480 +" already exists - skipping.");
484 String readerAcctName = getDefaultReaderUserID(tName);
485 if(!usersInRepo.contains(readerAcctName)) {
486 String secEncPasswd = SecurityUtils.createPasswordHash(
487 readerAcctName, DEFAULT_READER_PASSWORD);
488 pstmt.setString(1, readerAcctName); // set username param
489 pstmt.setString(2, secEncPasswd); // set passwd param
490 if (logger.isDebugEnabled()) {
491 logger.debug("createDefaultUsersAndAccounts adding user: "
492 +readerAcctName+" for tenant: "+tName);
494 pstmt.executeUpdate();
495 } else if (logger.isDebugEnabled()) {
496 logger.debug("createDefaultUsersAndAccounts: user: "+readerAcctName
497 +" already exists - skipping.");
501 } catch(Exception e) {
512 private static void findOrCreateDefaultAccounts(Connection conn, Hashtable<String, String> tenantInfo,
513 ArrayList<String> usersInRepo,
514 Hashtable<String, String> tenantAdminAcctCSIDs, Hashtable<String, String> tenantReaderAcctCSIDs)
515 throws SQLException, Exception {
516 // Third, create the accounts. Assume that if the users were already there,
517 // then the accounts were as well
518 PreparedStatement pstmt = null;
520 pstmt = conn.prepareStatement(INSERT_ACCOUNT_SQL); // create a statement
521 for(String tId : tenantInfo.keySet()) {
522 String tName = tenantInfo.get(tId);
523 String adminCSID = UUID.randomUUID().toString();
524 tenantAdminAcctCSIDs.put(tId, adminCSID);
525 String adminAcctName = getDefaultAdminUserID(tName);
526 if(!usersInRepo.contains(adminAcctName)) {
527 pstmt.setString(1, adminCSID); // set csid param
528 pstmt.setString(2, adminAcctName); // set email param (bogus)
529 pstmt.setString(3, adminAcctName); // set userid param
530 pstmt.setString(4, "Administrator");// set screen name param
531 if (logger.isDebugEnabled()) {
532 logger.debug("createDefaultAccounts adding account: "
533 +adminAcctName+" for tenant: "+tName);
535 pstmt.executeUpdate();
536 } else if (logger.isDebugEnabled()) {
537 logger.debug("createDefaultAccounts: user: "+adminAcctName
538 +" already exists - skipping account generation.");
541 String readerCSID = UUID.randomUUID().toString();
542 tenantReaderAcctCSIDs.put(tId, readerCSID);
543 String readerAcctName = getDefaultReaderUserID(tName);
544 if(!usersInRepo.contains(readerAcctName)) {
545 pstmt.setString(1, readerCSID); // set csid param
546 pstmt.setString(2, readerAcctName); // set email param (bogus)
547 pstmt.setString(3, readerAcctName); // set userid param
548 pstmt.setString(4, "Reader"); // set screen name param
549 if (logger.isDebugEnabled()) {
550 logger.debug("createDefaultAccounts adding account: "
551 +readerAcctName+" for tenant: "+tName);
553 pstmt.executeUpdate();
554 } else if (logger.isDebugEnabled()) {
555 logger.debug("createDefaultAccounts: user: "+readerAcctName
556 +" already exists - skipping account creation.");
560 } catch(Exception e) {
568 private static boolean findOrCreateTenantManagerUserAndAccount(Connection conn)
569 throws SQLException, Exception {
570 // Find or create the special tenant manager account.
571 // Later can make the user name for tenant manager be configurable, settable.
572 Statement stmt = null;
573 PreparedStatement pstmt = null;
574 boolean created = false;
576 boolean foundTMgrUser = false;
577 stmt = conn.createStatement();
578 ResultSet rs = stmt.executeQuery(QUERY_TENANT_MGR_USER_SQL);
579 // Should only find one - only consider it
581 String uName = rs.getString("username");
582 foundTMgrUser = uName.equals(TENANT_MANAGER_USER);
586 pstmt = conn.prepareStatement(INSERT_USER_SQL); // create a statement
587 String secEncPasswd = SecurityUtils.createPasswordHash(
588 TENANT_MANAGER_USER, DEFAULT_TENANT_MANAGER_PASSWORD);
589 pstmt.setString(1, TENANT_MANAGER_USER); // set username param
590 pstmt.setString(2, secEncPasswd); // set passwd param
591 if (logger.isDebugEnabled()) {
592 logger.debug("findOrCreateTenantManagerUserAndAccount adding tenant manager user: "
593 +TENANT_MANAGER_USER);
595 pstmt.executeUpdate();
597 // Now create the account to match
598 pstmt = conn.prepareStatement(INSERT_ACCOUNT_SQL); // create a statement
599 pstmt.setString(1, AuthN.TENANT_MANAGER_ACCT_ID); // set csid param
600 pstmt.setString(2, DEFAULT_TENANT_MANAGER_EMAIL); // set email param (bogus)
601 pstmt.setString(3, TENANT_MANAGER_USER); // set userid param
602 pstmt.setString(4, TENANT_MANAGER_SCREEN_NAME);// set screen name param
603 if (logger.isDebugEnabled()) {
604 logger.debug("findOrCreateTenantManagerUserAndAccount adding tenant manager account: "
605 +TENANT_MANAGER_USER);
607 pstmt.executeUpdate();
610 } else if (logger.isDebugEnabled()) {
611 logger.debug("findOrCreateTenantManagerUserAndAccount: tenant manager: "+TENANT_MANAGER_USER
612 +" already exists.");
614 } catch(Exception e) {
625 private static void bindDefaultAccountsToTenants(Connection conn, DatabaseProductType databaseProductType,
626 Hashtable<String, String> tenantInfo, ArrayList<String> usersInRepo,
627 Hashtable<String, String> tenantAdminAcctCSIDs, Hashtable<String, String> tenantReaderAcctCSIDs)
628 throws SQLException, Exception {
629 // Fourth, bind accounts to tenants. Assume that if the users were already there,
630 // then the accounts were bound to tenants correctly
631 PreparedStatement pstmt = null;
633 String insertAccountTenantSQL;
634 if (databaseProductType == DatabaseProductType.MYSQL) {
635 insertAccountTenantSQL =
636 "INSERT INTO accounts_tenants (TENANTS_ACCOUNTS_COMMON_CSID,tenant_id) "
638 } else if (databaseProductType == DatabaseProductType.POSTGRESQL) {
639 insertAccountTenantSQL =
640 "INSERT INTO accounts_tenants (HJID, TENANTS_ACCOUNTS_COMMON_CSID,tenant_id) "
641 + " VALUES(nextval('hibernate_sequence'), ?, ?)";
643 throw new Exception("Unrecognized database system.");
645 pstmt = conn.prepareStatement(insertAccountTenantSQL); // create a statement
646 for(String tId : tenantInfo.keySet()) {
647 String tName = tenantInfo.get(tId);
648 if(!usersInRepo.contains(getDefaultAdminUserID(tName))) {
649 String adminAcct = tenantAdminAcctCSIDs.get(tId);
650 pstmt.setString(1, adminAcct); // set acct CSID param
651 pstmt.setString(2, tId); // set tenant_id param
652 if (logger.isDebugEnabled()) {
653 logger.debug("createDefaultAccounts binding account id: "
654 +adminAcct+" to tenant id: "+tId);
656 pstmt.executeUpdate();
658 if(!usersInRepo.contains(getDefaultReaderUserID(tName))) {
659 String readerAcct = tenantReaderAcctCSIDs.get(tId);
660 pstmt.setString(1, readerAcct); // set acct CSID param
661 pstmt.setString(2, tId); // set tenant_id param
662 if (logger.isDebugEnabled()) {
663 logger.debug("createDefaultAccounts binding account id: "
664 +readerAcct+" to tenant id: "+tId);
666 pstmt.executeUpdate();
670 } catch(Exception e) {
679 private static String findOrCreateDefaultRoles(Connection conn, Hashtable<String, String> tenantInfo,
680 Hashtable<String, String> tenantAdminRoleCSIDs, Hashtable<String, String> tenantReaderRoleCSIDs)
681 throws SQLException, Exception {
682 // Fifth, fetch and save the default roles
683 String springAdminRoleCSID = null;
684 Statement stmt = null;
685 PreparedStatement pstmt = null;
687 final String querySpringRole =
688 "SELECT csid from roles WHERE rolename='"+SPRING_ADMIN_ROLE+"'";
689 stmt = conn.createStatement();
690 ResultSet rs = stmt.executeQuery(querySpringRole);
692 springAdminRoleCSID = rs.getString(1);
693 if (logger.isDebugEnabled()) {
694 logger.debug("createDefaultAccounts found Spring Admin role: "
695 +springAdminRoleCSID);
698 final String insertSpringAdminRoleSQL =
699 "INSERT INTO roles (csid, rolename, displayName, rolegroup, created_at, tenant_id) "
700 + "VALUES ('-1', 'ROLE_SPRING_ADMIN', 'SPRING_ADMIN', 'Spring Security Administrator', now(), '0')";
701 stmt.executeUpdate(insertSpringAdminRoleSQL);
702 springAdminRoleCSID = "-1";
703 if (logger.isDebugEnabled()) {
704 logger.debug("createDefaultAccounts CREATED Spring Admin role: "
705 +springAdminRoleCSID);
709 final String getRoleCSIDSql =
710 "SELECT csid from roles WHERE tenant_id=? and rolename=?";
711 pstmt = conn.prepareStatement(getRoleCSIDSql); // create a statement
713 for(String tId : tenantInfo.keySet()) {
714 pstmt.setString(1, tId); // set tenant_id param
715 pstmt.setString(2, getDefaultAdminRole(tId)); // set rolename param
716 rs = pstmt.executeQuery();
717 // extract data from the ResultSet
719 throw new RuntimeException("Cannot find role: "+getDefaultAdminRole(tId)
720 +" for tenant id: "+tId+" in roles!");
722 String tenantAdminRoleCSID = rs.getString(1);
723 if (logger.isDebugEnabled()) {
724 logger.debug("createDefaultAccounts found role: "
725 +getDefaultAdminRole(tId)+"("+tenantAdminRoleCSID
726 +") for tenant id: "+tId);
728 tenantAdminRoleCSIDs.put(tId, tenantAdminRoleCSID);
729 pstmt.setString(1, tId); // set tenant_id param
730 pstmt.setString(2, getDefaultReaderRole(tId)); // set rolename param
732 rs = pstmt.executeQuery();
733 // extract data from the ResultSet
735 throw new RuntimeException("Cannot find role: "+getDefaultReaderRole(tId)
736 +" for tenant id: "+tId+" in roles!");
738 String tenantReaderRoleCSID = rs.getString(1);
739 if (logger.isDebugEnabled()) {
740 logger.debug("createDefaultAccounts found role: "
741 +getDefaultReaderRole(tId)+"("+tenantReaderRoleCSID
742 +") for tenant id: "+tId);
744 tenantReaderRoleCSIDs.put(tId, tenantReaderRoleCSID);
748 } catch(Exception e) {
756 return springAdminRoleCSID;
759 private static String findTenantManagerRole(Connection conn )
760 throws SQLException, RuntimeException, Exception {
761 String tenantMgrRoleCSID = null;
762 PreparedStatement pstmt = null;
764 String rolename = getQualifiedRoleName(ALL_TENANTS_MANAGER_TENANT_ID,
765 ROLE_ALL_TENANTS_MANAGER);
766 pstmt = conn.prepareStatement(GET_TENANT_MGR_ROLE_SQL); // create a statement
768 pstmt.setString(1, rolename); // set rolename param
769 rs = pstmt.executeQuery();
771 tenantMgrRoleCSID = rs.getString(1);
772 if (logger.isDebugEnabled()) {
773 logger.debug("findTenantManagerRole found Tenant Mgr role: "
778 } catch(Exception e) {
784 if(tenantMgrRoleCSID==null)
785 throw new RuntimeException("findTenantManagerRole: Cound not find tenant Manager Role!");
786 return tenantMgrRoleCSID;
789 private static void bindAccountsToRoles(Connection conn, DatabaseProductType databaseProductType,
790 Hashtable<String, String> tenantInfo, ArrayList<String> usersInRepo,
791 String springAdminRoleCSID,
792 Hashtable<String, String> tenantAdminRoleCSIDs, Hashtable<String, String> tenantReaderRoleCSIDs,
793 Hashtable<String, String> tenantAdminAcctCSIDs, Hashtable<String, String> tenantReaderAcctCSIDs)
794 throws SQLException, Exception {
795 // Sixth, bind the accounts to roles. If the users already existed,
796 // we'll assume they were set up correctly.
797 PreparedStatement pstmt = null;
799 String insertAccountRoleSQL;
800 if (databaseProductType == DatabaseProductType.MYSQL) {
801 insertAccountRoleSQL = INSERT_ACCOUNT_ROLE_SQL_MYSQL;
802 } else if (databaseProductType == DatabaseProductType.POSTGRESQL) {
803 insertAccountRoleSQL = INSERT_ACCOUNT_ROLE_SQL_POSTGRES;
805 throw new Exception("Unrecognized database system.");
807 if (logger.isDebugEnabled()) {
808 logger.debug("createDefaultAccounts binding accounts to roles with SQL:\n"
809 +insertAccountRoleSQL);
811 pstmt = conn.prepareStatement(insertAccountRoleSQL); // create a statement
812 for(String tId : tenantInfo.keySet()) {
813 String adminUserId = getDefaultAdminUserID(tenantInfo.get(tId));
814 if(!usersInRepo.contains(adminUserId)) {
815 String adminAcct = tenantAdminAcctCSIDs.get(tId);
816 String adminRoleId = tenantAdminRoleCSIDs.get(tId);
817 pstmt.setString(1, adminAcct); // set acct CSID param
818 pstmt.setString(2, adminUserId); // set user_id param
819 pstmt.setString(3, adminRoleId); // set role_id param
820 pstmt.setString(4, getDefaultAdminRole(tId)); // set rolename param
821 if (logger.isDebugEnabled()) {
822 logger.debug("createDefaultAccounts binding account: "
823 +adminUserId+" to Admin role("+adminRoleId
824 +") for tenant id: "+tId);
826 pstmt.executeUpdate();
827 // Now add the Spring Admin Role to the admin accounts
828 pstmt.setString(3, springAdminRoleCSID); // set role_id param
829 pstmt.setString(4, SPRING_ADMIN_ROLE); // set rolename param
830 if (logger.isDebugEnabled()) {
831 logger.debug("createDefaultAccounts binding account: "
832 +adminUserId+" to Spring Admin role: "+springAdminRoleCSID);
834 pstmt.executeUpdate();
836 String readerUserId = getDefaultReaderUserID(tenantInfo.get(tId));
837 if(!usersInRepo.contains(readerUserId)) {
838 String readerAcct = tenantReaderAcctCSIDs.get(tId);
839 String readerRoleId = tenantReaderRoleCSIDs.get(tId);
840 pstmt.setString(1, readerAcct); // set acct CSID param
841 pstmt.setString(2, readerUserId); // set user_id param
842 pstmt.setString(3, readerRoleId); // set role_id param
843 pstmt.setString(4, getDefaultReaderRole(tId)); // set rolename param
844 if (logger.isDebugEnabled()) {
845 logger.debug("createDefaultAccounts binding account: "
846 +readerUserId+" to Reader role("+readerRoleId
847 +") for tenant id: "+tId);
849 pstmt.executeUpdate();
853 } catch(Exception e) {
861 private static void bindTenantManagerAccountRole(Connection conn, DatabaseProductType databaseProductType,
862 String tenantManagerUserID, String tenantManagerAccountID, String tenantManagerRoleID, String tenantManagerRoleName )
863 throws SQLException, Exception {
864 PreparedStatement pstmt = null;
866 String insertAccountRoleSQL;
867 if (databaseProductType == DatabaseProductType.MYSQL) {
868 insertAccountRoleSQL = INSERT_ACCOUNT_ROLE_SQL_MYSQL;
869 } else if (databaseProductType == DatabaseProductType.POSTGRESQL) {
870 insertAccountRoleSQL = INSERT_ACCOUNT_ROLE_SQL_POSTGRES;
872 throw new Exception("Unrecognized database system.");
874 if (logger.isDebugEnabled()) {
875 logger.debug("bindTenantManagerAccountRole binding account to role with SQL:\n"
876 +insertAccountRoleSQL);
878 pstmt = conn.prepareStatement(insertAccountRoleSQL); // create a statement
879 pstmt.setString(1, tenantManagerAccountID); // set acct CSID param
880 pstmt.setString(2, tenantManagerUserID); // set user_id param
881 pstmt.setString(3, tenantManagerRoleID); // set role_id param
882 pstmt.setString(4, tenantManagerRoleName); // set rolename param
883 if (logger.isDebugEnabled()) {
884 logger.debug("bindTenantManagerAccountRole binding user: "
885 +tenantManagerUserID+" to Admin role("+tenantManagerRoleName+")");
887 pstmt.executeUpdate();
888 /* At this point, tenant manager should not need the Spring Admin Role
889 pstmt.setString(3, springAdminRoleCSID); // set role_id param
890 pstmt.setString(4, SPRING_ADMIN_ROLE); // set rolename param
891 if (logger.isDebugEnabled()) {
892 logger.debug("createDefaultAccounts binding account: "
893 +adminUserId+" to Spring Admin role: "+springAdminRoleCSID);
895 pstmt.executeUpdate();
898 } catch(Exception e) {
906 public static void createDefaultAccounts(
907 TenantBindingConfigReaderImpl tenantBindingConfigReader,
908 DatabaseProductType databaseProductType,
909 String cspaceDatabaseName) throws Exception {
911 logger.debug("ServiceMain.createDefaultAccounts starting...");
913 Hashtable<String, String> tenantInfo = getTenantNamesFromConfig(tenantBindingConfigReader);
914 Connection conn = null;
915 // TODO - need to put in tests for existence first.
916 // We could just look for the accounts per tenant up front, and assume that
917 // the rest is there if the accounts are.
918 // Could add a sql script to remove these if need be - Spring only does roles,
919 // and we're not touching that, so we could safely toss the
920 // accounts, users, account-tenants, account-roles, and start over.
922 conn = getConnection(cspaceDatabaseName);
923 ArrayList<String> existingTenants = compileExistingTenants(conn, tenantInfo);
925 // Note that this only creates tenants not marked as "createDisabled"
926 createMissingTenants(conn, tenantInfo, existingTenants);
928 ArrayList<String> usersInRepo = findOrCreateDefaultUsers(conn, tenantInfo);
930 Hashtable<String, String> tenantAdminAcctCSIDs = new Hashtable<String, String>();
931 Hashtable<String, String> tenantReaderAcctCSIDs = new Hashtable<String, String>();
932 findOrCreateDefaultAccounts(conn, tenantInfo, usersInRepo,
933 tenantAdminAcctCSIDs, tenantReaderAcctCSIDs);
935 bindDefaultAccountsToTenants(conn, databaseProductType, tenantInfo, usersInRepo,
936 tenantAdminAcctCSIDs, tenantReaderAcctCSIDs);
938 Hashtable<String, String> tenantAdminRoleCSIDs = new Hashtable<String, String>();
939 Hashtable<String, String> tenantReaderRoleCSIDs = new Hashtable<String, String>();
940 String springAdminRoleCSID = findOrCreateDefaultRoles(conn, tenantInfo,
941 tenantAdminRoleCSIDs, tenantReaderRoleCSIDs);
943 bindAccountsToRoles(conn, databaseProductType,
944 tenantInfo, usersInRepo, springAdminRoleCSID,
945 tenantAdminRoleCSIDs, tenantReaderRoleCSIDs,
946 tenantAdminAcctCSIDs, tenantReaderAcctCSIDs);
948 boolean createdTenantMgrAccount = findOrCreateTenantManagerUserAndAccount(conn);
949 if(createdTenantMgrAccount) {
950 // If we created the account, we need to create the bindings. Otherwise, assume they
951 // are all set (from previous initialization).
952 String tenantManagerRoleCSID = findTenantManagerRole(conn);
953 bindTenantManagerAccountRole(conn, databaseProductType,
954 TENANT_MANAGER_USER, AuthN.TENANT_MANAGER_ACCT_ID,
955 tenantManagerRoleCSID, ROLE_ALL_TENANTS_MANAGER);
957 } catch (Exception e) {
958 logger.debug("Exception in createDefaultAccounts: " + e.getLocalizedMessage());
964 } catch (SQLException sqle) {
965 if (logger.isDebugEnabled()) {
966 logger.debug("SQL Exception closing statement/connection: " + sqle.getLocalizedMessage());
972 private static String getDefaultAdminRole(String tenantId) {
973 return ROLE_PREFIX+tenantId+TENANT_ADMIN_ROLE_SUFFIX;
976 private static String getDefaultReaderRole(String tenantId) {
977 return ROLE_PREFIX+tenantId+TENANT_READER_ROLE_SUFFIX;
980 private static String getDefaultAdminUserID(String tenantName) {
981 return TENANT_ADMIN_ACCT_PREFIX+tenantName;
984 private static String getDefaultReaderUserID(String tenantName) {
985 return TENANT_READER_ACCT_PREFIX+tenantName;
988 static public PermissionAction createPermissionAction(Permission perm,
989 ActionType actionType) {
990 PermissionAction pa = new PermissionAction();
992 CSpaceAction action = URIResourceImpl.getAction(actionType);
993 URIResourceImpl uriRes = new URIResourceImpl(perm.getTenantId(),
994 perm.getResourceName(), action);
995 pa.setName(actionType);
996 pa.setObjectIdentity(uriRes.getHashedId().toString());
997 pa.setObjectIdentityResource(uriRes.getId());
1002 static public PermissionAction update(Permission perm, PermissionAction permAction) {
1003 PermissionAction pa = new PermissionAction();
1005 CSpaceAction action = URIResourceImpl.getAction(permAction.getName());
1006 URIResourceImpl uriRes = new URIResourceImpl(perm.getTenantId(),
1007 perm.getResourceName(), action);
1008 pa.setObjectIdentity(uriRes.getHashedId().toString());
1009 pa.setObjectIdentityResource(uriRes.getId());
1014 private static HashSet<String> getTransitionVerbList(TenantBindingType tenantBinding, ServiceBindingType serviceBinding) {
1015 HashSet<String> result = new HashSet<String>();
1017 TransitionDefList transitionDefList = getTransitionDefList(tenantBinding, serviceBinding);
1018 for (TransitionDef transitionDef : transitionDefList.getTransitionDef()) {
1019 String transitionVerb = transitionDef.getName();
1020 String[] tokens = transitionVerb.split("_"); // Split the verb into words. The workflow verbs are compound words combined with the '_' character.
1021 result.add(tokens[0]); // We only care about the first word.
1027 private static TransitionDefList getTransitionDefList(TenantBindingType tenantBinding, ServiceBindingType serviceBinding) {
1028 TransitionDefList result = null;
1030 String serviceObjectName = serviceBinding.getObject().getName();
1031 DocumentHandler docHandler = ServiceConfigUtils.createDocumentHandlerInstance(
1032 tenantBinding, serviceBinding);
1033 Lifecycle lifecycle = docHandler.getLifecycle(serviceObjectName);
1034 if (lifecycle != null) {
1035 result = lifecycle.getTransitionDefList();
1037 } catch (Exception e) {
1038 // Ignore this exception and return an empty non-null TransitionDefList
1041 if (result == null) {
1042 if (serviceBinding.getType().equalsIgnoreCase(ServiceBindingUtils.SERVICE_TYPE_SECURITY) == false) {
1043 logger.debug("Could not retrieve a lifecycle transition definition list from: "
1044 + serviceBinding.getName()
1045 + " with tenant ID = "
1046 + tenantBinding.getId());
1048 // return an empty list
1049 result = new TransitionDefList();
1051 logger.debug("Successfully retrieved a lifecycle transition definition list from: "
1052 + serviceBinding.getName()
1053 + " with tenant ID = "
1054 + tenantBinding.getId());
1060 public static void createDefaultWorkflowPermissions(TenantBindingConfigReaderImpl tenantBindingConfigReader) throws Exception //FIXME: REM - 4/11/2012 - Rename to createWorkflowPermissions
1062 AuthZ.get().login(); //login to Spring Security manager
1064 EntityManagerFactory emf = JpaStorageUtils.getEntityManagerFactory(JpaStorageUtils.CS_PERSISTENCE_UNIT);
1065 EntityManager em = null;
1068 em = emf.createEntityManager();
1070 Hashtable<String, TenantBindingType> tenantBindings =
1071 tenantBindingConfigReader.getTenantBindings();
1072 for (String tenantId : tenantBindings.keySet()) {
1073 logger.info(String.format("Creating/verifying workflow permissions for tenant ID=%s.", tenantId));
1074 TenantBindingType tenantBinding = tenantBindings.get(tenantId);
1075 Role adminRole = AuthorizationCommon.getRole(em, tenantBinding.getId(), ROLE_TENANT_ADMINISTRATOR);
1076 Role readonlyRole = AuthorizationCommon.getRole(em, tenantBinding.getId(), ROLE_TENANT_READER);
1078 if (adminRole == null || readonlyRole == null) {
1079 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.");
1081 throw new RuntimeException("One or more of the required default CollectionSpace administrator roles is missing or was never created.");
1084 for (ServiceBindingType serviceBinding : tenantBinding.getServiceBindings()) {
1085 String prop = ServiceBindingUtils.getPropertyValue(serviceBinding, REFRESH_AUTZ_PROP);
1086 if (prop == null ? true : Boolean.parseBoolean(prop)) {
1088 em.getTransaction().begin();
1089 TransitionDefList transitionDefList = getTransitionDefList(tenantBinding, serviceBinding);
1090 HashSet<String> transitionVerbList = getTransitionVerbList(tenantBinding, serviceBinding);
1091 for (String transitionVerb : transitionVerbList) {
1093 // Create the permission for the admin role
1094 Permission adminPerm = createWorkflowPermission(tenantBinding, serviceBinding, transitionVerb, ACTIONGROUP_CRUDL);
1095 persist(em, adminPerm, adminRole, true, ACTIONGROUP_CRUDL);
1097 // Create the permission for the read-only role
1098 Permission readonlyPerm = createWorkflowPermission(tenantBinding, serviceBinding, transitionVerb, ACTIONGROUP_RL);
1099 persist(em, readonlyPerm, readonlyRole, true, ACTIONGROUP_RL); // Persist/store the permission and permrole records and related Spring Security info
1101 em.getTransaction().commit();
1102 } catch (IllegalStateException e) {
1103 logger.debug(e.getLocalizedMessage(), e); //We end up here if there is no document handler for the service -this is ok for some of the services.
1106 logger.warn("AuthZ refresh service binding property is set to FALSE so default permissions will NOT be refreshed for: "
1107 + serviceBinding.getName());
1112 } catch (Exception e) {
1113 if (em != null && em.getTransaction().isActive()) {
1114 em.getTransaction().rollback();
1116 if (logger.isDebugEnabled()) {
1117 logger.debug("Caught exception and rolling back permission creation: ", e);
1122 JpaStorageUtils.releaseEntityManagerFactory(emf);
1127 private static PermissionRoleRel findPermRoleRel(EntityManager em, String permissionId, String RoleId) {
1128 PermissionRoleRel result = null;
1131 String whereClause = "where permissionId = :id and roleId = :roleId";
1132 HashMap<String, Object> params = new HashMap<String, Object>();
1133 params.put("id", permissionId);
1134 params.put("roleId", RoleId);
1136 result = (PermissionRoleRel) JpaStorageUtils.getEntity(em,
1137 PermissionRoleRel.class.getCanonicalName(), whereClause, params);
1138 } catch (Exception e) {
1139 //Do nothing. Will return null;
1146 * Persists the Permission, PermissionRoleRel, and Spring Security table entries all in one transaction
1148 private static void persist(EntityManager em, Permission permission, Role role, boolean enforceTenancy, ActionGroup actionGroup) throws Exception {
1149 AuthorizationStore authzStore = new AuthorizationStore();
1150 // First persist the Permission record
1151 authzStore.store(em, permission);
1153 // If the PermRoleRel doesn't already exists then relate the permission and the role in a new PermissionRole (the service payload)
1154 // Create a PermissionRoleRel (the database relation table for the permission and role)
1155 PermissionRoleRel permRoleRel = findPermRoleRel(em, permission.getCsid(), role.getCsid());
1156 if (permRoleRel == null) {
1157 PermissionRole permRole = createPermissionRole(em, permission, role, enforceTenancy);
1158 List<PermissionRoleRel> permRoleRels = new ArrayList<PermissionRoleRel>();
1159 PermissionRoleUtil.buildPermissionRoleRel(em, permRole, SubjectType.ROLE, permRoleRels, false /*not for delete*/);
1160 for (PermissionRoleRel prr : permRoleRels) {
1161 authzStore.store(em, prr);
1163 Profiler profiler = new Profiler(AuthorizationCommon.class, 2);
1165 // Add a corresponding entry in the Spring Security Tables
1166 addPermissionsForUri(permission, permRole);
1168 logger.debug("Finished full perm generation for "
1169 + ":" + permission.getTenantId()
1170 + ":" + permission.getResourceName()
1171 + ":" + actionGroup.getName()
1172 + ":" + profiler.getCumulativeTime());