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};
74 * Inner class to deal with predefined ADMIN and READER action groupds
76 public class ActionGroup {
80 public String getName() {
85 static ActionGroup ACTIONGROUP_CRUDL;
86 static ActionGroup ACTIONGROUP_RL;
88 // A static block to initialize the predefined action groups
90 AuthorizationCommon ac = new AuthorizationCommon();
92 ACTIONGROUP_CRUDL = ac.new ActionGroup();
93 ACTIONGROUP_CRUDL.name = ACTIONGROUP_CRUDL_NAME;
94 ACTIONGROUP_CRUDL.actions = ACTIONSET_CRUDL;
96 ACTIONGROUP_RL = ac.new ActionGroup();
97 ACTIONGROUP_RL.name = ACTIONGROUP_RL_NAME;
98 ACTIONGROUP_RL.actions = ACTIONSET_RL;
102 final static Logger logger = LoggerFactory.getLogger(AuthorizationCommon.class);
105 // The "super" role has a predefined ID of "0" and a tenant ID of "0";
107 final public static String ROLE_ALL_TENANTS_MANAGER = "ALL_TENANTS_MANAGER";
108 final public static String ROLE_ALL_TENANTS_MANAGER_ID = "0";
109 final public static String ALL_TENANTS_MANAGER_TENANT_ID = "0";
111 final public static String ROLE_TENANT_ADMINISTRATOR = "TENANT_ADMINISTRATOR";
112 final public static String ROLE_TENANT_READER = "TENANT_READER";
114 public static final String TENANT_MANAGER_USER = "tenantManager";
115 public static final String TENANT_MANAGER_SCREEN_NAME = TENANT_MANAGER_USER;
116 public static final String DEFAULT_TENANT_MANAGER_PASSWORD = "manage";
117 public static final String DEFAULT_TENANT_MANAGER_EMAIL = "tenantManager@collectionspace.org";
119 public static final String TENANT_ADMIN_ACCT_PREFIX = "admin@";
120 public static final String TENANT_READER_ACCT_PREFIX = "reader@";
121 public static final String ROLE_PREFIX = "ROLE_";
122 public static final String SPRING_ADMIN_ROLE = "ROLE_SPRING_ADMIN";
123 public static final String TENANT_ADMIN_ROLE_SUFFIX = "_TENANT_ADMINISTRATOR";
124 public static final String TENANT_READER_ROLE_SUFFIX = "_TENANT_READER";
125 public static final String DEFAULT_ADMIN_PASSWORD = "Administrator";
126 public static final String DEFAULT_READER_PASSWORD = "reader";
128 public static final String ROLE_SPRING_ADMIN_ID = "-1";
129 public static final String ROLE_SPRING_ADMIN_NAME = "ROLE_SPRING_ADMIN";
131 // SQL for init tasks
132 final private static String INSERT_ACCOUNT_ROLE_SQL_MYSQL =
133 "INSERT INTO accounts_roles(account_id, user_id, role_id, role_name, created_at)"
134 +" VALUES(?, ?, ?, ?, now())";
135 final private static String INSERT_ACCOUNT_ROLE_SQL_POSTGRES =
136 "INSERT INTO accounts_roles(HJID, account_id, user_id, role_id, role_name, created_at)"
137 +" VALUES(nextval('hibernate_sequence'), ?, ?, ?, ?, now())";
138 final private static String QUERY_USERS_SQL =
139 "SELECT username FROM users WHERE username LIKE '"
140 +TENANT_ADMIN_ACCT_PREFIX+"%' OR username LIKE '"+TENANT_READER_ACCT_PREFIX+"%'";
141 final private static String INSERT_USER_SQL =
142 "INSERT INTO users (username,passwd, created_at) VALUES (?,?, now())";
143 final private static String INSERT_ACCOUNT_SQL =
144 "INSERT INTO accounts_common "
145 + "(csid, email, userid, status, screen_name, metadata_protection, roles_protection, created_at) "
146 + "VALUES (?,?,?,'ACTIVE',?, 'immutable', 'immutable', now())";
148 // TENANT MANAGER specific SQL
149 final private static String QUERY_TENANT_MGR_USER_SQL =
150 "SELECT username FROM users WHERE username = '"+TENANT_MANAGER_USER+"'";
151 final private static String GET_TENANT_MGR_ROLE_SQL =
152 "SELECT csid from roles WHERE tenant_id='"+ALL_TENANTS_MANAGER_TENANT_ID+"' and rolename=?";
154 public static Role getRole(String tenantId, String displayName) {
157 String roleName = AuthorizationCommon.getQualifiedRoleName(tenantId, displayName);
158 role = AuthorizationStore.getRoleByName(roleName, tenantId);
163 public static Role getRole(EntityManager em, String tenantId, String displayName) {
166 String roleName = AuthorizationCommon.getQualifiedRoleName(tenantId, displayName);
167 role = AuthorizationStore.getRoleByName(em, roleName, tenantId);
173 public static Role createRole(String tenantId, String name, String description) {
174 return createRole(tenantId, name, description, false /* mutable by default */);
177 public static Role createRole(String tenantId, String name, String description, boolean immutable) {
178 Role role = new Role();
180 role.setCreatedAtItem(new Date());
181 role.setDisplayName(name);
182 String roleName = AuthorizationCommon.getQualifiedRoleName(tenantId, name);
183 role.setRoleName(roleName);
184 String id = UUID.randomUUID().toString(); //FIXME: The qualified role name should be unique enough to use as an ID/key
186 role.setDescription(description);
187 role.setTenantId(tenantId);
188 if (immutable == true) {
189 role.setMetadataProtection(RoleClient.IMMUTABLE);
190 role.setPermsProtection(RoleClient.IMMUTABLE);
197 * Add permission to the Spring Security tables
198 * with assumption that resource is of type URI
199 * @param permission configuration
201 public static void addPermissionsForUri(Permission perm,
202 PermissionRole permRole) throws PermissionException {
204 // First check the integrity of the incoming arguments.
206 if (!perm.getCsid().equals(permRole.getPermission().get(0).getPermissionId())) {
207 throw new IllegalArgumentException("permission ids do not"
208 + " match for role=" + permRole.getRole().get(0).getRoleName()
209 + " with permissionId=" + permRole.getPermission().get(0).getPermissionId()
210 + " for permission with csid=" + perm.getCsid());
213 List<String> principals = new ArrayList<String>();
214 for (RoleValue roleValue : permRole.getRole()) {
215 principals.add(roleValue.getRoleName());
217 List<PermissionAction> permActions = perm.getAction();
218 for (PermissionAction permAction : permActions) {
220 CSpaceAction action = URIResourceImpl.getAction(permAction.getName());
221 URIResourceImpl uriRes = new URIResourceImpl(perm.getTenantId(),
222 perm.getResourceName(), action);
223 boolean grant = perm.getEffect().equals(EffectType.PERMIT) ? true : false;
224 AuthZ.get().addPermissions(uriRes, principals.toArray(new String[0]), grant);//CSPACE-4967
225 } catch (PermissionException e) {
227 // Only throw the exception if it is *not* an already-exists exception
229 if (e.getCause() instanceof AlreadyExistsException == false) {
236 private static Connection getConnection(String databaseName) throws NamingException, SQLException {
237 return JDBCTools.getConnection(JDBCTools.CSPACE_DATASOURCE_NAME,
242 * Spring security seems to require that all of our role names start
243 * with the ROLE_PREFIX string.
245 public static String getQualifiedRoleName(String tenantId, String name) {
246 String result = name;
248 String qualifiedName = ROLE_PREFIX + tenantId.toUpperCase() + "_" + name.toUpperCase();
249 if (name.equals(qualifiedName) == false) {
250 result = qualifiedName;
256 private static ActionGroup getActionGroup(String actionGroupStr) {
257 ActionGroup result = null;
259 if (actionGroupStr.equalsIgnoreCase(ACTIONGROUP_CRUDL_NAME)) {
260 result = ACTIONGROUP_CRUDL;
261 } else if (actionGroupStr.equalsIgnoreCase(ACTIONGROUP_RL_NAME)) {
262 result = ACTIONGROUP_RL;
268 public static Permission createPermission(String tenantId,
271 String actionGroupStr) {
272 Permission result = null;
274 ActionGroup actionGroup = getActionGroup(actionGroupStr);
275 result = createPermission(tenantId, resourceName, description, actionGroup);
280 private static Permission createPermission(String tenantId,
283 ActionGroup actionGroup) {
285 + "-" + resourceName.replace('/', '_') // Remove the slashes so the ID can be used in a URI/URL
286 + "-" + actionGroup.name;
287 Permission perm = new Permission();
289 perm.setDescription(description);
290 perm.setCreatedAtItem(new Date());
291 perm.setResourceName(resourceName.toLowerCase().trim());
292 perm.setEffect(EffectType.PERMIT);
293 perm.setTenantId(tenantId);
295 perm.setActionGroup(actionGroup.name);
296 ArrayList<PermissionAction> pas = new ArrayList<PermissionAction>();
298 for (ActionType actionType : actionGroup.actions) {
299 PermissionAction permAction = createPermissionAction(perm, actionType);
306 private static Permission createWorkflowPermission(TenantBindingType tenantBinding,
307 ServiceBindingType serviceBinding,
308 String transitionVerb,
309 ActionGroup actionGroup)
311 Permission result = null;
312 String workFlowServiceSuffix;
313 String transitionName;
314 if (transitionVerb != null) {
315 transitionName = transitionVerb;
316 workFlowServiceSuffix = WorkflowClient.SERVICE_AUTHZ_SUFFIX;
318 transitionName = ""; //since the transitionDef was null, we're assuming that this is the base workflow permission to be created
319 workFlowServiceSuffix = WorkflowClient.SERVICE_PATH;
322 String tenantId = tenantBinding.getId();
323 String resourceName = "/"
324 + serviceBinding.getName().toLowerCase().trim()
325 + workFlowServiceSuffix
327 String description = "A generated workflow permission for actiongroup " + actionGroup.name;
328 result = createPermission(tenantId, resourceName, description, actionGroup);
330 if (logger.isDebugEnabled() == true) {
331 logger.debug("Generated a workflow permission: "
332 + result.getResourceName()
333 + ":" + transitionName
334 + ":" + "tenant id=" + result.getTenantId()
335 + ":" + actionGroup.name);
341 private static PermissionRole createPermissionRole(EntityManager em,
342 Permission permission,
344 boolean enforceTenancy) throws Exception
346 PermissionRole permRole = new PermissionRole();
347 // Check to see if the tenant ID of the permission and the tenant ID of the role match
348 boolean tenantIdsMatch = role.getTenantId().equalsIgnoreCase(permission.getTenantId());
349 if (tenantIdsMatch == false && enforceTenancy == false) {
350 tenantIdsMatch = true; // If we don't need to enforce tenancy then we'll just consider them matched.
353 if (tenantIdsMatch == true) {
354 permRole.setSubject(SubjectType.ROLE);
356 // Set of the permission value list of the permrole
358 List<PermissionValue> permValues = new ArrayList<PermissionValue>();
359 PermissionValue permValue = new PermissionValue();
360 permValue.setPermissionId(permission.getCsid());
361 permValue.setResourceName(permission.getResourceName().toLowerCase());
362 permValue.setActionGroup(permission.getActionGroup());
363 permValues.add(permValue);
364 permRole.setPermission(permValues);
366 // Set of the role value list of the permrole
368 List<RoleValue> roleValues = new ArrayList<RoleValue>();
369 RoleValue rv = new RoleValue();
370 // This needs to use the qualified name, not the display name
371 rv.setRoleName(role.getRoleName());
372 rv.setRoleId(role.getCsid());
374 permRole.setRole(roleValues);
376 String errMsg = "The tenant ID of the role: " + role.getTenantId()
377 + " did not match the tenant ID of the permission: " + permission.getTenantId();
378 throw new Exception(errMsg);
384 private static Hashtable<String, String> getTenantNamesFromConfig(TenantBindingConfigReaderImpl tenantBindingConfigReader) {
386 // Note that this only handles tenants not marked as "createDisabled"
387 Hashtable<String, TenantBindingType> tenantBindings =
388 tenantBindingConfigReader.getTenantBindings();
389 Hashtable<String, String> tenantInfo = new Hashtable<String, String>();
390 for (TenantBindingType tenantBinding : tenantBindings.values()) {
391 String tId = tenantBinding.getId();
392 String tName = tenantBinding.getName();
393 tenantInfo.put(tId, tName);
394 if (logger.isDebugEnabled()) {
395 logger.debug("getTenantNamesFromConfig found configured tenant id: "+tId+" name: "+tName);
401 private static ArrayList<String> compileExistingTenants(Connection conn, Hashtable<String, String> tenantInfo)
402 throws SQLException, Exception {
403 Statement stmt = null;
404 ArrayList<String> existingTenants = new ArrayList<String>();
405 // First find or create the tenants
406 final String queryTenantSQL = "SELECT id,name FROM tenants";
408 stmt = conn.createStatement();
409 ResultSet rs = stmt.executeQuery(queryTenantSQL);
411 String tId = rs.getString("id");
412 String tName = rs.getString("name");
413 if(tenantInfo.containsKey(tId)) {
414 existingTenants.add(tId);
415 if(!tenantInfo.get(tId).equalsIgnoreCase(tName)) {
416 logger.warn("Configured name for tenant: "
417 +tId+" in repository: "+tName
418 +" does not match config'd name: "+ tenantInfo.get(tId));
423 } catch(Exception e) {
430 return existingTenants;
433 private static void createMissingTenants(Connection conn, Hashtable<String, String> tenantInfo,
434 ArrayList<String> existingTenants) throws SQLException, Exception {
435 // Need to define and look for a createDisabled attribute in tenant config
436 final String insertTenantSQL =
437 "INSERT INTO tenants (id,name,disabled,created_at) VALUES (?,?,FALSE,now())";
438 PreparedStatement pstmt = null;
440 pstmt = conn.prepareStatement(insertTenantSQL); // create a statement
441 for(String tId : tenantInfo.keySet()) {
442 if(existingTenants.contains(tId)) {
443 if (logger.isDebugEnabled()) {
444 logger.debug("createMissingTenants: tenant exists (skipping): "
445 +tenantInfo.get(tId));
449 pstmt.setString(1, tId); // set id param
450 pstmt.setString(2, tenantInfo.get(tId)); // set name param
451 if (logger.isDebugEnabled()) {
452 logger.debug("createMissingTenants adding entry for tenant: "+tId);
454 pstmt.executeUpdate();
457 } catch(Exception e) {
465 private static ArrayList<String> findOrCreateDefaultUsers(Connection conn, Hashtable<String, String> tenantInfo)
466 throws SQLException, Exception {
467 // Second find or create the users
468 Statement stmt = null;
469 PreparedStatement pstmt = null;
470 ArrayList<String> usersInRepo = new ArrayList<String>();
472 stmt = conn.createStatement();
473 ResultSet rs = stmt.executeQuery(QUERY_USERS_SQL);
475 String uName = rs.getString("username");
476 usersInRepo.add(uName);
479 pstmt = conn.prepareStatement(INSERT_USER_SQL); // create a statement
480 for(String tName : tenantInfo.values()) {
481 String adminAcctName = getDefaultAdminUserID(tName);
482 if(!usersInRepo.contains(adminAcctName)) {
483 String secEncPasswd = SecurityUtils.createPasswordHash(
484 adminAcctName, DEFAULT_ADMIN_PASSWORD);
485 pstmt.setString(1, adminAcctName); // set username param
486 pstmt.setString(2, secEncPasswd); // set passwd param
487 if (logger.isDebugEnabled()) {
488 logger.debug("createDefaultUsersAndAccounts adding user: "
489 +adminAcctName+" for tenant: "+tName);
491 pstmt.executeUpdate();
492 } else if (logger.isDebugEnabled()) {
493 logger.debug("createDefaultUsersAndAccounts: user: "+adminAcctName
494 +" already exists - skipping.");
498 String readerAcctName = getDefaultReaderUserID(tName);
499 if(!usersInRepo.contains(readerAcctName)) {
500 String secEncPasswd = SecurityUtils.createPasswordHash(
501 readerAcctName, DEFAULT_READER_PASSWORD);
502 pstmt.setString(1, readerAcctName); // set username param
503 pstmt.setString(2, secEncPasswd); // set passwd param
504 if (logger.isDebugEnabled()) {
505 logger.debug("createDefaultUsersAndAccounts adding user: "
506 +readerAcctName+" for tenant: "+tName);
508 pstmt.executeUpdate();
509 } else if (logger.isDebugEnabled()) {
510 logger.debug("createDefaultUsersAndAccounts: user: "+readerAcctName
511 +" already exists - skipping.");
515 } catch(Exception e) {
526 private static void findOrCreateDefaultAccounts(Connection conn, Hashtable<String, String> tenantInfo,
527 ArrayList<String> usersInRepo,
528 Hashtable<String, String> tenantAdminAcctCSIDs, Hashtable<String, String> tenantReaderAcctCSIDs)
529 throws SQLException, Exception {
530 // Third, create the accounts. Assume that if the users were already there,
531 // then the accounts were as well
532 PreparedStatement pstmt = null;
534 pstmt = conn.prepareStatement(INSERT_ACCOUNT_SQL); // create a statement
535 for(String tId : tenantInfo.keySet()) {
536 String tName = tenantInfo.get(tId);
537 String adminCSID = UUID.randomUUID().toString();
538 tenantAdminAcctCSIDs.put(tId, adminCSID);
539 String adminAcctName = getDefaultAdminUserID(tName);
540 if(!usersInRepo.contains(adminAcctName)) {
541 pstmt.setString(1, adminCSID); // set csid param
542 pstmt.setString(2, adminAcctName); // set email param (bogus)
543 pstmt.setString(3, adminAcctName); // set userid param
544 pstmt.setString(4, "Administrator");// set screen name param
545 if (logger.isDebugEnabled()) {
546 logger.debug("createDefaultAccounts adding account: "
547 +adminAcctName+" for tenant: "+tName);
549 pstmt.executeUpdate();
550 } else if (logger.isDebugEnabled()) {
551 logger.debug("createDefaultAccounts: user: "+adminAcctName
552 +" already exists - skipping account generation.");
555 String readerCSID = UUID.randomUUID().toString();
556 tenantReaderAcctCSIDs.put(tId, readerCSID);
557 String readerAcctName = getDefaultReaderUserID(tName);
558 if(!usersInRepo.contains(readerAcctName)) {
559 pstmt.setString(1, readerCSID); // set csid param
560 pstmt.setString(2, readerAcctName); // set email param (bogus)
561 pstmt.setString(3, readerAcctName); // set userid param
562 pstmt.setString(4, "Reader"); // set screen name param
563 if (logger.isDebugEnabled()) {
564 logger.debug("createDefaultAccounts adding account: "
565 +readerAcctName+" for tenant: "+tName);
567 pstmt.executeUpdate();
568 } else if (logger.isDebugEnabled()) {
569 logger.debug("createDefaultAccounts: user: "+readerAcctName
570 +" already exists - skipping account creation.");
574 } catch(Exception e) {
582 private static boolean findOrCreateTenantManagerUserAndAccount(Connection conn)
583 throws SQLException, Exception {
584 // Find or create the special tenant manager account.
585 // Later can make the user name for tenant manager be configurable, settable.
586 Statement stmt = null;
587 PreparedStatement pstmt = null;
588 boolean created = false;
590 boolean foundTMgrUser = false;
591 stmt = conn.createStatement();
592 ResultSet rs = stmt.executeQuery(QUERY_TENANT_MGR_USER_SQL);
593 // Should only find one - only consider it
595 String uName = rs.getString("username");
596 foundTMgrUser = uName.equals(TENANT_MANAGER_USER);
600 pstmt = conn.prepareStatement(INSERT_USER_SQL); // create a statement
601 String secEncPasswd = SecurityUtils.createPasswordHash(
602 TENANT_MANAGER_USER, DEFAULT_TENANT_MANAGER_PASSWORD);
603 pstmt.setString(1, TENANT_MANAGER_USER); // set username param
604 pstmt.setString(2, secEncPasswd); // set passwd param
605 if (logger.isDebugEnabled()) {
606 logger.debug("findOrCreateTenantManagerUserAndAccount adding tenant manager user: "
607 +TENANT_MANAGER_USER);
609 pstmt.executeUpdate();
611 // Now create the account to match
612 pstmt = conn.prepareStatement(INSERT_ACCOUNT_SQL); // create a statement
613 pstmt.setString(1, AuthN.TENANT_MANAGER_ACCT_ID); // set csid param
614 pstmt.setString(2, DEFAULT_TENANT_MANAGER_EMAIL); // set email param (bogus)
615 pstmt.setString(3, TENANT_MANAGER_USER); // set userid param
616 pstmt.setString(4, TENANT_MANAGER_SCREEN_NAME);// set screen name param
617 if (logger.isDebugEnabled()) {
618 logger.debug("findOrCreateTenantManagerUserAndAccount adding tenant manager account: "
619 +TENANT_MANAGER_USER);
621 pstmt.executeUpdate();
624 } else if (logger.isDebugEnabled()) {
625 logger.debug("findOrCreateTenantManagerUserAndAccount: tenant manager: "+TENANT_MANAGER_USER
626 +" already exists.");
628 } catch(Exception e) {
639 private static void bindDefaultAccountsToTenants(Connection conn, DatabaseProductType databaseProductType,
640 Hashtable<String, String> tenantInfo, ArrayList<String> usersInRepo,
641 Hashtable<String, String> tenantAdminAcctCSIDs, Hashtable<String, String> tenantReaderAcctCSIDs)
642 throws SQLException, Exception {
643 // Fourth, bind accounts to tenants. Assume that if the users were already there,
644 // then the accounts were bound to tenants correctly
645 PreparedStatement pstmt = null;
647 String insertAccountTenantSQL;
648 if (databaseProductType == DatabaseProductType.MYSQL) {
649 insertAccountTenantSQL =
650 "INSERT INTO accounts_tenants (TENANTS_ACCOUNTS_COMMON_CSID,tenant_id) "
652 } else if (databaseProductType == DatabaseProductType.POSTGRESQL) {
653 insertAccountTenantSQL =
654 "INSERT INTO accounts_tenants (HJID, TENANTS_ACCOUNTS_COMMON_CSID,tenant_id) "
655 + " VALUES(nextval('hibernate_sequence'), ?, ?)";
657 throw new Exception("Unrecognized database system.");
659 pstmt = conn.prepareStatement(insertAccountTenantSQL); // create a statement
660 for(String tId : tenantInfo.keySet()) {
661 String tName = tenantInfo.get(tId);
662 if(!usersInRepo.contains(getDefaultAdminUserID(tName))) {
663 String adminAcct = tenantAdminAcctCSIDs.get(tId);
664 pstmt.setString(1, adminAcct); // set acct CSID param
665 pstmt.setString(2, tId); // set tenant_id param
666 if (logger.isDebugEnabled()) {
667 logger.debug("createDefaultAccounts binding account id: "
668 +adminAcct+" to tenant id: "+tId);
670 pstmt.executeUpdate();
672 if(!usersInRepo.contains(getDefaultReaderUserID(tName))) {
673 String readerAcct = tenantReaderAcctCSIDs.get(tId);
674 pstmt.setString(1, readerAcct); // set acct CSID param
675 pstmt.setString(2, tId); // set tenant_id param
676 if (logger.isDebugEnabled()) {
677 logger.debug("createDefaultAccounts binding account id: "
678 +readerAcct+" to tenant id: "+tId);
680 pstmt.executeUpdate();
684 } catch(Exception e) {
693 private static String findOrCreateDefaultRoles(Connection conn, Hashtable<String, String> tenantInfo,
694 Hashtable<String, String> tenantAdminRoleCSIDs, Hashtable<String, String> tenantReaderRoleCSIDs)
695 throws SQLException, Exception {
696 // Fifth, fetch and save the default roles
697 String springAdminRoleCSID = null;
698 Statement stmt = null;
699 PreparedStatement pstmt = null;
701 final String querySpringRole =
702 "SELECT csid from roles WHERE rolename='"+SPRING_ADMIN_ROLE+"'";
703 stmt = conn.createStatement();
704 ResultSet rs = stmt.executeQuery(querySpringRole);
706 springAdminRoleCSID = rs.getString(1);
707 if (logger.isDebugEnabled()) {
708 logger.debug("createDefaultAccounts found Spring Admin role: "
709 +springAdminRoleCSID);
712 final String insertSpringAdminRoleSQL =
713 "INSERT INTO roles (csid, rolename, displayName, rolegroup, created_at, tenant_id) "
714 + "VALUES ('-1', 'ROLE_SPRING_ADMIN', 'SPRING_ADMIN', 'Spring Security Administrator', now(), '0')";
715 stmt.executeUpdate(insertSpringAdminRoleSQL);
716 springAdminRoleCSID = "-1";
717 if (logger.isDebugEnabled()) {
718 logger.debug("createDefaultAccounts CREATED Spring Admin role: "
719 +springAdminRoleCSID);
723 final String getRoleCSIDSql =
724 "SELECT csid from roles WHERE tenant_id=? and rolename=?";
725 pstmt = conn.prepareStatement(getRoleCSIDSql); // create a statement
727 for(String tId : tenantInfo.keySet()) {
728 pstmt.setString(1, tId); // set tenant_id param
729 pstmt.setString(2, getDefaultAdminRole(tId)); // set rolename param
730 rs = pstmt.executeQuery();
731 // extract data from the ResultSet
733 throw new RuntimeException("Cannot find role: "+getDefaultAdminRole(tId)
734 +" for tenant id: "+tId+" in roles!");
736 String tenantAdminRoleCSID = rs.getString(1);
737 if (logger.isDebugEnabled()) {
738 logger.debug("createDefaultAccounts found role: "
739 +getDefaultAdminRole(tId)+"("+tenantAdminRoleCSID
740 +") for tenant id: "+tId);
742 tenantAdminRoleCSIDs.put(tId, tenantAdminRoleCSID);
743 pstmt.setString(1, tId); // set tenant_id param
744 pstmt.setString(2, getDefaultReaderRole(tId)); // set rolename param
746 rs = pstmt.executeQuery();
747 // extract data from the ResultSet
749 throw new RuntimeException("Cannot find role: "+getDefaultReaderRole(tId)
750 +" for tenant id: "+tId+" in roles!");
752 String tenantReaderRoleCSID = rs.getString(1);
753 if (logger.isDebugEnabled()) {
754 logger.debug("createDefaultAccounts found role: "
755 +getDefaultReaderRole(tId)+"("+tenantReaderRoleCSID
756 +") for tenant id: "+tId);
758 tenantReaderRoleCSIDs.put(tId, tenantReaderRoleCSID);
762 } catch(Exception e) {
770 return springAdminRoleCSID;
773 private static String findTenantManagerRole(Connection conn )
774 throws SQLException, RuntimeException, Exception {
775 String tenantMgrRoleCSID = null;
776 PreparedStatement pstmt = null;
778 String rolename = getQualifiedRoleName(ALL_TENANTS_MANAGER_TENANT_ID,
779 ROLE_ALL_TENANTS_MANAGER);
780 pstmt = conn.prepareStatement(GET_TENANT_MGR_ROLE_SQL); // create a statement
782 pstmt.setString(1, rolename); // set rolename param
783 rs = pstmt.executeQuery();
785 tenantMgrRoleCSID = rs.getString(1);
786 if (logger.isDebugEnabled()) {
787 logger.debug("findTenantManagerRole found Tenant Mgr role: "
792 } catch(Exception e) {
798 if(tenantMgrRoleCSID==null)
799 throw new RuntimeException("findTenantManagerRole: Cound not find tenant Manager Role!");
800 return tenantMgrRoleCSID;
803 private static void bindAccountsToRoles(Connection conn, DatabaseProductType databaseProductType,
804 Hashtable<String, String> tenantInfo, ArrayList<String> usersInRepo,
805 String springAdminRoleCSID,
806 Hashtable<String, String> tenantAdminRoleCSIDs, Hashtable<String, String> tenantReaderRoleCSIDs,
807 Hashtable<String, String> tenantAdminAcctCSIDs, Hashtable<String, String> tenantReaderAcctCSIDs)
808 throws SQLException, Exception {
809 // Sixth, bind the accounts to roles. If the users already existed,
810 // we'll assume they were set up correctly.
811 PreparedStatement pstmt = null;
813 String insertAccountRoleSQL;
814 if (databaseProductType == DatabaseProductType.MYSQL) {
815 insertAccountRoleSQL = INSERT_ACCOUNT_ROLE_SQL_MYSQL;
816 } else if (databaseProductType == DatabaseProductType.POSTGRESQL) {
817 insertAccountRoleSQL = INSERT_ACCOUNT_ROLE_SQL_POSTGRES;
819 throw new Exception("Unrecognized database system.");
821 if (logger.isDebugEnabled()) {
822 logger.debug("createDefaultAccounts binding accounts to roles with SQL:\n"
823 +insertAccountRoleSQL);
825 pstmt = conn.prepareStatement(insertAccountRoleSQL); // create a statement
826 for(String tId : tenantInfo.keySet()) {
827 String adminUserId = getDefaultAdminUserID(tenantInfo.get(tId));
828 if(!usersInRepo.contains(adminUserId)) {
829 String adminAcct = tenantAdminAcctCSIDs.get(tId);
830 String adminRoleId = tenantAdminRoleCSIDs.get(tId);
831 pstmt.setString(1, adminAcct); // set acct CSID param
832 pstmt.setString(2, adminUserId); // set user_id param
833 pstmt.setString(3, adminRoleId); // set role_id param
834 pstmt.setString(4, getDefaultAdminRole(tId)); // set rolename param
835 if (logger.isDebugEnabled()) {
836 logger.debug("createDefaultAccounts binding account: "
837 +adminUserId+" to Admin role("+adminRoleId
838 +") for tenant id: "+tId);
840 pstmt.executeUpdate();
841 // Now add the Spring Admin Role to the admin accounts
842 pstmt.setString(3, springAdminRoleCSID); // set role_id param
843 pstmt.setString(4, SPRING_ADMIN_ROLE); // set rolename param
844 if (logger.isDebugEnabled()) {
845 logger.debug("createDefaultAccounts binding account: "
846 +adminUserId+" to Spring Admin role: "+springAdminRoleCSID);
848 pstmt.executeUpdate();
850 String readerUserId = getDefaultReaderUserID(tenantInfo.get(tId));
851 if(!usersInRepo.contains(readerUserId)) {
852 String readerAcct = tenantReaderAcctCSIDs.get(tId);
853 String readerRoleId = tenantReaderRoleCSIDs.get(tId);
854 pstmt.setString(1, readerAcct); // set acct CSID param
855 pstmt.setString(2, readerUserId); // set user_id param
856 pstmt.setString(3, readerRoleId); // set role_id param
857 pstmt.setString(4, getDefaultReaderRole(tId)); // set rolename param
858 if (logger.isDebugEnabled()) {
859 logger.debug("createDefaultAccounts binding account: "
860 +readerUserId+" to Reader role("+readerRoleId
861 +") for tenant id: "+tId);
863 pstmt.executeUpdate();
867 } catch(Exception e) {
875 private static void bindTenantManagerAccountRole(Connection conn, DatabaseProductType databaseProductType,
876 String tenantManagerUserID, String tenantManagerAccountID, String tenantManagerRoleID, String tenantManagerRoleName )
877 throws SQLException, Exception {
878 PreparedStatement pstmt = null;
880 String insertAccountRoleSQL;
881 if (databaseProductType == DatabaseProductType.MYSQL) {
882 insertAccountRoleSQL = INSERT_ACCOUNT_ROLE_SQL_MYSQL;
883 } else if (databaseProductType == DatabaseProductType.POSTGRESQL) {
884 insertAccountRoleSQL = INSERT_ACCOUNT_ROLE_SQL_POSTGRES;
886 throw new Exception("Unrecognized database system.");
888 if (logger.isDebugEnabled()) {
889 logger.debug("bindTenantManagerAccountRole binding account to role with SQL:\n"
890 +insertAccountRoleSQL);
892 pstmt = conn.prepareStatement(insertAccountRoleSQL); // create a statement
893 pstmt.setString(1, tenantManagerAccountID); // set acct CSID param
894 pstmt.setString(2, tenantManagerUserID); // set user_id param
895 pstmt.setString(3, tenantManagerRoleID); // set role_id param
896 pstmt.setString(4, tenantManagerRoleName); // set rolename param
897 if (logger.isDebugEnabled()) {
898 logger.debug("bindTenantManagerAccountRole binding user: "
899 +tenantManagerUserID+" to Admin role("+tenantManagerRoleName+")");
901 pstmt.executeUpdate();
902 /* At this point, tenant manager should not need the Spring Admin Role
903 pstmt.setString(3, springAdminRoleCSID); // set role_id param
904 pstmt.setString(4, SPRING_ADMIN_ROLE); // set rolename param
905 if (logger.isDebugEnabled()) {
906 logger.debug("createDefaultAccounts binding account: "
907 +adminUserId+" to Spring Admin role: "+springAdminRoleCSID);
909 pstmt.executeUpdate();
912 } catch(Exception e) {
920 public static void createDefaultAccounts(
921 TenantBindingConfigReaderImpl tenantBindingConfigReader,
922 DatabaseProductType databaseProductType,
923 String cspaceDatabaseName) throws Exception {
925 logger.debug("ServiceMain.createDefaultAccounts starting...");
927 Hashtable<String, String> tenantInfo = getTenantNamesFromConfig(tenantBindingConfigReader);
928 Connection conn = null;
929 // TODO - need to put in tests for existence first.
930 // We could just look for the accounts per tenant up front, and assume that
931 // the rest is there if the accounts are.
932 // Could add a sql script to remove these if need be - Spring only does roles,
933 // and we're not touching that, so we could safely toss the
934 // accounts, users, account-tenants, account-roles, and start over.
936 conn = getConnection(cspaceDatabaseName);
937 ArrayList<String> existingTenants = compileExistingTenants(conn, tenantInfo);
939 // Note that this only creates tenants not marked as "createDisabled"
940 createMissingTenants(conn, tenantInfo, existingTenants);
942 ArrayList<String> usersInRepo = findOrCreateDefaultUsers(conn, tenantInfo);
944 Hashtable<String, String> tenantAdminAcctCSIDs = new Hashtable<String, String>();
945 Hashtable<String, String> tenantReaderAcctCSIDs = new Hashtable<String, String>();
946 findOrCreateDefaultAccounts(conn, tenantInfo, usersInRepo,
947 tenantAdminAcctCSIDs, tenantReaderAcctCSIDs);
949 bindDefaultAccountsToTenants(conn, databaseProductType, tenantInfo, usersInRepo,
950 tenantAdminAcctCSIDs, tenantReaderAcctCSIDs);
952 Hashtable<String, String> tenantAdminRoleCSIDs = new Hashtable<String, String>();
953 Hashtable<String, String> tenantReaderRoleCSIDs = new Hashtable<String, String>();
954 String springAdminRoleCSID = findOrCreateDefaultRoles(conn, tenantInfo,
955 tenantAdminRoleCSIDs, tenantReaderRoleCSIDs);
957 bindAccountsToRoles(conn, databaseProductType,
958 tenantInfo, usersInRepo, springAdminRoleCSID,
959 tenantAdminRoleCSIDs, tenantReaderRoleCSIDs,
960 tenantAdminAcctCSIDs, tenantReaderAcctCSIDs);
962 boolean createdTenantMgrAccount = findOrCreateTenantManagerUserAndAccount(conn);
963 if(createdTenantMgrAccount) {
964 // If we created the account, we need to create the bindings. Otherwise, assume they
965 // are all set (from previous initialization).
966 String tenantManagerRoleCSID = findTenantManagerRole(conn);
967 bindTenantManagerAccountRole(conn, databaseProductType,
968 TENANT_MANAGER_USER, AuthN.TENANT_MANAGER_ACCT_ID,
969 tenantManagerRoleCSID, ROLE_ALL_TENANTS_MANAGER);
971 } catch (Exception e) {
972 logger.debug("Exception in createDefaultAccounts: " + e.getLocalizedMessage());
978 } catch (SQLException sqle) {
979 if (logger.isDebugEnabled()) {
980 logger.debug("SQL Exception closing statement/connection: " + sqle.getLocalizedMessage());
986 private static String getDefaultAdminRole(String tenantId) {
987 return ROLE_PREFIX+tenantId+TENANT_ADMIN_ROLE_SUFFIX;
990 private static String getDefaultReaderRole(String tenantId) {
991 return ROLE_PREFIX+tenantId+TENANT_READER_ROLE_SUFFIX;
994 private static String getDefaultAdminUserID(String tenantName) {
995 return TENANT_ADMIN_ACCT_PREFIX+tenantName;
998 private static String getDefaultReaderUserID(String tenantName) {
999 return TENANT_READER_ACCT_PREFIX+tenantName;
1002 static public PermissionAction createPermissionAction(Permission perm,
1003 ActionType actionType) {
1004 PermissionAction pa = new PermissionAction();
1006 CSpaceAction action = URIResourceImpl.getAction(actionType);
1007 URIResourceImpl uriRes = new URIResourceImpl(perm.getTenantId(),
1008 perm.getResourceName(), action);
1009 pa.setName(actionType);
1010 pa.setObjectIdentity(uriRes.getHashedId().toString());
1011 pa.setObjectIdentityResource(uriRes.getId());
1016 static public PermissionAction update(Permission perm, PermissionAction permAction) {
1017 PermissionAction pa = new PermissionAction();
1019 CSpaceAction action = URIResourceImpl.getAction(permAction.getName());
1020 URIResourceImpl uriRes = new URIResourceImpl(perm.getTenantId(),
1021 perm.getResourceName(), action);
1022 pa.setObjectIdentity(uriRes.getHashedId().toString());
1023 pa.setObjectIdentityResource(uriRes.getId());
1028 private static HashSet<String> getTransitionVerbList(TenantBindingType tenantBinding, ServiceBindingType serviceBinding) {
1029 HashSet<String> result = new HashSet<String>();
1031 TransitionDefList transitionDefList = getTransitionDefList(tenantBinding, serviceBinding);
1032 for (TransitionDef transitionDef : transitionDefList.getTransitionDef()) {
1033 String transitionVerb = transitionDef.getName();
1034 String[] tokens = transitionVerb.split("_"); // Split the verb into words. The workflow verbs are compound words combined with the '_' character.
1035 result.add(tokens[0]); // We only care about the first word.
1041 private static TransitionDefList getTransitionDefList(TenantBindingType tenantBinding, ServiceBindingType serviceBinding) {
1042 TransitionDefList result = null;
1044 String serviceObjectName = serviceBinding.getObject().getName();
1045 DocumentHandler docHandler = ServiceConfigUtils.createDocumentHandlerInstance(
1046 tenantBinding, serviceBinding);
1047 Lifecycle lifecycle = docHandler.getLifecycle(serviceObjectName);
1048 if (lifecycle != null) {
1049 result = lifecycle.getTransitionDefList();
1051 } catch (Exception e) {
1052 // Ignore this exception and return an empty non-null TransitionDefList
1055 if (result == null) {
1056 if (serviceBinding.getType().equalsIgnoreCase(ServiceBindingUtils.SERVICE_TYPE_SECURITY) == false) {
1057 logger.debug("Could not retrieve a lifecycle transition definition list from: "
1058 + serviceBinding.getName()
1059 + " with tenant ID = "
1060 + tenantBinding.getId());
1062 // return an empty list
1063 result = new TransitionDefList();
1065 logger.debug("Successfully retrieved a lifecycle transition definition list from: "
1066 + serviceBinding.getName()
1067 + " with tenant ID = "
1068 + tenantBinding.getId());
1074 public static void createDefaultWorkflowPermissions(TenantBindingConfigReaderImpl tenantBindingConfigReader) throws Exception //FIXME: REM - 4/11/2012 - Rename to createWorkflowPermissions
1076 AuthZ.get().login(); //login to Spring Security manager
1078 EntityManagerFactory emf = JpaStorageUtils.getEntityManagerFactory(JpaStorageUtils.CS_PERSISTENCE_UNIT);
1079 EntityManager em = null;
1082 em = emf.createEntityManager();
1084 Hashtable<String, TenantBindingType> tenantBindings =
1085 tenantBindingConfigReader.getTenantBindings();
1086 for (String tenantId : tenantBindings.keySet()) {
1087 logger.info(String.format("Creating/verifying workflow permissions for tenant ID=%s.", tenantId));
1088 TenantBindingType tenantBinding = tenantBindings.get(tenantId);
1089 Role adminRole = AuthorizationCommon.getRole(em, tenantBinding.getId(), ROLE_TENANT_ADMINISTRATOR);
1090 Role readonlyRole = AuthorizationCommon.getRole(em, tenantBinding.getId(), ROLE_TENANT_READER);
1091 for (ServiceBindingType serviceBinding : tenantBinding.getServiceBindings()) {
1092 String prop = ServiceBindingUtils.getPropertyValue(serviceBinding, REFRESH_AUTZ_PROP);
1093 if (prop == null ? true : Boolean.parseBoolean(prop)) {
1095 em.getTransaction().begin();
1096 TransitionDefList transitionDefList = getTransitionDefList(tenantBinding, serviceBinding);
1097 HashSet<String> transitionVerbList = getTransitionVerbList(tenantBinding, serviceBinding);
1098 for (String transitionVerb : transitionVerbList) {
1100 // Create the permission for the admin role
1101 Permission adminPerm = createWorkflowPermission(tenantBinding, serviceBinding, transitionVerb, ACTIONGROUP_CRUDL);
1102 persist(em, adminPerm, adminRole, true, ACTIONGROUP_CRUDL);
1104 // Create the permission for the read-only role
1105 Permission readonlyPerm = createWorkflowPermission(tenantBinding, serviceBinding, transitionVerb, ACTIONGROUP_RL);
1106 persist(em, readonlyPerm, readonlyRole, true, ACTIONGROUP_RL); // Persist/store the permission and permrole records and related Spring Security info
1108 em.getTransaction().commit();
1109 } catch (IllegalStateException e) {
1110 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.
1113 logger.warn("AuthZ refresh service binding property is set to FALSE so default permissions will NOT be refreshed for: "
1114 + serviceBinding.getName());
1119 } catch (Exception e) {
1120 if (em != null && em.getTransaction().isActive()) {
1121 em.getTransaction().rollback();
1123 if (logger.isDebugEnabled()) {
1124 logger.debug("Caught exception and rolling back permission creation: ", e);
1129 JpaStorageUtils.releaseEntityManagerFactory(emf);
1134 private static PermissionRoleRel findPermRoleRel(EntityManager em, String permissionId, String RoleId) {
1135 PermissionRoleRel result = null;
1138 String whereClause = "where permissionId = :id and roleId = :roleId";
1139 HashMap<String, Object> params = new HashMap<String, Object>();
1140 params.put("id", permissionId);
1141 params.put("roleId", RoleId);
1143 result = (PermissionRoleRel) JpaStorageUtils.getEntity(em,
1144 PermissionRoleRel.class.getCanonicalName(), whereClause, params);
1145 } catch (Exception e) {
1146 //Do nothing. Will return null;
1153 * Persists the Permission, PermissionRoleRel, and Spring Security table entries all in one transaction
1155 private static void persist(EntityManager em, Permission permission, Role role, boolean enforceTenancy, ActionGroup actionGroup) throws Exception {
1156 AuthorizationStore authzStore = new AuthorizationStore();
1157 // First persist the Permission record
1158 authzStore.store(em, permission);
1160 // If the PermRoleRel doesn't already exists then relate the permission and the role in a new PermissionRole (the service payload)
1161 // Create a PermissionRoleRel (the database relation table for the permission and role)
1162 PermissionRoleRel permRoleRel = findPermRoleRel(em, permission.getCsid(), role.getCsid());
1163 if (permRoleRel == null) {
1164 PermissionRole permRole = createPermissionRole(em, permission, role, enforceTenancy);
1165 List<PermissionRoleRel> permRoleRels = new ArrayList<PermissionRoleRel>();
1166 PermissionRoleUtil.buildPermissionRoleRel(em, permRole, SubjectType.ROLE, permRoleRels, false /*not for delete*/);
1167 for (PermissionRoleRel prr : permRoleRels) {
1168 authzStore.store(em, prr);
1170 Profiler profiler = new Profiler(AuthorizationCommon.class, 2);
1172 // Add a corresponding entry in the Spring Security Tables
1173 addPermissionsForUri(permission, permRole);
1175 logger.debug("Finished full perm generation for "
1176 + ":" + permission.getTenantId()
1177 + ":" + permission.getResourceName()
1178 + ":" + actionGroup.getName()
1179 + ":" + profiler.getCumulativeTime());