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