Handle COPY verb in container quota middleware

The patch fix the ability to bypass the account
quota using the COPY verb. The quotas for destination
container are now checked before the copy.

Change-Id: I9f409ea6c0893f36972aa1fabe31fb143e151ab5
Fixes: bug #1259565
This commit is contained in:
Fabien Boucher 2013-12-10 18:31:55 +01:00
parent 766ab4f0d0
commit 946984cb84
2 changed files with 73 additions and 6 deletions

View File

@ -79,9 +79,21 @@ class ContainerQuotaMiddleware(object):
return HTTPBadRequest(body='Invalid count quota.')
# check user uploads against quotas
elif obj and req.method == 'PUT':
container_info = get_container_info(
req.environ, self.app, swift_source='CQ')
elif obj and req.method in ('PUT', 'COPY'):
container_info = None
if req.method == 'PUT':
container_info = get_container_info(
req.environ, self.app, swift_source='CQ')
if req.method == 'COPY' and 'Destination' in req.headers:
dest = req.headers.get('Destination').lstrip('/')
path_info = req.environ['PATH_INFO']
req.environ['PATH_INFO'] = "/%s/%s/%s" % (
version, account, dest)
try:
container_info = get_container_info(
req.environ, self.app, swift_source='CQ')
finally:
req.environ['PATH_INFO'] = path_info
if not container_info or not is_success(container_info['status']):
# this will hopefully 404 later
return self.app
@ -90,10 +102,11 @@ class ContainerQuotaMiddleware(object):
'bytes' in container_info and \
container_info['meta']['quota-bytes'].isdigit():
content_length = (req.content_length or 0)
if 'x-copy-from' in req.headers:
src_cont, src_obj = check_copy_from_header(req)
if 'x-copy-from' in req.headers or req.method == 'COPY':
if 'x-copy-from' in req.headers:
container, obj = check_copy_from_header(req)
path = '/%s/%s/%s/%s' % (version, account,
src_cont, src_obj)
container, obj)
object_info = get_object_info(req.environ, self.app, path)
if not object_info or not object_info['length']:
content_length = 0

View File

@ -106,6 +106,18 @@ class TestContainerQuotas(unittest.TestCase):
res = req.get_response(app)
self.assertEquals(res.status_int, 413)
def test_exceed_bytes_quota_copy_verb(self):
app = container_quotas.ContainerQuotaMiddleware(FakeApp(), {})
cache = FakeCache({'bytes': 0, 'meta': {'quota-bytes': '2'}})
req = Request.blank('/v1/a/c2/o2',
environ={'REQUEST_METHOD': 'COPY',
'swift.object/a/c2/o2': {'length': 10},
'swift.cache': cache},
headers={'Destination': '/c/o'})
res = req.get_response(app)
self.assertEquals(res.status_int, 413)
def test_not_exceed_bytes_quota(self):
app = container_quotas.ContainerQuotaMiddleware(FakeApp(), {})
cache = FakeCache({'bytes': 0, 'meta': {'quota-bytes': '100'}})
@ -127,6 +139,17 @@ class TestContainerQuotas(unittest.TestCase):
res = req.get_response(app)
self.assertEquals(res.status_int, 200)
def test_not_exceed_bytes_quota_copy_verb(self):
app = container_quotas.ContainerQuotaMiddleware(FakeApp(), {})
cache = FakeCache({'bytes': 0, 'meta': {'quota-bytes': '100'}})
req = Request.blank('/v1/a/c2/o2',
environ={'REQUEST_METHOD': 'COPY',
'swift.object/a/c2/o2': {'length': 10},
'swift.cache': cache},
headers={'Destination': '/c/o'})
res = req.get_response(app)
self.assertEquals(res.status_int, 200)
def test_bytes_quota_copy_from_no_src(self):
app = container_quotas.ContainerQuotaMiddleware(FakeApp(), {})
cache = FakeCache({'bytes': 0, 'meta': {'quota-bytes': '100'}})
@ -148,6 +171,17 @@ class TestContainerQuotas(unittest.TestCase):
res = req.get_response(app)
self.assertEquals(res.status_int, 412)
def test_bytes_quota_copy_verb_no_src(self):
app = container_quotas.ContainerQuotaMiddleware(FakeApp(), {})
cache = FakeCache({'bytes': 0, 'meta': {'quota-bytes': '100'}})
req = Request.blank('/v1/a/c2/o3',
environ={'REQUEST_METHOD': 'COPY',
'swift.object/a/c2/o2': {'length': 10},
'swift.cache': cache},
headers={'Destination': '/c/o'})
res = req.get_response(app)
self.assertEquals(res.status_int, 200)
def test_exceed_counts_quota(self):
app = container_quotas.ContainerQuotaMiddleware(FakeApp(), {})
cache = FakeCache({'object_count': 1, 'meta': {'quota-count': '1'}})
@ -169,6 +203,16 @@ class TestContainerQuotas(unittest.TestCase):
res = req.get_response(app)
self.assertEquals(res.status_int, 413)
def test_exceed_counts_quota_copy_verb(self):
app = container_quotas.ContainerQuotaMiddleware(FakeApp(), {})
cache = FakeCache({'object_count': 1, 'meta': {'quota-count': '1'}})
req = Request.blank('/v1/a/c2/o2',
environ={'REQUEST_METHOD': 'COPY',
'swift.cache': cache},
headers={'Destination': '/c/o'})
res = req.get_response(app)
self.assertEquals(res.status_int, 413)
def test_not_exceed_counts_quota(self):
app = container_quotas.ContainerQuotaMiddleware(FakeApp(), {})
cache = FakeCache({'object_count': 1, 'meta': {'quota-count': '2'}})
@ -189,6 +233,16 @@ class TestContainerQuotas(unittest.TestCase):
res = req.get_response(app)
self.assertEquals(res.status_int, 200)
def test_not_exceed_counts_quota_copy_verb(self):
app = container_quotas.ContainerQuotaMiddleware(FakeApp(), {})
cache = FakeCache({'object_count': 1, 'meta': {'quota-count': '2'}})
req = Request.blank('/v1/a/c2/o2',
environ={'REQUEST_METHOD': 'COPY',
'swift.cache': cache},
headers={'Destination': '/c/o'})
res = req.get_response(app)
self.assertEquals(res.status_int, 200)
def test_invalid_quotas(self):
req = Request.blank(
'/v1/a/c',