fixed object locking mechanism in s3 (#4367)

This commit is contained in:
Macwan Nevil 2021-10-07 01:35:26 +05:30 committed by GitHub
parent 71b4b47523
commit a17d17ec6e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 55 additions and 27 deletions

View File

@ -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:

View File

@ -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>
""" """

View File

@ -109,4 +109,5 @@ TestAccAWSEc2CarrierGateway
TestAccDataSourceAwsNetworkInterface_ TestAccDataSourceAwsNetworkInterface_
TestAccAWSNatGateway TestAccAWSNatGateway
TestAccAWSRouteTable_ TestAccAWSRouteTable_
TestAccAWSRouteTableAssociation_ TestAccAWSRouteTableAssociation_
TestAccAWSS3Bucket_forceDestroyWithObjectLockEnabled