From 80061b56a438ff4e6a01955ada10b2a1e7ea8b8a Mon Sep 17 00:00:00 2001
From: OZGCloud <ozgcloud@mgm-tp.com>
Date: Tue, 1 Mar 2022 08:57:02 +0100
Subject: [PATCH] OZG-1070 OZG-2158 validate file size by configured max file
 size for postfach nachricht

---
 .../goofy/common/ValidationMessageCodes.java  |  1 +
 .../binaryfile/BinaryFileController.java      | 12 ++---
 .../BinaryFileMaxSizeConstraint.java          | 25 ++++++++++
 .../binaryfile/BinaryFileProperties.java      | 21 ++++++++
 .../binaryfile/BinaryFileRemoteService.java   |  6 +--
 .../common/binaryfile/BinaryFileService.java  |  6 ++-
 .../binaryfile/UploadBinaryFileRequest.java   | 10 +++-
 .../UploadBinaryFileSizeValidator.java        | 25 ++++++++++
 .../src/main/resources/application.yml        |  7 +++
 .../binaryfile/BinaryFileControllerTest.java  | 49 +++++++-----------
 .../UploadBinaryFileSizeValidatorTest.java    | 50 +++++++++++++++++++
 .../UploadBinaryFileTestFactory.java          | 11 ++--
 12 files changed, 171 insertions(+), 52 deletions(-)
 create mode 100644 goofy-server/src/main/java/de/itvsh/goofy/common/binaryfile/BinaryFileMaxSizeConstraint.java
 create mode 100644 goofy-server/src/main/java/de/itvsh/goofy/common/binaryfile/BinaryFileProperties.java
 create mode 100644 goofy-server/src/main/java/de/itvsh/goofy/common/binaryfile/UploadBinaryFileSizeValidator.java
 create mode 100644 goofy-server/src/test/java/de/itvsh/goofy/common/binaryfile/UploadBinaryFileSizeValidatorTest.java

diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/ValidationMessageCodes.java b/goofy-server/src/main/java/de/itvsh/goofy/common/ValidationMessageCodes.java
index 58616c47f8..e48ce06cd9 100644
--- a/goofy-server/src/main/java/de/itvsh/goofy/common/ValidationMessageCodes.java
+++ b/goofy-server/src/main/java/de/itvsh/goofy/common/ValidationMessageCodes.java
@@ -11,4 +11,5 @@ public class ValidationMessageCodes {
 	public static final String FIELD_SIZE = FIELD_PREFIX + "size";
 	public static final String FIELD_DATE_PAST = FIELD_PREFIX + "date_past";
 	public static final String FIELD_INVALID = FIELD_PREFIX + "invalid";
+	public static final String FIELD_FILE_SIZE_EXCEEDED = FIELD_PREFIX + "file_size_exceeded";
 }
\ No newline at end of file
diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/binaryfile/BinaryFileController.java b/goofy-server/src/main/java/de/itvsh/goofy/common/binaryfile/BinaryFileController.java
index 9766d370ea..cf573e9ad5 100644
--- a/goofy-server/src/main/java/de/itvsh/goofy/common/binaryfile/BinaryFileController.java
+++ b/goofy-server/src/main/java/de/itvsh/goofy/common/binaryfile/BinaryFileController.java
@@ -76,8 +76,10 @@ public class BinaryFileController {
 		return UploadBinaryFileRequest.builder()
 				.vorgangId(vorgangId)
 				.field(field)
-				.ozgFile(buildOzgFile(file))
 				.uploadStream(getInputStream(file))
+				.name(file.getOriginalFilename())
+				.contentType(file.getContentType())
+				.size(file.getSize())
 				.build();
 	}
 
@@ -89,14 +91,6 @@ public class BinaryFileController {
 		}
 	}
 
-	OzgFile buildOzgFile(MultipartFile file) {
-		return OzgFile.builder()
-				.name(file.getOriginalFilename())
-				.contentType(file.getContentType())
-				.size(file.getSize())
-				.build();
-	}
-
 	public CollectionModel<EntityModel<OzgFile>> getFiles(List<FileId> fileIds) {
 		var files = service.getFiles(mapToFileId(fileIds)).toList();
 
diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/binaryfile/BinaryFileMaxSizeConstraint.java b/goofy-server/src/main/java/de/itvsh/goofy/common/binaryfile/BinaryFileMaxSizeConstraint.java
new file mode 100644
index 0000000000..6d6cebd3de
--- /dev/null
+++ b/goofy-server/src/main/java/de/itvsh/goofy/common/binaryfile/BinaryFileMaxSizeConstraint.java
@@ -0,0 +1,25 @@
+package de.itvsh.goofy.common.binaryfile;
+
+import static java.lang.annotation.ElementType.*;
+import static java.lang.annotation.RetentionPolicy.*;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import javax.validation.Constraint;
+import javax.validation.Payload;
+
+import de.itvsh.goofy.common.ValidationMessageCodes;
+
+@Constraint(validatedBy = UploadBinaryFileSizeValidator.class)
+@Target({ TYPE })
+@Retention(RUNTIME)
+@Documented
+public @interface BinaryFileMaxSizeConstraint {
+	String message() default ValidationMessageCodes.FIELD_FILE_SIZE_EXCEEDED;
+
+	Class<?>[] groups() default {};
+
+	Class<? extends Payload>[] payload() default {};
+}
\ No newline at end of file
diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/binaryfile/BinaryFileProperties.java b/goofy-server/src/main/java/de/itvsh/goofy/common/binaryfile/BinaryFileProperties.java
new file mode 100644
index 0000000000..4d862539d7
--- /dev/null
+++ b/goofy-server/src/main/java/de/itvsh/goofy/common/binaryfile/BinaryFileProperties.java
@@ -0,0 +1,21 @@
+package de.itvsh.goofy.common.binaryfile;
+
+import java.util.Map;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+@Configuration
+@ConfigurationProperties(BinaryFileProperties.PREFIX)
+class BinaryFileProperties {
+
+	static final String PREFIX = "goofy.upload";
+
+	private Map<String, Integer> maxFileSize;
+
+}
\ No newline at end of file
diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/binaryfile/BinaryFileRemoteService.java b/goofy-server/src/main/java/de/itvsh/goofy/common/binaryfile/BinaryFileRemoteService.java
index 276becbcdd..ecce387c15 100644
--- a/goofy-server/src/main/java/de/itvsh/goofy/common/binaryfile/BinaryFileRemoteService.java
+++ b/goofy-server/src/main/java/de/itvsh/goofy/common/binaryfile/BinaryFileRemoteService.java
@@ -115,9 +115,9 @@ class BinaryFileRemoteService {
 						.setContext(contextService.createCallContext())
 						.setVorgangId(uploadBinaryFileRequest.getVorgangId())
 						.setField(uploadBinaryFileRequest.getField())
-						.setContentType(uploadBinaryFileRequest.getOzgFile().getContentType())
-						.setSize(uploadBinaryFileRequest.getOzgFile().getSize())
-						.setFileName(uploadBinaryFileRequest.getOzgFile().getName()))
+						.setContentType(uploadBinaryFileRequest.getContentType())
+						.setSize(uploadBinaryFileRequest.getSize())
+						.setFileName(uploadBinaryFileRequest.getName()))
 				.build();
 	}
 
diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/binaryfile/BinaryFileService.java b/goofy-server/src/main/java/de/itvsh/goofy/common/binaryfile/BinaryFileService.java
index df88413348..33f67ca1c7 100644
--- a/goofy-server/src/main/java/de/itvsh/goofy/common/binaryfile/BinaryFileService.java
+++ b/goofy-server/src/main/java/de/itvsh/goofy/common/binaryfile/BinaryFileService.java
@@ -5,13 +5,17 @@ import java.util.List;
 import java.util.concurrent.CompletableFuture;
 import java.util.stream.Stream;
 
+import javax.validation.Valid;
+
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
+import org.springframework.validation.annotation.Validated;
 
 import de.itvsh.goofy.common.file.OzgFile;
 import de.itvsh.goofy.common.file.OzgFileData;
 
 @Service
+@Validated
 class BinaryFileService {
 
 	@Autowired
@@ -22,7 +26,7 @@ class BinaryFileService {
 		return remoteService.uploadFile(vorgangId, fieldName, file);
 	}
 
-	public CompletableFuture<FileId> uploadFile(UploadBinaryFileRequest uploadBinaryFileRequest) {
+	public CompletableFuture<FileId> uploadFile(@Valid UploadBinaryFileRequest uploadBinaryFileRequest) {
 		return remoteService.uploadFile(uploadBinaryFileRequest);
 	}
 
diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/binaryfile/UploadBinaryFileRequest.java b/goofy-server/src/main/java/de/itvsh/goofy/common/binaryfile/UploadBinaryFileRequest.java
index 165aa46828..9c6eeddfca 100644
--- a/goofy-server/src/main/java/de/itvsh/goofy/common/binaryfile/UploadBinaryFileRequest.java
+++ b/goofy-server/src/main/java/de/itvsh/goofy/common/binaryfile/UploadBinaryFileRequest.java
@@ -2,16 +2,22 @@ package de.itvsh.goofy.common.binaryfile;
 
 import java.io.InputStream;
 
-import de.itvsh.goofy.common.file.OzgFile;
+import org.springframework.validation.annotation.Validated;
+
 import lombok.Builder;
 import lombok.Getter;
 
 @Getter
 @Builder
+@Validated
+@BinaryFileMaxSizeConstraint
 class UploadBinaryFileRequest {
 
 	private String vorgangId;
 	private String field;
-	private OzgFile ozgFile;
+
+	private String name;
+	private String contentType;
+	private long size;
 	private InputStream uploadStream;
 }
diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/binaryfile/UploadBinaryFileSizeValidator.java b/goofy-server/src/main/java/de/itvsh/goofy/common/binaryfile/UploadBinaryFileSizeValidator.java
new file mode 100644
index 0000000000..32555e8296
--- /dev/null
+++ b/goofy-server/src/main/java/de/itvsh/goofy/common/binaryfile/UploadBinaryFileSizeValidator.java
@@ -0,0 +1,25 @@
+package de.itvsh.goofy.common.binaryfile;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+
+import org.springframework.beans.factory.annotation.Autowired;
+
+class UploadBinaryFileSizeValidator implements ConstraintValidator<BinaryFileMaxSizeConstraint, UploadBinaryFileRequest> {
+
+	@Autowired
+	private BinaryFileProperties binaryFileProperties;
+
+	@Override
+	public boolean isValid(UploadBinaryFileRequest uploadRequest, ConstraintValidatorContext context) {
+		return uploadRequest.getSize() <= toMegabyte(getMaxValidSize(uploadRequest.getField()));
+	}
+
+	private long toMegabyte(Integer byteValue) {
+		return byteValue.longValue() * 1024 * 1024;
+	}
+
+	private Integer getMaxValidSize(String field) {
+		return binaryFileProperties.getMaxFileSize().get(field);
+	}
+}
\ No newline at end of file
diff --git a/goofy-server/src/main/resources/application.yml b/goofy-server/src/main/resources/application.yml
index d940267afa..e1d85b5e7b 100644
--- a/goofy-server/src/main/resources/application.yml
+++ b/goofy-server/src/main/resources/application.yml
@@ -14,6 +14,10 @@ spring:
   jackson:
     deserialization: 
       'ADJUST_DATES_TO_CONTEXT_TIME_ZONE': false
+  servlet:
+    multipart:
+      max-file-size: 40MB
+      max-request-size: 40MB
 
 server:
   http2:
@@ -48,6 +52,9 @@ goofy:
     api:
       user: goofyApiUser
       password:  
+  upload:
+    maxFileSize:
+      postfachNachrichtAttachment: 40
       
 keycloak:
   auth-server-url: http://localhost:8088/auth 
diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/binaryfile/BinaryFileControllerTest.java b/goofy-server/src/test/java/de/itvsh/goofy/common/binaryfile/BinaryFileControllerTest.java
index 396be3d268..f09961d401 100644
--- a/goofy-server/src/test/java/de/itvsh/goofy/common/binaryfile/BinaryFileControllerTest.java
+++ b/goofy-server/src/test/java/de/itvsh/goofy/common/binaryfile/BinaryFileControllerTest.java
@@ -131,7 +131,7 @@ class BinaryFileControllerTest {
 	}
 
 	@Nested
-	class TestBuildBinaryFileUpload {
+	class TestBuildBinaryFileUploadRequest {
 
 		@Test
 		void shouldContainsVorgangId() {
@@ -148,10 +148,24 @@ class BinaryFileControllerTest {
 		}
 
 		@Test
-		void shouldContainsOzgFile() {
+		void shouldContainsSize() {
+			var result = buildBinaryFileUploadRequest();
+
+			assertThat(result.getSize()).isEqualTo(OzgFileTestFactory.SIZE);
+		}
+
+		@Test
+		void shouldContainsName() {
+			var result = buildBinaryFileUploadRequest();
+
+			assertThat(result.getName()).isEqualTo(OzgFileTestFactory.NAME);
+		}
+
+		@Test
+		void shouldContainsContentType() {
 			var result = buildBinaryFileUploadRequest();
 
-			assertThat(result.getOzgFile()).usingRecursiveComparison().ignoringFields("id").isEqualTo(UploadBinaryFileTestFactory.OZG_FILE);
+			assertThat(result.getContentType()).isEqualTo(OzgFileTestFactory.CONTENT_TYPE);
 		}
 
 		@Test
@@ -189,35 +203,6 @@ class BinaryFileControllerTest {
 		}
 	}
 
-	@Nested
-	class TestBuildOzgFile {
-
-		@Test
-		void shouldContainsOriginalFilename() {
-			var ozgFile = callBuildOzgFile();
-
-			assertThat(ozgFile.getName()).isEqualTo(BinaryFileTestFactory.NAME);
-		}
-
-		@Test
-		void shouldContainsContentType() {
-			var ozgFile = callBuildOzgFile();
-
-			assertThat(ozgFile.getContentType()).isEqualTo(BinaryFileTestFactory.CONTENT_TYPE);
-		}
-
-		@Test
-		void shouldContainsSize() {
-			var ozgFile = callBuildOzgFile();
-
-			assertThat(ozgFile.getSize()).isEqualTo(BinaryFileTestFactory.SIZE);
-		}
-
-		private OzgFile callBuildOzgFile() {
-			return controller.buildOzgFile(BinaryFileTestFactory.TEST_FILE);
-		}
-	}
-
 	@Nested
 	class TestGetFile {
 
diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/binaryfile/UploadBinaryFileSizeValidatorTest.java b/goofy-server/src/test/java/de/itvsh/goofy/common/binaryfile/UploadBinaryFileSizeValidatorTest.java
new file mode 100644
index 0000000000..0288f9a48e
--- /dev/null
+++ b/goofy-server/src/test/java/de/itvsh/goofy/common/binaryfile/UploadBinaryFileSizeValidatorTest.java
@@ -0,0 +1,50 @@
+package de.itvsh.goofy.common.binaryfile;
+
+import static org.assertj.core.api.Assertions.*;
+import static org.mockito.Mockito.*;
+
+import java.util.Map;
+
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+
+class UploadBinaryFileSizeValidatorTest {
+
+	@InjectMocks
+	private UploadBinaryFileSizeValidator validator = new UploadBinaryFileSizeValidator();
+	@Mock
+	private BinaryFileProperties properties;
+
+	@Nested
+	class TestIsValid {
+
+		@Test
+		void shouldCallProperties() {
+			when(properties.getMaxFileSize()).thenReturn(Map.of(UploadBinaryFileTestFactory.FIELD, 1));
+
+			validator.isValid(UploadBinaryFileTestFactory.create(), null);
+
+			verify(properties).getMaxFileSize();
+		}
+
+		@Test
+		void shouldReturnFalse() {
+			when(properties.getMaxFileSize()).thenReturn(Map.of(UploadBinaryFileTestFactory.FIELD, 9));
+
+			var isValid = validator.isValid(UploadBinaryFileTestFactory.createBuilder().size(10 * 1024 * 1024).build(), null);
+
+			assertThat(isValid).isFalse();
+		}
+
+		@Test
+		void shouldReturnTrue() {
+			when(properties.getMaxFileSize()).thenReturn(Map.of(UploadBinaryFileTestFactory.FIELD, 10));
+
+			var isValid = validator.isValid(UploadBinaryFileTestFactory.createBuilder().size(10 * 1024 * 1024).build(), null);
+
+			assertThat(isValid).isTrue();
+		}
+	}
+}
diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/binaryfile/UploadBinaryFileTestFactory.java b/goofy-server/src/test/java/de/itvsh/goofy/common/binaryfile/UploadBinaryFileTestFactory.java
index 2842854536..4a4262bae0 100644
--- a/goofy-server/src/test/java/de/itvsh/goofy/common/binaryfile/UploadBinaryFileTestFactory.java
+++ b/goofy-server/src/test/java/de/itvsh/goofy/common/binaryfile/UploadBinaryFileTestFactory.java
@@ -1,16 +1,15 @@
 package de.itvsh.goofy.common.binaryfile;
 
+import static de.itvsh.goofy.common.file.OzgFileTestFactory.*;
+
 import java.io.InputStream;
 
-import de.itvsh.goofy.common.file.OzgFile;
-import de.itvsh.goofy.common.file.OzgFileTestFactory;
 import de.itvsh.goofy.vorgang.VorgangHeaderTestFactory;
 
 public class UploadBinaryFileTestFactory {
 
 	static final String VORGANG_ID = VorgangHeaderTestFactory.ID;
 	static final String FIELD = BinaryFileTestFactory.FIELD;
-	static final OzgFile OZG_FILE = OzgFileTestFactory.create();
 	static final InputStream UPLOAD_STREAM = BinaryFileTestFactory.STREAM;
 
 	public static UploadBinaryFileRequest create() {
@@ -19,9 +18,11 @@ public class UploadBinaryFileTestFactory {
 
 	public static UploadBinaryFileRequest.UploadBinaryFileRequestBuilder createBuilder() {
 		return UploadBinaryFileRequest.builder()
+				.name(NAME)
 				.vorgangId(VORGANG_ID)
 				.field(FIELD)
-				.ozgFile(OZG_FILE)
+				.size(SIZE)
+				.contentType(CONTENT_TYPE)
 				.uploadStream(UPLOAD_STREAM);
 	}
-}
+}
\ No newline at end of file
-- 
GitLab