add check extensions
Test Workflow / test (push) Successful in 3s

This commit is contained in:
vladp
2025-12-22 17:17:48 +07:00
parent 76e8f4ab1d
commit 33479ea9af
4 changed files with 99 additions and 35 deletions
@@ -387,11 +387,11 @@ public class ApiController {
return false; return false;
} }
return mimeType.startsWith("image/") || return mimeType.startsWith("image") ||
mimeType.startsWith("text/") || mimeType.startsWith("text") ||
mimeType.equals("application/pdf") || mimeType.equals("pdf") ||
mimeType.startsWith("video/") || mimeType.startsWith("video") ||
mimeType.startsWith("audio/"); mimeType.startsWith("audio");
} }
private Long getUserIdFromToken(String tokenHeader) { private Long getUserIdFromToken(String tokenHeader) {
@@ -2,21 +2,34 @@ package ru.soune.nocopy.entity.file;
import lombok.Getter; import lombok.Getter;
import java.util.Arrays;
import java.util.List;
@Getter @Getter
public enum FileType { public enum FileType {
PHOTO("photo"), PHOTO("photo", Arrays.asList("jpg", "jpeg", "png", "gif", "bmp", "webp")),
IMAGE("image"), IMAGE("image", Arrays.asList("jpg", "jpeg", "png", "gif", "bmp", "webp", "svg", "tiff")),
VIDEO("video"), VIDEO("video", Arrays.asList("mp4", "avi", "mov", "wmv", "flv", "mkv", "webm")),
AUDIO("audio"), AUDIO("audio", Arrays.asList("mp3", "wav", "ogg", "aac", "flac", "m4a")),
DOCUMENT("document"); DOCUMENT("document", Arrays.asList("pdf", "doc", "docx", "txt", "rtf", "odt", "xls", "xlsx", "ppt", "pptx"));
private final String code; private final String displayName;
private final List<String> allowedExtensions;
FileType(String code) { FileType(String displayName, List<String> allowedExtensions) {
this.code = code; this.displayName = displayName;
this.allowedExtensions = allowedExtensions;
} }
public String getCode() { public String getDisplayName() {
return code; return displayName;
}
public List<String> getAllowedExtensions() {
return allowedExtensions;
}
public boolean supportsExtension(String extension) {
return allowedExtensions.contains(extension.toLowerCase());
} }
} }
@@ -6,6 +6,7 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.validation.BeanPropertyBindingResult; import org.springframework.validation.BeanPropertyBindingResult;
import org.springframework.validation.BindingResult; import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import ru.soune.nocopy.dto.*; import ru.soune.nocopy.dto.*;
import ru.soune.nocopy.dto.file.*; import ru.soune.nocopy.dto.file.*;
import ru.soune.nocopy.entity.AuthToken; import ru.soune.nocopy.entity.AuthToken;
@@ -17,10 +18,8 @@ import ru.soune.nocopy.repository.AuthTokenRepository;
import ru.soune.nocopy.repository.FileUploadSessionRepository; import ru.soune.nocopy.repository.FileUploadSessionRepository;
import ru.soune.nocopy.service.file.FileUploadService; import ru.soune.nocopy.service.file.FileUploadService;
import java.util.Arrays; import java.util.*;
import java.util.HashMap; import java.util.stream.Collectors;
import java.util.Map;
import java.util.Optional;
@Slf4j @Slf4j
@Component @Component
@@ -70,18 +69,31 @@ public class FileUploadHandler implements RequestHandler {
} }
private BaseResponse handleInitUpload(BaseRequest request, FileUploadRequest fileRequest) { private BaseResponse handleInitUpload(BaseRequest request, FileUploadRequest fileRequest) {
try {
String token = fileRequest.getToken(); String token = fileRequest.getToken();
Optional<AuthToken> tokenOptional = authTokenRepository.findByToken(token); Optional<AuthToken> tokenOptional = authTokenRepository.findByToken(token);
if (tokenOptional.isEmpty()) {
return new BaseResponse(request.getMsgId(), MessageCode.INVALID_TOKEN.getCode(),
MessageCode.INVALID_TOKEN.getDescription(), Map.of("token", token));
}
AuthToken authToken = tokenOptional.orElseThrow(() -> new NotFoundAuthToken("Token not found")); AuthToken authToken = tokenOptional.orElseThrow(() -> new NotFoundAuthToken("Token not found"));
BindingResult bindingResult = new BeanPropertyBindingResult(fileRequest, "fileRequest"); BindingResult bindingResult = new BeanPropertyBindingResult(fileRequest, "fileRequest");
fileUploadRequestValidator.validate(fileRequest, bindingResult); fileUploadRequestValidator.validate(fileRequest, bindingResult);
if (bindingResult.hasErrors()) { if (bindingResult.hasErrors()) {
throw new ValidationException(bindingResult, request.getMsgId()); Map<String, String> fieldErrors = bindingResult.getFieldErrors()
.stream()
.collect(Collectors.toMap(
FieldError::getField,
fieldError -> fieldError.getDefaultMessage() != null
? fieldError.getDefaultMessage()
: "Validation error"));
return new BaseResponse(request.getMsgId(), MessageCode.INVALID_FIELD.getCode(),
MessageCode.INVALID_FIELD.getDescription(), fieldErrors);
} }
FileUploadSession session = fileUploadService.initUpload( FileUploadSession session = fileUploadService.initUpload(
@@ -101,15 +113,6 @@ public class FileUploadHandler implements RequestHandler {
return new BaseResponse(request.getMsgId(), MessageCode.SUCCESS.getCode(), return new BaseResponse(request.getMsgId(), MessageCode.SUCCESS.getCode(),
MessageCode.SUCCESS.getDescription(), fileResponse); MessageCode.SUCCESS.getDescription(), fileResponse);
} catch (NotFoundAuthToken e) {
InitFileResponse initFileResponse = InitFileResponse.builder()
.build();
throw new NotValidFieldException("Invalid or expired token: " + fileRequest.getToken(),
new BaseResponse(request.getMsgId(), MessageCode.INVALID_TOKEN.getCode(),
MessageCode.INVALID_TOKEN.getDescription(), initFileResponse));
}
} }
private BaseResponse handleGetProgress(BaseRequest request, FileUploadRequest fileRequest) { private BaseResponse handleGetProgress(BaseRequest request, FileUploadRequest fileRequest) {
@@ -7,9 +7,12 @@ import ru.soune.nocopy.dto.file.FileUploadRequest;
import ru.soune.nocopy.entity.file.FileType; import ru.soune.nocopy.entity.file.FileType;
import java.util.Arrays; import java.util.Arrays;
import java.util.List;
@Component @Component
public class FileUploadRequestValidator implements Validator { public class FileUploadRequestValidator implements Validator {
private final List<FileType> supportedFileTypes = Arrays.asList(FileType.values());
@Override @Override
public boolean supports(Class<?> clazz) { public boolean supports(Class<?> clazz) {
return FileUploadRequest.class.isAssignableFrom(clazz); return FileUploadRequest.class.isAssignableFrom(clazz);
@@ -21,7 +24,7 @@ public class FileUploadRequestValidator implements Validator {
validateType(request.getFileType(), errors); validateType(request.getFileType(), errors);
validateFileName(request.getFileName(), errors); validateFileName(request.getFileName(), errors);
validateExtension(request.getExtension(), errors); validateExtension(request.getExtension(), request.getFileName(), errors);
} }
private void validateType(String fileType, Errors errors) { private void validateType(String fileType, Errors errors) {
@@ -31,7 +34,12 @@ public class FileUploadRequestValidator implements Validator {
} }
try { try {
FileType.valueOf(fileType.toUpperCase()); FileType parsedType = FileType.valueOf(fileType.toUpperCase());
if (!supportedFileTypes.contains(parsedType)) {
errors.rejectValue("fileType", "fileType.unsupported",
"Unsupported file type. Valid types: " + Arrays.toString(FileType.values()));
}
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
errors.rejectValue("fileType", "fileType.invalid", errors.rejectValue("fileType", "fileType.invalid",
"Invalid file type. Valid types: " + Arrays.toString(FileType.values())); "Invalid file type. Valid types: " + Arrays.toString(FileType.values()));
@@ -50,10 +58,50 @@ public class FileUploadRequestValidator implements Validator {
} }
} }
private void validateExtension(String extension, Errors errors) { private void validateExtension(String fileType, String fileName, Errors errors) {
if (extension == null || extension.isBlank()) { if (fileType == null || fileType.isBlank()) {
errors.rejectValue("extension", "extension.required", "Extension is required"); errors.rejectValue("fileType", "fileType.required", "File type is required");
return; return;
} }
String fileExtension = getFileExtension(fileName);
if (fileExtension == null || fileExtension.isBlank()) {
errors.rejectValue("fileName", "fileName.invalid.extension", "File must have an extension");
return;
}
if (fileExtension.contains(".")) {
errors.rejectValue("extension", "extension.required", "Extension contains comma");
return;
}
try {
FileType parsedType = FileType.valueOf(fileType.toUpperCase());
if (!supportedFileTypes.contains(parsedType)) {
errors.rejectValue("fileType", "fileType.unsupported",
"Unsupported file type. Supported types: " + supportedFileTypes);
return;
}
if (!parsedType.supportsExtension(fileExtension)) {
errors.rejectValue("fileName", "fileType.extension.mismatch",
String.format("File extension '%s' does not match file type '%s'. Allowed extensions for %s: %s",
fileExtension, parsedType.getDisplayName(), parsedType.getDisplayName(),
parsedType.getAllowedExtensions()));
}
} catch (IllegalArgumentException e) {
errors.rejectValue("fileType", "fileType.invalid",
"Invalid file type. Valid types: " + Arrays.toString(FileType.values()));
}
}
private String getFileExtension(String fileName) {
if (fileName == null || fileName.lastIndexOf('.') == -1) {
return null;
}
return fileName.substring(fileName.lastIndexOf('.') + 1).toLowerCase();
} }
} }