From: remillet Date: Thu, 21 Dec 2017 23:04:15 +0000 (-0800) Subject: DRYD-220: Normalize AuthN/AuthZ objects to have UTC times in out going payloads. X-Git-Url: https://git.aero2k.de/?a=commitdiff_plain;h=2315b682de5d163f8c6bf90696aa52a354ce610d;p=tmp%2Fjakarta-migration.git DRYD-220: Normalize AuthN/AuthZ objects to have UTC times in out going payloads. --- diff --git a/services/JaxRsServiceProvider/src/main/java/org/collectionspace/services/aspect/HyperJaxb3TimezoneAspect.java b/services/JaxRsServiceProvider/src/main/java/org/collectionspace/services/aspect/HyperJaxb3TimezoneAspect.java deleted file mode 100644 index 34e2bf7e1..000000000 --- a/services/JaxRsServiceProvider/src/main/java/org/collectionspace/services/aspect/HyperJaxb3TimezoneAspect.java +++ /dev/null @@ -1,44 +0,0 @@ -/** - * An AOP (AspectJ) aspect to resolve the timezone related issue https://issues.collectionspace.org/browse/DRYD-182. - * - * See related config in files: src/main/resources/META-INF/aop.xml, src/main/webapp/WEB-INF/applicationContext-security.xml - * - */ -package org.collectionspace.services.aspect; - -import java.util.Date; -import javax.xml.datatype.XMLGregorianCalendar; - -import org.aspectj.lang.ProceedingJoinPoint; -import org.aspectj.lang.annotation.Aspect; -import org.aspectj.lang.annotation.Around; -import org.aspectj.lang.annotation.Pointcut; - -@Aspect -public class HyperJaxb3TimezoneAspect { - - @Around("methodsToBeProfiled()") - public Object profile(ProceedingJoinPoint pjp) throws Throwable { - try { - Date fromDate = (Date)pjp.getArgs()[0]; - XMLGregorianCalendar toDate = (XMLGregorianCalendar)pjp.getArgs()[1]; - - Object result = pjp.proceed(); - // - // Marshal the timezone info from the 'fromDate' Date instance into the XMLGregorianCalendar 'toDate' instance - // - toDate.setTimezone(fromDate.getTimezoneOffset()); - - return result; - } finally { - // No cleanup needed. - } - } - - /** - * Intercept all calls to the createCalendar() method of the XMLGregorianCalendarAsDateTime class. This is how HyperJaxb3 marshals datetime info from Hibernate/JPA into - * out AuthN/AuthZ class instances. - */ - @Pointcut("execution(* org.jvnet.hyperjaxb3.xml.bind.annotation.adapters.XMLGregorianCalendarAsDateTime.createCalendar(java.util.Date, javax.xml.datatype.XMLGregorianCalendar))") - public void methodsToBeProfiled() {} -} \ No newline at end of file diff --git a/services/JaxRsServiceProvider/src/main/java/org/collectionspace/services/aspect/JaxbXMLGregorianCalendarMarshal.java b/services/JaxRsServiceProvider/src/main/java/org/collectionspace/services/aspect/JaxbXMLGregorianCalendarMarshal.java new file mode 100644 index 000000000..21c334695 --- /dev/null +++ b/services/JaxRsServiceProvider/src/main/java/org/collectionspace/services/aspect/JaxbXMLGregorianCalendarMarshal.java @@ -0,0 +1,62 @@ +package org.collectionspace.services.aspect; + +import java.util.Date; + +import javax.xml.datatype.XMLGregorianCalendar; + +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; + +/** + * @author remillet + * + * This method intercepts the all calls to the the setCreatedAt() method of the AuthN/AuthZ JaxB classes. These classes + * are all derived from XML Schema by the HyperJaxB3 Maven build plugin, so we couldn't do this in classes themselved. + * + * This method sets the timezone of the incoming XMLGregorianCalendar instance to the current JVM timezone and then + * normalized the instance to UTC time. Doing this results in the correct marshaling of the instance into the convention + * used by the other CollectionSpace services. + * + */ + +@Aspect +public class JaxbXMLGregorianCalendarMarshal { + + @Around("setDateMethods()") + public Object profile(ProceedingJoinPoint pjp) throws Throwable { + Object result = null; + + try { + Object[] args = pjp.getArgs(); // gets us a read-only copy of the argument(s) + XMLGregorianCalendar toDate = (XMLGregorianCalendar)args[0]; // get the incoming date argument + if (toDate != null) { + toDate.setTimezone(new Date().getTimezoneOffset() * -1); // set the incoming date's timezone to the current JVM timezone + toDate = toDate.normalize(); // normalize to UTC time + args[0] = toDate; // setup the new arguments + result = pjp.proceed(args); // finish the call + } else { + result = pjp.proceed(); // finish the call + } + } finally { + // No cleanup needed. + } + + return result; + } + + /** + * Setup a pointcut for all CSpace classes with methods like setCreatedAt(javax.xml.datatype.XMLGregorianCalendar) and + * setUpdatedAt(javax.xml.datatype.XMLGregorianCalendar) + */ + @Pointcut("execution(void org.collectionspace.services..setCreatedAt(javax.xml.datatype.XMLGregorianCalendar))") + public void setCreatedAtCutPoint() {} + + @Pointcut("execution(void org.collectionspace.services..setUpdatedAt(javax.xml.datatype.XMLGregorianCalendar))") + public void setUpdateatedAtCutPoint() {} + + @Pointcut("setCreatedAtCutPoint() || setUpdateatedAtCutPoint()") + public void setDateMethods() {} + +} diff --git a/services/JaxRsServiceProvider/src/main/java/org/collectionspace/services/jaxrs/CSpaceResteasyBootstrap.java b/services/JaxRsServiceProvider/src/main/java/org/collectionspace/services/jaxrs/CSpaceResteasyBootstrap.java index 1f6e319d3..8147eefec 100644 --- a/services/JaxRsServiceProvider/src/main/java/org/collectionspace/services/jaxrs/CSpaceResteasyBootstrap.java +++ b/services/JaxRsServiceProvider/src/main/java/org/collectionspace/services/jaxrs/CSpaceResteasyBootstrap.java @@ -11,12 +11,14 @@ import org.collectionspace.services.account.Tenant; import org.collectionspace.services.account.TenantResource; import org.collectionspace.services.authorization.AuthZ; import org.collectionspace.services.client.AuthorityClient; + import org.collectionspace.services.common.CSWebApplicationException; import org.collectionspace.services.common.ResourceMap; import org.collectionspace.services.common.ServiceMain; import org.collectionspace.services.common.api.RefName; import org.collectionspace.services.common.config.TenantBindingConfigReaderImpl; import org.collectionspace.services.common.vocabulary.AuthorityResource; + import org.collectionspace.services.config.service.AuthorityInstanceType; import org.collectionspace.services.config.service.ServiceBindingType; import org.collectionspace.services.config.service.ServiceBindingType.AuthorityInstanceList; @@ -34,6 +36,7 @@ public class CSpaceResteasyBootstrap extends ResteasyBootstrap { java.util.logging.Logger logger = java.util.logging.Logger.getAnonymousLogger(); static final String RESET_AUTHORITIES_PROPERTY = "org.collectionspace.services.authorities.reset"; + private static final String QUICK_BOOT_PROPERTY = "org.collectionspace.services.quickboot"; @Override public void contextInitialized(ServletContextEvent event) { @@ -48,8 +51,11 @@ public class CSpaceResteasyBootstrap extends ResteasyBootstrap { Dispatcher disp = deployment.getDispatcher(); disp.getDefaultContextObjects().put(ResourceMap.class, app.getResourceMap()); - String resetAuthsString = System.getProperty(RESET_AUTHORITIES_PROPERTY, Boolean.FALSE.toString()); // Property can be set in the tomcat/bin/setenv.sh (or setenv.bat) file - initializeAuthorities(app.getResourceMap(), Boolean.valueOf(resetAuthsString)); + String quickBoot = System.getProperty(QUICK_BOOT_PROPERTY, Boolean.FALSE.toString()); // Property can be set in the tomcat/bin/setenv.sh (or setenv.bat) file + if (Boolean.valueOf(quickBoot) == false) { + String resetAuthsString = System.getProperty(RESET_AUTHORITIES_PROPERTY, Boolean.FALSE.toString()); // Property can be set in the tomcat/bin/setenv.sh (or setenv.bat) file + initializeAuthorities(app.getResourceMap(), Boolean.valueOf(resetAuthsString)); + } logger.log(Level.INFO, String.format("%tc [INFO] CollectionSpace Services' JAX-RS application started.", new Date())); } catch (Exception e) { diff --git a/services/JaxRsServiceProvider/src/main/resources/META-INF/aop.xml b/services/JaxRsServiceProvider/src/main/resources/META-INF/aop.xml index c1069b698..2ef53fb0c 100644 --- a/services/JaxRsServiceProvider/src/main/resources/META-INF/aop.xml +++ b/services/JaxRsServiceProvider/src/main/resources/META-INF/aop.xml @@ -3,12 +3,12 @@ - + - - + + \ No newline at end of file diff --git a/services/account/jaxb/src/main/resources/accounts_common.xsd b/services/account/jaxb/src/main/resources/accounts_common.xsd index e4c5daa5e..cdd208f3a 100644 --- a/services/account/jaxb/src/main/resources/accounts_common.xsd +++ b/services/account/jaxb/src/main/resources/accounts_common.xsd @@ -325,6 +325,15 @@ + + + + + + + + + diff --git a/services/account/pstore/src/main/resources/db/postgresql/account.sql b/services/account/pstore/src/main/resources/db/postgresql/account.sql index 7f1564e81..74f75df23 100644 --- a/services/account/pstore/src/main/resources/db/postgresql/account.sql +++ b/services/account/pstore/src/main/resources/db/postgresql/account.sql @@ -5,6 +5,6 @@ DROP TABLE IF EXISTS tenants CASCADE; DROP SEQUENCE IF EXISTS hibernate_sequence; create table accounts_common (csid varchar(128) not null, created_at timestamp not null, email varchar(255) not null, mobile varchar(255), person_ref_name varchar(255), phone varchar(255), screen_name varchar(128) not null, status varchar(15) not null, updated_at timestamp, userid varchar(128) not null, metadata_protection varchar(255), roles_protection varchar(255), primary key (csid)); create table accounts_tenants (HJID int8 not null, tenant_id varchar(128) not null, TENANTS_ACCOUNTS_COMMON_CSID varchar(128), primary key (HJID)); -create table tenants (id varchar(128) not null, created_at timestamp not null, name varchar(255) not null, authorities_initialized boolean not null, disabled boolean not null, updated_at timestamp, primary key (id)); +create table tenants (id varchar(128) not null, created_at timestamp not null, name varchar(255) not null, config_md5hash varchar(255), authorities_initialized boolean not null, disabled boolean not null, updated_at timestamp, primary key (id)); alter table accounts_tenants add constraint FKFDA649B05A9CEEB5 foreign key (TENANTS_ACCOUNTS_COMMON_CSID) references accounts_common; create sequence hibernate_sequence; diff --git a/services/authorization-mgt/import/pom.xml b/services/authorization-mgt/import/pom.xml index 77b43e9b6..d31186c2e 100644 --- a/services/authorization-mgt/import/pom.xml +++ b/services/authorization-mgt/import/pom.xml @@ -51,6 +51,12 @@ org.collectionspace.services org.collectionspace.services.account.client ${project.version} + + + commons-codec + commons-codec + + org.collectionspace.services @@ -58,6 +64,11 @@ ${project.version} + + commons-codec + commons-codec + 1.4 + org.testng testng @@ -145,6 +156,12 @@ ch.elca.el4j.modules module-xml_merge-common 3.1 + + + commons-codec + commons-codec + + org.collectionspace.services @@ -229,6 +246,10 @@ nuxeo-core-storage-sql org.nuxeo.ecm.core + + commons-codec + commons-codec + diff --git a/services/common-api/src/main/java/org/collectionspace/services/common/api/GregorianCalendarDateTimeUtils.java b/services/common-api/src/main/java/org/collectionspace/services/common/api/GregorianCalendarDateTimeUtils.java index 5ebca7b5d..8198d4300 100644 --- a/services/common-api/src/main/java/org/collectionspace/services/common/api/GregorianCalendarDateTimeUtils.java +++ b/services/common-api/src/main/java/org/collectionspace/services/common/api/GregorianCalendarDateTimeUtils.java @@ -22,6 +22,9 @@ import java.util.Date; import java.util.GregorianCalendar; import java.util.TimeZone; +import javax.xml.datatype.DatatypeConfigurationException; +import javax.xml.datatype.XMLGregorianCalendar; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -66,6 +69,10 @@ public class GregorianCalendarDateTimeUtils { public static GregorianCalendar currentDateAndTimeUTC() { return currentDateAndTime(DateUtils.UTCTimeZone()); } + + public static XMLGregorianCalendar currentXMLGregorianCalendarUTC() throws DatatypeConfigurationException { + return javax.xml.datatype.DatatypeFactory.newInstance().newXMLGregorianCalendar(currentDateAndTimeUTC()); + } /** * Returns a calendar date, representing the current date and time instance diff --git a/services/common/src/main/java/org/collectionspace/services/common/ServiceMain.java b/services/common/src/main/java/org/collectionspace/services/common/ServiceMain.java index ccb118ef9..263e01774 100644 --- a/services/common/src/main/java/org/collectionspace/services/common/ServiceMain.java +++ b/services/common/src/main/java/org/collectionspace/services/common/ServiceMain.java @@ -171,12 +171,12 @@ public class ServiceMain { // set our root directory setServerRootDir(); - // read in and set our Services config + // read in and set our Services config readAndSetServicesConfig(); - + // Set our AuthN's datasource to for the cspaceDataSource AuthN.setDataSource(JDBCTools.getDataSource(JDBCTools.CSPACE_DATASOURCE_NAME)); - + // In each tenant, set properties that don't already have values // to their default values. propagateConfiguredProperties(); @@ -219,39 +219,42 @@ public class ServiceMain { // // initializeEventListeners(); - + + // + // Mark if a tenant's bindings have changed since the last time we started, by comparing the MD5 hash of each tenant's bindings with that of + // the bindings the last time we started/launch. // - // Create all the default user accounts and permissions. Since some of our "cspace" database config files + String cspaceDatabaseName = getCspaceDatabaseName(); + Hashtable tenantBindings = tenantBindingConfigReader.getTenantBindings(); + for (String tenantId : tenantBindings.keySet()) { + TenantBindingType tenantBinding = tenantBindings.get(tenantId); + String persistedMD5Hash = AuthorizationCommon.getPersistedMD5Hash(tenantId, cspaceDatabaseName); + String currentMD5Hash = tenantBinding.getConfigMD5Hash(); + AuthorizationCommon.setTenantConfigMD5Hash(tenantId, currentMD5Hash); // store this for later. We'll persist this info with the tenant record. + tenantBinding.setConfigChangedSinceLastStart(hasConfigChanged(tenantBinding, persistedMD5Hash, currentMD5Hash)); + } + + // + // Create all the tenant records, default user accounts, roles, and permissions. Since some of our "cspace" database config files // for Spring need to be created at build time, the "cspace" database already will be suffixed with the // correct 'cspaceInstanceId' so we don't need to pass it to the JDBCTools methods. // try { - AuthorizationCommon.createDefaultWorkflowPermissions(tenantBindingConfigReader); - String cspaceDatabaseName = getCspaceDatabaseName(); DatabaseProductType databaseProductType = JDBCTools.getDatabaseProductType(JDBCTools.CSPACE_DATASOURCE_NAME, - cspaceDatabaseName); - AuthorizationCommon.createDefaultAccounts(tenantBindingConfigReader, databaseProductType, - cspaceDatabaseName); + cspaceDatabaseName); + + AuthorizationCommon.createTenants(tenantBindingConfigReader, databaseProductType, cspaceDatabaseName); + AuthorizationCommon.createDefaultWorkflowPermissions(tenantBindingConfigReader, databaseProductType, cspaceDatabaseName); + AuthorizationCommon.createDefaultAccounts(tenantBindingConfigReader, databaseProductType, cspaceDatabaseName); + AuthorizationCommon.persistTenantBindingsMD5Hash(tenantBindingConfigReader, databaseProductType, cspaceDatabaseName); } catch (Exception e) { - logger.error("Default accounts and permissions setup failed with exception(s): " + + logger.error("Default create/update of tenants, accounts, roles and permissions setup failed with exception(s): " + e.getLocalizedMessage(), e); throw e; } - // - // Ensure default vocabulary and authority instances and their corresponding terms exist. + // Log tenant status -shows all tenants' info and active status. // -// initializeVocabularies(); -// initializeAuthorities(); - - /* - * This might be useful for something, but the reader grants are better handled in the ReportPostInitHandler. - try { - handlePostNuxeoInitDBTasks(); - } catch(Throwable e) { - logger.error("handlePostNuxeoInitDBTasks failed with exception(s): " + e.getLocalizedMessage(), e); - } - */ showTenantStatus(); } @@ -398,6 +401,25 @@ public class ServiceMain { tenantBindingConfigReader = new TenantBindingConfigReaderImpl(getServerRootDir()); tenantBindingConfigReader.read(useAppGeneratedBindings); } + + /* + * Returns 'true' if the tenant bindings have change since we last started up the Services layer of if the 'forceUpdate' field in the bindings + * has been set to 'true' + * + */ + private static boolean hasConfigChanged(TenantBindingType tenantBinding, String persistedMD5Hash, + String currentMD5Hash) { + boolean result = false; + + if (persistedMD5Hash == null || tenantBinding.isForceUpdate() == true) { + result = true; + } else { + result = !persistedMD5Hash.equals(currentMD5Hash); // if the two hashes don't match, the tenant bindings have changed so we'll return 'true' + } + + return result; + } + private void propagateConfiguredProperties() { List repoPropListHolder = diff --git a/services/common/src/main/java/org/collectionspace/services/common/authorization_mgt/AuthorizationCommon.java b/services/common/src/main/java/org/collectionspace/services/common/authorization_mgt/AuthorizationCommon.java index 6e686f064..af328f7c1 100644 --- a/services/common/src/main/java/org/collectionspace/services/common/authorization_mgt/AuthorizationCommon.java +++ b/services/common/src/main/java/org/collectionspace/services/common/authorization_mgt/AuthorizationCommon.java @@ -1,6 +1,5 @@ package org.collectionspace.services.common.authorization_mgt; -import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.sql.Connection; import java.sql.PreparedStatement; @@ -13,6 +12,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Hashtable; import java.util.List; +import java.util.Map; import java.util.UUID; import javax.naming.NamingException; @@ -77,6 +77,11 @@ public class AuthorizationCommon { final private static String tokensalt = "74102328UserDetailsReset"; final private static int TIME_SCALAR = 100000; private static final String DEFAULT_PASSWORD_RESET_EMAIL_SUBJECT = "Password reset for CollectionSpace account"; + + // + // Keep track of the MD5 hash value for the tenant bindings + // + private static final Map tenantConfigMD5HashTable = new HashMap(); // // ActionGroup labels/constants @@ -149,6 +154,14 @@ public class AuthorizationCommon { public static final String IGNORE_TENANT_ID = null; // A null constant to indicate an empty/unused value for the tenant ID + public static String getTenantConfigMD5Hash(String tenantId) { + return tenantConfigMD5HashTable.get(tenantId); + } + + public static String setTenantConfigMD5Hash(String tenantId, String md5hash) { + return tenantConfigMD5HashTable.put(tenantId, md5hash); + } + public static Role getRole(String tenantId, String displayName) { Role role = null; @@ -427,39 +440,7 @@ public class AuthorizationCommon { return existingTenants; } - - private static void createMissingTenants(Connection conn, Hashtable tenantInfo, - ArrayList existingTenants) throws SQLException, Exception { - // Need to define and look for a createDisabled attribute in tenant config - final String insertTenantSQL = - "INSERT INTO tenants (id,name,authorities_initialized,disabled,created_at) VALUES (?,?,FALSE,FALSE,now())"; - PreparedStatement pstmt = null; - try { - pstmt = conn.prepareStatement(insertTenantSQL); // create a statement - for(String tId : tenantInfo.keySet()) { - if(existingTenants.contains(tId)) { - if (logger.isDebugEnabled()) { - logger.debug("createMissingTenants: tenant exists (skipping): " - +tenantInfo.get(tId)); - } - continue; - } - pstmt.setString(1, tId); // set id param - pstmt.setString(2, tenantInfo.get(tId)); // set name param - if (logger.isDebugEnabled()) { - logger.debug("createMissingTenants adding entry for tenant: "+tId); - } - pstmt.executeUpdate(); - } - pstmt.close(); - } catch(Exception e) { - throw e; - } finally { - if(pstmt!=null) - pstmt.close(); - } - } - + private static ArrayList findOrCreateDefaultUsers(Connection conn, Hashtable tenantInfo) throws SQLException, Exception { // Second find or create the users @@ -915,6 +896,37 @@ public class AuthorizationCommon { } } + /* + * Using the tenant bindings, ensure there are corresponding Tenant records (db columns). + */ + public static void createTenants( + TenantBindingConfigReaderImpl tenantBindingConfigReader, + DatabaseProductType databaseProductType, + String cspaceDatabaseName) throws Exception { + logger.debug("ServiceMain.createTenants starting..."); + Hashtable tenantInfo = getTenantNamesFromConfig(tenantBindingConfigReader); + Connection conn = null; + try { + conn = getConnection(cspaceDatabaseName); + ArrayList existingTenants = compileExistingTenants(conn, tenantInfo); + + // Note that this only creates tenants not marked as "createDisabled" + createMissingTenants(conn, tenantInfo, existingTenants); + } catch (Exception e) { + logger.debug("Exception in createTenants: " + e.getLocalizedMessage()); + throw e; + } finally { + try { + if (conn != null) + conn.close(); + } catch (SQLException sqle) { + if (logger.isDebugEnabled()) { + logger.debug("SQL Exception closing statement/connection: " + sqle.getLocalizedMessage()); + } + } + } + } + public static void createDefaultAccounts( TenantBindingConfigReaderImpl tenantBindingConfigReader, DatabaseProductType databaseProductType, @@ -932,10 +944,6 @@ public class AuthorizationCommon { // accounts, users, account-tenants, account-roles, and start over. try { conn = getConnection(cspaceDatabaseName); - ArrayList existingTenants = compileExistingTenants(conn, tenantInfo); - - // Note that this only creates tenants not marked as "createDisabled" - createMissingTenants(conn, tenantInfo, existingTenants); ArrayList usersInRepo = findOrCreateDefaultUsers(conn, tenantInfo); @@ -1069,7 +1077,16 @@ public class AuthorizationCommon { return result; } - public static void createDefaultWorkflowPermissions(TenantBindingConfigReaderImpl tenantBindingConfigReader) throws Exception //FIXME: REM - 4/11/2012 - Rename to createWorkflowPermissions + /** + * + * @param tenantBindingConfigReader + * @param databaseProductType + * @param cspaceDatabaseName + * @throws Exception + */ + public static void createDefaultWorkflowPermissions(TenantBindingConfigReaderImpl tenantBindingConfigReader, + DatabaseProductType databaseProductType, + String cspaceDatabaseName) throws Exception //FIXME: REM - 4/11/2012 - Rename to createWorkflowPermissions { java.util.logging.Logger logger = java.util.logging.Logger.getAnonymousLogger(); @@ -1081,11 +1098,14 @@ public class AuthorizationCommon { try { em = emf.createEntityManager(); - Hashtable tenantBindings = - tenantBindingConfigReader.getTenantBindings(); + Hashtable tenantBindings = tenantBindingConfigReader.getTenantBindings(); for (String tenantId : tenantBindings.keySet()) { logger.info(String.format("Creating/verifying workflow permissions for tenant ID=%s.", tenantId)); TenantBindingType tenantBinding = tenantBindings.get(tenantId); + if (tenantBinding.isConfigChangedSinceLastStart() == false) { + continue; // skip the rest of the loop and go to the next tenant + } + Role adminRole = AuthorizationCommon.getRole(em, tenantBinding.getId(), ROLE_TENANT_ADMINISTRATOR); Role readonlyRole = AuthorizationCommon.getRole(em, tenantBinding.getId(), ROLE_TENANT_READER); @@ -1136,7 +1156,74 @@ public class AuthorizationCommon { } } - private static PermissionRoleRel findPermRoleRel(EntityManager em, String permissionId, String RoleId) { + private static void createMissingTenants(Connection conn, Hashtable tenantInfo, + ArrayList existingTenants) throws SQLException, Exception { + // Need to define and look for a createDisabled attribute in tenant config + final String insertTenantSQL = + "INSERT INTO tenants (id,name,authorities_initialized,disabled,created_at) VALUES (?,?,FALSE,FALSE,now())"; + PreparedStatement pstmt = null; + try { + pstmt = conn.prepareStatement(insertTenantSQL); // create a statement + for(String tId : tenantInfo.keySet()) { + if(existingTenants.contains(tId)) { + if (logger.isDebugEnabled()) { + logger.debug("createMissingTenants: tenant exists (skipping): " + +tenantInfo.get(tId)); + } + continue; + } + pstmt.setString(1, tId); // set id param + pstmt.setString(2, tenantInfo.get(tId)); // set name param + if (logger.isDebugEnabled()) { + logger.debug("createMissingTenants adding entry for tenant: "+tId); + } + pstmt.executeUpdate(); + } + pstmt.close(); + } catch(Exception e) { + throw e; + } finally { + if(pstmt!=null) + pstmt.close(); + } + } + + public static String getPersistedMD5Hash(String tenantId, String cspaceDatabaseName) throws Exception { + String result = null; + + ArrayList existingTenants = new ArrayList(); + // First find or create the tenants + final String queryTenantSQL = String.format("SELECT id, name, config_md5hash FROM tenants WHERE id = '%s'", tenantId); + + Statement stmt = null; + Connection conn; + int rowCount = 0; + try { + conn = getConnection(cspaceDatabaseName); + stmt = conn.createStatement(); + ResultSet rs = stmt.executeQuery(queryTenantSQL); + while (rs.next()) { + if (rowCount > 0) { + 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'.", + tenantId, cspaceDatabaseName); + throw new Exception(errMsg); + } + String tId = rs.getString("id"); // for debugging only + String tName = rs.getString("name"); // for debugging only + result = rs.getString("config_md5hash"); + rowCount++; + } + rs.close(); + } catch(Exception e) { + throw e; + } finally { + if (stmt != null) stmt.close(); + } + + return result; + } + + private static PermissionRoleRel findPermRoleRel(EntityManager em, String permissionId, String RoleId) { PermissionRoleRel result = null; try { @@ -1270,4 +1357,30 @@ public class AuthorizationCommon { return result; } + + public static void persistTenantBindingsMD5Hash(TenantBindingConfigReaderImpl tenantBindingConfigReader, + DatabaseProductType databaseProductType, String cspaceDatabaseName) throws Exception { + // Need to define and look for a createDisabled attribute in tenant config + String updateTableSQL = "UPDATE tenants SET config_md5hash = ? WHERE id = ?"; + + Connection conn; + PreparedStatement pstmt = null; + try { + conn = getConnection(cspaceDatabaseName); + pstmt = conn.prepareStatement(updateTableSQL); // create a statement + for (String tId : AuthorizationCommon.tenantConfigMD5HashTable.keySet()) { + pstmt.setString(1, AuthorizationCommon.getTenantConfigMD5Hash(tId)); + pstmt.setString(2, tId); + if (logger.isDebugEnabled()) { + logger.debug("createMissingTenants adding entry for tenant: " + tId); + } + pstmt.executeUpdate(); + } + pstmt.close(); + } catch(Exception e) { + throw e; + } finally { + if (pstmt!=null) pstmt.close(); + } + } } diff --git a/services/common/src/main/java/org/collectionspace/services/common/config/ServiceConfigUtils.java b/services/common/src/main/java/org/collectionspace/services/common/config/ServiceConfigUtils.java index 978d2ba54..eb6d80565 100644 --- a/services/common/src/main/java/org/collectionspace/services/common/config/ServiceConfigUtils.java +++ b/services/common/src/main/java/org/collectionspace/services/common/config/ServiceConfigUtils.java @@ -47,6 +47,8 @@ import org.slf4j.LoggerFactory; public class ServiceConfigUtils { final static Logger logger = LoggerFactory.getLogger(ServiceConfigUtils.class); + + public static final String FORCE_BINDINGS_UPDATE = "-1"; public static DocHandlerParams.Params getDocHandlerParams(String tenantId, String serviceName) throws DocumentException { TenantBindingConfigReaderImpl tReader = diff --git a/services/common/src/main/java/org/collectionspace/services/common/config/TenantBindingConfigReaderImpl.java b/services/common/src/main/java/org/collectionspace/services/common/config/TenantBindingConfigReaderImpl.java index b55073769..3147500e2 100644 --- a/services/common/src/main/java/org/collectionspace/services/common/config/TenantBindingConfigReaderImpl.java +++ b/services/common/src/main/java/org/collectionspace/services/common/config/TenantBindingConfigReaderImpl.java @@ -27,11 +27,16 @@ import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; +import java.security.DigestInputStream; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Enumeration; import java.util.Hashtable; import java.util.List; +import org.apache.commons.codec.binary.Hex; +import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.io.FileUtils; import org.collectionspace.services.common.api.JEEServerDeployment; import org.collectionspace.services.common.api.Tools; @@ -159,7 +164,7 @@ public class TenantBindingConfigReaderImpl extends AbstractConfigReaderImpl + + + @@ -62,6 +69,7 @@ +