Enforce tagging restrictions for S3 object (#4417)

This commit is contained in:
Shreesha Addala 2021-10-15 15:10:28 -04:00 committed by GitHub
parent d72c6b7baa
commit c62bd5ca41
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 78 additions and 27 deletions

View File

@ -73,7 +73,7 @@ class BucketAlreadyExists(BucketError):
"select a different name and try again"
),
*args,
**kwargs
**kwargs,
)
@ -139,7 +139,7 @@ class InvalidPartOrder(S3ClientError):
"list must be specified in order by part number."
),
*args,
**kwargs
**kwargs,
)
@ -155,7 +155,7 @@ class InvalidPart(S3ClientError):
"entity tag might not have matched the part's entity tag."
),
*args,
**kwargs
**kwargs,
)
@ -167,7 +167,7 @@ class EntityTooSmall(S3ClientError):
"EntityTooSmall",
"Your proposed upload is smaller than the minimum allowed object size.",
*args,
**kwargs
**kwargs,
)
@ -181,7 +181,7 @@ class InvalidRequest(S3ClientError):
method
),
*args,
**kwargs
**kwargs,
)
@ -193,7 +193,7 @@ class IllegalLocationConstraintException(S3ClientError):
"IllegalLocationConstraintException",
"The unspecified location constraint is incompatible for the region specific endpoint this request was sent to.",
*args,
**kwargs
**kwargs,
)
@ -205,7 +205,7 @@ class MalformedXML(S3ClientError):
"MalformedXML",
"The XML you provided was not well-formed or did not validate against our published schema",
*args,
**kwargs
**kwargs,
)
@ -217,7 +217,7 @@ class MalformedACLError(S3ClientError):
"MalformedACLError",
"The XML you provided was not well-formed or did not validate against our published schema",
*args,
**kwargs
**kwargs,
)
@ -266,7 +266,7 @@ class InvalidNotificationDestination(S3ClientError):
"InvalidArgument",
"The notification destination service region is not valid for the bucket location constraint",
*args,
**kwargs
**kwargs,
)
@ -278,7 +278,7 @@ class InvalidNotificationEvent(S3ClientError):
"InvalidArgument",
"The event is not supported for notifications",
*args,
**kwargs
**kwargs,
)
@ -290,7 +290,7 @@ class InvalidStorageClass(S3ClientError):
"InvalidStorageClass",
"The storage class you specified is not valid",
*args,
**kwargs
**kwargs,
)
@ -311,7 +311,7 @@ class DuplicateTagKeys(S3ClientError):
"InvalidTag",
"Cannot provide multiple Tags with the same key",
*args,
**kwargs
**kwargs,
)
@ -341,7 +341,7 @@ class S3InvalidTokenError(S3ClientError):
"InvalidToken",
"The provided token is malformed or otherwise invalid.",
*args,
**kwargs
**kwargs,
)
@ -353,7 +353,7 @@ class BucketInvalidTokenError(BucketError):
"InvalidToken",
"The provided token is malformed or otherwise invalid.",
*args,
**kwargs
**kwargs,
)
@ -365,7 +365,7 @@ class S3InvalidAccessKeyIdError(S3ClientError):
"InvalidAccessKeyId",
"The AWS Access Key Id you provided does not exist in our records.",
*args,
**kwargs
**kwargs,
)
@ -377,7 +377,7 @@ class BucketInvalidAccessKeyIdError(S3ClientError):
"InvalidAccessKeyId",
"The AWS Access Key Id you provided does not exist in our records.",
*args,
**kwargs
**kwargs,
)
@ -389,7 +389,7 @@ class S3SignatureDoesNotMatchError(S3ClientError):
"SignatureDoesNotMatch",
"The request signature we calculated does not match the signature you provided. Check your key and signing method.",
*args,
**kwargs
**kwargs,
)
@ -401,7 +401,7 @@ class BucketSignatureDoesNotMatchError(S3ClientError):
"SignatureDoesNotMatch",
"The request signature we calculated does not match the signature you provided. Check your key and signing method.",
*args,
**kwargs
**kwargs,
)
@ -413,7 +413,7 @@ class NoSuchPublicAccessBlockConfiguration(S3ClientError):
"NoSuchPublicAccessBlockConfiguration",
"The public access block configuration was not found",
*args,
**kwargs
**kwargs,
)
@ -425,7 +425,7 @@ class InvalidPublicAccessBlockConfiguration(S3ClientError):
"InvalidRequest",
"Must specify at least one configuration.",
*args,
**kwargs
**kwargs,
)
@ -458,7 +458,7 @@ class NoSuchUpload(S3ClientError):
"NoSuchUpload",
"The specified upload does not exist. The upload ID may be invalid, or the upload may have been aborted or completed.",
*args,
**kwargs
**kwargs,
)
@ -472,7 +472,7 @@ class PreconditionFailed(S3ClientError):
"PreconditionFailed",
"At least one of the pre-conditions you specified did not hold",
condition=failed_condition,
**kwargs
**kwargs,
)
@ -487,7 +487,7 @@ class InvalidRange(S3ClientError):
"The requested range is not satisfiable",
range_requested=range_requested,
actual_size=actual_size,
**kwargs
**kwargs,
)
@ -499,7 +499,7 @@ class InvalidContinuationToken(S3ClientError):
"InvalidArgument",
"The continuation token provided is incorrect",
*args,
**kwargs
**kwargs,
)
@ -556,5 +556,14 @@ class InvalidFilterRuleName(InvalidArgumentError):
"FilterRule.Name",
value,
*args,
**kwargs
**kwargs,
)
class InvalidTagError(S3ClientError):
code = 400
def __init__(self, value, *args, **kwargs):
super(InvalidTagError, self).__init__(
"InvalidTag", value, *args, **kwargs,
)

View File

@ -26,7 +26,7 @@ from moto.core.utils import (
)
from moto.cloudwatch.models import MetricDatum
from moto.utilities.tagging_service import TaggingService
from .exceptions import (
from moto.s3.exceptions import (
AccessDeniedByLock,
BucketAlreadyExists,
BucketNeedsToBeNew,
@ -45,6 +45,7 @@ from .exceptions import (
InvalidPublicAccessBlockConfiguration,
WrongPublicAccessBlockAccountIdError,
NoSuchUpload,
InvalidTagError,
)
from .cloud_formation import cfn_to_api_encryption, is_replacement_update
from .utils import clean_key_name, _VersionedKeyStore, undo_clean_key_name
@ -1633,9 +1634,13 @@ class S3Backend(BaseBackend):
def set_key_tags(self, key, tags, key_name=None):
if key is None:
raise MissingKey(key_name)
boto_tags_dict = self.tagger.convert_dict_to_tags_input(tags)
errmsg = self.tagger.validate_tags(boto_tags_dict)
if errmsg:
raise InvalidTagError(errmsg)
self.tagger.delete_all_tags_for_resource(key.arn)
self.tagger.tag_resource(
key.arn, [{"Key": k, "Value": v} for (k, v) in tags.items()],
key.arn, boto_tags_dict,
)
return key

View File

@ -156,3 +156,8 @@ class TaggingService:
if errors
else ""
)
@staticmethod
def convert_dict_to_tags_input(tags):
""" Given a dictionary, return generic boto params for tags """
return [{"Key": k, "Value": v} for (k, v) in tags.items()]

View File

@ -34,6 +34,7 @@ import sure # noqa
from moto import settings, mock_s3, mock_s3_deprecated, mock_config
import moto.s3.models as s3model
from moto.s3.exceptions import InvalidTagError
from moto.core.exceptions import InvalidNextTokenException
from moto.settings import get_s3_default_key_buffer_size, S3_UPLOAD_PART_MIN_SIZE
from uuid import uuid4
@ -3412,6 +3413,19 @@ def test_boto3_copy_object_with_replacement_tagging():
Bucket="mybucket", Key="original", Body=b"test", Tagging="tag=old"
)
# using system tags will fail
with pytest.raises(ClientError) as err:
client.copy_object(
CopySource={"Bucket": "mybucket", "Key": "original"},
Bucket="mybucket",
Key="copy1",
TaggingDirective="REPLACE",
Tagging="aws:tag=invalid_key",
)
e = err.value
e.response["Error"]["Code"].should.equal("InvalidTag")
client.copy_object(
CopySource={"Bucket": "mybucket", "Key": "original"},
Bucket="mybucket",
@ -3937,6 +3951,13 @@ def test_boto3_put_object_with_tagging():
key = "key-with-tags"
s3.create_bucket(Bucket=bucket_name)
# using system tags will fail
with pytest.raises(ClientError) as err:
s3.put_object(Bucket=bucket_name, Key=key, Body="test", Tagging="aws:foo=bar")
e = err.value
e.response["Error"]["Code"].should.equal("InvalidTag")
s3.put_object(Bucket=bucket_name, Key=key, Body="test", Tagging="foo=bar")
s3.get_object_tagging(Bucket=bucket_name, Key=key)["TagSet"].should.contain(
@ -4666,6 +4687,17 @@ def test_boto3_put_object_tagging():
s3.put_object(Bucket=bucket_name, Key=key, Body="test")
# using system tags will fail
with pytest.raises(ClientError) as err:
s3.put_object_tagging(
Bucket=bucket_name,
Key=key,
Tagging={"TagSet": [{"Key": "aws:item1", "Value": "foo"},]},
)
e = err.value
e.response["Error"]["Code"].should.equal("InvalidTag")
resp = s3.put_object_tagging(
Bucket=bucket_name,
Key=key,