diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 8c8f71677..c9b2fffc0 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -13,12 +13,10 @@ name: "CodeQL" on: push: - branches: [s3mock-v2, main] + branches: [s3mock-v2, s3mock-v3, s3mock-v4, main] pull_request: # The branches below must be a subset of the branches above - branches: [s3mock-v2, main] - schedule: - - cron: '43 21 * * 6' + branches: [s3mock-v2, s3mock-v3, s3mock-v4, main] # Declare default permissions as read only. permissions: read-all @@ -64,12 +62,12 @@ jobs: # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs # queries: security-extended,security-and-quality - # Set up JDK 17, otherwise autobuild will fail below. + # Set up JDK 25, otherwise autobuild will fail below. - name: Set up JDK uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e # v5.1.0 with: - java-version: 21 - distribution: 'temurin' + java-version: 25 + distribution: 'oracle' cache: 'maven' # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). diff --git a/.github/workflows/maven-ci-and-prb.yml b/.github/workflows/maven-ci-and-prb.yml index a2292866e..836054af7 100644 --- a/.github/workflows/maven-ci-and-prb.yml +++ b/.github/workflows/maven-ci-and-prb.yml @@ -20,9 +20,9 @@ name: Maven Build on: push: - branches: [s3mock-v2, main] + branches: [s3mock-v2, s3mock-v3, s3mock-v4, main] pull_request: - branches: [s3mock-v2, main] + branches: [s3mock-v2, s3mock-v3, s3mock-v4, main] # Declare default permissions as read only. permissions: read-all diff --git a/.github/workflows/maven-release.yml b/.github/workflows/maven-release.yml index 8f0c3fd75..743a8f466 100644 --- a/.github/workflows/maven-release.yml +++ b/.github/workflows/maven-release.yml @@ -40,8 +40,9 @@ jobs: - name: Set up JDK uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e # v5.1.0 with: - java-version: 21 - distribution: 'temurin' + java-version: 25 + distribution: 'oracle' + cache: 'maven' # The release build pushes a Docker image to Docker Hub, so we need to log in - name: Perform docker login diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c8b44cb0..41cadc184 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -117,7 +117,7 @@ Whenever a 3rd party library is updated, S3Mock will update it's MINOR version. # PLANNED - 6.x - RELEASE TBD Version 6.x is JDK25 LTS bytecode compatible, with Docker integration. -Probably released with Spring Boot 5.x, updating baselines etc. as Spring Boot 5.x requires. +Will be released after Spring Boot 5.x, updating baselines etc. as Spring Boot 5.x requires. Any JUnit / direct Java usage support will most likely be dropped and only supported on a best-effort basis. (i.e., the modules will be deleted from the code base and not released anymore. It *may* be possible to @@ -149,7 +149,13 @@ Version 5.x is JDK17 LTS bytecode compatible, with Docker and JUnit / direct Jav ## 5.0.0 * Features and fixes + * Breaking change (file system): Remove "DisplayName" from Owner. (fixes #2738) + * AWS APIs stopped returning "DisplayName" in November 2025. + * This is unfortunately a breaking change for clients starting S3Mock on existing file systems. * Get object with range now returns the same headers as non-range calls. + * Docker: Copy "s3mock.jar" to "/opt/", run with absolute path reference to avoid issues when working directory is changed. (fixes #2827) + * S3Mock supports ChecksumType.FULL_OBJECT for Multipart uploads (fixes #2843) + * Return 412 on if-none-match=true when making CompleteMultipartRequest (fixes #2790) * Refactorings * Use Jackson 3 annotations and mappers. * AWS has deprecated SDK for Java v1 and will remove support EOY 2025. @@ -159,14 +165,23 @@ Version 5.x is JDK17 LTS bytecode compatible, with Docker and JUnit / direct Jav * Remove legacy properties for S3Mock configuration. * Move all controller-related code from "com.adobe.testing.s3mock" to "com.adobe.testing.s3mock.controller" package. * Remove Apache libraries like "commons-compress", "commons-codec" or "commons-lang3" from dependencies. Kotlin and Java standard library provide similar functionality. -* Version updates - * Bump Spring Boot version to 4.0.0 +* Version updates (deliverable dependencies) + * Bump Spring Boot version to 4.0.1 * Bump Spring Framework version to 7.0.1 - * Bump java version from 17 to 25 - * Compile with Java 25, target Java 17 + * Bump Java version partially from 17 to 25 + * Compile with Java 25, target Java 17. [This follows Spring guidance](https://spring.io/blog/2025/11/13/spring-framework-7-0-general-availability) * Docker container runs Java 25 * Bump TestContainers to 2.0.2 - * Bump Maven to 4.0.0 + * Bump kotlin.version from 2.2.21 to 2.3.0 + * Compile with Kotlin 2.3, target Kotlin 2.2. [This follows Spring guidance](https://spring.io/blog/2025/12/18/next-level-kotlin-support-in-spring-boot-4#kotlin-2-baseline) +* Version updates (build dependencies) + * Bump Maven to 4.0.0-rc5 (TODO: update to 4.0.0) + * Bump org.apache.maven.plugins:maven-release-plugin from 3.3.0 to 3.3.1 + * Bump com.puppycrawl.tools:checkstyle from 12.2.0 to 12.3.0 + * Bump actions/upload-artifact from 5.0.0 to 6.0.0 + * Bump github/codeql-action from 4.31.6 to 4.31.9 + * Bump actions/setup-java from 5.0.0 to 5.1.0 + * Bump step-security/harden-runner from 2.13.3 to 2.14.0 # DEPRECATED - 4.x Version 4.x is JDK17 LTS bytecode compatible, with Docker and JUnit / direct Java integration. diff --git a/docker/Dockerfile b/docker/Dockerfile index 1ae3c5143..b935c7546 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -59,7 +59,7 @@ ENV JAVA_HOME=/opt/java-minimal ENV PATH="$PATH:$JAVA_HOME/bin" COPY --from=staging_area "$JAVA_HOME" "$JAVA_HOME" -COPY ./target/s3mock-exec.jar s3mock.jar +COPY ./target/s3mock-exec.jar /opt/s3mock.jar ENV LANG=en_US.UTF-8 ENV LANGUAGE=en_US:en @@ -69,4 +69,4 @@ ENV root=/s3mockroot EXPOSE 9090 9191 # run the app on startup -ENTRYPOINT ["java", "--illegal-access=warn", "-Djava.security.egd=file:/dev/./urandom", "-XX:+UseZGC", "-XX:+ZGenerational", "-jar", "s3mock.jar" ] +ENTRYPOINT ["java", "--illegal-access=warn", "-Djava.security.egd=file:/dev/./urandom", "-XX:+UseZGC", "-XX:+ZGenerational", "-jar", "/opt/s3mock.jar" ] diff --git a/integration-tests/src/test/kotlin/com/adobe/testing/s3mock/its/AclIT.kt b/integration-tests/src/test/kotlin/com/adobe/testing/s3mock/its/AclIT.kt index d05ec86a7..530f89067 100644 --- a/integration-tests/src/test/kotlin/com/adobe/testing/s3mock/its/AclIT.kt +++ b/integration-tests/src/test/kotlin/com/adobe/testing/s3mock/its/AclIT.kt @@ -63,7 +63,6 @@ internal class AclIT : S3TestBase() { }.also { resp -> assertThat(resp.sdkHttpResponse().isSuccessful).isTrue() assertThat(resp.owner().id()).isNotBlank() - assertThat(resp.owner().displayName()).isNotBlank() assertThat(resp.grants()).hasSize(1) assertThat(resp.grants().first().permission()).isEqualTo(FULL_CONTROL) } @@ -86,7 +85,6 @@ internal class AclIT : S3TestBase() { acl.owner().also { owner -> assertThat(owner.id()).isEqualTo(DEFAULT_OWNER.id) - assertThat(owner.displayName()).isEqualTo(DEFAULT_OWNER.displayName) } acl.grants().also { @@ -102,7 +100,6 @@ internal class AclIT : S3TestBase() { .also { grantee -> assertThat(grantee).isNotNull assertThat(grantee.id()).isEqualTo(DEFAULT_OWNER.id) - assertThat(grantee.displayName()).isEqualTo(DEFAULT_OWNER.displayName) assertThat(grantee.type()).isEqualTo(CANONICAL_USER) } } @@ -126,7 +123,6 @@ internal class AclIT : S3TestBase() { it.accessControlPolicy { it.owner { it.id(userId) - it.displayName(userName) } it .grants( @@ -150,7 +146,6 @@ internal class AclIT : S3TestBase() { acl.owner().also { assertThat(it).isNotNull assertThat(it.id()).isEqualTo(userId) - assertThat(it.displayName()).isEqualTo(userName) } assertThat(acl.grants()).hasSize(1) diff --git a/integration-tests/src/test/kotlin/com/adobe/testing/s3mock/its/BucketIT.kt b/integration-tests/src/test/kotlin/com/adobe/testing/s3mock/its/BucketIT.kt index 723370e39..9b87e896b 100644 --- a/integration-tests/src/test/kotlin/com/adobe/testing/s3mock/its/BucketIT.kt +++ b/integration-tests/src/test/kotlin/com/adobe/testing/s3mock/its/BucketIT.kt @@ -167,7 +167,6 @@ internal class BucketIT : S3TestBase() { } assertThat(it.prefix()).isNull() assertThat(it.continuationToken()).isNull() - assertThat(it.owner().displayName()).isEqualTo("s3-mock-file-store") assertThat(it.owner().id()).isEqualTo("79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2be") } } @@ -205,7 +204,6 @@ internal class BucketIT : S3TestBase() { } assertThat(it.prefix()).isEqualTo(bucketName) assertThat(it.continuationToken()).isNull() - assertThat(it.owner().displayName()).isEqualTo("s3-mock-file-store") assertThat(it.owner().id()).isEqualTo("79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2be") } } @@ -245,7 +243,6 @@ internal class BucketIT : S3TestBase() { } assertThat(it.prefix()).isNull() assertThat(it.continuationToken()).isNotNull - assertThat(it.owner().displayName()).isEqualTo("s3-mock-file-store") assertThat(it.owner().id()).isEqualTo("79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2be") }.continuationToken() @@ -263,7 +260,6 @@ internal class BucketIT : S3TestBase() { } assertThat(it.prefix()).isNull() assertThat(it.continuationToken()).isNull() - assertThat(it.owner().displayName()).isEqualTo("s3-mock-file-store") assertThat(it.owner().id()).isEqualTo("79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2be") } } diff --git a/integration-tests/src/test/kotlin/com/adobe/testing/s3mock/its/GetPutDeleteObjectIT.kt b/integration-tests/src/test/kotlin/com/adobe/testing/s3mock/its/GetPutDeleteObjectIT.kt index db31228b5..9cf631f75 100644 --- a/integration-tests/src/test/kotlin/com/adobe/testing/s3mock/its/GetPutDeleteObjectIT.kt +++ b/integration-tests/src/test/kotlin/com/adobe/testing/s3mock/its/GetPutDeleteObjectIT.kt @@ -536,7 +536,7 @@ internal class GetPutDeleteObjectIT : S3TestBase() { ) }.also { assertThat(it.eTag()).isEqualTo(eTag.trim('"')) - // default storageClass is STANDARD, which is never returned from APIs except by GetObjectAttributes + // GetObjectAttributes returns the default storageClass "STANDARD", even though other APIs may not. assertThat(it.storageClass()).isEqualTo(StorageClass.STANDARD) assertThat(it.objectSize()).isEqualTo(UPLOAD_FILE_LENGTH) assertThat(it.checksum().checksumSHA1()).isEqualTo(expectedChecksum) diff --git a/integration-tests/src/test/kotlin/com/adobe/testing/s3mock/its/ListObjectsIT.kt b/integration-tests/src/test/kotlin/com/adobe/testing/s3mock/its/ListObjectsIT.kt index 766e1e7c6..d4af7fbb9 100644 --- a/integration-tests/src/test/kotlin/com/adobe/testing/s3mock/its/ListObjectsIT.kt +++ b/integration-tests/src/test/kotlin/com/adobe/testing/s3mock/its/ListObjectsIT.kt @@ -29,6 +29,7 @@ import software.amazon.awssdk.services.s3.model.ChecksumAlgorithm import software.amazon.awssdk.services.s3.model.CommonPrefix import software.amazon.awssdk.services.s3.model.EncodingType import software.amazon.awssdk.services.s3.model.NoSuchBucketException +import software.amazon.awssdk.services.s3.model.ObjectStorageClass import software.amazon.awssdk.services.s3.model.S3Object import software.amazon.awssdk.utils.http.SdkHttpUtils @@ -103,6 +104,14 @@ internal class ListObjectsIT : S3TestBase() { Tuple(arrayListOf(ChecksumAlgorithm.SHA256)), Tuple(arrayListOf(ChecksumAlgorithm.SHA256)), ) + // ListObjects returns the default storageClass "STANDARD", even though other APIs may not. + assertThat(it.contents()) + .hasSize(2) + .extracting(S3Object::storageClass) + .containsOnly( + Tuple(ObjectStorageClass.STANDARD), + Tuple(ObjectStorageClass.STANDARD), + ) } } @@ -168,6 +177,8 @@ internal class ListObjectsIT : S3TestBase() { listing.contents().also { assertThat(it).hasSize(1) assertThat(it[0].key()).isEqualTo(key) + // ListObjectsV2 returns the default storageClass "STANDARD", even though other APIs may not. + assertThat(it[0].storageClass()).isEqualTo(ObjectStorageClass.STANDARD) } } } diff --git a/integration-tests/src/test/kotlin/com/adobe/testing/s3mock/its/MultipartIT.kt b/integration-tests/src/test/kotlin/com/adobe/testing/s3mock/its/MultipartIT.kt index 732c03a0c..7ba5cab42 100644 --- a/integration-tests/src/test/kotlin/com/adobe/testing/s3mock/its/MultipartIT.kt +++ b/integration-tests/src/test/kotlin/com/adobe/testing/s3mock/its/MultipartIT.kt @@ -187,6 +187,174 @@ internal class MultipartIT : S3TestBase() { } } + @Test + @S3VerifiedSuccess(year = 2025) + fun testMultipartUpload_withChecksumType_COMPOSITE(testInfo: TestInfo) { + val bucketName = givenBucket(testInfo) + val initiateMultipartUploadResult = + s3Client + .createMultipartUpload { + it.bucket(bucketName) + it.key(UPLOAD_FILE_NAME) + it.checksumAlgorithm(ChecksumAlgorithm.CRC32) + it.checksumType(ChecksumType.COMPOSITE) + } + val uploadId = initiateMultipartUploadResult.uploadId() + val uploadPartResult = + s3Client.uploadPart( + { + it.bucket(initiateMultipartUploadResult.bucket()) + it.key(initiateMultipartUploadResult.key()) + it.uploadId(uploadId) + it.partNumber(1) + it.checksumAlgorithm(ChecksumAlgorithm.CRC32) + it.contentLength(UPLOAD_FILE_LENGTH) + }, + RequestBody.fromFile(UPLOAD_FILE), + ) + + val checksum = + DigestUtil.checksumMultipart( + listOf(UPLOAD_FILE_PATH), + DefaultChecksumAlgorithm.CRC32, + ) + + s3Client + .completeMultipartUpload { + it.bucket(initiateMultipartUploadResult.bucket()) + it.key(initiateMultipartUploadResult.key()) + it.uploadId(initiateMultipartUploadResult.uploadId()) + it.checksumType(ChecksumType.COMPOSITE) + it.multipartUpload { + it.parts({ + it.eTag(uploadPartResult.eTag()) + it.partNumber(1) + it.checksumCRC32(uploadPartResult.checksumCRC32()) + }) + } + }.also { + assertThat(it.checksumCRC32()).isEqualTo(checksum) + } + + val etag = "\"${DigestUtil.hexDigestMultipart(listOf(UPLOAD_FILE_PATH))}\"" + s3Client + .getObject { + it.bucket(initiateMultipartUploadResult.bucket()) + it.key(initiateMultipartUploadResult.key()) + it.checksumMode(ChecksumMode.ENABLED) + }.use { + assertThat(it.response().eTag()).isEqualTo(etag) + assertThat(it.response().checksumCRC32()).isEqualTo(checksum) + } + } + + @Test + @S3VerifiedSuccess(year = 2025) + fun testMultipartUpload_withChecksumType_throwsOn_DIFFERENT(testInfo: TestInfo) { + val bucketName = givenBucket(testInfo) + val initiateMultipartUploadResult = + s3Client + .createMultipartUpload { + it.bucket(bucketName) + it.key(UPLOAD_FILE_NAME) + it.checksumAlgorithm(ChecksumAlgorithm.CRC32) + it.checksumType(ChecksumType.COMPOSITE) + } + val uploadId = initiateMultipartUploadResult.uploadId() + val uploadPartResult = + s3Client.uploadPart( + { + it.bucket(initiateMultipartUploadResult.bucket()) + it.key(initiateMultipartUploadResult.key()) + it.uploadId(uploadId) + it.partNumber(1) + it.checksumAlgorithm(ChecksumAlgorithm.CRC32) + it.contentLength(UPLOAD_FILE_LENGTH) + }, + RequestBody.fromFile(UPLOAD_FILE), + ) + + assertThatThrownBy { + s3Client + .completeMultipartUpload { + it.bucket(initiateMultipartUploadResult.bucket()) + it.key(initiateMultipartUploadResult.key()) + it.uploadId(initiateMultipartUploadResult.uploadId()) + it.checksumType(ChecksumType.FULL_OBJECT) // intentionally different from creteMultipartUpload value + it.multipartUpload { + it.parts({ + it.eTag(uploadPartResult.eTag()) + it.partNumber(1) + it.checksumCRC32(uploadPartResult.checksumCRC32()) + }) + } + } + }.isInstanceOf(AwsServiceException::class.java) + .hasMessageContaining("Service: S3, Status Code: 400") + } + + @Test + @S3VerifiedSuccess(year = 2025) + fun testMultipartUpload_withChecksumType_FULL_OBJECT(testInfo: TestInfo) { + val bucketName = givenBucket(testInfo) + val initiateMultipartUploadResult = + s3Client + .createMultipartUpload { + it.bucket(bucketName) + it.key(UPLOAD_FILE_NAME) + it.checksumAlgorithm(ChecksumAlgorithm.CRC64_NVME) + it.checksumType(ChecksumType.FULL_OBJECT) + } + val uploadId = initiateMultipartUploadResult.uploadId() + val uploadPartResult = + s3Client.uploadPart( + { + it.bucket(initiateMultipartUploadResult.bucket()) + it.key(initiateMultipartUploadResult.key()) + it.uploadId(uploadId) + it.partNumber(1) + it.checksumAlgorithm(ChecksumAlgorithm.CRC64_NVME) + it.contentLength(UPLOAD_FILE_LENGTH) + }, + RequestBody.fromFile(UPLOAD_FILE), + ) + + val checksum = + DigestUtil.checksumFor( + UPLOAD_FILE_PATH, + DefaultChecksumAlgorithm.CRC64NVME, + ) + + s3Client + .completeMultipartUpload { + it.bucket(initiateMultipartUploadResult.bucket()) + it.key(initiateMultipartUploadResult.key()) + it.uploadId(initiateMultipartUploadResult.uploadId()) + it.checksumType(ChecksumType.FULL_OBJECT) + it.multipartUpload { + it.parts({ + it.eTag(uploadPartResult.eTag()) + it.partNumber(1) + it.checksumCRC64NVME(uploadPartResult.checksumCRC64NVME()) + }) + } + }.also { + assertThat(it.checksumCRC64NVME()).isEqualTo(checksum) + } + + val etag = "\"${DigestUtil.hexDigestMultipart(listOf(UPLOAD_FILE_PATH))}\"" + + s3Client + .getObject { + it.bucket(initiateMultipartUploadResult.bucket()) + it.key(initiateMultipartUploadResult.key()) + it.checksumMode(ChecksumMode.ENABLED) + }.use { + assertThat(it.response().eTag()).isEqualTo(etag) + assertThat(it.response().checksumCRC64NVME()).isEqualTo(checksum) + } + } + /** * Tests if a multipart upload with the last part being smaller than 5MB works. */ @@ -329,6 +497,7 @@ internal class MultipartIT : S3TestBase() { it.bucket(initiateMultipartUploadResult.bucket()) it.key(initiateMultipartUploadResult.key()) it.uploadId(initiateMultipartUploadResult.uploadId()) + it.checksumType(ChecksumType.COMPOSITE) it.multipartUpload { it.parts( { @@ -373,6 +542,7 @@ internal class MultipartIT : S3TestBase() { it.bucket(initiateMultipartUploadResult.bucket()) it.key(initiateMultipartUploadResult.key()) it.uploadId(initiateMultipartUploadResult.uploadId()) + it.checksumType(ChecksumType.COMPOSITE) it.multipartUpload { it.parts( { @@ -395,7 +565,6 @@ internal class MultipartIT : S3TestBase() { assertThat(completeMultipartUpload.bucket()).isEqualTo(completeMultipartUpload1.bucket()) assertThat(completeMultipartUpload.key()).isEqualTo(completeMultipartUpload1.key()) assertThat(completeMultipartUpload.eTag()).isEqualTo(completeMultipartUpload1.eTag()) - assertThat(completeMultipartUpload.checksumCRC32()).isEqualTo(completeMultipartUpload1.checksumCRC32()) assertThat(completeMultipartUpload.checksumType()).isEqualTo(completeMultipartUpload1.checksumType()) } @@ -1644,7 +1813,7 @@ internal class MultipartIT : S3TestBase() { it.sourceKey(sourceKey) it.sourceBucket(bucketName) it.partNumber(1) - it.copySourceRange("bytes=0-$UPLOAD_FILE_LENGTH") + it.copySourceRange("bytes=0-${UPLOAD_FILE_LENGTH - 1}") it.copySourceIfModifiedSince(now) } }.isInstanceOf(S3Exception::class.java) @@ -1913,6 +2082,54 @@ internal class MultipartIT : S3TestBase() { .hasMessageContaining(INVALID_PART) } + @Test + @S3VerifiedSuccess(year = 2025) + fun `CompleteMultipart fails with if-none-match=true`(testInfo: TestInfo) { + val (bucketName, response) = givenBucketAndObject(testInfo, UPLOAD_FILE_NAME) + val initiateMultipartUploadResult = + s3Client + .createMultipartUpload { + it.bucket(bucketName) + it.key(UPLOAD_FILE_NAME) + } + val uploadId = initiateMultipartUploadResult.uploadId() + + assertThat( + s3Client + .listMultipartUploads { + it.bucket(bucketName) + }.uploads(), + ).isNotEmpty + + val eTag = + s3Client + .uploadPart( + { + it.bucket(initiateMultipartUploadResult.bucket()) + it.key(initiateMultipartUploadResult.key()) + it.uploadId(uploadId) + it.partNumber(1) + }, + RequestBody.fromFile(UPLOAD_FILE), + ).eTag() + + assertThatThrownBy { + s3Client.completeMultipartUpload { + it.bucket(initiateMultipartUploadResult.bucket()) + it.key(initiateMultipartUploadResult.key()) + it.uploadId(uploadId) + it.ifNoneMatch("*") + it.multipartUpload { + it.parts({ + it.eTag(eTag) + it.partNumber(1) + }) + } + } + }.isInstanceOf(S3Exception::class.java) + .hasMessageContaining(PRECONDITION_FAILED.message) + } + private fun uploadPart( bucketName: String, key: String, @@ -1939,7 +2156,7 @@ internal class MultipartIT : S3TestBase() { private const val NO_SUCH_BUCKET = "The specified bucket does not exist" private const val INVALID_PART_NUMBER = "Part number must be an integer between 1 and 10000, inclusive" private const val INVALID_PART = - "One or more of the specified parts could not be found. " + - "The part might not have been uploaded, or the specified entity tag may not match the part's entity tag." + "One or more of the specified parts could not be found. " + + "The part may not have been uploaded, or the specified entity tag may not match the part's entity tag." } } diff --git a/integration-tests/src/test/kotlin/com/adobe/testing/s3mock/its/VersionsIT.kt b/integration-tests/src/test/kotlin/com/adobe/testing/s3mock/its/VersionsIT.kt index 3527d8111..bdfc25e0a 100644 --- a/integration-tests/src/test/kotlin/com/adobe/testing/s3mock/its/VersionsIT.kt +++ b/integration-tests/src/test/kotlin/com/adobe/testing/s3mock/its/VersionsIT.kt @@ -27,6 +27,7 @@ import software.amazon.awssdk.services.s3.S3Client import software.amazon.awssdk.services.s3.model.BucketVersioningStatus import software.amazon.awssdk.services.s3.model.ChecksumAlgorithm import software.amazon.awssdk.services.s3.model.ObjectAttributes +import software.amazon.awssdk.services.s3.model.ObjectVersionStorageClass import software.amazon.awssdk.services.s3.model.S3Exception import software.amazon.awssdk.services.s3.model.StorageClass @@ -100,7 +101,7 @@ internal class VersionsIT : S3TestBase() { ) }.also { assertThat(it.versionId()).isEqualTo(versionId) - // default storageClass is STANDARD, which is never returned from APIs + // GetObjectAttributes returns the default storageClass "STANDARD", even though other APIs may not. assertThat(it.storageClass()).isEqualTo(StorageClass.STANDARD) assertThat(it.objectSize()).isEqualTo(UPLOAD_FILE_LENGTH) assertThat(it.checksum().checksumSHA1()).isEqualTo(expectedChecksum) @@ -260,6 +261,8 @@ internal class VersionsIT : S3TestBase() { assertThat(listObjectVersions.hasVersions()).isTrue assertThat(listObjectVersions.versions()[0].key()).isEqualTo(UPLOAD_FILE_NAME) assertThat(listObjectVersions.versions()[0].versionId()).isEqualTo(versionId) + // ListObjectVersions returns the default storageClass "STANDARD", even though other APIs may not. + assertThat(listObjectVersions.versions()[0].storageClass()).isEqualTo(ObjectVersionStorageClass.STANDARD) assertThatThrownBy { s3Client.getObject { diff --git a/pom.xml b/pom.xml index 80b6bb541..450e994f7 100644 --- a/pom.xml +++ b/pom.xml @@ -95,7 +95,6 @@ s3mock-buildx 0.48.0 adobe/s3mock - 1.0.0 5.7.2 4.13.2 @@ -131,6 +130,7 @@ 0.0.14 3.5.0 26.0.2-1 + 2.1.0 @@ -158,31 +158,6 @@ s3mock-testsupport-common ${project.version} - - junit - junit - ${junit.version} - - - org.jetbrains.kotlin - kotlin-stdlib - ${kotlin.version} - - - org.jetbrains.kotlin - kotlin-reflect - ${kotlin.version} - - - org.jetbrains.kotlin - kotlin-test - ${kotlin.version} - - - org.jetbrains.kotlin - kotlin-test-junit - ${kotlin.version} - org.mockito.kotlin mockito-kotlin @@ -226,53 +201,10 @@ software.amazon.awssdk - aws-query-protocol - ${aws-v2.version} - - - software.amazon.awssdk - aws-xml-protocol - ${aws-v2.version} - - - software.amazon.awssdk - s3 - ${aws-v2.version} - - - software.amazon.awssdk - url-connection-client - ${aws-v2.version} - - - software.amazon.awssdk - aws-crt-client - ${aws-v2.version} - - - software.amazon.awssdk - regions - ${aws-v2.version} - - - software.amazon.awssdk - utils - ${aws-v2.version} - - - software.amazon.awssdk - auth - ${aws-v2.version} - - - software.amazon.awssdk - checksums - ${aws-v2.version} - - - software.amazon.awssdk - s3-transfer-manager + bom ${aws-v2.version} + pom + import aws.sdk.kotlin @@ -294,11 +226,6 @@ httpmime ${httpmime.version} - - org.jspecify - jspecify - ${jspecify.version} - org.jetbrains @@ -556,6 +483,25 @@ maven-jar-plugin ${maven-jar-plugin.version} + + org.jetbrains.dokka + dokka-maven-plugin + ${dokka-maven-plugin.version} + + + prepare-package + + javadocJar + + + + + + ${project.basedir}/src/main/kotlin + ${project.basedir}/src/main/java + + + maven-javadoc-plugin ${maven-javadoc-plugin.version} @@ -713,7 +659,8 @@ - maven-javadoc-plugin + org.jetbrains.dokka + dokka-maven-plugin maven-source-plugin diff --git a/server/pom.xml b/server/pom.xml index 92a0f2cab..f77b4351f 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -78,10 +78,6 @@ software.amazon.awssdk aws-crt-client - - org.jspecify - jspecify - org.jetbrains.kotlin kotlin-stdlib diff --git a/server/src/main/kotlin/com/adobe/testing/s3mock/S3Exception.kt b/server/src/main/kotlin/com/adobe/testing/s3mock/S3Exception.kt index 8ce571fc4..4a6c051c5 100644 --- a/server/src/main/kotlin/com/adobe/testing/s3mock/S3Exception.kt +++ b/server/src/main/kotlin/com/adobe/testing/s3mock/S3Exception.kt @@ -20,12 +20,12 @@ import org.springframework.http.HttpStatus /** * [RuntimeException] to communicate general S3 errors. * These are handled by ControllerConfiguration.S3MockExceptionHandler, - * mapped to [ErrorResponse] and serialized. + * mapped to [com.adobe.testing.s3mock.dto.ErrorResponse] and serialized. * [API Reference](https://docs.aws.amazon.com/AmazonS3/latest/API/ErrorResponses.html) */ class S3Exception /** - * Creates a new S3Exception to be mapped as an [ErrorResponse]. + * Creates a new S3Exception to be mapped as an [com.adobe.testing.s3mock.dto.ErrorResponse]. * * @param status The Error Status. * @param code The Error Code. @@ -43,7 +43,7 @@ class S3Exception ) val INVALID_PART: S3Exception = S3Exception( HttpStatus.BAD_REQUEST.value(), "InvalidPart", - "One or more of the specified parts could not be found. The part might not have been " + "One or more of the specified parts could not be found. The part may not have been " + "uploaded, or the specified entity tag may not match the part's entity tag." ) val INVALID_PART_ORDER: S3Exception = S3Exception( @@ -67,6 +67,14 @@ class S3Exception ) } + fun completeRequestWrongChecksumMode(checksumMode: String): S3Exception { + return S3Exception( + HttpStatus.BAD_REQUEST.value(), BAD_REQUEST_CODE, + ("The upload was created using the $checksumMode checksum mode. " + + "The complete request must use the same checksum mode.") + ) + } + val NO_SUCH_UPLOAD_MULTIPART: S3Exception = S3Exception( HttpStatus.NOT_FOUND.value(), "NoSuchUpload", "The specified multipart upload does not exist. The upload ID might be invalid, or the " diff --git a/server/src/main/kotlin/com/adobe/testing/s3mock/controller/ChecksumModeHeaderConverter.kt b/server/src/main/kotlin/com/adobe/testing/s3mock/controller/ChecksumModeHeaderConverter.kt index 35c8a3d32..81101e8f2 100644 --- a/server/src/main/kotlin/com/adobe/testing/s3mock/controller/ChecksumModeHeaderConverter.kt +++ b/server/src/main/kotlin/com/adobe/testing/s3mock/controller/ChecksumModeHeaderConverter.kt @@ -19,7 +19,7 @@ import com.adobe.testing.s3mock.dto.ChecksumMode import org.springframework.core.convert.converter.Converter /** - * Converts values of the [AwsHttpHeaders.X_AMZ_CHECKSUM_MODE] which is sent by the Amazon + * Converts values of the [com.adobe.testing.s3mock.util.AwsHttpHeaders.X_AMZ_CHECKSUM_MODE] which is sent by the Amazon * client. * Example: x-amz-checksum-mode: ENABLED * [API Reference](https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetObject.html) diff --git a/server/src/main/kotlin/com/adobe/testing/s3mock/controller/MultipartController.kt b/server/src/main/kotlin/com/adobe/testing/s3mock/controller/MultipartController.kt index 64ca0e973..f6e60e2ce 100644 --- a/server/src/main/kotlin/com/adobe/testing/s3mock/controller/MultipartController.kt +++ b/server/src/main/kotlin/com/adobe/testing/s3mock/controller/MultipartController.kt @@ -23,6 +23,7 @@ import com.adobe.testing.s3mock.dto.CompleteMultipartUploadResult import com.adobe.testing.s3mock.dto.CopyPartResult import com.adobe.testing.s3mock.dto.CopySource import com.adobe.testing.s3mock.dto.InitiateMultipartUploadResult +import com.adobe.testing.s3mock.dto.Initiator import com.adobe.testing.s3mock.dto.ListMultipartUploadsResult import com.adobe.testing.s3mock.dto.ListPartsResult import com.adobe.testing.s3mock.dto.ObjectKey @@ -60,6 +61,7 @@ import com.adobe.testing.s3mock.util.HeaderUtil.checksumAlgorithmFromHeader import com.adobe.testing.s3mock.util.HeaderUtil.checksumAlgorithmFromSdk import com.adobe.testing.s3mock.util.HeaderUtil.checksumFrom import com.adobe.testing.s3mock.util.HeaderUtil.checksumHeaderFrom +import com.adobe.testing.s3mock.util.HeaderUtil.checksumTypeFrom import com.adobe.testing.s3mock.util.HeaderUtil.encryptionHeadersFrom import com.adobe.testing.s3mock.util.HeaderUtil.storeHeadersFrom import com.adobe.testing.s3mock.util.HeaderUtil.userMetadataFrom @@ -381,7 +383,7 @@ class MultipartController( contentType, storeHeadersFrom(httpHeaders), Owner.DEFAULT_OWNER, - Owner.DEFAULT_OWNER, + Initiator.DEFAULT_INITIATOR, userMetadataFrom(httpHeaders), encryptionHeaders, tags, @@ -433,7 +435,7 @@ class MultipartController( multipartService.verifyMultipartParts(bucketName, objectName, uploadId, upload.parts) } val s3ObjectMetadata = objectService.getObject(bucketName, key.key, null) - objectService.verifyObjectMatching(match, noneMatch, null, null, s3ObjectMetadata) + objectService.verifyObjectMatching(bucketName, key.key, match, noneMatch) val locationWithEncodedKey = request .requestURL .toString() @@ -449,6 +451,7 @@ class MultipartController( encryptionHeadersFrom(httpHeaders), locationWithEncodedKey, checksumFrom(httpHeaders), + checksumTypeFrom(httpHeaders), checksumAlgorithmFromHeader(httpHeaders) )!! } else { diff --git a/server/src/main/kotlin/com/adobe/testing/s3mock/dto/Bucket.kt b/server/src/main/kotlin/com/adobe/testing/s3mock/dto/Bucket.kt index b4a9c1fc6..c9eb80a4d 100644 --- a/server/src/main/kotlin/com/adobe/testing/s3mock/dto/Bucket.kt +++ b/server/src/main/kotlin/com/adobe/testing/s3mock/dto/Bucket.kt @@ -31,7 +31,7 @@ data class Bucket( @param:JsonProperty("CreationDate", namespace = "http://s3.amazonaws.com/doc/2006-03-01/") val creationDate: String?, @param:JsonProperty("Name", namespace = "http://s3.amazonaws.com/doc/2006-03-01/") - val name: String, + val name: String?, @JsonIgnore val path: Path? ) { diff --git a/server/src/main/kotlin/com/adobe/testing/s3mock/dto/ChecksumType.kt b/server/src/main/kotlin/com/adobe/testing/s3mock/dto/ChecksumType.kt index 5cd6c1136..900c7a100 100644 --- a/server/src/main/kotlin/com/adobe/testing/s3mock/dto/ChecksumType.kt +++ b/server/src/main/kotlin/com/adobe/testing/s3mock/dto/ChecksumType.kt @@ -33,10 +33,12 @@ enum class ChecksumType @JsonCreator constructor(private val value: String) { } companion object { - fun fromString(value: String): ChecksumType? { + fun fromString(value: String?): ChecksumType? { return when (value) { "composite" -> COMPOSITE + "COMPOSITE" -> COMPOSITE "full_object" -> FULL_OBJECT + "FULL_OBJECT" -> FULL_OBJECT else -> null } } diff --git a/server/src/main/kotlin/com/adobe/testing/s3mock/dto/Initiator.kt b/server/src/main/kotlin/com/adobe/testing/s3mock/dto/Initiator.kt new file mode 100644 index 000000000..15680dfad --- /dev/null +++ b/server/src/main/kotlin/com/adobe/testing/s3mock/dto/Initiator.kt @@ -0,0 +1,35 @@ +/* + * Copyright 2017-2025 Adobe. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.adobe.testing.s3mock.dto + +import com.adobe.testing.S3Verified +import com.fasterxml.jackson.annotation.JsonProperty + +/** + * [API Reference](https://docs.aws.amazon.com/AmazonS3/latest/API/API_Initiator.html). + */ +@S3Verified(year = 2025) +data class Initiator( + @param:JsonProperty("DisplayName", namespace = "http://s3.amazonaws.com/doc/2006-03-01/") + val displayName: String?, + @param:JsonProperty("ID", namespace = "http://s3.amazonaws.com/doc/2006-03-01/") + val id: String? +) { + companion object { + val DEFAULT_INITIATOR: Initiator = + Initiator("s3-mock-file-store", "79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2be") + } +} diff --git a/server/src/main/kotlin/com/adobe/testing/s3mock/dto/ListPartsResult.kt b/server/src/main/kotlin/com/adobe/testing/s3mock/dto/ListPartsResult.kt index bf83b5b20..73a45fd42 100644 --- a/server/src/main/kotlin/com/adobe/testing/s3mock/dto/ListPartsResult.kt +++ b/server/src/main/kotlin/com/adobe/testing/s3mock/dto/ListPartsResult.kt @@ -34,7 +34,7 @@ data class ListPartsResult( @param:JsonProperty("ChecksumType", namespace = "http://s3.amazonaws.com/doc/2006-03-01/") val checksumType: ChecksumType?, @param:JsonProperty("Initiator", namespace = "http://s3.amazonaws.com/doc/2006-03-01/") - val initiator: Owner?, + val initiator: Initiator?, @param:JsonProperty("IsTruncated", namespace = "http://s3.amazonaws.com/doc/2006-03-01/") val isTruncated: Boolean, @param:JsonProperty("Key", namespace = "http://s3.amazonaws.com/doc/2006-03-01/") diff --git a/server/src/main/kotlin/com/adobe/testing/s3mock/dto/MultipartUpload.kt b/server/src/main/kotlin/com/adobe/testing/s3mock/dto/MultipartUpload.kt index 0539ce6e8..657bac141 100644 --- a/server/src/main/kotlin/com/adobe/testing/s3mock/dto/MultipartUpload.kt +++ b/server/src/main/kotlin/com/adobe/testing/s3mock/dto/MultipartUpload.kt @@ -34,7 +34,7 @@ data class MultipartUpload( @param:JsonProperty("Initiated", namespace = "http://s3.amazonaws.com/doc/2006-03-01/") val initiated: Date?, @param:JsonProperty("Initiator", namespace = "http://s3.amazonaws.com/doc/2006-03-01/") - val initiator: Owner?, + val initiator: Initiator?, @param:JsonProperty("Key", namespace = "http://s3.amazonaws.com/doc/2006-03-01/") val key: String, @param:JsonProperty("Owner", namespace = "http://s3.amazonaws.com/doc/2006-03-01/") diff --git a/server/src/main/kotlin/com/adobe/testing/s3mock/dto/Owner.kt b/server/src/main/kotlin/com/adobe/testing/s3mock/dto/Owner.kt index f561d4479..536fb7356 100644 --- a/server/src/main/kotlin/com/adobe/testing/s3mock/dto/Owner.kt +++ b/server/src/main/kotlin/com/adobe/testing/s3mock/dto/Owner.kt @@ -24,9 +24,6 @@ import com.fasterxml.jackson.annotation.JsonProperty */ @S3Verified(year = 2025) data class Owner( - @Deprecated("AWS deprecated this field in 2025-05") - @param:JsonProperty("DisplayName", namespace = "http://s3.amazonaws.com/doc/2006-03-01/") - val displayName: String? = null, @param:JsonProperty("ID", namespace = "http://s3.amazonaws.com/doc/2006-03-01/") val id: String? ) { @@ -34,9 +31,7 @@ data class Owner( /** * Default owner in S3Mock until support for ownership is implemented. */ - val DEFAULT_OWNER: Owner = - Owner("s3-mock-file-store", "79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2be") - val DEFAULT_OWNER_BUCKET: Owner = - Owner("s3-mock-file-store-bucket", "79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2df") + val DEFAULT_OWNER: Owner = Owner("79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2be") + val DEFAULT_OWNER_BUCKET: Owner = Owner("79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2df") } } diff --git a/server/src/main/kotlin/com/adobe/testing/s3mock/service/BucketService.kt b/server/src/main/kotlin/com/adobe/testing/s3mock/service/BucketService.kt index 64557d176..25b09aa7c 100644 --- a/server/src/main/kotlin/com/adobe/testing/s3mock/service/BucketService.kt +++ b/server/src/main/kotlin/com/adobe/testing/s3mock/service/BucketService.kt @@ -43,6 +43,7 @@ import com.adobe.testing.s3mock.util.AwsHttpHeaders.X_AMZ_BUCKET_LOCATION_TYPE import software.amazon.awssdk.utils.http.SdkHttpUtils.urlEncodeIgnoreSlashes import java.util.UUID import java.util.concurrent.ConcurrentHashMap +import kotlin.let open class BucketService( private val bucketStore: BucketStore, @@ -92,7 +93,9 @@ open class BucketService( if (buckets.size > maxBuckets) { nextContinuationToken = UUID.randomUUID().toString() buckets = buckets.subList(0, maxBuckets) - listBucketsPagingStateCache[nextContinuationToken] = buckets[maxBuckets - 1].name + buckets[maxBuckets - 1].name?.let { + listBucketsPagingStateCache[nextContinuationToken] = it + } } return ListAllMyBucketsResult(Owner.DEFAULT_OWNER, Buckets(buckets), prefix, nextContinuationToken) diff --git a/server/src/main/kotlin/com/adobe/testing/s3mock/service/MultipartService.kt b/server/src/main/kotlin/com/adobe/testing/s3mock/service/MultipartService.kt index 80bb22f57..8d950d0a7 100644 --- a/server/src/main/kotlin/com/adobe/testing/s3mock/service/MultipartService.kt +++ b/server/src/main/kotlin/com/adobe/testing/s3mock/service/MultipartService.kt @@ -22,6 +22,7 @@ import com.adobe.testing.s3mock.dto.CompleteMultipartUploadResult import com.adobe.testing.s3mock.dto.CompletedPart import com.adobe.testing.s3mock.dto.CopyPartResult import com.adobe.testing.s3mock.dto.InitiateMultipartUploadResult +import com.adobe.testing.s3mock.dto.Initiator import com.adobe.testing.s3mock.dto.ListMultipartUploadsResult import com.adobe.testing.s3mock.dto.ListPartsResult import com.adobe.testing.s3mock.dto.MultipartUpload @@ -161,6 +162,7 @@ open class MultipartService(private val bucketStore: BucketStore, private val mu encryptionHeaders: Map, location: String, checksum: String?, + checksumType: ChecksumType?, checksumAlgorithm: ChecksumAlgorithm? ): CompleteMultipartUploadResult? { val bucketMetadata = bucketStore.getBucketMetadata(bucketName) @@ -177,6 +179,7 @@ open class MultipartService(private val bucketStore: BucketStore, private val mu multipartUploadInfo, location, checksum, + checksumType, checksumAlgorithm ) } @@ -187,7 +190,7 @@ open class MultipartService(private val bucketStore: BucketStore, private val mu contentType: String?, storeHeaders: Map, owner: Owner, - initiator: Owner, + initiator: Initiator, userMetadata: Map, encryptionHeaders: Map, tags: List?, diff --git a/server/src/main/kotlin/com/adobe/testing/s3mock/service/ServiceBase.kt b/server/src/main/kotlin/com/adobe/testing/s3mock/service/ServiceBase.kt index d2bfd4842..2517b404d 100644 --- a/server/src/main/kotlin/com/adobe/testing/s3mock/service/ServiceBase.kt +++ b/server/src/main/kotlin/com/adobe/testing/s3mock/service/ServiceBase.kt @@ -93,15 +93,12 @@ abstract class ServiceBase { fun filterBy( contents: List, - selector: (T) -> String, + selector: (T) -> String?, compareTo: String? - ): List { - return if (!compareTo.isNullOrEmpty()) { - contents.filter { selector(it) > compareTo } - } else { - contents - } - } + ): List = + compareTo?.let { threshold -> + contents.filter { selector(it)?.let { candidate -> candidate > threshold } == true } + } ?: contents fun filterBy( contents: List, @@ -117,11 +114,11 @@ abstract class ServiceBase { fun filterBy( contents: List, - selector: (T) -> String, + selector: (T) -> String?, prefixes: List? ): List { return if (!prefixes.isNullOrEmpty()) { - contents.filter { content -> prefixes.none { prefix -> selector(content).startsWith(prefix) } } + contents.filter { content -> prefixes.none { prefix -> selector(content)?.startsWith(prefix) ?: false } } } else { contents } diff --git a/server/src/main/kotlin/com/adobe/testing/s3mock/store/MultipartStore.kt b/server/src/main/kotlin/com/adobe/testing/s3mock/store/MultipartStore.kt index a359a9599..fba238fdd 100644 --- a/server/src/main/kotlin/com/adobe/testing/s3mock/store/MultipartStore.kt +++ b/server/src/main/kotlin/com/adobe/testing/s3mock/store/MultipartStore.kt @@ -21,6 +21,7 @@ import com.adobe.testing.s3mock.dto.ChecksumAlgorithm import com.adobe.testing.s3mock.dto.ChecksumType import com.adobe.testing.s3mock.dto.CompleteMultipartUploadResult import com.adobe.testing.s3mock.dto.CompletedPart +import com.adobe.testing.s3mock.dto.Initiator import com.adobe.testing.s3mock.dto.MultipartUpload import com.adobe.testing.s3mock.dto.Owner import com.adobe.testing.s3mock.dto.Part @@ -64,7 +65,7 @@ open class MultipartStore(private val objectStore: ObjectStore, private val obje contentType: String?, storeHeaders: Map, owner: Owner, - initiator: Owner, + initiator: Initiator, userMetadata: Map, encryptionHeaders: Map, tags: List?, @@ -189,6 +190,7 @@ open class MultipartStore(private val objectStore: ObjectStore, private val obje uploadInfo: MultipartUploadInfo?, location: String, checksum: String?, + checksumType: ChecksumType?, checksumAlgorithm: ChecksumAlgorithm? ): CompleteMultipartUploadResult { requireNotNull(uploadInfo) { "Unknown upload $uploadId" } @@ -201,42 +203,42 @@ open class MultipartStore(private val objectStore: ObjectStore, private val obje toInputStream(partsPaths).use { input -> tempFile.outputStream().use { os -> input.transferTo(os) - val checksumFor = validateChecksums(uploadInfo, parts, partsPaths, checksum, checksumAlgorithm) - val etag = DigestUtil.hexDigestMultipart(partsPaths) - val s3ObjectMetadata = objectStore.storeS3ObjectMetadata( - bucket, - id, - key, - uploadInfo.contentType, - uploadInfo.storeHeaders, - tempFile, - uploadInfo.userMetadata, - encryptionHeaders, - etag, - uploadInfo.tags, - uploadInfo.checksumAlgorithm, - checksumFor, - uploadInfo.upload.owner, - uploadInfo.storageClass, - ChecksumType.COMPOSITE - ) - // delete parts and update MultipartInfo - partsPaths.forEach { runCatching { it.toFile().deleteRecursively() } } - val completedUploadInfo = uploadInfo.complete() - writeMetafile(bucket, completedUploadInfo) - return CompleteMultipartUploadResult.from( - location, - completedUploadInfo.bucket, - key, - etag, - completedUploadInfo, - checksumFor, - s3ObjectMetadata.checksumType, - checksumAlgorithm, - s3ObjectMetadata.versionId - ) } } + val checksumFor = validateChecksums(uploadInfo, tempFile, parts, partsPaths, checksum, checksumType, checksumAlgorithm) + val etag = DigestUtil.hexDigestMultipart(partsPaths) + val s3ObjectMetadata = objectStore.storeS3ObjectMetadata( + bucket, + id, + key, + uploadInfo.contentType, + uploadInfo.storeHeaders, + tempFile, + uploadInfo.userMetadata, + encryptionHeaders, + etag, + uploadInfo.tags, + uploadInfo.checksumAlgorithm, + checksumFor, + uploadInfo.upload.owner, + uploadInfo.storageClass, + checksumType + ) + // delete parts and update MultipartInfo + partsPaths.forEach { runCatching { it.toFile().deleteRecursively() } } + val completedUploadInfo = uploadInfo.complete() + writeMetafile(bucket, completedUploadInfo) + return CompleteMultipartUploadResult.from( + location, + completedUploadInfo.bucket, + key, + etag, + completedUploadInfo, + checksumFor, + s3ObjectMetadata.checksumType, + checksumAlgorithm, + s3ObjectMetadata.versionId + ) } catch (e: IOException) { throw IllegalStateException("Error finishing multipart upload bucket=$bucket, key=$key, id=$id, uploadId=$uploadId", e) } finally { @@ -421,14 +423,24 @@ open class MultipartStore(private val objectStore: ObjectStore, private val obje private fun validateChecksums( uploadInfo: MultipartUploadInfo, + tempFile: Path, completedParts: List, partsPaths: List, checksum: String?, + checksumType: ChecksumType?, checksumAlgorithm: ChecksumAlgorithm? ): String? { val checksumToValidate = checksum ?: uploadInfo.checksum val checksumAlgorithmToValidate = checksumAlgorithm ?: uploadInfo.checksumAlgorithm - val checksumFor = checksumFor(partsPaths, uploadInfo) + if(checksumType != null && uploadInfo.checksumType != null && checksumType != uploadInfo.checksumType) { + throw S3Exception.completeRequestWrongChecksumMode(uploadInfo.checksumType.name) + } + val checksumFor = if (uploadInfo.checksumType == ChecksumType.COMPOSITE) { + checksumFor(partsPaths, uploadInfo) + } else { + checksumFor(tempFile, uploadInfo) + } + if (checksumAlgorithmToValidate != null) { completedParts.forEach { part -> if (part.checksum(checksumAlgorithmToValidate) == null) { @@ -451,6 +463,11 @@ open class MultipartStore(private val objectStore: ObjectStore, private val obje DigestUtil.checksumMultipart(paths, algo.toChecksumAlgorithm()) } + private fun checksumFor(path: Path, uploadInfo: MultipartUploadInfo): String? = + uploadInfo.checksumAlgorithm?.let { algo -> + DigestUtil.checksumFor(path, algo.toChecksumAlgorithm()) + } + companion object { private val LOG: Logger = LoggerFactory.getLogger(MultipartStore::class.java) private const val PART_SUFFIX = ".part" diff --git a/server/src/main/kotlin/com/adobe/testing/s3mock/store/ObjectStore.kt b/server/src/main/kotlin/com/adobe/testing/s3mock/store/ObjectStore.kt index e9670a57e..f465313a1 100644 --- a/server/src/main/kotlin/com/adobe/testing/s3mock/store/ObjectStore.kt +++ b/server/src/main/kotlin/com/adobe/testing/s3mock/store/ObjectStore.kt @@ -121,7 +121,7 @@ open class ObjectStore( private fun privateCannedAcl(owner: Owner): AccessControlPolicy { val grant = Grant( - CanonicalUser(owner.displayName, owner.id), + CanonicalUser(null, owner.id), Grant.Permission.FULL_CONTROL ) return AccessControlPolicy(listOf(grant), owner) diff --git a/server/src/main/kotlin/com/adobe/testing/s3mock/store/S3ObjectMetadata.kt b/server/src/main/kotlin/com/adobe/testing/s3mock/store/S3ObjectMetadata.kt index 7b0c69121..7ee39f74f 100644 --- a/server/src/main/kotlin/com/adobe/testing/s3mock/store/S3ObjectMetadata.kt +++ b/server/src/main/kotlin/com/adobe/testing/s3mock/store/S3ObjectMetadata.kt @@ -53,7 +53,7 @@ data class S3ObjectMetadata( val policy: AccessControlPolicy?, val versionId: String?, val deleteMarker: Boolean = false, - val checksumType: ChecksumType? = ChecksumType.FULL_OBJECT + val checksumType: ChecksumType? ) { companion object { fun deleteMarker(metadata: S3ObjectMetadata, versionId: String?): S3ObjectMetadata = diff --git a/server/src/main/kotlin/com/adobe/testing/s3mock/util/CannedAclUtil.kt b/server/src/main/kotlin/com/adobe/testing/s3mock/util/CannedAclUtil.kt index b1acd7366..49d99d2bc 100644 --- a/server/src/main/kotlin/com/adobe/testing/s3mock/util/CannedAclUtil.kt +++ b/server/src/main/kotlin/com/adobe/testing/s3mock/util/CannedAclUtil.kt @@ -42,7 +42,7 @@ object CannedAclUtil { } private val defaultOwner = Owner.DEFAULT_OWNER - private val defaultOwnerUser = CanonicalUser(defaultOwner.displayName, defaultOwner.id) + private val defaultOwnerUser = CanonicalUser("s3-mock-file-store", defaultOwner.id) private fun policyWithOwner(vararg additionalGrants: Grant): AccessControlPolicy = AccessControlPolicy( @@ -54,7 +54,7 @@ object CannedAclUtil { policyWithOwner( Grant( CanonicalUser( - Owner.DEFAULT_OWNER_BUCKET.displayName, + "s3-mock-file-store", Owner.DEFAULT_OWNER_BUCKET.id ), Grant.Permission.READ @@ -65,7 +65,7 @@ object CannedAclUtil { policyWithOwner( Grant( CanonicalUser( - Owner.DEFAULT_OWNER_BUCKET.displayName, + "s3-mock-file-store", Owner.DEFAULT_OWNER_BUCKET.id ), Grant.Permission.READ diff --git a/server/src/main/kotlin/com/adobe/testing/s3mock/util/HeaderUtil.kt b/server/src/main/kotlin/com/adobe/testing/s3mock/util/HeaderUtil.kt index 2f206e2d8..c7e0ef72c 100644 --- a/server/src/main/kotlin/com/adobe/testing/s3mock/util/HeaderUtil.kt +++ b/server/src/main/kotlin/com/adobe/testing/s3mock/util/HeaderUtil.kt @@ -17,6 +17,7 @@ package com.adobe.testing.s3mock.util import com.adobe.testing.s3mock.dto.ChecksumAlgorithm +import com.adobe.testing.s3mock.dto.ChecksumType import com.adobe.testing.s3mock.dto.CopySource import com.adobe.testing.s3mock.dto.StorageClass import com.adobe.testing.s3mock.store.S3ObjectMetadata @@ -27,6 +28,7 @@ import com.adobe.testing.s3mock.util.AwsHttpHeaders.X_AMZ_CHECKSUM_CRC32C import com.adobe.testing.s3mock.util.AwsHttpHeaders.X_AMZ_CHECKSUM_CRC64NVME import com.adobe.testing.s3mock.util.AwsHttpHeaders.X_AMZ_CHECKSUM_SHA1 import com.adobe.testing.s3mock.util.AwsHttpHeaders.X_AMZ_CHECKSUM_SHA256 +import com.adobe.testing.s3mock.util.AwsHttpHeaders.X_AMZ_CHECKSUM_TYPE import com.adobe.testing.s3mock.util.AwsHttpHeaders.X_AMZ_CONTENT_SHA256 import com.adobe.testing.s3mock.util.AwsHttpHeaders.X_AMZ_COPY_SOURCE_VERSION_ID import com.adobe.testing.s3mock.util.AwsHttpHeaders.X_AMZ_SDK_CHECKSUM_ALGORITHM @@ -231,6 +233,13 @@ object HeaderUtil { else null } + @JvmStatic + fun checksumTypeFrom(headers: HttpHeaders): ChecksumType? { + return if (headers.containsHeader(X_AMZ_CHECKSUM_TYPE)) + ChecksumType.fromString(headers.getFirst(X_AMZ_CHECKSUM_TYPE)) + else null + } + @JvmStatic fun checksumFrom(headers: HttpHeaders): String? { return when { diff --git a/server/src/main/resources/application.properties b/server/src/main/resources/application.properties index 9a429e10e..3d88ecb37 100644 --- a/server/src/main/resources/application.properties +++ b/server/src/main/resources/application.properties @@ -25,7 +25,12 @@ spring.jmx.enabled=false com.adobe.testing.s3mock.store.region=us-east-1 # allow S3Mock to consume larger payloads -server.tomcat.max-swallow-size=10MB +com.adobe.testing.s3mock.max-payload-size=10MB +server.tomcat.max-part-count=-1 +server.tomcat.max-swallow-size=${com.adobe.testing.s3mock.max-payload-size} +server.tomcat.max-http-form-post-size=${com.adobe.testing.s3mock.max-payload-size} +spring.servlet.multipart.max-file-size=${com.adobe.testing.s3mock.max-payload-size} +spring.servlet.multipart.max-request-size=${com.adobe.testing.s3mock.max-payload-size} management.endpoints.web.discovery.enabled=false diff --git a/server/src/test/kotlin/com/adobe/testing/s3mock/controller/BaseControllerTest.kt b/server/src/test/kotlin/com/adobe/testing/s3mock/controller/BaseControllerTest.kt index d15308df8..65149b15f 100644 --- a/server/src/test/kotlin/com/adobe/testing/s3mock/controller/BaseControllerTest.kt +++ b/server/src/test/kotlin/com/adobe/testing/s3mock/controller/BaseControllerTest.kt @@ -140,7 +140,7 @@ internal abstract class BaseControllerTest { put(AwsHttpHeaders.X_AMZ_SERVER_SIDE_ENCRYPTION_AWS_KMS_KEY_ID, encryptionKey) } } - val TEST_OWNER = Owner("s3-mock-file-store", "123") + val TEST_OWNER = Owner("123") val TEST_BUCKETMETADATA = bucketMetadata() const val UPLOAD_FILE_NAME = "src/test/resources/sampleFile.txt" diff --git a/server/src/test/kotlin/com/adobe/testing/s3mock/controller/BucketControllerTest.kt b/server/src/test/kotlin/com/adobe/testing/s3mock/controller/BucketControllerTest.kt index ca92c23b4..6e7e23da4 100644 --- a/server/src/test/kotlin/com/adobe/testing/s3mock/controller/BucketControllerTest.kt +++ b/server/src/test/kotlin/com/adobe/testing/s3mock/controller/BucketControllerTest.kt @@ -80,7 +80,10 @@ import java.nio.file.Path import java.time.Instant @MockitoBean(types = [KmsKeyStore::class, ObjectService::class, MultipartService::class, ObjectController::class, MultipartController::class]) -@WebMvcTest(properties = ["com.adobe.testing.s3mock.store.region=us-east-1"]) +@WebMvcTest( + controllers = [BucketController::class], + properties = ["com.adobe.testing.s3mock.store.region=us-east-1"] +) internal class BucketControllerTest : BaseControllerTest() { @MockitoBean private lateinit var bucketService: BucketService diff --git a/server/src/test/kotlin/com/adobe/testing/s3mock/controller/ContextPathObjectStoreControllerTest.kt b/server/src/test/kotlin/com/adobe/testing/s3mock/controller/ContextPathObjectStoreControllerTest.kt index 64cb537ac..58d101886 100644 --- a/server/src/test/kotlin/com/adobe/testing/s3mock/controller/ContextPathObjectStoreControllerTest.kt +++ b/server/src/test/kotlin/com/adobe/testing/s3mock/controller/ContextPathObjectStoreControllerTest.kt @@ -37,7 +37,10 @@ import java.nio.file.Paths import java.time.Instant @MockitoBean(types = [KmsKeyStore::class, ObjectService::class, MultipartService::class, MultipartStore::class]) -@WebMvcTest(properties = ["com.adobe.testing.s3mock.controller.contextPath=s3-mock"]) +@WebMvcTest( + controllers = [ObjectController::class], + properties = ["com.adobe.testing.s3mock.controller.contextPath=s3-mock"] +) internal class ContextPathObjectStoreControllerTest : BaseControllerTest() { @MockitoBean private lateinit var bucketService: BucketService diff --git a/server/src/test/kotlin/com/adobe/testing/s3mock/controller/FaviconControllerTest.kt b/server/src/test/kotlin/com/adobe/testing/s3mock/controller/FaviconControllerTest.kt index b186b7169..5f457dfe7 100644 --- a/server/src/test/kotlin/com/adobe/testing/s3mock/controller/FaviconControllerTest.kt +++ b/server/src/test/kotlin/com/adobe/testing/s3mock/controller/FaviconControllerTest.kt @@ -26,7 +26,7 @@ import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status @MockitoBean(types = [KmsKeyStore::class, ObjectController::class, BucketController::class, MultipartController::class]) -@WebMvcTest +@WebMvcTest(controllers = [FaviconController::class]) internal class FaviconControllerTest : BaseControllerTest() { @Autowired private lateinit var mockMvc: MockMvc diff --git a/server/src/test/kotlin/com/adobe/testing/s3mock/controller/KmsValidationFilterTest.kt b/server/src/test/kotlin/com/adobe/testing/s3mock/controller/KmsValidationFilterTest.kt new file mode 100644 index 000000000..17327681d --- /dev/null +++ b/server/src/test/kotlin/com/adobe/testing/s3mock/controller/KmsValidationFilterTest.kt @@ -0,0 +1,109 @@ +/* + * Copyright 2017-2025 Adobe. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.adobe.testing.s3mock.controller + +import com.adobe.testing.s3mock.store.KmsKeyStore +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import org.mockito.kotlin.whenever +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.context.TestConfiguration +import org.springframework.boot.webmvc.test.autoconfigure.WebMvcTest +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Import +import org.springframework.http.MediaType +import org.springframework.http.ResponseEntity +import org.springframework.test.context.bean.override.mockito.MockitoBean +import org.springframework.test.web.servlet.MockMvc +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get +import org.springframework.test.web.servlet.result.MockMvcResultMatchers.content +import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RestController + +@MockitoBean(types = [KmsKeyStore::class]) +@WebMvcTest( + controllers = [KmsValidationFilterTest.DummyPassController::class], + properties = ["com.adobe.testing.s3mock.store.region=us-east-1"], + useDefaultFilters = false +) +@Import(KmsValidationFilterTest.Config::class) +internal class KmsValidationFilterTest { + + @Autowired + private lateinit var mockMvc: MockMvc + + @Autowired + private lateinit var kmsKeyStore: KmsKeyStore + + + @Test + fun `denies request with invalid kms key id`() { + val badKey = "bad-key-id" + whenever(kmsKeyStore.validateKeyId(badKey)).thenReturn(false) + + mockMvc.perform( + get("/internal-test/pass") + .param("dummy", "true") + .header("X-Dummy", "1") + .header("x-amz-server-side-encryption", "aws:kms") + .header("x-amz-server-side-encryption-aws-kms-key-id", badKey) + ) + .andExpect(status().isBadRequest) + .andExpect(content().contentType(MediaType.APPLICATION_XML)) + .andExpect(content().string(org.hamcrest.Matchers.containsString("Invalid keyId '$badKey'"))) + } + + @Test + fun `allows request with valid kms key id`() { + val goodKey = "good-key-id" + whenever(kmsKeyStore.validateKeyId(goodKey)).thenReturn(true) + + mockMvc.perform( + get("/internal-test/pass") + .param("dummy", "true") + .header("X-Dummy", "1") + .header("x-amz-server-side-encryption", "aws:kms") + .header("x-amz-server-side-encryption-aws-kms-key-id", goodKey) + ) + .andExpect { assertThat(it.response.status).isNotEqualTo(400) } + } + + @Test + fun `allows request when kms headers are missing`() { + mockMvc.perform( + get("/internal-test/pass") + .param("dummy", "true") + .header("X-Dummy", "1") + ).andExpect { assertThat(it.response.status).isNotEqualTo(400) } + } + + @TestConfiguration + internal class Config { + @Bean + fun kmsValidationFilter( + kmsKeyStore: KmsKeyStore + ) = KmsValidationFilter(kmsKeyStore, ControllerConfiguration().messageConverter()) + } + + @RestController + @RequestMapping("/internal-test", params = ["dummy=true"]) + internal class DummyPassController { + @GetMapping("/pass", headers = ["X-Dummy=1"]) + fun pass(): ResponseEntity = ResponseEntity.ok("ok") + } +} diff --git a/server/src/test/kotlin/com/adobe/testing/s3mock/controller/MultipartControllerTest.kt b/server/src/test/kotlin/com/adobe/testing/s3mock/controller/MultipartControllerTest.kt index 0b41c1395..eb64d646b 100644 --- a/server/src/test/kotlin/com/adobe/testing/s3mock/controller/MultipartControllerTest.kt +++ b/server/src/test/kotlin/com/adobe/testing/s3mock/controller/MultipartControllerTest.kt @@ -23,6 +23,7 @@ import com.adobe.testing.s3mock.dto.CompleteMultipartUploadResult import com.adobe.testing.s3mock.dto.CompletedPart import com.adobe.testing.s3mock.dto.CopyPartResult import com.adobe.testing.s3mock.dto.InitiateMultipartUploadResult +import com.adobe.testing.s3mock.dto.Initiator import com.adobe.testing.s3mock.dto.ListMultipartUploadsResult import com.adobe.testing.s3mock.dto.ListPartsResult import com.adobe.testing.s3mock.dto.MultipartUpload @@ -63,7 +64,7 @@ import java.util.Date import java.util.UUID @MockitoBean(types = [KmsKeyStore::class, ObjectController::class, BucketController::class]) -@WebMvcTest +@WebMvcTest(controllers = [MultipartController::class]) internal class MultipartControllerTest : BaseControllerTest() { @MockitoBean private lateinit var bucketService: BucketService @@ -244,7 +245,7 @@ internal class MultipartControllerTest : BaseControllerTest() { whenever(objectService.getObject(TEST_BUCKET_NAME, key, null)).thenReturn(s3meta) // create result with encryption headers to be echoed - val mpUpload = MultipartUpload(null, null, Date(), Owner.DEFAULT_OWNER, key, Owner.DEFAULT_OWNER, StorageClass.STANDARD, uploadId.toString()) + val mpUpload = MultipartUpload(null, null, Date(), Initiator.DEFAULT_INITIATOR, key, Owner.DEFAULT_OWNER, StorageClass.STANDARD, uploadId.toString()) val info = MultipartUploadInfo( mpUpload, "application/octet-stream", @@ -279,6 +280,7 @@ internal class MultipartControllerTest : BaseControllerTest() { anyOrNull(), any(), anyOrNull(), + anyOrNull(), anyOrNull() ) ).thenReturn(result) @@ -322,7 +324,7 @@ internal class MultipartControllerTest : BaseControllerTest() { val s3meta = s3ObjectMetadata(key, UUID.randomUUID().toString()) whenever(objectService.getObject(TEST_BUCKET_NAME, key, null)).thenReturn(s3meta) - val mpUpload = MultipartUpload(null, null, Date(), Owner.DEFAULT_OWNER, key, Owner.DEFAULT_OWNER, StorageClass.STANDARD, uploadId.toString()) + val mpUpload = MultipartUpload(null, null, Date(), Initiator.DEFAULT_INITIATOR, key, Owner.DEFAULT_OWNER, StorageClass.STANDARD, uploadId.toString()) val info = MultipartUploadInfo( mpUpload, "application/octet-stream", @@ -357,6 +359,7 @@ internal class MultipartControllerTest : BaseControllerTest() { anyOrNull(), any(), anyOrNull(), + anyOrNull(), anyOrNull() ) ).thenReturn(result) @@ -395,7 +398,7 @@ internal class MultipartControllerTest : BaseControllerTest() { val s3meta = s3ObjectMetadata(key, UUID.randomUUID().toString()) whenever(objectService.getObject(TEST_BUCKET_NAME, key, null)).thenReturn(s3meta) - val mpUpload = MultipartUpload(null, null, Date(), Owner.DEFAULT_OWNER, key, Owner.DEFAULT_OWNER, StorageClass.STANDARD, uploadId.toString()) + val mpUpload = MultipartUpload(null, null, Date(), Initiator.DEFAULT_INITIATOR, key, Owner.DEFAULT_OWNER, StorageClass.STANDARD, uploadId.toString()) val info = MultipartUploadInfo( mpUpload, "application/octet-stream", @@ -430,6 +433,7 @@ internal class MultipartControllerTest : BaseControllerTest() { anyOrNull(), any(), anyOrNull(), + anyOrNull(), anyOrNull() ) ).thenReturn(result) @@ -472,11 +476,10 @@ internal class MultipartControllerTest : BaseControllerTest() { doThrow(S3Exception.PRECONDITION_FAILED) .whenever(objectService) .verifyObjectMatching( + eq(TEST_BUCKET_NAME), + eq(key), anyOrNull(), anyOrNull(), - anyOrNull(), - anyOrNull(), - eq(s3meta) ) val uri = UriComponentsBuilder @@ -571,7 +574,7 @@ internal class MultipartControllerTest : BaseControllerTest() { null, null, Date(), - Owner.DEFAULT_OWNER, + Initiator.DEFAULT_INITIATOR, "my/key.txt", Owner.DEFAULT_OWNER, StorageClass.STANDARD, @@ -632,7 +635,7 @@ internal class MultipartControllerTest : BaseControllerTest() { val uploadIdMarker = "u-marker" val uploads = listOf( - MultipartUpload(null, null, Date(), Owner.DEFAULT_OWNER, "pre/a.txt", Owner.DEFAULT_OWNER, StorageClass.STANDARD, "u-1") + MultipartUpload(null, null, Date(), Initiator.DEFAULT_INITIATOR, "pre/a.txt", Owner.DEFAULT_OWNER, StorageClass.STANDARD, "u-1") ) val result = ListMultipartUploadsResult( TEST_BUCKET_NAME, @@ -687,7 +690,7 @@ internal class MultipartControllerTest : BaseControllerTest() { whenever(bucketService.verifyBucketExists(TEST_BUCKET_NAME)).thenReturn(bucketMeta) val uploads = listOf( - MultipartUpload(null, null, Date(), Owner.DEFAULT_OWNER, "k1", Owner.DEFAULT_OWNER, StorageClass.STANDARD, "u-1") + MultipartUpload(null, null, Date(), Initiator.DEFAULT_INITIATOR, "k1", Owner.DEFAULT_OWNER, StorageClass.STANDARD, "u-1") ) val result = ListMultipartUploadsResult( @@ -828,7 +831,7 @@ internal class MultipartControllerTest : BaseControllerTest() { TEST_BUCKET_NAME, null, null, - Owner.DEFAULT_OWNER, + Initiator.DEFAULT_INITIATOR, false, "my/key.txt", 1000, @@ -877,7 +880,7 @@ internal class MultipartControllerTest : BaseControllerTest() { TEST_BUCKET_NAME, null, null, - Owner.DEFAULT_OWNER, + Initiator.DEFAULT_INITIATOR, false, "my/key.txt", maxParts, @@ -930,7 +933,7 @@ internal class MultipartControllerTest : BaseControllerTest() { TEST_BUCKET_NAME, null, null, - Owner.DEFAULT_OWNER, + Initiator.DEFAULT_INITIATOR, true, "my/key.txt", maxParts, @@ -1460,7 +1463,7 @@ internal class MultipartControllerTest : BaseControllerTest() { argThat { this.startsWith("application/octet-stream") }, anyOrNull(), eq(Owner.DEFAULT_OWNER), - eq(Owner.DEFAULT_OWNER), + eq(Initiator.DEFAULT_INITIATOR), anyOrNull>(), anyOrNull>(), anyOrNull>(), @@ -1528,7 +1531,7 @@ internal class MultipartControllerTest : BaseControllerTest() { anyOrNull(), anyOrNull(), eq(Owner.DEFAULT_OWNER), - eq(Owner.DEFAULT_OWNER), + eq(Initiator.DEFAULT_INITIATOR), anyOrNull>(), eq(mapOf("x-amz-server-side-encryption" to "AES256")), anyOrNull>(), @@ -1571,7 +1574,7 @@ internal class MultipartControllerTest : BaseControllerTest() { anyOrNull(), anyOrNull(), eq(Owner.DEFAULT_OWNER), - eq(Owner.DEFAULT_OWNER), + eq(Initiator.DEFAULT_INITIATOR), anyOrNull>(), anyOrNull>(), anyOrNull>(), @@ -1613,7 +1616,7 @@ internal class MultipartControllerTest : BaseControllerTest() { eq(null), anyOrNull(), eq(Owner.DEFAULT_OWNER), - eq(Owner.DEFAULT_OWNER), + eq(Initiator.DEFAULT_INITIATOR), anyOrNull>(), anyOrNull>(), anyOrNull>(), diff --git a/server/src/test/kotlin/com/adobe/testing/s3mock/controller/ObjectControllerTest.kt b/server/src/test/kotlin/com/adobe/testing/s3mock/controller/ObjectControllerTest.kt index 0d771b2d3..6a860f5d1 100644 --- a/server/src/test/kotlin/com/adobe/testing/s3mock/controller/ObjectControllerTest.kt +++ b/server/src/test/kotlin/com/adobe/testing/s3mock/controller/ObjectControllerTest.kt @@ -79,7 +79,10 @@ import java.time.Instant import java.util.UUID @MockitoBean(types = [KmsKeyStore::class, MultipartService::class, BucketController::class, MultipartController::class]) -@WebMvcTest(properties = ["com.adobe.testing.s3mock.store.region=us-east-1"]) +@WebMvcTest( + controllers = [ObjectController::class], + properties = ["com.adobe.testing.s3mock.store.region=us-east-1"] +) internal class ObjectControllerTest : BaseControllerTest() { @MockitoBean private lateinit var objectService: ObjectService @@ -409,11 +412,8 @@ internal class ObjectControllerTest : BaseControllerTest() { givenBucket() val key = "name" - val owner = Owner( - "mtd@amazon.com", - "75aa57f09aa0c8caeab4f8c24e99d10f8e7faeebf76c078efc7c6caea54ba06a" - ) - val grantee = CanonicalUser(owner.displayName, owner.id) + val owner = Owner("75aa57f09aa0c8caeab4f8c24e99d10f8e7faeebf76c078efc7c6caea54ba06a") + val grantee = CanonicalUser(null, owner.id) val policy = AccessControlPolicy( listOf(Grant(grantee, Grant.Permission.FULL_CONTROL)), owner @@ -443,11 +443,8 @@ internal class ObjectControllerTest : BaseControllerTest() { givenBucket() val key = "name" - val owner = Owner( - "mtd@amazon.com", - "75aa57f09aa0c8caeab4f8c24e99d10f8e7faeebf76c078efc7c6caea54ba06a" - ) - val grantee = CanonicalUser(owner.displayName, owner.id) + val owner = Owner("75aa57f09aa0c8caeab4f8c24e99d10f8e7faeebf76c078efc7c6caea54ba06a") + val grantee = CanonicalUser(null, owner.id) val policy = AccessControlPolicy( listOf(Grant(grantee, Grant.Permission.FULL_CONTROL)), owner diff --git a/server/src/test/kotlin/com/adobe/testing/s3mock/dto/AccessControlPolicyTest.kt b/server/src/test/kotlin/com/adobe/testing/s3mock/dto/AccessControlPolicyTest.kt index 0211d81cb..fd67fc472 100644 --- a/server/src/test/kotlin/com/adobe/testing/s3mock/dto/AccessControlPolicyTest.kt +++ b/server/src/test/kotlin/com/adobe/testing/s3mock/dto/AccessControlPolicyTest.kt @@ -30,10 +30,7 @@ internal class AccessControlPolicyTest { val owner = iut.owner assertThat(owner).isNotNull() - assertThat(owner?.id).isEqualTo( - "75aa57f09aa0c8caeab4f8c24e99d10f8e7faeebf76c078efc7c6caea54ba06a" - ) - assertThat(owner?.displayName).isEqualTo("mtd@amazon.com") + assertThat(owner!!.id).isEqualTo("75aa57f09aa0c8caeab4f8c24e99d10f8e7faeebf76c078efc7c6caea54ba06a") assertThat(iut.accessControlList).hasSize(3) iut.accessControlList?.get(0).also { @@ -65,11 +62,8 @@ internal class AccessControlPolicyTest { @Test fun testSerialization(testInfo: TestInfo) { - val owner = Owner( - "mtd@amazon.com", - "75aa57f09aa0c8caeab4f8c24e99d10f8e7faeebf76c078efc7c6caea54ba06a" - ) - val grantee = CanonicalUser(owner.displayName, owner.id) + val owner = Owner("75aa57f09aa0c8caeab4f8c24e99d10f8e7faeebf76c078efc7c6caea54ba06a") + val grantee = CanonicalUser("mtd@amazon.com", owner.id) val group = Group(URI.create("http://acs.amazonaws.com/groups/s3/LogDelivery")) val customer = AmazonCustomerByEmail("xyz@amazon.com") diff --git a/server/src/test/kotlin/com/adobe/testing/s3mock/dto/CompleteMultipartUploadResultTest.kt b/server/src/test/kotlin/com/adobe/testing/s3mock/dto/CompleteMultipartUploadResultTest.kt index 893de6b1c..7db778ad9 100644 --- a/server/src/test/kotlin/com/adobe/testing/s3mock/dto/CompleteMultipartUploadResultTest.kt +++ b/server/src/test/kotlin/com/adobe/testing/s3mock/dto/CompleteMultipartUploadResultTest.kt @@ -35,9 +35,9 @@ internal class CompleteMultipartUploadResultTest { ChecksumAlgorithm.SHA256, ChecksumType.COMPOSITE, Date(1514477008120L), - Owner("displayName100", (100L).toString()), + Initiator("displayName100", (100L).toString()), "key", - Owner("displayName10", (10L).toString()), + Owner((10L).toString()), StorageClass.STANDARD, "uploadId", ), diff --git a/server/src/test/kotlin/com/adobe/testing/s3mock/dto/ListAllMyBucketsResultTest.kt b/server/src/test/kotlin/com/adobe/testing/s3mock/dto/ListAllMyBucketsResultTest.kt index ed40468df..fd31afb33 100644 --- a/server/src/test/kotlin/com/adobe/testing/s3mock/dto/ListAllMyBucketsResultTest.kt +++ b/server/src/test/kotlin/com/adobe/testing/s3mock/dto/ListAllMyBucketsResultTest.kt @@ -26,7 +26,7 @@ internal class ListAllMyBucketsResultTest { fun testSerialization(testInfo: TestInfo) { val iut = ListAllMyBucketsResult( - Owner("displayName", 10L.toString()), + Owner(10L.toString()), createBuckets(), "some-prefix", "50d8e003-0451-48fd-9c49-8208b151649c" diff --git a/server/src/test/kotlin/com/adobe/testing/s3mock/dto/ListBucketResultTest.kt b/server/src/test/kotlin/com/adobe/testing/s3mock/dto/ListBucketResultTest.kt index 05dfca094..fe89fda96 100644 --- a/server/src/test/kotlin/com/adobe/testing/s3mock/dto/ListBucketResultTest.kt +++ b/server/src/test/kotlin/com/adobe/testing/s3mock/dto/ListBucketResultTest.kt @@ -48,7 +48,7 @@ internal class ListBucketResultTest { "\"fba9dede5f27731c9771645a39863328\"", "key$it", "2009-10-12T17:50:30.000Z", - Owner("displayName", (10L + it).toString()), + Owner((10L + it).toString()), null, "434234", StorageClass.STANDARD diff --git a/server/src/test/kotlin/com/adobe/testing/s3mock/dto/ListBucketResultV2Test.kt b/server/src/test/kotlin/com/adobe/testing/s3mock/dto/ListBucketResultV2Test.kt index 104e7fbc4..53384ab97 100644 --- a/server/src/test/kotlin/com/adobe/testing/s3mock/dto/ListBucketResultV2Test.kt +++ b/server/src/test/kotlin/com/adobe/testing/s3mock/dto/ListBucketResultV2Test.kt @@ -53,7 +53,7 @@ internal class ListBucketResultV2Test { "\"fba9dede5f27731c9771645a39863328\"", "key$it", "2009-10-12T17:50:30.000Z", - Owner("displayName", (10L + it).toString()), + Owner((10L + it).toString()), null, "434234", StorageClass.STANDARD diff --git a/server/src/test/kotlin/com/adobe/testing/s3mock/dto/ListMultipartUploadsResultTest.kt b/server/src/test/kotlin/com/adobe/testing/s3mock/dto/ListMultipartUploadsResultTest.kt index b8b19c48b..e12d5b2fb 100644 --- a/server/src/test/kotlin/com/adobe/testing/s3mock/dto/ListMultipartUploadsResultTest.kt +++ b/server/src/test/kotlin/com/adobe/testing/s3mock/dto/ListMultipartUploadsResultTest.kt @@ -49,9 +49,9 @@ internal class ListMultipartUploadsResultTest { ChecksumAlgorithm.SHA256, ChecksumType.COMPOSITE, Date(1514477008120L), - Owner("displayName100$it", (100L + it).toString()), + Initiator("displayName100$it", (100L + it).toString()), "key$it", - Owner("displayName10$it", (10L + it).toString()), + Owner((10L + it).toString()), StorageClass.STANDARD, "uploadId$it", ) diff --git a/server/src/test/kotlin/com/adobe/testing/s3mock/dto/ListPartsResultTest.kt b/server/src/test/kotlin/com/adobe/testing/s3mock/dto/ListPartsResultTest.kt index e95a73e74..ab83e9912 100644 --- a/server/src/test/kotlin/com/adobe/testing/s3mock/dto/ListPartsResultTest.kt +++ b/server/src/test/kotlin/com/adobe/testing/s3mock/dto/ListPartsResultTest.kt @@ -29,12 +29,12 @@ internal class ListPartsResultTest { "bucketName", ChecksumAlgorithm.CRC32, ChecksumType.COMPOSITE, - Owner("id", "displayName"), + Initiator("id", "displayName"), false, "fileName", 1000, 100, - Owner("id", "displayName"), + Owner("displayName"), createParts(), 5, StorageClass.STANDARD, diff --git a/server/src/test/kotlin/com/adobe/testing/s3mock/service/ServiceTestBase.kt b/server/src/test/kotlin/com/adobe/testing/s3mock/service/ServiceTestBase.kt index 3130d3b05..d4a09c28f 100644 --- a/server/src/test/kotlin/com/adobe/testing/s3mock/service/ServiceTestBase.kt +++ b/server/src/test/kotlin/com/adobe/testing/s3mock/service/ServiceTestBase.kt @@ -148,7 +148,7 @@ internal abstract class ServiceTestBase { val lastModified = "lastModified" val etag = "etag" val size = "size" - val owner = Owner("name", 0L.toString()) + val owner = Owner(0L.toString()) return S3Object( ChecksumAlgorithm.SHA256, ChecksumType.FULL_OBJECT, @@ -166,7 +166,7 @@ internal abstract class ServiceTestBase { val lastModified = "lastModified" val etag = "etag" val size = "size" - val owner = Owner("name", 0L.toString()) + val owner = Owner(0L.toString()) return S3ObjectMetadata( id, key, diff --git a/server/src/test/kotlin/com/adobe/testing/s3mock/store/MultipartStoreTest.kt b/server/src/test/kotlin/com/adobe/testing/s3mock/store/MultipartStoreTest.kt index 8c4fa99fc..adb967699 100644 --- a/server/src/test/kotlin/com/adobe/testing/s3mock/store/MultipartStoreTest.kt +++ b/server/src/test/kotlin/com/adobe/testing/s3mock/store/MultipartStoreTest.kt @@ -83,7 +83,7 @@ internal class MultipartStoreTest : StoreTestBase() { DEFAULT_CONTENT_TYPE, storeHeaders(), TEST_OWNER, - TEST_OWNER, + TEST_INITIATOR, NO_USER_METADATA, NO_ENCRYPTION_HEADERS, NO_TAGS, @@ -123,7 +123,7 @@ internal class MultipartStoreTest : StoreTestBase() { DEFAULT_CONTENT_TYPE, storeHeaders(), TEST_OWNER, - TEST_OWNER, + TEST_INITIATOR, NO_USER_METADATA, NO_ENCRYPTION_HEADERS, NO_TAGS, @@ -172,7 +172,7 @@ internal class MultipartStoreTest : StoreTestBase() { DEFAULT_CONTENT_TYPE, storeHeaders(), TEST_OWNER, - TEST_OWNER, + TEST_INITIATOR, NO_USER_METADATA, NO_ENCRYPTION_HEADERS, NO_TAGS, @@ -211,6 +211,7 @@ internal class MultipartStoreTest : StoreTestBase() { multipartUploadInfo, "location", NO_CHECKSUM, + NO_CHECKSUMTYPE, NO_CHECKSUM_ALGORITHM, ) val md5 = MessageDigest.getInstance("MD5") @@ -251,7 +252,7 @@ internal class MultipartStoreTest : StoreTestBase() { DEFAULT_CONTENT_TYPE, storeHeaders(), TEST_OWNER, - TEST_OWNER, + TEST_INITIATOR, NO_USER_METADATA, NO_ENCRYPTION_HEADERS, NO_TAGS, @@ -288,6 +289,7 @@ internal class MultipartStoreTest : StoreTestBase() { multipartUploadInfo, "location", NO_CHECKSUM, + NO_CHECKSUMTYPE, NO_CHECKSUM_ALGORITHM, ) @@ -318,7 +320,7 @@ internal class MultipartStoreTest : StoreTestBase() { DEFAULT_CONTENT_TYPE, storeHeaders(), TEST_OWNER, - TEST_OWNER, + TEST_INITIATOR, userMetadata, NO_ENCRYPTION_HEADERS, NO_TAGS, @@ -355,6 +357,7 @@ internal class MultipartStoreTest : StoreTestBase() { multipartUploadInfo, "location", NO_CHECKSUM, + NO_CHECKSUMTYPE, NO_CHECKSUM_ALGORITHM, ) @@ -389,7 +392,7 @@ internal class MultipartStoreTest : StoreTestBase() { DEFAULT_CONTENT_TYPE, storeHeaders(), TEST_OWNER, - TEST_OWNER, + TEST_INITIATOR, userMetadata, NO_ENCRYPTION_HEADERS, NO_TAGS, @@ -445,6 +448,7 @@ internal class MultipartStoreTest : StoreTestBase() { multipartUploadInfo, "location", checksum, + ChecksumType.COMPOSITE, ChecksumAlgorithm.CRC32, ) } @@ -473,7 +477,7 @@ internal class MultipartStoreTest : StoreTestBase() { DEFAULT_CONTENT_TYPE, storeHeaders(), TEST_OWNER, - TEST_OWNER, + TEST_INITIATOR, NO_USER_METADATA, NO_ENCRYPTION_HEADERS, NO_TAGS, @@ -504,6 +508,7 @@ internal class MultipartStoreTest : StoreTestBase() { multipartUploadInfo, "location", wrongOverallChecksum, + ChecksumType.COMPOSITE, ChecksumAlgorithm.CRC32, ) }.isInstanceOf(S3Exception::class.java) @@ -532,7 +537,7 @@ internal class MultipartStoreTest : StoreTestBase() { DEFAULT_CONTENT_TYPE, storeHeaders(), TEST_OWNER, - TEST_OWNER, + TEST_INITIATOR, userMetadata, NO_ENCRYPTION_HEADERS, NO_TAGS, @@ -570,6 +575,7 @@ internal class MultipartStoreTest : StoreTestBase() { multipartUploadInfo, "location", checksum, + ChecksumType.COMPOSITE, ChecksumAlgorithm.CRC32, ) }.isInstanceOf(S3Exception::class.java) @@ -609,7 +615,7 @@ internal class MultipartStoreTest : StoreTestBase() { DEFAULT_CONTENT_TYPE, storeHeaders(), TEST_OWNER, - TEST_OWNER, + TEST_INITIATOR, NO_USER_METADATA, NO_ENCRYPTION_HEADERS, NO_TAGS, @@ -673,7 +679,7 @@ internal class MultipartStoreTest : StoreTestBase() { DEFAULT_CONTENT_TYPE, storeHeaders(), TEST_OWNER, - TEST_OWNER, + TEST_INITIATOR, NO_USER_METADATA, NO_ENCRYPTION_HEADERS, NO_TAGS, @@ -704,6 +710,7 @@ internal class MultipartStoreTest : StoreTestBase() { multipartUploadInfo, "location", NO_CHECKSUM, + NO_CHECKSUMTYPE, NO_CHECKSUM_ALGORITHM, ) @@ -728,7 +735,7 @@ internal class MultipartStoreTest : StoreTestBase() { DEFAULT_CONTENT_TYPE, storeHeaders(), TEST_OWNER, - TEST_OWNER, + TEST_INITIATOR, NO_USER_METADATA, NO_ENCRYPTION_HEADERS, NO_TAGS, @@ -757,6 +764,7 @@ internal class MultipartStoreTest : StoreTestBase() { multipartUploadInfo, "location", NO_CHECKSUM, + NO_CHECKSUMTYPE, NO_CHECKSUM_ALGORITHM, ) @@ -782,7 +790,7 @@ internal class MultipartStoreTest : StoreTestBase() { DEFAULT_CONTENT_TYPE, storeHeaders(), TEST_OWNER, - TEST_OWNER, + TEST_INITIATOR, NO_USER_METADATA, NO_ENCRYPTION_HEADERS, NO_TAGS, @@ -802,7 +810,7 @@ internal class MultipartStoreTest : StoreTestBase() { DEFAULT_CONTENT_TYPE, storeHeaders(), TEST_OWNER, - TEST_OWNER, + TEST_INITIATOR, NO_USER_METADATA, NO_ENCRYPTION_HEADERS, NO_TAGS, @@ -842,6 +850,7 @@ internal class MultipartStoreTest : StoreTestBase() { multipartUploadInfo1, "location", NO_CHECKSUM, + NO_CHECKSUMTYPE, NO_CHECKSUM_ALGORITHM, ) multipartStore.completeMultipartUpload( @@ -854,6 +863,7 @@ internal class MultipartStoreTest : StoreTestBase() { multipartUploadInfo2, "location", NO_CHECKSUM, + NO_CHECKSUMTYPE, NO_CHECKSUM_ALGORITHM, ) @@ -876,7 +886,7 @@ internal class MultipartStoreTest : StoreTestBase() { DEFAULT_CONTENT_TYPE, storeHeaders(), TEST_OWNER, - TEST_OWNER, + TEST_INITIATOR, NO_USER_METADATA, NO_ENCRYPTION_HEADERS, NO_TAGS, @@ -953,7 +963,7 @@ internal class MultipartStoreTest : StoreTestBase() { DEFAULT_CONTENT_TYPE, storeHeaders(), TEST_OWNER, - TEST_OWNER, + TEST_INITIATOR, NO_USER_METADATA, NO_ENCRYPTION_HEADERS, NO_TAGS, @@ -1025,7 +1035,7 @@ internal class MultipartStoreTest : StoreTestBase() { DEFAULT_CONTENT_TYPE, storeHeaders(), TEST_OWNER, - TEST_OWNER, + TEST_INITIATOR, NO_USER_METADATA, NO_ENCRYPTION_HEADERS, NO_TAGS, @@ -1096,7 +1106,7 @@ internal class MultipartStoreTest : StoreTestBase() { TEXT_PLAIN, storeHeaders(), TEST_OWNER, - TEST_OWNER, + TEST_INITIATOR, NO_USER_METADATA, NO_ENCRYPTION_HEADERS, NO_TAGS, @@ -1125,6 +1135,7 @@ internal class MultipartStoreTest : StoreTestBase() { multipartUploadInfo, "location", NO_CHECKSUM, + NO_CHECKSUMTYPE, NO_CHECKSUM_ALGORITHM, ) val s = objectStore.getS3ObjectMetadata(bucket, id, null)!!.dataPath diff --git a/server/src/test/kotlin/com/adobe/testing/s3mock/store/ObjectStoreTest.kt b/server/src/test/kotlin/com/adobe/testing/s3mock/store/ObjectStoreTest.kt index 46a9dec25..f4871e76f 100644 --- a/server/src/test/kotlin/com/adobe/testing/s3mock/store/ObjectStoreTest.kt +++ b/server/src/test/kotlin/com/adobe/testing/s3mock/store/ObjectStoreTest.kt @@ -333,11 +333,8 @@ internal class ObjectStoreTest : StoreTestBase() { @Test fun testStoreAndRetrieveAcl() { - val owner = Owner( - "mtd@amazon.com", - "75aa57f09aa0c8caeab4f8c24e99d10f8e7faeebf76c078efc7c6caea54ba06a" - ) - val grantee = CanonicalUser(owner.displayName, owner.id) + val owner = Owner("75aa57f09aa0c8caeab4f8c24e99d10f8e7faeebf76c078efc7c6caea54ba06a") + val grantee = CanonicalUser(null, owner.id) val policy = AccessControlPolicy( listOf(Grant(grantee, Grant.Permission.FULL_CONTROL)), owner diff --git a/server/src/test/kotlin/com/adobe/testing/s3mock/store/S3ObjectMetadataTest.kt b/server/src/test/kotlin/com/adobe/testing/s3mock/store/S3ObjectMetadataTest.kt index 286fa7f1f..0aaceb969 100644 --- a/server/src/test/kotlin/com/adobe/testing/s3mock/store/S3ObjectMetadataTest.kt +++ b/server/src/test/kotlin/com/adobe/testing/s3mock/store/S3ObjectMetadataTest.kt @@ -57,7 +57,6 @@ class S3ObjectMetadataTest { assertThat(iut.legalHold).isEqualTo(LegalHold(LegalHold.Status.ON)) assertThat(iut.retention).isEqualTo(Retention(Mode.GOVERNANCE, Instant.parse("2025-09-12T22:50:49.038Z"))) assertThat(iut.owner).isNotNull() - assertThat(iut.owner.displayName).isEqualTo("s3-mock-file-store") assertThat(iut.owner.id).isEqualTo("79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2be") assertThat(iut.storeHeaders).isEqualTo( mapOf( diff --git a/server/src/test/kotlin/com/adobe/testing/s3mock/store/StoreTestBase.kt b/server/src/test/kotlin/com/adobe/testing/s3mock/store/StoreTestBase.kt index d44f9b7fb..c10d6ed30 100644 --- a/server/src/test/kotlin/com/adobe/testing/s3mock/store/StoreTestBase.kt +++ b/server/src/test/kotlin/com/adobe/testing/s3mock/store/StoreTestBase.kt @@ -18,6 +18,7 @@ package com.adobe.testing.s3mock.store import com.adobe.testing.s3mock.dto.ChecksumAlgorithm import com.adobe.testing.s3mock.dto.ChecksumType +import com.adobe.testing.s3mock.dto.Initiator import com.adobe.testing.s3mock.dto.ObjectOwnership import com.adobe.testing.s3mock.dto.Owner import com.adobe.testing.s3mock.dto.Tag @@ -75,7 +76,8 @@ internal abstract class StoreTestBase { const val ENCODING_GZIP = "gzip" val NO_PREFIX: String? = null const val DEFAULT_CONTENT_TYPE = MediaType.APPLICATION_OCTET_STREAM_VALUE - val TEST_OWNER: Owner = Owner("s3-mock-file-store", "123") + val TEST_OWNER: Owner = Owner("123") + val TEST_INITIATOR: Initiator = Initiator("s3-mock-file-store", "123") val BUCKET_NAMES = mutableSetOf() } diff --git a/server/src/test/kotlin/com/adobe/testing/s3mock/util/HeaderUtilTest.kt b/server/src/test/kotlin/com/adobe/testing/s3mock/util/HeaderUtilTest.kt index d760cb25a..82d5ab615 100644 --- a/server/src/test/kotlin/com/adobe/testing/s3mock/util/HeaderUtilTest.kt +++ b/server/src/test/kotlin/com/adobe/testing/s3mock/util/HeaderUtilTest.kt @@ -80,7 +80,7 @@ internal class HeaderUtilTest { null, null, null, - Owner("name", 0L.toString()), + Owner(0L.toString()), null, null, ChecksumAlgorithm.SHA256, diff --git a/server/src/test/resources/com/adobe/testing/s3mock/dto/AccessControlPolicyTest_testDeserialization.xml b/server/src/test/resources/com/adobe/testing/s3mock/dto/AccessControlPolicyTest_testDeserialization.xml index 011198d96..f6ec147d5 100644 --- a/server/src/test/resources/com/adobe/testing/s3mock/dto/AccessControlPolicyTest_testDeserialization.xml +++ b/server/src/test/resources/com/adobe/testing/s3mock/dto/AccessControlPolicyTest_testDeserialization.xml @@ -19,7 +19,6 @@ 75aa57f09aa0c8caeab4f8c24e99d10f8e7faeebf76c078efc7c6caea54ba06a - mtd@amazon.com diff --git a/server/src/test/resources/com/adobe/testing/s3mock/dto/AccessControlPolicyTest_testSerialization.xml b/server/src/test/resources/com/adobe/testing/s3mock/dto/AccessControlPolicyTest_testSerialization.xml index 434f78617..a2458a788 100644 --- a/server/src/test/resources/com/adobe/testing/s3mock/dto/AccessControlPolicyTest_testSerialization.xml +++ b/server/src/test/resources/com/adobe/testing/s3mock/dto/AccessControlPolicyTest_testSerialization.xml @@ -19,7 +19,6 @@ 75aa57f09aa0c8caeab4f8c24e99d10f8e7faeebf76c078efc7c6caea54ba06a - mtd@amazon.com diff --git a/server/src/test/resources/com/adobe/testing/s3mock/dto/ListAllMyBucketsResultTest_testSerialization.xml b/server/src/test/resources/com/adobe/testing/s3mock/dto/ListAllMyBucketsResultTest_testSerialization.xml index c57633fb0..f24986036 100644 --- a/server/src/test/resources/com/adobe/testing/s3mock/dto/ListAllMyBucketsResultTest_testSerialization.xml +++ b/server/src/test/resources/com/adobe/testing/s3mock/dto/ListAllMyBucketsResultTest_testSerialization.xml @@ -19,7 +19,6 @@ 10 - displayName diff --git a/server/src/test/resources/com/adobe/testing/s3mock/dto/ListBucketResultTest_testSerialization.xml b/server/src/test/resources/com/adobe/testing/s3mock/dto/ListBucketResultTest_testSerialization.xml index caac62f2c..11102e9cb 100644 --- a/server/src/test/resources/com/adobe/testing/s3mock/dto/ListBucketResultTest_testSerialization.xml +++ b/server/src/test/resources/com/adobe/testing/s3mock/dto/ListBucketResultTest_testSerialization.xml @@ -32,7 +32,6 @@ STANDARD 10 - displayName SHA256 FULL_OBJECT @@ -45,7 +44,6 @@ STANDARD 11 - displayName SHA256 FULL_OBJECT diff --git a/server/src/test/resources/com/adobe/testing/s3mock/dto/ListBucketResultV2Test_testSerialization.xml b/server/src/test/resources/com/adobe/testing/s3mock/dto/ListBucketResultV2Test_testSerialization.xml index 19b6aaa36..24b7be4f5 100644 --- a/server/src/test/resources/com/adobe/testing/s3mock/dto/ListBucketResultV2Test_testSerialization.xml +++ b/server/src/test/resources/com/adobe/testing/s3mock/dto/ListBucketResultV2Test_testSerialization.xml @@ -29,7 +29,6 @@ STANDARD 10 - displayName SHA256 FULL_OBJECT @@ -42,7 +41,6 @@ STANDARD 11 - displayName SHA256 FULL_OBJECT diff --git a/server/src/test/resources/com/adobe/testing/s3mock/dto/ListMultipartUploadsResultTest_testSerialization.xml b/server/src/test/resources/com/adobe/testing/s3mock/dto/ListMultipartUploadsResultTest_testSerialization.xml index 4d69636d9..fbc85468b 100644 --- a/server/src/test/resources/com/adobe/testing/s3mock/dto/ListMultipartUploadsResultTest_testSerialization.xml +++ b/server/src/test/resources/com/adobe/testing/s3mock/dto/ListMultipartUploadsResultTest_testSerialization.xml @@ -34,7 +34,6 @@ uploadId0 10 - displayName100 100 @@ -50,7 +49,6 @@ uploadId1 11 - displayName101 101 diff --git a/server/src/test/resources/com/adobe/testing/s3mock/dto/ListPartsResultTest_testSerialization.xml b/server/src/test/resources/com/adobe/testing/s3mock/dto/ListPartsResultTest_testSerialization.xml index 536bd6d9a..e0762097f 100644 --- a/server/src/test/resources/com/adobe/testing/s3mock/dto/ListPartsResultTest_testSerialization.xml +++ b/server/src/test/resources/com/adobe/testing/s3mock/dto/ListPartsResultTest_testSerialization.xml @@ -29,7 +29,6 @@ 1000 100 - id displayName diff --git a/server/src/test/resources/com/adobe/testing/s3mock/store/S3ObjectMetadataTest_testDeserialization.json b/server/src/test/resources/com/adobe/testing/s3mock/store/S3ObjectMetadataTest_testDeserialization.json index 775a865a7..69aae9181 100644 --- a/server/src/test/resources/com/adobe/testing/s3mock/store/S3ObjectMetadataTest_testDeserialization.json +++ b/server/src/test/resources/com/adobe/testing/s3mock/store/S3ObjectMetadataTest_testDeserialization.json @@ -25,7 +25,6 @@ "RetainUntilDate": "2025-09-12T22:50:49.038Z" }, "owner": { - "DisplayName": "s3-mock-file-store", "ID": "79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2be" }, "storeHeaders": { @@ -54,7 +53,6 @@ } ], "Owner": { - "DisplayName": "s3-mock-file-store", "ID": "79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2be" } }, diff --git a/server/src/test/resources/com/adobe/testing/s3mock/store/S3ObjectMetadataTest_testSerialization.json b/server/src/test/resources/com/adobe/testing/s3mock/store/S3ObjectMetadataTest_testSerialization.json index ad61d21a0..1e07e4344 100644 --- a/server/src/test/resources/com/adobe/testing/s3mock/store/S3ObjectMetadataTest_testSerialization.json +++ b/server/src/test/resources/com/adobe/testing/s3mock/store/S3ObjectMetadataTest_testSerialization.json @@ -25,7 +25,6 @@ "RetainUntilDate": "2025-09-12T22:50:49.038Z" }, "owner": { - "DisplayName": "s3-mock-file-store", "ID": "79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2be" }, "storeHeaders": { @@ -54,7 +53,6 @@ } ], "Owner": { - "DisplayName": "s3-mock-file-store", "ID": "79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2be" } }, diff --git a/testsupport/common/pom.xml b/testsupport/common/pom.xml index 587df03fb..3f35068b7 100644 --- a/testsupport/common/pom.xml +++ b/testsupport/common/pom.xml @@ -43,10 +43,6 @@ software.amazon.awssdk url-connection-client - - org.jspecify - jspecify - org.assertj assertj-core diff --git a/testsupport/testcontainers/pom.xml b/testsupport/testcontainers/pom.xml index 3813b26cf..31ca11b9a 100644 --- a/testsupport/testcontainers/pom.xml +++ b/testsupport/testcontainers/pom.xml @@ -52,10 +52,6 @@ org.jetbrains.kotlin kotlin-stdlib - - org.jspecify - jspecify - com.adobe.testing s3mock diff --git a/testsupport/testng/pom.xml b/testsupport/testng/pom.xml index 502137c97..98872651c 100644 --- a/testsupport/testng/pom.xml +++ b/testsupport/testng/pom.xml @@ -39,10 +39,6 @@ org.testng testng - - org.jspecify - jspecify - org.assertj assertj-core