From 0f55086d956c95ff0cd1c732feeeddfc9811f242 Mon Sep 17 00:00:00 2001 From: hill-daniel <20245376+hill-daniel@users.noreply.github.com> Date: Mon, 28 Feb 2022 15:05:19 +0100 Subject: [PATCH 1/3] feat: detect mime type of uploaded file * former check relied on rocket chat, which just checks file extension. Can introduce security issues. --- pom.xml | 7 +++ .../UploadServiceApplication.java | 8 +++ .../exception/InvalidFileTypeException.java | 10 ++- .../api/facade/UploadFacade.java | 7 ++- .../api/service/FileService.java | 41 +++++++++++++ .../uploadservice/media/MimeTypeDetector.java | 10 +++ .../media/TikaMimeTypeDetector.java | 28 +++++++++ src/main/resources/application.properties | 3 + .../api/facade/TestMultipartFile.java | 50 +++++++++++++++ .../api/facade/UploadFacadeTest.java | 11 +++- .../api/service/FileServiceTest.java | 61 +++++++++++++++++++ 11 files changed, 231 insertions(+), 5 deletions(-) create mode 100644 src/main/java/de/caritas/cob/uploadservice/api/service/FileService.java create mode 100644 src/main/java/de/caritas/cob/uploadservice/media/MimeTypeDetector.java create mode 100644 src/main/java/de/caritas/cob/uploadservice/media/TikaMimeTypeDetector.java create mode 100644 src/test/java/de/caritas/cob/uploadservice/api/facade/TestMultipartFile.java create mode 100644 src/test/java/de/caritas/cob/uploadservice/api/service/FileServiceTest.java diff --git a/pom.xml b/pom.xml index df0aa5d..077eaef 100644 --- a/pom.xml +++ b/pom.xml @@ -155,6 +155,13 @@ ${log4j.version} + + + org.apache.tika + tika-core + 2.3.0 + + com.h2database diff --git a/src/main/java/de/caritas/cob/uploadservice/UploadServiceApplication.java b/src/main/java/de/caritas/cob/uploadservice/UploadServiceApplication.java index 1fc483f..43ee952 100644 --- a/src/main/java/de/caritas/cob/uploadservice/UploadServiceApplication.java +++ b/src/main/java/de/caritas/cob/uploadservice/UploadServiceApplication.java @@ -4,11 +4,14 @@ import de.caritas.cob.uploadservice.api.exception.KeycloakException; import de.caritas.cob.uploadservice.api.helper.AuthenticatedUser; +import de.caritas.cob.uploadservice.media.MimeTypeDetector; +import de.caritas.cob.uploadservice.media.TikaMimeTypeDetector; import java.util.Map; import java.util.Set; import java.util.concurrent.Executor; import java.util.stream.Collectors; import javax.servlet.http.HttpServletRequest; +import org.apache.tika.Tika; import org.keycloak.KeycloakSecurityContext; import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken; import org.keycloak.representations.AccessToken; @@ -166,4 +169,9 @@ public Executor taskExecutor() { public MultipartResolver multipartResolver() { return new StandardServletMultipartResolver(); } + + @Bean + public MimeTypeDetector mimeTypeDetector() { + return new TikaMimeTypeDetector(new Tika()); + } } diff --git a/src/main/java/de/caritas/cob/uploadservice/api/exception/InvalidFileTypeException.java b/src/main/java/de/caritas/cob/uploadservice/api/exception/InvalidFileTypeException.java index f75205c..a82aabd 100644 --- a/src/main/java/de/caritas/cob/uploadservice/api/exception/InvalidFileTypeException.java +++ b/src/main/java/de/caritas/cob/uploadservice/api/exception/InvalidFileTypeException.java @@ -2,13 +2,19 @@ import org.springframework.web.multipart.MultipartException; -/** Exception for wrong file type (upload) */ +/** + * Exception for wrong file type (upload) + */ public class InvalidFileTypeException extends MultipartException { + public InvalidFileTypeException(String msg) { + super(msg); + } + /** * Exception for wrong file type (upload). * - * @param msg String + * @param msg String * @param cause Throwable */ public InvalidFileTypeException(String msg, Throwable cause) { diff --git a/src/main/java/de/caritas/cob/uploadservice/api/facade/UploadFacade.java b/src/main/java/de/caritas/cob/uploadservice/api/facade/UploadFacade.java index be1a04e..7256ae2 100644 --- a/src/main/java/de/caritas/cob/uploadservice/api/facade/UploadFacade.java +++ b/src/main/java/de/caritas/cob/uploadservice/api/facade/UploadFacade.java @@ -9,6 +9,7 @@ import de.caritas.cob.uploadservice.api.helper.AuthenticatedUserHelper; import de.caritas.cob.uploadservice.api.helper.RocketChatUploadParameterEncrypter; import de.caritas.cob.uploadservice.api.helper.RocketChatUploadParameterSanitizer; +import de.caritas.cob.uploadservice.api.service.FileService; import de.caritas.cob.uploadservice.api.service.LiveEventNotificationService; import de.caritas.cob.uploadservice.api.service.LogService; import de.caritas.cob.uploadservice.api.service.RocketChatService; @@ -35,6 +36,7 @@ public class UploadFacade { private final @NonNull UploadTrackingService uploadTrackingService; private final @NonNull StatisticsService statisticsService; private final @NonNull AuthenticatedUser authenticatedUser; + private final @NonNull FileService fileService; /** * Upload a file with a message to a Rocket.Chat room. The message and the description are @@ -43,7 +45,7 @@ public class UploadFacade { *

If the statistics function is enabled, the assignment of the enquired is processed as * statistical event. * - * @param rocketChatCredentials {@link RocketChatCredentials} container + * @param rocketChatCredentials {@link RocketChatCredentials} container * @param rocketChatUploadParameter {@link RocketChatUploadParameter} container */ public void uploadFileToRoom( @@ -80,7 +82,7 @@ private UserRole resolveUserRole(AuthenticatedUser authenticatedUser) { * Upload a file with a message to a Rocket.Chat feedback room. The message and the description * are encrypted before it is sent to Rocket.Chat. * - * @param rocketChatCredentials {@link RocketChatCredentials} container + * @param rocketChatCredentials {@link RocketChatCredentials} container * @param rocketChatUploadParameter {@link RocketChatUploadParameter} container */ public void uploadFileToFeedbackRoom( @@ -104,6 +106,7 @@ private void sanitizeAndEncryptParametersAndUploadToRocketChatRoom( RocketChatUploadParameter rocketChatUploadParameter) { rocketChatUploadParameterSanitizer.sanitize(rocketChatUploadParameter); + fileService.verifyMimeType(rocketChatUploadParameter.getFile()); RocketChatUploadParameter encryptedRocketChatUploadParameter; try { encryptedRocketChatUploadParameter = diff --git a/src/main/java/de/caritas/cob/uploadservice/api/service/FileService.java b/src/main/java/de/caritas/cob/uploadservice/api/service/FileService.java new file mode 100644 index 0000000..4be820b --- /dev/null +++ b/src/main/java/de/caritas/cob/uploadservice/api/service/FileService.java @@ -0,0 +1,41 @@ +package de.caritas.cob.uploadservice.api.service; + +import static java.util.Objects.isNull; + +import de.caritas.cob.uploadservice.api.exception.InvalidFileTypeException; +import de.caritas.cob.uploadservice.api.exception.httpresponses.InternalServerErrorException; +import de.caritas.cob.uploadservice.media.MimeTypeDetector; +import java.util.Optional; +import java.util.Set; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +@RequiredArgsConstructor +@Service +public class FileService { + + private final MimeTypeDetector mimeTypeDetector; + @Value("#{${mimeTypeWhitelist}}") + private final Set mimeTypeWhitelist; + + public void verifyMimeType(MultipartFile multipartFile) { + if (isNull(multipartFile)) { + return; + } + + Optional mimeType; + try { + mimeType = mimeTypeDetector.detect(multipartFile.getInputStream()); + } catch (Exception e) { + throw new InternalServerErrorException( + "failed to detect mime type of file " + multipartFile.getOriginalFilename(), e, + LogService::logInternalServerError); + } + if (mimeType.isEmpty() || !mimeTypeWhitelist.contains(mimeType.get())) { + throw new InvalidFileTypeException( + "invalid mime type " + mimeType + " of file " + multipartFile.getOriginalFilename()); + } + } +} diff --git a/src/main/java/de/caritas/cob/uploadservice/media/MimeTypeDetector.java b/src/main/java/de/caritas/cob/uploadservice/media/MimeTypeDetector.java new file mode 100644 index 0000000..6910258 --- /dev/null +++ b/src/main/java/de/caritas/cob/uploadservice/media/MimeTypeDetector.java @@ -0,0 +1,10 @@ +package de.caritas.cob.uploadservice.media; + +import java.io.InputStream; +import java.util.Optional; + +public interface MimeTypeDetector { + + Optional detect(InputStream input); + +} diff --git a/src/main/java/de/caritas/cob/uploadservice/media/TikaMimeTypeDetector.java b/src/main/java/de/caritas/cob/uploadservice/media/TikaMimeTypeDetector.java new file mode 100644 index 0000000..3072e63 --- /dev/null +++ b/src/main/java/de/caritas/cob/uploadservice/media/TikaMimeTypeDetector.java @@ -0,0 +1,28 @@ +package de.caritas.cob.uploadservice.media; + +import static java.util.Objects.isNull; + +import java.io.InputStream; +import java.util.Optional; +import org.apache.tika.Tika; + +public class TikaMimeTypeDetector implements MimeTypeDetector { + + private final Tika tika; + + public TikaMimeTypeDetector(Tika tika) { + this.tika = tika; + } + + @Override + public Optional detect(InputStream input) { + if (isNull(input)) { + return Optional.empty(); + } + try { + return Optional.ofNullable(tika.detect(input)); + } catch (Exception e) { + throw new RuntimeException("failed to detect media type from input stream", e); + } + } +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 65ee9bb..d18ff02 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -84,3 +84,6 @@ spring.rabbitmq.password=admin # Statistics statistics.enabled=false statistics.rabbitmq.exchange.name=statistics.topic + +# MIME type whitelist for file upload. Supports png, jpeg, doc, docx and pdf. +mimeTypeWhitelist={'image/png','image/jpeg','application/msword','application/vnd.openxmlformats-officedocument.wordprocessingml.document','application/pdf'} \ No newline at end of file diff --git a/src/test/java/de/caritas/cob/uploadservice/api/facade/TestMultipartFile.java b/src/test/java/de/caritas/cob/uploadservice/api/facade/TestMultipartFile.java new file mode 100644 index 0000000..b98d31f --- /dev/null +++ b/src/test/java/de/caritas/cob/uploadservice/api/facade/TestMultipartFile.java @@ -0,0 +1,50 @@ +package de.caritas.cob.uploadservice.api.facade; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import org.springframework.web.multipart.MultipartFile; + +public class TestMultipartFile implements MultipartFile { + + @Override + public String getName() { + return null; + } + + @Override + public String getOriginalFilename() { + return null; + } + + @Override + public String getContentType() { + return null; + } + + @Override + public boolean isEmpty() { + return false; + } + + @Override + public long getSize() { + return 0; + } + + @Override + public byte[] getBytes() throws IOException { + return new byte[0]; + } + + @Override + public InputStream getInputStream() throws IOException { + return new ByteArrayInputStream(getBytes()); + } + + @Override + public void transferTo(File file) throws IOException, IllegalStateException { + + } +} diff --git a/src/test/java/de/caritas/cob/uploadservice/api/facade/UploadFacadeTest.java b/src/test/java/de/caritas/cob/uploadservice/api/facade/UploadFacadeTest.java index 466da9c..e61eec9 100644 --- a/src/test/java/de/caritas/cob/uploadservice/api/facade/UploadFacadeTest.java +++ b/src/test/java/de/caritas/cob/uploadservice/api/facade/UploadFacadeTest.java @@ -5,7 +5,6 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; @@ -21,6 +20,7 @@ import de.caritas.cob.uploadservice.api.helper.AuthenticatedUser; import de.caritas.cob.uploadservice.api.helper.RocketChatUploadParameterEncrypter; import de.caritas.cob.uploadservice.api.helper.RocketChatUploadParameterSanitizer; +import de.caritas.cob.uploadservice.api.service.FileService; import de.caritas.cob.uploadservice.api.service.LiveEventNotificationService; import de.caritas.cob.uploadservice.api.service.RocketChatService; import de.caritas.cob.uploadservice.api.service.UploadTrackingService; @@ -75,6 +75,11 @@ public class UploadFacadeTest { @Mock private AuthenticatedUser authenticatedUser; + @Mock + private FileService fileService; + + private TestMultipartFile multipartFile; + /** * Method: uploadFileToRoom */ @@ -85,6 +90,8 @@ public void setup() throws CustomCryptoException { .thenReturn(rocketChatUploadParameter); when(rocketChatUploadParameterEncrypter.encrypt(rocketChatUploadParameter)) .thenReturn(rocketChatUploadParameter); + multipartFile = new TestMultipartFile(); + when(rocketChatUploadParameter.getFile()).thenReturn(multipartFile); when(authenticatedUser.getRoles()) .thenReturn(SetUtils.unmodifiableSet(UserRole.CONSULTANT.getValue())); when(authenticatedUser.getUserId()) @@ -105,6 +112,7 @@ public void uploadFileToRoom_Should_UseServicesCorrectly_WhenNoExceptionIsThrown rocketChatUploadParameter.getRoomId()); verify(uploadTrackingService, times(1)).validateUploadLimit(any()); verify(uploadTrackingService, times(1)).trackUploadedFileForUser(any()); + verify(fileService).verifyMimeType(multipartFile); } @Test @@ -220,6 +228,7 @@ public void uploadFileToFeedbackRoom_Should_CallServicesCorrectly_WhenNoExceptio rocketChatUploadParameter.getRoomId()); verify(uploadTrackingService, times(1)).validateUploadLimit(any()); verify(uploadTrackingService, times(1)).trackUploadedFileForUser(any()); + verify(fileService).verifyMimeType(multipartFile); } @Test diff --git a/src/test/java/de/caritas/cob/uploadservice/api/service/FileServiceTest.java b/src/test/java/de/caritas/cob/uploadservice/api/service/FileServiceTest.java new file mode 100644 index 0000000..99f6c18 --- /dev/null +++ b/src/test/java/de/caritas/cob/uploadservice/api/service/FileServiceTest.java @@ -0,0 +1,61 @@ +package de.caritas.cob.uploadservice.api.service; + + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import de.caritas.cob.uploadservice.api.exception.InvalidFileTypeException; +import de.caritas.cob.uploadservice.api.exception.httpresponses.InternalServerErrorException; +import de.caritas.cob.uploadservice.api.facade.TestMultipartFile; +import de.caritas.cob.uploadservice.media.MimeTypeDetector; +import java.util.Optional; +import java.util.Set; +import org.junit.Before; +import org.junit.Test; + +public class FileServiceTest { + + private MimeTypeDetector mimeTypeDetector; + private FileService fileService; + + @Before + public void setUp() throws Exception { + mimeTypeDetector = mock(MimeTypeDetector.class); + fileService = new FileService(mimeTypeDetector, Set.of("application/jpeg")); + } + + @Test + public void verifyMimeType_should_verify_that_mime_type_is_whitelisted() { + when(mimeTypeDetector.detect(any())).thenReturn(Optional.of("application/jpeg")); + + fileService.verifyMimeType(new TestMultipartFile()); + } + + @Test(expected = InvalidFileTypeException.class) + public void verifyMimeType_should_fail_when_mime_type_of_file_is_not_whitelisted() { + when(mimeTypeDetector.detect(any())).thenReturn(Optional.of("application/octet-stream")); + + fileService.verifyMimeType(new TestMultipartFile()); + } + + @Test(expected = InvalidFileTypeException.class) + public void verifyMimeType_should_fail_when_mime_type_is_empty() { + when(mimeTypeDetector.detect(any())).thenReturn(Optional.empty()); + + fileService.verifyMimeType(new TestMultipartFile()); + } + + @Test(expected = InternalServerErrorException.class) + public void verifyMimeType_should_fail_if_input_reading_fails() { + doThrow(RuntimeException.class).when(mimeTypeDetector).detect(any()); + + fileService.verifyMimeType(new TestMultipartFile()); + } + + @Test + public void verifyMimeType_should_do_nothing_if_file_is_null() { + fileService.verifyMimeType(null); + } +} \ No newline at end of file From 2bca0c47d1358428d37eabd4d3672027d13a6470 Mon Sep 17 00:00:00 2001 From: hill-daniel <20245376+hill-daniel@users.noreply.github.com> Date: Mon, 28 Feb 2022 18:59:45 +0100 Subject: [PATCH 2/3] fix: use kebab case for property, add integration test for invalid media type --- .../api/service/FileService.java | 2 +- src/main/resources/application.properties | 2 +- .../controller/UploadControllerTestIT.java | 239 ++++++++++-------- 3 files changed, 138 insertions(+), 105 deletions(-) diff --git a/src/main/java/de/caritas/cob/uploadservice/api/service/FileService.java b/src/main/java/de/caritas/cob/uploadservice/api/service/FileService.java index 4be820b..d8fd887 100644 --- a/src/main/java/de/caritas/cob/uploadservice/api/service/FileService.java +++ b/src/main/java/de/caritas/cob/uploadservice/api/service/FileService.java @@ -17,7 +17,7 @@ public class FileService { private final MimeTypeDetector mimeTypeDetector; - @Value("#{${mimeTypeWhitelist}}") + @Value("#{${mime-type-whitelist}}") private final Set mimeTypeWhitelist; public void verifyMimeType(MultipartFile multipartFile) { diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index d18ff02..02c6cb1 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -86,4 +86,4 @@ statistics.enabled=false statistics.rabbitmq.exchange.name=statistics.topic # MIME type whitelist for file upload. Supports png, jpeg, doc, docx and pdf. -mimeTypeWhitelist={'image/png','image/jpeg','application/msword','application/vnd.openxmlformats-officedocument.wordprocessingml.document','application/pdf'} \ No newline at end of file +mime-type-whitelist={'image/png','image/jpeg','application/msword','application/vnd.openxmlformats-officedocument.wordprocessingml.document','application/pdf'} \ No newline at end of file diff --git a/src/test/java/de/caritas/cob/uploadservice/api/controller/UploadControllerTestIT.java b/src/test/java/de/caritas/cob/uploadservice/api/controller/UploadControllerTestIT.java index 9415569..368b52b 100644 --- a/src/test/java/de/caritas/cob/uploadservice/api/controller/UploadControllerTestIT.java +++ b/src/test/java/de/caritas/cob/uploadservice/api/controller/UploadControllerTestIT.java @@ -29,11 +29,10 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; import de.caritas.cob.uploadservice.api.authorization.RoleAuthorizationAuthorityMapper; import de.caritas.cob.uploadservice.api.container.RocketChatCredentials; import de.caritas.cob.uploadservice.api.container.RocketChatUploadParameter; +import de.caritas.cob.uploadservice.api.exception.InvalidFileTypeException; import de.caritas.cob.uploadservice.api.exception.httpresponses.QuotaReachedException; import de.caritas.cob.uploadservice.api.facade.UploadFacade; import de.caritas.cob.uploadservice.api.service.EncryptionService; @@ -81,15 +80,15 @@ public void uploadFileToRoom_Should_ReturnNotFound_WhenRoomIdIsMissing() throws MockPart fileToUpload = new MockPart(FORM_PARAM_FILE, "fileToUpload", "content".getBytes()); mvc.perform( - multipart(PATH_UPLOAD_FILE_TO_ROOM) - .part(fileToUpload) - .param(FORM_PARAM_SEND_NOTIFICATION, FORM_PARAM_SEND_NOTIFICATION_TRUE) - .param(FORM_PARAM_DESCRIPTION, FORM_PARAM_DESCRIPTION_VALUE) - .param(FORM_PARAM_MESSAGE, FORM_PARAM_MESSAGE_VALUE) - .param(FORM_PARAM_TMID, FORM_PARAM_TMID_VALUE) - .contentType(MediaType.MULTIPART_FORM_DATA) - .header(RC_TOKEN_HEADER_PARAMETER_NAME, RC_TOKEN) - .header(RC_USER_ID_HEADER_PARAMETER_NAME, RC_USER_ID)) + multipart(PATH_UPLOAD_FILE_TO_ROOM) + .part(fileToUpload) + .param(FORM_PARAM_SEND_NOTIFICATION, FORM_PARAM_SEND_NOTIFICATION_TRUE) + .param(FORM_PARAM_DESCRIPTION, FORM_PARAM_DESCRIPTION_VALUE) + .param(FORM_PARAM_MESSAGE, FORM_PARAM_MESSAGE_VALUE) + .param(FORM_PARAM_TMID, FORM_PARAM_TMID_VALUE) + .contentType(MediaType.MULTIPART_FORM_DATA) + .header(RC_TOKEN_HEADER_PARAMETER_NAME, RC_TOKEN) + .header(RC_USER_ID_HEADER_PARAMETER_NAME, RC_USER_ID)) .andExpect(status().isNotFound()); } @@ -100,15 +99,15 @@ public void uploadFileToFeedbackRoom_Should_ReturnNotFound_WhenRoomIdIsMissing() MockPart fileToUpload = new MockPart(FORM_PARAM_FILE, "fileToUpload", "content".getBytes()); mvc.perform( - multipart(PATH_UPLOAD_FILE_TO_FEEDBACK_ROOM) - .part(fileToUpload) - .param(FORM_PARAM_SEND_NOTIFICATION, FORM_PARAM_SEND_NOTIFICATION_TRUE) - .param(FORM_PARAM_DESCRIPTION, FORM_PARAM_DESCRIPTION_VALUE) - .param(FORM_PARAM_MESSAGE, FORM_PARAM_MESSAGE_VALUE) - .param(FORM_PARAM_TMID, FORM_PARAM_TMID_VALUE) - .contentType(MediaType.MULTIPART_FORM_DATA) - .header(RC_TOKEN_HEADER_PARAMETER_NAME, RC_TOKEN) - .header(RC_USER_ID_HEADER_PARAMETER_NAME, RC_USER_ID)) + multipart(PATH_UPLOAD_FILE_TO_FEEDBACK_ROOM) + .part(fileToUpload) + .param(FORM_PARAM_SEND_NOTIFICATION, FORM_PARAM_SEND_NOTIFICATION_TRUE) + .param(FORM_PARAM_DESCRIPTION, FORM_PARAM_DESCRIPTION_VALUE) + .param(FORM_PARAM_MESSAGE, FORM_PARAM_MESSAGE_VALUE) + .param(FORM_PARAM_TMID, FORM_PARAM_TMID_VALUE) + .contentType(MediaType.MULTIPART_FORM_DATA) + .header(RC_TOKEN_HEADER_PARAMETER_NAME, RC_TOKEN) + .header(RC_USER_ID_HEADER_PARAMETER_NAME, RC_USER_ID)) .andExpect(status().isNotFound()); } @@ -116,14 +115,14 @@ public void uploadFileToFeedbackRoom_Should_ReturnNotFound_WhenRoomIdIsMissing() public void uploadFileToRoom_Should_ReturnBadRequest_WhenFileIsMissing() throws Exception { mvc.perform( - multipart(PATH_UPLOAD_FILE_TO_ROOM + "/" + RC_ROOM_ID) - .param(FORM_PARAM_SEND_NOTIFICATION, FORM_PARAM_SEND_NOTIFICATION_TRUE) - .param(FORM_PARAM_DESCRIPTION, FORM_PARAM_DESCRIPTION_VALUE) - .param(FORM_PARAM_MESSAGE, FORM_PARAM_MESSAGE_VALUE) - .param(FORM_PARAM_TMID, FORM_PARAM_TMID_VALUE) - .contentType(MediaType.MULTIPART_FORM_DATA) - .header(RC_TOKEN_HEADER_PARAMETER_NAME, RC_TOKEN) - .header(RC_USER_ID_HEADER_PARAMETER_NAME, RC_USER_ID)) + multipart(PATH_UPLOAD_FILE_TO_ROOM + "/" + RC_ROOM_ID) + .param(FORM_PARAM_SEND_NOTIFICATION, FORM_PARAM_SEND_NOTIFICATION_TRUE) + .param(FORM_PARAM_DESCRIPTION, FORM_PARAM_DESCRIPTION_VALUE) + .param(FORM_PARAM_MESSAGE, FORM_PARAM_MESSAGE_VALUE) + .param(FORM_PARAM_TMID, FORM_PARAM_TMID_VALUE) + .contentType(MediaType.MULTIPART_FORM_DATA) + .header(RC_TOKEN_HEADER_PARAMETER_NAME, RC_TOKEN) + .header(RC_USER_ID_HEADER_PARAMETER_NAME, RC_USER_ID)) .andExpect(status().isBadRequest()); } @@ -134,14 +133,14 @@ public void uploadFileToRoom_Should_ReturnBadRequest_WhenSendNotificationIsMissi MockPart fileToUpload = new MockPart(FORM_PARAM_FILE, "fileToUpload", "content".getBytes()); mvc.perform( - multipart(PATH_UPLOAD_FILE_TO_ROOM + "/" + RC_ROOM_ID) - .part(fileToUpload) - .param(FORM_PARAM_DESCRIPTION, FORM_PARAM_DESCRIPTION_VALUE) - .param(FORM_PARAM_MESSAGE, FORM_PARAM_MESSAGE_VALUE) - .param(FORM_PARAM_TMID, FORM_PARAM_TMID_VALUE) - .contentType(MediaType.MULTIPART_FORM_DATA) - .header(RC_TOKEN_HEADER_PARAMETER_NAME, RC_TOKEN) - .header(RC_USER_ID_HEADER_PARAMETER_NAME, RC_USER_ID)) + multipart(PATH_UPLOAD_FILE_TO_ROOM + "/" + RC_ROOM_ID) + .part(fileToUpload) + .param(FORM_PARAM_DESCRIPTION, FORM_PARAM_DESCRIPTION_VALUE) + .param(FORM_PARAM_MESSAGE, FORM_PARAM_MESSAGE_VALUE) + .param(FORM_PARAM_TMID, FORM_PARAM_TMID_VALUE) + .contentType(MediaType.MULTIPART_FORM_DATA) + .header(RC_TOKEN_HEADER_PARAMETER_NAME, RC_TOKEN) + .header(RC_USER_ID_HEADER_PARAMETER_NAME, RC_USER_ID)) .andExpect(status().isBadRequest()); } @@ -150,14 +149,14 @@ public void uploadFileToFeedbackRoom_Should_ReturnBadRequest_WhenFileIsMissing() throws Exception { mvc.perform( - multipart(PATH_UPLOAD_FILE_TO_FEEDBACK_ROOM + "/" + RC_ROOM_ID) - .param(FORM_PARAM_SEND_NOTIFICATION, FORM_PARAM_SEND_NOTIFICATION_TRUE) - .param(FORM_PARAM_DESCRIPTION, FORM_PARAM_DESCRIPTION_VALUE) - .param(FORM_PARAM_MESSAGE, FORM_PARAM_MESSAGE_VALUE) - .param(FORM_PARAM_TMID, FORM_PARAM_TMID_VALUE) - .contentType(MediaType.MULTIPART_FORM_DATA) - .header(RC_TOKEN_HEADER_PARAMETER_NAME, RC_TOKEN) - .header(RC_USER_ID_HEADER_PARAMETER_NAME, RC_USER_ID)) + multipart(PATH_UPLOAD_FILE_TO_FEEDBACK_ROOM + "/" + RC_ROOM_ID) + .param(FORM_PARAM_SEND_NOTIFICATION, FORM_PARAM_SEND_NOTIFICATION_TRUE) + .param(FORM_PARAM_DESCRIPTION, FORM_PARAM_DESCRIPTION_VALUE) + .param(FORM_PARAM_MESSAGE, FORM_PARAM_MESSAGE_VALUE) + .param(FORM_PARAM_TMID, FORM_PARAM_TMID_VALUE) + .contentType(MediaType.MULTIPART_FORM_DATA) + .header(RC_TOKEN_HEADER_PARAMETER_NAME, RC_TOKEN) + .header(RC_USER_ID_HEADER_PARAMETER_NAME, RC_USER_ID)) .andExpect(status().isBadRequest()); } @@ -168,14 +167,14 @@ public void uploadFileToFeedbackRoom_Should_ReturnBadRequest_WhenSendNotificatio MockPart fileToUpload = new MockPart(FORM_PARAM_FILE, "fileToUpload", "content".getBytes()); mvc.perform( - multipart(PATH_UPLOAD_FILE_TO_FEEDBACK_ROOM + "/" + RC_ROOM_ID) - .part(fileToUpload) - .param(FORM_PARAM_DESCRIPTION, FORM_PARAM_DESCRIPTION_VALUE) - .param(FORM_PARAM_MESSAGE, FORM_PARAM_MESSAGE_VALUE) - .param(FORM_PARAM_TMID, FORM_PARAM_TMID_VALUE) - .contentType(MediaType.MULTIPART_FORM_DATA) - .header(RC_TOKEN_HEADER_PARAMETER_NAME, RC_TOKEN) - .header(RC_USER_ID_HEADER_PARAMETER_NAME, RC_USER_ID)) + multipart(PATH_UPLOAD_FILE_TO_FEEDBACK_ROOM + "/" + RC_ROOM_ID) + .part(fileToUpload) + .param(FORM_PARAM_DESCRIPTION, FORM_PARAM_DESCRIPTION_VALUE) + .param(FORM_PARAM_MESSAGE, FORM_PARAM_MESSAGE_VALUE) + .param(FORM_PARAM_TMID, FORM_PARAM_TMID_VALUE) + .contentType(MediaType.MULTIPART_FORM_DATA) + .header(RC_TOKEN_HEADER_PARAMETER_NAME, RC_TOKEN) + .header(RC_USER_ID_HEADER_PARAMETER_NAME, RC_USER_ID)) .andExpect(status().isBadRequest()); } @@ -185,15 +184,15 @@ public void uploadFileToRoom_Should_ReturnOk_WhenValidRequest() throws Exception MockPart fileToUpload = new MockPart(FORM_PARAM_FILE, "fileToUpload", "content".getBytes()); mvc.perform( - multipart(PATH_UPLOAD_FILE_TO_ROOM + "/" + RC_ROOM_ID) - .part(fileToUpload) - .param(FORM_PARAM_SEND_NOTIFICATION, FORM_PARAM_SEND_NOTIFICATION_TRUE) - .param(FORM_PARAM_DESCRIPTION, FORM_PARAM_DESCRIPTION_VALUE) - .param(FORM_PARAM_MESSAGE, FORM_PARAM_MESSAGE_VALUE) - .param(FORM_PARAM_TMID, FORM_PARAM_TMID_VALUE) - .contentType(MediaType.MULTIPART_FORM_DATA) - .header(RC_TOKEN_HEADER_PARAMETER_NAME, RC_TOKEN) - .header(RC_USER_ID_HEADER_PARAMETER_NAME, RC_USER_ID)) + multipart(PATH_UPLOAD_FILE_TO_ROOM + "/" + RC_ROOM_ID) + .part(fileToUpload) + .param(FORM_PARAM_SEND_NOTIFICATION, FORM_PARAM_SEND_NOTIFICATION_TRUE) + .param(FORM_PARAM_DESCRIPTION, FORM_PARAM_DESCRIPTION_VALUE) + .param(FORM_PARAM_MESSAGE, FORM_PARAM_MESSAGE_VALUE) + .param(FORM_PARAM_TMID, FORM_PARAM_TMID_VALUE) + .contentType(MediaType.MULTIPART_FORM_DATA) + .header(RC_TOKEN_HEADER_PARAMETER_NAME, RC_TOKEN) + .header(RC_USER_ID_HEADER_PARAMETER_NAME, RC_USER_ID)) .andExpect(status().isCreated()); } @@ -203,15 +202,15 @@ public void uploadFileToFeedbackRoom_Should_ReturnOk_WhenValidRequest() throws E MockPart fileToUpload = new MockPart(FORM_PARAM_FILE, "fileToUpload", "content".getBytes()); mvc.perform( - multipart(PATH_UPLOAD_FILE_TO_FEEDBACK_ROOM + "/" + RC_ROOM_ID) - .part(fileToUpload) - .param(FORM_PARAM_SEND_NOTIFICATION, FORM_PARAM_SEND_NOTIFICATION_TRUE) - .param(FORM_PARAM_DESCRIPTION, FORM_PARAM_DESCRIPTION_VALUE) - .param(FORM_PARAM_MESSAGE, FORM_PARAM_MESSAGE_VALUE) - .param(FORM_PARAM_TMID, FORM_PARAM_TMID_VALUE) - .contentType(MediaType.MULTIPART_FORM_DATA) - .header(RC_TOKEN_HEADER_PARAMETER_NAME, RC_TOKEN) - .header(RC_USER_ID_HEADER_PARAMETER_NAME, RC_USER_ID)) + multipart(PATH_UPLOAD_FILE_TO_FEEDBACK_ROOM + "/" + RC_ROOM_ID) + .part(fileToUpload) + .param(FORM_PARAM_SEND_NOTIFICATION, FORM_PARAM_SEND_NOTIFICATION_TRUE) + .param(FORM_PARAM_DESCRIPTION, FORM_PARAM_DESCRIPTION_VALUE) + .param(FORM_PARAM_MESSAGE, FORM_PARAM_MESSAGE_VALUE) + .param(FORM_PARAM_TMID, FORM_PARAM_TMID_VALUE) + .contentType(MediaType.MULTIPART_FORM_DATA) + .header(RC_TOKEN_HEADER_PARAMETER_NAME, RC_TOKEN) + .header(RC_USER_ID_HEADER_PARAMETER_NAME, RC_USER_ID)) .andExpect(status().isCreated()); } @@ -221,10 +220,10 @@ public void updateKey_Should_ReturnOk_WhenProvidedWithNewKey() throws Exception when(encryptionService.getMasterKey()).thenReturn(MASTER_KEY_1); mvc.perform( - post(PATH_UPDATE_KEY) - .contentType(MediaType.APPLICATION_JSON) - .content(MASTER_KEY_DTO_KEY_2) - .accept(MediaType.APPLICATION_JSON)) + post(PATH_UPDATE_KEY) + .contentType(MediaType.APPLICATION_JSON) + .content(MASTER_KEY_DTO_KEY_2) + .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()); } @@ -234,10 +233,10 @@ public void updateKey_Should_ReturnConflict_WhenProvidedWithSameKey() throws Exc when(encryptionService.getMasterKey()).thenReturn(MASTER_KEY_1); mvc.perform( - post(PATH_UPDATE_KEY) - .contentType(MediaType.APPLICATION_JSON) - .content(MASTER_KEY_DTO_KEY_1) - .accept(MediaType.APPLICATION_JSON)) + post(PATH_UPDATE_KEY) + .contentType(MediaType.APPLICATION_JSON) + .content(MASTER_KEY_DTO_KEY_1) + .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isConflict()); } @@ -247,12 +246,12 @@ public void uploadFileToRoom_Should_ReturnOk_When_notRequiredParamsAreMissing() MockPart fileToUpload = new MockPart(FORM_PARAM_FILE, "fileToUpload", "content".getBytes()); mvc.perform( - multipart(PATH_UPLOAD_FILE_TO_ROOM + "/" + RC_ROOM_ID) - .part(fileToUpload) - .param(FORM_PARAM_SEND_NOTIFICATION, FORM_PARAM_SEND_NOTIFICATION_TRUE) - .contentType(MediaType.MULTIPART_FORM_DATA) - .header(RC_TOKEN_HEADER_PARAMETER_NAME, RC_TOKEN) - .header(RC_USER_ID_HEADER_PARAMETER_NAME, RC_USER_ID)) + multipart(PATH_UPLOAD_FILE_TO_ROOM + "/" + RC_ROOM_ID) + .part(fileToUpload) + .param(FORM_PARAM_SEND_NOTIFICATION, FORM_PARAM_SEND_NOTIFICATION_TRUE) + .contentType(MediaType.MULTIPART_FORM_DATA) + .header(RC_TOKEN_HEADER_PARAMETER_NAME, RC_TOKEN) + .header(RC_USER_ID_HEADER_PARAMETER_NAME, RC_USER_ID)) .andExpect(status().isCreated()); } @@ -263,12 +262,12 @@ public void uploadFileToFeedbackRoom_Should_ReturnOk_When_notRequiredParamsAreMi MockPart fileToUpload = new MockPart(FORM_PARAM_FILE, "fileToUpload", "content".getBytes()); mvc.perform( - multipart(PATH_UPLOAD_FILE_TO_FEEDBACK_ROOM + "/" + RC_ROOM_ID) - .part(fileToUpload) - .param(FORM_PARAM_SEND_NOTIFICATION, FORM_PARAM_SEND_NOTIFICATION_TRUE) - .contentType(MediaType.MULTIPART_FORM_DATA) - .header(RC_TOKEN_HEADER_PARAMETER_NAME, RC_TOKEN) - .header(RC_USER_ID_HEADER_PARAMETER_NAME, RC_USER_ID)) + multipart(PATH_UPLOAD_FILE_TO_FEEDBACK_ROOM + "/" + RC_ROOM_ID) + .part(fileToUpload) + .param(FORM_PARAM_SEND_NOTIFICATION, FORM_PARAM_SEND_NOTIFICATION_TRUE) + .contentType(MediaType.MULTIPART_FORM_DATA) + .header(RC_TOKEN_HEADER_PARAMETER_NAME, RC_TOKEN) + .header(RC_USER_ID_HEADER_PARAMETER_NAME, RC_USER_ID)) .andExpect(status().isCreated()); } @@ -277,26 +276,60 @@ public void uploadFileToRoom_Should_ReturnForbiddenWithCustomHeader_When_quotaLi throws Exception { doThrow(new QuotaReachedException(LogService::logWarning)) .when(this.uploadFacade).uploadFileToRoom(any(RocketChatCredentials.class), - any(RocketChatUploadParameter.class), anyBoolean()); + any(RocketChatUploadParameter.class), anyBoolean()); MockPart fileToUpload = new MockPart(FORM_PARAM_FILE, "fileToUpload", "content".getBytes()); mvc.perform( - multipart(PATH_UPLOAD_FILE_TO_ROOM + "/" + RC_ROOM_ID) - .part(fileToUpload) - .param(FORM_PARAM_SEND_NOTIFICATION, FORM_PARAM_SEND_NOTIFICATION_TRUE) - .param(FORM_PARAM_DESCRIPTION, FORM_PARAM_DESCRIPTION_VALUE) - .param(FORM_PARAM_MESSAGE, FORM_PARAM_MESSAGE_VALUE) - .param(FORM_PARAM_TMID, FORM_PARAM_TMID_VALUE) - .contentType(MediaType.MULTIPART_FORM_DATA) - .header(RC_TOKEN_HEADER_PARAMETER_NAME, RC_TOKEN) - .header(RC_USER_ID_HEADER_PARAMETER_NAME, RC_USER_ID)) + multipart(PATH_UPLOAD_FILE_TO_ROOM + "/" + RC_ROOM_ID) + .part(fileToUpload) + .param(FORM_PARAM_SEND_NOTIFICATION, FORM_PARAM_SEND_NOTIFICATION_TRUE) + .param(FORM_PARAM_DESCRIPTION, FORM_PARAM_DESCRIPTION_VALUE) + .param(FORM_PARAM_MESSAGE, FORM_PARAM_MESSAGE_VALUE) + .param(FORM_PARAM_TMID, FORM_PARAM_TMID_VALUE) + .contentType(MediaType.MULTIPART_FORM_DATA) + .header(RC_TOKEN_HEADER_PARAMETER_NAME, RC_TOKEN) + .header(RC_USER_ID_HEADER_PARAMETER_NAME, RC_USER_ID)) .andExpect(status().isForbidden()) .andExpect(header().string("X-Reason", "QUOTA_REACHED")); } - private String convertObjectToJson(Object object) throws JsonProcessingException { - ObjectMapper mapper = new ObjectMapper(); - return mapper.writeValueAsString(object); + @Test + public void uploadFileToRoom_should_return_unsupported_media_type_on_InvalidFileTypeException() + throws Exception { + doThrow(InvalidFileTypeException.class).when(uploadFacade) + .uploadFileToRoom(any(RocketChatCredentials.class), any(RocketChatUploadParameter.class), + anyBoolean()); + MockPart fileToUpload = new MockPart(FORM_PARAM_FILE, "fileToUpload", "content".getBytes()); + + mvc.perform( + multipart(PATH_UPLOAD_FILE_TO_ROOM + "/" + RC_ROOM_ID) + .part(fileToUpload) + .param(FORM_PARAM_SEND_NOTIFICATION, FORM_PARAM_SEND_NOTIFICATION_TRUE) + .param(FORM_PARAM_DESCRIPTION, FORM_PARAM_DESCRIPTION_VALUE) + .param(FORM_PARAM_MESSAGE, FORM_PARAM_MESSAGE_VALUE) + .param(FORM_PARAM_TMID, FORM_PARAM_TMID_VALUE) + .contentType(MediaType.MULTIPART_FORM_DATA) + .header(RC_TOKEN_HEADER_PARAMETER_NAME, RC_TOKEN) + .header(RC_USER_ID_HEADER_PARAMETER_NAME, RC_USER_ID)) + .andExpect(status().isUnsupportedMediaType()); + } + + @Test + public void uploadFileToFeedbackRoom_should_return_unsupported_media_type_on_InvalidFileTypeException() + throws Exception { + doThrow(InvalidFileTypeException.class).when(uploadFacade) + .uploadFileToFeedbackRoom(any(RocketChatCredentials.class), + any(RocketChatUploadParameter.class), anyBoolean()); + MockPart fileToUpload = new MockPart(FORM_PARAM_FILE, "fileToUpload", "content".getBytes()); + + mvc.perform( + multipart(PATH_UPLOAD_FILE_TO_FEEDBACK_ROOM + "/" + RC_ROOM_ID) + .part(fileToUpload) + .param(FORM_PARAM_SEND_NOTIFICATION, FORM_PARAM_SEND_NOTIFICATION_TRUE) + .contentType(MediaType.MULTIPART_FORM_DATA) + .header(RC_TOKEN_HEADER_PARAMETER_NAME, RC_TOKEN) + .header(RC_USER_ID_HEADER_PARAMETER_NAME, RC_USER_ID)) + .andExpect(status().isUnsupportedMediaType()); } } From ff7d06f1db0fabe470ea7bb9c1c2b199fb64cca6 Mon Sep 17 00:00:00 2001 From: hill-daniel <20245376+hill-daniel@users.noreply.github.com> Date: Mon, 28 Feb 2022 19:09:02 +0100 Subject: [PATCH 3/3] fix: add tika specific mime type for Open Office and MS Office documents --- src/main/resources/application.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 02c6cb1..903ef23 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -86,4 +86,4 @@ statistics.enabled=false statistics.rabbitmq.exchange.name=statistics.topic # MIME type whitelist for file upload. Supports png, jpeg, doc, docx and pdf. -mime-type-whitelist={'image/png','image/jpeg','application/msword','application/vnd.openxmlformats-officedocument.wordprocessingml.document','application/pdf'} \ No newline at end of file +mime-type-whitelist={'image/png','image/jpeg','application/msword','application/vnd.openxmlformats-officedocument.wordprocessingml.document','application/pdf','application/x-tika-ooxml'} \ No newline at end of file