From 433a57da68ca429888b89ccf4c179dcb84dfab94 Mon Sep 17 00:00:00 2001 From: Sam Watson Date: Tue, 8 Feb 2022 03:02:09 -0700 Subject: [PATCH] raise an error on attempt to copy s3 object to itself without changing anything (#4838) --- moto/s3/exceptions.py | 10 ++++++++++ moto/s3/models.py | 11 +++++++++++ tests/test_s3/test_s3.py | 22 ++++++++++++++++++++++ 3 files changed, 43 insertions(+) diff --git a/moto/s3/exceptions.py b/moto/s3/exceptions.py index dda9f0a34..1ad5b52f7 100644 --- a/moto/s3/exceptions.py +++ b/moto/s3/exceptions.py @@ -575,6 +575,16 @@ class BucketMustHaveLockeEnabled(S3ClientError): ) +class CopyObjectMustChangeSomething(S3ClientError): + code = 400 + + def __init__(self): + super().__init__( + "InvalidRequest", + "This copy request is illegal because it is trying to copy an object to itself without changing the object's metadata, storage class, website redirect location or encryption attributes.", + ) + + class InvalidFilterRuleName(InvalidArgumentError): code = 400 diff --git a/moto/s3/models.py b/moto/s3/models.py index bf19c5a40..1314c23b3 100644 --- a/moto/s3/models.py +++ b/moto/s3/models.py @@ -37,6 +37,7 @@ from moto.s3.exceptions import ( AccessDeniedByLock, BucketAlreadyExists, BucketNeedsToBeNew, + CopyObjectMustChangeSomething, MissingBucket, InvalidBucketName, InvalidPart, @@ -2016,6 +2017,16 @@ class S3Backend(BaseBackend, CloudWatchMetricProvider): kms_key_id=None, bucket_key_enabled=False, ): + if ( + src_key.name == dest_key_name + and src_key.bucket_name == dest_bucket_name + and storage == src_key.storage_class + and acl == src_key.acl + and encryption == src_key.encryption + and kms_key_id == src_key.kms_key_id + and bucket_key_enabled == (src_key.bucket_key_enabled or False) + ): + raise CopyObjectMustChangeSomething new_key = self.put_object( bucket_name=dest_bucket_name, diff --git a/tests/test_s3/test_s3.py b/tests/test_s3/test_s3.py index 2fecf195b..58b90706d 100644 --- a/tests/test_s3/test_s3.py +++ b/tests/test_s3/test_s3.py @@ -683,6 +683,28 @@ def test_copy_key_replace_metadata_boto3(): resp["ETag"].should.equal(initial["ETag"]) +@mock_s3 +def test_copy_key_without_changes_should_error_boto3(): + client = boto3.client("s3", region_name=DEFAULT_REGION_NAME) + bucket_name = "my_bucket" + s3 = boto3.resource("s3", region_name=DEFAULT_REGION_NAME) + key_name = "my_key" + key = s3.Object(bucket_name, key_name) + + s3.create_bucket(Bucket=bucket_name) + key.put(Body=b"some value") + + with pytest.raises(ClientError) as e: + client.copy_object( + Bucket=bucket_name, + CopySource="{}/{}".format(bucket_name, key_name), + Key=key_name, + ) + e.value.response["Error"]["Message"].should.equal( + "This copy request is illegal because it is trying to copy an object to itself without changing the object's metadata, storage class, website redirect location or encryption attributes." + ) + + @freeze_time("2012-01-01 12:00:00") @mock_s3 def test_last_modified_boto3():