summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--gluster/swift/common/Glusterfs.py14
-rw-r--r--gluster/swift/common/constraints.py39
-rw-r--r--test/functional/gluster_swift_tests.py33
-rw-r--r--test/unit/common/test_constraints.py59
-rw-r--r--test/unit/proxy/test_server.py10
5 files changed, 149 insertions, 6 deletions
diff --git a/gluster/swift/common/Glusterfs.py b/gluster/swift/common/Glusterfs.py
index 55012d3..5d2cab1 100644
--- a/gluster/swift/common/Glusterfs.py
+++ b/gluster/swift/common/Glusterfs.py
@@ -37,6 +37,7 @@ _allow_mount_per_server = False
_implicit_dir_objects = False
_container_update_object_count = False
_account_update_container_count = False
+_ignore_unsupported_headers = False
if _fs_conf.read(os.path.join(SWIFT_DIR, 'fs.conf')):
try:
@@ -97,6 +98,19 @@ if _fs_conf.read(os.path.join(SWIFT_DIR, 'fs.conf')):
except (NoSectionError, NoOptionError):
pass
+ # -- Hidden configuration option --
+ # Ignore unsupported headers and allow them in a request without
+ # returning a 400-BadRequest. This setting can be set to
+ # allow unsupported headers such as X-Delete-At and
+ # X-Delete-After even though they will not be used.
+ try:
+ _ignore_unsupported_headers = \
+ _fs_conf.get('DEFAULT',
+ 'ignore_unsupported_headers',
+ "no") in TRUE_VALUES
+ except (NoSectionError, NoOptionError):
+ pass
+
NAME = 'glusterfs'
diff --git a/gluster/swift/common/constraints.py b/gluster/swift/common/constraints.py
index 523277d..7681c49 100644
--- a/gluster/swift/common/constraints.py
+++ b/gluster/swift/common/constraints.py
@@ -23,6 +23,7 @@ import swift.common.ring as _ring
from gluster.swift.common import Glusterfs, ring
MAX_OBJECT_NAME_COMPONENT_LENGTH = 255
+UNSUPPORTED_HEADERS = ['x-delete-at', 'x-delete-after']
def set_object_name_component_length(len=None):
@@ -54,8 +55,41 @@ def validate_obj_name_component(obj):
return 'cannot be . or ..'
return ''
+
+def validate_headers(req):
+ """
+ Validate client header requests
+ :param req: Http request
+ """
+ if not Glusterfs._ignore_unsupported_headers:
+ for unsupported_header in UNSUPPORTED_HEADERS:
+ if unsupported_header in req.headers:
+ return '%s headers are not supported' \
+ % ','.join(UNSUPPORTED_HEADERS)
+ return ''
+
# Save the original check object creation
__check_object_creation = swift.common.constraints.check_object_creation
+__check_metadata = swift.common.constraints.check_metadata
+
+
+def gluster_check_metadata(req, target_type, POST=True):
+ """
+ :param req: HTTP request object
+ :param target_type: Value from POST passed to __check_metadata
+ :param POST: Only call __check_metadata on POST since Swift only
+ calls check_metadata on POSTs.
+ """
+ ret = None
+ if POST:
+ ret = __check_metadata(req, target_type)
+ if ret is None:
+ bdy = validate_headers(req)
+ if bdy:
+ ret = HTTPBadRequest(body=bdy,
+ request=req,
+ content_type='text/plain')
+ return ret
# Define our new one which invokes the original
@@ -85,11 +119,14 @@ def gluster_check_object_creation(req, object_name):
ret = HTTPBadRequest(body=bdy,
request=req,
content_type='text/plain')
+ if ret is None:
+ ret = gluster_check_metadata(req, 'object', POST=False)
return ret
-# Replace the original check object creation with ours
+# Replace the original checks with ours
swift.common.constraints.check_object_creation = gluster_check_object_creation
+swift.common.constraints.check_metadata = gluster_check_metadata
# Replace the original check mount with ours
swift.common.constraints.check_mount = Glusterfs.mount
diff --git a/test/functional/gluster_swift_tests.py b/test/functional/gluster_swift_tests.py
index d7a833e..0a721b6 100644
--- a/test/functional/gluster_swift_tests.py
+++ b/test/functional/gluster_swift_tests.py
@@ -58,6 +58,37 @@ class TestFile(Base):
data_read = file.read()
self.assertEquals(data,data_read)
+ def testInvalidHeadersPUT(self):
+ file = self.env.container.file(Utils.create_name())
+ self.assertRaises(ResponseError,
+ file.write_random,
+ self.env.file_size,
+ hdrs={'X-Delete-At': '9876545321'})
+ self.assert_status(400)
+ self.assertRaises(ResponseError,
+ file.write_random,
+ self.env.file_size,
+ hdrs={'X-Delete-After': '60'})
+ self.assert_status(400)
+
+ def testInvalidHeadersPOST(self):
+ file = self.env.container.file(Utils.create_name())
+ file.write_random(self.env.file_size)
+ headers = file.make_headers(cfg={})
+ headers.update({ 'X-Delete-At' : '987654321'})
+ # Need to call conn.make_request instead of file.sync_metadata
+ # because sync_metadata calls make_headers. make_headers()
+ # overwrites any headers in file.metadata as 'user' metadata
+ # by appending 'X-Object-Meta-' to any of the headers
+ # in file.metadata.
+ file.conn.make_request('POST', file.path, hdrs=headers, cfg={})
+ self.assertEqual(400, file.conn.response.status)
+
+ headers = file.make_headers(cfg={})
+ headers.update({ 'X-Delete-After' : '60'})
+ file.conn.make_request('POST', file.path, hdrs=headers, cfg={})
+ self.assertEqual(400, file.conn.response.status)
+
class TestFileUTF8(Base2, TestFile):
set_up = False
@@ -336,5 +367,3 @@ class TestMultiProtocolAccess(Base):
md5_returned = hashlib.md5(data_read_from_mountP).hexdigest()
self.assertEquals(md5_returned,file_info['etag'])
fhOnMountPoint.close()
-
-
diff --git a/test/unit/common/test_constraints.py b/test/unit/common/test_constraints.py
index 7139094..180721c 100644
--- a/test/unit/common/test_constraints.py
+++ b/test/unit/common/test_constraints.py
@@ -16,7 +16,7 @@
import unittest
import swift.common.constraints
from nose import SkipTest
-from mock import patch
+from mock import Mock, patch
from gluster.swift.common import constraints as cnt
@@ -75,12 +75,65 @@ class TestConstraints(unittest.TestCase):
self.assertTrue(cnt.validate_obj_name_component('..'))
self.assertTrue(cnt.validate_obj_name_component(''))
+ def test_validate_headers(self):
+ req = Mock()
+ req.headers = []
+ self.assertEqual(cnt.validate_headers(req), '')
+ req.headers = ['x-some-header']
+ self.assertEqual(cnt.validate_headers(req), '')
+ req.headers = ['x-delete-at', 'x-some-header']
+ self.assertNotEqual(cnt.validate_headers(req), '')
+ req.headers = ['x-delete-after', 'x-some-header']
+ self.assertNotEqual(cnt.validate_headers(req), '')
+ req.headers = ['x-delete-at', 'x-delete-after', 'x-some-header']
+ self.assertNotEqual(cnt.validate_headers(req), '')
+
+ def test_validate_headers_ignoring_config_set(self):
+ with patch('gluster.swift.common.constraints.'
+ 'Glusterfs._ignore_unsupported_headers', True):
+ req = Mock()
+ req.headers = []
+ self.assertEqual(cnt.validate_headers(req), '')
+ req.headers = ['x-some-header']
+ self.assertEqual(cnt.validate_headers(req), '')
+ req.headers = ['x-delete-at', 'x-some-header']
+ self.assertEqual(cnt.validate_headers(req), '')
+ req.headers = ['x-delete-after', 'x-some-header']
+ self.assertEqual(cnt.validate_headers(req), '')
+ req.headers = ['x-delete-at', 'x-delete-after', 'x-some-header']
+ self.assertEqual(cnt.validate_headers(req), '')
+
+ def test_gluster_check_metadata(self):
+ mock_check_metadata = Mock()
+ with patch('gluster.swift.common.constraints.__check_metadata',
+ mock_check_metadata):
+ req = Mock()
+ req.headers = []
+ cnt.gluster_check_metadata(req, 'object')
+ self.assertTrue(1, mock_check_metadata.call_count)
+ cnt.gluster_check_metadata(req, 'object', POST=False)
+ self.assertTrue(1, mock_check_metadata.call_count)
+ req.headers = ['x-some-header']
+ self.assertEqual(cnt.gluster_check_metadata(req, 'object', POST=False), None)
+ req.headers = ['x-delete-at', 'x-some-header']
+ self.assertNotEqual(cnt.gluster_check_metadata(req, 'object', POST=False), None)
+ req.headers = ['x-delete-after', 'x-some-header']
+ self.assertNotEqual(cnt.gluster_check_metadata(req, 'object', POST=False), None)
+ req.headers = ['x-delete-at', 'x-delete-after', 'x-some-header']
+ self.assertNotEqual(cnt.gluster_check_metadata(req, 'object', POST=False), None)
+
def test_gluster_check_object_creation(self):
with patch('gluster.swift.common.constraints.__check_object_creation',
mock_check_object_creation):
- self.assertFalse(cnt.gluster_check_object_creation(None, 'dir/z'))
+ req = Mock()
+ req.headers = []
+ self.assertFalse(cnt.gluster_check_object_creation(req, 'dir/z'))
def test_gluster_check_object_creation_err(self):
with patch('gluster.swift.common.constraints.__check_object_creation',
mock_check_object_creation):
- self.assertTrue(cnt.gluster_check_object_creation(None, 'dir/.'))
+ req = Mock()
+ req.headers = []
+ self.assertTrue(cnt.gluster_check_object_creation(req, 'dir/.'))
+ req.headers = ['x-delete-at']
+ self.assertTrue(cnt.gluster_check_object_creation(req, 'dir/z'))
diff --git a/test/unit/proxy/test_server.py b/test/unit/proxy/test_server.py
index 490e8fc..25976ba 100644
--- a/test/unit/proxy/test_server.py
+++ b/test/unit/proxy/test_server.py
@@ -4354,6 +4354,7 @@ class TestObjectController(unittest.TestCase):
self.assert_(called[0])
def test_POST_converts_delete_after_to_delete_at(self):
+ raise SkipTest("X-Delete-At and X-Delete-After are not supported")
with save_globals():
controller = proxy_server.ObjectController(self.app, 'account',
'container', 'object')
@@ -4390,6 +4391,7 @@ class TestObjectController(unittest.TestCase):
time.time = orig_time
def test_POST_non_int_delete_after(self):
+ raise SkipTest("X-Delete-At and X-Delete-After are not supported")
with save_globals():
controller = proxy_server.ObjectController(self.app, 'account',
'container', 'object')
@@ -4404,6 +4406,7 @@ class TestObjectController(unittest.TestCase):
self.assertTrue('Non-integer X-Delete-After' in res.body)
def test_POST_negative_delete_after(self):
+ raise SkipTest("X-Delete-At and X-Delete-After are not supported")
with save_globals():
controller = proxy_server.ObjectController(self.app, 'account',
'container', 'object')
@@ -4418,6 +4421,7 @@ class TestObjectController(unittest.TestCase):
self.assertTrue('X-Delete-At in past' in res.body)
def test_POST_delete_at(self):
+ raise SkipTest("X-Delete-At and X-Delete-After are not supported")
with save_globals():
given_headers = {}
@@ -4462,6 +4466,7 @@ class TestObjectController(unittest.TestCase):
self.assertTrue('X-Delete-At in past' in resp.body)
def test_PUT_converts_delete_after_to_delete_at(self):
+ raise SkipTest("X-Delete-At and X-Delete-After are not supported")
with save_globals():
controller = proxy_server.ObjectController(self.app, 'account',
'container', 'object')
@@ -4484,6 +4489,7 @@ class TestObjectController(unittest.TestCase):
time.time = orig_time
def test_PUT_non_int_delete_after(self):
+ raise SkipTest("X-Delete-At and X-Delete-After are not supported")
with save_globals():
controller = proxy_server.ObjectController(self.app, 'account',
'container', 'object')
@@ -4499,6 +4505,7 @@ class TestObjectController(unittest.TestCase):
self.assertTrue('Non-integer X-Delete-After' in res.body)
def test_PUT_negative_delete_after(self):
+ raise SkipTest("X-Delete-At and X-Delete-After are not supported")
with save_globals():
controller = proxy_server.ObjectController(self.app, 'account',
'container', 'object')
@@ -4514,6 +4521,7 @@ class TestObjectController(unittest.TestCase):
self.assertTrue('X-Delete-At in past' in res.body)
def test_PUT_delete_at(self):
+ raise SkipTest("X-Delete-At and X-Delete-After are not supported")
with save_globals():
given_headers = {}
@@ -4890,6 +4898,7 @@ class TestObjectController(unittest.TestCase):
@mock.patch('time.time', new=lambda: STATIC_TIME)
def test_PUT_x_delete_at_with_fewer_container_replicas(self):
+ raise SkipTest("X-Delete-At and X-Delete-After are not supported")
self.app.container_ring.set_replicas(2)
delete_at_timestamp = int(time.time()) + 100000
@@ -4925,6 +4934,7 @@ class TestObjectController(unittest.TestCase):
@mock.patch('time.time', new=lambda: STATIC_TIME)
def test_PUT_x_delete_at_with_more_container_replicas(self):
+ raise SkipTest("X-Delete-At and X-Delete-After are not supported")
self.app.container_ring.set_replicas(4)
self.app.expiring_objects_account = 'expires'
self.app.expiring_objects_container_divisor = 60