From 0c8e314a707b4585533adeebf537f61d542902d0 Mon Sep 17 00:00:00 2001 From: AlejandroBlanco Date: Mon, 29 Sep 2025 11:39:36 +0100 Subject: [PATCH] If-Match verification with quoted or unquoted etags --- .../testing/s3mock/its/GetPutDeleteObjectIT.kt | 17 +++++++++++++++++ .../testing/s3mock/service/ObjectService.java | 8 +++++--- 2 files changed, 22 insertions(+), 3 deletions(-) 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 160eb45e0..d3b9fb1ba 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 @@ -1336,6 +1336,23 @@ internal class GetPutDeleteObjectIT : S3TestBase() { } } + @Test + @S3VerifiedSuccess(year = 2025) + fun `GET object succeeds with unquoted if-match header`(testInfo: TestInfo) { + val (bucketName, putObjectResponse) = givenBucketAndObject(testInfo, UPLOAD_FILE_NAME) + val matchingEtag = putObjectResponse.eTag() + val unquotedEtag = matchingEtag.substring(1,matchingEtag.length - 1) + + s3Client.getObject { + it.bucket(bucketName) + it.key(UPLOAD_FILE_NAME) + it.ifMatch(unquotedEtag) + }.use { + assertThat(it.response().eTag()).isEqualTo(matchingEtag) + assertThat(it.response().contentLength()).isEqualTo(UPLOAD_FILE_LENGTH) + } + } + @Test @S3VerifiedSuccess(year = 2025) fun `GET object succeeds with if-match=true and if-unmodified-since=false`(testInfo: TestInfo) { diff --git a/server/src/main/java/com/adobe/testing/s3mock/service/ObjectService.java b/server/src/main/java/com/adobe/testing/s3mock/service/ObjectService.java index 8d2dab691..83b8873a9 100644 --- a/server/src/main/java/com/adobe/testing/s3mock/service/ObjectService.java +++ b/server/src/main/java/com/adobe/testing/s3mock/service/ObjectService.java @@ -368,11 +368,13 @@ public void verifyObjectMatching( var setMatch = match != null && !match.isEmpty(); if (setMatch) { - if (match.contains(WILDCARD_ETAG) || match.contains(WILDCARD) || match.contains(etag)) { - // request cares only that the object exists or that the etag matches. + assert etag != null; + var unquotedEtag = etag.replace("\"", ""); + if (match.contains(WILDCARD_ETAG) || match.contains(WILDCARD) || match.contains(unquotedEtag) + || match.contains(etag)) { LOG.debug("Object {} exists", s3ObjectMetadata.key()); return; - } else if (!match.contains(etag)) { + } else if (!match.contains(unquotedEtag) && !match.contains(etag)) { LOG.debug("Object {} does not match etag {}", s3ObjectMetadata.key(), etag); throw PRECONDITION_FAILED; }