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