fix S3 PutObjectRetention and PutObjectLegalHold not using versionId (#6295)
This commit is contained in:
parent
7a4eac06d0
commit
fae7ee76b3
@ -1522,10 +1522,14 @@ class S3Response(BaseResponse):
|
|||||||
acl = self.backend.get_bucket(bucket_name).acl
|
acl = self.backend.get_bucket(bucket_name).acl
|
||||||
tagging = self._tagging_from_headers(request.headers)
|
tagging = self._tagging_from_headers(request.headers)
|
||||||
|
|
||||||
|
if "versionId" in query:
|
||||||
|
version_id = query["versionId"][0]
|
||||||
|
else:
|
||||||
|
version_id = None
|
||||||
|
|
||||||
if "retention" in query:
|
if "retention" in query:
|
||||||
if not lock_enabled:
|
if not lock_enabled:
|
||||||
raise LockNotEnabled
|
raise LockNotEnabled
|
||||||
version_id = query.get("VersionId")
|
|
||||||
retention = self._mode_until_from_body()
|
retention = self._mode_until_from_body()
|
||||||
self.backend.put_object_retention(
|
self.backend.put_object_retention(
|
||||||
bucket_name, key_name, version_id=version_id, retention=retention
|
bucket_name, key_name, version_id=version_id, retention=retention
|
||||||
@ -1535,7 +1539,6 @@ class S3Response(BaseResponse):
|
|||||||
if "legal-hold" in query:
|
if "legal-hold" in query:
|
||||||
if not lock_enabled:
|
if not lock_enabled:
|
||||||
raise LockNotEnabled
|
raise LockNotEnabled
|
||||||
version_id = query.get("VersionId")
|
|
||||||
legal_hold_status = self._legal_hold_status_from_xml(body)
|
legal_hold_status = self._legal_hold_status_from_xml(body)
|
||||||
self.backend.put_object_legal_hold(
|
self.backend.put_object_legal_hold(
|
||||||
bucket_name, key_name, version_id, legal_hold_status
|
bucket_name, key_name, version_id, legal_hold_status
|
||||||
@ -1547,10 +1550,6 @@ class S3Response(BaseResponse):
|
|||||||
return 200, response_headers, ""
|
return 200, response_headers, ""
|
||||||
|
|
||||||
if "tagging" in query:
|
if "tagging" in query:
|
||||||
if "versionId" in query:
|
|
||||||
version_id = query["versionId"][0]
|
|
||||||
else:
|
|
||||||
version_id = None
|
|
||||||
key_to_tag = self.backend.get_object(
|
key_to_tag = self.backend.get_object(
|
||||||
bucket_name, key_name, version_id=version_id
|
bucket_name, key_name, version_id=version_id
|
||||||
)
|
)
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import time
|
import time
|
||||||
import boto3
|
import boto3
|
||||||
import datetime
|
import datetime
|
||||||
import botocore
|
import pytest
|
||||||
from moto import mock_s3
|
from moto import mock_s3
|
||||||
from botocore.config import Config
|
from botocore.config import Config
|
||||||
|
from botocore.client import ClientError
|
||||||
from moto.s3.responses import DEFAULT_REGION_NAME
|
from moto.s3.responses import DEFAULT_REGION_NAME
|
||||||
import sure # noqa # pylint: disable=unused-import
|
import sure # noqa # pylint: disable=unused-import
|
||||||
|
|
||||||
@ -34,7 +35,7 @@ def test_locked_object():
|
|||||||
try:
|
try:
|
||||||
s3.delete_object(Bucket=bucket_name, Key=key_name, VersionId=version_id)
|
s3.delete_object(Bucket=bucket_name, Key=key_name, VersionId=version_id)
|
||||||
deleted = True
|
deleted = True
|
||||||
except botocore.client.ClientError as e:
|
except ClientError as e:
|
||||||
e.response["Error"]["Code"].should.equal("AccessDenied")
|
e.response["Error"]["Code"].should.equal("AccessDenied")
|
||||||
|
|
||||||
deleted.should.equal(False)
|
deleted.should.equal(False)
|
||||||
@ -64,7 +65,7 @@ def test_fail_locked_object():
|
|||||||
ObjectLockMode="COMPLIANCE",
|
ObjectLockMode="COMPLIANCE",
|
||||||
ObjectLockRetainUntilDate=until,
|
ObjectLockRetainUntilDate=until,
|
||||||
)
|
)
|
||||||
except botocore.client.ClientError as e:
|
except ClientError as e:
|
||||||
e.response["Error"]["Code"].should.equal("InvalidRequest")
|
e.response["Error"]["Code"].should.equal("InvalidRequest")
|
||||||
failed = True
|
failed = True
|
||||||
|
|
||||||
@ -99,7 +100,7 @@ def test_put_object_lock():
|
|||||||
try:
|
try:
|
||||||
s3.delete_object(Bucket=bucket_name, Key=key_name, VersionId=version_id)
|
s3.delete_object(Bucket=bucket_name, Key=key_name, VersionId=version_id)
|
||||||
deleted = True
|
deleted = True
|
||||||
except botocore.client.ClientError as e:
|
except ClientError as e:
|
||||||
e.response["Error"]["Code"].should.equal("AccessDenied")
|
e.response["Error"]["Code"].should.equal("AccessDenied")
|
||||||
|
|
||||||
deleted.should.equal(False)
|
deleted.should.equal(False)
|
||||||
@ -135,7 +136,7 @@ def test_put_object_legal_hold():
|
|||||||
try:
|
try:
|
||||||
s3.delete_object(Bucket=bucket_name, Key=key_name, VersionId=version_id)
|
s3.delete_object(Bucket=bucket_name, Key=key_name, VersionId=version_id)
|
||||||
deleted = True
|
deleted = True
|
||||||
except botocore.client.ClientError as e:
|
except ClientError as e:
|
||||||
e.response["Error"]["Code"].should.equal("AccessDenied")
|
e.response["Error"]["Code"].should.equal("AccessDenied")
|
||||||
|
|
||||||
deleted.should.equal(False)
|
deleted.should.equal(False)
|
||||||
@ -181,7 +182,7 @@ def test_put_default_lock():
|
|||||||
try:
|
try:
|
||||||
s3.delete_object(Bucket=bucket_name, Key=key_name, VersionId=version_id)
|
s3.delete_object(Bucket=bucket_name, Key=key_name, VersionId=version_id)
|
||||||
deleted = True
|
deleted = True
|
||||||
except botocore.client.ClientError as e:
|
except ClientError as e:
|
||||||
e.response["Error"]["Code"].should.equal("AccessDenied")
|
e.response["Error"]["Code"].should.equal("AccessDenied")
|
||||||
|
|
||||||
deleted.should.equal(False)
|
deleted.should.equal(False)
|
||||||
@ -194,3 +195,111 @@ def test_put_default_lock():
|
|||||||
response["ObjectLockConfiguration"]["Rule"]["DefaultRetention"][
|
response["ObjectLockConfiguration"]["Rule"]["DefaultRetention"][
|
||||||
"Days"
|
"Days"
|
||||||
].should.equal(days)
|
].should.equal(days)
|
||||||
|
|
||||||
|
|
||||||
|
@mock_s3
|
||||||
|
def test_put_object_legal_hold_with_versions():
|
||||||
|
s3 = boto3.client("s3", config=Config(region_name=DEFAULT_REGION_NAME))
|
||||||
|
|
||||||
|
bucket_name = "put-legal-bucket"
|
||||||
|
key_name = "file.txt"
|
||||||
|
|
||||||
|
s3.create_bucket(Bucket=bucket_name, ObjectLockEnabledForBucket=True)
|
||||||
|
|
||||||
|
put_obj_1 = s3.put_object(Bucket=bucket_name, Body=b"test", Key=key_name)
|
||||||
|
version_id_1 = put_obj_1["VersionId"]
|
||||||
|
# lock the object with the version, locking the version 1
|
||||||
|
s3.put_object_legal_hold(
|
||||||
|
Bucket=bucket_name,
|
||||||
|
Key=key_name,
|
||||||
|
VersionId=version_id_1,
|
||||||
|
LegalHold={"Status": "ON"},
|
||||||
|
)
|
||||||
|
|
||||||
|
# put an object on the same key, effectively creating a version 2 of the object
|
||||||
|
put_obj_2 = s3.put_object(Bucket=bucket_name, Body=b"test", Key=key_name)
|
||||||
|
version_id_2 = put_obj_2["VersionId"]
|
||||||
|
# also lock the version 2 of the object
|
||||||
|
s3.put_object_legal_hold(
|
||||||
|
Bucket=bucket_name,
|
||||||
|
Key=key_name,
|
||||||
|
VersionId=version_id_2,
|
||||||
|
LegalHold={"Status": "ON"},
|
||||||
|
)
|
||||||
|
|
||||||
|
# assert that the version 1 is locked
|
||||||
|
head_obj_1 = s3.head_object(
|
||||||
|
Bucket=bucket_name, Key=key_name, VersionId=version_id_1
|
||||||
|
)
|
||||||
|
assert head_obj_1["ObjectLockLegalHoldStatus"] == "ON"
|
||||||
|
|
||||||
|
# remove the lock from the version 1 of the object
|
||||||
|
s3.put_object_legal_hold(
|
||||||
|
Bucket=bucket_name,
|
||||||
|
Key=key_name,
|
||||||
|
VersionId=version_id_1,
|
||||||
|
LegalHold={"Status": "OFF"},
|
||||||
|
)
|
||||||
|
|
||||||
|
# assert that you can now delete the version 1 of the object
|
||||||
|
s3.delete_object(Bucket=bucket_name, Key=key_name, VersionId=version_id_1)
|
||||||
|
|
||||||
|
with pytest.raises(ClientError) as e:
|
||||||
|
s3.head_object(Bucket=bucket_name, Key=key_name, VersionId=version_id_1)
|
||||||
|
assert e.value.response["Error"]["Code"] == "404"
|
||||||
|
|
||||||
|
# cleaning
|
||||||
|
s3.put_object_legal_hold(
|
||||||
|
Bucket=bucket_name,
|
||||||
|
Key=key_name,
|
||||||
|
VersionId=version_id_2,
|
||||||
|
LegalHold={"Status": "OFF"},
|
||||||
|
)
|
||||||
|
s3.delete_object(Bucket=bucket_name, Key=key_name, VersionId=version_id_2)
|
||||||
|
s3.delete_bucket(Bucket=bucket_name)
|
||||||
|
|
||||||
|
|
||||||
|
@mock_s3
|
||||||
|
def test_put_object_lock_with_versions():
|
||||||
|
s3 = boto3.client("s3", config=Config(region_name=DEFAULT_REGION_NAME))
|
||||||
|
|
||||||
|
bucket_name = "put-lock-bucket-test"
|
||||||
|
key_name = "file.txt"
|
||||||
|
seconds_lock = 2
|
||||||
|
|
||||||
|
s3.create_bucket(Bucket=bucket_name, ObjectLockEnabledForBucket=True)
|
||||||
|
|
||||||
|
put_obj_1 = s3.put_object(Bucket=bucket_name, Body=b"test", Key=key_name)
|
||||||
|
version_id_1 = put_obj_1["VersionId"]
|
||||||
|
put_obj_2 = s3.put_object(Bucket=bucket_name, Body=b"test", Key=key_name)
|
||||||
|
version_id_2 = put_obj_2["VersionId"]
|
||||||
|
|
||||||
|
until = datetime.datetime.utcnow() + datetime.timedelta(seconds=seconds_lock)
|
||||||
|
|
||||||
|
s3.put_object_retention(
|
||||||
|
Bucket=bucket_name,
|
||||||
|
Key=key_name,
|
||||||
|
VersionId=version_id_1,
|
||||||
|
Retention={"Mode": "COMPLIANCE", "RetainUntilDate": until},
|
||||||
|
)
|
||||||
|
|
||||||
|
# assert that you can delete the locked version 1 of the object
|
||||||
|
deleted = False
|
||||||
|
try:
|
||||||
|
s3.delete_object(Bucket=bucket_name, Key=key_name, VersionId=version_id_1)
|
||||||
|
deleted = True
|
||||||
|
except ClientError as e:
|
||||||
|
e.response["Error"]["Code"].should.equal("AccessDenied")
|
||||||
|
|
||||||
|
deleted.should.equal(False)
|
||||||
|
|
||||||
|
# assert that you can delete the version 2 of the object, not concerned by the lock
|
||||||
|
s3.delete_object(Bucket=bucket_name, Key=key_name, VersionId=version_id_2)
|
||||||
|
with pytest.raises(ClientError) as e:
|
||||||
|
s3.head_object(Bucket=bucket_name, Key=key_name, VersionId=version_id_2)
|
||||||
|
assert e.value.response["Error"]["Code"] == "404"
|
||||||
|
|
||||||
|
# cleaning
|
||||||
|
time.sleep(seconds_lock)
|
||||||
|
s3.delete_object(Bucket=bucket_name, Key=key_name, VersionId=version_id_1)
|
||||||
|
s3.delete_bucket(Bucket=bucket_name)
|
||||||
|
Loading…
Reference in New Issue
Block a user