Allow marker dir objects ending with slash
Allow a slash at the end of object file name but ONLY if the PUT request also has a "Content-type: application/directory" header. Change-Id: Ic775de052ee3635e95f5e32ca6e2038909fe1005 Signed-off-by: Prashanth Pai <ppai@redhat.com>
This commit is contained in:
parent
aa1bfb3e67
commit
c6aae2b0cc
@ -37,17 +37,37 @@ SOF_MAX_OBJECT_FILENAME_LENGTH = 221
|
|||||||
# segment should not exceed 221.
|
# segment should not exceed 221.
|
||||||
|
|
||||||
|
|
||||||
def validate_obj_name_component(obj, last_component=False):
|
def validate_obj_name_component(obj, req, last_component=False):
|
||||||
|
|
||||||
|
marker_dir = False
|
||||||
|
if req.headers.get('content-type', None):
|
||||||
|
marker_dir = req.headers.get('content-type', '').lower()\
|
||||||
|
== 'application/directory'
|
||||||
|
|
||||||
if not obj:
|
if not obj:
|
||||||
return 'cannot begin, end, or have contiguous %s\'s' % os.path.sep
|
# Encountered extra slash somewhere, so obj component is empty
|
||||||
if not last_component:
|
if last_component:
|
||||||
|
if marker_dir:
|
||||||
|
# Allow directory marker objects if it ends with slash
|
||||||
|
pass # Check further for length, don't return yet
|
||||||
|
else:
|
||||||
|
return 'can end with a slash only if it is a directory marker'\
|
||||||
|
' object with "Content-Type: application/directory" header'
|
||||||
|
else:
|
||||||
|
return 'cannot begin, end, or have contiguous %s\'s' % os.path.sep
|
||||||
|
|
||||||
|
if not last_component or (last_component and marker_dir):
|
||||||
|
# Will result in directory creation
|
||||||
if len(obj) > SOF_MAX_DIR_NAME_LENGTH:
|
if len(obj) > SOF_MAX_DIR_NAME_LENGTH:
|
||||||
return 'too long (%d)' % len(obj)
|
return 'has component %s too long (%d)' % (obj, len(obj))
|
||||||
else:
|
else:
|
||||||
|
# Last component: will result in file creation
|
||||||
if len(obj) > SOF_MAX_OBJECT_FILENAME_LENGTH:
|
if len(obj) > SOF_MAX_OBJECT_FILENAME_LENGTH:
|
||||||
return 'too long (%d)' % len(obj)
|
return 'has component %s too long (%d)' % (obj, len(obj))
|
||||||
|
|
||||||
if obj == '.' or obj == '..':
|
if obj == '.' or obj == '..':
|
||||||
return 'cannot be . or ..'
|
return 'cannot have . or ..'
|
||||||
|
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
|
|
||||||
@ -72,11 +92,14 @@ def check_object_creation(req, object_name):
|
|||||||
for i, obj in enumerate(object_name_components):
|
for i, obj in enumerate(object_name_components):
|
||||||
if i == (len(object_name_components) - 1):
|
if i == (len(object_name_components) - 1):
|
||||||
last_component = True
|
last_component = True
|
||||||
reason = validate_obj_name_component(obj, last_component)
|
reason = validate_obj_name_component(obj, req, last_component)
|
||||||
if reason:
|
if reason:
|
||||||
bdy = 'Invalid object name "%s", component "%s" %s' \
|
bdy = 'Invalid object name "%s", object path %s' \
|
||||||
% (object_name, obj, reason)
|
% (object_name, reason)
|
||||||
ret = HTTPBadRequest(body=bdy,
|
ret = HTTPBadRequest(body=bdy,
|
||||||
request=req,
|
request=req,
|
||||||
content_type='text/plain')
|
content_type='text/plain')
|
||||||
|
# Return on first invalid component, does not check rest of
|
||||||
|
# the object path
|
||||||
|
break
|
||||||
return ret
|
return ret
|
||||||
|
@ -101,7 +101,8 @@ class TestConstraintsMiddleware(unittest.TestCase):
|
|||||||
).get_response(self.test_check)
|
).get_response(self.test_check)
|
||||||
self.assertEquals(resp.status_int, 400)
|
self.assertEquals(resp.status_int, 400)
|
||||||
self.assertTrue('Invalid object name' in resp.body)
|
self.assertTrue('Invalid object name' in resp.body)
|
||||||
self.assertTrue('cannot begin, end, or have' in resp.body)
|
self.assertTrue('can end with a slash only if it is a directory'
|
||||||
|
in resp.body)
|
||||||
|
|
||||||
def test_PUT_object_named_dot(self):
|
def test_PUT_object_named_dot(self):
|
||||||
path = '/V1.0/a/c2/.'
|
path = '/V1.0/a/c2/.'
|
||||||
@ -115,7 +116,7 @@ class TestConstraintsMiddleware(unittest.TestCase):
|
|||||||
).get_response(self.test_check)
|
).get_response(self.test_check)
|
||||||
self.assertEquals(resp.status_int, 400)
|
self.assertEquals(resp.status_int, 400)
|
||||||
self.assertTrue('Invalid object name' in resp.body)
|
self.assertTrue('Invalid object name' in resp.body)
|
||||||
self.assertTrue('cannot be . or ..' in resp.body)
|
self.assertTrue('cannot have . or ..' in resp.body)
|
||||||
|
|
||||||
def test_PUT_container_with_long_names(self):
|
def test_PUT_container_with_long_names(self):
|
||||||
longname = 'c' * 256
|
longname = 'c' * 256
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
import unittest
|
import unittest
|
||||||
from mock import Mock
|
from mock import patch, Mock
|
||||||
from swiftonfile.swift.common import constraints as cnt
|
from swiftonfile.swift.common import constraints as cnt
|
||||||
|
|
||||||
|
|
||||||
@ -26,39 +26,42 @@ class TestConstraints(unittest.TestCase):
|
|||||||
""" Tests for common.constraints """
|
""" Tests for common.constraints """
|
||||||
|
|
||||||
def test_validate_obj_name_component(self):
|
def test_validate_obj_name_component(self):
|
||||||
|
req = Mock()
|
||||||
|
|
||||||
# Non-last object name component - success
|
# Non-last object name component - success
|
||||||
for i in (220, 221, 222, 254, 255):
|
for i in (220, 221, 222, 254, 255):
|
||||||
obj_comp_name = 'a' * i
|
obj_comp_name = 'a' * i
|
||||||
self.assertFalse(cnt.validate_obj_name_component(obj_comp_name))
|
self.assertFalse(cnt.validate_obj_name_component(obj_comp_name,
|
||||||
|
req))
|
||||||
|
|
||||||
# Last object name component - success
|
# Last object name component - success
|
||||||
for i in (220, 221):
|
for i in (220, 221):
|
||||||
obj_comp_name = 'a' * i
|
obj_comp_name = 'a' * i
|
||||||
self.assertFalse(
|
self.assertFalse(
|
||||||
cnt.validate_obj_name_component(obj_comp_name, True))
|
cnt.validate_obj_name_component(obj_comp_name, req, True))
|
||||||
|
|
||||||
def test_validate_obj_name_component_err(self):
|
def test_validate_obj_name_component_err(self):
|
||||||
|
req = Mock()
|
||||||
|
|
||||||
# Non-last object name component - err
|
# Non-last object name component - err
|
||||||
for i in (256, 257):
|
for i in (256, 257):
|
||||||
obj_comp_name = 'a' * i
|
obj_comp_name = 'a' * i
|
||||||
result = cnt.validate_obj_name_component(obj_comp_name)
|
result = cnt.validate_obj_name_component(obj_comp_name, req)
|
||||||
self.assertEqual(result, "too long (%d)" % i)
|
self.assertTrue(("too long (%d)" % i) in result)
|
||||||
|
|
||||||
# Last object name component - err
|
# Last object name component - err
|
||||||
for i in (222, 223):
|
for i in (222, 223):
|
||||||
obj_comp_name = 'a' * i
|
obj_comp_name = 'a' * i
|
||||||
result = cnt.validate_obj_name_component(obj_comp_name, True)
|
result = cnt.validate_obj_name_component(obj_comp_name, req, True)
|
||||||
self.assertEqual(result, "too long (%d)" % i)
|
self.assertTrue(("too long (%d)" % i) in result)
|
||||||
|
|
||||||
self.assertTrue(cnt.validate_obj_name_component('.'))
|
self.assertTrue(cnt.validate_obj_name_component('.', req))
|
||||||
self.assertTrue(cnt.validate_obj_name_component('..'))
|
self.assertTrue(cnt.validate_obj_name_component('..', req))
|
||||||
self.assertTrue(cnt.validate_obj_name_component(''))
|
self.assertTrue(cnt.validate_obj_name_component('', req))
|
||||||
|
|
||||||
def test_check_object_creation(self):
|
def test_check_object_creation(self):
|
||||||
req = Mock()
|
req = Mock()
|
||||||
req.headers = []
|
req.headers = dict()
|
||||||
|
|
||||||
valid_object_names = ["a/b/c/d",
|
valid_object_names = ["a/b/c/d",
|
||||||
'/'.join(("1@3%&*0-", "};+=]|")),
|
'/'.join(("1@3%&*0-", "};+=]|")),
|
||||||
@ -74,3 +77,16 @@ class TestConstraints(unittest.TestCase):
|
|||||||
'/'.join(('a' * 255, 'b' * 255, 'c' * 222))]
|
'/'.join(('a' * 255, 'b' * 255, 'c' * 222))]
|
||||||
for o in invalid_object_names:
|
for o in invalid_object_names:
|
||||||
self.assertTrue(cnt.check_object_creation(req, o))
|
self.assertTrue(cnt.check_object_creation(req, o))
|
||||||
|
|
||||||
|
# Check for creation of directory marker objects that ends with slash
|
||||||
|
with patch.dict(req.headers, {'content-type':
|
||||||
|
'application/directory'}):
|
||||||
|
self.assertFalse(cnt.check_object_creation(req, "a/b/c/d/"))
|
||||||
|
|
||||||
|
# Check creation of objects ending with slash having any other content
|
||||||
|
# type than application/directory is not allowed
|
||||||
|
for content_type in ('text/plain', 'text/html', 'image/jpg',
|
||||||
|
'application/octet-stream', 'blah/blah'):
|
||||||
|
with patch.dict(req.headers, {'content-type':
|
||||||
|
content_type}):
|
||||||
|
self.assertTrue(cnt.check_object_creation(req, "a/b/c/d/"))
|
||||||
|
Loading…
Reference in New Issue
Block a user