]> git.aero2k.de Git - tmp/jakarta-migration.git/blob
6e686f06416791634c2810ea71decf0da2f5fbed
[tmp/jakarta-migration.git] /
1 package org.collectionspace.services.common.authorization_mgt;
2
3 import java.security.MessageDigest;
4 import java.security.NoSuchAlgorithmException;
5 import java.sql.Connection;
6 import java.sql.PreparedStatement;
7 import java.sql.ResultSet;
8 import java.sql.SQLException;
9 import java.sql.Statement;
10 import java.util.ArrayList;
11 import java.util.Date;
12 import java.util.HashMap;
13 import java.util.HashSet;
14 import java.util.Hashtable;
15 import java.util.List;
16 import java.util.UUID;
17
18 import javax.naming.NamingException;
19 import javax.persistence.EntityManager;
20 import javax.persistence.EntityManagerFactory;
21
22 import org.collectionspace.authentication.AuthN;
23 import org.collectionspace.services.account.AccountListItem;
24
25 import org.collectionspace.services.authentication.Token;
26 import org.collectionspace.services.authorization.AuthZ;
27 import org.collectionspace.services.authorization.CSpaceAction;
28 import org.collectionspace.services.authorization.PermissionException;
29 import org.collectionspace.services.authorization.PermissionRole;
30 import org.collectionspace.services.authorization.PermissionRoleRel;
31 import org.collectionspace.services.authorization.PermissionValue;
32 import org.collectionspace.services.authorization.Role;
33 import org.collectionspace.services.authorization.RoleValue;
34 import org.collectionspace.services.authorization.SubjectType;
35 import org.collectionspace.services.authorization.URIResourceImpl;
36 import org.collectionspace.services.authorization.perms.ActionType;
37 import org.collectionspace.services.authorization.perms.EffectType;
38 import org.collectionspace.services.authorization.perms.Permission;
39 import org.collectionspace.services.authorization.perms.PermissionAction;
40
41 import org.collectionspace.services.client.Profiler;
42 import org.collectionspace.services.client.RoleClient;
43 import org.collectionspace.services.client.workflow.WorkflowClient;
44
45 import org.collectionspace.services.common.config.ServiceConfigUtils;
46 import org.collectionspace.services.common.config.TenantBindingConfigReaderImpl;
47 import org.collectionspace.services.common.context.ServiceBindingUtils;
48 import org.collectionspace.services.common.document.DocumentHandler;
49 import org.collectionspace.services.common.security.SecurityUtils;
50 import org.collectionspace.services.common.storage.DatabaseProductType;
51 import org.collectionspace.services.common.storage.JDBCTools;
52 import org.collectionspace.services.common.storage.jpa.JpaStorageUtils;
53
54 import org.collectionspace.services.config.service.ServiceBindingType;
55 import org.collectionspace.services.config.tenant.EmailConfig;
56 import org.collectionspace.services.config.tenant.PasswordResetConfig;
57 import org.collectionspace.services.config.tenant.TenantBindingType;
58
59 import org.collectionspace.services.lifecycle.Lifecycle;
60 import org.collectionspace.services.lifecycle.TransitionDef;
61 import org.collectionspace.services.lifecycle.TransitionDefList;
62
63 //import org.mortbay.log.Log;
64 import org.slf4j.Logger;
65 import org.slf4j.LoggerFactory;
66 import org.springframework.security.acls.model.AlreadyExistsException;
67
68
69 public class AuthorizationCommon {
70         
71         final public static String REFRESH_AUTZ_PROP = "refreshAuthZOnStartup";
72         
73         //
74         // For token generation and password reset
75         //
76         final private static String DEFAULT_PASSWORD_RESET_EMAIL_MESSAGE = "Hello {{greeting}},\n\r\n\rYou've started the process to reset your CollectionSpace account password. To finish resetting your password, go to the Reset Password page {{link}} on CollectionSpace.\n\r\n\rIf clicking the link doesn't work, copy and paste the following link into your browser address bar and click Go.\n\r\n\r{{link}}\n\r Thanks,\n\r\n\r CollectionSpace Administrator\n\r\n\rPlease do not reply to this email. This mailbox is not monitored and you will not receive a response. For assistance, contact your CollectionSpace Administrator directly.";
77         final private static String tokensalt = "74102328UserDetailsReset";
78         final private static int TIME_SCALAR = 100000;
79         private static final String DEFAULT_PASSWORD_RESET_EMAIL_SUBJECT = "Password reset for CollectionSpace account";
80
81     //
82     // ActionGroup labels/constants
83     //
84         
85         // for READ-WRITE
86     final public static String ACTIONGROUP_CRUDL_NAME = "CRUDL";
87     final public static ActionType[] ACTIONSET_CRUDL = {ActionType.CREATE, ActionType.READ, ActionType.UPDATE, ActionType.DELETE, ActionType.SEARCH};
88     // for READ-ONLY
89     final public static String ACTIONGROUP_RL_NAME = "RL";
90     final public static ActionType[] ACTIONSET_RL = {ActionType.READ, ActionType.SEARCH};
91         
92         static ActionGroup ACTIONGROUP_CRUDL;
93         static ActionGroup ACTIONGROUP_RL;
94         
95         // A static block to initialize the predefined action groups
96         static {
97                 // For admin
98                 ACTIONGROUP_CRUDL = new ActionGroup();
99                 ACTIONGROUP_CRUDL.name = ACTIONGROUP_CRUDL_NAME;
100                 ACTIONGROUP_CRUDL.actions = ACTIONSET_CRUDL;
101                 // For reader
102                 ACTIONGROUP_RL = new ActionGroup();
103                 ACTIONGROUP_RL.name = ACTIONGROUP_RL_NAME;
104                 ACTIONGROUP_RL.actions = ACTIONSET_RL;
105
106         }
107         
108     final static Logger logger = LoggerFactory.getLogger(AuthorizationCommon.class);
109
110     final public static String ROLE_TENANT_ADMINISTRATOR = "TENANT_ADMINISTRATOR";
111     final public static String ROLE_TENANT_READER = "TENANT_READER";
112         
113     public static final String TENANT_MANAGER_USER = "tenantManager"; 
114     public static final String TENANT_MANAGER_SCREEN_NAME = TENANT_MANAGER_USER; 
115     public static final String DEFAULT_TENANT_MANAGER_PASSWORD = "manage"; 
116     public static final String DEFAULT_TENANT_MANAGER_EMAIL = "tenantManager@collectionspace.org"; 
117     
118     public static final String TENANT_ADMIN_ACCT_PREFIX = "admin@"; 
119     public static final String TENANT_READER_ACCT_PREFIX = "reader@"; 
120     public static final String ROLE_PREFIX = "ROLE_"; 
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";
125     
126     // SQL for init tasks
127         final private static String INSERT_ACCOUNT_ROLE_SQL_MYSQL = 
128                         "INSERT INTO accounts_roles(account_id, user_id, role_id, role_name, created_at)"
129                                         +" VALUES(?, ?, ?, ?, now())";
130         final private static String INSERT_ACCOUNT_ROLE_SQL_POSTGRES =
131                         "INSERT INTO accounts_roles(HJID, account_id, user_id, role_id, role_name, created_at)"
132                                         +" VALUES(nextval('hibernate_sequence'), ?, ?, ?, ?, now())";
133         final private static String QUERY_USERS_SQL = 
134                 "SELECT username FROM users WHERE username LIKE '"
135                         +TENANT_ADMIN_ACCT_PREFIX+"%' OR username LIKE '"+TENANT_READER_ACCT_PREFIX+"%'";
136         final private static String INSERT_USER_SQL = 
137                         "INSERT INTO users (username,passwd, created_at) VALUES (?,?, now())";
138         final private static String INSERT_ACCOUNT_SQL = 
139                         "INSERT INTO accounts_common "
140                                         + "(csid, email, userid, status, screen_name, metadata_protection, roles_protection, created_at) "
141                                         + "VALUES (?,?,?,'ACTIVE',?, 'immutable', 'immutable', now())";
142         
143         // TENANT MANAGER specific SQL
144         final private static String QUERY_TENANT_MGR_USER_SQL = 
145                 "SELECT username FROM users WHERE username = '"+TENANT_MANAGER_USER+"'";
146         final private static String GET_TENANT_MGR_ROLE_SQL =
147                         "SELECT csid from roles WHERE tenant_id='" + AuthN.ALL_TENANTS_MANAGER_TENANT_ID + "' and rolename=?";
148
149         public static final String IGNORE_TENANT_ID = null; // A null constant to indicate an empty/unused value for the tenant ID
150
151
152     public static Role getRole(String tenantId, String displayName) {
153         Role role = null;
154         
155         String roleName = AuthorizationCommon.getQualifiedRoleName(tenantId, displayName);
156         role = AuthorizationStore.getRoleByName(roleName, tenantId);
157         
158         return role;
159     }
160     
161     public static Role getRole(EntityManager em, String tenantId, String displayName) {
162         Role role = null;
163         
164         String roleName = AuthorizationCommon.getQualifiedRoleName(tenantId, displayName);
165         role = AuthorizationStore.getRoleByName(em, roleName, tenantId);
166         
167         return role;
168     }
169     
170     
171     public static Role createRole(String tenantId, String name, String description) {
172         return createRole(tenantId, name, description, false /* mutable by default */);
173     }
174     
175     public static Role createRole(String tenantId, String name, String description, boolean immutable) {
176         Role role = new Role();
177         
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
183         role.setCsid(id);
184                 role.setDescription(description);
185         role.setTenantId(tenantId);
186         if (immutable == true) {
187                 role.setMetadataProtection(RoleClient.IMMUTABLE);
188                 role.setPermsProtection(RoleClient.IMMUTABLE);
189         }
190         
191         return role;
192     }
193     
194     /**
195      * Add permission to the Spring Security tables
196      * with assumption that resource is of type URI
197      * @param permission configuration
198      */
199     public static void addPermissionsForUri(Permission perm,
200             PermissionRole permRole) throws PermissionException {
201         //
202         // First check the integrity of the incoming arguments.
203         //
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());
209         }
210         
211         List<String> principals = new ArrayList<String>();        
212         for (RoleValue roleValue : permRole.getRole()) {
213             principals.add(roleValue.getRoleName());
214         }
215         List<PermissionAction> permActions = perm.getAction();
216         for (PermissionAction permAction : permActions) {
217                 try {
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) {
224                         //
225                         // Only throw the exception if it is *not* an already-exists exception
226                         //
227                         if (e.getCause() instanceof AlreadyExistsException == false) {
228                                 throw e;
229                         }
230                 }
231         }
232     }
233     
234     private static Connection getConnection(String databaseName) throws NamingException, SQLException {
235         return JDBCTools.getConnection(JDBCTools.CSPACE_DATASOURCE_NAME,
236                         databaseName);
237     }
238     
239     /*
240      * Spring security seems to require that all of our role names start
241      * with the ROLE_PREFIX string.
242      */
243     public static String getQualifiedRoleName(String tenantId, String name) {
244         String result = name;
245         
246         String qualifiedName = ROLE_PREFIX + tenantId.toUpperCase() + "_" + name.toUpperCase();         
247         if (name.equals(qualifiedName) == false) {
248                 result = qualifiedName;
249         }
250         
251         return result;
252     }
253         
254     private static ActionGroup getActionGroup(String actionGroupStr) {
255         ActionGroup result = null;
256         
257         if (actionGroupStr.equalsIgnoreCase(ACTIONGROUP_CRUDL_NAME)) {
258                 result = ACTIONGROUP_CRUDL;
259         } else if (actionGroupStr.equalsIgnoreCase(ACTIONGROUP_RL_NAME)) {
260                 result = ACTIONGROUP_RL;
261         }
262         
263         return result;
264     }
265     
266     public static Permission createPermission(String tenantId,
267                 String resourceName,
268                 String description,
269                 String actionGroupStr) {
270         Permission result = null;
271         
272         ActionGroup actionGroup = getActionGroup(actionGroupStr);
273         result = createPermission(tenantId, resourceName, description, actionGroup);
274         
275         return result;
276     }
277     
278     private static Permission createPermission(String tenantId,
279                 String resourceName,
280                 String description,
281                 ActionGroup actionGroup) {
282         String id = tenantId
283                         + "-" + resourceName.replace('/', '_') // Remove the slashes so the ID can be used in a URI/URL
284                         + "-" + actionGroup.name;
285         Permission perm = new Permission();
286         perm.setCsid(id);
287         perm.setDescription(description);
288         perm.setCreatedAtItem(new Date());
289         perm.setResourceName(resourceName.toLowerCase().trim());
290         perm.setEffect(EffectType.PERMIT);
291         perm.setTenantId(tenantId);
292         
293         perm.setActionGroup(actionGroup.name);
294         ArrayList<PermissionAction> pas = new ArrayList<PermissionAction>();
295         perm.setAction(pas);
296         for (ActionType actionType : actionGroup.actions) {
297                 PermissionAction permAction = createPermissionAction(perm, actionType);
298                 pas.add(permAction);
299         }
300         
301         return perm;
302     }
303     
304     private static Permission createWorkflowPermission(TenantBindingType tenantBinding,
305                 ServiceBindingType serviceBinding,
306                 String transitionVerb,
307                 ActionGroup actionGroup)
308     {
309         Permission result = null;
310         String workFlowServiceSuffix;
311         String transitionName;
312         if (transitionVerb != null) {
313                 transitionName = transitionVerb;
314                 workFlowServiceSuffix = WorkflowClient.SERVICE_AUTHZ_SUFFIX;
315         } else {
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;
318         }
319         
320         String tenantId = tenantBinding.getId();
321         String resourceName = "/"
322                         + serviceBinding.getName().toLowerCase().trim()
323                         + workFlowServiceSuffix
324                         + transitionName;
325         String description = "A generated workflow permission for actiongroup " + actionGroup.name;
326         result = createPermission(tenantId, resourceName, description, actionGroup);
327         
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);
334         }
335         
336         return result;
337     }
338     
339     private static PermissionRole createPermissionRole(EntityManager em,
340                 Permission permission,
341                 Role role,
342                 boolean enforceTenancy) throws Exception
343     {
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.
349         }
350                         
351                 if (tenantIdsMatch == true) {
352                 permRole.setSubject(SubjectType.ROLE);
353                 //
354                 // Set of the permission value list of the permrole
355                 //
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);
363                 //
364                 // Set of the role value list of the permrole
365                 //
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());
371             roleValues.add(rv);
372             permRole.setRole(roleValues);
373                 } else {
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);
377                 }
378         
379         return permRole;
380     }
381     
382     private static Hashtable<String, String> getTenantNamesFromConfig(TenantBindingConfigReaderImpl tenantBindingConfigReader) {
383
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);
394                 }
395         }
396         return tenantInfo;
397     }
398     
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";
405         try {
406                 stmt = conn.createStatement();
407                 ResultSet rs = stmt.executeQuery(queryTenantSQL);
408                 while (rs.next()) {
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));
417                                 }
418                         }
419                 }
420                 rs.close();
421         } catch(Exception e) {
422                 throw e;
423         } finally {
424                 if(stmt!=null)
425                         stmt.close();
426         }
427
428         return existingTenants;
429     }
430     
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,authorities_initialized,disabled,created_at) VALUES (?,?,FALSE,FALSE,now())";
436         PreparedStatement pstmt = null;
437         try {
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));
444                                 }
445                                 continue;
446                         }
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);
451                         }
452                         pstmt.executeUpdate();
453                 }
454                 pstmt.close();
455         } catch(Exception e) {
456                 throw e;
457         } finally {
458                 if(pstmt!=null)
459                         pstmt.close();
460         }
461     }
462     
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>();
469         try {
470                 stmt = conn.createStatement();
471                 ResultSet rs = stmt.executeQuery(QUERY_USERS_SQL);
472                 while (rs.next()) {
473                         String uName = rs.getString("username");
474                         usersInRepo.add(uName);
475                 }
476                 rs.close();
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);
488                                 }
489                                 pstmt.executeUpdate();
490                         } else if (logger.isDebugEnabled()) {
491                                 logger.debug("createDefaultUsersAndAccounts: user: "+adminAcctName
492                                                 +" already exists - skipping.");
493                         }
494
495
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);
505                                 }
506                                 pstmt.executeUpdate();
507                         } else if (logger.isDebugEnabled()) {
508                                 logger.debug("createDefaultUsersAndAccounts: user: "+readerAcctName
509                                                 +" already exists - skipping.");
510                         }
511                 }
512                 pstmt.close();
513         } catch(Exception e) {
514                 throw e;
515         } finally {
516                 if(stmt!=null)
517                         stmt.close();
518                 if(pstmt!=null)
519                         pstmt.close();
520         }
521         return usersInRepo;
522     }
523     
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;
531         try {
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);
546                                 }
547                                 pstmt.executeUpdate();
548                         } else if (logger.isDebugEnabled()) {
549                                 logger.debug("createDefaultAccounts: user: "+adminAcctName
550                                                 +" already exists - skipping account generation.");
551                         }
552
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);
564                                 }
565                                 pstmt.executeUpdate();
566                         } else if (logger.isDebugEnabled()) {
567                                 logger.debug("createDefaultAccounts: user: "+readerAcctName
568                                                 +" already exists - skipping account creation.");
569                         }
570                 }
571                 pstmt.close();
572         } catch(Exception e) {
573                 throw e;
574         } finally {
575                 if(pstmt!=null)
576                         pstmt.close();
577         }
578     }
579     
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;
587         try {
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
592                 if(rs.next()) {
593                         String uName = rs.getString("username");
594                         foundTMgrUser = uName.equals(TENANT_MANAGER_USER);
595                 }
596                 rs.close();
597                 if(!foundTMgrUser) {
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);
606                         }
607                         pstmt.executeUpdate();
608                 pstmt.close();
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);
618                                 }
619                                 pstmt.executeUpdate();
620                         pstmt.close();
621                         created = true;
622                 } else if (logger.isDebugEnabled()) {
623                         logger.debug("findOrCreateTenantManagerUserAndAccount: tenant manager: "+TENANT_MANAGER_USER
624                                         +" already exists.");
625                 }
626         } catch(Exception e) {
627                 throw e;
628         } finally {
629                 if(stmt!=null)
630                         stmt.close();
631                 if(pstmt!=null)
632                         pstmt.close();
633         }
634         return created;
635     }
636     
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;
644         try {
645                 String insertAccountTenantSQL;
646                 if (databaseProductType == DatabaseProductType.MYSQL) {
647                         insertAccountTenantSQL =
648                                         "INSERT INTO accounts_tenants (TENANTS_ACCOUNTS_COMMON_CSID,tenant_id) "
649                                                         + " VALUES(?, ?)";
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'), ?, ?)";
654                 } else {
655                         throw new Exception("Unrecognized database system.");
656                 }
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);
667                                 }
668                                 pstmt.executeUpdate();
669                         }
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);
677                                 }
678                                 pstmt.executeUpdate();
679                         }
680                 }
681                 pstmt.close();
682         } catch(Exception e) {
683                 throw e;
684         } finally {
685                 if(pstmt!=null)
686                         pstmt.close();
687         }
688     }
689     
690     
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;
698         try {
699                 final String querySpringRole = 
700                                 "SELECT csid from roles WHERE rolename='"+AuthN.ROLE_SPRING_ADMIN_NAME+"'";
701                 stmt = conn.createStatement();
702                 ResultSet rs = stmt.executeQuery(querySpringRole);
703                 if(rs.next()) {
704                         springAdminRoleCSID = rs.getString(1);
705                         if (logger.isDebugEnabled()) {
706                                 logger.debug("createDefaultAccounts found Spring Admin role: "
707                                                 +springAdminRoleCSID);
708                         }
709                 } else {
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);
718                         }
719                 }
720                 rs.close();
721                 final String getRoleCSIDSql =
722                                 "SELECT csid from roles WHERE tenant_id=? and rolename=?";
723                 pstmt = conn.prepareStatement(getRoleCSIDSql); // create a statement
724                 rs = null;
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
730                         if(!rs.next()) {
731                                 throw new RuntimeException("Cannot find role: "+getDefaultAdminRole(tId)
732                                                 +" for tenant id: "+tId+" in roles!");
733                         }
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);
739                         }
740                         tenantAdminRoleCSIDs.put(tId, tenantAdminRoleCSID);
741                         pstmt.setString(1, tId);                                                // set tenant_id param
742                         pstmt.setString(2, getDefaultReaderRole(tId));  // set rolename param
743                         rs.close();
744                         rs = pstmt.executeQuery();
745                         // extract data from the ResultSet
746                         if(!rs.next()) {
747                                 throw new RuntimeException("Cannot find role: "+getDefaultReaderRole(tId)
748                                                 +" for tenant id: "+tId+" in roles!");
749                         }
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);
755                         }
756                         tenantReaderRoleCSIDs.put(tId, tenantReaderRoleCSID);
757                         rs.close();
758                 }
759                 pstmt.close();
760         } catch(Exception e) {
761                 throw e;
762         } finally {
763                 if(stmt!=null)
764                         stmt.close();
765                 if(pstmt!=null)
766                         pstmt.close();
767         }
768         return springAdminRoleCSID;
769     }
770
771     private static String findTenantManagerRole(Connection conn ) 
772                         throws SQLException, RuntimeException, Exception {
773                 String tenantMgrRoleCSID = null;
774         PreparedStatement pstmt = null;
775         try {
776                 String rolename = getQualifiedRoleName(AuthN.ALL_TENANTS_MANAGER_TENANT_ID, 
777                                 AuthN.ROLE_ALL_TENANTS_MANAGER);                
778                 pstmt = conn.prepareStatement(GET_TENANT_MGR_ROLE_SQL); // create a statement
779                 ResultSet rs = null;
780                 pstmt.setString(1, rolename);   // set rolename param
781                 rs = pstmt.executeQuery();
782                 if(rs.next()) {
783                         tenantMgrRoleCSID = rs.getString(1);
784                         if (logger.isDebugEnabled()) {
785                                 logger.debug("findTenantManagerRole found Tenant Mgr role: "
786                                                 +tenantMgrRoleCSID);
787                         }
788                 }
789                 rs.close();
790         } catch(Exception e) {
791                 throw e;
792         } finally {
793                 if(pstmt!=null)
794                         pstmt.close();
795         }
796         if(tenantMgrRoleCSID==null)
797                 throw new RuntimeException("findTenantManagerRole: Cound not find tenant Manager Role!");
798         return tenantMgrRoleCSID;
799     }
800
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;
810         try {
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;
816                 } else {
817                         throw new Exception("Unrecognized database system.");
818                 }
819                 if (logger.isDebugEnabled()) {
820                         logger.debug("createDefaultAccounts binding accounts to roles with SQL:\n"
821                                         +insertAccountRoleSQL);
822                 }
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);
837                                 }
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, AuthN.ROLE_SPRING_ADMIN_NAME);               // set rolename param
842                                 if (logger.isDebugEnabled()) {
843                                         logger.debug("createDefaultAccounts binding account: "
844                                                         +adminUserId+" to Spring Admin role: "+springAdminRoleCSID);
845                                 }
846                                 pstmt.executeUpdate();
847                         }
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);
860                                 }
861                                 pstmt.executeUpdate();
862                         }
863                 }
864                 pstmt.close();
865         } catch(Exception e) {
866                 throw e;
867         } finally {
868                 if(pstmt!=null)
869                         pstmt.close();
870         }
871     }
872     
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;
877         try {
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;
883                 } else {
884                         throw new Exception("Unrecognized database system.");
885                 }
886                 if (logger.isDebugEnabled()) {
887                         logger.debug("bindTenantManagerAccountRole binding account to role with SQL:\n"
888                                         +insertAccountRoleSQL);
889                 }
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+")");
898                 }
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);
906                 }
907                 pstmt.executeUpdate();
908                 */
909                 pstmt.close();
910         } catch(Exception e) {
911                 throw e;
912         } finally {
913                 if(pstmt!=null)
914                         pstmt.close();
915         }
916     }
917     
918     public static void createDefaultAccounts(
919                 TenantBindingConfigReaderImpl tenantBindingConfigReader,
920                 DatabaseProductType databaseProductType,
921                 String cspaceDatabaseName) throws Exception {
922
923         logger.debug("ServiceMain.createDefaultAccounts starting...");
924         
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.
933         try {
934                 conn = getConnection(cspaceDatabaseName);
935                 ArrayList<String> existingTenants = compileExistingTenants(conn, tenantInfo);
936                 
937                 // Note that this only creates tenants not marked as "createDisabled"
938                 createMissingTenants(conn, tenantInfo, existingTenants);
939                 
940                 ArrayList<String> usersInRepo = findOrCreateDefaultUsers(conn, tenantInfo);
941                 
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);
946
947                 bindDefaultAccountsToTenants(conn, databaseProductType, tenantInfo, usersInRepo,
948                                 tenantAdminAcctCSIDs, tenantReaderAcctCSIDs);
949                 
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);
954                 
955                 bindAccountsToRoles(conn,  databaseProductType,
956                                 tenantInfo, usersInRepo, springAdminRoleCSID,
957                                 tenantAdminRoleCSIDs, tenantReaderRoleCSIDs,
958                                 tenantAdminAcctCSIDs, tenantReaderAcctCSIDs);
959                 
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, AuthN.ROLE_ALL_TENANTS_MANAGER);
968                 }
969         } catch (Exception e) {
970                         logger.debug("Exception in createDefaultAccounts: " + e.getLocalizedMessage());
971                 throw e;
972                 } finally {
973                         try {
974                                 if (conn != null)
975                                         conn.close();
976                         } catch (SQLException sqle) {
977                                 if (logger.isDebugEnabled()) {
978                                         logger.debug("SQL Exception closing statement/connection: " + sqle.getLocalizedMessage());
979                                 }
980                         }
981                 }       
982     }
983     
984     private static String getDefaultAdminRole(String tenantId) {
985         return ROLE_PREFIX+tenantId+TENANT_ADMIN_ROLE_SUFFIX;
986     }
987     
988     private static String getDefaultReaderRole(String tenantId) {
989         return ROLE_PREFIX+tenantId+TENANT_READER_ROLE_SUFFIX;
990     }
991     
992     private static String getDefaultAdminUserID(String tenantName) {
993         return TENANT_ADMIN_ACCT_PREFIX+tenantName;
994     }
995     
996     private static String getDefaultReaderUserID(String tenantName) {
997         return TENANT_READER_ACCT_PREFIX+tenantName;
998     }
999     
1000         static public PermissionAction createPermissionAction(Permission perm,
1001                         ActionType actionType) {
1002         PermissionAction pa = new PermissionAction();
1003
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());
1010             
1011             return pa;
1012         }
1013
1014         static public PermissionAction update(Permission perm, PermissionAction permAction) {
1015         PermissionAction pa = new PermissionAction();
1016
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());
1022             
1023             return pa;
1024         }
1025         
1026         private static HashSet<String> getTransitionVerbList(TenantBindingType tenantBinding, ServiceBindingType serviceBinding) {
1027                 HashSet<String> result = new HashSet<String>();
1028                 
1029                 TransitionDefList transitionDefList = getTransitionDefList(tenantBinding, serviceBinding);
1030         for (TransitionDef transitionDef : transitionDefList.getTransitionDef()) {
1031                 String transitionVerb = transitionDef.getName();
1032                 String[] tokens = transitionVerb.split("_");  // Split the verb into words.  The workflow verbs are compound words combined with the '_' character.
1033                 result.add(tokens[0]); // We only care about the first word.
1034         }
1035
1036         return result;
1037         }
1038         
1039         private static TransitionDefList getTransitionDefList(TenantBindingType tenantBinding, ServiceBindingType serviceBinding) {
1040                 TransitionDefList result = null;
1041                 try {
1042                         String serviceObjectName = serviceBinding.getObject().getName();
1043                 DocumentHandler docHandler = ServiceConfigUtils.createDocumentHandlerInstance(
1044                                 tenantBinding, serviceBinding);
1045                 Lifecycle lifecycle = docHandler.getLifecycle(serviceObjectName);
1046                 if (lifecycle != null) {
1047                         result = lifecycle.getTransitionDefList();
1048                 }
1049                 } catch (Exception e) {
1050                         // Ignore this exception and return an empty non-null TransitionDefList
1051                 }
1052                 
1053                 if (result == null) {
1054                         if (serviceBinding.getType().equalsIgnoreCase(ServiceBindingUtils.SERVICE_TYPE_SECURITY) == false) {
1055                                 logger.debug("Could not retrieve a lifecycle transition definition list from: "
1056                                                 + serviceBinding.getName()
1057                                                 + " with tenant ID = "
1058                                                 + tenantBinding.getId());
1059                         }
1060                         // return an empty list                 
1061                         result = new TransitionDefList();
1062                 } else {
1063                         logger.debug("Successfully retrieved a lifecycle transition definition list from: "
1064                                         + serviceBinding.getName()
1065                                         + " with tenant ID = "
1066                                         + tenantBinding.getId());
1067                 }
1068                 
1069                 return result;
1070         }
1071         
1072     public static void createDefaultWorkflowPermissions(TenantBindingConfigReaderImpl tenantBindingConfigReader) throws Exception //FIXME: REM - 4/11/2012 - Rename to createWorkflowPermissions
1073     {
1074         java.util.logging.Logger logger = java.util.logging.Logger.getAnonymousLogger();
1075
1076         AuthZ.get().login(); //login to Spring Security manager
1077         
1078         EntityManagerFactory emf = JpaStorageUtils.getEntityManagerFactory(JpaStorageUtils.CS_PERSISTENCE_UNIT);
1079         EntityManager em = null;
1080
1081         try {
1082             em = emf.createEntityManager();
1083
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                         
1092                         if (adminRole == null || readonlyRole == null) {
1093                                 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.");
1094                                 logger.info(msg);
1095                                 throw new RuntimeException("One or more of the required default CollectionSpace administrator roles is missing or was never created.");
1096                         }
1097                         
1098                         for (ServiceBindingType serviceBinding : tenantBinding.getServiceBindings()) {
1099                                 String prop = ServiceBindingUtils.getPropertyValue(serviceBinding, REFRESH_AUTZ_PROP);
1100                                 if (prop == null ? true : Boolean.parseBoolean(prop)) {
1101                                                 try {
1102                                                 em.getTransaction().begin();
1103                                                 TransitionDefList transitionDefList = getTransitionDefList(tenantBinding, serviceBinding);
1104                                                 HashSet<String> transitionVerbList = getTransitionVerbList(tenantBinding, serviceBinding);
1105                                                 for (String transitionVerb : transitionVerbList) {
1106                                                         //
1107                                                         // Create the permission for the admin role
1108                                                         Permission adminPerm = createWorkflowPermission(tenantBinding, serviceBinding, transitionVerb, ACTIONGROUP_CRUDL);
1109                                                         persist(em, adminPerm, adminRole, true, ACTIONGROUP_CRUDL);
1110                                                         //
1111                                                         // Create the permission for the read-only role
1112                                                         Permission readonlyPerm = createWorkflowPermission(tenantBinding, serviceBinding, transitionVerb, ACTIONGROUP_RL);                                                      
1113                                                         persist(em, readonlyPerm, readonlyRole, true, ACTIONGROUP_RL); // Persist/store the permission and permrole records and related Spring Security info
1114                                                 }
1115                                                 em.getTransaction().commit();
1116                                         } catch (IllegalStateException e) {
1117                                                 logger.fine(e.getLocalizedMessage()); //We end up here if there is no document handler for the service -this is ok for some of the services.
1118                                         }
1119                                 } else {
1120                                         logger.warning("AuthZ refresh service binding property is set to FALSE so default permissions will NOT be refreshed for: "
1121                                                         + serviceBinding.getName());
1122                                 }
1123                         }
1124                 }
1125             em.close();
1126         } catch (Exception e) {
1127             if (em != null && em.getTransaction().isActive()) {
1128                 em.getTransaction().rollback();
1129             }
1130             logger.fine("Caught exception and rolling back permission creation: " + e.getMessage());
1131             throw e;
1132         } finally {
1133             if (em != null) {
1134                 JpaStorageUtils.releaseEntityManagerFactory(emf);
1135             }
1136         }
1137     }
1138     
1139     private static PermissionRoleRel findPermRoleRel(EntityManager em, String permissionId, String RoleId) {
1140         PermissionRoleRel result = null;
1141         
1142         try {
1143                 String whereClause = "where permissionId = :id and roleId = :roleId";
1144                 HashMap<String, Object> params = new HashMap<String, Object>();
1145                 params.put("id", permissionId);
1146                 params.put("roleId", RoleId);        
1147         
1148                 result = (PermissionRoleRel) JpaStorageUtils.getEntity(em,
1149                                 PermissionRoleRel.class.getCanonicalName(), whereClause, params);
1150         } catch (Exception e) {
1151                 //Do nothing. Will return null;
1152         }
1153                 
1154         return result;
1155     }
1156     
1157     /*
1158      * Persists the Permission, PermissionRoleRel, and Spring Security table entries all in one transaction
1159      */
1160     private static void persist(EntityManager em, Permission permission, Role role, boolean enforceTenancy, ActionGroup actionGroup) throws Exception {
1161                 AuthorizationStore authzStore = new AuthorizationStore();
1162                 // First persist the Permission record
1163                 authzStore.store(em, permission);
1164                 
1165                 // If the PermRoleRel doesn't already exists then relate the permission and the role in a new PermissionRole (the service payload)
1166                 // Create a PermissionRoleRel (the database relation table for the permission and role)
1167                 PermissionRoleRel permRoleRel = findPermRoleRel(em, permission.getCsid(), role.getCsid());
1168                 if (permRoleRel == null) {
1169                         PermissionRole permRole = createPermissionRole(em, permission, role, enforceTenancy);
1170                 List<PermissionRoleRel> permRoleRels = new ArrayList<PermissionRoleRel>();
1171                 PermissionRoleUtil.buildPermissionRoleRel(em, permRole, SubjectType.ROLE, permRoleRels,
1172                                 false /*not for delete*/, role.getTenantId());
1173                 for (PermissionRoleRel prr : permRoleRels) {
1174                     authzStore.store(em, prr);
1175                 }
1176                         Profiler profiler = new Profiler(AuthorizationCommon.class, 2);
1177                         profiler.start();
1178                         // Add a corresponding entry in the Spring Security Tables
1179                         addPermissionsForUri(permission, permRole);
1180                         profiler.stop();
1181                         logger.debug("Finished full perm generation for "
1182                                         + ":" + permission.getTenantId()
1183                                         + ":" + permission.getResourceName()
1184                                         + ":" + actionGroup.getName()
1185                                         + ":" + profiler.getCumulativeTime());
1186                 }
1187         
1188     }
1189         
1190         public static boolean hasTokenExpired(EmailConfig emailConfig, Token token) throws NoSuchAlgorithmException {
1191                 boolean result = false;
1192                 
1193                 int maxConfigSeconds = emailConfig.getPasswordResetConfig().getTokenExpirationSeconds().intValue();
1194                 int maxTokenSeconds = token.getExpireSeconds().intValue();
1195                 
1196                 long createdTime = token.getCreatedAtItem().getTime();          
1197                 long configExpirationTime = createdTime + maxConfigSeconds * 1000;              // the current tenant config for how long a token stays valid
1198                 long tokenDefinedExirationTime = createdTime + maxTokenSeconds * 1000;  // the tenant config for how long a token stays valid when the token was created.
1199                 
1200                 if (configExpirationTime != tokenDefinedExirationTime) {
1201                         String msg = String.format("The configured expiration time for the token = '%s' changed from when the token was created.",
1202                                         token.getId());
1203                         logger.warn(msg);
1204                 }
1205                 //
1206                 // Note: the current tenant bindings config for expiration takes precedence over the config used to create the token.
1207                 //
1208                 if (System.currentTimeMillis() >= configExpirationTime) {
1209                         result = true;
1210                 }
1211                 
1212                 return result;
1213         }
1214                 
1215         /*
1216          * Validate that the password reset configuration is correct.
1217          */
1218         private static String validatePasswordResetConfig(PasswordResetConfig passwordResetConfig) {
1219                 String result = null;
1220                 
1221                 if (passwordResetConfig != null) {
1222                         result = passwordResetConfig.getMessage();
1223                         if (result == null || result.length() == 0) {
1224                                 result = DEFAULT_PASSWORD_RESET_EMAIL_MESSAGE;
1225                                 logger.warn("Could not find a password reset message in the tenant's configuration.  Using the default one");
1226                         }
1227                         
1228                         if (result.contains("{{link}}") == false) {
1229                                 logger.warn("The tenant's password reset message does not contain a required '{{link}}' marker.");
1230                                 result = null;
1231                         }
1232                         
1233                         if (passwordResetConfig.getLoginpage() == null || passwordResetConfig.getLoginpage().trim().isEmpty()) {
1234                                 logger.warn("The tenant's password reset configuration is missing a 'loginpage' value.  It should be set to something like '/collectionspace/ui/core/html/index.html'.");
1235                                 result = null;
1236                         }
1237                         
1238                     String subject = passwordResetConfig.getSubject();
1239                     if (subject == null || subject.trim().isEmpty()) {
1240                         passwordResetConfig.setSubject(DEFAULT_PASSWORD_RESET_EMAIL_SUBJECT);
1241                     }
1242
1243                 }
1244                 
1245                 return result;
1246         }
1247         
1248         /*
1249          * Generate a password reset message. Embeds an authorization token to reset a user's password.
1250          */
1251         public static String generatePasswordResetEmailMessage(EmailConfig emailConfig, AccountListItem accountListItem, Token token) throws Exception {
1252                 String result = null;
1253                 
1254                 result = validatePasswordResetConfig(emailConfig.getPasswordResetConfig());
1255                 if (result == null) {
1256                         String errMsg = String.format("The password reset configuration for the tenant ID='%s' is missing or malformed.  Could not initiate a password reset for user ID='%s. See the log files for more details.",
1257                                         token.getTenantId(), accountListItem.getEmail());
1258                         throw new Exception(errMsg);
1259                 }
1260                 
1261                 String link = emailConfig.getBaseurl() + emailConfig.getPasswordResetConfig().getLoginpage() + "?token=" + token.getId();
1262                 result = result.replaceAll("\\{\\{link\\}\\}", link);
1263                 
1264                 if (result.contains("{{greeting}}")) {
1265                         String greeting = accountListItem.getScreenName();
1266                         result = result.replaceAll("\\{\\{greeting\\}\\}", greeting);
1267                         result = result.replaceAll("\\\\n", "\\\n");
1268                         result = result.replaceAll("\\\\r", "\\\r");
1269                 }                       
1270                 
1271                 return result;
1272         }
1273 }