diff --git a/datasafe-rest-impl/src/main/java/de/adorsys/datasafe/rest/impl/controller/AuthenticateController.java b/datasafe-rest-impl/src/main/java/de/adorsys/datasafe/rest/impl/controller/AuthenticateController.java index 59f79c43c..a876601d8 100644 --- a/datasafe-rest-impl/src/main/java/de/adorsys/datasafe/rest/impl/controller/AuthenticateController.java +++ b/datasafe-rest-impl/src/main/java/de/adorsys/datasafe/rest/impl/controller/AuthenticateController.java @@ -15,6 +15,7 @@ import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.userdetails.User; +import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; @@ -31,7 +32,7 @@ public class AuthenticateController { private final AuthenticationManager authenticationManager; @PostMapping(SecurityConstants.AUTH_LOGIN_URL) - public void authenticate(@RequestBody UserDTO credentialsDTO, HttpServletResponse response) { + public void authenticate(@RequestBody @Validated UserDTO credentialsDTO, HttpServletResponse response) { String username = credentialsDTO.getUserName(); String password = credentialsDTO.getPassword(); UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password); diff --git a/datasafe-rest-impl/src/main/java/de/adorsys/datasafe/rest/impl/controller/DocumentController.java b/datasafe-rest-impl/src/main/java/de/adorsys/datasafe/rest/impl/controller/DocumentController.java index d28e74c07..6c1209207 100644 --- a/datasafe-rest-impl/src/main/java/de/adorsys/datasafe/rest/impl/controller/DocumentController.java +++ b/datasafe-rest-impl/src/main/java/de/adorsys/datasafe/rest/impl/controller/DocumentController.java @@ -12,6 +12,8 @@ import de.adorsys.datasafe.types.api.resource.PrivateResource; import de.adorsys.datasafe.types.api.resource.StorageIdentifier; import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; @@ -49,11 +51,16 @@ public class DocumentController { */ @SneakyThrows @GetMapping(value = "/document/{*path}", produces = APPLICATION_OCTET_STREAM_VALUE) - public void readDocument(@RequestHeader String user, - @RequestHeader String password, + public void readDocument(@RequestHeader @NotBlank String user, + @RequestHeader @NotBlank String password, @RequestHeader(defaultValue = StorageIdentifier.DEFAULT_ID) String storageId, - @PathVariable String path, + @PathVariable @NotBlank String path, HttpServletResponse response) { + // Validate and sanitize path + if (path.contains("..")) { + throw new IllegalArgumentException("Invalid path"); + } + UserIDAuth userIDAuth = new UserIDAuth(new UserID(user), ReadKeyPasswordHelper.getForString(password)); ReadRequest request = ReadRequest.forPrivate(userIDAuth, new StorageIdentifier(storageId), path); @@ -72,11 +79,16 @@ public void readDocument(@RequestHeader String user, */ @SneakyThrows @PutMapping(value = "/document/{*path}", consumes = MULTIPART_FORM_DATA_VALUE) - public void writeDocument(@RequestHeader String user, - @RequestHeader String password, + public void writeDocument(@RequestHeader @NotBlank String user, + @RequestHeader @NotBlank String password, @RequestHeader(defaultValue = StorageIdentifier.DEFAULT_ID) String storageId, @PathVariable String path, - @RequestParam("file") MultipartFile file) { + @RequestParam("file") @NotNull MultipartFile file) { + // Validate and sanitize path + if (path.contains("..")) { + throw new IllegalArgumentException("Invalid path"); + } + UserIDAuth userIDAuth = new UserIDAuth(new UserID(user), ReadKeyPasswordHelper.getForString(password)); WriteRequest request = WriteRequest.forPrivate(userIDAuth, new StorageIdentifier(storageId), path); @@ -91,14 +103,20 @@ public void writeDocument(@RequestHeader String user, * lists files in user's private space. */ @GetMapping("/documents/{*path}") - public List listDocuments(@RequestHeader String user, - @RequestHeader String password, + public List listDocuments(@RequestHeader @NotBlank String user, + @RequestHeader @NotBlank String password, @RequestHeader(defaultValue = StorageIdentifier.DEFAULT_ID) String storageId, @PathVariable(required = false) String path) { UserIDAuth userIDAuth = new UserIDAuth(new UserID(user), ReadKeyPasswordHelper.getForString(password)); path = Optional.ofNullable(path) .map(it -> it.replaceAll("^\\.$", "")) .orElse("./"); + + // Validate and sanitize path + if (path.contains("..")) { + throw new IllegalArgumentException("Invalid path"); + } + try { List documentList = datasafeService.privateService().list( ListRequest.forPrivate(userIDAuth, new StorageIdentifier(storageId), path)) @@ -115,10 +133,16 @@ public List listDocuments(@RequestHeader String user, * deletes files from user's private space. */ @DeleteMapping("/document/{*path}") - public void removeDocument(@RequestHeader String user, - @RequestHeader String password, + public void removeDocument(@RequestHeader @NotBlank String user, + @RequestHeader @NotBlank String password, @RequestHeader(defaultValue = StorageIdentifier.DEFAULT_ID) String storageId, - @PathVariable String path) { + @PathVariable @NotBlank String path) { + + // Validate and sanitize path + if (path.contains("..")) { + throw new IllegalArgumentException("Invalid path"); + } + UserIDAuth userIDAuth = new UserIDAuth(new UserID(user), ReadKeyPasswordHelper.getForString(password)); RemoveRequest request = RemoveRequest.forPrivate(userIDAuth, new StorageIdentifier(storageId), path); diff --git a/datasafe-rest-impl/src/main/java/de/adorsys/datasafe/rest/impl/controller/InboxController.java b/datasafe-rest-impl/src/main/java/de/adorsys/datasafe/rest/impl/controller/InboxController.java index 5f86a1f1b..42ed50988 100644 --- a/datasafe-rest-impl/src/main/java/de/adorsys/datasafe/rest/impl/controller/InboxController.java +++ b/datasafe-rest-impl/src/main/java/de/adorsys/datasafe/rest/impl/controller/InboxController.java @@ -12,6 +12,8 @@ import de.adorsys.datasafe.types.api.resource.BasePrivateResource; import de.adorsys.datasafe.types.api.resource.PrivateResource; import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; @@ -52,10 +54,10 @@ public class InboxController { */ @SneakyThrows @PutMapping(value = "/inbox/document/{*path}", consumes = MULTIPART_FORM_DATA_VALUE) - public void writeToInbox(@RequestHeader String user, - @RequestHeader String password, - @RequestHeader Set recipients, - @PathVariable String path, + public void writeToInbox(@RequestHeader @NotBlank String user, + @RequestHeader @NotBlank String password, + @RequestHeader Set<@NotBlank String> recipients, + @PathVariable @NotBlank String path, @RequestParam("file") MultipartFile file) { UserIDAuth fromUser = new UserIDAuth(new UserID(user), ReadKeyPasswordHelper.getForString(password)); Set toUsers = recipients.stream().map(UserID::new).collect(Collectors.toSet()); @@ -72,9 +74,9 @@ public void writeToInbox(@RequestHeader String user, */ @SneakyThrows @GetMapping(value = "/inbox/document/{*path}", produces = APPLICATION_OCTET_STREAM_VALUE) - public void readFromInbox(@RequestHeader String user, - @RequestHeader String password, - @PathVariable String path, + public void readFromInbox(@RequestHeader @NotBlank String user, + @RequestHeader @NotBlank String password, + @PathVariable @NotBlank String path, HttpServletResponse response) { path = path.replaceAll("^/", ""); UserIDAuth userIDAuth = new UserIDAuth(new UserID(user), ReadKeyPasswordHelper.getForString(password)); @@ -93,9 +95,9 @@ public void readFromInbox(@RequestHeader String user, * Deletes file from users' INBOX. */ @DeleteMapping("/inbox/document/{*path}") - public void deleteFromInbox(@RequestHeader String user, - @RequestHeader String password, - @PathVariable String path) { + public void deleteFromInbox(@RequestHeader @NotBlank String user, + @RequestHeader @NotBlank String password, + @PathVariable @NotBlank String path) { path = path.replaceAll("^/", ""); UserIDAuth userIDAuth = new UserIDAuth(new UserID(user), ReadKeyPasswordHelper.getForString(password)); PrivateResource resource = BasePrivateResource.forPrivate(path); @@ -108,8 +110,8 @@ public void deleteFromInbox(@RequestHeader String user, * list files in users' INBOX. */ @GetMapping(value = "/inbox/documents/{*path}", produces = APPLICATION_JSON_VALUE) - public List listInbox(@RequestHeader String user, - @RequestHeader String password, + public List listInbox(@RequestHeader @NotBlank String user, + @RequestHeader @NotBlank String password, @PathVariable(required = false) String path) { path = path.replaceAll("^/", ""); UserIDAuth userIDAuth = new UserIDAuth(new UserID(user), ReadKeyPasswordHelper.getForString(password)); diff --git a/datasafe-rest-impl/src/main/java/de/adorsys/datasafe/rest/impl/controller/UserController.java b/datasafe-rest-impl/src/main/java/de/adorsys/datasafe/rest/impl/controller/UserController.java index 5d78aece9..d9f616d73 100644 --- a/datasafe-rest-impl/src/main/java/de/adorsys/datasafe/rest/impl/controller/UserController.java +++ b/datasafe-rest-impl/src/main/java/de/adorsys/datasafe/rest/impl/controller/UserController.java @@ -14,6 +14,7 @@ import de.adorsys.datasafe.rest.impl.exceptions.UserExistsException; import de.adorsys.datasafe.types.api.resource.StorageIdentifier; import de.adorsys.datasafe.types.api.types.ReadKeyPassword; +import jakarta.validation.constraints.NotBlank; import lombok.RequiredArgsConstructor; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.DeleteMapping; @@ -61,8 +62,8 @@ public void createUser(@Validated @RequestBody UserDTO userDTO) { } @PostMapping("/password") - public void changePassword(@RequestHeader String user, - @RequestHeader String password, + public void changePassword(@RequestHeader @NotBlank String user, + @RequestHeader @NotBlank String password, @Validated @RequestBody NewPasswordDTO newPassword) { ReadKeyPassword readKeyPassword = ReadKeyPasswordHelper.getForString(password); UserIDAuth auth = new UserIDAuth(new UserID(user), readKeyPassword); @@ -70,24 +71,24 @@ public void changePassword(@RequestHeader String user, } @GetMapping("/publicProfile") - public UserPublicProfileDTO getPublicProfile(@RequestHeader String user, - @RequestHeader String password) { + public UserPublicProfileDTO getPublicProfile(@RequestHeader @NotBlank String user, + @RequestHeader @NotBlank String password) { ReadKeyPassword readKeyPassword = ReadKeyPasswordHelper.getForString(password); UserIDAuth auth = new UserIDAuth(new UserID(user), readKeyPassword); return UserPublicProfileDTO.from(dataSafeService.userProfile().publicProfile(auth.getUserID())); } @GetMapping("/privateProfile") - public UserPrivateProfileDTO getPrivateProfile(@RequestHeader String user, - @RequestHeader String password) { + public UserPrivateProfileDTO getPrivateProfile(@RequestHeader @NotBlank String user, + @RequestHeader @NotBlank String password) { ReadKeyPassword readKeyPassword = ReadKeyPasswordHelper.getForString(password); UserIDAuth auth = new UserIDAuth(new UserID(user), readKeyPassword); return UserPrivateProfileDTO.from(dataSafeService.userProfile().privateProfile(auth)); } @PostMapping("/publicProfile") - public void updatePublicProfile(@RequestHeader String user, - @RequestHeader String password, + public void updatePublicProfile(@RequestHeader @NotBlank String user, + @RequestHeader @NotBlank String password, @Validated @RequestBody UserPublicProfileDTO profileDto) { ReadKeyPassword readKeyPassword = ReadKeyPasswordHelper.getForString(password); UserIDAuth auth = new UserIDAuth(new UserID(user), readKeyPassword); @@ -95,8 +96,8 @@ public void updatePublicProfile(@RequestHeader String user, } @PostMapping("/privateProfile") - public void updatePrivateProfile(@RequestHeader String user, - @RequestHeader String password, + public void updatePrivateProfile(@RequestHeader @NotBlank String user, + @RequestHeader @NotBlank String password, @Validated @RequestBody UserPrivateProfileDTO profileDto) { ReadKeyPassword readKeyPassword = ReadKeyPasswordHelper.getForString(password); UserIDAuth auth = new UserIDAuth(new UserID(user), readKeyPassword); @@ -104,8 +105,8 @@ public void updatePrivateProfile(@RequestHeader String user, } @PostMapping("/storages") - public void addStorageCredentials(@RequestHeader String user, - @RequestHeader String password, + public void addStorageCredentials(@RequestHeader @NotBlank String user, + @RequestHeader @NotBlank String password, @Validated @RequestBody StorageCredsDTO creds) { ReadKeyPassword readKeyPassword = ReadKeyPasswordHelper.getForString(password); UserIDAuth auth = new UserIDAuth(new UserID(user), readKeyPassword); @@ -117,9 +118,9 @@ public void addStorageCredentials(@RequestHeader String user, } @DeleteMapping("/storages") - public void removeStorageCredentials(@RequestHeader String user, - @RequestHeader String password, - @RequestHeader String storageId) { + public void removeStorageCredentials(@RequestHeader @NotBlank String user, + @RequestHeader @NotBlank String password, + @RequestHeader @NotBlank String storageId) { ReadKeyPassword readKeyPassword = ReadKeyPasswordHelper.getForString(password); UserIDAuth auth = new UserIDAuth(new UserID(user), readKeyPassword); dataSafeService.userProfile().deregisterStorageCredentials(auth, new StorageIdentifier(storageId)); @@ -132,8 +133,8 @@ public void removeStorageCredentials(@RequestHeader String user, * @param password user password. */ @DeleteMapping - public void deleteUser(@RequestHeader String user, - @RequestHeader String password) { + public void deleteUser(@RequestHeader @NotBlank String user, + @RequestHeader @NotBlank String password) { UserIDAuth auth = new UserIDAuth(new UserID(user), ReadKeyPasswordHelper.getForString(password)); if (!dataSafeService.userProfile().userExists(auth.getUserID())) { throw new UserDoesNotExistsException("user '" + auth.getUserID().getValue() + "' does not exists"); diff --git a/datasafe-rest-impl/src/main/java/de/adorsys/datasafe/rest/impl/controller/VersionController.java b/datasafe-rest-impl/src/main/java/de/adorsys/datasafe/rest/impl/controller/VersionController.java index ac91da98e..cb329260b 100644 --- a/datasafe-rest-impl/src/main/java/de/adorsys/datasafe/rest/impl/controller/VersionController.java +++ b/datasafe-rest-impl/src/main/java/de/adorsys/datasafe/rest/impl/controller/VersionController.java @@ -16,6 +16,7 @@ import de.adorsys.datasafe.types.api.resource.StorageIdentifier; import de.adorsys.datasafe.types.api.resource.Versioned; import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotBlank; import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; @@ -49,10 +50,10 @@ public class VersionController { * lists latest versions of files in user's private space. */ @GetMapping(value = "/versioned/{*path}", produces = APPLICATION_JSON_VALUE) - public List listVersionedDocuments(@RequestHeader String user, - @RequestHeader String password, + public List listVersionedDocuments(@RequestHeader @NotBlank String user, + @RequestHeader @NotBlank String password, @RequestHeader(defaultValue = StorageIdentifier.DEFAULT_ID) String storageId, - @PathVariable(required = false) String path) { + @PathVariable(required = false) String path) { UserIDAuth userIDAuth = new UserIDAuth(new UserID(user), ReadKeyPasswordHelper.getForString(password)); path = path.replaceAll("^/", ""); try { @@ -73,10 +74,10 @@ public List listVersionedDocuments(@RequestHeader String user, */ @SneakyThrows @GetMapping(value = "/versioned/{*path}", produces = APPLICATION_OCTET_STREAM_VALUE) - public void readVersionedDocument(@RequestHeader String user, - @RequestHeader String password, + public void readVersionedDocument(@RequestHeader @NotBlank String user, + @RequestHeader @NotBlank String password, @RequestHeader(defaultValue = StorageIdentifier.DEFAULT_ID) String storageId, - @PathVariable String path, + @PathVariable @NotBlank String path, HttpServletResponse response) { path = path.replaceAll("^/", ""); UserIDAuth userIDAuth = new UserIDAuth(new UserID(user), ReadKeyPasswordHelper.getForString(password)); @@ -97,10 +98,10 @@ public void readVersionedDocument(@RequestHeader String user, */ @SneakyThrows @PutMapping(value = "/versioned/{*path}", consumes = MULTIPART_FORM_DATA_VALUE) - public void writeVersionedDocument(@RequestHeader String user, - @RequestHeader String password, + public void writeVersionedDocument(@RequestHeader @NotBlank String user, + @RequestHeader @NotBlank String password, @RequestHeader(defaultValue = StorageIdentifier.DEFAULT_ID) String storageId, - @PathVariable String path, + @PathVariable @NotBlank String path, @RequestParam("file") MultipartFile file) { path = path.replaceAll("^/", ""); UserIDAuth userIDAuth = new UserIDAuth(new UserID(user), ReadKeyPasswordHelper.getForString(password)); @@ -117,10 +118,10 @@ public void writeVersionedDocument(@RequestHeader String user, * deletes latest version of file from user's private space. */ @DeleteMapping("/versioned/{*path}") - public void deleteVersionedDocument(@RequestHeader String user, - @RequestHeader String password, + public void deleteVersionedDocument(@RequestHeader @NotBlank String user, + @RequestHeader @NotBlank String password, @RequestHeader(defaultValue = StorageIdentifier.DEFAULT_ID) String storageId, - @PathVariable String path) { + @PathVariable @NotBlank String path) { path = path.replaceAll("^/", ""); UserIDAuth userIDAuth = new UserIDAuth(new UserID(user), ReadKeyPasswordHelper.getForString(password)); RemoveRequest request = @@ -133,8 +134,8 @@ public void deleteVersionedDocument(@RequestHeader String user, * list of file versions. */ @GetMapping(value = "/versions/list/{*path}", produces = APPLICATION_JSON_VALUE) - public List versionsOf(@RequestHeader String user, - @RequestHeader String password, + public List versionsOf(@RequestHeader @NotBlank String user, + @RequestHeader @NotBlank String password, @RequestHeader(defaultValue = StorageIdentifier.DEFAULT_ID) String storageId, @PathVariable(required = false) String path) { UserIDAuth userIDAuth = new UserIDAuth(new UserID(user), ReadKeyPasswordHelper.getForString(password)); diff --git a/datasafe-types-api/src/main/java/de/adorsys/datasafe/types/api/resource/BasePrivateResource.java b/datasafe-types-api/src/main/java/de/adorsys/datasafe/types/api/resource/BasePrivateResource.java index 954cb636f..97d6dd870 100644 --- a/datasafe-types-api/src/main/java/de/adorsys/datasafe/types/api/resource/BasePrivateResource.java +++ b/datasafe-types-api/src/main/java/de/adorsys/datasafe/types/api/resource/BasePrivateResource.java @@ -27,7 +27,6 @@ private BasePrivateResource() { this.encryptedPath = EMPTY_URI; } - // TODO: Hide it public BasePrivateResource(Uri containerUri) { this.container = containerUri; this.decryptedPath = EMPTY_URI;