Merge "Support x-amz-copy-source-if-* for Upload Part Copy"
This commit is contained in:
@@ -19,7 +19,6 @@ from swift3.response import MissingSecurityHeader, \
|
||||
from swift3.etree import fromstring, XMLSyntaxError, DocumentInvalid
|
||||
from swift3.utils import LOGGER, MULTIUPLOAD_SUFFIX
|
||||
|
||||
from swift.common.utils import split_path
|
||||
|
||||
"""
|
||||
Acl Handlers:
|
||||
@@ -102,14 +101,6 @@ class BaseAclHandler(object):
|
||||
self.user_id = self.req.user_id
|
||||
self.headers = self.req.headers if headers is None else headers
|
||||
|
||||
def _check_copy_source(self, app):
|
||||
if 'X-Amz-Copy-Source' in self.req.headers:
|
||||
src_path = self.req.headers['X-Amz-Copy-Source']
|
||||
src_path = src_path if src_path.startswith('/') else \
|
||||
('/' + src_path)
|
||||
src_bucket, src_obj = split_path(src_path, 0, 2, True)
|
||||
self._handle_acl(app, 'HEAD', src_bucket, src_obj, 'READ')
|
||||
|
||||
def handle_acl(self, app, method):
|
||||
method = method or self.method
|
||||
if hasattr(self, method):
|
||||
@@ -292,9 +283,23 @@ class PartAclHandler(MultiUploadAclHandler):
|
||||
"""
|
||||
PartAclHandler: Handler for PartController
|
||||
"""
|
||||
def PUT(self, app):
|
||||
# Upload Part
|
||||
self._check_copy_source(app)
|
||||
def __init__(self, req, container, obj, headers):
|
||||
# pylint: disable-msg=E1003
|
||||
super(MultiUploadAclHandler, self).__init__(req, container, obj,
|
||||
headers)
|
||||
self.check_copy_src = False
|
||||
if self.container.endswith(MULTIUPLOAD_SUFFIX):
|
||||
self.container = self.container[:-len(MULTIUPLOAD_SUFFIX)]
|
||||
else:
|
||||
self.check_copy_src = True
|
||||
|
||||
def HEAD(self, app):
|
||||
if self.check_copy_src:
|
||||
# For check_copy_source
|
||||
return self._handle_acl(app, 'HEAD', self.container, self.obj)
|
||||
else:
|
||||
# For _check_upload_info
|
||||
self._handle_acl(app, 'HEAD', self.container, '')
|
||||
|
||||
|
||||
class UploadsAclHandler(MultiUploadAclHandler):
|
||||
@@ -359,7 +364,7 @@ ACL_MAP = {
|
||||
# GET Object
|
||||
('GET', 'GET', 'object'):
|
||||
{'Permission': 'READ'},
|
||||
# PUT Object Copy
|
||||
# PUT Object Copy, Upload Part Copy
|
||||
('PUT', 'HEAD', 'object'):
|
||||
{'Permission': 'READ'},
|
||||
# Initiate Multipart Upload
|
||||
|
||||
@@ -108,6 +108,7 @@ class PartController(Controller):
|
||||
req.object_name = '%s/%s/%d' % (req.object_name, upload_id,
|
||||
part_number)
|
||||
|
||||
req.check_copy_source(self.app)
|
||||
resp = req.get_response(self.app)
|
||||
|
||||
# TODO: set xml body for copy requests.
|
||||
|
||||
@@ -658,19 +658,22 @@ class TestSwift3MultiUpload(Swift3TestCase):
|
||||
self.assertEquals(status.split()[0], '200')
|
||||
|
||||
def _test_copy_for_s3acl(self, account, src_permission=None,
|
||||
src_path='/src_bucket/src_obj'):
|
||||
src_path='/src_bucket/src_obj',
|
||||
head_resp=swob.HTTPOk, put_header={}):
|
||||
owner = 'test:tester'
|
||||
grants = [Grant(User(account), src_permission)] \
|
||||
if src_permission else [Grant(User(owner), 'FULL_CONTROL')]
|
||||
src_o_headers = encode_acl('object', ACL(Owner(owner, owner), grants))
|
||||
self.swift.register('HEAD', '/v1/AUTH_test/src_bucket/src_obj',
|
||||
swob.HTTPOk, src_o_headers, None)
|
||||
head_resp, src_o_headers, None)
|
||||
|
||||
put_headers = {'Authorization': 'AWS %s:hmac' % account,
|
||||
'X-Amz-Copy-Source': src_path}
|
||||
put_headers.update(put_header)
|
||||
req = Request.blank(
|
||||
'/bucket/object?partNumber=1&uploadId=X',
|
||||
environ={'REQUEST_METHOD': 'PUT'},
|
||||
headers={'Authorization': 'AWS %s:hmac' % account,
|
||||
'X-Amz-Copy-Source': src_path})
|
||||
headers=put_headers)
|
||||
return self.call_swift3(req)
|
||||
|
||||
@s3acl(s3acl_only=True)
|
||||
@@ -721,5 +724,143 @@ class TestSwift3MultiUpload(Swift3TestCase):
|
||||
self._test_copy_for_s3acl('test:write', 'WRITE', '/bucket/')
|
||||
self.assertEquals(status.split()[0], '400')
|
||||
|
||||
@s3acl
|
||||
def test_upload_part_copy_headers_error(self):
|
||||
account = 'test:tester'
|
||||
etag = '7dfa07a8e59ddbcd1dc84d4c4f82aea1'
|
||||
last_modified_since = 'Fri, 01 Apr 2014 12:00:00 GMT'
|
||||
|
||||
header = {'X-Amz-Copy-Source-If-Match': etag}
|
||||
status, header, body = \
|
||||
self._test_copy_for_s3acl(account,
|
||||
head_resp=swob.HTTPPreconditionFailed,
|
||||
put_header=header)
|
||||
self.assertEquals(self._get_error_code(body), 'PreconditionFailed')
|
||||
|
||||
header = {'X-Amz-Copy-Source-If-None-Match': etag}
|
||||
status, header, body = \
|
||||
self._test_copy_for_s3acl(account,
|
||||
head_resp=swob.HTTPNotModified,
|
||||
put_header=header)
|
||||
self.assertEquals(self._get_error_code(body), 'PreconditionFailed')
|
||||
|
||||
header = {'X-Amz-Copy-Source-If-Modified-Since': last_modified_since}
|
||||
status, header, body = \
|
||||
self._test_copy_for_s3acl(account,
|
||||
head_resp=swob.HTTPNotModified,
|
||||
put_header=header)
|
||||
self.assertEquals(self._get_error_code(body), 'PreconditionFailed')
|
||||
|
||||
header = \
|
||||
{'X-Amz-Copy-Source-If-Unmodified-Since': last_modified_since}
|
||||
status, header, body = \
|
||||
self._test_copy_for_s3acl(account,
|
||||
head_resp=swob.HTTPPreconditionFailed,
|
||||
put_header=header)
|
||||
self.assertEquals(self._get_error_code(body), 'PreconditionFailed')
|
||||
|
||||
def test_upload_part_copy_headers_with_match(self):
|
||||
account = 'test:tester'
|
||||
etag = '7dfa07a8e59ddbcd1dc84d4c4f82aea1'
|
||||
last_modified_since = 'Fri, 01 Apr 2014 12:00:00 GMT'
|
||||
|
||||
header = {'X-Amz-Copy-Source-If-Match': etag,
|
||||
'X-Amz-Copy-Source-If-Modified-Since': last_modified_since}
|
||||
status, header, body = \
|
||||
self._test_copy_for_s3acl(account, put_header=header)
|
||||
|
||||
self.assertEquals(status.split()[0], '200')
|
||||
|
||||
self.assertEquals(len(self.swift.calls_with_headers), 3)
|
||||
_, _, headers = self.swift.calls_with_headers[-2]
|
||||
self.assertEquals(headers['If-Match'], etag)
|
||||
self.assertEquals(headers['If-Modified-Since'], last_modified_since)
|
||||
_, _, headers = self.swift.calls_with_headers[-1]
|
||||
self.assertTrue(headers.get('If-Match') is None)
|
||||
self.assertTrue(headers.get('If-Modified-Since') is None)
|
||||
_, _, headers = self.swift.calls_with_headers[0]
|
||||
self.assertTrue(headers.get('If-Match') is None)
|
||||
self.assertTrue(headers.get('If-Modified-Since') is None)
|
||||
|
||||
@s3acl(s3acl_only=True)
|
||||
def test_upload_part_copy_headers_with_match_and_s3acl(self):
|
||||
account = 'test:tester'
|
||||
etag = '7dfa07a8e59ddbcd1dc84d4c4f82aea1'
|
||||
last_modified_since = 'Fri, 01 Apr 2014 12:00:00 GMT'
|
||||
|
||||
header = {'X-Amz-Copy-Source-If-Match': etag,
|
||||
'X-Amz-Copy-Source-If-Modified-Since': last_modified_since}
|
||||
status, header, body = \
|
||||
self._test_copy_for_s3acl(account, put_header=header)
|
||||
|
||||
self.assertEquals(status.split()[0], '200')
|
||||
self.assertEquals(len(self.swift.calls_with_headers), 4)
|
||||
# Before the check of the copy source in the case of s3acl is valid,
|
||||
# Swift3 check the bucket write permissions and the object existence
|
||||
# of the destination.
|
||||
_, _, headers = self.swift.calls_with_headers[-3]
|
||||
self.assertTrue(headers.get('If-Match') is None)
|
||||
self.assertTrue(headers.get('If-Modified-Since') is None)
|
||||
_, _, headers = self.swift.calls_with_headers[-2]
|
||||
self.assertEquals(headers['If-Match'], etag)
|
||||
self.assertEquals(headers['If-Modified-Since'], last_modified_since)
|
||||
_, _, headers = self.swift.calls_with_headers[-1]
|
||||
self.assertTrue(headers.get('If-Match') is None)
|
||||
self.assertTrue(headers.get('If-Modified-Since') is None)
|
||||
_, _, headers = self.swift.calls_with_headers[0]
|
||||
self.assertTrue(headers.get('If-Match') is None)
|
||||
self.assertTrue(headers.get('If-Modified-Since') is None)
|
||||
|
||||
def test_upload_part_copy_headers_with_not_match(self):
|
||||
account = 'test:tester'
|
||||
etag = '7dfa07a8e59ddbcd1dc84d4c4f82aea1'
|
||||
last_modified_since = 'Fri, 01 Apr 2014 12:00:00 GMT'
|
||||
|
||||
header = {'X-Amz-Copy-Source-If-None-Match': etag,
|
||||
'X-Amz-Copy-Source-If-Unmodified-Since': last_modified_since}
|
||||
status, header, body = \
|
||||
self._test_copy_for_s3acl(account, put_header=header)
|
||||
|
||||
self.assertEquals(status.split()[0], '200')
|
||||
self.assertEquals(len(self.swift.calls_with_headers), 3)
|
||||
_, _, headers = self.swift.calls_with_headers[-2]
|
||||
self.assertEquals(headers['If-None-Match'], etag)
|
||||
self.assertEquals(headers['If-Unmodified-Since'], last_modified_since)
|
||||
_, _, headers = self.swift.calls_with_headers[-1]
|
||||
self.assertTrue(headers.get('If-None-Match') is None)
|
||||
self.assertTrue(headers.get('If-Unmodified-Since') is None)
|
||||
_, _, headers = self.swift.calls_with_headers[0]
|
||||
self.assertTrue(headers.get('If-None-Match') is None)
|
||||
self.assertTrue(headers.get('If-Unmodified-Since') is None)
|
||||
|
||||
@s3acl(s3acl_only=True)
|
||||
def test_upload_part_copy_headers_with_not_match_and_s3acl(self):
|
||||
account = 'test:tester'
|
||||
etag = '7dfa07a8e59ddbcd1dc84d4c4f82aea1'
|
||||
last_modified_since = 'Fri, 01 Apr 2014 12:00:00 GMT'
|
||||
|
||||
header = {'X-Amz-Copy-Source-If-None-Match': etag,
|
||||
'X-Amz-Copy-Source-If-Unmodified-Since': last_modified_since}
|
||||
status, header, body = \
|
||||
self._test_copy_for_s3acl(account, put_header=header)
|
||||
|
||||
self.assertEquals(status.split()[0], '200')
|
||||
self.assertEquals(len(self.swift.calls_with_headers), 4)
|
||||
# Before the check of the copy source in the case of s3acl is valid,
|
||||
# Swift3 check the bucket write permissions and the object existence
|
||||
# of the destination.
|
||||
_, _, headers = self.swift.calls_with_headers[-3]
|
||||
self.assertTrue(headers.get('If-Match') is None)
|
||||
self.assertTrue(headers.get('If-Modified-Since') is None)
|
||||
_, _, headers = self.swift.calls_with_headers[-2]
|
||||
self.assertEquals(headers['If-None-Match'], etag)
|
||||
self.assertEquals(headers['If-Unmodified-Since'], last_modified_since)
|
||||
self.assertTrue(headers.get('If-Match') is None)
|
||||
self.assertTrue(headers.get('If-Modified-Since') is None)
|
||||
_, _, headers = self.swift.calls_with_headers[-1]
|
||||
self.assertTrue(headers.get('If-None-Match') is None)
|
||||
self.assertTrue(headers.get('If-Unmodified-Since') is None)
|
||||
_, _, headers = self.swift.calls_with_headers[0]
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
Reference in New Issue
Block a user