diff --git a/etc/proxy-server.conf-sample b/etc/proxy-server.conf-sample index 636da064..8720c75d 100644 --- a/etc/proxy-server.conf-sample +++ b/etc/proxy-server.conf-sample @@ -33,6 +33,10 @@ use = egg:swift3#swift3 # response. # max_bucket_listing = 1000 # +# Set the maximum number of objects we can delete with the Multi-Object Delete +# operation. +# max_multi_delete_objects = 1000 +# # Specify a host name of your Swift cluster. This enables virtual-hosted style # requests. # storage_domain = diff --git a/swift3/cfg.py b/swift3/cfg.py index 09fa8a95..bcce2f31 100644 --- a/swift3/cfg.py +++ b/swift3/cfg.py @@ -53,5 +53,6 @@ class Config(dict): CONF = Config({ 'location': 'US', 'max_bucket_listing': 1000, + 'max_multi_delete_objects': 1000, 'storage_domain': '', }) diff --git a/swift3/controllers/multi_delete.py b/swift3/controllers/multi_delete.py index 3f6428de..cd052337 100644 --- a/swift3/controllers/multi_delete.py +++ b/swift3/controllers/multi_delete.py @@ -18,6 +18,8 @@ from swift3.etree import Element, SubElement, fromstring, tostring, \ XMLSyntaxError, DocumentInvalid from swift3.response import HTTPOk, S3NotImplemented, NoSuchKey, \ ErrorResponse, MalformedXML +from swift3.cfg import CONF +from swift3.utils import LOGGER MAX_MULTI_DELETE_BODY_SIZE = 61365 @@ -33,10 +35,7 @@ class MultiObjectDeleteController(Controller): Handles Delete Multiple Objects. """ def object_key_iter(xml): - try: - elem = fromstring(xml, 'Delete') - except (XMLSyntaxError, DocumentInvalid): - raise MalformedXML() + elem = fromstring(xml, 'Delete') for obj in elem.iterchildren('Object'): key = obj.find('./Key').text @@ -48,8 +47,20 @@ class MultiObjectDeleteController(Controller): elem = Element('DeleteResult') - xml = req.xml(MAX_MULTI_DELETE_BODY_SIZE, check_md5=True) - for key, version in object_key_iter(xml): + try: + xml = req.xml(MAX_MULTI_DELETE_BODY_SIZE, check_md5=True) + delete_list = list(object_key_iter(xml)) + if len(delete_list) > CONF.max_multi_delete_objects: + raise MalformedXML() + except (XMLSyntaxError, DocumentInvalid): + raise MalformedXML() + except ErrorResponse: + raise + except Exception as e: + LOGGER.error(e) + raise + + for key, version in delete_list: if version is not None: # TODO: delete the specific version of the object raise S3NotImplemented() diff --git a/swift3/test/unit/test_multi_delete.py b/swift3/test/unit/test_multi_delete.py index 4192e725..23d1578d 100644 --- a/swift3/test/unit/test_multi_delete.py +++ b/swift3/test/unit/test_multi_delete.py @@ -22,6 +22,7 @@ from swift.common.swob import Request from swift3.test.unit import Swift3TestCase from swift3.etree import tostring, Element, SubElement +from swift3.cfg import CONF class TestSwift3MultiDelete(Swift3TestCase): @@ -97,5 +98,21 @@ class TestSwift3MultiDelete(Swift3TestCase): status, headers, body = self.call_swift3(req) self.assertEquals(self._get_error_code(body), 'InvalidRequest') + def test_object_multi_DELETE_too_many_keys(self): + elem = Element('Delete') + for i in range(CONF.max_multi_delete_objects + 1): + obj = SubElement(elem, 'Object') + SubElement(obj, 'Key').text = str(i) + body = tostring(elem, use_s3ns=False) + content_md5 = md5(body).digest().encode('base64').strip() + + req = Request.blank('/bucket?delete', + environ={'REQUEST_METHOD': 'POST'}, + headers={'Authorization': 'AWS test:tester:hmac', + 'Content-MD5': content_md5}, + body=body) + status, headers, body = self.call_swift3(req) + self.assertEquals(self._get_error_code(body), 'MalformedXML') + if __name__ == '__main__': unittest.main()