Return 403 on unauthorized upload when over account quota
If you try an unauthorized upload into a container that is over quota you get a 403 instead of a 413, but if you try to unauthorized upload when an *account* is over quota you can see the 413 even though the upload would have been rejected by the authorize callback. By wrapping the authorize callback associated with the incoming request we can make sure to only return our 413 when the request would have been authorized otherwise. Drive by doc fixes thanks to acoles: * State that container_quotas should be after auth middleware in the class doc string. * Add note to proxy-server.conf.sample that account_quotas should be after auth middleware. The equivalent statements are already in place for each quota middleware. Doc-Impact Closes-Bug: #1387415 Change-Id: I2a88b3ec79d35bfdd73ea6ad64e376b7c7af4ea6
This commit is contained in:
parent
16e1e1e3c6
commit
f9bed74d1b
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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):
|
||||
|
@ -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')]
|
||||
|
Loading…
Reference in New Issue
Block a user