Skip to content

Commit 8c9298a

Browse files
committed
Validate size of all uploaded parts per operation
JIRA:LMCROSSITXSADEPLOY-2948
1 parent 9259002 commit 8c9298a

File tree

9 files changed

+90
-42
lines changed

9 files changed

+90
-42
lines changed

multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/util/ApplicationConfiguration.java

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
import org.cloudfoundry.multiapps.controller.core.configuration.Environment;
2525
import org.cloudfoundry.multiapps.controller.core.health.model.HealthCheckConfiguration;
2626
import org.cloudfoundry.multiapps.controller.core.health.model.ImmutableHealthCheckConfiguration;
27-
import org.cloudfoundry.multiapps.controller.persistence.util.Configuration;
2827
import org.cloudfoundry.multiapps.mta.handlers.ConfigurationParser;
2928
import org.cloudfoundry.multiapps.mta.model.Platform;
3029
import org.slf4j.Logger;
@@ -270,10 +269,6 @@ private Set<String> getNotSensitiveConfigVariables() {
270269
CFG_SNAKEYAML_MAX_ALIASES_FOR_COLLECTIONS, CFG_SERVICE_HANDLING_MAX_PARALLEL_THREADS);
271270
}
272271

273-
public Configuration getFileConfiguration() {
274-
return new Configuration(getMaxUploadSize());
275-
}
276-
277272
public URL getControllerUrl() {
278273
if (controllerUrl == null) {
279274
controllerUrl = getControllerUrlFromEnvironment();

multiapps-controller-persistence/src/main/java/org/cloudfoundry/multiapps/controller/persistence/util/Configuration.java

Lines changed: 0 additions & 21 deletions
This file was deleted.

multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/Messages.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,9 @@ public class Messages {
6868
public static final String ERROR_WHILE_STARTING_ASYNC_UPLOAD_OF_APP_WITH_NAME_0 = "Error while starting async upload of app with name {0}";
6969
public static final String REQUIRED_APPLICATION_TO_POLL_0_NOT_FOUND = "Required application to poll: \"{0}\" not found";
7070
public static final String NOT_INTEGER_PARAMETER_VALUE = "Value \"{0}\" of parameter \"{1}\" is not integer";
71+
public static final String ERROR_OCURRED_DURING_VALIDATION_OF_FILES_0 = "Error occurred during validation of files \"{0}\"";
72+
public static final String SIZE_OF_ALL_OPERATIONS_FILES_0_EXCEEDS_MAX_UPLOAD_SIZE_1 = "Size of all operation files \"{0}\" exceeds max upload size limit \"{1}\"";
73+
7174
// Audit log messages
7275

7376
// ERROR log messages

multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/ValidateDeployParametersStep.java

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,15 @@
88
import javax.inject.Named;
99

1010
import org.apache.commons.io.IOUtils;
11+
import org.cloudfoundry.multiapps.common.ContentException;
1112
import org.cloudfoundry.multiapps.common.SLException;
1213
import org.cloudfoundry.multiapps.controller.client.util.ResilientOperationExecutor;
1314
import org.cloudfoundry.multiapps.controller.persistence.model.FileEntry;
1415
import org.cloudfoundry.multiapps.controller.persistence.model.ImmutableFileEntry;
1516
import org.cloudfoundry.multiapps.controller.persistence.services.FileStorageException;
1617
import org.cloudfoundry.multiapps.controller.process.Messages;
1718
import org.cloudfoundry.multiapps.controller.process.stream.ArchiveStreamWithName;
19+
import org.cloudfoundry.multiapps.controller.process.util.FileSweeper;
1820
import org.cloudfoundry.multiapps.controller.process.util.MergedArchiveStreamCreator;
1921
import org.cloudfoundry.multiapps.controller.process.variables.Variables;
2022
import org.springframework.beans.factory.config.BeanDefinition;
@@ -46,6 +48,7 @@ protected String getStepErrorMessage(ProcessContext context) {
4648

4749
private void validateParameters(ProcessContext context) {
4850
validateExtensionDescriptorFileIds(context);
51+
validateFilesSizeLimit(context);
4952
validateArchive(context);
5053
}
5154

@@ -87,6 +90,40 @@ private void validateDescriptorSize(FileEntry file) {
8790
}
8891
}
8992

93+
private void validateFilesSizeLimit(ProcessContext context) {
94+
try {
95+
checkFileSizeOfAllFiles(context);
96+
} catch (FileStorageException e) {
97+
throw new SLException(e, MessageFormat.format(Messages.ERROR_OCURRED_DURING_VALIDATION_OF_FILES_0, e.getMessage()));
98+
}
99+
100+
}
101+
102+
private void checkFileSizeOfAllFiles(ProcessContext context) throws FileStorageException {
103+
long maxFileSizeLimit = configuration.getMaxUploadSize();
104+
List<FileEntry> fileEntries = fileService.listFilesBySpaceAndOperationId(context.getVariable(Variables.SPACE_GUID),
105+
context.getVariable(Variables.CORRELATION_ID));
106+
long sizeOfAllFiles = getSizeOfAllFiles(fileEntries);
107+
if (sizeOfAllFiles >= maxFileSizeLimit) {
108+
deleteFiles(context, fileEntries);
109+
throw new ContentException(Messages.SIZE_OF_ALL_OPERATIONS_FILES_0_EXCEEDS_MAX_UPLOAD_SIZE_1, sizeOfAllFiles, maxFileSizeLimit);
110+
}
111+
}
112+
113+
private long getSizeOfAllFiles(List<FileEntry> fileEntries) {
114+
return fileEntries.stream()
115+
.mapToLong(fileEntry -> fileEntry.getSize()
116+
.longValue())
117+
.sum();
118+
}
119+
120+
private void deleteFiles(ProcessContext context, List<FileEntry> fileEntries) throws FileStorageException {
121+
FileSweeper fileSweeper = new FileSweeper(context.getVariable(Variables.SPACE_GUID),
122+
fileService,
123+
context.getVariable(Variables.CORRELATION_ID));
124+
fileSweeper.sweep(fileEntries);
125+
}
126+
90127
private void validateArchive(ProcessContext context) {
91128
String[] archivePartIds = getArchivePartIds(context);
92129
if (archivePartIds.length == 1) {

multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/util/FileSweeper.java

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package org.cloudfoundry.multiapps.controller.process.util;
22

33
import java.text.MessageFormat;
4+
import java.util.List;
45

56
import org.cloudfoundry.multiapps.controller.persistence.model.FileEntry;
67
import org.cloudfoundry.multiapps.controller.persistence.services.FileService;
@@ -31,14 +32,24 @@ public void sweep(String fileIds) throws FileStorageException {
3132
}
3233
}
3334

35+
public void sweep(List<FileEntry> fileEntries) throws FileStorageException {
36+
for (FileEntry fileEntry : fileEntries) {
37+
sweepFileEntry(fileEntry);
38+
}
39+
}
40+
3441
private void sweepSingle(String fileId) throws FileStorageException {
3542
FileEntry fileEntry = fileService.getFile(spaceId, fileId);
43+
sweepFileEntry(fileEntry);
44+
}
45+
46+
private void sweepFileEntry(FileEntry fileEntry) throws FileStorageException {
3647
if (operationId.equals(fileEntry.getOperationId())) {
37-
fileService.deleteFile(spaceId, fileId);
38-
LOGGER.info(MessageFormat.format(Messages.FILE_WITH_ID_0_WAS_DELETED, fileId));
48+
fileService.deleteFile(spaceId, fileEntry.getId());
49+
LOGGER.info(MessageFormat.format(Messages.FILE_WITH_ID_0_WAS_DELETED, fileEntry.getId()));
3950
return;
4051
}
41-
LOGGER.warn(MessageFormat.format(Messages.FILE_WITH_ID_0_OPERATION_OWNERSHIP_CHANGED_FROM_0_TO_1, fileId, operationId,
52+
LOGGER.warn(MessageFormat.format(Messages.FILE_WITH_ID_0_OPERATION_OWNERSHIP_CHANGED_FROM_0_TO_1, fileEntry.getId(), operationId,
4253
fileEntry.getOperationId()));
4354
}
4455

multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/ValidateDeployParametersStepTest.java

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,14 @@
1111
import java.nio.file.Paths;
1212
import java.text.MessageFormat;
1313
import java.time.Duration;
14+
import java.util.List;
1415
import java.util.stream.Stream;
1516

1617
import org.cloudfoundry.multiapps.common.SLException;
1718
import org.cloudfoundry.multiapps.controller.core.util.ApplicationConfiguration;
1819
import org.cloudfoundry.multiapps.controller.persistence.model.FileEntry;
1920
import org.cloudfoundry.multiapps.controller.persistence.model.ImmutableFileEntry;
2021
import org.cloudfoundry.multiapps.controller.persistence.services.FileStorageException;
21-
import org.cloudfoundry.multiapps.controller.persistence.util.Configuration;
2222
import org.cloudfoundry.multiapps.controller.process.Messages;
2323
import org.cloudfoundry.multiapps.controller.process.util.FilePartsMerger;
2424
import org.cloudfoundry.multiapps.controller.process.variables.Variables;
@@ -35,6 +35,7 @@ class ValidateDeployParametersStepTest extends SyncFlowableStepTest<ValidateDepl
3535
private static final String EXISTING_BIGGER_FILE_ID = "existingBiggerFileId";
3636
private static final String NOT_EXISTING_FILE_ID = "notExistingFileId";
3737
private static final String MERGED_ARCHIVE_NAME = "test-merged";
38+
private static final String EXCEEDING_FILE_SIZE_ID = "exceedingFileSizeId";
3839
private static final String EXCEPTION_START_MESSAGE = "Error validating parameters: ";
3940

4041
private StepInput stepInput;
@@ -63,14 +64,23 @@ private static Stream<Arguments> testExecution() {
6364

6465
// [4] Process chunked file
6566
Arguments.of(new StepInput(MERGED_ARCHIVE_NAME + ".part.0," + MERGED_ARCHIVE_NAME + ".part.1,"
66-
+ MERGED_ARCHIVE_NAME + ".part.2", null, 1, VersionRule.HIGHER.toString()), null, true, ""));
67+
+ MERGED_ARCHIVE_NAME + ".part.2", null, 1, VersionRule.HIGHER.toString()), null, true, ""),
68+
69+
// [5] Max size of entries exceeded
70+
Arguments.of(new StepInput(EXCEEDING_FILE_SIZE_ID + ".part.0," + EXCEEDING_FILE_SIZE_ID + ".part.1,"
71+
+ EXCEEDING_FILE_SIZE_ID + ".part.2," + EXCEEDING_FILE_SIZE_ID + ".part.3," + EXCEEDING_FILE_SIZE_ID
72+
+ ".part.4", null, 1, VersionRule.HIGHER.toString()),
73+
MessageFormat.format(Messages.SIZE_OF_ALL_OPERATIONS_FILES_0_EXCEEDS_MAX_UPLOAD_SIZE_1, 5368709120L,
74+
4294967296L),
75+
true, ""));
6776
}
6877

6978
private static FileEntry createFileEntry(String id, String name, long size) {
7079
return ImmutableFileEntry.builder()
7180
.id(id)
7281
.name(name)
7382
.size(BigInteger.valueOf(size))
83+
.operationId("test")
7484
.build();
7585
}
7686

@@ -93,7 +103,7 @@ private void initializeComponents(StepInput stepInput, boolean isArchiveChunked)
93103
this.stepInput = stepInput;
94104
this.isArchiveChunked = isArchiveChunked;
95105
prepareContext();
96-
prepareFileService();
106+
prepareFileService(stepInput.appArchiveId);
97107
prepareArchiveMerger();
98108
prepareConfiguration();
99109
}
@@ -108,7 +118,7 @@ private void prepareContext() {
108118
context.setVariable(Variables.MTA_NAMESPACE, "namespace");
109119
}
110120

111-
private void prepareFileService() throws FileStorageException {
121+
private void prepareFileService(String appArchiveId) throws FileStorageException {
112122
Mockito.when(fileService.getFile("space-id", EXISTING_FILE_ID))
113123
.thenReturn(createFileEntry(EXISTING_FILE_ID, "some-file-entry-name", 1024 * 1024L));
114124
Mockito.when(fileService.getFile("space-id", MERGED_ARCHIVE_NAME + ".part.0"))
@@ -126,6 +136,20 @@ private void prepareFileService() throws FileStorageException {
126136
.thenReturn(null);
127137
Mockito.when(fileService.addFile(Mockito.any(FileEntry.class), Mockito.any(InputStream.class)))
128138
.thenReturn(createFileEntry(EXISTING_FILE_ID, MERGED_ARCHIVE_TEST_MTAR, 1024 * 1024 * 1024L));
139+
if (appArchiveId.contains(EXCEEDING_FILE_SIZE_ID)) {
140+
List<FileEntry> fileEntries = List.of(createFileEntry(EXCEEDING_FILE_SIZE_ID + ".part.0", EXCEEDING_FILE_SIZE_ID + ".part.0",
141+
1024 * 1024 * 1024),
142+
createFileEntry(EXCEEDING_FILE_SIZE_ID + ".part.1", EXCEEDING_FILE_SIZE_ID + ".part.1",
143+
1024 * 1024 * 1024),
144+
createFileEntry(EXCEEDING_FILE_SIZE_ID + ".part.2", EXCEEDING_FILE_SIZE_ID + ".part.2",
145+
1024 * 1024 * 1024),
146+
createFileEntry(EXCEEDING_FILE_SIZE_ID + ".part.3", EXCEEDING_FILE_SIZE_ID + ".part.3",
147+
1024 * 1024 * 1024),
148+
createFileEntry(EXCEEDING_FILE_SIZE_ID + ".part.4", EXCEEDING_FILE_SIZE_ID + ".part.4",
149+
1024 * 1024 * 1024));
150+
Mockito.when(fileService.listFilesBySpaceAndOperationId(Mockito.anyString(), Mockito.anyString()))
151+
.thenReturn(fileEntries);
152+
}
129153
}
130154

131155
private void prepareArchiveMerger() {
@@ -137,8 +161,8 @@ private void prepareArchiveMerger() {
137161
private void prepareConfiguration() {
138162
Mockito.when(configuration.getMaxMtaDescriptorSize())
139163
.thenReturn(ApplicationConfiguration.DEFAULT_MAX_MTA_DESCRIPTOR_SIZE);
140-
Mockito.when(configuration.getFileConfiguration())
141-
.thenReturn(new Configuration(ApplicationConfiguration.DEFAULT_MAX_UPLOAD_SIZE));
164+
Mockito.when(configuration.getMaxUploadSize())
165+
.thenReturn(ApplicationConfiguration.DEFAULT_MAX_UPLOAD_SIZE);
142166
}
143167

144168
private void validate() {

multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/util/OperationInFinalStateHandlerTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ private void prepareFileService(String fileIds, String fileOwnershipProcessId) t
146146
}
147147
for (String fileId : fileIds.split(",")) {
148148
FileEntry entry = ImmutableFileEntry.builder()
149-
.name(fileId)
149+
.id(fileId)
150150
.operationId(fileOwnershipProcessId)
151151
.build();
152152
Mockito.when(fileService.getFile(SPACE_ID, fileId))

multiapps-controller-web/src/main/java/org/cloudfoundry/multiapps/controller/web/api/impl/FilesApiServiceImpl.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@
5454
import org.cloudfoundry.multiapps.controller.persistence.services.AsyncUploadJobService;
5555
import org.cloudfoundry.multiapps.controller.persistence.services.FileService;
5656
import org.cloudfoundry.multiapps.controller.persistence.services.FileStorageException;
57-
import org.cloudfoundry.multiapps.controller.persistence.util.Configuration;
5857
import org.cloudfoundry.multiapps.controller.web.Constants;
5958
import org.cloudfoundry.multiapps.controller.web.Messages;
6059
import org.cloudfoundry.multiapps.controller.web.util.SecurityContextUtil;
@@ -372,7 +371,7 @@ private FileEntry doUploadFileFromUrl(String spaceGuid, String namespace, String
372371
.firstValueAsLong(Constants.CONTENT_LENGTH)
373372
.orElseThrow(() -> new SLException(Messages.FILE_URL_RESPONSE_DID_NOT_RETURN_CONTENT_LENGTH));
374373

375-
long maxUploadSize = new Configuration().getMaxUploadSize();
374+
long maxUploadSize = configuration.getMaxUploadSize();
376375
if (fileSize > maxUploadSize) {
377376
throw new SLException(MessageFormat.format(Messages.MAX_UPLOAD_SIZE_EXCEEDED, maxUploadSize));
378377
}

multiapps-controller-web/src/test/java/org/cloudfoundry/multiapps/controller/web/api/impl/FilesApiServiceImplTest.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@
4242
import org.cloudfoundry.multiapps.controller.persistence.services.AsyncUploadJobService;
4343
import org.cloudfoundry.multiapps.controller.persistence.services.FileService;
4444
import org.cloudfoundry.multiapps.controller.persistence.services.FileStorageException;
45-
import org.cloudfoundry.multiapps.controller.persistence.util.Configuration;
4645
import org.junit.jupiter.api.Assertions;
4746
import org.junit.jupiter.api.BeforeAll;
4847
import org.junit.jupiter.api.BeforeEach;
@@ -65,7 +64,6 @@
6564

6665
class FilesApiServiceImplTest {
6766

68-
private static final long MAX_PERMITTED_SIZE = new Configuration().getMaxUploadSize();
6967
private static final String MTA_ID = "anatz";
7068
private static final String FILE_URL = Base64.getUrlEncoder()
7169
.encodeToString("https://host.domain/test.mtar?query=true".getBytes(StandardCharsets.UTF_8));
@@ -100,7 +98,7 @@ protected ResilientOperationExecutor getResilientOperationExecutor() {
10098
@Mock
10199
private ExecutorService asyncFileUploadExecutor;
102100
@Mock
103-
private ApplicationConfiguration configuration = new ApplicationConfiguration();
101+
private ApplicationConfiguration configuration;
104102
@Spy
105103
private DescriptorParserFacadeFactory descriptorParserFactory = new DescriptorParserFacadeFactory(configuration);
106104
@Mock
@@ -222,6 +220,8 @@ void testUploadFileFromUrl() throws Exception {
222220
.thenReturn(fileEntry);
223221
Mockito.when(fileService.getFile(Mockito.eq(SPACE_GUID), Mockito.eq(fileEntry.getId())))
224222
.thenReturn(fileEntry);
223+
Mockito.when(configuration.getMaxUploadSize())
224+
.thenReturn(ApplicationConfiguration.DEFAULT_MAX_UPLOAD_SIZE);
225225
Future<?> future = Mockito.mock(Future.class);
226226
when(future.isDone()).thenReturn(true);
227227
prepareAsyncExecutor(future);
@@ -345,7 +345,7 @@ void testUploadFromUrlWhenThereIsValidExistingJob() {
345345

346346
@Test
347347
void testFileUrlReturnsContentLengthAboveMaxUploadSize() throws Exception {
348-
long invalidFileSize = MAX_PERMITTED_SIZE + 1024;
348+
long invalidFileSize = ApplicationConfiguration.DEFAULT_MAX_UPLOAD_SIZE + 1024;
349349
String fileSize = Long.toString(invalidFileSize);
350350
AsyncUploadJobsQuery query = Mockito.mock(AsyncUploadJobsQuery.class, Answers.RETURNS_SELF);
351351
String error = "content length exceeds max permitted size of 4GB";

0 commit comments

Comments
 (0)