Enforce tagging restrictions for S3 object (#4417)
This commit is contained in:
parent
d72c6b7baa
commit
c62bd5ca41
@ -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,
|
||||
)
|
||||
|
@ -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
|
||||
|
||||
|
@ -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()]
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user