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"
|
"select a different name and try again"
|
||||||
),
|
),
|
||||||
*args,
|
*args,
|
||||||
**kwargs
|
**kwargs,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -139,7 +139,7 @@ class InvalidPartOrder(S3ClientError):
|
|||||||
"list must be specified in order by part number."
|
"list must be specified in order by part number."
|
||||||
),
|
),
|
||||||
*args,
|
*args,
|
||||||
**kwargs
|
**kwargs,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -155,7 +155,7 @@ class InvalidPart(S3ClientError):
|
|||||||
"entity tag might not have matched the part's entity tag."
|
"entity tag might not have matched the part's entity tag."
|
||||||
),
|
),
|
||||||
*args,
|
*args,
|
||||||
**kwargs
|
**kwargs,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -167,7 +167,7 @@ class EntityTooSmall(S3ClientError):
|
|||||||
"EntityTooSmall",
|
"EntityTooSmall",
|
||||||
"Your proposed upload is smaller than the minimum allowed object size.",
|
"Your proposed upload is smaller than the minimum allowed object size.",
|
||||||
*args,
|
*args,
|
||||||
**kwargs
|
**kwargs,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -181,7 +181,7 @@ class InvalidRequest(S3ClientError):
|
|||||||
method
|
method
|
||||||
),
|
),
|
||||||
*args,
|
*args,
|
||||||
**kwargs
|
**kwargs,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -193,7 +193,7 @@ class IllegalLocationConstraintException(S3ClientError):
|
|||||||
"IllegalLocationConstraintException",
|
"IllegalLocationConstraintException",
|
||||||
"The unspecified location constraint is incompatible for the region specific endpoint this request was sent to.",
|
"The unspecified location constraint is incompatible for the region specific endpoint this request was sent to.",
|
||||||
*args,
|
*args,
|
||||||
**kwargs
|
**kwargs,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -205,7 +205,7 @@ class MalformedXML(S3ClientError):
|
|||||||
"MalformedXML",
|
"MalformedXML",
|
||||||
"The XML you provided was not well-formed or did not validate against our published schema",
|
"The XML you provided was not well-formed or did not validate against our published schema",
|
||||||
*args,
|
*args,
|
||||||
**kwargs
|
**kwargs,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -217,7 +217,7 @@ class MalformedACLError(S3ClientError):
|
|||||||
"MalformedACLError",
|
"MalformedACLError",
|
||||||
"The XML you provided was not well-formed or did not validate against our published schema",
|
"The XML you provided was not well-formed or did not validate against our published schema",
|
||||||
*args,
|
*args,
|
||||||
**kwargs
|
**kwargs,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -266,7 +266,7 @@ class InvalidNotificationDestination(S3ClientError):
|
|||||||
"InvalidArgument",
|
"InvalidArgument",
|
||||||
"The notification destination service region is not valid for the bucket location constraint",
|
"The notification destination service region is not valid for the bucket location constraint",
|
||||||
*args,
|
*args,
|
||||||
**kwargs
|
**kwargs,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -278,7 +278,7 @@ class InvalidNotificationEvent(S3ClientError):
|
|||||||
"InvalidArgument",
|
"InvalidArgument",
|
||||||
"The event is not supported for notifications",
|
"The event is not supported for notifications",
|
||||||
*args,
|
*args,
|
||||||
**kwargs
|
**kwargs,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -290,7 +290,7 @@ class InvalidStorageClass(S3ClientError):
|
|||||||
"InvalidStorageClass",
|
"InvalidStorageClass",
|
||||||
"The storage class you specified is not valid",
|
"The storage class you specified is not valid",
|
||||||
*args,
|
*args,
|
||||||
**kwargs
|
**kwargs,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -311,7 +311,7 @@ class DuplicateTagKeys(S3ClientError):
|
|||||||
"InvalidTag",
|
"InvalidTag",
|
||||||
"Cannot provide multiple Tags with the same key",
|
"Cannot provide multiple Tags with the same key",
|
||||||
*args,
|
*args,
|
||||||
**kwargs
|
**kwargs,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -341,7 +341,7 @@ class S3InvalidTokenError(S3ClientError):
|
|||||||
"InvalidToken",
|
"InvalidToken",
|
||||||
"The provided token is malformed or otherwise invalid.",
|
"The provided token is malformed or otherwise invalid.",
|
||||||
*args,
|
*args,
|
||||||
**kwargs
|
**kwargs,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -353,7 +353,7 @@ class BucketInvalidTokenError(BucketError):
|
|||||||
"InvalidToken",
|
"InvalidToken",
|
||||||
"The provided token is malformed or otherwise invalid.",
|
"The provided token is malformed or otherwise invalid.",
|
||||||
*args,
|
*args,
|
||||||
**kwargs
|
**kwargs,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -365,7 +365,7 @@ class S3InvalidAccessKeyIdError(S3ClientError):
|
|||||||
"InvalidAccessKeyId",
|
"InvalidAccessKeyId",
|
||||||
"The AWS Access Key Id you provided does not exist in our records.",
|
"The AWS Access Key Id you provided does not exist in our records.",
|
||||||
*args,
|
*args,
|
||||||
**kwargs
|
**kwargs,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -377,7 +377,7 @@ class BucketInvalidAccessKeyIdError(S3ClientError):
|
|||||||
"InvalidAccessKeyId",
|
"InvalidAccessKeyId",
|
||||||
"The AWS Access Key Id you provided does not exist in our records.",
|
"The AWS Access Key Id you provided does not exist in our records.",
|
||||||
*args,
|
*args,
|
||||||
**kwargs
|
**kwargs,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -389,7 +389,7 @@ class S3SignatureDoesNotMatchError(S3ClientError):
|
|||||||
"SignatureDoesNotMatch",
|
"SignatureDoesNotMatch",
|
||||||
"The request signature we calculated does not match the signature you provided. Check your key and signing method.",
|
"The request signature we calculated does not match the signature you provided. Check your key and signing method.",
|
||||||
*args,
|
*args,
|
||||||
**kwargs
|
**kwargs,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -401,7 +401,7 @@ class BucketSignatureDoesNotMatchError(S3ClientError):
|
|||||||
"SignatureDoesNotMatch",
|
"SignatureDoesNotMatch",
|
||||||
"The request signature we calculated does not match the signature you provided. Check your key and signing method.",
|
"The request signature we calculated does not match the signature you provided. Check your key and signing method.",
|
||||||
*args,
|
*args,
|
||||||
**kwargs
|
**kwargs,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -413,7 +413,7 @@ class NoSuchPublicAccessBlockConfiguration(S3ClientError):
|
|||||||
"NoSuchPublicAccessBlockConfiguration",
|
"NoSuchPublicAccessBlockConfiguration",
|
||||||
"The public access block configuration was not found",
|
"The public access block configuration was not found",
|
||||||
*args,
|
*args,
|
||||||
**kwargs
|
**kwargs,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -425,7 +425,7 @@ class InvalidPublicAccessBlockConfiguration(S3ClientError):
|
|||||||
"InvalidRequest",
|
"InvalidRequest",
|
||||||
"Must specify at least one configuration.",
|
"Must specify at least one configuration.",
|
||||||
*args,
|
*args,
|
||||||
**kwargs
|
**kwargs,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -458,7 +458,7 @@ class NoSuchUpload(S3ClientError):
|
|||||||
"NoSuchUpload",
|
"NoSuchUpload",
|
||||||
"The specified upload does not exist. The upload ID may be invalid, or the upload may have been aborted or completed.",
|
"The specified upload does not exist. The upload ID may be invalid, or the upload may have been aborted or completed.",
|
||||||
*args,
|
*args,
|
||||||
**kwargs
|
**kwargs,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -472,7 +472,7 @@ class PreconditionFailed(S3ClientError):
|
|||||||
"PreconditionFailed",
|
"PreconditionFailed",
|
||||||
"At least one of the pre-conditions you specified did not hold",
|
"At least one of the pre-conditions you specified did not hold",
|
||||||
condition=failed_condition,
|
condition=failed_condition,
|
||||||
**kwargs
|
**kwargs,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -487,7 +487,7 @@ class InvalidRange(S3ClientError):
|
|||||||
"The requested range is not satisfiable",
|
"The requested range is not satisfiable",
|
||||||
range_requested=range_requested,
|
range_requested=range_requested,
|
||||||
actual_size=actual_size,
|
actual_size=actual_size,
|
||||||
**kwargs
|
**kwargs,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -499,7 +499,7 @@ class InvalidContinuationToken(S3ClientError):
|
|||||||
"InvalidArgument",
|
"InvalidArgument",
|
||||||
"The continuation token provided is incorrect",
|
"The continuation token provided is incorrect",
|
||||||
*args,
|
*args,
|
||||||
**kwargs
|
**kwargs,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -556,5 +556,14 @@ class InvalidFilterRuleName(InvalidArgumentError):
|
|||||||
"FilterRule.Name",
|
"FilterRule.Name",
|
||||||
value,
|
value,
|
||||||
*args,
|
*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.cloudwatch.models import MetricDatum
|
||||||
from moto.utilities.tagging_service import TaggingService
|
from moto.utilities.tagging_service import TaggingService
|
||||||
from .exceptions import (
|
from moto.s3.exceptions import (
|
||||||
AccessDeniedByLock,
|
AccessDeniedByLock,
|
||||||
BucketAlreadyExists,
|
BucketAlreadyExists,
|
||||||
BucketNeedsToBeNew,
|
BucketNeedsToBeNew,
|
||||||
@ -45,6 +45,7 @@ from .exceptions import (
|
|||||||
InvalidPublicAccessBlockConfiguration,
|
InvalidPublicAccessBlockConfiguration,
|
||||||
WrongPublicAccessBlockAccountIdError,
|
WrongPublicAccessBlockAccountIdError,
|
||||||
NoSuchUpload,
|
NoSuchUpload,
|
||||||
|
InvalidTagError,
|
||||||
)
|
)
|
||||||
from .cloud_formation import cfn_to_api_encryption, is_replacement_update
|
from .cloud_formation import cfn_to_api_encryption, is_replacement_update
|
||||||
from .utils import clean_key_name, _VersionedKeyStore, undo_clean_key_name
|
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):
|
def set_key_tags(self, key, tags, key_name=None):
|
||||||
if key is None:
|
if key is None:
|
||||||
raise MissingKey(key_name)
|
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.delete_all_tags_for_resource(key.arn)
|
||||||
self.tagger.tag_resource(
|
self.tagger.tag_resource(
|
||||||
key.arn, [{"Key": k, "Value": v} for (k, v) in tags.items()],
|
key.arn, boto_tags_dict,
|
||||||
)
|
)
|
||||||
return key
|
return key
|
||||||
|
|
||||||
|
@ -156,3 +156,8 @@ class TaggingService:
|
|||||||
if errors
|
if errors
|
||||||
else ""
|
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
|
from moto import settings, mock_s3, mock_s3_deprecated, mock_config
|
||||||
import moto.s3.models as s3model
|
import moto.s3.models as s3model
|
||||||
|
from moto.s3.exceptions import InvalidTagError
|
||||||
from moto.core.exceptions import InvalidNextTokenException
|
from moto.core.exceptions import InvalidNextTokenException
|
||||||
from moto.settings import get_s3_default_key_buffer_size, S3_UPLOAD_PART_MIN_SIZE
|
from moto.settings import get_s3_default_key_buffer_size, S3_UPLOAD_PART_MIN_SIZE
|
||||||
from uuid import uuid4
|
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"
|
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(
|
client.copy_object(
|
||||||
CopySource={"Bucket": "mybucket", "Key": "original"},
|
CopySource={"Bucket": "mybucket", "Key": "original"},
|
||||||
Bucket="mybucket",
|
Bucket="mybucket",
|
||||||
@ -3937,6 +3951,13 @@ def test_boto3_put_object_with_tagging():
|
|||||||
key = "key-with-tags"
|
key = "key-with-tags"
|
||||||
s3.create_bucket(Bucket=bucket_name)
|
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.put_object(Bucket=bucket_name, Key=key, Body="test", Tagging="foo=bar")
|
||||||
|
|
||||||
s3.get_object_tagging(Bucket=bucket_name, Key=key)["TagSet"].should.contain(
|
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")
|
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(
|
resp = s3.put_object_tagging(
|
||||||
Bucket=bucket_name,
|
Bucket=bucket_name,
|
||||||
Key=key,
|
Key=key,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user