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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,11 +156,13 @@ Version 4.x is JDK17 LTS bytecode compatible, with Docker and JUnit / direct Jav
* Support Browser-Based Uploads Using POST (fixes #2200)
* https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-UsingHTTPPOST.html
* Refactorings
* TBD
* Validate all integration tests against S3, fix S3Mock where necessary
* These were corner cases where error messages were incorrect, or proper validations were missing.
* Migrate all integration tests to AWS SDK v2, remove AWS SDK v1 tests from the integration-tests module
* Version updates (deliverable dependencies)
* TBD
* Version updates (build dependencies)
* TBD
* Bump actions/setup-java from 4.7.0 to 4.7.1

## 4.0.0
Version 4.x is JDK17 LTS bytecode compatible, with Docker and JUnit / direct Java integration.
Expand All @@ -170,6 +172,7 @@ Version 4.x is JDK17 LTS bytecode compatible, with Docker and JUnit / direct Jav
* Allow overriding headers in head object
* Implement If-(Un)modified-Since handling (fixes #829)
* Close all InputStreams and OutputStreams
* Checksums are returned for MultipartUploads as part of the response body
* Add AWS SDK V1 deprecation notice
* AWS has deprecated SDK for Java v1, and will remove support EOY 2025.
* S3Mock will remove usage of Java v1 early 2026.
Expand All @@ -178,7 +181,7 @@ Version 4.x is JDK17 LTS bytecode compatible, with Docker and JUnit / direct Jav
* "FROM" in Dockerfile did not match "as"
* Delete files on shutdown using a `DisposableBean` instead of `File#deleteOnExit()`
* Version updates (deliverable dependencies)
* Bump spring-boot.version from 3.3.5 to 3.4.4
* Bump spring-boot.version from 3.3.3 to 3.4.4
* Jackson 2.18.2 to 2.17.2 (remove override, use Spring-Boot supplied version)
* Bump aws-v2.version from 2.29.29 to 2.31.17
* Bump aws.version from 1.12.779 to 1.12.780
Expand Down
15 changes: 0 additions & 15 deletions integration-tests/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -48,21 +48,6 @@
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-s3</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,34 +27,34 @@ import software.amazon.awssdk.services.s3.model.ObjectOwnership
import software.amazon.awssdk.services.s3.model.Permission.FULL_CONTROL
import software.amazon.awssdk.services.s3.model.Type.CANONICAL_USER

internal class AclITV2 : S3TestBase() {
private val s3ClientV2: S3Client = createS3ClientV2()
internal class AclIT : S3TestBase() {
private val s3Client: S3Client = createS3Client()

@Test
@S3VerifiedSuccess(year = 2024)
fun testPutCannedAcl_OK(testInfo: TestInfo) {
@S3VerifiedSuccess(year = 2025)
fun `put canned ACL returns OK, get ACL returns the ACL`(testInfo: TestInfo) {
val sourceKey = UPLOAD_FILE_NAME
val bucketName = bucketName(testInfo)

//create bucket that sets ownership to non-default to allow setting ACLs.
s3ClientV2.createBucket {
s3Client.createBucket {
it.bucket(bucketName)
it.objectOwnership(ObjectOwnership.OBJECT_WRITER)
}.also {
assertThat(it.sdkHttpResponse().isSuccessful).isTrue()
}

givenObjectV2(bucketName, sourceKey)
givenObject(bucketName, sourceKey)

s3ClientV2.putObjectAcl {
s3Client.putObjectAcl {
it.bucket(bucketName)
it.key(sourceKey)
it.acl(ObjectCannedACL.PRIVATE)
}.also {
assertThat(it.sdkHttpResponse().isSuccessful).isTrue()
}

s3ClientV2.getObjectAcl {
s3Client.getObjectAcl {
it.bucket(bucketName)
it.key(sourceKey)
}.also {
Expand All @@ -69,11 +69,11 @@ internal class AclITV2 : S3TestBase() {
@Test
@S3VerifiedFailure(year = 2022,
reason = "Owner and Grantee not available on test AWS account.")
fun testGetAcl_noAcl(testInfo: TestInfo) {
fun `get ACL returns canned 'private' ACL`(testInfo: TestInfo) {
val sourceKey = UPLOAD_FILE_NAME
val (bucketName, _) = givenBucketAndObjectV2(testInfo, sourceKey)
val (bucketName, _) = givenBucketAndObject(testInfo, sourceKey)

val acl = s3ClientV2.getObjectAcl {
val acl = s3Client.getObjectAcl {
it.bucket(bucketName)
it.key(sourceKey)
}
Expand All @@ -100,16 +100,16 @@ internal class AclITV2 : S3TestBase() {
@Test
@S3VerifiedFailure(year = 2022,
reason = "Owner and Grantee not available on test AWS account.")
fun testPutAndGetAcl(testInfo: TestInfo) {
fun `put ACL returns OK, get ACL returns the ACL`(testInfo: TestInfo) {
val sourceKey = UPLOAD_FILE_NAME
val (bucketName, _) = givenBucketAndObjectV2(testInfo, sourceKey)
val (bucketName, _) = givenBucketAndObject(testInfo, sourceKey)

val userId = "79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2ab"
val userName = "John Doe"
val granteeId = "79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2ef"
val granteeName = "Jane Doe"
val granteeEmail = "jane@doe.com"
s3ClientV2.putObjectAcl {
s3Client.putObjectAcl {
it.bucket(bucketName)
it.key(sourceKey)
it.accessControlPolicy {
Expand All @@ -129,7 +129,7 @@ internal class AclITV2 : S3TestBase() {
}
}

val acl = s3ClientV2.getObjectAcl {
val acl = s3Client.getObjectAcl {
it.bucket(bucketName)
it.key(sourceKey)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2017-2024 Adobe.
* 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.
Expand All @@ -23,18 +23,16 @@ import org.junit.jupiter.api.TestInfo
import software.amazon.awssdk.core.checksums.Algorithm
import software.amazon.awssdk.core.sync.RequestBody
import software.amazon.awssdk.services.s3.model.ChecksumAlgorithm
import software.amazon.awssdk.services.s3.model.GetObjectRequest
import software.amazon.awssdk.services.s3.model.PutObjectRequest
import java.io.File
import java.io.FileInputStream
import java.io.InputStream

/**
* Chunked encoding with signing is only active in AWS SDK v2 when endpoint is http
*/
internal class AwsChunkedEndcodingITV2 : S3TestBase() {
internal class AwsChunkedEndcodingIT : S3TestBase() {

private val s3ClientV2 = createS3ClientV2(serviceEndpointHttp)
private val s3Client = createS3Client(serviceEndpointHttp)

/**
* Unfortunately the S3 API does not persist or return data that would let us verify if signed and chunked encoding
Expand All @@ -46,19 +44,19 @@ internal class AwsChunkedEndcodingITV2 : S3TestBase() {
year = 2023,
reason = "Only works with http endpoints"
)
fun testPutObject_checksum(testInfo: TestInfo) {
val bucket = givenBucketV2(testInfo)
fun `put object with checksum returns correct checksum, get object returns checksum`(testInfo: TestInfo) {
val bucket = givenBucket(testInfo)
val uploadFile = File(UPLOAD_FILE_NAME)
val uploadFileIs: InputStream = FileInputStream(uploadFile)
val expectedEtag = "\"${DigestUtil.hexDigest(uploadFileIs)}\""
val expectedChecksum = DigestUtil.checksumFor(uploadFile.toPath(), Algorithm.SHA256)

val putObjectResponse = s3ClientV2.putObject(
PutObjectRequest.builder()
.bucket(bucket)
.key(UPLOAD_FILE_NAME)
.checksumAlgorithm(ChecksumAlgorithm.SHA256)
.build(),
val putObjectResponse = s3Client.putObject(
{
it.bucket(bucket)
it.key(UPLOAD_FILE_NAME)
it.checksumAlgorithm(ChecksumAlgorithm.SHA256)
},
RequestBody.fromFile(uploadFile)
)

Expand All @@ -67,12 +65,10 @@ internal class AwsChunkedEndcodingITV2 : S3TestBase() {
assertThat(it).isEqualTo(expectedChecksum)
}

s3ClientV2.getObject(
GetObjectRequest.builder()
.bucket(bucket)
.key(UPLOAD_FILE_NAME)
.build()
).also { getObjectResponse ->
s3Client.getObject {
it.bucket(bucket)
it.key(UPLOAD_FILE_NAME)
}.also { getObjectResponse ->
assertThat(getObjectResponse.response().eTag()).isEqualTo(expectedEtag)
assertThat(getObjectResponse.response().contentLength()).isEqualTo(uploadFile.length())

Expand All @@ -93,26 +89,24 @@ internal class AwsChunkedEndcodingITV2 : S3TestBase() {
year = 2023,
reason = "Only works with http endpoints"
)
fun testPutObject_etagCreation(testInfo: TestInfo) {
val bucket = givenBucketV2(testInfo)
fun `put object creates correct etag, get object returns etag`(testInfo: TestInfo) {
val bucket = givenBucket(testInfo)
val uploadFile = File(UPLOAD_FILE_NAME)
val uploadFileIs: InputStream = FileInputStream(uploadFile)
val expectedEtag = "\"${DigestUtil.hexDigest(uploadFileIs)}\""

s3ClientV2.putObject(
PutObjectRequest.builder()
.bucket(bucket)
.key(UPLOAD_FILE_NAME)
.build(),
s3Client.putObject(
{
it.bucket(bucket)
it.key(UPLOAD_FILE_NAME)
},
RequestBody.fromFile(uploadFile)
)

s3ClientV2.getObject(
GetObjectRequest.builder()
.bucket(bucket)
.key(UPLOAD_FILE_NAME)
.build()
).also {
s3Client.getObject {
it.bucket(bucket)
it.key(UPLOAD_FILE_NAME)
}.also {
assertThat(it.response().eTag()).isEqualTo(expectedEtag)
assertThat(it.response().contentLength()).isEqualTo(uploadFile.length())
}
Expand Down
Loading