diff --git a/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/Messages.java b/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/Messages.java index d0469ba013..ad2f796214 100644 --- a/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/Messages.java +++ b/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/Messages.java @@ -242,6 +242,14 @@ public final class Messages { public static final String FETCH_TOKEN_AUDIT_LOG_CONFIG = "Access token fetch"; + public static final String READ_ENV_FROM_ENVIRONMENT = "Read {0} from environment for space with id: {1}"; + + public static final String SUBSCRIPTION_CREATE = "Create configuration-subscription in space with id: {0}"; + public static final String SUBSCRIPTION_UPDATE = "Update configuration-subscription in space with id: {0}"; + + public static final String ENTRY_CREATE = "Create configuration-entry in space with id: {0}"; + public static final String ENTRY_UPDATE = "Update configuration-entry in space with id: {0}"; + // Audit log configuration public static final String GET_CSRF_TOKEN_AUDIT_LOG_CONFIG = "CSRF token get "; @@ -265,6 +273,12 @@ public final class Messages { public static final String MTA_INFO_AUDIT_LOG_CONFIG = "MTA info"; public static final String MTA_LIST_AUDIT_LOG_CONFIG = "MTA list"; + public static final String ENVIRONMENT_VARIABLE_AUDIT_LOG_CONFIG = "Environment variable"; + + public static final String SUBSCRIPTION_CONFIG = "Configuration subscription"; + + public static final String ENTRY_CONFIG = "Configuration entry"; + public static final String API_INFO_AUDIT_LOG_CONFIG = "Api info"; public static final String IGNORING_NAMESPACE_PARAMETERS = "Ignoring parameter \"{0}\" , as the MTA is not deployed with namespace!"; public static final String NAMESPACE_PARSING_ERROR_MESSAGE = "Cannot parse \"{0}\" flag - expected a boolean format."; diff --git a/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/auditlogging/ApplicationConfigurationAuditLog.java b/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/auditlogging/ApplicationConfigurationAuditLog.java new file mode 100644 index 0000000000..181addc603 --- /dev/null +++ b/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/auditlogging/ApplicationConfigurationAuditLog.java @@ -0,0 +1,22 @@ +package org.cloudfoundry.multiapps.controller.core.auditlogging; + +import org.cloudfoundry.multiapps.controller.core.Messages; +import org.cloudfoundry.multiapps.controller.core.auditlogging.model.AuditLogConfiguration; + +import java.text.MessageFormat; + +public class ApplicationConfigurationAuditLog { + + private final AuditLoggingFacade auditLoggingFacade; + + public ApplicationConfigurationAuditLog(AuditLoggingFacade auditLoggingFacade) { + this.auditLoggingFacade = auditLoggingFacade; + } + + public void logEnvironmentVariableRead(String envVariableName, String spaceGuid) { + String performedAction = MessageFormat.format(Messages.READ_ENV_FROM_ENVIRONMENT, envVariableName, spaceGuid); + auditLoggingFacade.logDataAccessAuditLog(new AuditLogConfiguration(spaceGuid, + performedAction, + Messages.ENVIRONMENT_VARIABLE_AUDIT_LOG_CONFIG)); + } +} diff --git a/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/auditlogging/AuditLogBean.java b/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/auditlogging/AuditLogBean.java index 0bad67c1eb..bfc38ee240 100644 --- a/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/auditlogging/AuditLogBean.java +++ b/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/auditlogging/AuditLogBean.java @@ -1,10 +1,10 @@ package org.cloudfoundry.multiapps.controller.core.auditlogging; +import jakarta.inject.Inject; import org.cloudfoundry.multiapps.controller.core.auditlogging.impl.AuditLoggingFacadeSLImpl; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import jakarta.inject.Inject; import javax.sql.DataSource; @Configuration @@ -57,4 +57,22 @@ public OperationsApiServiceAuditLog buildOperationsApiServiceAuditLog(AuditLoggi public MtaConfigurationPurgerAuditLog buildMtaConfigurationPurgerAuditLog(AuditLoggingFacade auditLoggingFacade) { return new MtaConfigurationPurgerAuditLog(auditLoggingFacade); } + + @Bean + @Inject + public ApplicationConfigurationAuditLog buildApplicationConfigurationAuditLog(AuditLoggingFacade auditLoggingFacade) { + return new ApplicationConfigurationAuditLog(auditLoggingFacade); + } + + @Bean + @Inject + public ConfigurationSubscriptionServiceAuditLog buildAConfigurationSubscriptionServiceAuditLog(AuditLoggingFacade auditLoggingFacade) { + return new ConfigurationSubscriptionServiceAuditLog(auditLoggingFacade); + } + + @Bean + @Inject + public ConfigurationEntryServiceAuditLog buildAConfigurationEntryServiceAuditLog(AuditLoggingFacade auditLoggingFacade) { + return new ConfigurationEntryServiceAuditLog(auditLoggingFacade); + } } diff --git a/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/auditlogging/AuditLoggingFacade.java b/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/auditlogging/AuditLoggingFacade.java index 86c495d799..7220307dd6 100644 --- a/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/auditlogging/AuditLoggingFacade.java +++ b/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/auditlogging/AuditLoggingFacade.java @@ -1,11 +1,19 @@ package org.cloudfoundry.multiapps.controller.core.auditlogging; -import org.cloudfoundry.multiapps.controller.core.auditlogging.model.ConfigurationChangeActions; import org.cloudfoundry.multiapps.controller.core.auditlogging.model.AuditLogConfiguration; +import org.cloudfoundry.multiapps.controller.core.auditlogging.model.ConfigurationChangeActions; + +import java.util.List; public interface AuditLoggingFacade { void logSecurityIncident(AuditLogConfiguration configuration); + void logDataAccessAuditLog(AuditLogConfiguration configuration); + void logConfigurationChangeAuditLog(AuditLogConfiguration configuration, ConfigurationChangeActions configurationAction); + + void logConfigurationChangeAuditLog(AuditLogConfiguration configuration, + ConfigurationChangeActions configurationAction, + List attributes); } diff --git a/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/auditlogging/ConfigurationEntryServiceAuditLog.java b/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/auditlogging/ConfigurationEntryServiceAuditLog.java new file mode 100644 index 0000000000..0540ec2ed7 --- /dev/null +++ b/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/auditlogging/ConfigurationEntryServiceAuditLog.java @@ -0,0 +1,72 @@ +package org.cloudfoundry.multiapps.controller.core.auditlogging; + +import org.cloudfoundry.multiapps.common.util.JsonUtil; +import org.cloudfoundry.multiapps.controller.core.Messages; +import org.cloudfoundry.multiapps.controller.core.auditlogging.model.AuditLogConfiguration; +import org.cloudfoundry.multiapps.controller.core.auditlogging.model.ConfigurationChangeActions; +import org.cloudfoundry.multiapps.controller.persistence.model.ConfigurationEntry; + +import java.text.MessageFormat; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +public class ConfigurationEntryServiceAuditLog { + + private static final String PROVIDER_ID_PROPERTY_NAME = "providerId"; + private static final String PROVIDER_NID_PROPERTY_NAME = "providerNid"; + private static final String PROVIDER_VERSION_PROPERTY_NAME = "providerVersion"; + private static final String PROVIDER_NAMESPACE_PROPERTY_NAME = "providerNamespace"; + private static final String PROVIDER_TARGET_PROPERTY_NAME = "providerTarget"; + private static final String PROVIDER_CONTENT_PROPERTY_NAME = "providerContent"; + private static final String PROVIDER_CONTENT_ID_PROPERTY_NAME = "providerContentId"; + + private final AuditLoggingFacade auditLoggingFacade; + + public ConfigurationEntryServiceAuditLog(AuditLoggingFacade auditLoggingFacade) { + this.auditLoggingFacade = auditLoggingFacade; + } + + public void logAddConfigurationEntry(String username, String spaceGuid, ConfigurationEntry entry) { + String performedAction = MessageFormat.format(Messages.ENTRY_CREATE, spaceGuid); + auditLoggingFacade.logConfigurationChangeAuditLog(new AuditLogConfiguration(username, + spaceGuid, + performedAction, + Messages.ENTRY_CONFIG, + createAddEntryConfigurationIdentifier(entry)), + ConfigurationChangeActions.CONFIGURATION_CREATE); + } + + public void logUpdateConfigurationEntry(String username, String spaceGuid, ConfigurationEntry oldEntry, ConfigurationEntry newEntry) { + String performedAction = MessageFormat.format(Messages.ENTRY_UPDATE, spaceGuid); + + List attributes = List.of(oldEntry.getConfigurationType(), JsonUtil.toJson(oldEntry), + JsonUtil.toJson(newEntry)); + + auditLoggingFacade.logConfigurationChangeAuditLog(new AuditLogConfiguration(username, + spaceGuid, + performedAction, + Messages.ENTRY_CONFIG), + ConfigurationChangeActions.CONFIGURATION_UPDATE, attributes); + } + + private Map createAddEntryConfigurationIdentifier(ConfigurationEntry entry) { + Map identifiers = new HashMap<>(); + String providerTarget = entry.getTargetSpace() + .getOrganizationName() + + "/" + entry.getTargetSpace() + .getSpaceName(); + + identifiers.put(PROVIDER_ID_PROPERTY_NAME, entry.getProviderId()); + identifiers.put(PROVIDER_NID_PROPERTY_NAME, entry.getProviderNid()); + identifiers.put(PROVIDER_VERSION_PROPERTY_NAME, Objects.toString(entry.getProviderVersion())); + identifiers.put(PROVIDER_NAMESPACE_PROPERTY_NAME, entry.getProviderNamespace()); + identifiers.put(PROVIDER_TARGET_PROPERTY_NAME, providerTarget); + identifiers.put(PROVIDER_CONTENT_PROPERTY_NAME, entry.getContent()); + identifiers.put(PROVIDER_CONTENT_ID_PROPERTY_NAME, entry.getContentId()); + + return identifiers; + } + +} diff --git a/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/auditlogging/ConfigurationSubscriptionServiceAuditLog.java b/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/auditlogging/ConfigurationSubscriptionServiceAuditLog.java new file mode 100644 index 0000000000..77ab881f20 --- /dev/null +++ b/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/auditlogging/ConfigurationSubscriptionServiceAuditLog.java @@ -0,0 +1,62 @@ +package org.cloudfoundry.multiapps.controller.core.auditlogging; + +import org.cloudfoundry.multiapps.common.util.JsonUtil; +import org.cloudfoundry.multiapps.controller.core.Messages; +import org.cloudfoundry.multiapps.controller.core.auditlogging.model.AuditLogConfiguration; +import org.cloudfoundry.multiapps.controller.core.auditlogging.model.ConfigurationChangeActions; +import org.cloudfoundry.multiapps.controller.persistence.model.ConfigurationSubscription; + +import java.text.MessageFormat; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class ConfigurationSubscriptionServiceAuditLog { + + private static final String SUBSCRIPTION_ID_PROPERTY_NAME = "subscriptionId"; + private static final String APPLICATION_ID_PROPERTY_NAME = "applicationId"; + private static final String MTA_ID_PROPERTY_NAME = "mtaId"; + + private final AuditLoggingFacade auditLoggingFacade; + + public ConfigurationSubscriptionServiceAuditLog(AuditLoggingFacade auditLoggingFacade) { + this.auditLoggingFacade = auditLoggingFacade; + } + + public void logAddConfigurationSubscription(String username, String spaceGuid, ConfigurationSubscription subscription) { + String performedAction = MessageFormat.format(Messages.SUBSCRIPTION_CREATE, spaceGuid); + auditLoggingFacade.logConfigurationChangeAuditLog(new AuditLogConfiguration(username, + spaceGuid, + performedAction, + Messages.SUBSCRIPTION_CONFIG, + createAddSubscriptionConfigurationIdentifier( + subscription)), + ConfigurationChangeActions.CONFIGURATION_CREATE); + } + + public void logUpdateConfigurationSubscription(String username, String spaceGuid, ConfigurationSubscription oldSubscription, + ConfigurationSubscription updatedSubscription) { + + String performedAction = MessageFormat.format(Messages.SUBSCRIPTION_UPDATE, spaceGuid); + + List attributes = List.of(oldSubscription.getConfigurationType(), JsonUtil.toJson(oldSubscription), + JsonUtil.toJson(updatedSubscription)); + + auditLoggingFacade.logConfigurationChangeAuditLog(new AuditLogConfiguration(username, + spaceGuid, + performedAction, + Messages.SUBSCRIPTION_CONFIG), + ConfigurationChangeActions.CONFIGURATION_UPDATE, attributes); + } + + private Map createAddSubscriptionConfigurationIdentifier(ConfigurationSubscription subscription) { + Map identifiers = new HashMap<>(); + + identifiers.put(APPLICATION_ID_PROPERTY_NAME, subscription.getAppName()); + identifiers.put(MTA_ID_PROPERTY_NAME, subscription.getMtaId()); + identifiers.put(SUBSCRIPTION_ID_PROPERTY_NAME, String.valueOf(subscription.getId())); + + return identifiers; + } + +} diff --git a/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/auditlogging/impl/AuditLoggingFacadeSLImpl.java b/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/auditlogging/impl/AuditLoggingFacadeSLImpl.java index 82375a1260..a026ac5cca 100644 --- a/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/auditlogging/impl/AuditLoggingFacadeSLImpl.java +++ b/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/auditlogging/impl/AuditLoggingFacadeSLImpl.java @@ -10,6 +10,7 @@ import org.cloudfoundry.multiapps.controller.core.auditlogging.model.ConfigurationChangeActions; import javax.sql.DataSource; +import java.util.List; public class AuditLoggingFacadeSLImpl implements AuditLoggingFacade { @@ -35,6 +36,12 @@ public void logConfigurationChangeAuditLog(AuditLogConfiguration configuration, writeMessage(auditLogManager.getConfigLogger(), configuration.getPerformedAction(), Level.WARN); } + @Override + public void logConfigurationChangeAuditLog(AuditLogConfiguration configuration, ConfigurationChangeActions configurationAction, + List attributes) { + writeMessage(auditLogManager.getConfigLogger(), configuration.getPerformedAction(), Level.WARN); + } + private void writeMessage(Logger logger, String message, Level level) { Exception loggingException = null; synchronized (auditLogManager) { diff --git a/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/auditlogging/model/AuditLogConfiguration.java b/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/auditlogging/model/AuditLogConfiguration.java index 10718b41bf..e0d71e9da4 100644 --- a/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/auditlogging/model/AuditLogConfiguration.java +++ b/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/auditlogging/model/AuditLogConfiguration.java @@ -36,6 +36,14 @@ public AuditLogConfiguration(String userId, String spaceId, String performedActi this.parameters = parameters; } + public AuditLogConfiguration(String spaceId, String performedAction, String configuration) { + this.spaceId = spaceId; + this.userId = null; + this.performedAction = performedAction; + this.configuration = configuration; + this.parameters = Collections.emptyMap(); + } + public String getPerformedAction() { return performedAction; } diff --git a/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/liquibase/RecoveringLockService.java b/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/liquibase/RecoveringLockService.java index 1f6d3aac40..131eadf5ca 100644 --- a/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/liquibase/RecoveringLockService.java +++ b/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/liquibase/RecoveringLockService.java @@ -1,18 +1,17 @@ package org.cloudfoundry.multiapps.controller.core.liquibase; -import static java.text.MessageFormat.format; - -import java.util.Date; -import java.util.concurrent.TimeUnit; - +import liquibase.exception.LockException; +import liquibase.lockservice.DatabaseChangeLogLock; +import liquibase.lockservice.StandardLockService; import org.cloudfoundry.multiapps.controller.core.Messages; import org.cloudfoundry.multiapps.controller.core.util.ApplicationConfiguration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import liquibase.exception.LockException; -import liquibase.lockservice.DatabaseChangeLogLock; -import liquibase.lockservice.StandardLockService; +import java.util.Date; +import java.util.concurrent.TimeUnit; + +import static java.text.MessageFormat.format; public class RecoveringLockService extends StandardLockService { @@ -22,7 +21,7 @@ public class RecoveringLockService extends StandardLockService { private final long changeLogLockDuration; public RecoveringLockService() { - ApplicationConfiguration configuration = new ApplicationConfiguration(); + ApplicationConfiguration configuration = new ApplicationConfiguration(null); this.changeLogLockAttempts = configuration.getChangeLogLockAttempts(); this.changeLogLockDuration = configuration.getChangeLogLockDuration(); setChangeLogLockWaitTime(configuration.getChangeLogLockPollRate()); @@ -31,7 +30,7 @@ public RecoveringLockService() { @Override public int getPriority() { return super.getPriority() + 1; // Liquibase chooses which LockService to use based on its priority. This line makes sure that our - // custom lock service has a higher priority than the standard one (which it extends). + // custom lock service has a higher priority than the standard one (which it extends). } @Override diff --git a/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/util/ApplicationConfiguration.java b/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/util/ApplicationConfiguration.java index f69fb3b608..2404c4b88b 100644 --- a/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/util/ApplicationConfiguration.java +++ b/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/util/ApplicationConfiguration.java @@ -8,6 +8,7 @@ import org.cloudfoundry.multiapps.common.util.JsonUtil; import org.cloudfoundry.multiapps.common.util.MiscUtil; import org.cloudfoundry.multiapps.controller.core.Messages; +import org.cloudfoundry.multiapps.controller.core.auditlogging.ApplicationConfigurationAuditLog; import org.cloudfoundry.multiapps.controller.core.configuration.Environment; import org.cloudfoundry.multiapps.controller.core.health.model.HealthCheckConfiguration; import org.cloudfoundry.multiapps.controller.core.health.model.ImmutableHealthCheckConfiguration; @@ -37,6 +38,12 @@ public class ApplicationConfiguration { private static final Logger LOGGER = LoggerFactory.getLogger(ApplicationConfiguration.class); // Environment variables: + static final String CONTROLLER_URL = "CONTROLLER_URL"; + static final String SPACE_GUID = "SPACE_GUID"; + static final String ORGANISATION_NAME = "ORGANISATION_NAME"; + static final String DEPLOY_SERVICE_URL = "DEPLOY_SERVICE_URL"; + static final String APPLICATION_GUID = "APPLICATION_GUID"; + static final String CFG_PLATFORM = "PLATFORM"; // Mandatory static final String CFG_MAX_UPLOAD_SIZE = "MAX_UPLOAD_SIZE"; static final String CFG_MAX_MTA_DESCRIPTOR_SIZE = "MAX_MTA_DESCRIPTOR_SIZE"; @@ -218,13 +225,24 @@ public class ApplicationConfiguration { private Integer threadsForFileStorageUpload; private Boolean isHealthCheckEnabled; - public ApplicationConfiguration() { - this(new Environment()); + private final ApplicationConfigurationAuditLog applicationConfigurationAuditLog; + + public ApplicationConfiguration(ApplicationConfigurationAuditLog applicationConfigurationAuditLog) { + this(new Environment(), applicationConfigurationAuditLog); } @Inject - public ApplicationConfiguration(Environment environment) { + public ApplicationConfiguration(Environment environment, ApplicationConfigurationAuditLog applicationConfigurationAuditLog) { this.environment = environment; + this.applicationConfigurationAuditLog = applicationConfigurationAuditLog; + } + + private void auditLog(String envVariableName, String space) { + applicationConfigurationAuditLog.logEnvironmentVariableRead(envVariableName, space); + } + + public void auditLog(String envVariableName) { + applicationConfigurationAuditLog.logEnvironmentVariableRead(envVariableName, getSpaceGuid()); } public void load() { @@ -288,6 +306,7 @@ public URL getControllerUrl() { if (controllerUrl == null) { controllerUrl = getControllerUrlFromEnvironment(); } + auditLog(CONTROLLER_URL); return controllerUrl; } @@ -295,6 +314,7 @@ public Platform getPlatform() { if (platform == null) { platform = getPlatformFromEnvironment(); } + auditLog(CFG_PLATFORM); return platform; } @@ -302,6 +322,7 @@ public Long getMaxUploadSize() { if (maxUploadSize == null) { maxUploadSize = getMaxUploadSizeFromEnvironment(); } + auditLog(CFG_MAX_UPLOAD_SIZE); return maxUploadSize; } @@ -309,6 +330,7 @@ public Long getMaxMtaDescriptorSize() { if (maxMtaDescriptorSize == null) { maxMtaDescriptorSize = getMaxMtaDescriptorSizeFromEnvironment(); } + auditLog(CFG_MAX_MTA_DESCRIPTOR_SIZE); return maxMtaDescriptorSize; } @@ -316,6 +338,7 @@ public Long getMaxManifestSize() { if (maxManifestSize == null) { maxManifestSize = getMaxManifestSizeFromEnvironment(); } + auditLog(CFG_MAX_MANIFEST_SIZE); return maxManifestSize; } @@ -323,6 +346,7 @@ public Long getMaxResourceFileSize() { if (maxResourceFileSize == null) { maxResourceFileSize = getMaxResourceFileSizeFromEnvironment(); } + auditLog(CFG_MAX_RESOURCE_FILE_SIZE); return maxResourceFileSize; } @@ -330,6 +354,7 @@ public Long getMaxResolvedExternalContentSize() { if (maxResolvedExternalContentSize == null) { maxResolvedExternalContentSize = getMaxResolvedExternalContentSizeFromEnvironment(); } + auditLog(CFG_MAX_RESOLVED_EXTERNAL_CONTENT_SIZE); return maxResolvedExternalContentSize; } @@ -337,6 +362,7 @@ public String getCronExpressionForOldData() { if (cronExpressionForOldData == null) { cronExpressionForOldData = getCronExpressionForOldDataFromEnvironment(); } + auditLog(CFG_CRON_EXPRESSION_FOR_OLD_DATA); return cronExpressionForOldData; } @@ -344,6 +370,7 @@ public String getExecutionTimeForFinishedProcesses() { if (executionTimeForFinishedProcesses == null) { executionTimeForFinishedProcesses = getExecutionTimeForFinishedProcessesFromEnvironment(); } + auditLog(CFG_EXECUTION_TIME_FOR_FINISHED_PROCESSES); return executionTimeForFinishedProcesses; } @@ -351,6 +378,7 @@ public Long getMaxTtlForOldData() { if (maxTtlForOldData == null) { maxTtlForOldData = getMaxTtlForOldDataFromEnvironment(); } + auditLog(CFG_MAX_TTL_FOR_OLD_DATA); return maxTtlForOldData; } @@ -358,12 +386,14 @@ public Boolean shouldUseXSAuditLogging() { if (useXSAuditLogging == null) { useXSAuditLogging = shouldUseXSAuditLoggingFromEnvironment(); } + auditLog(CFG_USE_XS_AUDIT_LOGGING); return useXSAuditLogging; } public String getSpaceGuid() { if (spaceGuid == null) { spaceGuid = getSpaceGuidFromEnvironment(); + auditLog(SPACE_GUID, spaceGuid); } return spaceGuid; } @@ -372,6 +402,7 @@ public String getOrgName() { if (orgName == null) { orgName = getOrgNameFromEnvironment(); } + auditLog(ORGANISATION_NAME); return orgName; } @@ -379,6 +410,7 @@ public String getDeployServiceUrl() { if (deployServiceUrl == null) { deployServiceUrl = getDeployServiceUrlFromEnvironment(); } + auditLog(DEPLOY_SERVICE_URL); return deployServiceUrl; } @@ -386,6 +418,7 @@ public Boolean isBasicAuthEnabled() { if (basicAuthEnabled == null) { basicAuthEnabled = isBasicAuthEnabledThroughEnvironment(); } + auditLog(CFG_BASIC_AUTH_ENABLED); return basicAuthEnabled; } @@ -393,6 +426,7 @@ public Integer getSpringSchedulerTaskExecutorThreads() { if (springSchedulerTaskExecutorThreads == null) { springSchedulerTaskExecutorThreads = getSpringSchedulerTaskExecutorThreadsFromEnvironment(); } + auditLog(CFG_SPRING_SCHEDULER_TASK_EXECUTOR_THREADS); return springSchedulerTaskExecutorThreads; } @@ -400,6 +434,7 @@ public Integer getFilesAsyncUploadExecutorMaxThreads() { if (filesAsyncUploadExecutorThreads == null) { filesAsyncUploadExecutorThreads = getFilesAsyncUploadExecutorMaxThreadsFromEnvironment(); } + auditLog(CFG_FILES_ASYNC_UPLOAD_EXECUTOR_MAX_THREADS); return filesAsyncUploadExecutorThreads; } @@ -407,6 +442,7 @@ public String getGlobalAuditorUser() { if (globalAuditorUser == null) { globalAuditorUser = getGlobalAuditorUserFromEnvironment(); } + auditLog(CFG_GLOBAL_AUDITOR_USER); return globalAuditorUser; } @@ -414,6 +450,7 @@ public String getGlobalAuditorPassword() { if (globalAuditorPassword == null) { globalAuditorPassword = getGlobalAuditorPasswordFromEnvironment(); } + auditLog(CFG_GLOBAL_AUDITOR_PASSWORD); return globalAuditorPassword; } @@ -421,6 +458,7 @@ public String getGlobalAuditorOrigin() { if (globalAuditorOrigin == null) { globalAuditorOrigin = getGlobalAuditorOriginFromEnvironment(); } + auditLog(CFG_GLOBAL_AUDITOR_ORIGIN); return globalAuditorOrigin; } @@ -428,6 +466,7 @@ public int getDbConnectionThreads() { if (dbConnectionThreads == null) { dbConnectionThreads = getDbConnectionThreadsFromEnvironment(); } + auditLog(CFG_DB_CONNECTION_THREADS); return dbConnectionThreads; } @@ -435,6 +474,7 @@ public int getStepPollingIntervalInSeconds() { if (stepPollingIntervalInSeconds == null) { stepPollingIntervalInSeconds = getStepPollingIntervalFromEnvironment(); } + auditLog(CFG_STEP_POLLING_INTERVAL_IN_SECONDS); return stepPollingIntervalInSeconds; } @@ -442,6 +482,7 @@ public Boolean shouldSkipSslValidation() { if (skipSslValidation == null) { skipSslValidation = shouldSkipSslValidationBasedOnEnvironment(); } + auditLog(CFG_SKIP_SSL_VALIDATION); return skipSslValidation; } @@ -449,6 +490,7 @@ public String getVersion() { if (version == null) { version = getVersionFromEnvironment(); } + auditLog(CFG_VERSION); return version; } @@ -456,6 +498,9 @@ public Integer getChangeLogLockPollRate() { if (changeLogLockPollRate == null) { changeLogLockPollRate = getChangeLogLockPollRateFromEnvironment(); } + if (applicationConfigurationAuditLog != null) { + auditLog(CFG_CHANGE_LOG_LOCK_POLL_RATE); + } return changeLogLockPollRate; } @@ -463,6 +508,10 @@ public Integer getChangeLogLockDuration() { if (changeLogLockDuration == null) { changeLogLockDuration = getChangeLogLockDurationFromEnvironment(); } + if (applicationConfigurationAuditLog != null) { + auditLog(CFG_CHANGE_LOG_LOCK_DURATION); + } + return changeLogLockDuration; } @@ -470,6 +519,9 @@ public Integer getChangeLogLockAttempts() { if (changeLogLockAttempts == null) { changeLogLockAttempts = getChangeLogLockAttemptsFromEnvironment(); } + if (applicationConfigurationAuditLog != null) { + auditLog(CFG_CHANGE_LOG_LOCK_ATTEMPTS); + } return changeLogLockAttempts; } @@ -477,6 +529,7 @@ public String getGlobalConfigSpace() { if (globalConfigSpace == null) { globalConfigSpace = getGlobalConfigSpaceFromEnvironment(); } + auditLog(CFG_GLOBAL_CONFIG_SPACE); return globalConfigSpace; } @@ -491,6 +544,7 @@ public String getApplicationGuid() { if (applicationGuid == null) { applicationGuid = getApplicationGuidFromEnvironment(); } + auditLog(APPLICATION_GUID); return applicationGuid; } @@ -498,6 +552,7 @@ public Integer getApplicationInstanceIndex() { if (applicationInstanceIndex == null) { applicationInstanceIndex = getApplicationInstanceIndexFromEnvironment(); } + auditLog(CFG_CF_INSTANCE_INDEX); return applicationInstanceIndex; } @@ -505,6 +560,7 @@ public Integer getAuditLogClientCoreThreads() { if (auditLogClientCoreThreads == null) { auditLogClientCoreThreads = getAuditLogClientCoreThreadsFromEnvironment(); } + auditLog(CFG_AUDIT_LOG_CLIENT_CORE_THREADS); return auditLogClientCoreThreads; } @@ -512,6 +568,7 @@ public Integer getAuditLogClientMaxThreads() { if (auditLogClientMaxThreads == null) { auditLogClientMaxThreads = getAuditLogClientMaxThreadsFromEnvironment(); } + auditLog(CFG_AUDIT_LOG_CLIENT_MAX_THREADS); return auditLogClientMaxThreads; } @@ -519,6 +576,7 @@ public Integer getAuditLogClientQueueCapacity() { if (auditLogClientQueueCapacity == null) { auditLogClientQueueCapacity = getAuditLogClientQueueCapacityFromEnvironment(); } + auditLog(CFG_AUDIT_LOG_CLIENT_QUEUE_CAPACITY); return auditLogClientQueueCapacity; } @@ -526,6 +584,7 @@ public Integer getAuditLogClientKeepAlive() { if (auditLogClientKeepAlive == null) { auditLogClientKeepAlive = getAuditLogClientKeepAliveFromEnvironment(); } + auditLog(CFG_AUDIT_LOG_CLIENT_KEEP_ALIVE); return auditLogClientKeepAlive; } @@ -533,6 +592,7 @@ public Integer getFlowableJobExecutorCoreThreads() { if (flowableJobExecutorCoreThreads == null) { flowableJobExecutorCoreThreads = getFlowableJobExecutorCoreThreadsFromEnvironment(); } + auditLog(CFG_FLOWABLE_JOB_EXECUTOR_CORE_THREADS); return flowableJobExecutorCoreThreads; } @@ -540,6 +600,7 @@ public Integer getFlowableJobExecutorMaxThreads() { if (flowableJobExecutorMaxThreads == null) { flowableJobExecutorMaxThreads = getFlowableJobExecutorMaxThreadsFromEnvironment(); } + auditLog(CFG_FLOWABLE_JOB_EXECUTOR_MAX_THREADS); return flowableJobExecutorMaxThreads; } @@ -547,6 +608,7 @@ public Integer getFlowableJobExecutorQueueCapacity() { if (flowableJobExecutorQueueCapacity == null) { flowableJobExecutorQueueCapacity = getFlowableJobExecutorQueueCapacityFromEnvironment(); } + auditLog(CFG_FLOWABLE_JOB_EXECUTOR_QUEUE_CAPACITY); return flowableJobExecutorQueueCapacity; } @@ -554,6 +616,7 @@ public Integer getFssCacheUpdateTimeoutMinutes() { if (fssCacheUpdateTimeoutMinutes == null) { fssCacheUpdateTimeoutMinutes = getFssCacheUpdateTimeoutMinutesFromEnvironment(); } + auditLog(CFG_FSS_CACHE_UPDATE_TIMEOUT_MINUTES); return fssCacheUpdateTimeoutMinutes; } @@ -561,6 +624,7 @@ public Integer getThreadMonitorCacheUpdateInSeconds() { if (threadMonitorCacheUpdateInSeconds == null) { threadMonitorCacheUpdateInSeconds = getThreadMonitorCacheUpdateInSecondsFromEnvironment(); } + auditLog(CFG_THREAD_MONITOR_CACHE_UPDATE_IN_SECONDS); return threadMonitorCacheUpdateInSeconds; } @@ -568,6 +632,7 @@ public Integer getSpaceDeveloperCacheExpirationInSeconds() { if (spaceDeveloperCacheTimeInSeconds == null) { spaceDeveloperCacheTimeInSeconds = getSpaceDeveloperCacheTimeInSecondsFromEnvironment(); } + auditLog(CFG_SPACE_DEVELOPER_CACHE_TIME_IN_SECONDS); return spaceDeveloperCacheTimeInSeconds; } @@ -575,6 +640,7 @@ public Duration getControllerClientSslHandshakeTimeout() { if (controllerClientSslHandshakeTimeout == null) { controllerClientSslHandshakeTimeout = getControllerClientSslHandshakeTimeoutFromEnvironment(); } + auditLog(CFG_CONTROLLER_CLIENT_SSL_HANDSHAKE_TIMEOUT_IN_SECONDS); return controllerClientSslHandshakeTimeout; } @@ -582,6 +648,7 @@ public Duration getControllerClientConnectTimeout() { if (controllerClientConnectTimeout == null) { controllerClientConnectTimeout = getControllerClientConnectTimeoutFromEnvironment(); } + auditLog(CFG_CONTROLLER_CLIENT_CONNECT_TIMEOUT_IN_SECONDS); return controllerClientConnectTimeout; } @@ -589,6 +656,7 @@ public Integer getControllerClientConnectionPoolSize() { if (controllerClientConnectionPoolSize == null) { controllerClientConnectionPoolSize = getControllerClientConnectionPoolSizeFromEnvironment(); } + auditLog(CFG_CONTROLLER_CLIENT_CONNECTION_POOL_SIZE); return controllerClientConnectionPoolSize; } @@ -596,6 +664,7 @@ public Integer getControllerClientThreadPoolSize() { if (controllerClientThreadPoolSize == null) { controllerClientThreadPoolSize = getControllerClientThreadPoolSizeFromEnvironment(); } + auditLog(CFG_CONTROLLER_CLIENT_THREAD_POOL_SIZE); return controllerClientThreadPoolSize; } @@ -603,6 +672,7 @@ public Duration getControllerClientResponseTimeout() { if (controllerClientResponseTimeout == null) { controllerClientResponseTimeout = getControllerClientResponseTimeoutFromEnvironment(); } + auditLog(CFG_CONTROLLER_CLIENT_RESPONSE_TIMEOUT); return controllerClientResponseTimeout; } @@ -610,6 +680,7 @@ public String getCertificateCN() { if (certificateCN == null) { certificateCN = getCertificateCNFromEnvironment(); } + auditLog(CFG_CERTIFICATE_CN); return certificateCN; } @@ -617,6 +688,7 @@ public Integer getMicrometerStepInSeconds() { if (micrometerStepInSeconds == null) { micrometerStepInSeconds = getMicrometerStepInSecondsFromEnvironment(); } + auditLog(CFG_MICROMETER_STEP_IN_SECONDS); return micrometerStepInSeconds; } @@ -624,6 +696,7 @@ public Integer getDbTransactionTimeoutInSeconds() { if (dbTransactionTimeoutInSeconds == null) { dbTransactionTimeoutInSeconds = getDbTransactionTimeoutInSecondsFromEnvironment(); } + auditLog(CFG_DB_TRANSACTION_TIMEOUT_IN_SECONDS); return dbTransactionTimeoutInSeconds; } @@ -631,6 +704,7 @@ public Integer getSnakeyamlMaxAliasesForCollections() { if (snakeyamlMaxAliasesForCollections == null) { snakeyamlMaxAliasesForCollections = getSnakeyamlMaxAliasesForCollectionsFromEnvironment(); } + auditLog(CFG_SNAKEYAML_MAX_ALIASES_FOR_COLLECTIONS); return snakeyamlMaxAliasesForCollections; } @@ -638,6 +712,7 @@ public Integer getServiceHandlingMaxParallelThreads() { if (serviceHandlingMaxParallelThreads == null) { serviceHandlingMaxParallelThreads = getServiceHandlingMaxParallelThreadsFromEnvironment(); } + auditLog(CFG_SERVICE_HANDLING_MAX_PARALLEL_THREADS); return serviceHandlingMaxParallelThreads; } @@ -645,6 +720,7 @@ public Integer getAbortedOperationsTtlInSeconds() { if (abortedOperationsTtlInSeconds == null) { abortedOperationsTtlInSeconds = getAbortedOperationsTtlInSecondsFromEnvironment(); } + auditLog(CFG_ABORTED_OPERATIONS_TTL_IN_MINUTES); return abortedOperationsTtlInSeconds; } @@ -652,6 +728,7 @@ public boolean isOnStartFilesWithoutContentCleanerEnabled() { if (isOnStartFilesWithoutContentCleanerEnabledThroughEnvironment == null) { isOnStartFilesWithoutContentCleanerEnabledThroughEnvironment = isOnStartFilesWithoutContentCleanerEnabledThroughEnvironment(); } + auditLog(CFG_ENABLE_ON_START_FILES_WITHOUT_CONTENT_CLEANER); return isOnStartFilesWithoutContentCleanerEnabledThroughEnvironment; } @@ -659,6 +736,7 @@ public int getThreadsForFileUploadToController() { if (threadsForFileUploadToController == null) { threadsForFileUploadToController = getThreadsForFileUploadToControllerFromEnvironment(); } + auditLog(CFG_THREADS_FOR_FILE_UPLOAD_TO_CONTROLLER); return threadsForFileUploadToController; } @@ -666,6 +744,7 @@ public int getThreadsForFileStorageUpload() { if (threadsForFileStorageUpload == null) { threadsForFileStorageUpload = getThreadsForFileStorageUploadFromEnvironment(); } + auditLog(CFG_THREADS_FOR_FILE_STORAGE_UPLOAD); return threadsForFileStorageUpload; } @@ -673,6 +752,7 @@ public boolean isHealthCheckEnabled() { if (isHealthCheckEnabled == null) { isHealthCheckEnabled = isHealthCheckEnabledFromEnvironment(); } + auditLog(CFG_IS_HEALTH_CHECK_ENABLED); return isHealthCheckEnabled; } @@ -1135,5 +1215,4 @@ private String getCertificateCNFromEnvironment() { LOGGER.info(format(Messages.CERTIFICATE_CN, value)); return value; } - } \ No newline at end of file diff --git a/multiapps-controller-core/src/test/java/org/cloudfoundry/multiapps/controller/core/util/ApplicationConfigurationTest.java b/multiapps-controller-core/src/test/java/org/cloudfoundry/multiapps/controller/core/util/ApplicationConfigurationTest.java index a8ce36cd55..699e624c91 100644 --- a/multiapps-controller-core/src/test/java/org/cloudfoundry/multiapps/controller/core/util/ApplicationConfigurationTest.java +++ b/multiapps-controller-core/src/test/java/org/cloudfoundry/multiapps/controller/core/util/ApplicationConfigurationTest.java @@ -1,11 +1,5 @@ package org.cloudfoundry.multiapps.controller.core.util; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.Mockito.when; - import java.net.URL; import java.text.MessageFormat; import java.time.Duration; @@ -16,6 +10,7 @@ import org.cloudfoundry.multiapps.common.test.TestUtil; import org.cloudfoundry.multiapps.common.util.JsonUtil; import org.cloudfoundry.multiapps.controller.core.Messages; +import org.cloudfoundry.multiapps.controller.core.auditlogging.ApplicationConfigurationAuditLog; import org.cloudfoundry.multiapps.controller.core.auditlogging.AuditLoggingFacade; import org.cloudfoundry.multiapps.controller.core.configuration.Environment; import org.cloudfoundry.multiapps.controller.core.health.model.HealthCheckConfiguration; @@ -30,6 +25,12 @@ import org.mockito.Mockito; import org.mockito.MockitoAnnotations; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.when; + class ApplicationConfigurationTest { private static final String VCAP_APPLICATION_WITHOUT_SPACE = "vcap-application-without-space.json"; @@ -45,6 +46,9 @@ class ApplicationConfigurationTest { @InjectMocks private ApplicationConfiguration configuration; + @Mock + private ApplicationConfigurationAuditLog applicationConfigurationAuditLog; + @BeforeEach void setUp() throws Exception { MockitoAnnotations.openMocks(this) @@ -66,7 +70,7 @@ void testGetControllerUrl() throws Exception { @Test void testGetControllerUrlWithInvalidValue() { - String invalidUrl = "blabla"; + String invalidUrl = "blablabla"; Map vcapApplication = Map.of("cf_api", invalidUrl); Exception e = assertThrows(IllegalArgumentException.class, () -> getControllerUrlWithVcapApplication(vcapApplication)); assertEquals(MessageFormat.format(Messages.INVALID_CONTROLLER_URL, invalidUrl), e.getMessage()); @@ -75,7 +79,7 @@ void testGetControllerUrlWithInvalidValue() { private URL getControllerUrlWithVcapApplication(Map vcapApplication) { String vcapApplicationJson = JsonUtil.toJson(vcapApplication); when(environment.getString(ApplicationConfiguration.CFG_VCAP_APPLICATION)).thenReturn(vcapApplicationJson); - ApplicationConfiguration testedConfiguration = new ApplicationConfiguration(environment); + ApplicationConfiguration testedConfiguration = new ApplicationConfiguration(environment, applicationConfigurationAuditLog); return testedConfiguration.getControllerUrl(); } diff --git a/multiapps-controller-persistence/src/main/java/org/cloudfoundry/multiapps/controller/persistence/services/ConfigurationEntryService.java b/multiapps-controller-persistence/src/main/java/org/cloudfoundry/multiapps/controller/persistence/services/ConfigurationEntryService.java index 02c6efb1cb..afc7533be4 100644 --- a/multiapps-controller-persistence/src/main/java/org/cloudfoundry/multiapps/controller/persistence/services/ConfigurationEntryService.java +++ b/multiapps-controller-persistence/src/main/java/org/cloudfoundry/multiapps/controller/persistence/services/ConfigurationEntryService.java @@ -3,10 +3,10 @@ import java.util.List; import java.util.Objects; +import com.fasterxml.jackson.core.type.TypeReference; import jakarta.inject.Inject; import jakarta.inject.Named; import jakarta.persistence.EntityManagerFactory; - import org.apache.commons.lang3.ObjectUtils; import org.cloudfoundry.multiapps.common.ConflictException; import org.cloudfoundry.multiapps.common.NotFoundException; @@ -20,8 +20,6 @@ import org.cloudfoundry.multiapps.controller.persistence.query.impl.ConfigurationEntryQueryImpl; import org.cloudfoundry.multiapps.mta.model.Version; -import com.fasterxml.jackson.core.type.TypeReference; - @Named public class ConfigurationEntryService extends PersistenceService { diff --git a/multiapps-controller-persistence/src/main/java/org/cloudfoundry/multiapps/controller/persistence/services/ConfigurationSubscriptionService.java b/multiapps-controller-persistence/src/main/java/org/cloudfoundry/multiapps/controller/persistence/services/ConfigurationSubscriptionService.java index fd34cc1130..fcbc490ef9 100644 --- a/multiapps-controller-persistence/src/main/java/org/cloudfoundry/multiapps/controller/persistence/services/ConfigurationSubscriptionService.java +++ b/multiapps-controller-persistence/src/main/java/org/cloudfoundry/multiapps/controller/persistence/services/ConfigurationSubscriptionService.java @@ -1,13 +1,8 @@ package org.cloudfoundry.multiapps.controller.persistence.services; -import static java.text.MessageFormat.format; - -import java.util.Map; - import jakarta.inject.Inject; import jakarta.inject.Named; import jakarta.persistence.EntityManagerFactory; - import org.apache.commons.lang3.ObjectUtils; import org.cloudfoundry.multiapps.common.ConflictException; import org.cloudfoundry.multiapps.common.NotFoundException; @@ -22,6 +17,10 @@ import org.cloudfoundry.multiapps.controller.persistence.query.ConfigurationSubscriptionQuery; import org.cloudfoundry.multiapps.controller.persistence.query.impl.ConfigurationSubscriptionQueryImpl; +import java.util.Map; + +import static java.text.MessageFormat.format; + @Named public class ConfigurationSubscriptionService extends PersistenceService { diff --git a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/CreateSubscriptionsStep.java b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/CreateSubscriptionsStep.java index 09994ba95c..65abf85b9c 100644 --- a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/CreateSubscriptionsStep.java +++ b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/CreateSubscriptionsStep.java @@ -1,12 +1,9 @@ package org.cloudfoundry.multiapps.controller.process.steps; -import java.text.MessageFormat; -import java.util.List; - import jakarta.inject.Inject; import jakarta.inject.Named; import jakarta.persistence.NoResultException; - +import org.cloudfoundry.multiapps.controller.core.auditlogging.ConfigurationSubscriptionServiceAuditLog; import org.cloudfoundry.multiapps.controller.persistence.model.ConfigurationSubscription; import org.cloudfoundry.multiapps.controller.persistence.model.ConfigurationSubscription.ResourceDto; import org.cloudfoundry.multiapps.controller.persistence.services.ConfigurationSubscriptionService; @@ -15,6 +12,9 @@ import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.context.annotation.Scope; +import java.text.MessageFormat; +import java.util.List; + @Named("createSubscriptionsStep") @Scope(BeanDefinition.SCOPE_PROTOTYPE) public class CreateSubscriptionsStep extends SyncFlowableStep { @@ -22,6 +22,9 @@ public class CreateSubscriptionsStep extends SyncFlowableStep { @Inject private ConfigurationSubscriptionService configurationSubscriptionService; + @Inject + private ConfigurationSubscriptionServiceAuditLog configurationSubscriptionServiceAuditLog; + @Override protected StepPhase executeStep(ProcessContext context) { getStepLogger().debug(Messages.CREATING_SUBSCRIPTIONS); @@ -29,7 +32,7 @@ protected StepPhase executeStep(ProcessContext context) { List subscriptions = context.getVariable(Variables.SUBSCRIPTIONS_TO_CREATE); for (ConfigurationSubscription subscription : subscriptions) { - createSubscription(subscription); + createSubscription(context, subscription); getStepLogger().debug(Messages.CREATED_SUBSCRIPTION, subscription.getId()); } @@ -42,14 +45,19 @@ protected String getStepErrorMessage(ProcessContext context) { return Messages.ERROR_CREATING_SUBSCRIPTIONS; } - protected void createSubscription(ConfigurationSubscription subscription) { + protected void createSubscription(ProcessContext context, ConfigurationSubscription subscription) { infoSubscriptionCreation(subscription); ConfigurationSubscription existingSubscription = detectSubscription(subscription); if (existingSubscription != null) { configurationSubscriptionService.update(existingSubscription, subscription); + configurationSubscriptionServiceAuditLog.logUpdateConfigurationSubscription(context.getVariable(Variables.USER), + context.getVariable(Variables.SPACE_GUID), + existingSubscription, subscription); return; } configurationSubscriptionService.add(subscription); + configurationSubscriptionServiceAuditLog.logAddConfigurationSubscription(context.getVariable(Variables.USER), + context.getVariable(Variables.SPACE_GUID), subscription); } private void infoSubscriptionCreation(ConfigurationSubscription subscription) { diff --git a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/PublishConfigurationEntriesStep.java b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/PublishConfigurationEntriesStep.java index d0cd8fecc1..21a8756696 100644 --- a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/PublishConfigurationEntriesStep.java +++ b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/PublishConfigurationEntriesStep.java @@ -1,17 +1,11 @@ package org.cloudfoundry.multiapps.controller.process.steps; -import java.text.MessageFormat; -import java.util.Collections; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; - import jakarta.inject.Inject; import jakarta.inject.Named; - import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.ObjectUtils; import org.cloudfoundry.multiapps.controller.client.lib.domain.CloudApplicationExtended; +import org.cloudfoundry.multiapps.controller.core.auditlogging.ConfigurationEntryServiceAuditLog; import org.cloudfoundry.multiapps.controller.core.model.DynamicResolvableParameter; import org.cloudfoundry.multiapps.controller.core.security.serialization.SecureSerialization; import org.cloudfoundry.multiapps.controller.persistence.model.ConfigurationEntry; @@ -22,6 +16,12 @@ import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.context.annotation.Scope; +import java.text.MessageFormat; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + @Named("publishProvidedDependenciesStep") @Scope(BeanDefinition.SCOPE_PROTOTYPE) public class PublishConfigurationEntriesStep extends SyncFlowableStep { @@ -29,6 +29,9 @@ public class PublishConfigurationEntriesStep extends SyncFlowableStep { @Inject private ConfigurationEntryService configurationEntryService; + @Inject + private ConfigurationEntryServiceAuditLog configurationEntryServiceAuditLog; + @Inject private ConfigurationEntryDynamicParameterResolver dynamicParameterResolver; @@ -46,10 +49,11 @@ protected StepPhase executeStep(ProcessContext context) { return StepPhase.DONE; } Set dynamicResolvableParameters = context.getVariable(Variables.DYNAMIC_RESOLVABLE_PARAMETERS); - List resolvedEntriesToPublish = dynamicParameterResolver.resolveDynamicParametersOfConfigurationEntries(entriesToPublish, - dynamicResolvableParameters); + List resolvedEntriesToPublish = dynamicParameterResolver.resolveDynamicParametersOfConfigurationEntries( + entriesToPublish, + dynamicResolvableParameters); - List publishedEntries = publish(resolvedEntriesToPublish); + List publishedEntries = publish(context, resolvedEntriesToPublish); getStepLogger().debug(Messages.PUBLISHED_ENTRIES, SecureSerialization.toJson(publishedEntries)); context.setVariable(Variables.PUBLISHED_ENTRIES, publishedEntries); @@ -63,18 +67,22 @@ protected String getStepErrorMessage(ProcessContext context) { return Messages.ERROR_PUBLISHING_PUBLIC_PROVIDED_DEPENDENCIES; } - private List publish(List entriesToPublish) { + private List publish(ProcessContext context, List entriesToPublish) { return entriesToPublish.stream() - .map(this::publishConfigurationEntry) + .map(entry -> publishConfigurationEntry(context, entry)) .collect(Collectors.toList()); } - private ConfigurationEntry publishConfigurationEntry(ConfigurationEntry entry) { + private ConfigurationEntry publishConfigurationEntry(ProcessContext context, ConfigurationEntry entry) { infoConfigurationPublishment(entry); ConfigurationEntry currentEntry = getExistingEntry(entry); if (currentEntry == null) { + configurationEntryServiceAuditLog.logAddConfigurationEntry(context.getVariable(Variables.USER), + context.getVariable(Variables.SPACE_GUID), entry); return configurationEntryService.add(entry); } else { + configurationEntryServiceAuditLog.logUpdateConfigurationEntry(context.getVariable(Variables.USER), + context.getVariable(Variables.SPACE_GUID), currentEntry, entry); return configurationEntryService.update(currentEntry, entry); } } diff --git a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/CreateSubscriptionsStepTest.java b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/CreateSubscriptionsStepTest.java index 9851c808e5..457772137c 100644 --- a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/CreateSubscriptionsStepTest.java +++ b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/CreateSubscriptionsStepTest.java @@ -1,17 +1,8 @@ package org.cloudfoundry.multiapps.controller.process.steps; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.when; - -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Stream; - import org.cloudfoundry.multiapps.common.test.TestUtil; import org.cloudfoundry.multiapps.common.util.JsonUtil; +import org.cloudfoundry.multiapps.controller.core.auditlogging.ConfigurationSubscriptionServiceAuditLog; import org.cloudfoundry.multiapps.controller.core.test.MockBuilder; import org.cloudfoundry.multiapps.controller.persistence.model.ConfigurationSubscription; import org.cloudfoundry.multiapps.controller.persistence.model.ConfigurationSubscription.ResourceDto; @@ -26,11 +17,21 @@ import org.mockito.Mock; import org.mockito.Mockito; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.when; + class CreateSubscriptionsStepTest extends SyncFlowableStepTest { public static Stream testExecute() { return Stream.of( -// @formatter:off + // @formatter:off // (0) Arguments.of("create-subscriptions-step-input-00.json", null), // (1) @@ -46,6 +47,9 @@ public static Stream testExecute() { @Mock(answer = Answers.RETURNS_SELF) private ConfigurationSubscriptionQuery configurationSubscriptionQuery; + @Mock + private ConfigurationSubscriptionServiceAuditLog configurationSubscriptionServiceAuditLog; + @ParameterizedTest @MethodSource void testExecute(String inputLocation, String expectedExceptionMessage) { @@ -87,10 +91,14 @@ private void prepareSubscriptionService(StepInput input) { if (resourceDto == null) { continue; } - ConfigurationSubscriptionQuery queryMock = new MockBuilder<>(configurationSubscriptionQuery).on(query -> query.appName(subscription.getAppName())) - .on(query -> query.spaceId(subscription.getSpaceId())) - .on(query -> query.resourceName(resourceDto.getName())) - .on(query -> query.mtaId(subscription.getMtaId())) + ConfigurationSubscriptionQuery queryMock = new MockBuilder<>(configurationSubscriptionQuery).on( + query -> query.appName(subscription.getAppName())) + .on(query -> query.spaceId( + subscription.getSpaceId())) + .on(query -> query.resourceName( + resourceDto.getName())) + .on(query -> query.mtaId( + subscription.getMtaId())) .build(); doReturn(setId(subscription)).when(queryMock) .singleResult(); diff --git a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/PublishConfigurationEntriesStepTest.java b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/PublishConfigurationEntriesStepTest.java index b36d67f658..32f6a11fd7 100644 --- a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/PublishConfigurationEntriesStepTest.java +++ b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/PublishConfigurationEntriesStepTest.java @@ -1,20 +1,13 @@ package org.cloudfoundry.multiapps.controller.process.steps; -import static org.junit.jupiter.api.Assertions.fail; -import static org.mockito.AdditionalAnswers.returnsFirstArg; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.when; - -import java.util.Collections; -import java.util.List; -import java.util.Objects; -import java.util.stream.Stream; - +import com.fasterxml.jackson.core.type.TypeReference; +import com.sap.cloudfoundry.client.facade.domain.CloudMetadata; import org.apache.commons.collections4.CollectionUtils; import org.cloudfoundry.multiapps.common.test.TestUtil; import org.cloudfoundry.multiapps.common.util.JsonUtil; import org.cloudfoundry.multiapps.controller.client.lib.domain.CloudApplicationExtended; import org.cloudfoundry.multiapps.controller.client.lib.domain.ImmutableCloudApplicationExtended; +import org.cloudfoundry.multiapps.controller.core.auditlogging.ConfigurationEntryServiceAuditLog; import org.cloudfoundry.multiapps.controller.core.test.MockBuilder; import org.cloudfoundry.multiapps.controller.persistence.model.ConfigurationEntry; import org.cloudfoundry.multiapps.controller.persistence.query.ConfigurationEntryQuery; @@ -30,8 +23,15 @@ import org.mockito.Mock; import org.mockito.Mockito; -import com.fasterxml.jackson.core.type.TypeReference; -import com.sap.cloudfoundry.client.facade.domain.CloudMetadata; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.fail; +import static org.mockito.AdditionalAnswers.returnsFirstArg; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.when; class PublishConfigurationEntriesStepTest extends SyncFlowableStepTest { @@ -52,9 +52,12 @@ private static class StepInput { @Mock private ConfigurationEntryDynamicParameterResolver dynamicParameterResolver; + @Mock + private ConfigurationEntryServiceAuditLog configurationEntryServiceAuditLog; + public static Stream test() { return Stream.of( -// @formatter:off + // @formatter:off Arguments.of("publish-configuration-entries-step-input-1.json"), Arguments.of("publish-configuration-entries-step-input-2.json"), Arguments.of("publish-configuration-entries-step-input-3.json"), @@ -95,19 +98,24 @@ public void initializeParameters(StepInput input) { public void prepareConfigurationEntryService() { when(configurationEntryService.createQuery()).thenReturn(configurationEntryQuery); for (ConfigurationEntry entry : existingConfigurationEntries) { - ConfigurationEntryQuery entryQueryMock = new MockBuilder<>(configurationEntryQuery).on(query -> query.providerNid(entry.getProviderNid())) - .on(query -> query.providerId(entry.getProviderId())) - .on(query -> query.version(entry.getProviderVersion() - .toString())) - .on(query -> query.target(Mockito.eq(entry.getTargetSpace()))) + ConfigurationEntryQuery entryQueryMock = new MockBuilder<>(configurationEntryQuery).on( + query -> query.providerNid(entry.getProviderNid())) + .on(query -> query.providerId( + entry.getProviderId())) + .on(query -> query.version( + entry.getProviderVersion() + .toString())) + .on(query -> query.target( + Mockito.eq(entry.getTargetSpace()))) .build(); doReturn(List.of(entry)).when(entryQueryMock) - .list(); + .list(); } } private void prapareDynamicParameterResolver(StepInput input) { - when(dynamicParameterResolver.resolveDynamicParametersOfConfigurationEntries(Mockito.anyList(), Mockito.anySet())).then(returnsFirstArg()); + when(dynamicParameterResolver.resolveDynamicParametersOfConfigurationEntries(Mockito.anyList(), Mockito.anySet())).then( + returnsFirstArg()); } private void prepareContext(StepInput input) { diff --git a/multiapps-controller-web/src/main/java/org/cloudfoundry/multiapps/controller/web/configuration/AsyncProcessLoggerConfiguration.java b/multiapps-controller-web/src/main/java/org/cloudfoundry/multiapps/controller/web/configuration/AsyncProcessLoggerConfiguration.java index 153c312f83..ea8963e9a9 100644 --- a/multiapps-controller-web/src/main/java/org/cloudfoundry/multiapps/controller/web/configuration/AsyncProcessLoggerConfiguration.java +++ b/multiapps-controller-web/src/main/java/org/cloudfoundry/multiapps/controller/web/configuration/AsyncProcessLoggerConfiguration.java @@ -1,18 +1,20 @@ package org.cloudfoundry.multiapps.controller.web.configuration; -import java.util.concurrent.Executor; - +import jakarta.inject.Inject; import org.cloudfoundry.multiapps.controller.core.util.ApplicationConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import java.util.concurrent.Executor; + @Configuration @EnableAsync public class AsyncProcessLoggerConfiguration { - - final ApplicationConfiguration configuration = new ApplicationConfiguration(); + + @Inject + private ApplicationConfiguration configuration; @Bean("asyncExecutor") public Executor getAsyncExecutor() { diff --git a/multiapps-controller-web/src/test/java/org/cloudfoundry/multiapps/controller/web/monitoring/FssMonitorTest.java b/multiapps-controller-web/src/test/java/org/cloudfoundry/multiapps/controller/web/monitoring/FssMonitorTest.java index 772ead4824..592d749b40 100644 --- a/multiapps-controller-web/src/test/java/org/cloudfoundry/multiapps/controller/web/monitoring/FssMonitorTest.java +++ b/multiapps-controller-web/src/test/java/org/cloudfoundry/multiapps/controller/web/monitoring/FssMonitorTest.java @@ -1,15 +1,7 @@ package org.cloudfoundry.multiapps.controller.web.monitoring; -import static org.junit.jupiter.api.Assertions.assertEquals; - -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.time.LocalDateTime; -import java.util.stream.Stream; - import org.apache.commons.io.FileUtils; +import org.cloudfoundry.multiapps.controller.core.auditlogging.ApplicationConfigurationAuditLog; import org.cloudfoundry.multiapps.controller.core.util.ApplicationConfiguration; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; @@ -17,6 +9,17 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.time.LocalDateTime; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertEquals; class FssMonitorTest { @@ -24,6 +27,9 @@ class FssMonitorTest { private FssMonitor fssMonitor; + @Mock + private ApplicationConfigurationAuditLog applicationConfigurationAuditLog; + @BeforeAll static void setUpBeforeAll() throws IOException { tempDir = Files.createTempDirectory("testMonitor"); @@ -36,7 +42,8 @@ static void teardownAfterAll() throws IOException { @BeforeEach void setUpBefore() { - ApplicationConfiguration appConfigurations = new ApplicationConfiguration(); + MockitoAnnotations.openMocks(this); + ApplicationConfiguration appConfigurations = new ApplicationConfiguration(applicationConfigurationAuditLog); fssMonitor = new FssMonitor(appConfigurations); } @@ -51,7 +58,8 @@ void testGetUsedSpace(File path, LocalDateTime lastCheck, long cachedValue, long static Stream testGetUsedSpace() throws IOException { return Stream.of(Arguments.of(tempDir.toFile(), LocalDateTime.now(), 10, 10), Arguments.of(tempDir.toFile(), LocalDateTime.now() - .minusMinutes(10), + .minusMinutes( + 10), 200, 200), Arguments.of(tempDir.toFile(), LocalDateTime.now() .minusMinutes(50), diff --git a/multiapps-controller-web/src/test/java/org/cloudfoundry/multiapps/controller/web/security/AuthorizationCheckerTest.java b/multiapps-controller-web/src/test/java/org/cloudfoundry/multiapps/controller/web/security/AuthorizationCheckerTest.java index 286fca6606..4d870a4123 100644 --- a/multiapps-controller-web/src/test/java/org/cloudfoundry/multiapps/controller/web/security/AuthorizationCheckerTest.java +++ b/multiapps-controller-web/src/test/java/org/cloudfoundry/multiapps/controller/web/security/AuthorizationCheckerTest.java @@ -1,13 +1,5 @@ package org.cloudfoundry.multiapps.controller.web.security; -import java.time.Instant; -import java.time.temporal.ChronoUnit; -import java.util.Collections; -import java.util.EnumSet; -import java.util.Set; -import java.util.UUID; -import java.util.stream.Stream; - import com.sap.cloudfoundry.client.facade.domain.CloudOrganization; import com.sap.cloudfoundry.client.facade.domain.CloudSpace; import com.sap.cloudfoundry.client.facade.domain.ImmutableCloudMetadata; @@ -16,6 +8,7 @@ import com.sap.cloudfoundry.client.facade.domain.UserRole; import com.sap.cloudfoundry.client.facade.oauth2.OAuth2AccessTokenWithAdditionalInfo; import com.sap.cloudfoundry.client.facade.rest.CloudSpaceClient; +import org.cloudfoundry.multiapps.controller.core.auditlogging.ApplicationConfigurationAuditLog; import org.cloudfoundry.multiapps.controller.core.cf.CloudControllerClientFactory; import org.cloudfoundry.multiapps.controller.core.cf.clients.CfRolesGetter; import org.cloudfoundry.multiapps.controller.core.cf.clients.WebClientFactory; @@ -35,6 +28,14 @@ import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.server.ResponseStatusException; +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.Collections; +import java.util.EnumSet; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Stream; + import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; @@ -57,14 +58,19 @@ class AuthorizationCheckerTest { private WebClientFactory webClientFactory; @Mock private CfRolesGetter rolesGetter; - private final ApplicationConfiguration applicationConfiguration = new ApplicationConfiguration(); + + @Mock + private ApplicationConfigurationAuditLog applicationConfigurationAuditLog; + + private ApplicationConfiguration applicationConfiguration; private AuthorizationChecker authorizationChecker; @BeforeEach - void setUp() throws Exception { - MockitoAnnotations.openMocks(this) - .close(); - authorizationChecker = new AuthorizationChecker(clientFactory, tokenService, applicationConfiguration, webClientFactory) { + void setUp() { + MockitoAnnotations.openMocks(this); + applicationConfiguration = new ApplicationConfiguration(applicationConfigurationAuditLog); + authorizationChecker = new AuthorizationChecker( + clientFactory, tokenService, applicationConfiguration, webClientFactory) { @Override protected CfRolesGetter getRolesGetter(OAuth2AccessTokenWithAdditionalInfo token) { return rolesGetter;