From a673ee2d56ec8bc989673a8ef884d2566abf72d1 Mon Sep 17 00:00:00 2001 From: Crag Wolfe Date: Sun, 28 Aug 2016 20:33:20 -0400 Subject: [PATCH] Refactor, add encrypt/decrypt data dict functions to crypt Just a refactor, no change in functionality. The functions added to crypt are used to encrypt / decrypt resource properties data dicts. Note that they should not be used for encrypting / decrypting other things such as params or user creds (which are just strings). An intermediate json conversion of each value in a dict takes place before it is encrypted/decrypted. Change-Id: Id6bcc90cbf430095719315ac7e9d3e8c9e745012 --- heat/common/crypt.py | 26 ++++++++++++++++++++++++++ heat/objects/resource.py | 16 +++------------- heat/tests/test_crypt.py | 24 ++++++++++++++++++++++++ 3 files changed, 53 insertions(+), 13 deletions(-) diff --git a/heat/common/crypt.py b/heat/common/crypt.py index a4b7a9b53a..d5619dff0d 100644 --- a/heat/common/crypt.py +++ b/heat/common/crypt.py @@ -17,6 +17,7 @@ import sys from Crypto.Cipher import AES from cryptography import fernet from oslo_config import cfg +from oslo_serialization import jsonutils from oslo_utils import encodeutils from oslo_utils import importutils @@ -92,6 +93,31 @@ def decrypt(method, data, encryption_key=None): return encodeutils.safe_decode(value, 'utf-8') +def encrypted_dict(data, encryption_key=None): + 'Return an encrypted dict. Values converted to json before encrypted' + return_data = {} + if not data: + return return_data + for prop_name, prop_value in data.items(): + prop_string = jsonutils.dumps(prop_value) + encrypted_value = encrypt(prop_string, encryption_key) + return_data[prop_name] = encrypted_value + return return_data + + +def decrypted_dict(data, encryption_key=None): + 'Return a decrypted dict. Assume input values are encrypted json fields.' + return_data = {} + if not data: + return return_data + for prop_name, prop_value in data.items(): + method, value = prop_value + decrypted_value = decrypt(method, value, encryption_key) + prop_string = jsonutils.loads(decrypted_value) + return_data[prop_name] = prop_string + return return_data + + def oslo_decrypt_v1(value, encryption_key=None): encryption_key = get_valid_encryption_key(encryption_key) sym = SymmetricCrypto() diff --git a/heat/objects/resource.py b/heat/objects/resource.py index d95f54acae..0c9b34b03e 100644 --- a/heat/objects/resource.py +++ b/heat/objects/resource.py @@ -18,7 +18,6 @@ import collections from oslo_config import cfg -from oslo_serialization import jsonutils from oslo_versionedobjects import base from oslo_versionedobjects import fields import six @@ -104,13 +103,8 @@ class Resource( resource[field] = db_resource[field] if resource.properties_data_encrypted and resource.properties_data: - properties_data = {} - for prop_name, prop_value in resource.properties_data.items(): - method, value = prop_value - decrypted_value = crypt.decrypt(method, value) - prop_string = jsonutils.loads(decrypted_value) - properties_data[prop_name] = prop_string - resource.properties_data = properties_data + decrypted_data = crypt.decrypted_dict(resource.properties_data) + resource.properties_data = decrypted_data resource._context = context resource.obj_reset_changes() @@ -237,11 +231,7 @@ class Resource( @staticmethod def encrypt_properties_data(data): if cfg.CONF.encrypt_parameters_and_properties and data: - result = {} - for prop_name, prop_value in data.items(): - prop_string = jsonutils.dumps(prop_value) - encrypted_value = crypt.encrypt(prop_string) - result[prop_name] = encrypted_value + result = crypt.encrypted_dict(data) return (True, result) return (False, data) diff --git a/heat/tests/test_crypt.py b/heat/tests/test_crypt.py index 98542b6837..b0097e2971 100644 --- a/heat/tests/test_crypt.py +++ b/heat/tests/test_crypt.py @@ -37,3 +37,27 @@ class CryptTest(common.HeatTestCase): exp_msg = ('heat.conf misconfigured, auth_encryption_key ' 'must be 32 characters') self.assertIn(exp_msg, six.text_type(err)) + + def _test_encrypt_decrypt_dict(self, encryption_key=None): + data = {'p1': u'happy', + '2': [u'a', u'little', u'blue'], + 'p3': {u'really': u'exited', u'ok int': 9}, + '4': u'', + 'p5': True, + '6': 7} + encrypted_data = crypt.encrypted_dict(data, encryption_key) + for k in encrypted_data: + self.assertEqual('cryptography_decrypt_v1', + encrypted_data[k][0]) + self.assertEqual(2, len(encrypted_data[k])) + # the keys remain the same + self.assertEqual(set(data), set(encrypted_data)) + + decrypted_data = crypt.decrypted_dict(encrypted_data, encryption_key) + self.assertEqual(data, decrypted_data) + + def test_encrypt_decrypt_dict_custom_enc_key(self): + self._test_encrypt_decrypt_dict('just for testing not so great re') + + def test_encrypt_decrypt_dict_default_enc_key(self): + self._test_encrypt_decrypt_dict()