diff --git a/.gitignore b/.gitignore index 95877e91c6b9b631e2dc0c2364b89ee0a9991584..93b7680ae1b0be06d7ec891bbbcc07473b3a38ef 100644 --- a/.gitignore +++ b/.gitignore @@ -37,5 +37,5 @@ out/ ### VS Code ### .vscode/ /develop/.env -vault-cluster-vault-2024-03-29T19_23_51.689Z.json -vault-cluster-vault-2025-05-20T07_47_01.633Z.json +vault-kadi-prod.json +vault-omidb-stage.json diff --git a/src/main/java/cz/tul/cxi/DDRcore/component/S3Component.java b/src/main/java/cz/tul/cxi/DDRcore/component/S3Component.java index b12f643c4e182c54757fd654315f739d7aca6ec0..75ea65e84b96e79a2ff9c157abcd85205f622823 100644 --- a/src/main/java/cz/tul/cxi/DDRcore/component/S3Component.java +++ b/src/main/java/cz/tul/cxi/DDRcore/component/S3Component.java @@ -4,6 +4,8 @@ import cz.tul.cxi.DDRcore.model.BucketPolicy; import cz.tul.cxi.DDRcore.model.S3Bucket; import cz.tul.cxi.DDRcore.model.S3Object; import cz.tul.cxi.DDRcore.model.Statement; + +import java.io.IOException; import java.nio.file.Path; import java.time.Duration; import java.util.HashSet; @@ -12,6 +14,7 @@ import java.util.Map; import java.util.Set; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; +import org.springframework.web.multipart.MultipartFile; import software.amazon.awssdk.core.ResponseInputStream; import software.amazon.awssdk.core.exception.SdkClientException; import software.amazon.awssdk.core.sync.RequestBody; @@ -279,4 +282,27 @@ public class S3Component { .build(); s3Client.putObject(putRequest, sourcePath); } + + public void putFile(String bucketName, String key, MultipartFile file) throws IOException, S3Exception { + PutObjectRequest objectRequest = + PutObjectRequest.builder().bucket(bucketName).key(key).build(); + + s3Client.putObject( + objectRequest, + RequestBody.fromInputStream(file.getInputStream(), file.getSize())); + } + + public boolean fileExists(String bucketName, String key) throws S3Exception { + try { + HeadObjectRequest headObjectRequest = HeadObjectRequest.builder() + .bucket(bucketName) + .key(key) + .build(); + + s3Client.headObject(headObjectRequest); + return true; + } catch (NoSuchKeyException e) { + return false; + } + } } diff --git a/src/main/java/cz/tul/cxi/DDRcore/controller/RestController.java b/src/main/java/cz/tul/cxi/DDRcore/controller/RestController.java index 4f9d20008c76fbf99c635fc33a22e97421239c37..ca9136f41fc9f239fe85b9501994235f5d4ec922 100644 --- a/src/main/java/cz/tul/cxi/DDRcore/controller/RestController.java +++ b/src/main/java/cz/tul/cxi/DDRcore/controller/RestController.java @@ -1,6 +1,7 @@ package cz.tul.cxi.DDRcore.controller; import com.fasterxml.jackson.core.JsonProcessingException; +import cz.tul.cxi.DDRcore.exception.FileExistsException; import cz.tul.cxi.DDRcore.model.Credentials; import cz.tul.cxi.DDRcore.model.S3Bucket; import cz.tul.cxi.DDRcore.model.S3Object; @@ -13,6 +14,7 @@ import org.springframework.http.HttpStatusCode; import org.springframework.http.ResponseEntity; import org.springframework.lang.NonNull; import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; import software.amazon.awssdk.core.exception.SdkClientException; import software.amazon.awssdk.services.s3.model.S3Exception; @@ -311,9 +313,52 @@ public class RestController { @DeleteMapping("/objects/delete") public ResponseEntity deleteObject( - @RequestParam String bucketName, @RequestParam String key) { - s3Service.deleteObject(bucketName, key); - return ResponseEntity.ok("Object deleted successfully."); + @RequestParam String bucketName, @RequestParam String name, @RequestParam @NonNull String einfraId) { + try { + verifyVaultCredentials(einfraId); + s3Service.deleteObject(bucketName, name); + return ResponseEntity.ok("Object deleted successfully."); + } catch (S3Exception ex) { + return handleS3Exception(ex, bucketName); + } + } + + @PostMapping("/file/upsert") + public ResponseEntity uploadFile( + @RequestParam @NonNull String bucketName, + @RequestParam @NonNull String name, + @RequestParam @NonNull String einfraId, + @RequestBody MultipartFile file) { + try { + verifyVaultCredentials(einfraId); + s3Service.putFile(bucketName, name, file); + return ResponseEntity.ok("File uploaded."); + } catch (S3Exception ex) { + return handleS3Exception(ex, bucketName); + } catch (IOException e) { + return handleIOException( + e, "IOException for bucketName: %s, name: %s.".formatted(bucketName, name)); + } + } + + @PostMapping("/file/upload") + public ResponseEntity uploadFileCheckIfExists( + @RequestParam @NonNull String bucketName, + @RequestParam @NonNull String name, + @RequestParam @NonNull String einfraId, + @RequestBody MultipartFile file) { + try { + verifyVaultCredentials(einfraId); + s3Service.putFileCheckIfExists(bucketName, name, file); + return ResponseEntity.ok("File uploaded."); + } catch (FileExistsException ex) { + return handleFileExistsException(ex); + } catch (S3Exception ex) { + return handleS3Exception(ex, bucketName); + } catch (IOException e) { + return handleIOException( + e, "IOException for bucketName: %s, name: %s.".formatted(bucketName, name)); + } } private void verifyVaultCredentials(String eInfraId) { @@ -347,4 +392,9 @@ public class RestController { return ResponseEntity.badRequest() .body("Access denied. Einfra ID: %s, bucketName: %s".formatted(einfraId, bucketName)); } + + private ResponseEntity handleFileExistsException(FileExistsException ex) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) + .body(ex.getMessage()); + } } diff --git a/src/main/java/cz/tul/cxi/DDRcore/exception/FileExistsException.java b/src/main/java/cz/tul/cxi/DDRcore/exception/FileExistsException.java new file mode 100644 index 0000000000000000000000000000000000000000..48e27f5b1d5fe16becbdb3ec000a582bdb92fd1e --- /dev/null +++ b/src/main/java/cz/tul/cxi/DDRcore/exception/FileExistsException.java @@ -0,0 +1,7 @@ +package cz.tul.cxi.DDRcore.exception; + +public class FileExistsException extends RuntimeException { + public FileExistsException(String bucketName, String key) { + super("File already exists: " + bucketName + "/" + key); + } +} diff --git a/src/main/java/cz/tul/cxi/DDRcore/service/S3Service.java b/src/main/java/cz/tul/cxi/DDRcore/service/S3Service.java index 56fba1a9cf818ae229457b75eb119dc78b7802a6..94fb06dccb75055e39ffd16b94b2738859313a5e 100644 --- a/src/main/java/cz/tul/cxi/DDRcore/service/S3Service.java +++ b/src/main/java/cz/tul/cxi/DDRcore/service/S3Service.java @@ -10,6 +10,7 @@ import java.util.Set; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; +import cz.tul.cxi.DDRcore.exception.FileExistsException; import org.springframework.stereotype.Service; import com.fasterxml.jackson.core.JsonProcessingException; @@ -22,6 +23,7 @@ import cz.tul.cxi.DDRcore.model.S3Bucket; import cz.tul.cxi.DDRcore.model.S3Object; import cz.tul.cxi.DDRcore.model.Statement; import lombok.extern.slf4j.Slf4j; +import org.springframework.web.multipart.MultipartFile; import software.amazon.awssdk.core.ResponseInputStream; import software.amazon.awssdk.core.exception.SdkClientException; import software.amazon.awssdk.core.exception.SdkException; @@ -395,4 +397,16 @@ public class S3Service { public void updateBucketPolicy(String bucketName, String policyJson) { s3Component.updateBucketPolicy(bucketName, policyJson); } + + public void putFile(String bucketName, String name, MultipartFile file) throws IOException, S3Exception { + s3Component.putFile(bucketName, name, file); + } + + public void putFileCheckIfExists(String bucketName, String name, MultipartFile file) throws IOException, S3Exception { + if(s3Component.fileExists(bucketName, name)) { + throw new FileExistsException(bucketName, name); + } + + s3Component.putFile(bucketName, name, file); + } }