Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 1 addition & 5 deletions multiapps-controller-core/pom.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<artifactId>multiapps-controller-core</artifactId>
Expand All @@ -13,10 +13,6 @@
</parent>

<dependencies>
<dependency>
<groupId>org.apache.tika</groupId>
<artifactId>tika-core</artifactId>
</dependency>
<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
Expand Down
3 changes: 0 additions & 3 deletions multiapps-controller-core/src/main/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,8 @@
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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,6 @@ 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}\"...";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

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;
Expand All @@ -18,21 +19,9 @@ public DescriptorParserFacadeFactory(ApplicationConfiguration applicationConfigu
}

public DescriptorParserFacade getInstance() {
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;
YamlParser yamlParser = new YamlParser(loaderOptions);
return new DescriptorParserFacade(yamlParser);
}
}
Original file line number Diff line number Diff line change
@@ -1,69 +0,0 @@
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<String> 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<String> 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<String> 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());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,4 @@ 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));
}

}

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
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;
Expand All @@ -32,6 +28,12 @@
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());
Expand Down Expand Up @@ -118,7 +120,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 {
Expand Down Expand Up @@ -219,8 +221,4 @@ protected ProcessLoggerPersister getProcessLogsPersister() {
return processLoggerPersister;
}

protected Consumer<String> getStepWarningLoggerConsumer() {
return message -> getStepLogger().warn(message);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,10 @@
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;
Expand All @@ -28,18 +25,19 @@
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, FileMimeTypeValidator fileMimeTypeValidator) {
public ValidateDeployParametersStep(ExecutorService fileStorageThreadPool) {
this.fileStorageThreadPool = fileStorageThreadPool;
this.fileMimeTypeValidator = fileMimeTypeValidator;
}

@Override
Expand All @@ -64,10 +62,8 @@ private void validateParameters(ProcessContext context) {
List<FileEntry> extensionDescriptors = validateExtensionDescriptorFileIds(context);
List<FileEntry> 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 {
Expand Down Expand Up @@ -185,7 +181,6 @@ private void mergeArchive(ProcessContext context, List<FileEntry> 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()));
Expand All @@ -194,17 +189,13 @@ private void mergeArchive(ProcessContext context, List<FileEntry> 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<FileEntry> getArchivePartEntries(ProcessContext context, String[] appArchivePartsId) {
return Arrays.stream(appArchivePartsId)
return Arrays.stream(appArchivePartsId)
.map(appArchivePartId -> findFile(context, appArchivePartId))
.toList();
}
Expand Down
Loading
Loading