Skip to content

Commit faf0267

Browse files
committed
feat: add Validators & FileZipManager
1 parent 6527dbb commit faf0267

File tree

14 files changed

+2210
-22
lines changed

14 files changed

+2210
-22
lines changed

build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ dependencies {
5656

5757
implementation 'org.apache.commons:commons-lang3:3.20.0'
5858
implementation 'commons-io:commons-io:2.21.0'
59+
implementation 'org.apache.commons:commons-compress:1.28.0'
5960

6061
testImplementation 'org.springframework.boot:spring-boot-starter-test'
6162
testImplementation 'org.springframework.boot:spring-boot-testcontainers'
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package com.example.arInfra.config;
2+
3+
import jakarta.servlet.MultipartConfigElement;
4+
import org.springframework.beans.factory.annotation.Value;
5+
import org.springframework.boot.web.servlet.MultipartConfigFactory;
6+
import org.springframework.context.annotation.Bean;
7+
import org.springframework.context.annotation.Configuration;
8+
import org.springframework.util.unit.DataSize;
9+
import org.springframework.web.multipart.MultipartResolver;
10+
import org.springframework.web.multipart.support.StandardServletMultipartResolver;
11+
12+
/**
13+
* Secure multipart file upload configuration.
14+
*
15+
* <p>Security measures implemented:
16+
*
17+
* <ul>
18+
* <li>Configurable maximum file sizes to prevent DoS attacks
19+
* <li>Request size limits to prevent memory exhaustion
20+
* <li>File size threshold for in-memory vs disk storage
21+
* <li>Temporary file location for secure file handling
22+
* </ul>
23+
*/
24+
@Configuration
25+
public class MultipartConfigurer {
26+
27+
@Value("${spring.servlet.multipart.max-file-size:10MB}")
28+
private String maxFileSize;
29+
30+
@Value("${spring.servlet.multipart.max-request-size:10MB}")
31+
private String maxRequestSize;
32+
33+
@Value("${spring.servlet.multipart.file-size-threshold:2MB}")
34+
private String fileSizeThreshold;
35+
36+
@Value("${spring.servlet.multipart.location:${java.io.tmpdir}}")
37+
private String location;
38+
39+
/**
40+
* Configures multipart file upload limits with security controls.
41+
*
42+
* @return configured MultipartConfigElement
43+
*/
44+
@Bean
45+
public MultipartConfigElement multipartConfigElement() {
46+
MultipartConfigFactory factory = new MultipartConfigFactory();
47+
48+
factory.setMaxFileSize(DataSize.parse(maxFileSize));
49+
factory.setMaxRequestSize(DataSize.parse(maxRequestSize));
50+
factory.setFileSizeThreshold(DataSize.parse(fileSizeThreshold));
51+
factory.setLocation(location);
52+
53+
return factory.createMultipartConfig();
54+
}
55+
56+
/**
57+
* Standard servlet multipart resolver.
58+
*
59+
* @return configured MultipartResolver
60+
*/
61+
@Bean
62+
public MultipartResolver multipartResolver() {
63+
return new StandardServletMultipartResolver();
64+
}
65+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package com.example.arInfra.endpoint.rest.controller;
2+
3+
import static java.lang.String.format;
4+
import static org.owasp.encoder.Encode.forJava;
5+
6+
import com.example.arInfra.InfraGenerated;
7+
import lombok.extern.slf4j.Slf4j;
8+
import org.springframework.beans.factory.annotation.Value;
9+
import org.springframework.http.HttpStatus;
10+
import org.springframework.http.ProblemDetail;
11+
import org.springframework.web.bind.annotation.ExceptionHandler;
12+
import org.springframework.web.bind.annotation.RestControllerAdvice;
13+
import org.springframework.web.multipart.MaxUploadSizeExceededException;
14+
import org.springframework.web.multipart.MultipartException;
15+
16+
/**
17+
* Global exception handler for multipart file upload errors.
18+
*
19+
* <p>Provides secure error handling without exposing sensitive system information.
20+
*/
21+
@Slf4j
22+
@InfraGenerated
23+
@RestControllerAdvice
24+
public class MultipartExceptionHandler {
25+
26+
@Value("${spring.servlet.multipart.max-file-size:10MB}")
27+
private String maxFileSize;
28+
29+
@Value("${spring.servlet.multipart.max-request-size:10MB}")
30+
private String maxRequestSize;
31+
32+
/**
33+
* Handles file size exceeded exceptions with user-friendly messages.
34+
*
35+
* @param ex the exception
36+
* @return problem detail response
37+
*/
38+
@ExceptionHandler(MaxUploadSizeExceededException.class)
39+
public ProblemDetail handleMaxUploadSizeExceeded(MaxUploadSizeExceededException ex) {
40+
log.warn("File upload size limit exceeded", ex);
41+
42+
ProblemDetail problemDetail =
43+
ProblemDetail.forStatusAndDetail(
44+
HttpStatus.PAYLOAD_TOO_LARGE,
45+
format(
46+
"Upload size exceeds the maximum allowed. Max file size: %s, Max request size: %s",
47+
forJava(maxFileSize), forJava(maxRequestSize)));
48+
49+
problemDetail.setTitle("File Size Limit Exceeded");
50+
problemDetail.setProperty("maxFileSize", maxFileSize);
51+
problemDetail.setProperty("maxRequestSize", maxRequestSize);
52+
53+
return problemDetail;
54+
}
55+
56+
/**
57+
* Handles general multipart exceptions.
58+
*
59+
* @param ex the exception
60+
* @return problem detail response
61+
*/
62+
@ExceptionHandler(MultipartException.class)
63+
public ProblemDetail handleMultipartException(MultipartException ex) {
64+
log.error("Multipart request processing failed", ex);
65+
66+
ProblemDetail problemDetail =
67+
ProblemDetail.forStatusAndDetail(
68+
HttpStatus.BAD_REQUEST,
69+
"Failed to process multipart request. Please ensure your file meets the requirements.");
70+
71+
problemDetail.setTitle("Multipart Request Error");
72+
73+
return problemDetail;
74+
}
75+
}

0 commit comments

Comments
 (0)