Allow users to extract tars to containers with ACLs set

This fixes bug: https://bugs.launchpad.net/swift/+bug/1203182 (well
it should :) I don't have keystone installed but the same issue
existed with tempauth).

Change-Id: I6f4045f484b27c0153a9244e0dbf2641cbc9e84e
This commit is contained in:
David Goetz 2013-07-23 15:00:13 -07:00 committed by John Dickinson
parent 4beb674adc
commit 4893aacc67
2 changed files with 60 additions and 32 deletions

View File

@ -196,20 +196,31 @@ class Bulk(object):
def create_container(self, req, container_path):
"""
Makes a subrequest to create a new container.
Checks if the container exists and if not try to create it.
:params container_path: an unquoted path to a container to be created
:returns: None on success
:raises: CreateContainerError on creation error
:returns: True if created container, False if container exists
:raises: CreateContainerError when unable to create container
"""
new_env = req.environ.copy()
new_env['PATH_INFO'] = container_path
new_env['swift.source'] = 'EA'
create_cont_req = Request.blank(container_path, environ=new_env)
resp = create_cont_req.get_response(self.app)
if resp.status_int // 100 != 2:
raise CreateContainerError(
"Create Container Failed: " + container_path,
resp.status_int, resp.status)
new_env['REQUEST_METHOD'] = 'HEAD'
head_cont_req = Request.blank(container_path, environ=new_env)
resp = head_cont_req.get_response(self.app)
if resp.is_success:
return False
if resp.status_int == 404:
new_env = req.environ.copy()
new_env['PATH_INFO'] = container_path
new_env['swift.source'] = 'EA'
new_env['REQUEST_METHOD'] = 'PUT'
create_cont_req = Request.blank(container_path, environ=new_env)
resp = create_cont_req.get_response(self.app)
if resp.is_success:
return True
raise CreateContainerError(
"Create Container Failed: " + container_path,
resp.status_int, resp.status)
def get_objs_to_delete(self, req):
"""
@ -361,7 +372,7 @@ class Bulk(object):
failed_files = []
last_yield = time()
separator = ''
existing_containers = set()
containers_accessed = set()
try:
if not out_content_type:
raise HTTPNotAcceptable(request=req)
@ -382,6 +393,7 @@ class Bulk(object):
fileobj=req.body_file)
failed_response_type = HTTPBadRequest
req.environ['eventlet.minimum_write_chunk_size'] = 0
containers_created = 0
while True:
if last_yield + self.yield_frequency < time():
separator = '\r\n\r\n'
@ -414,30 +426,33 @@ class Bulk(object):
quote(obj_path[:MAX_PATH_LENGTH]),
HTTPRequestEntityTooLarge().status])
continue
if container not in existing_containers:
container_failure = None
if container not in containers_accessed:
cont_path = '/'.join(['', vrs, account, container])
try:
self.create_container(
req, '/'.join(['', vrs, account, container]))
existing_containers.add(container)
if self.create_container(req, cont_path):
containers_created += 1
if containers_created > self.max_containers:
raise HTTPBadRequest(
'More than %d containers to create '
'from tar.' % self.max_containers)
except CreateContainerError, err:
failed_files.append([
quote(obj_path[:MAX_PATH_LENGTH]),
err.status])
# the object PUT to this container still may
# succeed if acls are set
container_failure = [
quote(cont_path[:MAX_PATH_LENGTH]),
err.status]
if err.status_int == HTTP_UNAUTHORIZED:
raise HTTPUnauthorized(request=req)
continue
except ValueError:
failed_files.append([
quote(obj_path[:MAX_PATH_LENGTH]),
HTTPBadRequest().status])
continue
if len(existing_containers) > self.max_containers:
raise HTTPBadRequest(
'More than %d base level containers in tar.' %
self.max_containers)
tar_file = tar.extractfile(tar_info)
new_env = req.environ.copy()
new_env['REQUEST_METHOD'] = 'PUT'
new_env['wsgi.input'] = tar_file
new_env['PATH_INFO'] = destination
new_env['CONTENT_LENGTH'] = tar_info.size
@ -446,9 +461,12 @@ class Bulk(object):
'%s BulkExpand' % req.environ.get('HTTP_USER_AGENT')
create_obj_req = Request.blank(destination, new_env)
resp = create_obj_req.get_response(self.app)
if resp.status_int // 100 == 2:
containers_accessed.add(container)
if resp.is_success:
resp_dict['Number Files Created'] += 1
else:
if container_failure:
failed_files.append(container_failure)
if resp.status_int == HTTP_UNAUTHORIZED:
failed_files.append([
quote(obj_path[:MAX_PATH_LENGTH]),

View File

@ -37,8 +37,12 @@ class FakeApp(object):
if env['PATH_INFO'].startswith('/unauth/'):
return Response(status=401)(env, start_response)
if env['PATH_INFO'].startswith('/create_cont/'):
if env['REQUEST_METHOD'] == 'HEAD':
return Response(status='404 Not Found')(env, start_response)
return Response(status='201 Created')(env, start_response)
if env['PATH_INFO'].startswith('/create_cont_fail/'):
if env['REQUEST_METHOD'] == 'HEAD':
return Response(status='403 Forbidden')(env, start_response)
return Response(status='404 Not Found')(env, start_response)
if env['PATH_INFO'].startswith('/create_obj_unauth/'):
if env['PATH_INFO'].endswith('/cont'):
@ -48,6 +52,12 @@ class FakeApp(object):
if len(env['PATH_INFO']) > self.max_pathlen:
return Response(status='400 Bad Request')(env, start_response)
return Response(status='201 Created')(env, start_response)
if env['PATH_INFO'].startswith('/tar_works_cont_head_fail/'):
if env['REQUEST_METHOD'] == 'HEAD':
return Response(status='404 Not Found')(env, start_response)
if len(env['PATH_INFO']) > 100:
return Response(status='400 Bad Request')(env, start_response)
return Response(status='201 Created')(env, start_response)
if env['PATH_INFO'].startswith('/delete_works/'):
self.delete_paths.append(env['PATH_INFO'])
if len(env['PATH_INFO']) > self.max_pathlen:
@ -122,11 +132,13 @@ class TestUntar(unittest.TestCase):
req = Request.blank('/')
self.assertEquals(
self.bulk.create_container(req, '/create_cont/acc/cont'),
None)
True)
self.assertEquals(self.app.calls, 2)
self.assertRaises(
bulk.CreateContainerError,
self.bulk.create_container,
req, '/create_cont_fail/acc/cont')
self.assertEquals(self.app.calls, 3)
def test_extract_tar_works(self):
# On systems where $TMPDIR is long (like OS X), we need to do this
@ -295,9 +307,7 @@ class TestUntar(unittest.TestCase):
self.assertEquals(self.app.calls, 1)
resp_data = json.loads(resp_body)
self.assertEquals(resp_data['Response Status'], '401 Unauthorized')
self.assertEquals(
resp_data['Errors'],
[['base_fails1/sub_dir1/sub1_file1', '401 Unauthorized']])
self.assertEquals(resp_data['Errors'], [])
def test_extract_tar_fail_obj_401(self):
self.build_tar()
@ -392,16 +402,16 @@ class TestUntar(unittest.TestCase):
with patch.object(self.bulk, 'max_containers', 1):
self.app.calls = 0
body = open(os.path.join(self.testdir, 'tar_fails.tar')).read()
req = Request.blank('/tar_works/acc/', body=body,
req = Request.blank('/tar_works_cont_head_fail/acc/', body=body,
headers={'Accept': 'application/json'})
req.headers['transfer-encoding'] = 'chunked'
resp_body = self.handle_extract_and_iter(req, '')
self.assertEquals(self.app.calls, 3)
self.assertEquals(self.app.calls, 5)
resp_data = json.loads(resp_body)
self.assertEquals(resp_data['Response Status'], '400 Bad Request')
self.assertEquals(
resp_data['Response Body'],
'More than 1 base level containers in tar.')
'More than 1 containers to create from tar.')
def test_extract_tar_fail_create_cont(self):
dir_tree = [{'base_fails1': [
@ -416,8 +426,8 @@ class TestUntar(unittest.TestCase):
req.headers['transfer-encoding'] = 'chunked'
resp_body = self.handle_extract_and_iter(req, '')
resp_data = json.loads(resp_body)
self.assertEquals(self.app.calls, 4)
self.assertEquals(len(resp_data['Errors']), 4)
self.assertEquals(self.app.calls, 5)
self.assertEquals(len(resp_data['Errors']), 5)
def test_extract_tar_fail_create_cont_value_err(self):
self.build_tar()