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.Hashtable;
12 import java.util.List;
13 import java.util.UUID;
15 import javax.naming.NamingException;
16 import javax.persistence.EntityManager;
17 import javax.persistence.EntityManagerFactory;
19 import org.collectionspace.authentication.AuthN;
20 import org.collectionspace.services.authorization.AuthZ;
21 import org.collectionspace.services.authorization.CSpaceAction;
22 import org.collectionspace.services.authorization.PermissionException;
23 import org.collectionspace.services.authorization.PermissionRole;
24 import org.collectionspace.services.authorization.PermissionRoleRel;
25 import org.collectionspace.services.authorization.PermissionValue;
26 import org.collectionspace.services.authorization.Role;
27 import org.collectionspace.services.authorization.RoleValue;
28 import org.collectionspace.services.authorization.SubjectType;
29 import org.collectionspace.services.authorization.URIResourceImpl;
30 import org.collectionspace.services.authorization.perms.ActionType;
31 import org.collectionspace.services.authorization.perms.EffectType;
32 import org.collectionspace.services.authorization.perms.Permission;
33 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;
49 import org.collectionspace.services.lifecycle.Lifecycle;
50 import org.collectionspace.services.lifecycle.TransitionDef;
51 import org.collectionspace.services.lifecycle.TransitionDefList;
53 //import org.mortbay.log.Log;
54 import org.slf4j.Logger;
55 import org.slf4j.LoggerFactory;
57 import org.springframework.security.acls.model.AlreadyExistsException;
60 public class AuthorizationCommon {
62 final public static String REFRESH_AUTZ_PROP = "refreshAuthZOnStartup";
64 // ActionGroup labels/constants
68 final public static String ACTIONGROUP_CRUDL_NAME = "CRUDL";
69 final public static ActionType[] ACTIONSET_CRUDL = {ActionType.CREATE, ActionType.READ, ActionType.UPDATE, ActionType.DELETE, ActionType.SEARCH};
71 final public static String ACTIONGROUP_RL_NAME = "RL";
72 final public static ActionType[] ACTIONSET_RL = {ActionType.READ, ActionType.SEARCH};
76 * Inner class to deal with predefined ADMIN and READER action groupds
78 public class ActionGroup {
83 static ActionGroup ACTIONGROUP_CRUDL;
84 static ActionGroup ACTIONGROUP_RL;
86 // A static block to initialize the predefined action groups
88 AuthorizationCommon ac = new AuthorizationCommon();
90 ACTIONGROUP_CRUDL = ac.new ActionGroup();
91 ACTIONGROUP_CRUDL.name = ACTIONGROUP_CRUDL_NAME;
92 ACTIONGROUP_CRUDL.actions = ACTIONSET_CRUDL;
94 ACTIONGROUP_RL = ac.new ActionGroup();
95 ACTIONGROUP_RL.name = ACTIONGROUP_RL_NAME;
96 ACTIONGROUP_RL.actions = ACTIONSET_RL;
100 final static Logger logger = LoggerFactory.getLogger(AuthorizationCommon.class);
103 // The "super" role has a predefined ID of "0" and a tenant ID of "0";
105 final public static String ROLE_ALL_TENANTS_MANAGER = "ALL_TENANTS_MANAGER";
106 final public static String ROLE_ALL_TENANTS_MANAGER_ID = "0";
107 final public static String ALL_TENANTS_MANAGER_TENANT_ID = "0";
109 final public static String ROLE_TENANT_ADMINISTRATOR = "TENANT_ADMINISTRATOR";
110 final public static String ROLE_TENANT_READER = "TENANT_READER";
112 public static final String TENANT_MANAGER_USER = "tenantManager";
113 public static final String TENANT_MANAGER_SCREEN_NAME = TENANT_MANAGER_USER;
114 public static final String DEFAULT_TENANT_MANAGER_PASSWORD = "manage";
115 public static final String DEFAULT_TENANT_MANAGER_EMAIL = "tenantManager@collectionspace.org";
117 public static final String TENANT_ADMIN_ACCT_PREFIX = "admin@";
118 public static final String TENANT_READER_ACCT_PREFIX = "reader@";
119 public static final String ROLE_PREFIX = "ROLE_";
120 public static final String SPRING_ADMIN_ROLE = "ROLE_SPRING_ADMIN";
121 public static final String TENANT_ADMIN_ROLE_SUFFIX = "_TENANT_ADMINISTRATOR";
122 public static final String TENANT_READER_ROLE_SUFFIX = "_TENANT_READER";
123 public static final String DEFAULT_ADMIN_PASSWORD = "Administrator";
124 public static final String DEFAULT_READER_PASSWORD = "reader";
126 public static final String ROLE_SPRING_ADMIN_ID = "-1";
127 public static final String ROLE_SPRING_ADMIN_NAME = "ROLE_SPRING_ADMIN";
129 // SQL for init tasks
130 final private static String INSERT_ACCOUNT_ROLE_SQL_MYSQL =
131 "INSERT INTO accounts_roles(account_id, user_id, role_id, role_name, created_at)"
132 +" VALUES(?, ?, ?, ?, now())";
133 final private static String INSERT_ACCOUNT_ROLE_SQL_POSTGRES =
134 "INSERT INTO accounts_roles(HJID, account_id, user_id, role_id, role_name, created_at)"
135 +" VALUES(nextval('hibernate_sequence'), ?, ?, ?, ?, now())";
136 final private static String QUERY_USERS_SQL =
137 "SELECT username FROM users WHERE username LIKE '"
138 +TENANT_ADMIN_ACCT_PREFIX+"%' OR username LIKE '"+TENANT_READER_ACCT_PREFIX+"%'";
139 final private static String INSERT_USER_SQL =
140 "INSERT INTO users (username,passwd, created_at) VALUES (?,?, now())";
141 final private static String INSERT_ACCOUNT_SQL =
142 "INSERT INTO accounts_common "
143 + "(csid, email, userid, status, screen_name, metadata_protection, roles_protection, created_at) "
144 + "VALUES (?,?,?,'ACTIVE',?, 'immutable', 'immutable', now())";
146 // TENANT MANAGER specific SQL
147 final private static String QUERY_TENANT_MGR_USER_SQL =
148 "SELECT username FROM users WHERE username = '"+TENANT_MANAGER_USER+"'";
149 final private static String GET_TENANT_MGR_ROLE_SQL =
150 "SELECT csid from roles WHERE tenant_id='"+ALL_TENANTS_MANAGER_TENANT_ID+"' and rolename=?";
152 public static Role getRole(String tenantId, String displayName) {
155 String roleName = AuthorizationCommon.getQualifiedRoleName(tenantId, displayName);
156 role = AuthorizationStore.getRoleByName(roleName, tenantId);
161 public static Role getRole(EntityManager em, String tenantId, String displayName) {
164 String roleName = AuthorizationCommon.getQualifiedRoleName(tenantId, displayName);
165 role = AuthorizationStore.getRoleByName(em, roleName, tenantId);
171 public static Role createRole(String tenantId, String name, String description) {
172 return createRole(tenantId, name, description, false /* mutable by default */);
175 public static Role createRole(String tenantId, String name, String description, boolean immutable) {
176 Role role = new Role();
178 role.setCreatedAtItem(new Date());
179 role.setDisplayName(name);
180 String roleName = AuthorizationCommon.getQualifiedRoleName(tenantId, name);
181 role.setRoleName(roleName);
182 String id = UUID.randomUUID().toString(); //FIXME: The qualified role name should be unique enough to use as an ID/key
184 role.setDescription(description);
185 role.setTenantId(tenantId);
186 if (immutable == true) {
187 role.setMetadataProtection(RoleClient.IMMUTABLE);
188 role.setPermsProtection(RoleClient.IMMUTABLE);
195 * Add permission to the Spring Security tables
196 * with assumption that resource is of type URI
197 * @param permission configuration
199 public static void addPermissionsForUri(Permission perm,
200 PermissionRole permRole) throws PermissionException {
202 // First check the integrity of the incoming arguments.
204 if (!perm.getCsid().equals(permRole.getPermission().get(0).getPermissionId())) {
205 throw new IllegalArgumentException("permission ids do not"
206 + " match for role=" + permRole.getRole().get(0).getRoleName()
207 + " with permissionId=" + permRole.getPermission().get(0).getPermissionId()
208 + " for permission with csid=" + perm.getCsid());
211 List<String> principals = new ArrayList<String>();
212 for (RoleValue roleValue : permRole.getRole()) {
213 principals.add(roleValue.getRoleName());
215 List<PermissionAction> permActions = perm.getAction();
216 for (PermissionAction permAction : permActions) {
218 CSpaceAction action = URIResourceImpl.getAction(permAction.getName());
219 URIResourceImpl uriRes = new URIResourceImpl(perm.getTenantId(),
220 perm.getResourceName(), action);
221 boolean grant = perm.getEffect().equals(EffectType.PERMIT) ? true : false;
222 AuthZ.get().addPermissions(uriRes, principals.toArray(new String[0]), grant);//CSPACE-4967
223 } catch (PermissionException e) {
225 // Only throw the exception if it is *not* an already-exists exception
227 if (e.getCause() instanceof AlreadyExistsException == false) {
234 private static Connection getConnection(String databaseName) throws NamingException, SQLException {
235 return JDBCTools.getConnection(JDBCTools.CSPACE_DATASOURCE_NAME,
240 * Spring security seems to require that all of our role names start
241 * with the ROLE_PREFIX string.
243 public static String getQualifiedRoleName(String tenantId, String name) {
244 String result = name;
246 String qualifiedName = ROLE_PREFIX + tenantId.toUpperCase() + "_" + name.toUpperCase();
247 if (name.equals(qualifiedName) == false) {
248 result = qualifiedName;
254 private static ActionGroup getActionGroup(String actionGroupStr) {
255 ActionGroup result = null;
257 if (actionGroupStr.equalsIgnoreCase(ACTIONGROUP_CRUDL_NAME)) {
258 result = ACTIONGROUP_CRUDL;
259 } else if (actionGroupStr.equalsIgnoreCase(ACTIONGROUP_RL_NAME)) {
260 result = ACTIONGROUP_RL;
266 public static Permission createPermission(String tenantId,
269 String actionGroupStr) {
270 Permission result = null;
272 ActionGroup actionGroup = getActionGroup(actionGroupStr);
273 result = createPermission(tenantId, resourceName, description, actionGroup);
278 private static Permission createPermission(String tenantId,
281 ActionGroup actionGroup) {
283 + "-" + resourceName.replace('/', '_') // Remove the slashes so the ID can be used in a URI/URL
284 + "-" + actionGroup.name;
285 Permission perm = new Permission();
287 perm.setDescription(description);
288 perm.setCreatedAtItem(new Date());
289 perm.setResourceName(resourceName.toLowerCase().trim());
290 perm.setEffect(EffectType.PERMIT);
291 perm.setTenantId(tenantId);
293 perm.setActionGroup(actionGroup.name);
294 ArrayList<PermissionAction> pas = new ArrayList<PermissionAction>();
296 for (ActionType actionType : actionGroup.actions) {
297 PermissionAction permAction = createPermissionAction(perm, actionType);
304 private static Permission createWorkflowPermission(TenantBindingType tenantBinding,
305 ServiceBindingType serviceBinding,
306 TransitionDef transitionDef,
307 ActionGroup actionGroup)
309 Permission result = null;
310 String workFlowServiceSuffix;
311 String transitionName;
312 if (transitionDef != null) {
313 transitionName = transitionDef.getName();
314 workFlowServiceSuffix = WorkflowClient.SERVICE_AUTHZ_SUFFIX;
316 transitionName = ""; //since the transitionDef was null, we're assuming that this is the base workflow permission to be created
317 workFlowServiceSuffix = WorkflowClient.SERVICE_PATH;
320 String tenantId = tenantBinding.getId();
321 String resourceName = "/"
322 + serviceBinding.getName().toLowerCase().trim()
323 + workFlowServiceSuffix
325 String description = "A generated workflow permission for actiongroup " + actionGroup.name;
326 result = createPermission(tenantId, resourceName, description, actionGroup);
328 if (logger.isDebugEnabled() == true) {
329 logger.debug("Generated a workflow permission: "
330 + result.getResourceName()
331 + ":" + transitionName
332 + ":" + "tenant id=" + result.getTenantId()
333 + ":" + actionGroup.name);
339 private static PermissionRole createPermissionRole(EntityManager em,
340 Permission permission,
342 boolean enforceTenancy) throws Exception
344 PermissionRole permRole = new PermissionRole();
345 // Check to see if the tenant ID of the permission and the tenant ID of the role match
346 boolean tenantIdsMatch = role.getTenantId().equalsIgnoreCase(permission.getTenantId());
347 if (tenantIdsMatch == false && enforceTenancy == false) {
348 tenantIdsMatch = true; // If we don't need to enforce tenancy then we'll just consider them matched.
351 if (tenantIdsMatch == true) {
352 permRole.setSubject(SubjectType.ROLE);
354 // Set of the permission value list of the permrole
356 List<PermissionValue> permValues = new ArrayList<PermissionValue>();
357 PermissionValue permValue = new PermissionValue();
358 permValue.setPermissionId(permission.getCsid());
359 permValue.setResourceName(permission.getResourceName().toLowerCase());
360 permValue.setActionGroup(permission.getActionGroup());
361 permValues.add(permValue);
362 permRole.setPermission(permValues);
364 // Set of the role value list of the permrole
366 List<RoleValue> roleValues = new ArrayList<RoleValue>();
367 RoleValue rv = new RoleValue();
368 // This needs to use the qualified name, not the display name
369 rv.setRoleName(role.getRoleName());
370 rv.setRoleId(role.getCsid());
372 permRole.setRole(roleValues);
374 String errMsg = "The tenant ID of the role: " + role.getTenantId()
375 + " did not match the tenant ID of the permission: " + permission.getTenantId();
376 throw new Exception(errMsg);
382 private static Hashtable<String, String> getTenantNamesFromConfig(TenantBindingConfigReaderImpl tenantBindingConfigReader) {
384 // Note that this only handles tenants not marked as "createDisabled"
385 Hashtable<String, TenantBindingType> tenantBindings =
386 tenantBindingConfigReader.getTenantBindings();
387 Hashtable<String, String> tenantInfo = new Hashtable<String, String>();
388 for (TenantBindingType tenantBinding : tenantBindings.values()) {
389 String tId = tenantBinding.getId();
390 String tName = tenantBinding.getName();
391 tenantInfo.put(tId, tName);
392 if (logger.isDebugEnabled()) {
393 logger.debug("getTenantNamesFromConfig found configured tenant id: "+tId+" name: "+tName);
399 private static ArrayList<String> compileExistingTenants(Connection conn, Hashtable<String, String> tenantInfo)
400 throws SQLException, Exception {
401 Statement stmt = null;
402 ArrayList<String> existingTenants = new ArrayList<String>();
403 // First find or create the tenants
404 final String queryTenantSQL = "SELECT id,name FROM tenants";
406 stmt = conn.createStatement();
407 ResultSet rs = stmt.executeQuery(queryTenantSQL);
409 String tId = rs.getString("id");
410 String tName = rs.getString("name");
411 if(tenantInfo.containsKey(tId)) {
412 existingTenants.add(tId);
413 if(!tenantInfo.get(tId).equalsIgnoreCase(tName)) {
414 logger.warn("Configured name for tenant: "
415 +tId+" in repository: "+tName
416 +" does not match config'd name: "+ tenantInfo.get(tId));
421 } catch(Exception e) {
428 return existingTenants;
431 private static void createMissingTenants(Connection conn, Hashtable<String, String> tenantInfo,
432 ArrayList<String> existingTenants) throws SQLException, Exception {
433 // Need to define and look for a createDisabled attribute in tenant config
434 final String insertTenantSQL =
435 "INSERT INTO tenants (id,name,disabled,created_at) VALUES (?,?,FALSE,now())";
436 PreparedStatement pstmt = null;
438 pstmt = conn.prepareStatement(insertTenantSQL); // create a statement
439 for(String tId : tenantInfo.keySet()) {
440 if(existingTenants.contains(tId)) {
441 if (logger.isDebugEnabled()) {
442 logger.debug("createMissingTenants: tenant exists (skipping): "
443 +tenantInfo.get(tId));
447 pstmt.setString(1, tId); // set id param
448 pstmt.setString(2, tenantInfo.get(tId)); // set name param
449 if (logger.isDebugEnabled()) {
450 logger.debug("createMissingTenants adding entry for tenant: "+tId);
452 pstmt.executeUpdate();
455 } catch(Exception e) {
463 private static ArrayList<String> findOrCreateDefaultUsers(Connection conn, Hashtable<String, String> tenantInfo)
464 throws SQLException, Exception {
465 // Second find or create the users
466 Statement stmt = null;
467 PreparedStatement pstmt = null;
468 ArrayList<String> usersInRepo = new ArrayList<String>();
470 stmt = conn.createStatement();
471 ResultSet rs = stmt.executeQuery(QUERY_USERS_SQL);
473 String uName = rs.getString("username");
474 usersInRepo.add(uName);
477 pstmt = conn.prepareStatement(INSERT_USER_SQL); // create a statement
478 for(String tName : tenantInfo.values()) {
479 String adminAcctName = getDefaultAdminUserID(tName);
480 if(!usersInRepo.contains(adminAcctName)) {
481 String secEncPasswd = SecurityUtils.createPasswordHash(
482 adminAcctName, DEFAULT_ADMIN_PASSWORD);
483 pstmt.setString(1, adminAcctName); // set username param
484 pstmt.setString(2, secEncPasswd); // set passwd param
485 if (logger.isDebugEnabled()) {
486 logger.debug("createDefaultUsersAndAccounts adding user: "
487 +adminAcctName+" for tenant: "+tName);
489 pstmt.executeUpdate();
490 } else if (logger.isDebugEnabled()) {
491 logger.debug("createDefaultUsersAndAccounts: user: "+adminAcctName
492 +" already exists - skipping.");
496 String readerAcctName = getDefaultReaderUserID(tName);
497 if(!usersInRepo.contains(readerAcctName)) {
498 String secEncPasswd = SecurityUtils.createPasswordHash(
499 readerAcctName, DEFAULT_READER_PASSWORD);
500 pstmt.setString(1, readerAcctName); // set username param
501 pstmt.setString(2, secEncPasswd); // set passwd param
502 if (logger.isDebugEnabled()) {
503 logger.debug("createDefaultUsersAndAccounts adding user: "
504 +readerAcctName+" for tenant: "+tName);
506 pstmt.executeUpdate();
507 } else if (logger.isDebugEnabled()) {
508 logger.debug("createDefaultUsersAndAccounts: user: "+readerAcctName
509 +" already exists - skipping.");
513 } catch(Exception e) {
524 private static void findOrCreateDefaultAccounts(Connection conn, Hashtable<String, String> tenantInfo,
525 ArrayList<String> usersInRepo,
526 Hashtable<String, String> tenantAdminAcctCSIDs, Hashtable<String, String> tenantReaderAcctCSIDs)
527 throws SQLException, Exception {
528 // Third, create the accounts. Assume that if the users were already there,
529 // then the accounts were as well
530 PreparedStatement pstmt = null;
532 pstmt = conn.prepareStatement(INSERT_ACCOUNT_SQL); // create a statement
533 for(String tId : tenantInfo.keySet()) {
534 String tName = tenantInfo.get(tId);
535 String adminCSID = UUID.randomUUID().toString();
536 tenantAdminAcctCSIDs.put(tId, adminCSID);
537 String adminAcctName = getDefaultAdminUserID(tName);
538 if(!usersInRepo.contains(adminAcctName)) {
539 pstmt.setString(1, adminCSID); // set csid param
540 pstmt.setString(2, adminAcctName); // set email param (bogus)
541 pstmt.setString(3, adminAcctName); // set userid param
542 pstmt.setString(4, "Administrator");// set screen name param
543 if (logger.isDebugEnabled()) {
544 logger.debug("createDefaultAccounts adding account: "
545 +adminAcctName+" for tenant: "+tName);
547 pstmt.executeUpdate();
548 } else if (logger.isDebugEnabled()) {
549 logger.debug("createDefaultAccounts: user: "+adminAcctName
550 +" already exists - skipping account generation.");
553 String readerCSID = UUID.randomUUID().toString();
554 tenantReaderAcctCSIDs.put(tId, readerCSID);
555 String readerAcctName = getDefaultReaderUserID(tName);
556 if(!usersInRepo.contains(readerAcctName)) {
557 pstmt.setString(1, readerCSID); // set csid param
558 pstmt.setString(2, readerAcctName); // set email param (bogus)
559 pstmt.setString(3, readerAcctName); // set userid param
560 pstmt.setString(4, "Reader"); // set screen name param
561 if (logger.isDebugEnabled()) {
562 logger.debug("createDefaultAccounts adding account: "
563 +readerAcctName+" for tenant: "+tName);
565 pstmt.executeUpdate();
566 } else if (logger.isDebugEnabled()) {
567 logger.debug("createDefaultAccounts: user: "+readerAcctName
568 +" already exists - skipping account creation.");
572 } catch(Exception e) {
580 private static boolean findOrCreateTenantManagerUserAndAccount(Connection conn)
581 throws SQLException, Exception {
582 // Find or create the special tenant manager account.
583 // Later can make the user name for tenant manager be configurable, settable.
584 Statement stmt = null;
585 PreparedStatement pstmt = null;
586 boolean created = false;
588 boolean foundTMgrUser = false;
589 stmt = conn.createStatement();
590 ResultSet rs = stmt.executeQuery(QUERY_TENANT_MGR_USER_SQL);
591 // Should only find one - only consider it
593 String uName = rs.getString("username");
594 foundTMgrUser = uName.equals(TENANT_MANAGER_USER);
598 pstmt = conn.prepareStatement(INSERT_USER_SQL); // create a statement
599 String secEncPasswd = SecurityUtils.createPasswordHash(
600 TENANT_MANAGER_USER, DEFAULT_TENANT_MANAGER_PASSWORD);
601 pstmt.setString(1, TENANT_MANAGER_USER); // set username param
602 pstmt.setString(2, secEncPasswd); // set passwd param
603 if (logger.isDebugEnabled()) {
604 logger.debug("findOrCreateTenantManagerUserAndAccount adding tenant manager user: "
605 +TENANT_MANAGER_USER);
607 pstmt.executeUpdate();
609 // Now create the account to match
610 pstmt = conn.prepareStatement(INSERT_ACCOUNT_SQL); // create a statement
611 pstmt.setString(1, AuthN.TENANT_MANAGER_ACCT_ID); // set csid param
612 pstmt.setString(2, DEFAULT_TENANT_MANAGER_EMAIL); // set email param (bogus)
613 pstmt.setString(3, TENANT_MANAGER_USER); // set userid param
614 pstmt.setString(4, TENANT_MANAGER_SCREEN_NAME);// set screen name param
615 if (logger.isDebugEnabled()) {
616 logger.debug("findOrCreateTenantManagerUserAndAccount adding tenant manager account: "
617 +TENANT_MANAGER_USER);
619 pstmt.executeUpdate();
622 } else if (logger.isDebugEnabled()) {
623 logger.debug("findOrCreateTenantManagerUserAndAccount: tenant manager: "+TENANT_MANAGER_USER
624 +" already exists.");
626 } catch(Exception e) {
637 private static void bindDefaultAccountsToTenants(Connection conn, DatabaseProductType databaseProductType,
638 Hashtable<String, String> tenantInfo, ArrayList<String> usersInRepo,
639 Hashtable<String, String> tenantAdminAcctCSIDs, Hashtable<String, String> tenantReaderAcctCSIDs)
640 throws SQLException, Exception {
641 // Fourth, bind accounts to tenants. Assume that if the users were already there,
642 // then the accounts were bound to tenants correctly
643 PreparedStatement pstmt = null;
645 String insertAccountTenantSQL;
646 if (databaseProductType == DatabaseProductType.MYSQL) {
647 insertAccountTenantSQL =
648 "INSERT INTO accounts_tenants (TENANTS_ACCOUNTS_COMMON_CSID,tenant_id) "
650 } else if (databaseProductType == DatabaseProductType.POSTGRESQL) {
651 insertAccountTenantSQL =
652 "INSERT INTO accounts_tenants (HJID, TENANTS_ACCOUNTS_COMMON_CSID,tenant_id) "
653 + " VALUES(nextval('hibernate_sequence'), ?, ?)";
655 throw new Exception("Unrecognized database system.");
657 pstmt = conn.prepareStatement(insertAccountTenantSQL); // create a statement
658 for(String tId : tenantInfo.keySet()) {
659 String tName = tenantInfo.get(tId);
660 if(!usersInRepo.contains(getDefaultAdminUserID(tName))) {
661 String adminAcct = tenantAdminAcctCSIDs.get(tId);
662 pstmt.setString(1, adminAcct); // set acct CSID param
663 pstmt.setString(2, tId); // set tenant_id param
664 if (logger.isDebugEnabled()) {
665 logger.debug("createDefaultAccounts binding account id: "
666 +adminAcct+" to tenant id: "+tId);
668 pstmt.executeUpdate();
670 if(!usersInRepo.contains(getDefaultReaderUserID(tName))) {
671 String readerAcct = tenantReaderAcctCSIDs.get(tId);
672 pstmt.setString(1, readerAcct); // set acct CSID param
673 pstmt.setString(2, tId); // set tenant_id param
674 if (logger.isDebugEnabled()) {
675 logger.debug("createDefaultAccounts binding account id: "
676 +readerAcct+" to tenant id: "+tId);
678 pstmt.executeUpdate();
682 } catch(Exception e) {
691 private static String findOrCreateDefaultRoles(Connection conn, Hashtable<String, String> tenantInfo,
692 Hashtable<String, String> tenantAdminRoleCSIDs, Hashtable<String, String> tenantReaderRoleCSIDs)
693 throws SQLException, Exception {
694 // Fifth, fetch and save the default roles
695 String springAdminRoleCSID = null;
696 Statement stmt = null;
697 PreparedStatement pstmt = null;
699 final String querySpringRole =
700 "SELECT csid from roles WHERE rolename='"+SPRING_ADMIN_ROLE+"'";
701 stmt = conn.createStatement();
702 ResultSet rs = stmt.executeQuery(querySpringRole);
704 springAdminRoleCSID = rs.getString(1);
705 if (logger.isDebugEnabled()) {
706 logger.debug("createDefaultAccounts found Spring Admin role: "
707 +springAdminRoleCSID);
710 final String insertSpringAdminRoleSQL =
711 "INSERT INTO roles (csid, rolename, displayName, rolegroup, created_at, tenant_id) "
712 + "VALUES ('-1', 'ROLE_SPRING_ADMIN', 'SPRING_ADMIN', 'Spring Security Administrator', now(), '0')";
713 stmt.executeUpdate(insertSpringAdminRoleSQL);
714 springAdminRoleCSID = "-1";
715 if (logger.isDebugEnabled()) {
716 logger.debug("createDefaultAccounts CREATED Spring Admin role: "
717 +springAdminRoleCSID);
721 final String getRoleCSIDSql =
722 "SELECT csid from roles WHERE tenant_id=? and rolename=?";
723 pstmt = conn.prepareStatement(getRoleCSIDSql); // create a statement
725 for(String tId : tenantInfo.keySet()) {
726 pstmt.setString(1, tId); // set tenant_id param
727 pstmt.setString(2, getDefaultAdminRole(tId)); // set rolename param
728 rs = pstmt.executeQuery();
729 // extract data from the ResultSet
731 throw new RuntimeException("Cannot find role: "+getDefaultAdminRole(tId)
732 +" for tenant id: "+tId+" in roles!");
734 String tenantAdminRoleCSID = rs.getString(1);
735 if (logger.isDebugEnabled()) {
736 logger.debug("createDefaultAccounts found role: "
737 +getDefaultAdminRole(tId)+"("+tenantAdminRoleCSID
738 +") for tenant id: "+tId);
740 tenantAdminRoleCSIDs.put(tId, tenantAdminRoleCSID);
741 pstmt.setString(1, tId); // set tenant_id param
742 pstmt.setString(2, getDefaultReaderRole(tId)); // set rolename param
744 rs = pstmt.executeQuery();
745 // extract data from the ResultSet
747 throw new RuntimeException("Cannot find role: "+getDefaultReaderRole(tId)
748 +" for tenant id: "+tId+" in roles!");
750 String tenantReaderRoleCSID = rs.getString(1);
751 if (logger.isDebugEnabled()) {
752 logger.debug("createDefaultAccounts found role: "
753 +getDefaultReaderRole(tId)+"("+tenantReaderRoleCSID
754 +") for tenant id: "+tId);
756 tenantReaderRoleCSIDs.put(tId, tenantReaderRoleCSID);
760 } catch(Exception e) {
768 return springAdminRoleCSID;
771 private static String findTenantManagerRole(Connection conn )
772 throws SQLException, RuntimeException, Exception {
773 String tenantMgrRoleCSID = null;
774 PreparedStatement pstmt = null;
776 String rolename = getQualifiedRoleName(ALL_TENANTS_MANAGER_TENANT_ID,
777 ROLE_ALL_TENANTS_MANAGER);
778 pstmt = conn.prepareStatement(GET_TENANT_MGR_ROLE_SQL); // create a statement
780 pstmt.setString(1, rolename); // set rolename param
781 rs = pstmt.executeQuery();
783 tenantMgrRoleCSID = rs.getString(1);
784 if (logger.isDebugEnabled()) {
785 logger.debug("findTenantManagerRole found Tenant Mgr role: "
790 } catch(Exception e) {
796 if(tenantMgrRoleCSID==null)
797 throw new RuntimeException("findTenantManagerRole: Cound not find tenant Manager Role!");
798 return tenantMgrRoleCSID;
801 private static void bindAccountsToRoles(Connection conn, DatabaseProductType databaseProductType,
802 Hashtable<String, String> tenantInfo, ArrayList<String> usersInRepo,
803 String springAdminRoleCSID,
804 Hashtable<String, String> tenantAdminRoleCSIDs, Hashtable<String, String> tenantReaderRoleCSIDs,
805 Hashtable<String, String> tenantAdminAcctCSIDs, Hashtable<String, String> tenantReaderAcctCSIDs)
806 throws SQLException, Exception {
807 // Sixth, bind the accounts to roles. If the users already existed,
808 // we'll assume they were set up correctly.
809 PreparedStatement pstmt = null;
811 String insertAccountRoleSQL;
812 if (databaseProductType == DatabaseProductType.MYSQL) {
813 insertAccountRoleSQL = INSERT_ACCOUNT_ROLE_SQL_MYSQL;
814 } else if (databaseProductType == DatabaseProductType.POSTGRESQL) {
815 insertAccountRoleSQL = INSERT_ACCOUNT_ROLE_SQL_POSTGRES;
817 throw new Exception("Unrecognized database system.");
819 if (logger.isDebugEnabled()) {
820 logger.debug("createDefaultAccounts binding accounts to roles with SQL:\n"
821 +insertAccountRoleSQL);
823 pstmt = conn.prepareStatement(insertAccountRoleSQL); // create a statement
824 for(String tId : tenantInfo.keySet()) {
825 String adminUserId = getDefaultAdminUserID(tenantInfo.get(tId));
826 if(!usersInRepo.contains(adminUserId)) {
827 String adminAcct = tenantAdminAcctCSIDs.get(tId);
828 String adminRoleId = tenantAdminRoleCSIDs.get(tId);
829 pstmt.setString(1, adminAcct); // set acct CSID param
830 pstmt.setString(2, adminUserId); // set user_id param
831 pstmt.setString(3, adminRoleId); // set role_id param
832 pstmt.setString(4, getDefaultAdminRole(tId)); // set rolename param
833 if (logger.isDebugEnabled()) {
834 logger.debug("createDefaultAccounts binding account: "
835 +adminUserId+" to Admin role("+adminRoleId
836 +") for tenant id: "+tId);
838 pstmt.executeUpdate();
839 // Now add the Spring Admin Role to the admin accounts
840 pstmt.setString(3, springAdminRoleCSID); // set role_id param
841 pstmt.setString(4, SPRING_ADMIN_ROLE); // set rolename param
842 if (logger.isDebugEnabled()) {
843 logger.debug("createDefaultAccounts binding account: "
844 +adminUserId+" to Spring Admin role: "+springAdminRoleCSID);
846 pstmt.executeUpdate();
848 String readerUserId = getDefaultReaderUserID(tenantInfo.get(tId));
849 if(!usersInRepo.contains(readerUserId)) {
850 String readerAcct = tenantReaderAcctCSIDs.get(tId);
851 String readerRoleId = tenantReaderRoleCSIDs.get(tId);
852 pstmt.setString(1, readerAcct); // set acct CSID param
853 pstmt.setString(2, readerUserId); // set user_id param
854 pstmt.setString(3, readerRoleId); // set role_id param
855 pstmt.setString(4, getDefaultReaderRole(tId)); // set rolename param
856 if (logger.isDebugEnabled()) {
857 logger.debug("createDefaultAccounts binding account: "
858 +readerUserId+" to Reader role("+readerRoleId
859 +") for tenant id: "+tId);
861 pstmt.executeUpdate();
865 } catch(Exception e) {
873 private static void bindTenantManagerAccountRole(Connection conn, DatabaseProductType databaseProductType,
874 String tenantManagerUserID, String tenantManagerAccountID, String tenantManagerRoleID, String tenantManagerRoleName )
875 throws SQLException, Exception {
876 PreparedStatement pstmt = null;
878 String insertAccountRoleSQL;
879 if (databaseProductType == DatabaseProductType.MYSQL) {
880 insertAccountRoleSQL = INSERT_ACCOUNT_ROLE_SQL_MYSQL;
881 } else if (databaseProductType == DatabaseProductType.POSTGRESQL) {
882 insertAccountRoleSQL = INSERT_ACCOUNT_ROLE_SQL_POSTGRES;
884 throw new Exception("Unrecognized database system.");
886 if (logger.isDebugEnabled()) {
887 logger.debug("bindTenantManagerAccountRole binding account to role with SQL:\n"
888 +insertAccountRoleSQL);
890 pstmt = conn.prepareStatement(insertAccountRoleSQL); // create a statement
891 pstmt.setString(1, tenantManagerAccountID); // set acct CSID param
892 pstmt.setString(2, tenantManagerUserID); // set user_id param
893 pstmt.setString(3, tenantManagerRoleID); // set role_id param
894 pstmt.setString(4, tenantManagerRoleName); // set rolename param
895 if (logger.isDebugEnabled()) {
896 logger.debug("bindTenantManagerAccountRole binding user: "
897 +tenantManagerUserID+" to Admin role("+tenantManagerRoleName+")");
899 pstmt.executeUpdate();
900 /* At this point, tenant manager should not need the Spring Admin Role
901 pstmt.setString(3, springAdminRoleCSID); // set role_id param
902 pstmt.setString(4, SPRING_ADMIN_ROLE); // set rolename param
903 if (logger.isDebugEnabled()) {
904 logger.debug("createDefaultAccounts binding account: "
905 +adminUserId+" to Spring Admin role: "+springAdminRoleCSID);
907 pstmt.executeUpdate();
910 } catch(Exception e) {
918 public static void createDefaultAccounts(
919 TenantBindingConfigReaderImpl tenantBindingConfigReader,
920 DatabaseProductType databaseProductType,
921 String cspaceDatabaseName) throws Exception {
923 logger.debug("ServiceMain.createDefaultAccounts starting...");
925 Hashtable<String, String> tenantInfo = getTenantNamesFromConfig(tenantBindingConfigReader);
926 Connection conn = null;
927 // TODO - need to put in tests for existence first.
928 // We could just look for the accounts per tenant up front, and assume that
929 // the rest is there if the accounts are.
930 // Could add a sql script to remove these if need be - Spring only does roles,
931 // and we're not touching that, so we could safely toss the
932 // accounts, users, account-tenants, account-roles, and start over.
934 conn = getConnection(cspaceDatabaseName);
935 ArrayList<String> existingTenants = compileExistingTenants(conn, tenantInfo);
937 // Note that this only creates tenants not marked as "createDisabled"
938 createMissingTenants(conn, tenantInfo, existingTenants);
940 ArrayList<String> usersInRepo = findOrCreateDefaultUsers(conn, tenantInfo);
942 Hashtable<String, String> tenantAdminAcctCSIDs = new Hashtable<String, String>();
943 Hashtable<String, String> tenantReaderAcctCSIDs = new Hashtable<String, String>();
944 findOrCreateDefaultAccounts(conn, tenantInfo, usersInRepo,
945 tenantAdminAcctCSIDs, tenantReaderAcctCSIDs);
947 bindDefaultAccountsToTenants(conn, databaseProductType, tenantInfo, usersInRepo,
948 tenantAdminAcctCSIDs, tenantReaderAcctCSIDs);
950 Hashtable<String, String> tenantAdminRoleCSIDs = new Hashtable<String, String>();
951 Hashtable<String, String> tenantReaderRoleCSIDs = new Hashtable<String, String>();
952 String springAdminRoleCSID = findOrCreateDefaultRoles(conn, tenantInfo,
953 tenantAdminRoleCSIDs, tenantReaderRoleCSIDs);
955 bindAccountsToRoles(conn, databaseProductType,
956 tenantInfo, usersInRepo, springAdminRoleCSID,
957 tenantAdminRoleCSIDs, tenantReaderRoleCSIDs,
958 tenantAdminAcctCSIDs, tenantReaderAcctCSIDs);
960 boolean createdTenantMgrAccount = findOrCreateTenantManagerUserAndAccount(conn);
961 if(createdTenantMgrAccount) {
962 // If we created the account, we need to create the bindings. Otherwise, assume they
963 // are all set (from previous initialization).
964 String tenantManagerRoleCSID = findTenantManagerRole(conn);
965 bindTenantManagerAccountRole(conn, databaseProductType,
966 TENANT_MANAGER_USER, AuthN.TENANT_MANAGER_ACCT_ID,
967 tenantManagerRoleCSID, ROLE_ALL_TENANTS_MANAGER);
969 } catch (Exception e) {
970 logger.debug("Exception in createDefaultAccounts: " + e.getLocalizedMessage());
976 } catch (SQLException sqle) {
977 if (logger.isDebugEnabled()) {
978 logger.debug("SQL Exception closing statement/connection: " + sqle.getLocalizedMessage());
984 private static String getDefaultAdminRole(String tenantId) {
985 return ROLE_PREFIX+tenantId+TENANT_ADMIN_ROLE_SUFFIX;
988 private static String getDefaultReaderRole(String tenantId) {
989 return ROLE_PREFIX+tenantId+TENANT_READER_ROLE_SUFFIX;
992 private static String getDefaultAdminUserID(String tenantName) {
993 return TENANT_ADMIN_ACCT_PREFIX+tenantName;
996 private static String getDefaultReaderUserID(String tenantName) {
997 return TENANT_READER_ACCT_PREFIX+tenantName;
1000 static public PermissionAction createPermissionAction(Permission perm,
1001 ActionType actionType) {
1002 PermissionAction pa = new PermissionAction();
1004 CSpaceAction action = URIResourceImpl.getAction(actionType);
1005 URIResourceImpl uriRes = new URIResourceImpl(perm.getTenantId(),
1006 perm.getResourceName(), action);
1007 pa.setName(actionType);
1008 pa.setObjectIdentity(uriRes.getHashedId().toString());
1009 pa.setObjectIdentityResource(uriRes.getId());
1014 static public PermissionAction update(Permission perm, PermissionAction permAction) {
1015 PermissionAction pa = new PermissionAction();
1017 CSpaceAction action = URIResourceImpl.getAction(permAction.getName());
1018 URIResourceImpl uriRes = new URIResourceImpl(perm.getTenantId(),
1019 perm.getResourceName(), action);
1020 pa.setObjectIdentity(uriRes.getHashedId().toString());
1021 pa.setObjectIdentityResource(uriRes.getId());
1026 private static TransitionDefList getTransitionDefList(TenantBindingType tenantBinding, ServiceBindingType serviceBinding) {
1027 TransitionDefList result = null;
1029 String serviceObjectName = serviceBinding.getObject().getName();
1030 DocumentHandler docHandler = ServiceConfigUtils.createDocumentHandlerInstance(
1031 tenantBinding, serviceBinding);
1032 Lifecycle lifecycle = docHandler.getLifecycle(serviceObjectName);
1033 if (lifecycle != null) {
1034 result = lifecycle.getTransitionDefList();
1036 } catch (Exception e) {
1037 // Ignore this exception and return an empty non-null TransitionDefList
1040 if (result == null) {
1041 if (serviceBinding.getType().equalsIgnoreCase(ServiceBindingUtils.SERVICE_TYPE_SECURITY) == false) {
1042 logger.warn("Could not retrieve a lifecycle transition definition list from: "
1043 + serviceBinding.getName()
1044 + " with tenant ID = "
1045 + tenantBinding.getId());
1047 // return an empty list
1048 result = new TransitionDefList();
1050 logger.debug("Successfully retrieved a lifecycle transition definition list from: "
1051 + serviceBinding.getName()
1052 + " with tenant ID = "
1053 + tenantBinding.getId());
1059 public static void createDefaultWorkflowPermissions(TenantBindingConfigReaderImpl tenantBindingConfigReader) throws Exception //FIXME: REM - 4/11/2012 - Rename to createWorkflowPermissions
1061 AuthZ.get().login(); //login to Spring Security manager
1063 EntityManagerFactory emf = JpaStorageUtils.getEntityManagerFactory(JpaStorageUtils.CS_PERSISTENCE_UNIT);
1064 EntityManager em = null;
1067 em = emf.createEntityManager();
1069 Hashtable<String, TenantBindingType> tenantBindings =
1070 tenantBindingConfigReader.getTenantBindings();
1071 for (String tenantId : tenantBindings.keySet()) {
1072 TenantBindingType tenantBinding = tenantBindings.get(tenantId);
1073 Role adminRole = AuthorizationCommon.getRole(em, tenantBinding.getId(), ROLE_TENANT_ADMINISTRATOR);
1074 Role readonlyRole = AuthorizationCommon.getRole(em, tenantBinding.getId(), ROLE_TENANT_READER);
1075 for (ServiceBindingType serviceBinding : tenantBinding.getServiceBindings()) {
1076 String prop = ServiceBindingUtils.getPropertyValue(serviceBinding, REFRESH_AUTZ_PROP);
1077 if (prop == null ? true : Boolean.parseBoolean(prop)) {
1079 em.getTransaction().begin();
1080 TransitionDefList transitionDefList = getTransitionDefList(tenantBinding, serviceBinding);
1081 for (TransitionDef transitionDef : transitionDefList.getTransitionDef()) {
1083 // Create the permission for the admin role
1084 Permission adminPerm = createWorkflowPermission(tenantBinding, serviceBinding, transitionDef, ACTIONGROUP_CRUDL);
1085 persist(em, adminPerm, adminRole, true);
1087 // Create the permission for the read-only role
1088 Permission readonlyPerm = createWorkflowPermission(tenantBinding, serviceBinding, transitionDef, ACTIONGROUP_RL);
1090 Profiler profiler = new Profiler(AuthorizationCommon.class, 1);
1091 profiler.start("createDefaultPermissions started:" + readonlyPerm.getCsid());
1092 persist(em, readonlyPerm, readonlyRole, true); // Persist/store the permission and permrole records and related Spring Security info
1094 logger.debug("Finished full perm generation for "
1095 + ":" + tenantBinding.getId()
1096 + ":" + serviceBinding.getName()
1097 + ":" + transitionDef.getName()
1098 + ":" + ACTIONGROUP_RL
1099 + ":" + profiler.getCumulativeTime());
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) 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_RL
1172 + ":" + profiler.getCumulativeTime());