diff --git a/swift/common/middleware/account_quotas.py b/swift/common/middleware/account_quotas.py index 01a8336a0d..a3a6ab8d7f 100644 --- a/swift/common/middleware/account_quotas.py +++ b/swift/common/middleware/account_quotas.py @@ -67,12 +67,22 @@ class AccountQuotaMiddleware(object): return self.app try: - ver, acc, cont, obj = request.split_path(2, 4, rest_with_last=True) + ver, account, container, obj = request.split_path( + 2, 4, rest_with_last=True) except ValueError: return self.app - new_quota = request.headers.get('X-Account-Meta-Quota-Bytes') - remove_quota = request.headers.get('X-Remove-Account-Meta-Quota-Bytes') + if not container: + # account request, so we pay attention to the quotas + new_quota = request.headers.get( + 'X-Account-Meta-Quota-Bytes') + remove_quota = request.headers.get( + 'X-Remove-Account-Meta-Quota-Bytes') + else: + # container or object request; even if the quota headers are set + # in the request, they're meaningless + new_quota = remove_quota = None + if remove_quota: new_quota = 0 # X-Remove dominates if both are present @@ -85,11 +95,14 @@ class AccountQuotaMiddleware(object): if new_quota is not None: return HTTPForbidden() + if obj and request.method == "POST": + return self.app + copy_from = request.headers.get('X-Copy-From') content_length = (request.content_length or 0) if obj and copy_from: - path = '/' + ver + '/' + acc + '/' + copy_from.lstrip('/') + path = '/' + ver + '/' + account + '/' + copy_from.lstrip('/') object_info = get_object_info(request.environ, self.app, path) if not object_info or not object_info['length']: content_length = 0 diff --git a/test/unit/common/middleware/test_account_quotas.py b/test/unit/common/middleware/test_account_quotas.py index 137309df9f..623c758104 100644 --- a/test/unit/common/middleware/test_account_quotas.py +++ b/test/unit/common/middleware/test_account_quotas.py @@ -86,6 +86,33 @@ class TestAccountQuota(unittest.TestCase): res = req.get_response(app) self.assertEquals(res.status_int, 200) + def test_obj_request_ignores_attempt_to_set_quotas(self): + # If you try to set X-Account-Meta-* on an object, it's ignored, so + # the quota middleware shouldn't complain about it even if we're not a + # reseller admin. + headers = [('x-account-bytes-used', '1000')] + app = account_quotas.AccountQuotaMiddleware(FakeApp(headers)) + cache = FakeCache(None) + req = Request.blank('/v1/a/c/o', + headers={'X-Account-Meta-Quota-Bytes': '99999'}, + environ={'REQUEST_METHOD': 'PUT', + 'swift.cache': cache}) + res = req.get_response(app) + self.assertEquals(res.status_int, 200) + + def test_container_request_ignores_attempt_to_set_quotas(self): + # As with an object, if you try to set X-Account-Meta-* on a + # container, it's ignored. + headers = [('x-account-bytes-used', '1000')] + app = account_quotas.AccountQuotaMiddleware(FakeApp(headers)) + cache = FakeCache(None) + req = Request.blank('/v1/a/c', + headers={'X-Account-Meta-Quota-Bytes': '99999'}, + environ={'REQUEST_METHOD': 'PUT', + 'swift.cache': cache}) + res = req.get_response(app) + self.assertEquals(res.status_int, 200) + def test_exceed_bytes_quota(self): headers = [('x-account-bytes-used', '1000'), ('x-account-meta-quota-bytes', '0')] @@ -97,6 +124,18 @@ class TestAccountQuota(unittest.TestCase): res = req.get_response(app) self.assertEquals(res.status_int, 413) + def test_over_quota_obj_post_still_works(self): + headers = [('x-account-bytes-used', '1001'), + ('x-account-meta-quota-bytes', '1000')] + app = account_quotas.AccountQuotaMiddleware(FakeApp(headers)) + cache = FakeCache(None) + req = Request.blank('/v1/a/c/o', + environ={'REQUEST_METHOD': 'POST', + 'HTTP_X_OBJECT_META_BERT': 'ernie', + 'swift.cache': cache}) + res = req.get_response(app) + self.assertEquals(res.status_int, 200) + def test_exceed_bytes_quota_copy_from(self): headers = [('x-account-bytes-used', '500'), ('x-account-meta-quota-bytes', '1000'), @@ -139,7 +178,7 @@ class TestAccountQuota(unittest.TestCase): ('x-account-meta-quota-bytes', '0')] app = account_quotas.AccountQuotaMiddleware(FakeApp(headers)) cache = FakeCache(None) - req = Request.blank('/v1/a/c/o', + req = Request.blank('/v1/a', environ={'REQUEST_METHOD': 'PUT', 'swift.cache': cache, 'reseller_request': True}) @@ -194,7 +233,7 @@ class TestAccountQuota(unittest.TestCase): headers = [('x-account-bytes-used', '0'), ] app = account_quotas.AccountQuotaMiddleware(FakeApp(headers)) cache = FakeCache(None) - req = Request.blank('/v1/a/c', + req = Request.blank('/v1/a', environ={'REQUEST_METHOD': 'POST', 'swift.cache': cache, 'HTTP_X_ACCOUNT_META_QUOTA_BYTES': 'abc', @@ -206,7 +245,7 @@ class TestAccountQuota(unittest.TestCase): headers = [('x-account-bytes-used', '0'), ] app = account_quotas.AccountQuotaMiddleware(FakeApp(headers)) cache = FakeCache(None) - req = Request.blank('/v1/a/c', + req = Request.blank('/v1/a', environ={'REQUEST_METHOD': 'POST', 'swift.cache': cache, 'HTTP_X_ACCOUNT_META_QUOTA_BYTES': '100'}) @@ -217,7 +256,7 @@ class TestAccountQuota(unittest.TestCase): headers = [('x-account-bytes-used', '0'), ] app = account_quotas.AccountQuotaMiddleware(FakeApp(headers)) cache = FakeCache(None) - req = Request.blank('/v1/a/c', + req = Request.blank('/v1/a', environ={'REQUEST_METHOD': 'POST', 'swift.cache': cache, 'HTTP_X_ACCOUNT_META_QUOTA_BYTES': '100', @@ -229,7 +268,7 @@ class TestAccountQuota(unittest.TestCase): headers = [('x-account-bytes-used', '0'), ] app = account_quotas.AccountQuotaMiddleware(FakeApp(headers)) cache = FakeCache(None) - req = Request.blank('/v1/a/c', + req = Request.blank('/v1/a', environ={'REQUEST_METHOD': 'POST', 'swift.cache': cache, 'HTTP_X_ACCOUNT_META_QUOTA_BYTES': ''}) @@ -240,7 +279,7 @@ class TestAccountQuota(unittest.TestCase): headers = [('x-account-bytes-used', '0'), ] app = account_quotas.AccountQuotaMiddleware(FakeApp(headers)) cache = FakeCache(None) - req = Request.blank('/v1/a/c', environ={ + req = Request.blank('/v1/a', environ={ 'REQUEST_METHOD': 'POST', 'swift.cache': cache, 'HTTP_X_REMOVE_ACCOUNT_META_QUOTA_BYTES': 'True'}) @@ -250,7 +289,7 @@ class TestAccountQuota(unittest.TestCase): def test_delete_quotas_reseller(self): headers = [('x-account-bytes-used', '0'), ] app = account_quotas.AccountQuotaMiddleware(FakeApp(headers)) - req = Request.blank('/v1/a/c', + req = Request.blank('/v1/a', environ={'REQUEST_METHOD': 'POST', 'HTTP_X_ACCOUNT_META_QUOTA_BYTES': '', 'reseller_request': True}) @@ -261,7 +300,7 @@ class TestAccountQuota(unittest.TestCase): headers = [('x-account-bytes-used', '0'), ] app = account_quotas.AccountQuotaMiddleware(FakeApp(headers)) cache = FakeCache(None) - req = Request.blank('/v1/a/c', environ={ + req = Request.blank('/v1/a', environ={ 'REQUEST_METHOD': 'POST', 'swift.cache': cache, 'HTTP_X_REMOVE_ACCOUNT_META_QUOTA_BYTES': 'True', @@ -277,7 +316,7 @@ class TestAccountQuota(unittest.TestCase): environ={'REQUEST_METHOD': 'PUT', 'swift.cache': cache}) res = req.get_response(app) - #Response code of 200 because authentication itself is not done here + # Response code of 200 because authentication itself is not done here self.assertEquals(res.status_int, 200)