From 009b02bcd521626996d38d4983d02a4cf3d890de Mon Sep 17 00:00:00 2001 From: Jon Michaelchuck Date: Mon, 26 Nov 2018 15:56:46 -0800 Subject: [PATCH 1/2] Raise a client error if PutBucketTags request contains duplicate keys A PutBucketTags request with duplicate keys will raise a ClientError with code InvalidTag and message 'Cannot provide multiple Tags with the same key'. --- moto/s3/exceptions.py | 9 +++++++++ moto/s3/models.py | 5 ++++- tests/test_s3/test_s3.py | 18 ++++++++++++++++++ 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/moto/s3/exceptions.py b/moto/s3/exceptions.py index 26515dfd2..f78e24943 100644 --- a/moto/s3/exceptions.py +++ b/moto/s3/exceptions.py @@ -178,3 +178,12 @@ class InvalidStorageClass(S3ClientError): "InvalidStorageClass", "The storage class you specified is not valid", *args, **kwargs) + +class DuplicateTagKeys(S3ClientError): + code = 400 + + def __init__(self, *args, **kwargs): + super(DuplicateTagKeys, self).__init__( + "InvalidTag", + "Cannot provide multiple Tags with the same key", + *args, **kwargs) diff --git a/moto/s3/models.py b/moto/s3/models.py index bb4d7848c..fd53417fa 100644 --- a/moto/s3/models.py +++ b/moto/s3/models.py @@ -15,7 +15,7 @@ from bisect import insort from moto.core import BaseBackend, BaseModel from moto.core.utils import iso_8601_datetime_with_milliseconds, rfc_1123_datetime from .exceptions import BucketAlreadyExists, MissingBucket, InvalidPart, EntityTooSmall, MissingKey, \ - InvalidNotificationDestination, MalformedXML, InvalidStorageClass + InvalidNotificationDestination, MalformedXML, InvalidStorageClass, DuplicateTagKeys from .utils import clean_key_name, _VersionedKeyStore UPLOAD_ID_BYTES = 43 @@ -773,6 +773,9 @@ class S3Backend(BaseBackend): return key def put_bucket_tagging(self, bucket_name, tagging): + tag_keys = [tag.key for tag in tagging.tag_set.tags] + if len(tag_keys) != len(set(tag_keys)): + raise DuplicateTagKeys() bucket = self.get_bucket(bucket_name) bucket.set_tags(tagging) diff --git a/tests/test_s3/test_s3.py b/tests/test_s3/test_s3.py index 6e339abb6..ffafc0dfd 100644 --- a/tests/test_s3/test_s3.py +++ b/tests/test_s3/test_s3.py @@ -1553,6 +1553,24 @@ def test_boto3_put_bucket_tagging(): }) resp['ResponseMetadata']['HTTPStatusCode'].should.equal(200) + # With duplicate tag keys: + with assert_raises(ClientError) as err: + resp = s3.put_bucket_tagging(Bucket=bucket_name, + Tagging={ + "TagSet": [ + { + "Key": "TagOne", + "Value": "ValueOne" + }, + { + "Key": "TagOne", + "Value": "ValueOneAgain" + } + ] + }) + e = err.exception + e.response["Error"]["Code"].should.equal("InvalidTag") + e.response["Error"]["Message"].should.equal("Cannot provide multiple Tags with the same key") @mock_s3 def test_boto3_get_bucket_tagging(): From d29869bf9b678c5573f0ff343dd204c2459142a3 Mon Sep 17 00:00:00 2001 From: Jon Michaelchuck Date: Tue, 27 Nov 2018 08:32:30 -0800 Subject: [PATCH 2/2] flake8 fix --- moto/s3/exceptions.py | 1 + 1 file changed, 1 insertion(+) diff --git a/moto/s3/exceptions.py b/moto/s3/exceptions.py index f78e24943..c7d82ddfd 100644 --- a/moto/s3/exceptions.py +++ b/moto/s3/exceptions.py @@ -179,6 +179,7 @@ class InvalidStorageClass(S3ClientError): "The storage class you specified is not valid", *args, **kwargs) + class DuplicateTagKeys(S3ClientError): code = 400