acl: use S3 ACL for object and bucket APIs

Change-Id: I8ec79fe4cea1370cbf433a5c20f5e4cdf5b0d298
This commit is contained in:
Masaki Tsukuda
2014-11-27 17:05:58 +09:00
parent 12efe66170
commit fef3260b53
7 changed files with 696 additions and 55 deletions

View File

@@ -24,6 +24,7 @@ from swift3.etree import Element, SubElement, tostring, fromstring, \
from swift3.response import HTTPOk, S3NotImplemented, InvalidArgument, \
MalformedXML, InvalidLocationConstraint
from swift3.cfg import CONF
from swift3.subresource import ACL, Owner
from swift3.utils import LOGGER
MAX_PUT_BUCKET_BODY_SIZE = 10240
@@ -116,9 +117,6 @@ class BucketController(Controller):
"""
Handle PUT Bucket request
"""
if 'HTTP_X_AMZ_ACL' in req.environ:
handle_acl_header(req)
xml = req.xml(MAX_PUT_BUCKET_BODY_SIZE)
if xml:
# check location
@@ -135,7 +133,26 @@ class BucketController(Controller):
# Swift3 cannot support multiple reagions now.
raise InvalidLocationConstraint()
resp = req.get_response(self.app)
if CONF.s3_acl:
req_acl = ACL.from_headers(req.headers,
Owner(req.user_id, req.user_id))
# To avoid overwriting the existing bucket's ACL, we send PUT
# request first before setting the ACL to make sure that the target
# container does not exist.
resp = req.get_response(self.app)
# update metadata
req.bucket_acl = req_acl
# FIXME If this request is failed, there is a possibility that the
# bucket which has no ACL is left.
req.get_response(self.app, 'POST')
else:
if 'HTTP_X_AMZ_ACL' in req.environ:
handle_acl_header(req)
resp = req.get_response(self.app)
resp.status = HTTP_OK
resp.location = '/' + req.container_name

View File

@@ -14,10 +14,13 @@
# limitations under the License.
from swift.common.http import HTTP_OK
from swift.common.utils import split_path
from swift3.controllers.base import Controller
from swift3.response import AccessDenied, HTTPOk
from swift3.response import AccessDenied, HTTPOk, NoSuchKey
from swift3.etree import Element, SubElement, tostring
from swift3.subresource import ACL, Owner
from swift3.cfg import CONF
class ObjectController(Controller):
@@ -26,6 +29,7 @@ class ObjectController(Controller):
"""
def GETorHEAD(self, req):
resp = req.get_response(self.app)
if req.method == 'HEAD':
resp.app_iter = None
@@ -53,6 +57,30 @@ class ObjectController(Controller):
"""
Handle PUT Object and PUT Object (Copy) request
"""
if CONF.s3_acl:
if 'HTTP_X_AMZ_COPY_SOURCE' in req.environ:
src_path = req.environ['HTTP_X_AMZ_COPY_SOURCE']
src_path = src_path if src_path[0] == '/' else ('/' + src_path)
src_bucket, src_obj = split_path(src_path, 0, 2, True)
req.get_response(self.app, 'HEAD', src_bucket, src_obj,
permission='READ')
b_resp = req.get_response(self.app, 'HEAD', obj='')
# To avoid overwriting the existing object by unauthorized user,
# we send HEAD request first before writing the object to make
# sure that the target object does not exist or the user that sent
# the PUT request have write permission.
try:
req.get_response(self.app, 'HEAD')
except NoSuchKey:
pass
req_acl = ACL.from_headers(req.headers,
b_resp.bucket_acl.owner,
Owner(req.user_id, req.user_id))
req.object_acl = req_acl
resp = req.get_response(self.app)
if 'HTTP_X_COPY_FROM' in req.environ:

View File

@@ -27,7 +27,8 @@ def get_acl(headers, body, bucket_owner, object_owner=None):
"""
Get ACL instance from S3 (e.g. x-amz-grant) headers or S3 acl xml body.
"""
acl = ACL.from_headers(headers, bucket_owner, object_owner)
acl = ACL.from_headers(headers, bucket_owner, object_owner,
as_private=False)
if acl is None:
# Get acl from request body if possible.
@@ -65,13 +66,9 @@ class AclController(Controller):
"""
Handles GET Bucket acl and GET Object acl.
"""
resp = req.get_response(self.app, 'HEAD')
if req.is_object_request:
acl = resp.object_acl
else:
acl = resp.bucket_acl
acl.check_permission(req.user_id, 'READ_ACP')
resp = req.get_response(self.app, 'HEAD', permission='READ_ACP')
acl = getattr(resp, '%s_acl' %
('object' if req.is_object_request else 'bucket'))
resp = HTTPOk()
resp.body = tostring(acl.elem())
@@ -83,16 +80,15 @@ class AclController(Controller):
Handles PUT Bucket acl and PUT Object acl.
"""
if req.is_object_request:
b_resp = req.get_response(self.app, 'HEAD', obj='')
o_resp = req.get_response(self.app, 'HEAD')
b_resp = req.get_response(self.app, 'HEAD', obj='',
skip_check=True)
o_resp = req.get_response(self.app, 'HEAD', permission='WRITE_ACP')
req_acl = get_acl(req.headers, req.xml(ACL.max_xml_length),
b_resp.bucket_acl.owner,
o_resp.object_acl.owner)
# Don't change the owner of the resource by PUT acl request.
o_resp.object_acl.check_owner(req_acl.owner.id)
o_resp.object_acl.check_permission(req.user_id, 'WRITE_ACP')
for g in req_acl.grants:
LOGGER.debug('Grant %s %s permission on the object /%s/%s' %
@@ -107,22 +103,22 @@ class AclController(Controller):
# So headers['X-Copy-From'] for copy request is added here.
headers['X-Copy-From'] = quote(src_path)
headers['Content-Length'] = 0
req.get_response(self.app, 'PUT', headers=headers)
req.get_response(self.app, 'PUT', headers=headers,
skip_check=True)
else:
resp = req.get_response(self.app, 'HEAD')
resp = req.get_response(self.app, 'HEAD', permission='WRITE_ACP')
req_acl = get_acl(req.headers, req.xml(ACL.max_xml_length),
resp.bucket_acl.owner)
# Don't change the owner of the resource by PUT acl request.
resp.bucket_acl.check_owner(req_acl.owner.id)
resp.bucket_acl.check_permission(req.user_id, 'WRITE_ACP')
for g in req_acl.grants:
LOGGER.debug('Grant %s %s permission on the bucket /%s' %
(g.grantee, g.permission, req.container_name))
req.bucket_acl = req_acl
req.get_response(self.app, 'POST')
req.get_response(self.app, 'POST', skip_check=True)
return HTTPOk()