Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package cloudpage.controller;

import cloudpage.dto.FolderContentItemDto;
import cloudpage.dto.FolderDto;
import cloudpage.dto.PageResponseDto;
import cloudpage.service.FolderService;
import cloudpage.service.UserService;
import java.io.IOException;
Expand All @@ -27,6 +29,24 @@ public FolderDto getFolderByPath(@RequestParam String path) throws IOException {
return folderService.getFolderTree(user.getRootFolderPath(), path);
}

@GetMapping("/content")
public PageResponseDto<FolderContentItemDto> getFolderContent(
@RequestParam(required = false, defaultValue = "") String path,
@RequestParam int page,
@RequestParam int size,
@RequestParam(required = false) String sort)
throws IOException {
if (page < 0) {
throw new IllegalArgumentException("page must be greater than or equal to 0");
}
if (size <= 0) {
throw new IllegalArgumentException("size must be greater than 0");
}

var user = userService.getCurrentUser();
return folderService.getFolderContentPage(user.getRootFolderPath(), path, page, size, sort);
}

@PostMapping
public FolderDto createFolder(@RequestParam String parentPath, @RequestParam String name)
throws IOException {
Expand Down
17 changes: 17 additions & 0 deletions backend/src/main/java/cloudpage/dto/FolderContentItemDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package cloudpage.dto;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
@AllArgsConstructor
public class FolderContentItemDto {

private String name;
private String path;
private boolean directory;
private long size;
private String mimeType;
}
17 changes: 17 additions & 0 deletions backend/src/main/java/cloudpage/dto/PageResponseDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package cloudpage.dto;

import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
@AllArgsConstructor
public class PageResponseDto<T> {

private List<T> content;
private long totalElements;
private int totalPages;
private int pageNumber;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package cloudpage.exceptions;

/** Exception for file access and read operation failures */
public class FileAccessException extends RuntimeException {
private static final long serialVersionUID = 1L;

public FileAccessException() {}

public FileAccessException(String message) {
super(message);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,14 @@ public ResponseEntity<String> handleInvalidPathException(InvalidPathException ex
public ResponseEntity<String> handleFileDeletionException(FileDeletionException ex) {
return new ResponseEntity<>(ex.getMessage(), HttpStatus.BAD_REQUEST);
}

@ExceptionHandler(FileAccessException.class)
public ResponseEntity<String> handleFileAccessException(FileAccessException ex) {
return new ResponseEntity<>(ex.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
}

@ExceptionHandler(IllegalArgumentException.class)
public ResponseEntity<String> handleIllegalArgumentException(IllegalArgumentException ex) {
return new ResponseEntity<>(ex.getMessage(), HttpStatus.BAD_REQUEST);
}
}
35 changes: 32 additions & 3 deletions backend/src/main/java/cloudpage/service/FileService.java
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,38 @@ public String readFileContent(String rootPath, String relativeFilePath) throws I
return Files.readString(file);
}

private void validatePath(String rootPath, Path path) {
if (!path.toAbsolutePath().startsWith(Paths.get(rootPath).toAbsolutePath())) {
throw new InvalidPathException("Access outside the user's root folder is forbidden: " + path);
private void validatePath(String rootPath, Path path) throws IOException {
Path rootReal = Paths.get(rootPath).toRealPath().normalize();
Path pathReal;

// If path exists, resolve symlinks to get the real path
if (Files.exists(path)) {
pathReal = path.toRealPath().normalize();
} else {
// For non-existent paths, resolve the parent if it exists
Path parent = path.getParent();
if (parent != null && Files.exists(parent)) {
Path parentReal = parent.toRealPath().normalize();
// Check if the resolved parent is within root
if (!parentReal.startsWith(rootReal)) {
throw new InvalidPathException("Path traversal attempt detected: " + path);
}
// Construct the child path from the resolved parent
Path fileName = path.getFileName();
if (fileName != null) {
pathReal = parentReal.resolve(fileName).normalize();
} else {
pathReal = parentReal;
}
} else {
// Parent doesn't exist or is null, validate using absolute path
// This is a fallback for edge cases
pathReal = path.toAbsolutePath().normalize();
}
}

if (!pathReal.startsWith(rootReal)) {
throw new InvalidPathException("Path traversal attempt detected: " + path);
}
}

Expand Down
Loading