From fb5fcb189e32746f8c884cd53cb5239a384bc070 Mon Sep 17 00:00:00 2001 From: Tim Burke Date: Tue, 2 Aug 2016 21:50:45 -0700 Subject: [PATCH] Fix encryption-delimiter interaction Previously, if a container listing produced `subdir` elements the decrypter would raise a KeyError. Additionally, update the functests so this sort of thing would have been caught at the gate. Closes-Bug: 1609904 Change-Id: Idc1907d19f90af7a086f45f8faecee9fbc3c69c2 --- swift/common/middleware/crypto/decrypter.py | 5 ++- test/functional/swift_test_client.py | 37 ++++++++++++------- test/functional/tests.py | 32 ++++++++++++---- .../middleware/crypto/test_decrypter.py | 26 +++++++++---- 4 files changed, 70 insertions(+), 30 deletions(-) diff --git a/swift/common/middleware/crypto/decrypter.py b/swift/common/middleware/crypto/decrypter.py index 46e2dbc484..e797ddde33 100644 --- a/swift/common/middleware/crypto/decrypter.py +++ b/swift/common/middleware/crypto/decrypter.py @@ -395,8 +395,9 @@ class DecrypterContContext(BaseDecrypterContext): return [new_body] def decrypt_obj_dict(self, obj_dict, key): - ciphertext = obj_dict['hash'] - obj_dict['hash'] = self.decrypt_value_with_meta(ciphertext, key) + if 'hash' in obj_dict: + ciphertext = obj_dict['hash'] + obj_dict['hash'] = self.decrypt_value_with_meta(ciphertext, key) return obj_dict def process_xml_resp(self, key, resp_iter): diff --git a/test/functional/swift_test_client.py b/test/functional/swift_test_client.py index 98262f5892..67660400a8 100644 --- a/test/functional/swift_test_client.py +++ b/test/functional/swift_test_client.py @@ -565,27 +565,38 @@ class Container(Base): files = json.loads(self.conn.response.read()) for file_item in files: - file_item['name'] = file_item['name'].encode('utf-8') - file_item['content_type'] = file_item['content_type'].\ - encode('utf-8') + for key in ('name', 'subdir', 'content_type'): + if key in file_item: + file_item[key] = file_item[key].encode('utf-8') return files elif format_type == 'xml': files = [] tree = minidom.parseString(self.conn.response.read()) - for x in tree.getElementsByTagName('object'): + container = tree.getElementsByTagName('container')[0] + for x in container.childNodes: file_item = {} - for key in ['name', 'hash', 'bytes', 'content_type', - 'last_modified']: - - file_item[key] = x.getElementsByTagName(key)[0].\ - childNodes[0].nodeValue + if x.tagName == 'object': + for key in ['name', 'hash', 'bytes', 'content_type', + 'last_modified']: + file_item[key] = x.getElementsByTagName(key)[0].\ + childNodes[0].nodeValue + elif x.tagName == 'subdir': + file_item['subdir'] = x.getElementsByTagName( + 'name')[0].childNodes[0].nodeValue + else: + raise ValueError('Found unexpected element %s' + % x.tagName) files.append(file_item) for file_item in files: - file_item['name'] = file_item['name'].encode('utf-8') - file_item['content_type'] = file_item['content_type'].\ - encode('utf-8') - file_item['bytes'] = int(file_item['bytes']) + if 'subdir' in file_item: + file_item['subdir'] = file_item['subdir'].\ + encode('utf-8') + else: + file_item['name'] = file_item['name'].encode('utf-8') + file_item['content_type'] = file_item['content_type'].\ + encode('utf-8') + file_item['bytes'] = int(file_item['bytes']) return files else: content = self.conn.response.read() diff --git a/test/functional/tests.py b/test/functional/tests.py index 29194964d1..693144b59e 100644 --- a/test/functional/tests.py +++ b/test/functional/tests.py @@ -573,13 +573,19 @@ class TestContainer(Base): for format_type in [None, 'json', 'xml']: for prefix in prefixs: - files = cont.files(parms={'prefix': prefix}) + files = cont.files(parms={'prefix': prefix, + 'format': format_type}) + if isinstance(files[0], dict): + files = [x.get('name', x.get('subdir')) for x in files] self.assertEqual(files, sorted(prefix_files[prefix])) for format_type in [None, 'json', 'xml']: for prefix in prefixs: files = cont.files(parms={'limit': limit_count, - 'prefix': prefix}) + 'prefix': prefix, + 'format': format_type}) + if isinstance(files[0], dict): + files = [x.get('name', x.get('subdir')) for x in files] self.assertEqual(len(files), limit_count) for file_item in files: @@ -596,12 +602,24 @@ class TestContainer(Base): file_item = cont.file(f) self.assertTrue(file_item.write_random()) - results = cont.files() - results = cont.files(parms={'delimiter': delimiter}) - self.assertEqual(results, ['test', 'test-']) + for format_type in [None, 'json', 'xml']: + results = cont.files(parms={'format': format_type}) + if isinstance(results[0], dict): + results = [x.get('name', x.get('subdir')) for x in results] + self.assertEqual(results, ['test', 'test-bar', 'test-foo']) - results = cont.files(parms={'delimiter': delimiter, 'reverse': 'yes'}) - self.assertEqual(results, ['test-', 'test']) + results = cont.files(parms={'delimiter': delimiter, + 'format': format_type}) + if isinstance(results[0], dict): + results = [x.get('name', x.get('subdir')) for x in results] + self.assertEqual(results, ['test', 'test-']) + + results = cont.files(parms={'delimiter': delimiter, + 'format': format_type, + 'reverse': 'yes'}) + if isinstance(results[0], dict): + results = [x.get('name', x.get('subdir')) for x in results] + self.assertEqual(results, ['test-', 'test']) def testListDelimiterAndPrefix(self): cont = self.env.account.container(Utils.create_name()) diff --git a/test/unit/common/middleware/crypto/test_decrypter.py b/test/unit/common/middleware/crypto/test_decrypter.py index b70d65029b..d38cdb0950 100644 --- a/test/unit/common/middleware/crypto/test_decrypter.py +++ b/test/unit/common/middleware/crypto/test_decrypter.py @@ -874,6 +874,8 @@ class TestDecrypterContainerRequests(unittest.TestCase): pt_etag2 = 'ac0374ed4d43635f803c82469d0b5a10' key = fetch_crypto_keys()['container'] + subdir = {"subdir": "pseudo-dir/"} + obj_dict_1 = {"bytes": 16, "last_modified": "2015-04-14T23:33:06.439040", "hash": encrypt_and_append_meta( @@ -888,7 +890,7 @@ class TestDecrypterContainerRequests(unittest.TestCase): "name": "testfile2", "content_type": content_type_2} - listing = [obj_dict_1, obj_dict_2] + listing = [subdir, obj_dict_1, obj_dict_2] fake_body = json.dumps(listing) resp = self._make_cont_get_req(fake_body, 'json') @@ -897,11 +899,12 @@ class TestDecrypterContainerRequests(unittest.TestCase): body = resp.body self.assertEqual(len(body), int(resp.headers['Content-Length'])) body_json = json.loads(body) - self.assertEqual(2, len(body_json)) + self.assertEqual(3, len(body_json)) + self.assertDictEqual(subdir, body_json[0]) obj_dict_1['hash'] = pt_etag1 - self.assertDictEqual(obj_dict_1, body_json[0]) + self.assertDictEqual(obj_dict_1, body_json[1]) obj_dict_2['hash'] = pt_etag2 - self.assertDictEqual(obj_dict_2, body_json[1]) + self.assertDictEqual(obj_dict_2, body_json[2]) def test_GET_container_json_with_crypto_override(self): content_type_1 = 'image/jpeg' @@ -958,6 +961,10 @@ class TestDecrypterContainerRequests(unittest.TestCase): self.assertIn("Cipher must be AES_CTR_256", self.decrypter.logger.get_lines_for_level('error')[0]) + def _assert_element(self, name, expected, element): + self.assertEqual(element.tagName, name) + self._assert_element_contains_dict(expected, element) + def _assert_element_contains_dict(self, expected, element): for k, v in expected.items(): entry = element.getElementsByTagName(k) @@ -976,6 +983,7 @@ class TestDecrypterContainerRequests(unittest.TestCase): fake_body = ''' \ +test-subdir\ \ ''' + encrypt_and_append_meta(pt_etag1.encode('utf8'), key) + '''\ \ @@ -1001,21 +1009,23 @@ class TestDecrypterContainerRequests(unittest.TestCase): self.assertEqual('testc', containers[0].attributes.getNamedItem("name").value) - objs = tree.getElementsByTagName('object') - self.assertEqual(2, len(objs)) + results = containers[0].childNodes + self.assertEqual(3, len(results)) + + self._assert_element('subdir', {"name": "test-subdir"}, results[0]) obj_dict_1 = {"bytes": "16", "last_modified": "2015-04-19T02:37:39.601660", "hash": pt_etag1, "name": "testfile", "content_type": content_type_1} - self._assert_element_contains_dict(obj_dict_1, objs[0]) + self._assert_element('object', obj_dict_1, results[1]) obj_dict_2 = {"bytes": "24", "last_modified": "2015-04-19T02:37:39.684740", "hash": pt_etag2, "name": "testfile2", "content_type": content_type_2} - self._assert_element_contains_dict(obj_dict_2, objs[1]) + self._assert_element('object', obj_dict_2, results[2]) def test_GET_container_xml_with_crypto_override(self): content_type_1 = 'image/jpeg'