diff --git a/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/fileresource/FileResourceBlocklist.java b/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/fileresource/FileResourceBlocklist.java index 62890142979c..713a3a90b7a7 100644 --- a/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/fileresource/FileResourceBlocklist.java +++ b/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/fileresource/FileResourceBlocklist.java @@ -34,7 +34,7 @@ * @author Lars Helge Overland */ public class FileResourceBlocklist { - private static final Set CONTENT_TYPES = + private static final Set BLOCKED_CONTENT_TYPES = Set.of( // Web "text/html", @@ -54,7 +54,7 @@ public class FileResourceBlocklist { "application/x-sh", "application/x-csh"); - private static final Set FILE_EXTENSIONS = + private static final Set BLOCKED_FILE_EXTENSIONS = Set.of( // Web "html", @@ -93,11 +93,11 @@ public static boolean isValid(FileResource fileResource) { return false; } - if (CONTENT_TYPES.contains(fileResource.getContentType().toLowerCase())) { + if (BLOCKED_CONTENT_TYPES.contains(fileResource.getContentType().toLowerCase())) { return false; } - if (FILE_EXTENSIONS.contains( + if (BLOCKED_FILE_EXTENSIONS.contains( FilenameUtils.getExtension(fileResource.getName().toLowerCase()))) { return false; } diff --git a/dhis-2/dhis-test-web-api/src/test/java/org/hisp/dhis/webapi/controller/FileResourceControllerTest.java b/dhis-2/dhis-test-web-api/src/test/java/org/hisp/dhis/webapi/controller/FileResourceControllerTest.java index da5deaeae07f..c61286e154c5 100644 --- a/dhis-2/dhis-test-web-api/src/test/java/org/hisp/dhis/webapi/controller/FileResourceControllerTest.java +++ b/dhis-2/dhis-test-web-api/src/test/java/org/hisp/dhis/webapi/controller/FileResourceControllerTest.java @@ -101,10 +101,11 @@ void testSaveGoodAvatar() throws IOException { } @Test - void testSaveOrgUnitImage() { + void testSaveOrgUnitImage() throws IOException { + File file = new ClassPathResource("file/dhis2.png").getFile(); MockMultipartFile image = new MockMultipartFile( - "file", "OU_profile_image.png", "image/png", "<>".getBytes()); + "file", "OU_profile_image.png", "image/png", Files.readAllBytes(file.toPath())); HttpResponse response = POST_MULTIPART("/fileResources?domain=ORG_UNIT", image); JsonObject savedObject = response.content(HttpStatus.ACCEPTED).getObject("response").getObject("fileResource"); @@ -119,10 +120,11 @@ void testSaveOrgUnitImage() { } @Test - void testSaveOrgUnitImageWithUid() { + void testSaveOrgUnitImageWithUid() throws IOException { + File file = new ClassPathResource("file/dhis2.png").getFile(); MockMultipartFile image = new MockMultipartFile( - "file", "OU_profile_image.png", "image/png", "<>".getBytes()); + "file", "OU_profile_image.png", "image/png", Files.readAllBytes(file.toPath())); HttpResponse response = POST_MULTIPART("/fileResources?domain=ORG_UNIT&uid=0123456789a", image); JsonObject savedObject = response.content(HttpStatus.ACCEPTED).getObject("response").getObject("fileResource"); @@ -131,10 +133,11 @@ void testSaveOrgUnitImageWithUid() { } @Test - void testSaveOrgUnitImageWithUid_Update() { + void testSaveOrgUnitImageWithUid_Update() throws IOException { + File file = new ClassPathResource("file/dhis2.png").getFile(); MockMultipartFile image = new MockMultipartFile( - "file", "OU_profile_image.png", "image/png", "<>".getBytes()); + "file", "OU_profile_image.png", "image/png", Files.readAllBytes(file.toPath())); HttpResponse response = POST_MULTIPART("/fileResources?domain=ORG_UNIT&uid=0123456789x", image); JsonObject savedObject = response.content(HttpStatus.ACCEPTED).getObject("response").getObject("fileResource"); @@ -144,7 +147,7 @@ void testSaveOrgUnitImageWithUid_Update() { // now update the resource with a different image but the same UID MockMultipartFile image2 = new MockMultipartFile( - "file", "OU_profile_image2.png", "image/png", "<>".getBytes()); + "file", "OU_profile_image2.png", "image/png", Files.readAllBytes(file.toPath())); JsonWebMessage message = assertWebMessage( diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/FileResourceController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/FileResourceController.java index c1c9e26ba3bf..76357db12f8c 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/FileResourceController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/FileResourceController.java @@ -144,10 +144,12 @@ public void getFileResourceData( } response.setContentType(fileResource.getContentType()); + response.setHeader( HttpHeaders.CONTENT_LENGTH, String.valueOf(fileResourceService.getFileResourceContentLength(fileResource))); response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "filename=" + fileResource.getName()); + HeaderUtils.setSecurityHeaders( response, dhisConfig.getProperty(ConfigurationKey.CSP_HEADER_VALUE)); @@ -174,10 +176,18 @@ public WebMessage saveFileResource( if (domain.equals(FileResourceDomain.ICON)) { validateCustomIconFile(file); fileResource = fileResourceUtils.saveFileResource(uid, resizeIconToDefaultSize(file), domain); + } else if (domain.equals(FileResourceDomain.USER_AVATAR)) { fileResourceUtils.validateUserAvatar(file); fileResource = fileResourceUtils.saveFileResource(uid, resizeAvatarToDefaultSize(file), domain); + + } else if (domain.equals(FileResourceDomain.ORG_UNIT)) { + fileResourceUtils.validateOrgUnitImage(file); + fileResource = + fileResourceUtils.saveFileResource( + uid, fileResourceUtils.resizeOrgToDefaultSize(file), domain); + } else { fileResource = fileResourceUtils.saveFileResource(uid, file, domain); } diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/utils/FileResourceUtils.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/utils/FileResourceUtils.java index e9d657b9a886..48db99821d74 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/utils/FileResourceUtils.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/utils/FileResourceUtils.java @@ -93,13 +93,34 @@ public class FileResourceUtils { private static final int AVATAR_TARGET_HEIGHT = 200; private static final int AVATAR_TARGET_WIDTH = 200; - private static final long MAX_AVATAR_FILE_SIZE_IN_BYTES = 2_000_000; + private static final int ORGUNIT_TARGET_HEIGHT = 800; + private static final int ORGUNIT_TARGET_WIDTH = 800; - private static final List ALLOWED_AVATAR_FILE_EXTENSIONS = + private static final long MAX_AVATAR_IMAGE_SIZE_IN_BYTES = 2_000_000; + private static final long MAX_ORGUNIT_IMAGE_SIZE_IN_BYTES = 8_000_000; + + private static final List ALLOWED_IMAGE_FILE_EXTENSIONS = List.of("jpg", "jpeg", "png", "gif"); - private static final List ALLOWED_AVATAR_MIME_TYPES = + private static final List ALLOWED_IMAGE_MIME_TYPES = List.of("image/jpeg", "image/png", "image/gif"); + private static class MultipartFileByteSource extends ByteSource { + private MultipartFile file; + + public MultipartFileByteSource(MultipartFile file) { + this.file = file; + } + + @Override + public InputStream openStream() { + try { + return file.getInputStream(); + } catch (IOException ioe) { + return new NullInputStream(0); + } + } + } + /** * Transfers the given multipart file content to a local temporary file. * @@ -232,31 +253,16 @@ public FileResource saveFileResource(String uid, MultipartFile file, FileResourc return fileResource; } - // ------------------------------------------------------------------------- - // Inner classes - // ------------------------------------------------------------------------- - - private class MultipartFileByteSource extends ByteSource { - private MultipartFile file; - - public MultipartFileByteSource(MultipartFile file) { - this.file = file; - } - - @Override - public InputStream openStream() { - try { - return file.getInputStream(); - } catch (IOException ioe) { - return new NullInputStream(0); - } - } + public void validateOrgUnitImage(MultipartFile file) { + validateContentType(file.getContentType(), ALLOWED_IMAGE_MIME_TYPES); + validateFileExtension(file.getOriginalFilename(), ALLOWED_IMAGE_FILE_EXTENSIONS); + validateFileSize(file, MAX_ORGUNIT_IMAGE_SIZE_IN_BYTES); } public void validateUserAvatar(@Nonnull MultipartFile file) { - validateContentType(file.getContentType(), ALLOWED_AVATAR_MIME_TYPES); - validateFileExtension(file.getOriginalFilename(), ALLOWED_AVATAR_FILE_EXTENSIONS); - validateFileSize(file, MAX_AVATAR_FILE_SIZE_IN_BYTES); + validateContentType(file.getContentType(), ALLOWED_IMAGE_MIME_TYPES); + validateFileExtension(file.getOriginalFilename(), ALLOWED_IMAGE_FILE_EXTENSIONS); + validateFileSize(file, MAX_AVATAR_IMAGE_SIZE_IN_BYTES); } private void validateContentType(String contentType, @Nonnull List validExtensions) { @@ -342,4 +348,9 @@ public static MultipartFile resizeAvatarToDefaultSize(MultipartFile multipartFil return resizeImageToCustomSize( multipartFile, AVATAR_TARGET_WIDTH, AVATAR_TARGET_HEIGHT, Mode.AUTOMATIC); } + + public MultipartFile resizeOrgToDefaultSize(MultipartFile multipartFile) throws IOException { + return resizeImageToCustomSize( + multipartFile, ORGUNIT_TARGET_WIDTH, ORGUNIT_TARGET_HEIGHT, Mode.AUTOMATIC); + } }