Merge "Fix response of DELETE Multiple Objects without bucket write permission"
This commit is contained in:
@@ -17,7 +17,7 @@ from swift3.controllers.base import Controller, bucket_operation
|
|||||||
from swift3.etree import Element, SubElement, fromstring, tostring, \
|
from swift3.etree import Element, SubElement, fromstring, tostring, \
|
||||||
XMLSyntaxError, DocumentInvalid
|
XMLSyntaxError, DocumentInvalid
|
||||||
from swift3.response import HTTPOk, S3NotImplemented, NoSuchKey, \
|
from swift3.response import HTTPOk, S3NotImplemented, NoSuchKey, \
|
||||||
ErrorResponse, MalformedXML, UserKeyMustBeSpecified
|
ErrorResponse, MalformedXML, UserKeyMustBeSpecified, AccessDenied
|
||||||
from swift3.cfg import CONF
|
from swift3.cfg import CONF
|
||||||
from swift3.utils import LOGGER
|
from swift3.utils import LOGGER
|
||||||
|
|
||||||
@@ -29,6 +29,19 @@ class MultiObjectDeleteController(Controller):
|
|||||||
Handles Delete Multiple Objects, which is logged as a MULTI_OBJECT_DELETE
|
Handles Delete Multiple Objects, which is logged as a MULTI_OBJECT_DELETE
|
||||||
operation in the S3 server log.
|
operation in the S3 server log.
|
||||||
"""
|
"""
|
||||||
|
def _gen_error_body(self, error, elem, delete_list):
|
||||||
|
for key, version in delete_list:
|
||||||
|
if version is not None:
|
||||||
|
# TODO: delete the specific version of the object
|
||||||
|
raise S3NotImplemented()
|
||||||
|
|
||||||
|
error_elem = SubElement(elem, 'Error')
|
||||||
|
SubElement(error_elem, 'Key').text = key
|
||||||
|
SubElement(error_elem, 'Code').text = error.__class__.__name__
|
||||||
|
SubElement(error_elem, 'Message').text = error._msg
|
||||||
|
|
||||||
|
return tostring(elem)
|
||||||
|
|
||||||
@bucket_operation
|
@bucket_operation
|
||||||
def POST(self, req):
|
def POST(self, req):
|
||||||
"""
|
"""
|
||||||
@@ -45,9 +58,6 @@ class MultiObjectDeleteController(Controller):
|
|||||||
|
|
||||||
yield key, version
|
yield key, version
|
||||||
|
|
||||||
# check bucket existence
|
|
||||||
req.get_response(self.app, 'HEAD')
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
xml = req.xml(MAX_MULTI_DELETE_BODY_SIZE, check_md5=True)
|
xml = req.xml(MAX_MULTI_DELETE_BODY_SIZE, check_md5=True)
|
||||||
elem = fromstring(xml, 'Delete')
|
elem = fromstring(xml, 'Delete')
|
||||||
@@ -71,6 +81,13 @@ class MultiObjectDeleteController(Controller):
|
|||||||
|
|
||||||
elem = Element('DeleteResult')
|
elem = Element('DeleteResult')
|
||||||
|
|
||||||
|
# check bucket existence
|
||||||
|
try:
|
||||||
|
req.get_response(self.app, 'HEAD')
|
||||||
|
except AccessDenied as error:
|
||||||
|
body = self._gen_error_body(error, elem, delete_list)
|
||||||
|
return HTTPOk(body=body)
|
||||||
|
|
||||||
for key, version in delete_list:
|
for key, version in delete_list:
|
||||||
if version is not None:
|
if version is not None:
|
||||||
# TODO: delete the specific version of the object
|
# TODO: delete the specific version of the object
|
||||||
|
|||||||
@@ -173,13 +173,16 @@ class TestSwift3MultiDelete(Swift3TestCase):
|
|||||||
self.assertEquals(self._get_error_code(body), 'MalformedXML')
|
self.assertEquals(self._get_error_code(body), 'MalformedXML')
|
||||||
|
|
||||||
def _test_object_multi_DELETE(self, account):
|
def _test_object_multi_DELETE(self, account):
|
||||||
self.swift.register('DELETE', '/v1/AUTH_test/bucket/Key1',
|
self.keys = ['Key1', 'Key2']
|
||||||
swob.HTTPNoContent, {}, None)
|
self.swift.register(
|
||||||
self.swift.register('DELETE', '/v1/AUTH_test/bucket/Key2',
|
'DELETE', '/v1/AUTH_test/bucket/%s' % self.keys[0],
|
||||||
swob.HTTPNotFound, {}, None)
|
swob.HTTPNoContent, {}, None)
|
||||||
|
self.swift.register(
|
||||||
|
'DELETE', '/v1/AUTH_test/bucket/%s' % self.keys[1],
|
||||||
|
swob.HTTPNotFound, {}, None)
|
||||||
|
|
||||||
elem = Element('Delete')
|
elem = Element('Delete')
|
||||||
for key in ['Key1', 'Key2']:
|
for key in self.keys:
|
||||||
obj = SubElement(elem, 'Object')
|
obj = SubElement(elem, 'Object')
|
||||||
SubElement(obj, 'Key').text = key
|
SubElement(obj, 'Key').text = key
|
||||||
body = tostring(elem, use_s3ns=False)
|
body = tostring(elem, use_s3ns=False)
|
||||||
@@ -198,14 +201,21 @@ class TestSwift3MultiDelete(Swift3TestCase):
|
|||||||
@s3acl(s3acl_only=True)
|
@s3acl(s3acl_only=True)
|
||||||
def test_object_multi_DELETE_without_permission(self):
|
def test_object_multi_DELETE_without_permission(self):
|
||||||
status, headers, body = self._test_object_multi_DELETE('test:other')
|
status, headers, body = self._test_object_multi_DELETE('test:other')
|
||||||
self.assertEquals(self._get_error_code(body), 'AccessDenied')
|
self.assertEquals(status.split()[0], '200')
|
||||||
|
elem = fromstring(body)
|
||||||
|
errors = elem.findall('Error')
|
||||||
|
self.assertEquals(len(errors), len(self.keys))
|
||||||
|
for e in errors:
|
||||||
|
self.assertTrue(e.find('Key').text in self.keys)
|
||||||
|
self.assertEquals(e.find('Code').text, 'AccessDenied')
|
||||||
|
self.assertEquals(e.find('Message').text, 'Access Denied.')
|
||||||
|
|
||||||
@s3acl(s3acl_only=True)
|
@s3acl(s3acl_only=True)
|
||||||
def test_object_multi_DELETE_with_write_permission(self):
|
def test_object_multi_DELETE_with_write_permission(self):
|
||||||
status, headers, body = self._test_object_multi_DELETE('test:write')
|
status, headers, body = self._test_object_multi_DELETE('test:write')
|
||||||
self.assertEquals(status.split()[0], '200')
|
self.assertEquals(status.split()[0], '200')
|
||||||
elem = fromstring(body)
|
elem = fromstring(body)
|
||||||
self.assertEquals(len(elem.findall('Deleted')), 2)
|
self.assertEquals(len(elem.findall('Deleted')), len(self.keys))
|
||||||
|
|
||||||
@s3acl(s3acl_only=True)
|
@s3acl(s3acl_only=True)
|
||||||
def test_object_multi_DELETE_with_fullcontrol_permission(self):
|
def test_object_multi_DELETE_with_fullcontrol_permission(self):
|
||||||
@@ -213,7 +223,7 @@ class TestSwift3MultiDelete(Swift3TestCase):
|
|||||||
self._test_object_multi_DELETE('test:full_control')
|
self._test_object_multi_DELETE('test:full_control')
|
||||||
self.assertEquals(status.split()[0], '200')
|
self.assertEquals(status.split()[0], '200')
|
||||||
elem = fromstring(body)
|
elem = fromstring(body)
|
||||||
self.assertEquals(len(elem.findall('Deleted')), 2)
|
self.assertEquals(len(elem.findall('Deleted')), len(self.keys))
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|||||||
Reference in New Issue
Block a user