diff --git a/etc/proxy-server.conf-sample b/etc/proxy-server.conf-sample index e17f0a7703..51995e392b 100644 --- a/etc/proxy-server.conf-sample +++ b/etc/proxy-server.conf-sample @@ -532,10 +532,6 @@ use = egg:swift#bulk # delete_container_retry_count = 0 -# Note: Put after auth in the pipeline. -[filter:container-quotas] -use = egg:swift#container_quotas - # Note: Put after auth and staticweb in the pipeline. [filter:slo] use = egg:swift#slo @@ -568,6 +564,11 @@ use = egg:swift#dlo # Time limit on GET requests (seconds) # max_get_time = 86400 +# Note: Put after auth in the pipeline. +[filter:container-quotas] +use = egg:swift#container_quotas + +# Note: Put after auth in the pipeline. [filter:account-quotas] use = egg:swift#account_quotas diff --git a/swift/common/middleware/account_quotas.py b/swift/common/middleware/account_quotas.py index a3c7dc96cd..fcb55b5573 100644 --- a/swift/common/middleware/account_quotas.py +++ b/swift/common/middleware/account_quotas.py @@ -53,7 +53,8 @@ account size has been updated. """ from swift.common.constraints import check_copy_from_header -from swift.common.swob import HTTPForbidden, Response, HTTPBadRequest, wsgify +from swift.common.swob import HTTPForbidden, HTTPBadRequest, \ + HTTPRequestEntityTooLarge, wsgify from swift.common.utils import register_swift_info from swift.proxy.controllers.base import get_account_info, get_object_info @@ -136,7 +137,18 @@ class AccountQuotaMiddleware(object): new_size = int(account_info['bytes']) + content_length if quota < new_size: - return Response(status=413, body='Upload exceeds quota.') + resp = HTTPRequestEntityTooLarge(body='Upload exceeds quota.') + if 'swift.authorize' in request.environ: + orig_authorize = request.environ['swift.authorize'] + + def reject_authorize(*args, **kwargs): + aresp = orig_authorize(*args, **kwargs) + if aresp: + return aresp + return resp + request.environ['swift.authorize'] = reject_authorize + else: + return resp return self.app diff --git a/swift/common/middleware/container_quotas.py b/swift/common/middleware/container_quotas.py index 3edaf1fb2a..4aef895d2f 100644 --- a/swift/common/middleware/container_quotas.py +++ b/swift/common/middleware/container_quotas.py @@ -40,10 +40,21 @@ set: | X-Container-Meta-Quota-Count | Maximum object count of the | | | container. | +---------------------------------------------+-------------------------------+ + +The ``container_quotas`` middleware should be added to the pipeline in your +``/etc/swift/proxy-server.conf`` file just after any auth middleware. +For example:: + + [pipeline:main] + pipeline = catch_errors cache tempauth container_quotas proxy-server + + [filter:container_quotas] + use = egg:swift#container_quotas """ from swift.common.constraints import check_copy_from_header from swift.common.http import is_success -from swift.common.swob import Response, HTTPBadRequest, wsgify +from swift.common.swob import HTTPRequestEntityTooLarge, HTTPBadRequest, \ + wsgify from swift.common.utils import register_swift_info from swift.proxy.controllers.base import get_container_info, get_object_info @@ -60,7 +71,7 @@ class ContainerQuotaMiddleware(object): aresp = req.environ['swift.authorize'](req) if aresp: return aresp - return Response(status=413, body='Upload exceeds quota.') + return HTTPRequestEntityTooLarge(body='Upload exceeds quota.') @wsgify def __call__(self, req): diff --git a/test/unit/common/middleware/test_account_quotas.py b/test/unit/common/middleware/test_account_quotas.py index 75bd1d70eb..e8c2563c5a 100644 --- a/test/unit/common/middleware/test_account_quotas.py +++ b/test/unit/common/middleware/test_account_quotas.py @@ -13,7 +13,7 @@ import unittest -from swift.common.swob import Request +from swift.common.swob import Request, wsgify, HTTPForbidden from swift.common.middleware import account_quotas @@ -51,6 +51,10 @@ class FakeApp(object): self.headers = headers def __call__(self, env, start_response): + if 'swift.authorize' in env: + aresp = env['swift.authorize'](Request(env)) + if aresp: + return aresp(env, start_response) if env['REQUEST_METHOD'] == "HEAD" and \ env['PATH_INFO'] == '/v1/a/c2/o2': env_key = get_object_env_key('a', 'c2', 'o2') @@ -67,6 +71,21 @@ class FakeApp(object): return [] +class FakeAuthFilter(object): + + def __init__(self, app): + self.app = app + + @wsgify + def __call__(self, req): + def authorize(req): + if req.headers['x-auth-token'] == 'secret': + return + return HTTPForbidden(request=req) + req.environ['swift.authorize'] = authorize + return req.get_response(self.app) + + class TestAccountQuota(unittest.TestCase): def test_unauthorized(self): @@ -142,6 +161,54 @@ class TestAccountQuota(unittest.TestCase): self.assertEquals(res.status_int, 413) self.assertEquals(res.body, 'Upload exceeds quota.') + def test_exceed_quota_not_authorized(self): + headers = [('x-account-bytes-used', '1000'), + ('x-account-meta-quota-bytes', '0')] + app = FakeAuthFilter( + account_quotas.AccountQuotaMiddleware(FakeApp(headers))) + cache = FakeCache(None) + req = Request.blank('/v1/a/c/o', method='PUT', + headers={'x-auth-token': 'bad-secret'}, + environ={'swift.cache': cache}) + res = req.get_response(app) + self.assertEquals(res.status_int, 403) + + def test_exceed_quota_authorized(self): + headers = [('x-account-bytes-used', '1000'), + ('x-account-meta-quota-bytes', '0')] + app = FakeAuthFilter( + account_quotas.AccountQuotaMiddleware(FakeApp(headers))) + cache = FakeCache(None) + req = Request.blank('/v1/a/c/o', method='PUT', + headers={'x-auth-token': 'secret'}, + environ={'swift.cache': cache}) + res = req.get_response(app) + self.assertEquals(res.status_int, 413) + + def test_under_quota_not_authorized(self): + headers = [('x-account-bytes-used', '0'), + ('x-account-meta-quota-bytes', '1000')] + app = FakeAuthFilter( + account_quotas.AccountQuotaMiddleware(FakeApp(headers))) + cache = FakeCache(None) + req = Request.blank('/v1/a/c/o', method='PUT', + headers={'x-auth-token': 'bad-secret'}, + environ={'swift.cache': cache}) + res = req.get_response(app) + self.assertEquals(res.status_int, 403) + + def test_under_quota_authorized(self): + headers = [('x-account-bytes-used', '0'), + ('x-account-meta-quota-bytes', '1000')] + app = FakeAuthFilter( + account_quotas.AccountQuotaMiddleware(FakeApp(headers))) + cache = FakeCache(None) + req = Request.blank('/v1/a/c/o', method='PUT', + headers={'x-auth-token': 'secret'}, + environ={'swift.cache': cache}) + res = req.get_response(app) + self.assertEquals(res.status_int, 200) + def test_over_quota_container_create_still_works(self): headers = [('x-account-bytes-used', '1001'), ('x-account-meta-quota-bytes', '1000')]