Skip to content

Commit 4c56403

Browse files
committed
Verify object tagging, fixes
Includes minor fixes.
1 parent 6fd38c4 commit 4c56403

File tree

6 files changed

+88
-6
lines changed

6 files changed

+88
-6
lines changed

server/src/main/java/com/adobe/testing/s3mock/ObjectController.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -554,6 +554,7 @@ public ResponseEntity<Void> putObjectTagging(
554554
var bucket = bucketService.verifyBucketExists(bucketName);
555555

556556
var s3ObjectMetadata = objectService.verifyObjectExists(bucketName, key.key(), versionId);
557+
objectService.verifyObjectTags(body.tagSet().tags());
557558
objectService.setObjectTags(bucketName, key.key(), versionId, body.tagSet().tags());
558559
return ResponseEntity
559560
.ok()
@@ -673,8 +674,7 @@ public ResponseEntity<Retention> getObjectRetention(
673674
@RequestParam(value = VERSION_ID, required = false) @Nullable String versionId) {
674675
var bucket = bucketService.verifyBucketExists(bucketName);
675676
bucketService.verifyBucketObjectLockEnabled(bucketName);
676-
var s3ObjectMetadata = objectService.verifyObjectLockConfiguration(bucketName, key.key(),
677-
versionId);
677+
var s3ObjectMetadata = objectService.verifyObjectLockConfiguration(bucketName, key.key(), versionId);
678678

679679
return ResponseEntity
680680
.ok()

server/src/main/java/com/adobe/testing/s3mock/S3Exception.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,11 @@ public class S3Exception extends RuntimeException {
4343
"The list of parts was not in ascending order. The parts list must be specified in "
4444
+ "order by part number.");
4545

46+
public static final S3Exception INVALID_TAG =
47+
new S3Exception(BAD_REQUEST.value(), "InvalidTag",
48+
"Your request contains tag input that is not valid. For example, your request might contain "
49+
+ "duplicate keys, keys or values that are too long, or system tags.");
50+
4651
public static S3Exception completeRequestMissingChecksum(String algorithm, Integer partNumber) {
4752
return new S3Exception(BAD_REQUEST.value(), BAD_REQUEST_CODE,
4853
"The upload was created using a " + algorithm + " checksum. "

server/src/main/java/com/adobe/testing/s3mock/service/ObjectService.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import static com.adobe.testing.s3mock.S3Exception.BAD_REQUEST_CONTENT;
2020
import static com.adobe.testing.s3mock.S3Exception.BAD_REQUEST_MD5;
2121
import static com.adobe.testing.s3mock.S3Exception.INVALID_REQUEST_RETAIN_DATE;
22+
import static com.adobe.testing.s3mock.S3Exception.INVALID_TAG;
2223
import static com.adobe.testing.s3mock.S3Exception.NOT_FOUND_OBJECT_LOCK;
2324
import static com.adobe.testing.s3mock.S3Exception.NOT_MODIFIED;
2425
import static com.adobe.testing.s3mock.S3Exception.NO_SUCH_KEY;
@@ -175,6 +176,31 @@ public void setObjectTags(String bucketName, String key, @Nullable String versio
175176
objectStore.storeObjectTags(bucketMetadata, uuid, versionId, tags);
176177
}
177178

179+
public void verifyObjectTags(List<Tag> tags) {
180+
if (tags.size() > 50) {
181+
throw INVALID_TAG;
182+
}
183+
for (var tag : tags) {
184+
verifyTagKey(tag.key());
185+
verifyTagValue(tag.value());
186+
}
187+
}
188+
189+
void verifyTagKey(String key) {
190+
if (key.length() > 127) {
191+
throw INVALID_TAG;
192+
}
193+
if (!key.matches("[\\w.\\-=:/@ ]+")) {
194+
throw INVALID_TAG;
195+
}
196+
}
197+
198+
void verifyTagValue(String value) {
199+
if (value.length() > 255) {
200+
throw INVALID_TAG;
201+
}
202+
}
203+
178204
public void setLegalHold(String bucketName, String key, @Nullable String versionId, LegalHold legalHold) {
179205
var bucketMetadata = bucketStore.getBucketMetadata(bucketName);
180206
var uuid = bucketMetadata.getID(key);

server/src/main/java/com/adobe/testing/s3mock/store/ObjectStore.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,9 @@ public void storeRetention(BucketMetadata bucket, UUID id, @Nullable String vers
279279
public S3ObjectMetadata getS3ObjectMetadata(BucketMetadata bucket, UUID id, @Nullable String versionId) {
280280
if (bucket.isVersioningEnabled() && versionId == null) {
281281
var s3ObjectVersions = getS3ObjectVersions(bucket, id);
282-
versionId = s3ObjectVersions.getLatestVersion();
282+
if (s3ObjectVersions != null) {
283+
versionId = s3ObjectVersions.getLatestVersion();
284+
}
283285
}
284286
var metaPath = getMetaFilePath(bucket, id, versionId);
285287

@@ -462,10 +464,10 @@ private boolean doDeleteVersion(BucketMetadata bucket, UUID id, String versionId
462464
synchronized (lockStore.get(id)) {
463465
try {
464466
var existingVersions = getS3ObjectVersions(bucket, id);
465-
if (existingVersions.versions().size() <= 1) {
467+
if (existingVersions != null && existingVersions.versions().size() <= 1) {
466468
//this is the last version of an object, delete object completely.
467469
return doDeleteObject(bucket, id);
468-
} else {
470+
} else if (existingVersions != null) {
469471
//there is at least one version of an object left, delete only the version.
470472
existingVersions.deleteVersion(versionId);
471473
writeVersionsfile(bucket, id, existingVersions);

server/src/main/java/com/adobe/testing/s3mock/store/S3ObjectMetadata.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ public record S3ObjectMetadata(
7676
checksumType = checksumType == null ? ChecksumType.FULL_OBJECT : checksumType;
7777
}
7878

79-
public static S3ObjectMetadata deleteMarker(S3ObjectMetadata metadata, String versionId) {
79+
public static S3ObjectMetadata deleteMarker(S3ObjectMetadata metadata, @Nullable String versionId) {
8080
return new S3ObjectMetadata(metadata.id,
8181
metadata.key(),
8282
metadata.size(),

server/src/test/kotlin/com/adobe/testing/s3mock/service/ObjectServiceTest.kt

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,13 @@ package com.adobe.testing.s3mock.service
1717

1818
import com.adobe.testing.s3mock.ChecksumTestUtil
1919
import com.adobe.testing.s3mock.S3Exception
20+
import com.adobe.testing.s3mock.S3Exception.INVALID_TAG
2021
import com.adobe.testing.s3mock.dto.ChecksumAlgorithm
2122
import com.adobe.testing.s3mock.dto.Delete
2223
import com.adobe.testing.s3mock.dto.Mode
2324
import com.adobe.testing.s3mock.dto.Retention
2425
import com.adobe.testing.s3mock.dto.S3ObjectIdentifier
26+
import com.adobe.testing.s3mock.dto.Tag
2527
import com.adobe.testing.s3mock.store.BucketMetadata
2628
import com.adobe.testing.s3mock.store.MultipartStore
2729
import com.adobe.testing.s3mock.util.AwsHttpHeaders
@@ -308,6 +310,53 @@ internal class ObjectServiceTest : ServiceTestBase() {
308310
assertThat(tempFileAndChecksum.right).contains("Y8S4/uAGut7vjdFZQjLKZ7P28V9EPWb4BIoeniuM0mY=")
309311
}
310312

313+
@Test
314+
fun `store tags succeeds`() {
315+
val tags = listOf(Tag("key1", "value1"), Tag("key2", "value2"))
316+
iut.verifyObjectTags(tags)
317+
}
318+
319+
@Test
320+
fun `store tags succeeds with all allowed characters`() {
321+
val tags = listOf(Tag("key1-=:/@ ", "value1"), Tag("key2", "value2"))
322+
iut.verifyObjectTags(tags)
323+
}
324+
325+
@Test
326+
fun `store tags fails with too many tags`() {
327+
val tags = mutableListOf<Tag>()
328+
for( i in 0 .. 60) {
329+
tags.add(Tag("key$i", "value$i"))
330+
}
331+
assertThatThrownBy {
332+
iut.verifyObjectTags(tags)
333+
}.isEqualTo(INVALID_TAG)
334+
}
335+
336+
@Test
337+
fun `stora tags fails with illegal characters`() {
338+
val tags = listOf(Tag("key1%()", "value1"))
339+
assertThatThrownBy {
340+
iut.verifyObjectTags(tags)
341+
}.isEqualTo(INVALID_TAG)
342+
}
343+
344+
@Test
345+
fun `stora tags fails with key gt 127 characters`() {
346+
val tags = listOf(Tag("Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque pena", "value1"))
347+
assertThatThrownBy {
348+
iut.verifyObjectTags(tags)
349+
}.isEqualTo(INVALID_TAG)
350+
}
351+
352+
@Test
353+
fun `stora tags fails with value gt 255 characters`() {
354+
val tags = listOf(Tag("key1", "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, s"))
355+
assertThatThrownBy {
356+
iut.verifyObjectTags(tags)
357+
}.isEqualTo(INVALID_TAG)
358+
}
359+
311360
@Throws(IOException::class)
312361
private fun toTempFile(path: Path, algorithm: software.amazon.awssdk.checksums.spi.ChecksumAlgorithm): Path {
313362
val (inputStream, _) = ChecksumTestUtil.prepareInputStream(path.toFile(), false, algorithm)

0 commit comments

Comments
 (0)