From 5fc51aee6f650aecfa1de3bcbc6e59969ab95ff8 Mon Sep 17 00:00:00 2001 From: Yavor16 Date: Tue, 8 Apr 2025 14:36:47 +0300 Subject: [PATCH] Add uploaded file validation LMCROSSITXSADEPLOY-3175 --- multiapps-controller-core/pom.xml | 6 +- .../src/main/java/module-info.java | 3 + .../multiapps/controller/core/Messages.java | 2 + .../DescriptorParserFacadeFactory.java | 17 ++- .../parameters/FileMimeTypeValidator.java | 69 ++++++++++++ .../DescriptorParserFacadeFactoryTest.java | 12 +++ .../parameters/FileMimeTypeValidatorTest.java | 66 ++++++++++++ .../core/helpers/duplicate-keys.yaml | 10 ++ .../validators/parameters/duplicate-key.yaml | 9 ++ .../validators/parameters/valid-format.yaml | 8 ++ .../process/steps/SyncFlowableStep.java | 18 ++-- .../steps/ValidateDeployParametersStep.java | 19 +++- .../ValidateDeployParametersStepTest.java | 102 ++++++++++-------- pom.xml | 7 ++ 14 files changed, 284 insertions(+), 64 deletions(-) create mode 100644 multiapps-controller-core/src/test/java/org/cloudfoundry/multiapps/controller/core/validators/parameters/FileMimeTypeValidatorTest.java create mode 100644 multiapps-controller-core/src/test/resources/org/cloudfoundry/multiapps/controller/core/helpers/duplicate-keys.yaml create mode 100644 multiapps-controller-core/src/test/resources/org/cloudfoundry/multiapps/controller/core/validators/parameters/duplicate-key.yaml create mode 100644 multiapps-controller-core/src/test/resources/org/cloudfoundry/multiapps/controller/core/validators/parameters/valid-format.yaml diff --git a/multiapps-controller-core/pom.xml b/multiapps-controller-core/pom.xml index 18bc1f250f..ba8cb0f388 100644 --- a/multiapps-controller-core/pom.xml +++ b/multiapps-controller-core/pom.xml @@ -1,5 +1,5 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 multiapps-controller-core @@ -13,6 +13,10 @@ + + org.apache.tika + tika-core + jakarta.xml.bind jakarta.xml.bind-api diff --git a/multiapps-controller-core/src/main/java/module-info.java b/multiapps-controller-core/src/main/java/module-info.java index 325b230667..b35ea391c4 100644 --- a/multiapps-controller-core/src/main/java/module-info.java +++ b/multiapps-controller-core/src/main/java/module-info.java @@ -59,8 +59,11 @@ requires org.apache.commons.collections4; requires org.apache.commons.io; requires org.apache.commons.lang3; + requires org.apache.httpcomponents.httpclient; + requires org.apache.httpcomponents.httpcore; requires org.apache.httpcomponents.client5.httpclient5; requires org.apache.httpcomponents.core5.httpcore5; + requires org.apache.tika.core; requires org.cloudfoundry.multiapps.common; requires org.cloudfoundry.multiapps.controller.api; requires org.slf4j; 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 979102b2e6..98244df0b7 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 @@ -91,6 +91,8 @@ public final class Messages { public static final String BUILDPACKS_REQUIRED_FOR_CNB = "Buildpacks must be provided when lifecycle is set to 'cnb'."; public static final String DOCKER_INFO_REQUIRED = "Docker information must be provided when lifecycle is set to 'docker'."; public static final String BUILDPACKS_NOT_ALLOWED_WITH_DOCKER = "Buildpacks must not be provided when lifecycle is set to 'docker'."; + public static final String EXTENSION_DESCRIPTORS_COULD_NOT_BE_PARSED_TO_VALID_YAML = "Extension descriptor(s) could not be parsed as a valid YAML file. These descriptors may fail future deployments once stricter validation is enforced. Please review and correct them now to avoid future issues. Use at your own risk"; + public static final String UNSUPPORTED_FILE_FORMAT = "Unsupported file format! \"{0}\" detected"; // Warning messages public static final String ENVIRONMENT_VARIABLE_IS_NOT_SET_USING_DEFAULT = "Environment variable \"{0}\" is not set. Using default \"{1}\"..."; diff --git a/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/helpers/DescriptorParserFacadeFactory.java b/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/helpers/DescriptorParserFacadeFactory.java index 83d2a716c5..7c54fd7c51 100644 --- a/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/helpers/DescriptorParserFacadeFactory.java +++ b/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/helpers/DescriptorParserFacadeFactory.java @@ -2,7 +2,6 @@ import jakarta.inject.Inject; import jakarta.inject.Named; - import org.cloudfoundry.multiapps.common.util.YamlParser; import org.cloudfoundry.multiapps.controller.core.util.ApplicationConfiguration; import org.cloudfoundry.multiapps.mta.handlers.DescriptorParserFacade; @@ -19,9 +18,21 @@ public DescriptorParserFacadeFactory(ApplicationConfiguration applicationConfigu } public DescriptorParserFacade getInstance() { - LoaderOptions loaderOptions = new LoaderOptions(); - loaderOptions.setMaxAliasesForCollections(applicationConfiguration.getSnakeyamlMaxAliasesForCollections()); + LoaderOptions loaderOptions = createLoaderOptions(true); YamlParser yamlParser = new YamlParser(loaderOptions); return new DescriptorParserFacade(yamlParser); } + + public DescriptorParserFacade getInstanceWithDisabledDuplicateKeys() { + LoaderOptions loaderOptions = createLoaderOptions(false); + YamlParser yamlParser = new YamlParser(loaderOptions); + return new DescriptorParserFacade(yamlParser); + } + + private LoaderOptions createLoaderOptions(boolean shouldAllowDuplicateKeys) { + LoaderOptions loaderOptions = new LoaderOptions(); + loaderOptions.setMaxAliasesForCollections(applicationConfiguration.getSnakeyamlMaxAliasesForCollections()); + loaderOptions.setAllowDuplicateKeys(shouldAllowDuplicateKeys); + return loaderOptions; + } } diff --git a/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/validators/parameters/FileMimeTypeValidator.java b/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/validators/parameters/FileMimeTypeValidator.java index e69de29bb2..61f1d675b0 100644 --- a/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/validators/parameters/FileMimeTypeValidator.java +++ b/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/validators/parameters/FileMimeTypeValidator.java @@ -0,0 +1,69 @@ +package org.cloudfoundry.multiapps.controller.core.validators.parameters; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.text.MessageFormat; +import java.util.function.Consumer; + +import jakarta.inject.Inject; +import jakarta.inject.Named; +import org.apache.tika.Tika; +import org.cloudfoundry.multiapps.common.ParsingException; +import org.cloudfoundry.multiapps.common.SLException; +import org.cloudfoundry.multiapps.controller.core.Messages; +import org.cloudfoundry.multiapps.controller.core.helpers.DescriptorParserFacadeFactory; +import org.cloudfoundry.multiapps.controller.persistence.services.FileService; +import org.cloudfoundry.multiapps.controller.persistence.services.FileStorageException; + +@Named +public class FileMimeTypeValidator { + + private static final String APPLICATION_OCTET_STREAM_MIME_TYPE = "application/octet-stream"; + private static final String APPLICATION_ZIP_MIME_TYPE = "application/zip"; + private static final String TEXT_PLAIN_MIME_TYPE = "text/plain"; + private static final Tika mimeTypeDetector = new Tika(); + private final DescriptorParserFacadeFactory descriptorParserFactory; + private final FileService fileService; + + @Inject + public FileMimeTypeValidator(FileService fileService, DescriptorParserFacadeFactory descriptorParserFactory) { + this.fileService = fileService; + this.descriptorParserFactory = descriptorParserFactory; + } + + public void validateFileType(String spaceGuid, String appArchiveId, Consumer stepLogger) { + try (InputStream fileInputStream = fileService.openInputStream(spaceGuid, appArchiveId); + InputStream bufferedFileInputStream = new BufferedInputStream(fileInputStream)) { + validateInputStreamMimeType(bufferedFileInputStream, stepLogger); + } catch (FileStorageException | IOException e) { + throw new SLException(e); + } + } + + private void validateInputStreamMimeType(InputStream uploadedFileInputStream, Consumer stepLogger) throws IOException { + String detectedType = getFileMimeType(uploadedFileInputStream); + + switch (detectedType) { + case TEXT_PLAIN_MIME_TYPE -> validateYamlFile(uploadedFileInputStream, stepLogger); + case APPLICATION_ZIP_MIME_TYPE, APPLICATION_OCTET_STREAM_MIME_TYPE -> { + } + default -> stepLogger.accept(MessageFormat.format(Messages.UNSUPPORTED_FILE_FORMAT, detectedType)); + } + + } + + private String getFileMimeType(InputStream uploadedFileInputStream) throws IOException { + return mimeTypeDetector.detect(uploadedFileInputStream); + } + + private void validateYamlFile(InputStream uploadedFileInputStream, Consumer stepLogger) { + try { + descriptorParserFactory.getInstanceWithDisabledDuplicateKeys() + .parseExtensionDescriptor(uploadedFileInputStream); + } catch (ParsingException e) { + stepLogger.accept(Messages.EXTENSION_DESCRIPTORS_COULD_NOT_BE_PARSED_TO_VALID_YAML); + stepLogger.accept(e.getMessage()); + } + } +} \ No newline at end of file diff --git a/multiapps-controller-core/src/test/java/org/cloudfoundry/multiapps/controller/core/helpers/DescriptorParserFacadeFactoryTest.java b/multiapps-controller-core/src/test/java/org/cloudfoundry/multiapps/controller/core/helpers/DescriptorParserFacadeFactoryTest.java index f7307cf104..fa8f79b64a 100644 --- a/multiapps-controller-core/src/test/java/org/cloudfoundry/multiapps/controller/core/helpers/DescriptorParserFacadeFactoryTest.java +++ b/multiapps-controller-core/src/test/java/org/cloudfoundry/multiapps/controller/core/helpers/DescriptorParserFacadeFactoryTest.java @@ -23,4 +23,16 @@ void testGetInstance() { Assertions.assertThrows(ParsingException.class, () -> instance.parseDeploymentDescriptor(mtadYaml)); } + @Test + void testParseWithDuplicateValues() { + final int maxAliases = 5; + ApplicationConfiguration applicationConfiguration = Mockito.mock(ApplicationConfiguration.class); + Mockito.when(applicationConfiguration.getSnakeyamlMaxAliasesForCollections()) + .thenReturn(maxAliases); + DescriptorParserFacadeFactory descriptorParserFacadeFactory = new DescriptorParserFacadeFactory(applicationConfiguration); + DescriptorParserFacade instance = descriptorParserFacadeFactory.getInstanceWithDisabledDuplicateKeys(); + InputStream mtadYaml = getClass().getResourceAsStream("duplicate-keys.yaml"); + Assertions.assertThrows(ParsingException.class, () -> instance.parseDeploymentDescriptor(mtadYaml)); + } + } \ No newline at end of file diff --git a/multiapps-controller-core/src/test/java/org/cloudfoundry/multiapps/controller/core/validators/parameters/FileMimeTypeValidatorTest.java b/multiapps-controller-core/src/test/java/org/cloudfoundry/multiapps/controller/core/validators/parameters/FileMimeTypeValidatorTest.java new file mode 100644 index 0000000000..f5fb0a56ee --- /dev/null +++ b/multiapps-controller-core/src/test/java/org/cloudfoundry/multiapps/controller/core/validators/parameters/FileMimeTypeValidatorTest.java @@ -0,0 +1,66 @@ +package org.cloudfoundry.multiapps.controller.core.validators.parameters; + +import java.io.InputStream; +import java.util.UUID; + +import org.cloudfoundry.multiapps.controller.core.Messages; +import org.cloudfoundry.multiapps.controller.core.helpers.DescriptorParserFacadeFactory; +import org.cloudfoundry.multiapps.controller.core.util.ApplicationConfiguration; +import org.cloudfoundry.multiapps.controller.persistence.services.FileService; +import org.cloudfoundry.multiapps.controller.persistence.services.FileStorageException; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.when; + +class FileMimeTypeValidatorTest { + + @Mock + private FileService fileService; + + private FileMimeTypeValidator fileMimeTypeValidator; + + private final String TEST_SPACE_ID = UUID.randomUUID() + .toString(); + private final String TEST_FILE_ID = UUID.randomUUID() + .toString(); + + private DescriptorParserFacadeFactory descriptorParserFacadeFactory; + + @BeforeEach + void setUp() throws Exception { + MockitoAnnotations.openMocks(this) + .close(); + int maxAliases = 5; + ApplicationConfiguration applicationConfiguration = Mockito.mock(ApplicationConfiguration.class); + when(applicationConfiguration.getSnakeyamlMaxAliasesForCollections()) + .thenReturn(maxAliases); + descriptorParserFacadeFactory = new DescriptorParserFacadeFactory(applicationConfiguration); + fileMimeTypeValidator = new FileMimeTypeValidator(fileService, descriptorParserFacadeFactory); + } + + @Test + void testValidationWithCorrectYaml() throws FileStorageException { + InputStream mtadYaml = getClass().getResourceAsStream("valid-format.yaml"); + when(fileService.openInputStream(TEST_SPACE_ID, TEST_FILE_ID)).thenReturn(mtadYaml); + fileMimeTypeValidator = new FileMimeTypeValidator(fileService, descriptorParserFacadeFactory); + fileMimeTypeValidator.validateFileType(TEST_SPACE_ID, TEST_FILE_ID, message -> { + }); + } + + @Test + void testValidationWithDuplicateKeys() throws FileStorageException { + InputStream mtadYaml = getClass().getResourceAsStream("duplicate-key.yaml"); + when(fileService.openInputStream(TEST_SPACE_ID, TEST_FILE_ID)).thenReturn(mtadYaml); + fileMimeTypeValidator = new FileMimeTypeValidator(fileService, descriptorParserFacadeFactory); + fileMimeTypeValidator.validateFileType(TEST_SPACE_ID, TEST_FILE_ID, message -> { + boolean doesMessageContainWarningMessage = message.contains(Messages.EXTENSION_DESCRIPTORS_COULD_NOT_BE_PARSED_TO_VALID_YAML); + boolean doesMessageContainException = message.contains("Error while parsing YAML string"); + assertTrue(doesMessageContainException || doesMessageContainWarningMessage); + }); + } +} diff --git a/multiapps-controller-core/src/test/resources/org/cloudfoundry/multiapps/controller/core/helpers/duplicate-keys.yaml b/multiapps-controller-core/src/test/resources/org/cloudfoundry/multiapps/controller/core/helpers/duplicate-keys.yaml new file mode 100644 index 0000000000..15ae1a9efe --- /dev/null +++ b/multiapps-controller-core/src/test/resources/org/cloudfoundry/multiapps/controller/core/helpers/duplicate-keys.yaml @@ -0,0 +1,10 @@ +_schema-version: "3.1" +ID: com.test.1 +version: 1.0.0 + +modules: + - name: spark-service-admin + type: abc + properties: + MAX_SIZE: а + MAX_SIZE: а \ No newline at end of file diff --git a/multiapps-controller-core/src/test/resources/org/cloudfoundry/multiapps/controller/core/validators/parameters/duplicate-key.yaml b/multiapps-controller-core/src/test/resources/org/cloudfoundry/multiapps/controller/core/validators/parameters/duplicate-key.yaml new file mode 100644 index 0000000000..71634817fd --- /dev/null +++ b/multiapps-controller-core/src/test/resources/org/cloudfoundry/multiapps/controller/core/validators/parameters/duplicate-key.yaml @@ -0,0 +1,9 @@ +_schema-version: "3.3" +ID: com.test +extends: com.test + +modules: + - name: spark-service-admin + properties: + MAX_SIZE: а + MAX_SIZE: а diff --git a/multiapps-controller-core/src/test/resources/org/cloudfoundry/multiapps/controller/core/validators/parameters/valid-format.yaml b/multiapps-controller-core/src/test/resources/org/cloudfoundry/multiapps/controller/core/validators/parameters/valid-format.yaml new file mode 100644 index 0000000000..7779c6b762 --- /dev/null +++ b/multiapps-controller-core/src/test/resources/org/cloudfoundry/multiapps/controller/core/validators/parameters/valid-format.yaml @@ -0,0 +1,8 @@ +_schema-version: "3.3" +ID: com.test +extends: com.test + +modules: + - name: spark-service-admin + properties: + MAX_SIZE: а diff --git a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/SyncFlowableStep.java b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/SyncFlowableStep.java index 784959dcda..935c81f35b 100644 --- a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/SyncFlowableStep.java +++ b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/SyncFlowableStep.java @@ -1,10 +1,14 @@ package org.cloudfoundry.multiapps.controller.process.steps; import java.util.function.BiFunction; +import java.util.function.Consumer; +import com.sap.cloudfoundry.client.facade.CloudControllerException; +import com.sap.cloudfoundry.client.facade.CloudOperationException; +import com.sap.cloudfoundry.client.facade.CloudServiceBrokerException; +import io.netty.handler.timeout.TimeoutException; import jakarta.inject.Inject; import jakarta.inject.Named; - import org.apache.commons.lang3.StringUtils; import org.cloudfoundry.multiapps.common.ContentException; import org.cloudfoundry.multiapps.common.SLException; @@ -28,12 +32,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.sap.cloudfoundry.client.facade.CloudControllerException; -import com.sap.cloudfoundry.client.facade.CloudOperationException; -import com.sap.cloudfoundry.client.facade.CloudServiceBrokerException; - -import io.netty.handler.timeout.TimeoutException; - public abstract class SyncFlowableStep implements JavaDelegate { protected final Logger logger = LoggerFactory.getLogger(getClass()); @@ -120,7 +118,7 @@ private void handleException(ProcessContext context, Exception e) { * * @param context flowable context of the step * @param e thrown exception from {@link #executeStep(ProcessContext) executeStep} and pre-processed by - * {@link #handleException(ProcessContext, Exception) handleException} + * {@link #handleException(ProcessContext, Exception) handleException} * @throws Exception in case derivative methods throw exception */ protected void onStepError(ProcessContext context, Exception e) throws Exception { @@ -221,4 +219,8 @@ protected ProcessLoggerPersister getProcessLogsPersister() { return processLoggerPersister; } + protected Consumer getStepWarningLoggerConsumer() { + return message -> getStepLogger().warn(message); + } + } diff --git a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/ValidateDeployParametersStep.java b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/ValidateDeployParametersStep.java index 256bc33faf..615ada8866 100644 --- a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/ValidateDeployParametersStep.java +++ b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/ValidateDeployParametersStep.java @@ -8,10 +8,13 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; +import jakarta.inject.Inject; +import jakarta.inject.Named; import org.apache.commons.io.IOUtils; import org.cloudfoundry.multiapps.common.ContentException; import org.cloudfoundry.multiapps.common.SLException; import org.cloudfoundry.multiapps.controller.client.util.ResilientOperationExecutor; +import org.cloudfoundry.multiapps.controller.core.validators.parameters.FileMimeTypeValidator; import org.cloudfoundry.multiapps.controller.persistence.model.FileEntry; import org.cloudfoundry.multiapps.controller.persistence.model.ImmutableFileEntry; import org.cloudfoundry.multiapps.controller.persistence.services.FileStorageException; @@ -25,19 +28,18 @@ import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.context.annotation.Scope; -import jakarta.inject.Inject; -import jakarta.inject.Named; - @Named("validateDeployParametersStep") @Scope(BeanDefinition.SCOPE_PROTOTYPE) public class ValidateDeployParametersStep extends SyncFlowableStep { private final ResilientOperationExecutor resilientOperationExecutor = new ResilientOperationExecutor(); private final ExecutorService fileStorageThreadPool; + private final FileMimeTypeValidator fileMimeTypeValidator; @Inject - public ValidateDeployParametersStep(ExecutorService fileStorageThreadPool) { + public ValidateDeployParametersStep(ExecutorService fileStorageThreadPool, FileMimeTypeValidator fileMimeTypeValidator) { this.fileStorageThreadPool = fileStorageThreadPool; + this.fileMimeTypeValidator = fileMimeTypeValidator; } @Override @@ -62,8 +64,10 @@ private void validateParameters(ProcessContext context) { List extensionDescriptors = validateExtensionDescriptorFileIds(context); List archivePartEntries = getArchivePartEntries(context); validateFilesSizeLimit(context, archivePartEntries, extensionDescriptors); + extensionDescriptors.forEach(this::validateFileEntryType); if (archivePartEntries.size() == 1) { + validateFileEntryType(archivePartEntries.get(0)); getStepLogger().infoWithoutProgressMessage(Messages.ARCHIVE_WAS_NOT_SPLIT_TOTAL_SIZE_IN_BYTES_0, archivePartEntries.get(0) .getSize()); } else { @@ -181,6 +185,7 @@ private void mergeArchive(ProcessContext context, List archivePartEnt archivePartEntries.size(), archiveSize); FileEntry uploadedArchive = persistArchive(archiveStreamWithName, context, archiveSize); context.setVariable(Variables.APP_ARCHIVE_ID, uploadedArchive.getId()); + validateFileEntryType(uploadedArchive); getStepLogger().infoWithoutProgressMessage(MessageFormat.format(Messages.ARCHIVE_WITH_ID_0_AND_NAME_1_WAS_STORED, uploadedArchive.getId(), archiveStreamWithName.getArchiveName())); @@ -189,13 +194,17 @@ private void mergeArchive(ProcessContext context, List archivePartEnt } } + private void validateFileEntryType(FileEntry fileEntry) { + fileMimeTypeValidator.validateFileType(fileEntry.getSpace(), fileEntry.getId(), getStepWarningLoggerConsumer()); + } + private String[] getArchivePartIds(ProcessContext context) { String archiveId = context.getRequiredVariable(Variables.APP_ARCHIVE_ID); return archiveId.split(","); } private List getArchivePartEntries(ProcessContext context, String[] appArchivePartsId) { - return Arrays.stream(appArchivePartsId) + return Arrays.stream(appArchivePartsId) .map(appArchivePartId -> findFile(context, appArchivePartId)) .toList(); } diff --git a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/ValidateDeployParametersStepTest.java b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/ValidateDeployParametersStepTest.java index 51cf648610..168e3c9821 100644 --- a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/ValidateDeployParametersStepTest.java +++ b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/ValidateDeployParametersStepTest.java @@ -1,11 +1,5 @@ package org.cloudfoundry.multiapps.controller.process.steps; -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.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.when; - import java.io.InputStream; import java.math.BigInteger; import java.nio.file.Files; @@ -21,6 +15,7 @@ import org.cloudfoundry.multiapps.common.SLException; import org.cloudfoundry.multiapps.controller.core.util.ApplicationConfiguration; +import org.cloudfoundry.multiapps.controller.core.validators.parameters.FileMimeTypeValidator; import org.cloudfoundry.multiapps.controller.persistence.model.FileEntry; import org.cloudfoundry.multiapps.controller.persistence.model.ImmutableFileEntry; import org.cloudfoundry.multiapps.controller.persistence.services.FileStorageException; @@ -33,6 +28,15 @@ import org.junit.jupiter.params.provider.MethodSource; import org.mockito.Mockito; +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.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + class ValidateDeployParametersStepTest extends SyncFlowableStepTest { private static final String MERGED_ARCHIVE_TEST_MTAR = "merged-archive-test.mtar"; @@ -48,36 +52,37 @@ class ValidateDeployParametersStepTest extends SyncFlowableStepTest testExecution() { return Stream.of( - // [1] No file associated with the specified file id - Arguments.of(new StepInput(EXISTING_FILE_ID, NOT_EXISTING_FILE_ID + "," + EXISTING_FILE_ID, 1, null), - MessageFormat.format(Messages.ERROR_NO_FILE_ASSOCIATED_WITH_THE_SPECIFIED_FILE_ID_0_IN_SPACE_1, - "notExistingFileId", "space-id"), - false, ""), - - // [2] Valid parameters - Arguments.of(new StepInput(EXISTING_FILE_ID, - EXISTING_FILE_ID + "," + EXISTING_FILE_ID, - 1, - VersionRule.HIGHER.toString()), - null, false, ""), - - // [3] Max descriptor size exceeded - Arguments.of(new StepInput(EXISTING_FILE_ID, EXISTING_BIGGER_FILE_ID, 1, VersionRule.HIGHER.toString()), - MessageFormat.format(org.cloudfoundry.multiapps.mta.Messages.ERROR_SIZE_OF_FILE_EXCEEDS_CONFIGURED_MAX_SIZE_LIMIT, - "1048577", "extDescriptorFile", "1048576"), - false, ""), - - // [4] Process chunked file - Arguments.of(new StepInput(MERGED_ARCHIVE_NAME + ".part.0," + MERGED_ARCHIVE_NAME + ".part.1," - + MERGED_ARCHIVE_NAME + ".part.2", null, 1, VersionRule.HIGHER.toString()), null, true, ""), - - // [5] Max size of entries exceeded - Arguments.of(new StepInput(EXCEEDING_FILE_SIZE_ID + ".part.0," + EXCEEDING_FILE_SIZE_ID + ".part.1," - + EXCEEDING_FILE_SIZE_ID + ".part.2," + EXCEEDING_FILE_SIZE_ID + ".part.3," + EXCEEDING_FILE_SIZE_ID - + ".part.4", null, 1, VersionRule.HIGHER.toString()), - MessageFormat.format(Messages.SIZE_OF_ALL_OPERATIONS_FILES_0_EXCEEDS_MAX_UPLOAD_SIZE_1, 5368709120L, - 4294967296L), - true, "")); + // [1] No file associated with the specified file id + Arguments.of(new StepInput(EXISTING_FILE_ID, NOT_EXISTING_FILE_ID + "," + EXISTING_FILE_ID, 1, null), + MessageFormat.format(Messages.ERROR_NO_FILE_ASSOCIATED_WITH_THE_SPECIFIED_FILE_ID_0_IN_SPACE_1, + "notExistingFileId", "space-id"), + false, ""), + + // [2] Valid parameters + Arguments.of(new StepInput(EXISTING_FILE_ID, + EXISTING_FILE_ID + "," + EXISTING_FILE_ID, + 1, + VersionRule.HIGHER.toString()), + null, false, ""), + + // [3] Max descriptor size exceeded + Arguments.of(new StepInput(EXISTING_FILE_ID, EXISTING_BIGGER_FILE_ID, 1, VersionRule.HIGHER.toString()), + MessageFormat.format(org.cloudfoundry.multiapps.mta.Messages.ERROR_SIZE_OF_FILE_EXCEEDS_CONFIGURED_MAX_SIZE_LIMIT, + "1048577", "extDescriptorFile", "1048576"), + false, ""), + + // [4] Process chunked file + Arguments.of(new StepInput(MERGED_ARCHIVE_NAME + ".part.0," + MERGED_ARCHIVE_NAME + ".part.1," + + MERGED_ARCHIVE_NAME + ".part.2", null, 1, VersionRule.HIGHER.toString()), null, true, ""), + + // [5] Max size of entries exceeded + Arguments.of(new StepInput(EXCEEDING_FILE_SIZE_ID + ".part.0," + EXCEEDING_FILE_SIZE_ID + ".part.1," + + EXCEEDING_FILE_SIZE_ID + ".part.2," + EXCEEDING_FILE_SIZE_ID + ".part.3," + + EXCEEDING_FILE_SIZE_ID + + ".part.4", null, 1, VersionRule.HIGHER.toString()), + MessageFormat.format(Messages.SIZE_OF_ALL_OPERATIONS_FILES_0_EXCEEDS_MAX_UPLOAD_SIZE_1, 5368709120L, + 4294967296L), + true, "")); } private static FileEntry createFileEntry(String id, String name, long size) { @@ -125,15 +130,15 @@ private void prepareContext() { private void prepareFileService(String appArchiveId) throws FileStorageException { when(fileService.getFile("space-id", EXISTING_FILE_ID)) - .thenReturn(createFileEntry(EXISTING_FILE_ID, "some-file-entry-name", 1024 * 1024L)); + .thenReturn(createFileEntry(EXISTING_FILE_ID, "some-file-entry-name", 1024 * 1024L)); when(fileService.getFile("space-id", MERGED_ARCHIVE_NAME + ".part.0")) - .thenReturn(createFileEntry(MERGED_ARCHIVE_NAME + ".part.0", MERGED_ARCHIVE_NAME + ".part.0", 1024 * 1024L)); + .thenReturn(createFileEntry(MERGED_ARCHIVE_NAME + ".part.0", MERGED_ARCHIVE_NAME + ".part.0", 1024 * 1024L)); when(fileService.getFile("space-id", MERGED_ARCHIVE_NAME + ".part.1")) - .thenReturn(createFileEntry(MERGED_ARCHIVE_NAME + ".part.1", MERGED_ARCHIVE_NAME + ".part.1", 1024 * 1024L)); + .thenReturn(createFileEntry(MERGED_ARCHIVE_NAME + ".part.1", MERGED_ARCHIVE_NAME + ".part.1", 1024 * 1024L)); when(fileService.getFile("space-id", MERGED_ARCHIVE_NAME + ".part.2")) - .thenReturn(createFileEntry(MERGED_ARCHIVE_NAME + ".part.2", MERGED_ARCHIVE_NAME + ".part.2", 1024 * 1024L)); + .thenReturn(createFileEntry(MERGED_ARCHIVE_NAME + ".part.2", MERGED_ARCHIVE_NAME + ".part.2", 1024 * 1024L)); when(fileService.getFile("space-id", EXCEEDING_FILE_SIZE_ID + ".part.0")) .thenReturn(createFileEntry(MERGED_ARCHIVE_NAME + ".part.0", MERGED_ARCHIVE_NAME + ".part.0", 1024 * 1024 * 1024)); @@ -151,11 +156,11 @@ private void prepareFileService(String appArchiveId) throws FileStorageException .thenReturn(createFileEntry(MERGED_ARCHIVE_NAME + ".part.4", MERGED_ARCHIVE_NAME + ".part.4", 1024 * 1024 * 1024)); when(fileService.getFile("space-id", EXISTING_BIGGER_FILE_ID)) - .thenReturn(createFileEntry(EXISTING_BIGGER_FILE_ID, "extDescriptorFile", 1024 * 1024L + 1)); + .thenReturn(createFileEntry(EXISTING_BIGGER_FILE_ID, "extDescriptorFile", 1024 * 1024L + 1)); when(fileService.getFile("space-id", NOT_EXISTING_FILE_ID)) - .thenReturn(null); + .thenReturn(null); when(fileService.addFile(any(FileEntry.class), any(InputStream.class))) - .thenReturn(createFileEntry(EXISTING_FILE_ID, MERGED_ARCHIVE_TEST_MTAR, 1024 * 1024 * 1024L)); + .thenReturn(createFileEntry(EXISTING_FILE_ID, MERGED_ARCHIVE_TEST_MTAR, 1024 * 1024 * 1024L)); if (appArchiveId.contains(EXCEEDING_FILE_SIZE_ID)) { List fileEntries = List.of(createFileEntry(EXCEEDING_FILE_SIZE_ID + ".part.0", EXCEEDING_FILE_SIZE_ID + ".part.0", 1024 * 1024 * 1024), @@ -168,7 +173,7 @@ private void prepareFileService(String appArchiveId) throws FileStorageException createFileEntry(EXCEEDING_FILE_SIZE_ID + ".part.4", EXCEEDING_FILE_SIZE_ID + ".part.4", 1024 * 1024 * 1024)); when(fileService.listFilesBySpaceAndOperationId(Mockito.anyString(), Mockito.anyString())) - .thenReturn(fileEntries); + .thenReturn(fileEntries); } } @@ -179,9 +184,9 @@ private void prepareArchiveMerger() { private void prepareConfiguration() { when(configuration.getMaxMtaDescriptorSize()) - .thenReturn(ApplicationConfiguration.DEFAULT_MAX_MTA_DESCRIPTOR_SIZE); + .thenReturn(ApplicationConfiguration.DEFAULT_MAX_MTA_DESCRIPTOR_SIZE); when(configuration.getMaxUploadSize()) - .thenReturn(ApplicationConfiguration.DEFAULT_MAX_UPLOAD_SIZE); + .thenReturn(ApplicationConfiguration.DEFAULT_MAX_UPLOAD_SIZE); } private void validate() { @@ -204,7 +209,10 @@ protected ValidateDeployParametersStep createStep() { futureTask.run(); return futureTask; }); - return new ValidateDeployParametersStep(executorService); + FileMimeTypeValidator fileMimeTypeValidator = mock(FileMimeTypeValidator.class); + doNothing().when(fileMimeTypeValidator) + .validateFileType(anyString(), anyString(), any()); + return new ValidateDeployParametersStep(executorService, fileMimeTypeValidator); } private static class StepInput { diff --git a/pom.xml b/pom.xml index a8217d9dfa..b55ecdebdc 100644 --- a/pom.xml +++ b/pom.xml @@ -58,6 +58,7 @@ 3.0.0 2.1.9 1.27.1 + 3.2.0 multiapps-controller-client @@ -467,6 +468,12 @@ slf4j-jdk14 ${slf4j.version} + + + org.apache.tika + tika-core + ${tika-core.version} + commons-io