fixed object locking mechanism in s3 (#4367)
This commit is contained in:
parent
71b4b47523
commit
a17d17ec6e
@ -1598,6 +1598,9 @@ class S3Backend(BaseBackend):
|
||||
def get_object_acl(self, key):
|
||||
return key.acl
|
||||
|
||||
def get_object_legal_hold(self, key):
|
||||
return key.lock_legal_status
|
||||
|
||||
def get_object_lock_configuration(self, bucket_name):
|
||||
bucket = self.get_bucket(bucket_name)
|
||||
return (
|
||||
@ -1631,7 +1634,7 @@ class S3Backend(BaseBackend):
|
||||
)
|
||||
|
||||
def put_object_lock_configuration(
|
||||
self, bucket_name, lock_enabled, mode, days, years
|
||||
self, bucket_name, lock_enabled, mode=None, days=None, years=None
|
||||
):
|
||||
bucket = self.get_bucket(bucket_name)
|
||||
|
||||
@ -1861,7 +1864,7 @@ class S3Backend(BaseBackend):
|
||||
key = self.get_object(bucket_name, key_name, version_id=version_id)
|
||||
self.tagger.delete_all_tags_for_resource(key.arn)
|
||||
|
||||
def delete_object(self, bucket_name, key_name, version_id=None):
|
||||
def delete_object(self, bucket_name, key_name, version_id=None, bypass=False):
|
||||
key_name = clean_key_name(key_name)
|
||||
bucket = self.get_bucket(bucket_name)
|
||||
|
||||
@ -1882,7 +1885,11 @@ class S3Backend(BaseBackend):
|
||||
for key in bucket.keys.getlist(key_name):
|
||||
if str(key.version_id) == str(version_id):
|
||||
|
||||
if hasattr(key, "is_locked") and key.is_locked:
|
||||
if (
|
||||
hasattr(key, "is_locked")
|
||||
and key.is_locked
|
||||
and not bypass
|
||||
):
|
||||
raise AccessDeniedByLock
|
||||
|
||||
if type(key) is FakeDeleteMarker:
|
||||
|
@ -692,12 +692,12 @@ class ResponseObject(_TemplateEnvironmentMixin, ActionAuthenticatorMixin):
|
||||
|
||||
self.backend.put_object_lock_configuration(
|
||||
bucket_name,
|
||||
config["enabled"],
|
||||
config["mode"],
|
||||
config["days"],
|
||||
config["years"],
|
||||
config.get("enabled"),
|
||||
config.get("mode"),
|
||||
config.get("days"),
|
||||
config.get("years"),
|
||||
)
|
||||
return ""
|
||||
return 200, {}, ""
|
||||
|
||||
if "versioning" in querystring:
|
||||
ver = re.search("<Status>([A-Za-z]+)</Status>", body.decode())
|
||||
@ -832,7 +832,10 @@ class ResponseObject(_TemplateEnvironmentMixin, ActionAuthenticatorMixin):
|
||||
bucket_name, self._acl_from_headers(request.headers)
|
||||
)
|
||||
|
||||
if request.headers.get("x-amz-bucket-object-lock-enabled", "") == "True":
|
||||
if (
|
||||
request.headers.get("x-amz-bucket-object-lock-enabled", "").lower()
|
||||
== "true"
|
||||
):
|
||||
new_bucket.object_lock_enabled = True
|
||||
new_bucket.versioning_status = "Enabled"
|
||||
|
||||
@ -1224,7 +1227,7 @@ class ResponseObject(_TemplateEnvironmentMixin, ActionAuthenticatorMixin):
|
||||
bucket_name, query, key_name, headers=request.headers
|
||||
)
|
||||
elif method == "DELETE":
|
||||
return self._key_response_delete(bucket_name, query, key_name)
|
||||
return self._key_response_delete(headers, bucket_name, query, key_name)
|
||||
elif method == "POST":
|
||||
return self._key_response_post(request, body, bucket_name, query, key_name)
|
||||
else:
|
||||
@ -1312,6 +1315,10 @@ class ResponseObject(_TemplateEnvironmentMixin, ActionAuthenticatorMixin):
|
||||
tags = self.backend.get_object_tagging(key)["Tags"]
|
||||
template = self.response_template(S3_OBJECT_TAGGING_RESPONSE)
|
||||
return 200, response_headers, template.render(tags=tags)
|
||||
if "legal-hold" in query:
|
||||
legal_hold = self.backend.get_object_legal_hold(key)
|
||||
template = self.response_template(S3_OBJECT_LEGAL_HOLD)
|
||||
return 200, response_headers, template.render(legal_hold=legal_hold)
|
||||
|
||||
response_headers.update(key.metadata)
|
||||
response_headers.update(key.response_dict)
|
||||
@ -1567,23 +1574,27 @@ class ResponseObject(_TemplateEnvironmentMixin, ActionAuthenticatorMixin):
|
||||
return 404, response_headers, ""
|
||||
|
||||
def _lock_config_from_xml(self, xml):
|
||||
response_dict = {"enabled": False, "mode": None, "days": None, "years": None}
|
||||
parsed_xml = xmltodict.parse(xml)
|
||||
enabled = (
|
||||
parsed_xml["ObjectLockConfiguration"]["ObjectLockEnabled"] == "Enabled"
|
||||
)
|
||||
response_dict["enabled"] = enabled
|
||||
|
||||
default_retention = parsed_xml["ObjectLockConfiguration"]["Rule"][
|
||||
"DefaultRetention"
|
||||
]
|
||||
|
||||
default_retention = parsed_xml.get("ObjectLockConfiguration").get("Rule")
|
||||
if default_retention:
|
||||
default_retention = default_retention.get("DefaultRetention")
|
||||
mode = default_retention["Mode"]
|
||||
days = int(default_retention["Days"]) if "Days" in default_retention else 0
|
||||
years = int(default_retention["Years"]) if "Years" in default_retention else 0
|
||||
days = int(default_retention.get("Days", 0))
|
||||
years = int(default_retention.get("Years", 0))
|
||||
|
||||
if days and years:
|
||||
raise MalformedXML
|
||||
response_dict["mode"] = mode
|
||||
response_dict["days"] = days
|
||||
response_dict["years"] = years
|
||||
|
||||
return {"enabled": enabled, "mode": mode, "days": days, "years": years}
|
||||
return response_dict
|
||||
|
||||
def _acl_from_xml(self, xml):
|
||||
parsed_xml = xmltodict.parse(xml)
|
||||
@ -1884,7 +1895,7 @@ class ResponseObject(_TemplateEnvironmentMixin, ActionAuthenticatorMixin):
|
||||
config = parsed_xml["AccelerateConfiguration"]
|
||||
return config["Status"]
|
||||
|
||||
def _key_response_delete(self, bucket_name, query, key_name):
|
||||
def _key_response_delete(self, headers, bucket_name, query, key_name):
|
||||
self._set_action("KEY", "DELETE", query)
|
||||
self._authenticate_and_authorize_s3_action()
|
||||
|
||||
@ -1899,8 +1910,9 @@ class ResponseObject(_TemplateEnvironmentMixin, ActionAuthenticatorMixin):
|
||||
)
|
||||
template = self.response_template(S3_DELETE_KEY_TAGGING_RESPONSE)
|
||||
return 204, {}, template.render(version_id=version_id)
|
||||
bypass = headers.get("X-Amz-Bypass-Governance-Retention")
|
||||
success, response_meta = self.backend.delete_object(
|
||||
bucket_name, key_name, version_id=version_id
|
||||
bucket_name, key_name, version_id=version_id, bypass=bypass
|
||||
)
|
||||
response_headers = {}
|
||||
if response_meta is not None:
|
||||
@ -2312,6 +2324,12 @@ S3_OBJECT_ACL_RESPONSE = """<?xml version="1.0" encoding="UTF-8"?>
|
||||
</AccessControlList>
|
||||
</AccessControlPolicy>"""
|
||||
|
||||
S3_OBJECT_LEGAL_HOLD = """<?xml version="1.0" encoding="UTF-8"?>
|
||||
<LegalHold>
|
||||
<Status>{{ legal_hold }}</Status>
|
||||
</LegalHold>
|
||||
"""
|
||||
|
||||
S3_OBJECT_TAGGING_RESPONSE = """\
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Tagging xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
|
||||
@ -2662,13 +2680,15 @@ S3_BUCKET_LOCK_CONFIGURATION = """
|
||||
{% else %}
|
||||
<ObjectLockEnabled>Disabled</ObjectLockEnabled>
|
||||
{% endif %}
|
||||
{% if mode %}
|
||||
<Rule>
|
||||
<DefaultRetention>
|
||||
<Mode>{{mode}}</Mode>
|
||||
<Days>{{days}}</Days>
|
||||
<Years>{{years}}</Years>
|
||||
</DefaultRetention>
|
||||
#</Rule>
|
||||
</Rule>
|
||||
{% endif %}
|
||||
</ObjectLockConfiguration>
|
||||
"""
|
||||
|
||||
|
@ -110,3 +110,4 @@ TestAccDataSourceAwsNetworkInterface_
|
||||
TestAccAWSNatGateway
|
||||
TestAccAWSRouteTable_
|
||||
TestAccAWSRouteTableAssociation_
|
||||
TestAccAWSS3Bucket_forceDestroyWithObjectLockEnabled
|
Loading…
Reference in New Issue
Block a user