From c38731ecbbfbbfc66fe41c0cf14d6d86ee17ebd2 Mon Sep 17 00:00:00 2001 From: Steve Pulec Date: Wed, 11 Nov 2015 20:26:29 -0500 Subject: [PATCH] Add ACL support for S3 buckets. --- moto/s3/models.py | 13 +++++++++++++ moto/s3/responses.py | 17 +++++++++++++---- tests/test_s3/test_s3.py | 25 +++++++++++++++++++++++++ 3 files changed, 51 insertions(+), 4 deletions(-) diff --git a/moto/s3/models.py b/moto/s3/models.py index 0375775cf..9e023f68d 100644 --- a/moto/s3/models.py +++ b/moto/s3/models.py @@ -245,6 +245,7 @@ class FakeBucket(object): self.rules = [] self.policy = None self.website_configuration = None + self.acl = get_canned_acl('private') @property def location(self): @@ -284,6 +285,9 @@ class FakeBucket(object): raise NotImplementedError('"Fn::GetAtt" : [ "{0}" , "WebsiteURL" ]"') raise UnformattedGetAttTemplateException() + def set_acl(self, acl): + self.acl = acl + class S3Backend(BaseBackend): @@ -484,4 +488,13 @@ class S3Backend(BaseBackend): if acl is not None: key.set_acl(acl) + def set_bucket_acl(self, bucket_name, acl): + bucket = self.get_bucket(bucket_name) + bucket.set_acl(acl) + + def get_bucket_acl(self, bucket_name): + bucket = self.get_bucket(bucket_name) + return bucket.acl + + s3_backend = S3Backend() diff --git a/moto/s3/responses.py b/moto/s3/responses.py index ff174854f..27a6d4536 100644 --- a/moto/s3/responses.py +++ b/moto/s3/responses.py @@ -80,7 +80,7 @@ class ResponseObject(_TemplateEnvironmentMixin): elif method == 'GET': return self._bucket_response_get(bucket_name, querystring, headers) elif method == 'PUT': - return self._bucket_response_put(body, region_name, bucket_name, querystring, headers) + return self._bucket_response_put(request, body, region_name, bucket_name, querystring, headers) elif method == 'DELETE': return self._bucket_response_delete(body, bucket_name, querystring, headers) elif method == 'POST': @@ -128,6 +128,10 @@ class ResponseObject(_TemplateEnvironmentMixin): elif 'website' in querystring: website_configuration = self.backend.get_bucket_website_configuration(bucket_name) return website_configuration + elif 'acl' in querystring: + bucket = self.backend.get_bucket(bucket_name) + template = self.response_template(S3_OBJECT_ACL_RESPONSE) + return template.render(obj=bucket) elif 'versions' in querystring: delimiter = querystring.get('delimiter', [None])[0] encoding_type = querystring.get('encoding-type', [None])[0] @@ -168,7 +172,7 @@ class ResponseObject(_TemplateEnvironmentMixin): result_folders=result_folders ) - def _bucket_response_put(self, body, region_name, bucket_name, querystring, headers): + def _bucket_response_put(self, request, body, region_name, bucket_name, querystring, headers): if 'versioning' in querystring: ver = re.search('([A-Za-z]+)', body) if ver: @@ -187,6 +191,11 @@ class ResponseObject(_TemplateEnvironmentMixin): elif 'policy' in querystring: self.backend.set_bucket_policy(bucket_name, body) return 'True' + elif 'acl' in querystring: + acl = self._acl_from_headers(request.headers) + # TODO: Support the XML-based ACL format + self.backend.set_bucket_acl(bucket_name, acl) + return "" elif 'website' in querystring: self.backend.set_bucket_website_configuration(bucket_name, body) return "" @@ -351,7 +360,7 @@ class ResponseObject(_TemplateEnvironmentMixin): bucket_name, key_name, version_id=version_id) if 'acl' in query: template = self.response_template(S3_OBJECT_ACL_RESPONSE) - return 200, headers, template.render(key=key) + return 200, headers, template.render(obj=key) if key: headers.update(key.metadata) @@ -696,7 +705,7 @@ S3_OBJECT_ACL_RESPONSE = """ webfile - {% for grant in key.acl.grants %} + {% for grant in obj.acl.grants %} {% for grantee in grant.grantees %}