]> git.aero2k.de Git - tmp/jakarta-migration.git/blob
c9376c2685b5e0bfcfd400069d9dfa49340da63c
[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 import org.collectionspace.services.common.config.ServiceConfigUtils;
45 import org.collectionspace.services.common.config.TenantBindingConfigReaderImpl;
46 import org.collectionspace.services.common.context.ServiceBindingUtils;
47 import org.collectionspace.services.common.document.DocumentHandler;
48 import org.collectionspace.services.common.security.SecurityUtils;
49 import org.collectionspace.services.common.storage.DatabaseProductType;
50 import org.collectionspace.services.common.storage.JDBCTools;
51 import org.collectionspace.services.common.storage.jpa.JpaStorageUtils;
52 import org.collectionspace.services.config.service.ServiceBindingType;
53 import org.collectionspace.services.config.tenant.EmailConfig;
54 import org.collectionspace.services.config.tenant.PasswordResetConfig;
55 import org.collectionspace.services.config.tenant.TenantBindingType;
56 import org.collectionspace.services.lifecycle.Lifecycle;
57 import org.collectionspace.services.lifecycle.TransitionDef;
58 import org.collectionspace.services.lifecycle.TransitionDefList;
59
60 //import org.mortbay.log.Log;
61 import org.slf4j.Logger;
62 import org.slf4j.LoggerFactory;
63 import org.springframework.security.acls.model.AlreadyExistsException;
64
65
66 public class AuthorizationCommon {
67         
68         final public static String REFRESH_AUTZ_PROP = "refreshAuthZOnStartup";
69         
70         //
71         // For token generation and password reset
72         //
73         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.";
74         final private static String tokensalt = "74102328UserDetailsReset";
75         final private static int TIME_SCALAR = 100000;
76         private static final String DEFAULT_PASSWORD_RESET_EMAIL_SUBJECT = "Password reset for CollectionSpace account";
77
78     //
79     // ActionGroup labels/constants
80     //
81         
82         // for READ-WRITE
83     final public static String ACTIONGROUP_CRUDL_NAME = "CRUDL";
84     final public static ActionType[] ACTIONSET_CRUDL = {ActionType.CREATE, ActionType.READ, ActionType.UPDATE, ActionType.DELETE, ActionType.SEARCH};
85     // for READ-ONLY
86     final public static String ACTIONGROUP_RL_NAME = "RL";
87     final public static ActionType[] ACTIONSET_RL = {ActionType.READ, ActionType.SEARCH};
88         
89         static ActionGroup ACTIONGROUP_CRUDL;
90         static ActionGroup ACTIONGROUP_RL;
91         
92         // A static block to initialize the predefined action groups
93         static {
94                 // For admin
95                 ACTIONGROUP_CRUDL = new ActionGroup();
96                 ACTIONGROUP_CRUDL.name = ACTIONGROUP_CRUDL_NAME;
97                 ACTIONGROUP_CRUDL.actions = ACTIONSET_CRUDL;
98                 // For reader
99                 ACTIONGROUP_RL = new ActionGroup();
100                 ACTIONGROUP_RL.name = ACTIONGROUP_RL_NAME;
101                 ACTIONGROUP_RL.actions = ACTIONSET_RL;
102
103         }
104         
105     final static Logger logger = LoggerFactory.getLogger(AuthorizationCommon.class);
106
107     final public static String ROLE_TENANT_ADMINISTRATOR = "TENANT_ADMINISTRATOR";
108     final public static String ROLE_TENANT_READER = "TENANT_READER";
109         
110     public static final String TENANT_MANAGER_USER = "tenantManager"; 
111     public static final String TENANT_MANAGER_SCREEN_NAME = TENANT_MANAGER_USER; 
112     public static final String DEFAULT_TENANT_MANAGER_PASSWORD = "manage"; 
113     public static final String DEFAULT_TENANT_MANAGER_EMAIL = "tenantManager@collectionspace.org"; 
114     
115     public static final String TENANT_ADMIN_ACCT_PREFIX = "admin@"; 
116     public static final String TENANT_READER_ACCT_PREFIX = "reader@"; 
117     public static final String ROLE_PREFIX = "ROLE_"; 
118     public static final String TENANT_ADMIN_ROLE_SUFFIX = "_TENANT_ADMINISTRATOR"; 
119     public static final String TENANT_READER_ROLE_SUFFIX = "_TENANT_READER"; 
120     public static final String DEFAULT_ADMIN_PASSWORD = "Administrator";
121     public static final String DEFAULT_READER_PASSWORD = "reader";
122     
123     // SQL for init tasks
124         final private static String INSERT_ACCOUNT_ROLE_SQL_MYSQL = 
125                         "INSERT INTO accounts_roles(account_id, user_id, role_id, role_name, created_at)"
126                                         +" VALUES(?, ?, ?, ?, now())";
127         final private static String INSERT_ACCOUNT_ROLE_SQL_POSTGRES =
128                         "INSERT INTO accounts_roles(HJID, account_id, user_id, role_id, role_name, created_at)"
129                                         +" VALUES(nextval('hibernate_sequence'), ?, ?, ?, ?, now())";
130         final private static String QUERY_USERS_SQL = 
131                 "SELECT username FROM users WHERE username LIKE '"
132                         +TENANT_ADMIN_ACCT_PREFIX+"%' OR username LIKE '"+TENANT_READER_ACCT_PREFIX+"%'";
133         final private static String INSERT_USER_SQL = 
134                         "INSERT INTO users (username,passwd, created_at) VALUES (?,?, now())";
135         final private static String INSERT_ACCOUNT_SQL = 
136                         "INSERT INTO accounts_common "
137                                         + "(csid, email, userid, status, screen_name, metadata_protection, roles_protection, created_at) "
138                                         + "VALUES (?,?,?,'ACTIVE',?, 'immutable', 'immutable', now())";
139         
140         // TENANT MANAGER specific SQL
141         final private static String QUERY_TENANT_MGR_USER_SQL = 
142                 "SELECT username FROM users WHERE username = '"+TENANT_MANAGER_USER+"'";
143         final private static String GET_TENANT_MGR_ROLE_SQL =
144                         "SELECT csid from roles WHERE tenant_id='" + AuthN.ALL_TENANTS_MANAGER_TENANT_ID + "' and rolename=?";
145
146
147     public static Role getRole(String tenantId, String displayName) {
148         Role role = null;
149         
150         String roleName = AuthorizationCommon.getQualifiedRoleName(tenantId, displayName);
151         role = AuthorizationStore.getRoleByName(roleName, tenantId);
152         
153         return role;
154     }
155     
156     public static Role getRole(EntityManager em, String tenantId, String displayName) {
157         Role role = null;
158         
159         String roleName = AuthorizationCommon.getQualifiedRoleName(tenantId, displayName);
160         role = AuthorizationStore.getRoleByName(em, roleName, tenantId);
161         
162         return role;
163     }
164     
165     
166     public static Role createRole(String tenantId, String name, String description) {
167         return createRole(tenantId, name, description, false /* mutable by default */);
168     }
169     
170     public static Role createRole(String tenantId, String name, String description, boolean immutable) {
171         Role role = new Role();
172         
173         role.setCreatedAtItem(new Date());
174         role.setDisplayName(name);
175         String roleName = AuthorizationCommon.getQualifiedRoleName(tenantId, name);     
176         role.setRoleName(roleName);
177         String id = UUID.randomUUID().toString(); //FIXME: The qualified role name should be unique enough to use as an ID/key
178         role.setCsid(id);
179                 role.setDescription(description);
180         role.setTenantId(tenantId);
181         if (immutable == true) {
182                 role.setMetadataProtection(RoleClient.IMMUTABLE);
183                 role.setPermsProtection(RoleClient.IMMUTABLE);
184         }
185         
186         return role;
187     }
188     
189     /**
190      * Add permission to the Spring Security tables
191      * with assumption that resource is of type URI
192      * @param permission configuration
193      */
194     public static void addPermissionsForUri(Permission perm,
195             PermissionRole permRole) throws PermissionException {
196         //
197         // First check the integrity of the incoming arguments.
198         //
199         if (!perm.getCsid().equals(permRole.getPermission().get(0).getPermissionId())) {
200             throw new IllegalArgumentException("permission ids do not"
201                     + " match for role=" + permRole.getRole().get(0).getRoleName()
202                     + " with permissionId=" + permRole.getPermission().get(0).getPermissionId()
203                     + " for permission with csid=" + perm.getCsid());
204         }
205         
206         List<String> principals = new ArrayList<String>();        
207         for (RoleValue roleValue : permRole.getRole()) {
208             principals.add(roleValue.getRoleName());
209         }
210         List<PermissionAction> permActions = perm.getAction();
211         for (PermissionAction permAction : permActions) {
212                 try {
213                     CSpaceAction action = URIResourceImpl.getAction(permAction.getName()); 
214                     URIResourceImpl uriRes = new URIResourceImpl(perm.getTenantId(),
215                             perm.getResourceName(), action);
216                     boolean grant = perm.getEffect().equals(EffectType.PERMIT) ? true : false;
217                     AuthZ.get().addPermissions(uriRes, principals.toArray(new String[0]), grant);//CSPACE-4967
218                 } catch (PermissionException e) {
219                         //
220                         // Only throw the exception if it is *not* an already-exists exception
221                         //
222                         if (e.getCause() instanceof AlreadyExistsException == false) {
223                                 throw e;
224                         }
225                 }
226         }
227     }
228     
229     private static Connection getConnection(String databaseName) throws NamingException, SQLException {
230         return JDBCTools.getConnection(JDBCTools.CSPACE_DATASOURCE_NAME,
231                         databaseName);
232     }
233     
234     /*
235      * Spring security seems to require that all of our role names start
236      * with the ROLE_PREFIX string.
237      */
238     public static String getQualifiedRoleName(String tenantId, String name) {
239         String result = name;
240         
241         String qualifiedName = ROLE_PREFIX + tenantId.toUpperCase() + "_" + name.toUpperCase();         
242         if (name.equals(qualifiedName) == false) {
243                 result = qualifiedName;
244         }
245         
246         return result;
247     }
248         
249     private static ActionGroup getActionGroup(String actionGroupStr) {
250         ActionGroup result = null;
251         
252         if (actionGroupStr.equalsIgnoreCase(ACTIONGROUP_CRUDL_NAME)) {
253                 result = ACTIONGROUP_CRUDL;
254         } else if (actionGroupStr.equalsIgnoreCase(ACTIONGROUP_RL_NAME)) {
255                 result = ACTIONGROUP_RL;
256         }
257         
258         return result;
259     }
260     
261     public static Permission createPermission(String tenantId,
262                 String resourceName,
263                 String description,
264                 String actionGroupStr) {
265         Permission result = null;
266         
267         ActionGroup actionGroup = getActionGroup(actionGroupStr);
268         result = createPermission(tenantId, resourceName, description, actionGroup);
269         
270         return result;
271     }
272     
273     private static Permission createPermission(String tenantId,
274                 String resourceName,
275                 String description,
276                 ActionGroup actionGroup) {
277         String id = tenantId
278                         + "-" + resourceName.replace('/', '_') // Remove the slashes so the ID can be used in a URI/URL
279                         + "-" + actionGroup.name;
280         Permission perm = new Permission();
281         perm.setCsid(id);
282         perm.setDescription(description);
283         perm.setCreatedAtItem(new Date());
284         perm.setResourceName(resourceName.toLowerCase().trim());
285         perm.setEffect(EffectType.PERMIT);
286         perm.setTenantId(tenantId);
287         
288         perm.setActionGroup(actionGroup.name);
289         ArrayList<PermissionAction> pas = new ArrayList<PermissionAction>();
290         perm.setAction(pas);
291         for (ActionType actionType : actionGroup.actions) {
292                 PermissionAction permAction = createPermissionAction(perm, actionType);
293                 pas.add(permAction);
294         }
295         
296         return perm;
297     }
298     
299     private static Permission createWorkflowPermission(TenantBindingType tenantBinding,
300                 ServiceBindingType serviceBinding,
301                 String transitionVerb,
302                 ActionGroup actionGroup)
303     {
304         Permission result = null;
305         String workFlowServiceSuffix;
306         String transitionName;
307         if (transitionVerb != null) {
308                 transitionName = transitionVerb;
309                 workFlowServiceSuffix = WorkflowClient.SERVICE_AUTHZ_SUFFIX;
310         } else {
311                 transitionName = ""; //since the transitionDef was null, we're assuming that this is the base workflow permission to be created                 
312                 workFlowServiceSuffix = WorkflowClient.SERVICE_PATH;
313         }
314         
315         String tenantId = tenantBinding.getId();
316         String resourceName = "/"
317                         + serviceBinding.getName().toLowerCase().trim()
318                         + workFlowServiceSuffix
319                         + transitionName;
320         String description = "A generated workflow permission for actiongroup " + actionGroup.name;
321         result = createPermission(tenantId, resourceName, description, actionGroup);
322         
323         if (logger.isDebugEnabled() == true) {
324                 logger.debug("Generated a workflow permission: "
325                                 + result.getResourceName()
326                                 + ":" + transitionName
327                                 + ":" + "tenant id=" + result.getTenantId()
328                                 + ":" + actionGroup.name);
329         }
330         
331         return result;
332     }
333     
334     private static PermissionRole createPermissionRole(EntityManager em,
335                 Permission permission,
336                 Role role,
337                 boolean enforceTenancy) throws Exception
338     {
339         PermissionRole permRole = new PermissionRole();
340         // Check to see if the tenant ID of the permission and the tenant ID of the role match
341         boolean tenantIdsMatch = role.getTenantId().equalsIgnoreCase(permission.getTenantId());
342         if (tenantIdsMatch == false && enforceTenancy == false) {
343                 tenantIdsMatch = true; // If we don't need to enforce tenancy then we'll just consider them matched.
344         }
345                         
346                 if (tenantIdsMatch == true) {
347                 permRole.setSubject(SubjectType.ROLE);
348                 //
349                 // Set of the permission value list of the permrole
350                 //
351                 List<PermissionValue> permValues = new ArrayList<PermissionValue>();
352                 PermissionValue permValue = new PermissionValue();
353                 permValue.setPermissionId(permission.getCsid());
354                 permValue.setResourceName(permission.getResourceName().toLowerCase());
355                 permValue.setActionGroup(permission.getActionGroup());
356                 permValues.add(permValue);
357                 permRole.setPermission(permValues);
358                 //
359                 // Set of the role value list of the permrole
360                 //
361                 List<RoleValue> roleValues = new ArrayList<RoleValue>();
362                 RoleValue rv = new RoleValue();
363             // This needs to use the qualified name, not the display name
364             rv.setRoleName(role.getRoleName());
365             rv.setRoleId(role.getCsid());
366             roleValues.add(rv);
367             permRole.setRole(roleValues);
368                 } else {
369                 String errMsg = "The tenant ID of the role: " + role.getTenantId()
370                                 + " did not match the tenant ID of the permission: " + permission.getTenantId();
371                 throw new Exception(errMsg);
372                 }
373         
374         return permRole;
375     }
376     
377     private static Hashtable<String, String> getTenantNamesFromConfig(TenantBindingConfigReaderImpl tenantBindingConfigReader) {
378
379         // Note that this only handles tenants not marked as "createDisabled"
380         Hashtable<String, TenantBindingType> tenantBindings =
381                         tenantBindingConfigReader.getTenantBindings();
382         Hashtable<String, String> tenantInfo = new Hashtable<String, String>();
383         for (TenantBindingType tenantBinding : tenantBindings.values()) {
384                 String tId = tenantBinding.getId();
385                 String tName = tenantBinding.getName();
386                 tenantInfo.put(tId, tName);
387                 if (logger.isDebugEnabled()) {
388                         logger.debug("getTenantNamesFromConfig found configured tenant id: "+tId+" name: "+tName);
389                 }
390         }
391         return tenantInfo;
392     }
393     
394     private static ArrayList<String> compileExistingTenants(Connection conn, Hashtable<String, String> tenantInfo)
395         throws SQLException, Exception {
396         Statement stmt = null;
397         ArrayList<String> existingTenants = new ArrayList<String>();
398         // First find or create the tenants
399         final String queryTenantSQL = "SELECT id,name FROM tenants";
400         try {
401                 stmt = conn.createStatement();
402                 ResultSet rs = stmt.executeQuery(queryTenantSQL);
403                 while (rs.next()) {
404                         String tId = rs.getString("id");
405                         String tName = rs.getString("name");
406                         if(tenantInfo.containsKey(tId)) {
407                                 existingTenants.add(tId);
408                                 if(!tenantInfo.get(tId).equalsIgnoreCase(tName)) {
409                                         logger.warn("Configured name for tenant: "
410                                                         +tId+" in repository: "+tName
411                                                         +" does not match config'd name: "+ tenantInfo.get(tId));
412                                 }
413                         }
414                 }
415                 rs.close();
416         } catch(Exception e) {
417                 throw e;
418         } finally {
419                 if(stmt!=null)
420                         stmt.close();
421         }
422
423         return existingTenants;
424     }
425     
426     private static void createMissingTenants(Connection conn, Hashtable<String, String> tenantInfo,
427                 ArrayList<String> existingTenants) throws SQLException, Exception {
428                 // Need to define and look for a createDisabled attribute in tenant config
429         final String insertTenantSQL = 
430                 "INSERT INTO tenants (id,name,authorities_initialized,disabled,created_at) VALUES (?,?,FALSE,FALSE,now())";
431         PreparedStatement pstmt = null;
432         try {
433                 pstmt = conn.prepareStatement(insertTenantSQL); // create a statement
434                 for(String tId : tenantInfo.keySet()) {
435                         if(existingTenants.contains(tId)) {
436                                 if (logger.isDebugEnabled()) {
437                                         logger.debug("createMissingTenants: tenant exists (skipping): "
438                                                         +tenantInfo.get(tId));
439                                 }
440                                 continue;
441                         }
442                         pstmt.setString(1, tId);                                        // set id param
443                         pstmt.setString(2, tenantInfo.get(tId));        // set name param
444                         if (logger.isDebugEnabled()) {
445                                 logger.debug("createMissingTenants adding entry for tenant: "+tId);
446                         }
447                         pstmt.executeUpdate();
448                 }
449                 pstmt.close();
450         } catch(Exception e) {
451                 throw e;
452         } finally {
453                 if(pstmt!=null)
454                         pstmt.close();
455         }
456     }
457     
458     private static ArrayList<String> findOrCreateDefaultUsers(Connection conn, Hashtable<String, String> tenantInfo) 
459                 throws SQLException, Exception {
460         // Second find or create the users
461         Statement stmt = null;
462         PreparedStatement pstmt = null;
463         ArrayList<String> usersInRepo = new ArrayList<String>();
464         try {
465                 stmt = conn.createStatement();
466                 ResultSet rs = stmt.executeQuery(QUERY_USERS_SQL);
467                 while (rs.next()) {
468                         String uName = rs.getString("username");
469                         usersInRepo.add(uName);
470                 }
471                 rs.close();
472                 pstmt = conn.prepareStatement(INSERT_USER_SQL); // create a statement
473                 for(String tName : tenantInfo.values()) {
474                         String adminAcctName = getDefaultAdminUserID(tName);
475                         if(!usersInRepo.contains(adminAcctName)) {
476                                 String secEncPasswd = SecurityUtils.createPasswordHash(
477                                                 adminAcctName, DEFAULT_ADMIN_PASSWORD);
478                                 pstmt.setString(1, adminAcctName);      // set username param
479                                 pstmt.setString(2, secEncPasswd);       // set passwd param
480                                 if (logger.isDebugEnabled()) {
481                                         logger.debug("createDefaultUsersAndAccounts adding user: "
482                                                         +adminAcctName+" for tenant: "+tName);
483                                 }
484                                 pstmt.executeUpdate();
485                         } else if (logger.isDebugEnabled()) {
486                                 logger.debug("createDefaultUsersAndAccounts: user: "+adminAcctName
487                                                 +" already exists - skipping.");
488                         }
489
490
491                         String readerAcctName =  getDefaultReaderUserID(tName);
492                         if(!usersInRepo.contains(readerAcctName)) {
493                                 String secEncPasswd = SecurityUtils.createPasswordHash(
494                                                 readerAcctName, DEFAULT_READER_PASSWORD);
495                                 pstmt.setString(1, readerAcctName);     // set username param
496                                 pstmt.setString(2, secEncPasswd);       // set passwd param
497                                 if (logger.isDebugEnabled()) {
498                                         logger.debug("createDefaultUsersAndAccounts adding user: "
499                                                         +readerAcctName+" for tenant: "+tName);
500                                 }
501                                 pstmt.executeUpdate();
502                         } else if (logger.isDebugEnabled()) {
503                                 logger.debug("createDefaultUsersAndAccounts: user: "+readerAcctName
504                                                 +" already exists - skipping.");
505                         }
506                 }
507                 pstmt.close();
508         } catch(Exception e) {
509                 throw e;
510         } finally {
511                 if(stmt!=null)
512                         stmt.close();
513                 if(pstmt!=null)
514                         pstmt.close();
515         }
516         return usersInRepo;
517     }
518     
519     private static void findOrCreateDefaultAccounts(Connection conn, Hashtable<String, String> tenantInfo,
520                 ArrayList<String> usersInRepo,
521                 Hashtable<String, String> tenantAdminAcctCSIDs, Hashtable<String, String> tenantReaderAcctCSIDs) 
522                         throws SQLException, Exception {
523         // Third, create the accounts. Assume that if the users were already there,
524         // then the accounts were as well
525         PreparedStatement pstmt = null;
526         try {
527                 pstmt = conn.prepareStatement(INSERT_ACCOUNT_SQL); // create a statement
528                 for(String tId : tenantInfo.keySet()) {
529                         String tName = tenantInfo.get(tId);
530                         String adminCSID = UUID.randomUUID().toString();
531                         tenantAdminAcctCSIDs.put(tId, adminCSID);
532                         String adminAcctName =  getDefaultAdminUserID(tName);
533                         if(!usersInRepo.contains(adminAcctName)) {
534                                 pstmt.setString(1, adminCSID);                  // set csid param
535                                 pstmt.setString(2, adminAcctName);      // set email param (bogus)
536                                 pstmt.setString(3, adminAcctName);      // set userid param
537                                 pstmt.setString(4, "Administrator");// set screen name param
538                                 if (logger.isDebugEnabled()) {
539                                         logger.debug("createDefaultAccounts adding account: "
540                                                         +adminAcctName+" for tenant: "+tName);
541                                 }
542                                 pstmt.executeUpdate();
543                         } else if (logger.isDebugEnabled()) {
544                                 logger.debug("createDefaultAccounts: user: "+adminAcctName
545                                                 +" already exists - skipping account generation.");
546                         }
547
548                         String readerCSID = UUID.randomUUID().toString();       
549                         tenantReaderAcctCSIDs.put(tId, readerCSID);
550                         String readerAcctName =  getDefaultReaderUserID(tName);
551                         if(!usersInRepo.contains(readerAcctName)) {
552                                 pstmt.setString(1, readerCSID);         // set csid param
553                                 pstmt.setString(2, readerAcctName);     // set email param (bogus)
554                                 pstmt.setString(3, readerAcctName);     // set userid param
555                                 pstmt.setString(4, "Reader");           // set screen name param
556                                 if (logger.isDebugEnabled()) {
557                                         logger.debug("createDefaultAccounts adding account: "
558                                                         +readerAcctName+" for tenant: "+tName);
559                                 }
560                                 pstmt.executeUpdate();
561                         } else if (logger.isDebugEnabled()) {
562                                 logger.debug("createDefaultAccounts: user: "+readerAcctName
563                                                 +" already exists - skipping account creation.");
564                         }
565                 }
566                 pstmt.close();
567         } catch(Exception e) {
568                 throw e;
569         } finally {
570                 if(pstmt!=null)
571                         pstmt.close();
572         }
573     }
574     
575     private static boolean findOrCreateTenantManagerUserAndAccount(Connection conn) 
576                         throws SQLException, Exception {
577         // Find or create the special tenant manager account.
578         // Later can make the user name for tenant manager be configurable, settable.
579         Statement stmt = null;
580         PreparedStatement pstmt = null;
581         boolean created = false;
582         try {
583                 boolean foundTMgrUser = false;
584                 stmt = conn.createStatement();
585                 ResultSet rs = stmt.executeQuery(QUERY_TENANT_MGR_USER_SQL);
586                 // Should only find one - only consider it
587                 if(rs.next()) {
588                         String uName = rs.getString("username");
589                         foundTMgrUser = uName.equals(TENANT_MANAGER_USER);
590                 }
591                 rs.close();
592                 if(!foundTMgrUser) {
593                         pstmt = conn.prepareStatement(INSERT_USER_SQL); // create a statement
594                         String secEncPasswd = SecurityUtils.createPasswordHash(
595                                         TENANT_MANAGER_USER, DEFAULT_TENANT_MANAGER_PASSWORD);
596                         pstmt.setString(1, TENANT_MANAGER_USER);        // set username param
597                         pstmt.setString(2, secEncPasswd);       // set passwd param
598                         if (logger.isDebugEnabled()) {
599                                 logger.debug("findOrCreateTenantManagerUserAndAccount adding tenant manager user: "
600                                                 +TENANT_MANAGER_USER);
601                         }
602                         pstmt.executeUpdate();
603                 pstmt.close();
604                 // Now create the account to match
605                         pstmt = conn.prepareStatement(INSERT_ACCOUNT_SQL); // create a statement
606                                 pstmt.setString(1, AuthN.TENANT_MANAGER_ACCT_ID);                // set csid param
607                                 pstmt.setString(2, DEFAULT_TENANT_MANAGER_EMAIL);       // set email param (bogus)
608                                 pstmt.setString(3, TENANT_MANAGER_USER);        // set userid param
609                                 pstmt.setString(4, TENANT_MANAGER_SCREEN_NAME);// set screen name param
610                                 if (logger.isDebugEnabled()) {
611                                         logger.debug("findOrCreateTenantManagerUserAndAccount adding tenant manager account: "
612                                                         +TENANT_MANAGER_USER);
613                                 }
614                                 pstmt.executeUpdate();
615                         pstmt.close();
616                         created = true;
617                 } else if (logger.isDebugEnabled()) {
618                         logger.debug("findOrCreateTenantManagerUserAndAccount: tenant manager: "+TENANT_MANAGER_USER
619                                         +" already exists.");
620                 }
621         } catch(Exception e) {
622                 throw e;
623         } finally {
624                 if(stmt!=null)
625                         stmt.close();
626                 if(pstmt!=null)
627                         pstmt.close();
628         }
629         return created;
630     }
631     
632     private static void bindDefaultAccountsToTenants(Connection conn, DatabaseProductType databaseProductType,
633                 Hashtable<String, String> tenantInfo, ArrayList<String> usersInRepo,
634                 Hashtable<String, String> tenantAdminAcctCSIDs, Hashtable<String, String> tenantReaderAcctCSIDs) 
635                         throws SQLException, Exception {
636         // Fourth, bind accounts to tenants. Assume that if the users were already there,
637         // then the accounts were bound to tenants correctly
638         PreparedStatement pstmt = null;
639         try {
640                 String insertAccountTenantSQL;
641                 if (databaseProductType == DatabaseProductType.MYSQL) {
642                         insertAccountTenantSQL =
643                                         "INSERT INTO accounts_tenants (TENANTS_ACCOUNTS_COMMON_CSID,tenant_id) "
644                                                         + " VALUES(?, ?)";
645                 } else if (databaseProductType == DatabaseProductType.POSTGRESQL) {
646                         insertAccountTenantSQL =
647                                         "INSERT INTO accounts_tenants (HJID, TENANTS_ACCOUNTS_COMMON_CSID,tenant_id) "
648                                                         + " VALUES(nextval('hibernate_sequence'), ?, ?)";
649                 } else {
650                         throw new Exception("Unrecognized database system.");
651                 }
652                 pstmt = conn.prepareStatement(insertAccountTenantSQL); // create a statement
653                 for(String tId : tenantInfo.keySet()) {
654                         String tName = tenantInfo.get(tId);
655                         if(!usersInRepo.contains(getDefaultAdminUserID(tName))) {
656                                 String adminAcct = tenantAdminAcctCSIDs.get(tId);
657                                 pstmt.setString(1, adminAcct);          // set acct CSID param
658                                 pstmt.setString(2, tId);                        // set tenant_id param
659                                 if (logger.isDebugEnabled()) {
660                                         logger.debug("createDefaultAccounts binding account id: "
661                                                         +adminAcct+" to tenant id: "+tId);
662                                 }
663                                 pstmt.executeUpdate();
664                         }
665                         if(!usersInRepo.contains(getDefaultReaderUserID(tName))) {
666                                 String readerAcct = tenantReaderAcctCSIDs.get(tId);
667                                 pstmt.setString(1, readerAcct);         // set acct CSID param
668                                 pstmt.setString(2, tId);                        // set tenant_id param
669                                 if (logger.isDebugEnabled()) {
670                                         logger.debug("createDefaultAccounts binding account id: "
671                                                         +readerAcct+" to tenant id: "+tId);
672                                 }
673                                 pstmt.executeUpdate();
674                         }
675                 }
676                 pstmt.close();
677         } catch(Exception e) {
678                 throw e;
679         } finally {
680                 if(pstmt!=null)
681                         pstmt.close();
682         }
683     }
684     
685     
686     private static String findOrCreateDefaultRoles(Connection conn, Hashtable<String, String> tenantInfo,
687                 Hashtable<String, String> tenantAdminRoleCSIDs, Hashtable<String, String> tenantReaderRoleCSIDs) 
688                         throws SQLException, Exception {
689         // Fifth, fetch and save the default roles
690                 String springAdminRoleCSID = null;
691         Statement stmt = null;
692         PreparedStatement pstmt = null;
693         try {
694                 final String querySpringRole = 
695                                 "SELECT csid from roles WHERE rolename='"+AuthN.ROLE_SPRING_ADMIN_NAME+"'";
696                 stmt = conn.createStatement();
697                 ResultSet rs = stmt.executeQuery(querySpringRole);
698                 if(rs.next()) {
699                         springAdminRoleCSID = rs.getString(1);
700                         if (logger.isDebugEnabled()) {
701                                 logger.debug("createDefaultAccounts found Spring Admin role: "
702                                                 +springAdminRoleCSID);
703                         }
704                 } else {
705                         final String insertSpringAdminRoleSQL =
706                                         "INSERT INTO roles (csid, rolename, displayName, rolegroup, created_at, tenant_id) "
707                                                         + "VALUES ('-1', 'ROLE_SPRING_ADMIN', 'SPRING_ADMIN', 'Spring Security Administrator', now(), '0')";
708                         stmt.executeUpdate(insertSpringAdminRoleSQL);
709                         springAdminRoleCSID = "-1";
710                         if (logger.isDebugEnabled()) {
711                                 logger.debug("createDefaultAccounts CREATED Spring Admin role: "
712                                                 +springAdminRoleCSID);
713                         }
714                 }
715                 rs.close();
716                 final String getRoleCSIDSql =
717                                 "SELECT csid from roles WHERE tenant_id=? and rolename=?";
718                 pstmt = conn.prepareStatement(getRoleCSIDSql); // create a statement
719                 rs = null;
720                 for(String tId : tenantInfo.keySet()) {
721                         pstmt.setString(1, tId);                                                // set tenant_id param
722                         pstmt.setString(2, getDefaultAdminRole(tId));   // set rolename param
723                         rs = pstmt.executeQuery();
724                         // extract data from the ResultSet
725                         if(!rs.next()) {
726                                 throw new RuntimeException("Cannot find role: "+getDefaultAdminRole(tId)
727                                                 +" for tenant id: "+tId+" in roles!");
728                         }
729                         String tenantAdminRoleCSID = rs.getString(1);
730                         if (logger.isDebugEnabled()) {
731                                 logger.debug("createDefaultAccounts found role: "
732                                                 +getDefaultAdminRole(tId)+"("+tenantAdminRoleCSID
733                                                 +") for tenant id: "+tId);
734                         }
735                         tenantAdminRoleCSIDs.put(tId, tenantAdminRoleCSID);
736                         pstmt.setString(1, tId);                                                // set tenant_id param
737                         pstmt.setString(2, getDefaultReaderRole(tId));  // set rolename param
738                         rs.close();
739                         rs = pstmt.executeQuery();
740                         // extract data from the ResultSet
741                         if(!rs.next()) {
742                                 throw new RuntimeException("Cannot find role: "+getDefaultReaderRole(tId)
743                                                 +" for tenant id: "+tId+" in roles!");
744                         }
745                         String tenantReaderRoleCSID = rs.getString(1);
746                         if (logger.isDebugEnabled()) {
747                                 logger.debug("createDefaultAccounts found role: "
748                                                 +getDefaultReaderRole(tId)+"("+tenantReaderRoleCSID
749                                                 +") for tenant id: "+tId);
750                         }
751                         tenantReaderRoleCSIDs.put(tId, tenantReaderRoleCSID);
752                         rs.close();
753                 }
754                 pstmt.close();
755         } catch(Exception e) {
756                 throw e;
757         } finally {
758                 if(stmt!=null)
759                         stmt.close();
760                 if(pstmt!=null)
761                         pstmt.close();
762         }
763         return springAdminRoleCSID;
764     }
765
766     private static String findTenantManagerRole(Connection conn ) 
767                         throws SQLException, RuntimeException, Exception {
768                 String tenantMgrRoleCSID = null;
769         PreparedStatement pstmt = null;
770         try {
771                 String rolename = getQualifiedRoleName(AuthN.ALL_TENANTS_MANAGER_TENANT_ID, 
772                                 AuthN.ROLE_ALL_TENANTS_MANAGER);                
773                 pstmt = conn.prepareStatement(GET_TENANT_MGR_ROLE_SQL); // create a statement
774                 ResultSet rs = null;
775                 pstmt.setString(1, rolename);   // set rolename param
776                 rs = pstmt.executeQuery();
777                 if(rs.next()) {
778                         tenantMgrRoleCSID = rs.getString(1);
779                         if (logger.isDebugEnabled()) {
780                                 logger.debug("findTenantManagerRole found Tenant Mgr role: "
781                                                 +tenantMgrRoleCSID);
782                         }
783                 }
784                 rs.close();
785         } catch(Exception e) {
786                 throw e;
787         } finally {
788                 if(pstmt!=null)
789                         pstmt.close();
790         }
791         if(tenantMgrRoleCSID==null)
792                 throw new RuntimeException("findTenantManagerRole: Cound not find tenant Manager Role!");
793         return tenantMgrRoleCSID;
794     }
795
796     private static void bindAccountsToRoles(Connection conn,  DatabaseProductType databaseProductType,
797                 Hashtable<String, String> tenantInfo, ArrayList<String> usersInRepo,
798                 String springAdminRoleCSID,
799                 Hashtable<String, String> tenantAdminRoleCSIDs, Hashtable<String, String> tenantReaderRoleCSIDs,
800                 Hashtable<String, String> tenantAdminAcctCSIDs, Hashtable<String, String> tenantReaderAcctCSIDs) 
801                         throws SQLException, Exception {
802         // Sixth, bind the accounts to roles. If the users already existed,
803         // we'll assume they were set up correctly.
804         PreparedStatement pstmt = null;
805         try {
806                 String insertAccountRoleSQL;
807                 if (databaseProductType == DatabaseProductType.MYSQL) {
808                         insertAccountRoleSQL = INSERT_ACCOUNT_ROLE_SQL_MYSQL;
809                 } else if (databaseProductType == DatabaseProductType.POSTGRESQL) {
810                         insertAccountRoleSQL = INSERT_ACCOUNT_ROLE_SQL_POSTGRES;
811                 } else {
812                         throw new Exception("Unrecognized database system.");
813                 }
814                 if (logger.isDebugEnabled()) {
815                         logger.debug("createDefaultAccounts binding accounts to roles with SQL:\n"
816                                         +insertAccountRoleSQL);
817                 }
818                 pstmt = conn.prepareStatement(insertAccountRoleSQL); // create a statement
819                 for(String tId : tenantInfo.keySet()) {
820                         String adminUserId =  getDefaultAdminUserID(tenantInfo.get(tId));
821                         if(!usersInRepo.contains(adminUserId)) {
822                                 String adminAcct = tenantAdminAcctCSIDs.get(tId);
823                                 String adminRoleId = tenantAdminRoleCSIDs.get(tId);
824                                 pstmt.setString(1, adminAcct);          // set acct CSID param
825                                 pstmt.setString(2, adminUserId);        // set user_id param
826                                 pstmt.setString(3, adminRoleId);        // set role_id param
827                                 pstmt.setString(4, getDefaultAdminRole(tId));   // set rolename param
828                                 if (logger.isDebugEnabled()) {
829                                         logger.debug("createDefaultAccounts binding account: "
830                                                         +adminUserId+" to Admin role("+adminRoleId
831                                                         +") for tenant id: "+tId);
832                                 }
833                                 pstmt.executeUpdate();
834                                 // Now add the Spring Admin Role to the admin accounts
835                                 pstmt.setString(3, springAdminRoleCSID);        // set role_id param
836                                 pstmt.setString(4, AuthN.ROLE_SPRING_ADMIN_NAME);               // set rolename param
837                                 if (logger.isDebugEnabled()) {
838                                         logger.debug("createDefaultAccounts binding account: "
839                                                         +adminUserId+" to Spring Admin role: "+springAdminRoleCSID);
840                                 }
841                                 pstmt.executeUpdate();
842                         }
843                         String readerUserId = getDefaultReaderUserID(tenantInfo.get(tId));
844                         if(!usersInRepo.contains(readerUserId)) {
845                                 String readerAcct = tenantReaderAcctCSIDs.get(tId);
846                                 String readerRoleId = tenantReaderRoleCSIDs.get(tId);
847                                 pstmt.setString(1, readerAcct);         // set acct CSID param
848                                 pstmt.setString(2, readerUserId);       // set user_id param
849                                 pstmt.setString(3, readerRoleId);       // set role_id param
850                                 pstmt.setString(4, getDefaultReaderRole(tId));  // set rolename param
851                                 if (logger.isDebugEnabled()) {
852                                         logger.debug("createDefaultAccounts binding account: "
853                                                         +readerUserId+" to Reader role("+readerRoleId
854                                                         +") for tenant id: "+tId);
855                                 }
856                                 pstmt.executeUpdate();
857                         }
858                 }
859                 pstmt.close();
860         } catch(Exception e) {
861                 throw e;
862         } finally {
863                 if(pstmt!=null)
864                         pstmt.close();
865         }
866     }
867     
868     private static void bindTenantManagerAccountRole(Connection conn,  DatabaseProductType databaseProductType,
869                 String tenantManagerUserID, String tenantManagerAccountID, String tenantManagerRoleID, String tenantManagerRoleName ) 
870                         throws SQLException, Exception {
871         PreparedStatement pstmt = null;
872         try {
873                 String insertAccountRoleSQL;
874                 if (databaseProductType == DatabaseProductType.MYSQL) {
875                         insertAccountRoleSQL = INSERT_ACCOUNT_ROLE_SQL_MYSQL;
876                 } else if (databaseProductType == DatabaseProductType.POSTGRESQL) {
877                         insertAccountRoleSQL = INSERT_ACCOUNT_ROLE_SQL_POSTGRES;
878                 } else {
879                         throw new Exception("Unrecognized database system.");
880                 }
881                 if (logger.isDebugEnabled()) {
882                         logger.debug("bindTenantManagerAccountRole binding account to role with SQL:\n"
883                                         +insertAccountRoleSQL);
884                 }
885                 pstmt = conn.prepareStatement(insertAccountRoleSQL); // create a statement
886                 pstmt.setString(1, tenantManagerAccountID);             // set acct CSID param
887                 pstmt.setString(2, tenantManagerUserID);        // set user_id param
888                 pstmt.setString(3, tenantManagerRoleID);        // set role_id param
889                 pstmt.setString(4, tenantManagerRoleName);      // set rolename param
890                 if (logger.isDebugEnabled()) {
891                         logger.debug("bindTenantManagerAccountRole binding user: "
892                                         +tenantManagerUserID+" to Admin role("+tenantManagerRoleName+")");
893                 }
894                 pstmt.executeUpdate();
895                 /* At this point, tenant manager should not need the Spring Admin Role
896                 pstmt.setString(3, springAdminRoleCSID);        // set role_id param
897                 pstmt.setString(4, SPRING_ADMIN_ROLE);          // set rolename param
898                 if (logger.isDebugEnabled()) {
899                         logger.debug("createDefaultAccounts binding account: "
900                                         +adminUserId+" to Spring Admin role: "+springAdminRoleCSID);
901                 }
902                 pstmt.executeUpdate();
903                 */
904                 pstmt.close();
905         } catch(Exception e) {
906                 throw e;
907         } finally {
908                 if(pstmt!=null)
909                         pstmt.close();
910         }
911     }
912     
913     public static void createDefaultAccounts(
914                 TenantBindingConfigReaderImpl tenantBindingConfigReader,
915                 DatabaseProductType databaseProductType,
916                 String cspaceDatabaseName) throws Exception {
917
918         logger.debug("ServiceMain.createDefaultAccounts starting...");
919         
920         Hashtable<String, String> tenantInfo = getTenantNamesFromConfig(tenantBindingConfigReader);
921         Connection conn = null;
922         // TODO - need to put in tests for existence first.
923         // We could just look for the accounts per tenant up front, and assume that
924         // the rest is there if the accounts are.
925         // Could add a sql script to remove these if need be - Spring only does roles, 
926         // and we're not touching that, so we could safely toss the 
927         // accounts, users, account-tenants, account-roles, and start over.
928         try {
929                 conn = getConnection(cspaceDatabaseName);
930                 ArrayList<String> existingTenants = compileExistingTenants(conn, tenantInfo);
931                 
932                 // Note that this only creates tenants not marked as "createDisabled"
933                 createMissingTenants(conn, tenantInfo, existingTenants);
934                 
935                 ArrayList<String> usersInRepo = findOrCreateDefaultUsers(conn, tenantInfo);
936                 
937                 Hashtable<String, String> tenantAdminAcctCSIDs = new Hashtable<String, String>();
938                 Hashtable<String, String> tenantReaderAcctCSIDs = new Hashtable<String, String>();
939                 findOrCreateDefaultAccounts(conn, tenantInfo, usersInRepo,
940                                 tenantAdminAcctCSIDs, tenantReaderAcctCSIDs);
941
942                 bindDefaultAccountsToTenants(conn, databaseProductType, tenantInfo, usersInRepo,
943                                 tenantAdminAcctCSIDs, tenantReaderAcctCSIDs);
944                 
945                 Hashtable<String, String> tenantAdminRoleCSIDs = new Hashtable<String, String>();
946                 Hashtable<String, String> tenantReaderRoleCSIDs = new Hashtable<String, String>();
947                 String springAdminRoleCSID = findOrCreateDefaultRoles(conn, tenantInfo,
948                                 tenantAdminRoleCSIDs, tenantReaderRoleCSIDs);
949                 
950                 bindAccountsToRoles(conn,  databaseProductType,
951                                 tenantInfo, usersInRepo, springAdminRoleCSID,
952                                 tenantAdminRoleCSIDs, tenantReaderRoleCSIDs,
953                                 tenantAdminAcctCSIDs, tenantReaderAcctCSIDs);
954                 
955                 boolean createdTenantMgrAccount = findOrCreateTenantManagerUserAndAccount(conn);
956                 if(createdTenantMgrAccount) {
957                         // If we created the account, we need to create the bindings. Otherwise, assume they
958                         // are all set (from previous initialization).
959                         String tenantManagerRoleCSID = findTenantManagerRole(conn);
960                         bindTenantManagerAccountRole(conn, databaseProductType, 
961                                         TENANT_MANAGER_USER, AuthN.TENANT_MANAGER_ACCT_ID, 
962                                         tenantManagerRoleCSID, AuthN.ROLE_ALL_TENANTS_MANAGER);
963                 }
964         } catch (Exception e) {
965                         logger.debug("Exception in createDefaultAccounts: " + e.getLocalizedMessage());
966                 throw e;
967                 } finally {
968                         try {
969                                 if (conn != null)
970                                         conn.close();
971                         } catch (SQLException sqle) {
972                                 if (logger.isDebugEnabled()) {
973                                         logger.debug("SQL Exception closing statement/connection: " + sqle.getLocalizedMessage());
974                                 }
975                         }
976                 }       
977     }
978     
979     private static String getDefaultAdminRole(String tenantId) {
980         return ROLE_PREFIX+tenantId+TENANT_ADMIN_ROLE_SUFFIX;
981     }
982     
983     private static String getDefaultReaderRole(String tenantId) {
984         return ROLE_PREFIX+tenantId+TENANT_READER_ROLE_SUFFIX;
985     }
986     
987     private static String getDefaultAdminUserID(String tenantName) {
988         return TENANT_ADMIN_ACCT_PREFIX+tenantName;
989     }
990     
991     private static String getDefaultReaderUserID(String tenantName) {
992         return TENANT_READER_ACCT_PREFIX+tenantName;
993     }
994     
995         static public PermissionAction createPermissionAction(Permission perm,
996                         ActionType actionType) {
997         PermissionAction pa = new PermissionAction();
998
999             CSpaceAction action = URIResourceImpl.getAction(actionType);
1000             URIResourceImpl uriRes = new URIResourceImpl(perm.getTenantId(),
1001                     perm.getResourceName(), action);
1002             pa.setName(actionType);
1003             pa.setObjectIdentity(uriRes.getHashedId().toString());
1004             pa.setObjectIdentityResource(uriRes.getId());
1005             
1006             return pa;
1007         }
1008
1009         static public PermissionAction update(Permission perm, PermissionAction permAction) {
1010         PermissionAction pa = new PermissionAction();
1011
1012             CSpaceAction action = URIResourceImpl.getAction(permAction.getName());
1013             URIResourceImpl uriRes = new URIResourceImpl(perm.getTenantId(),
1014                     perm.getResourceName(), action);
1015             pa.setObjectIdentity(uriRes.getHashedId().toString());
1016             pa.setObjectIdentityResource(uriRes.getId());
1017             
1018             return pa;
1019         }
1020         
1021         private static HashSet<String> getTransitionVerbList(TenantBindingType tenantBinding, ServiceBindingType serviceBinding) {
1022                 HashSet<String> result = new HashSet<String>();
1023                 
1024                 TransitionDefList transitionDefList = getTransitionDefList(tenantBinding, serviceBinding);
1025         for (TransitionDef transitionDef : transitionDefList.getTransitionDef()) {
1026                 String transitionVerb = transitionDef.getName();
1027                 String[] tokens = transitionVerb.split("_");  // Split the verb into words.  The workflow verbs are compound words combined with the '_' character.
1028                 result.add(tokens[0]); // We only care about the first word.
1029         }
1030
1031         return result;
1032         }
1033         
1034         private static TransitionDefList getTransitionDefList(TenantBindingType tenantBinding, ServiceBindingType serviceBinding) {
1035                 TransitionDefList result = null;
1036                 try {
1037                         String serviceObjectName = serviceBinding.getObject().getName();
1038                 DocumentHandler docHandler = ServiceConfigUtils.createDocumentHandlerInstance(
1039                                 tenantBinding, serviceBinding);
1040                 Lifecycle lifecycle = docHandler.getLifecycle(serviceObjectName);
1041                 if (lifecycle != null) {
1042                         result = lifecycle.getTransitionDefList();
1043                 }
1044                 } catch (Exception e) {
1045                         // Ignore this exception and return an empty non-null TransitionDefList
1046                 }
1047                 
1048                 if (result == null) {
1049                         if (serviceBinding.getType().equalsIgnoreCase(ServiceBindingUtils.SERVICE_TYPE_SECURITY) == false) {
1050                                 logger.debug("Could not retrieve a lifecycle transition definition list from: "
1051                                                 + serviceBinding.getName()
1052                                                 + " with tenant ID = "
1053                                                 + tenantBinding.getId());
1054                         }
1055                         // return an empty list                 
1056                         result = new TransitionDefList();
1057                 } else {
1058                         logger.debug("Successfully retrieved a lifecycle transition definition list from: "
1059                                         + serviceBinding.getName()
1060                                         + " with tenant ID = "
1061                                         + tenantBinding.getId());
1062                 }
1063                 
1064                 return result;
1065         }
1066         
1067     public static void createDefaultWorkflowPermissions(TenantBindingConfigReaderImpl tenantBindingConfigReader) throws Exception //FIXME: REM - 4/11/2012 - Rename to createWorkflowPermissions
1068     {
1069         AuthZ.get().login(); //login to Spring Security manager
1070         
1071         EntityManagerFactory emf = JpaStorageUtils.getEntityManagerFactory(JpaStorageUtils.CS_PERSISTENCE_UNIT);
1072         EntityManager em = null;
1073
1074         try {
1075             em = emf.createEntityManager();
1076
1077                 Hashtable<String, TenantBindingType> tenantBindings =
1078                         tenantBindingConfigReader.getTenantBindings();
1079                 for (String tenantId : tenantBindings.keySet()) {
1080                         logger.info(String.format("Creating/verifying workflow permissions for tenant ID=%s.", tenantId));
1081                         TenantBindingType tenantBinding = tenantBindings.get(tenantId);
1082                         Role adminRole = AuthorizationCommon.getRole(em, tenantBinding.getId(), ROLE_TENANT_ADMINISTRATOR);
1083                         Role readonlyRole = AuthorizationCommon.getRole(em, tenantBinding.getId(), ROLE_TENANT_READER);
1084                         
1085                         if (adminRole == null || readonlyRole == null) {
1086                                 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.");
1087                                 logger.error(msg);
1088                                 throw new RuntimeException("One or more of the required default CollectionSpace administrator roles is missing or was never created.");
1089                         }
1090                         
1091                         for (ServiceBindingType serviceBinding : tenantBinding.getServiceBindings()) {
1092                                 String prop = ServiceBindingUtils.getPropertyValue(serviceBinding, REFRESH_AUTZ_PROP);
1093                                 if (prop == null ? true : Boolean.parseBoolean(prop)) {
1094                                                 try {
1095                                                 em.getTransaction().begin();
1096                                                 TransitionDefList transitionDefList = getTransitionDefList(tenantBinding, serviceBinding);
1097                                                 HashSet<String> transitionVerbList = getTransitionVerbList(tenantBinding, serviceBinding);
1098                                                 for (String transitionVerb : transitionVerbList) {
1099                                                         //
1100                                                         // Create the permission for the admin role
1101                                                         Permission adminPerm = createWorkflowPermission(tenantBinding, serviceBinding, transitionVerb, ACTIONGROUP_CRUDL);
1102                                                         persist(em, adminPerm, adminRole, true, ACTIONGROUP_CRUDL);
1103                                                         //
1104                                                         // Create the permission for the read-only role
1105                                                         Permission readonlyPerm = createWorkflowPermission(tenantBinding, serviceBinding, transitionVerb, ACTIONGROUP_RL);                                                      
1106                                                         persist(em, readonlyPerm, readonlyRole, true, ACTIONGROUP_RL); // Persist/store the permission and permrole records and related Spring Security info
1107                                                 }
1108                                                 em.getTransaction().commit();
1109                                         } catch (IllegalStateException e) {
1110                                                 logger.debug(e.getLocalizedMessage(), e); //We end up here if there is no document handler for the service -this is ok for some of the services.
1111                                         }
1112                                 } else {
1113                                         logger.warn("AuthZ refresh service binding property is set to FALSE so default permissions will NOT be refreshed for: "
1114                                                         + serviceBinding.getName());
1115                                 }
1116                         }
1117                 }
1118             em.close();
1119         } catch (Exception e) {
1120             if (em != null && em.getTransaction().isActive()) {
1121                 em.getTransaction().rollback();
1122             }
1123             if (logger.isDebugEnabled()) {
1124                 logger.debug("Caught exception and rolling back permission creation: ", e);
1125             }
1126             throw e;
1127         } finally {
1128             if (em != null) {
1129                 JpaStorageUtils.releaseEntityManagerFactory(emf);
1130             }
1131         }
1132     }
1133     
1134     private static PermissionRoleRel findPermRoleRel(EntityManager em, String permissionId, String RoleId) {
1135         PermissionRoleRel result = null;
1136         
1137         try {
1138                 String whereClause = "where permissionId = :id and roleId = :roleId";
1139                 HashMap<String, Object> params = new HashMap<String, Object>();
1140                 params.put("id", permissionId);
1141                 params.put("roleId", RoleId);        
1142         
1143                 result = (PermissionRoleRel) JpaStorageUtils.getEntity(em,
1144                                 PermissionRoleRel.class.getCanonicalName(), whereClause, params);
1145         } catch (Exception e) {
1146                 //Do nothing. Will return null;
1147         }
1148                 
1149         return result;
1150     }
1151     
1152     /*
1153      * Persists the Permission, PermissionRoleRel, and Spring Security table entries all in one transaction
1154      */
1155     private static void persist(EntityManager em, Permission permission, Role role, boolean enforceTenancy, ActionGroup actionGroup) throws Exception {
1156                 AuthorizationStore authzStore = new AuthorizationStore();
1157                 // First persist the Permission record
1158                 authzStore.store(em, permission);
1159                 
1160                 // If the PermRoleRel doesn't already exists then relate the permission and the role in a new PermissionRole (the service payload)
1161                 // Create a PermissionRoleRel (the database relation table for the permission and role)
1162                 PermissionRoleRel permRoleRel = findPermRoleRel(em, permission.getCsid(), role.getCsid());
1163                 if (permRoleRel == null) {
1164                         PermissionRole permRole = createPermissionRole(em, permission, role, enforceTenancy);
1165                 List<PermissionRoleRel> permRoleRels = new ArrayList<PermissionRoleRel>();
1166                 PermissionRoleUtil.buildPermissionRoleRel(em, permRole, SubjectType.ROLE, permRoleRels, false /*not for delete*/);
1167                 for (PermissionRoleRel prr : permRoleRels) {
1168                     authzStore.store(em, prr);
1169                 }
1170                         Profiler profiler = new Profiler(AuthorizationCommon.class, 2);
1171                         profiler.start();
1172                         // Add a corresponding entry in the Spring Security Tables
1173                         addPermissionsForUri(permission, permRole);
1174                         profiler.stop();
1175                         logger.debug("Finished full perm generation for "
1176                                         + ":" + permission.getTenantId()
1177                                         + ":" + permission.getResourceName()
1178                                         + ":" + actionGroup.getName()
1179                                         + ":" + profiler.getCumulativeTime());
1180                 }
1181         
1182     }
1183         
1184         public static boolean hasTokenExpired(EmailConfig emailConfig, Token token) throws NoSuchAlgorithmException {
1185                 boolean result = false;
1186                 
1187                 int maxConfigSeconds = emailConfig.getPasswordResetConfig().getTokenExpirationSeconds().intValue();
1188                 int maxTokenSeconds = token.getExpireSeconds().intValue();
1189                 
1190                 long createdTime = token.getCreatedAtItem().getTime();          
1191                 long configExpirationTime = createdTime + maxConfigSeconds * 1000;              // the current tenant config for how long a token stays valid
1192                 long tokenDefinedExirationTime = createdTime + maxTokenSeconds * 1000;  // the tenant config for how long a token stays valid when the token was created.
1193                 
1194                 if (configExpirationTime != tokenDefinedExirationTime) {
1195                         String msg = String.format("The configured expiration time for the token = '%s' changed from when the token was created.",
1196                                         token.getId());
1197                         logger.warn(msg);
1198                 }
1199                 //
1200                 // Note: the current tenant bindings config for expiration takes precedence over the config used to create the token.
1201                 //
1202                 if (System.currentTimeMillis() >= configExpirationTime) {
1203                         result = true;
1204                 }
1205                 
1206                 return result;
1207         }
1208                 
1209         /*
1210          * Validate that the password reset configuration is correct.
1211          */
1212         private static String validatePasswordResetConfig(PasswordResetConfig passwordResetConfig) {
1213                 String result = null;
1214                 
1215                 if (passwordResetConfig != null) {
1216                         result = passwordResetConfig.getMessage();
1217                         if (result == null || result.length() == 0) {
1218                                 result = DEFAULT_PASSWORD_RESET_EMAIL_MESSAGE;
1219                                 logger.warn("Could not find a password reset message in the tenant's configuration.  Using the default one");
1220                         }
1221                         
1222                         if (result.contains("{{link}}") == false) {
1223                                 logger.warn("The tenant's password reset message does not contain a required '{{link}}' marker.");
1224                                 result = null;
1225                         }
1226                         
1227                         if (passwordResetConfig.getLoginpage() == null || passwordResetConfig.getLoginpage().trim().isEmpty()) {
1228                                 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'.");
1229                                 result = null;
1230                         }
1231                         
1232                     String subject = passwordResetConfig.getSubject();
1233                     if (subject == null || subject.trim().isEmpty()) {
1234                         passwordResetConfig.setSubject(DEFAULT_PASSWORD_RESET_EMAIL_SUBJECT);
1235                     }
1236
1237                 }
1238                 
1239                 return result;
1240         }
1241         
1242         /*
1243          * Generate a password reset message. Embeds an authorization token to reset a user's password.
1244          */
1245         public static String generatePasswordResetEmailMessage(EmailConfig emailConfig, AccountListItem accountListItem, Token token) throws Exception {
1246                 String result = null;
1247                 
1248                 result = validatePasswordResetConfig(emailConfig.getPasswordResetConfig());
1249                 if (result == null) {
1250                         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.",
1251                                         token.getTenantId(), accountListItem.getEmail());
1252                         throw new Exception(errMsg);
1253                 }
1254                 
1255                 String link = emailConfig.getBaseurl() + emailConfig.getPasswordResetConfig().getLoginpage() + "?token=" + token.getId();
1256                 result = result.replaceAll("\\{\\{link\\}\\}", link);
1257                 
1258                 if (result.contains("{{greeting}}")) {
1259                         String greeting = accountListItem.getScreenName();
1260                         result = result.replaceAll("\\{\\{greeting\\}\\}", greeting);
1261                         result = result.replaceAll("\\\\n", "\\\n");
1262                         result = result.replaceAll("\\\\r", "\\\r");
1263                 }                       
1264                 
1265                 return result;
1266         }
1267 }