diff --git a/file-manager/src/main/resources/reference.conf b/file-manager/src/main/resources/reference.conf index 22d0d2f2..24118e9a 100644 --- a/file-manager/src/main/resources/reference.conf +++ b/file-manager/src/main/resources/reference.conf @@ -1 +1,3 @@ -interop-commons.storage.max-concurrency = 50 \ No newline at end of file +interop-commons.storage.max-concurrency = 50 +get-url-duration-minutes = 5 +put-url-duration-minutes = 1 \ No newline at end of file diff --git a/file-manager/src/main/scala/it/pagopa/interop/commons/files/StorageConfiguration.scala b/file-manager/src/main/scala/it/pagopa/interop/commons/files/StorageConfiguration.scala index 89bc5f96..5014f5e8 100644 --- a/file-manager/src/main/scala/it/pagopa/interop/commons/files/StorageConfiguration.scala +++ b/file-manager/src/main/scala/it/pagopa/interop/commons/files/StorageConfiguration.scala @@ -3,5 +3,7 @@ package it.pagopa.interop.commons.files import com.typesafe.config.ConfigFactory object StorageConfiguration { - val maxConcurrency: Int = ConfigFactory.load().getInt("interop-commons.storage.max-concurrency") + val maxConcurrency: Int = ConfigFactory.load().getInt("interop-commons.storage.max-concurrency") + val getUrlDurationMinutes: Int = ConfigFactory.load().getInt("get-url-duration-minutes") + val putUrlDurationMinutes: Int = ConfigFactory.load().getInt("put-url-duration-minutes") } diff --git a/file-manager/src/main/scala/it/pagopa/interop/commons/files/service/FileManager.scala b/file-manager/src/main/scala/it/pagopa/interop/commons/files/service/FileManager.scala index a6d219fa..bac9cd46 100644 --- a/file-manager/src/main/scala/it/pagopa/interop/commons/files/service/FileManager.scala +++ b/file-manager/src/main/scala/it/pagopa/interop/commons/files/service/FileManager.scala @@ -42,7 +42,9 @@ trait FileManager { def close(): Unit - def generatePresignedUrl(containerPath: String, path: String): Try[String] + def generateGetPresignedUrl(containerPath: String, path: String): Try[String] + + def generatePutPresignedUrl(containerPath: String, path: String): Try[String] } object FileManager { diff --git a/file-manager/src/main/scala/it/pagopa/interop/commons/files/service/impl/FileManagerImpl.scala b/file-manager/src/main/scala/it/pagopa/interop/commons/files/service/impl/FileManagerImpl.scala index 2382ffbe..7e72edc6 100644 --- a/file-manager/src/main/scala/it/pagopa/interop/commons/files/service/impl/FileManagerImpl.scala +++ b/file-manager/src/main/scala/it/pagopa/interop/commons/files/service/impl/FileManagerImpl.scala @@ -86,5 +86,7 @@ final class FileManagerImpl(blockingExecutionContext: ExecutionContextExecutor) pathCreated.resolve(fileName.stripMargin('/')) } - override def generatePresignedUrl(containerPath: String, path: String): Try[String] = Try(path) + override def generateGetPresignedUrl(containerPath: String, path: String): Try[String] = Try(path) + + override def generatePutPresignedUrl(containerPath: String, path: String): Try[String] = Try(path) } diff --git a/file-manager/src/main/scala/it/pagopa/interop/commons/files/service/impl/S3ManagerImpl.scala b/file-manager/src/main/scala/it/pagopa/interop/commons/files/service/impl/S3ManagerImpl.scala index 442c87aa..9e5256f6 100644 --- a/file-manager/src/main/scala/it/pagopa/interop/commons/files/service/impl/S3ManagerImpl.scala +++ b/file-manager/src/main/scala/it/pagopa/interop/commons/files/service/impl/S3ManagerImpl.scala @@ -14,7 +14,7 @@ import software.amazon.awssdk.http.async.SdkAsyncHttpClient import software.amazon.awssdk.http.nio.netty.NettyNioAsyncHttpClient import software.amazon.awssdk.services.s3.model._ import software.amazon.awssdk.services.s3.presigner.S3Presigner -import software.amazon.awssdk.services.s3.presigner.model.GetObjectPresignRequest +import software.amazon.awssdk.services.s3.presigner.model.{GetObjectPresignRequest, PutObjectPresignRequest} import software.amazon.awssdk.services.s3.{S3AsyncClient, S3AsyncClientBuilder, S3Configuration} import java.io.{ByteArrayOutputStream, File} @@ -167,22 +167,41 @@ final class S3ManagerImpl(blockingExecutionContext: ExecutionContextExecutor)( def calcContentMd5(byteArray: Array[Byte]): String = new String(Base64.encodeBase64(DigestUtils.md5(byteArray))) - override def generatePresignedUrl(containerPath: String, path: String): Try[String] = { + override def generateGetPresignedUrl(containerPath: String, path: String): Try[String] = { Using(S3Presigner.create()) { s3Presigner => - val getObjectRequest = GetObjectRequest + val objectRequest: GetObjectRequest = GetObjectRequest .builder() .bucket(containerPath) .key(path) .build() - val presignRequest = GetObjectPresignRequest + val presignRequest: GetObjectPresignRequest = GetObjectPresignRequest .builder() - .signatureDuration(Duration.ofMinutes(1)) - .getObjectRequest(getObjectRequest) + .signatureDuration(Duration.ofMinutes(StorageConfiguration.getUrlDurationMinutes.toLong)) + .getObjectRequest(objectRequest) .build() val presignedGetObjectRequest = s3Presigner.presignGetObject(presignRequest) presignedGetObjectRequest.url().toString } } + + override def generatePutPresignedUrl(containerPath: String, path: String): Try[String] = { + Using(S3Presigner.create()) { s3Presigner => + val objectRequest: PutObjectRequest = PutObjectRequest + .builder() + .bucket(containerPath) + .key(path) + .build() + + val presignRequest: PutObjectPresignRequest = PutObjectPresignRequest + .builder() + .signatureDuration(Duration.ofMinutes(StorageConfiguration.putUrlDurationMinutes.toLong)) + .putObjectRequest(objectRequest) + .build() + + val presignedGetObjectRequest = s3Presigner.presignPutObject(presignRequest) + presignedGetObjectRequest.url().toString + } + } } diff --git a/file-manager/src/test/scala/it/pagopa/interop/commons/files/S3FileManagerTest.scala b/file-manager/src/test/scala/it/pagopa/interop/commons/files/S3FileManagerTest.scala index 8ad00e15..8671eab4 100644 --- a/file-manager/src/test/scala/it/pagopa/interop/commons/files/S3FileManagerTest.scala +++ b/file-manager/src/test/scala/it/pagopa/interop/commons/files/S3FileManagerTest.scala @@ -65,8 +65,8 @@ class S3FileManagerTest "have a single file with just one is written" in { val filesAndContent: Future[(List[String], String)] = for { - _ <- fileManager.storeBytes("testBucket", "testFolder", "testFile")("testFile".getBytes()) - files <- fileManager.listFiles("testBucket")("/testFolder") + _ <- fileManager.storeBytes("testBucket", "testFolder", "testFile")("testFile".getBytes()) + files <- fileManager.listFiles("testBucket")("/testFolder") content <- fileManager.getFile("testBucket")("/testFolder/testFile").map(new String(_)) } yield (files, content) @@ -77,10 +77,10 @@ class S3FileManagerTest "have two files, one of whom in the root dir" in { val filesAndContent: Future[(List[String], String, String)] = for { - _ <- fileManager.storeBytes("testBucket", "", "rootFile")("rootFile".getBytes()) - _ <- fileManager.storeBytes("testBucket", "testFolder", "testFile")("testFile".getBytes()) - files <- fileManager.listFiles("testBucket")("") - content <- fileManager.getFile("testBucket")("/testFolder/testFile").map(new String(_)) + _ <- fileManager.storeBytes("testBucket", "", "rootFile")("rootFile".getBytes()) + _ <- fileManager.storeBytes("testBucket", "testFolder", "testFile")("testFile".getBytes()) + files <- fileManager.listFiles("testBucket")("") + content <- fileManager.getFile("testBucket")("/testFolder/testFile").map(new String(_)) rootContent <- fileManager.getFile("testBucket")("/rootFile").map(new String(_)) } yield (files, content, rootContent) @@ -91,23 +91,27 @@ class S3FileManagerTest } "get all the files" in { val filesF: Future[Map[String, Array[Byte]]] = for { - _ <- fileManager.storeBytes("testBucket", "", "rootFile")("rootFile".getBytes()) - _ <- fileManager.storeBytes("testBucket", "testFolder", "testFile")("testFile".getBytes()) - _ <- fileManager.storeBytes("testBucket", "testFolder/nestedFolder", "nestedFile")("nestedFile".getBytes()) + _ <- fileManager.storeBytes("testBucket", "", "rootFile")("rootFile".getBytes()) + _ <- fileManager.storeBytes("testBucket", "testFolder", "testFile")("testFile".getBytes()) + _ <- fileManager.storeBytes("testBucket", "testFolder/nestedFolder", "nestedFile")("nestedFile".getBytes()) files <- fileManager.getAllFiles("testBucket")("") } yield files - val files = filesF.futureValue + val files = filesF.futureValue val contentMap = files.map { case (k, v) => (k, new String(v)) } - val expected = Map( - "rootFile" -> "rootFile", - "testFolder/testFile" -> "testFile", + val expected = Map( + "rootFile" -> "rootFile", + "testFolder/testFile" -> "testFile", "testFolder/nestedFolder/nestedFile" -> "nestedFile" ) assert(contentMap == expected) } - "Get presigned urls" in { - val url = fileManager.generatePresignedUrl("testBucket", "testFolder/testFile").get + "Get presigned url" in { + val url = fileManager.generateGetPresignedUrl("testBucket", "testFolder/testFile").get + assert(url.nonEmpty) + } + "Put presigned url" in { + val url = fileManager.generatePutPresignedUrl("testBucket", "testFolder/testFile").get assert(url.nonEmpty) } }