Check for missing/unexpected data in encryption utility

Closes-Bug: #1559302
Change-Id: I753c3e697fdaa4a282d9808035b0e3745914d153
This commit is contained in:
Jason Dunsmore 2016-03-16 16:10:48 -05:00
parent fe57943380
commit 2009e57b4a
2 changed files with 114 additions and 19 deletions

View File

@ -1233,11 +1233,13 @@ def db_encrypt_parameters_and_properties(ctxt, encryption_key, batch_size=50):
:param batch_size: number of templates requested from db in each iteration. :param batch_size: number of templates requested from db in each iteration.
50 means that heat requests 50 templates, encrypt them 50 means that heat requests 50 templates, encrypt them
and proceed with next 50 items. and proceed with next 50 items.
:return: list of exceptions encountered during encryption
""" """
from heat.engine import template from heat.engine import template
session = get_session() session = get_session()
with session.begin(): with session.begin():
query = session.query(models.RawTemplate) query = session.query(models.RawTemplate)
excs = []
for raw_template in _get_batch( for raw_template in _get_batch(
session=session, ctxt=ctxt, query=query, session=session, ctxt=ctxt, query=query,
model=models.RawTemplate, batch_size=batch_size): model=models.RawTemplate, batch_size=batch_size):
@ -1247,15 +1249,21 @@ def db_encrypt_parameters_and_properties(ctxt, encryption_key, batch_size=50):
param_schemata = tmpl.param_schemata() param_schemata = tmpl.param_schemata()
env = raw_template.environment env = raw_template.environment
if (not env or
'parameters' not in env or
not tmpl.param_schemata()):
continue
if 'encrypted_param_names' in env: if 'encrypted_param_names' in env:
encrypted_params = env['encrypted_param_names'] encrypted_params = env['encrypted_param_names']
else: else:
encrypted_params = [] encrypted_params = []
for param_name, param_val in env['parameters'].items(): for param_name, param_val in env['parameters'].items():
if ((param_name in encrypted_params) or if ((param_name in encrypted_params) or
(not param_schemata[param_name].hidden)): (not param_schemata[param_name].hidden)):
continue continue
encrypted_val = crypt.encrypt(param_val, encryption_key) encrypted_val = crypt.encrypt(six.text_type(param_val),
encryption_key)
env['parameters'][param_name] = encrypted_val env['parameters'][param_name] = encrypted_val
encrypted_params.append(param_name) encrypted_params.append(param_name)
@ -1264,9 +1272,10 @@ def db_encrypt_parameters_and_properties(ctxt, encryption_key, batch_size=50):
environment['encrypted_param_names'] = encrypted_params environment['encrypted_param_names'] = encrypted_params
raw_template_update(ctxt, raw_template.id, raw_template_update(ctxt, raw_template.id,
{'environment': environment}) {'environment': environment})
except Exception: except Exception as exc:
LOG.exception(_LE('Failed to encrypt parameters of raw ' LOG.exception(_LE('Failed to encrypt parameters of raw '
'template %(id)d'), {'id': raw_template.id}) 'template %(id)d'), {'id': raw_template.id})
excs.append(exc)
continue continue
query = session.query(models.Resource).filter( query = session.query(models.Resource).filter(
@ -1277,6 +1286,8 @@ def db_encrypt_parameters_and_properties(ctxt, encryption_key, batch_size=50):
batch_size=batch_size): batch_size=batch_size):
try: try:
result = {} result = {}
if not resource.properties_data:
continue
for prop_name, prop_value in resource.properties_data.items(): for prop_name, prop_value in resource.properties_data.items():
prop_string = jsonutils.dumps(prop_value) prop_string = jsonutils.dumps(prop_value)
encrypted_value = crypt.encrypt(prop_string, encrypted_value = crypt.encrypt(prop_string,
@ -1288,10 +1299,12 @@ def db_encrypt_parameters_and_properties(ctxt, encryption_key, batch_size=50):
{'properties_data': result, {'properties_data': result,
'properties_data_encrypted': True}, 'properties_data_encrypted': True},
resource.atomic_key) resource.atomic_key)
except Exception: except Exception as exc:
LOG.exception(_LE('Failed to encrypt properties_data of ' LOG.exception(_LE('Failed to encrypt properties_data of '
'resource %(id)d'), {'id': resource.id}) 'resource %(id)d'), {'id': resource.id})
excs.append(exc)
continue continue
return excs
def db_decrypt_parameters_and_properties(ctxt, encryption_key, batch_size=50): def db_decrypt_parameters_and_properties(ctxt, encryption_key, batch_size=50):
@ -1303,8 +1316,10 @@ def db_decrypt_parameters_and_properties(ctxt, encryption_key, batch_size=50):
:param batch_size: number of templates requested from db in each iteration. :param batch_size: number of templates requested from db in each iteration.
50 means that heat requests 50 templates, encrypt them 50 means that heat requests 50 templates, encrypt them
and proceed with next 50 items. and proceed with next 50 items.
:return: list of exceptions encountered during decryption
""" """
session = get_session() session = get_session()
excs = []
with session.begin(): with session.begin():
query = session.query(models.RawTemplate) query = session.query(models.RawTemplate)
for raw_template in _get_batch( for raw_template in _get_batch(
@ -1324,9 +1339,10 @@ def db_decrypt_parameters_and_properties(ctxt, encryption_key, batch_size=50):
environment['encrypted_param_names'] = [] environment['encrypted_param_names'] = []
raw_template_update(ctxt, raw_template.id, raw_template_update(ctxt, raw_template.id,
{'environment': environment}) {'environment': environment})
except Exception: except Exception as exc:
LOG.exception(_LE('Failed to decrypt parameters of raw ' LOG.exception(_LE('Failed to decrypt parameters of raw '
'template %(id)d'), {'id': raw_template.id}) 'template %(id)d'), {'id': raw_template.id})
excs.append(exc)
continue continue
query = session.query(models.Resource).filter( query = session.query(models.Resource).filter(
@ -1349,10 +1365,12 @@ def db_decrypt_parameters_and_properties(ctxt, encryption_key, batch_size=50):
{'properties_data': result, {'properties_data': result,
'properties_data_encrypted': False}, 'properties_data_encrypted': False},
resource.atomic_key) resource.atomic_key)
except Exception: except Exception as exc:
LOG.exception(_LE('Failed to decrypt properties_data of ' LOG.exception(_LE('Failed to decrypt properties_data of '
'resource %(id)d'), {'id': resource.id}) 'resource %(id)d'), {'id': resource.id})
excs.append(exc)
continue continue
return excs
def _get_batch(session, ctxt, query, model, batch_size=50): def _get_batch(session, ctxt, query, model, batch_size=50):

View File

@ -2928,7 +2928,7 @@ class DBAPICryptParamsPropsTest(common.HeatTestCase):
def _create_template(self): def _create_template(self):
"""Initialize sample template.""" """Initialize sample template."""
t = template_format.parse(''' self.t = template_format.parse('''
heat_template_version: 2013-05-23 heat_template_version: 2013-05-23
parameters: parameters:
param1: param1:
@ -2973,7 +2973,7 @@ class DBAPICryptParamsPropsTest(common.HeatTestCase):
type: GenericResourceType type: GenericResourceType
''') ''')
template = { template = {
'template': t, 'template': self.t,
'files': {'foo': 'bar'}, 'files': {'foo': 'bar'},
'environment': { 'environment': {
'parameters': { 'parameters': {
@ -2996,7 +2996,7 @@ class DBAPICryptParamsPropsTest(common.HeatTestCase):
'param_comma_list': '[\"Hola\", \"Senor\"]'} 'param_comma_list': '[\"Hola\", \"Senor\"]'}
raw_templates = session.query(models.RawTemplate).all() raw_templates = session.query(models.RawTemplate).all()
self.assertTrue(raw_templates) self.assertNotEqual([], raw_templates)
for r_tmpl in raw_templates: for r_tmpl in raw_templates:
for param_name, param_value in hidden_params_dict.items(): for param_name, param_value in hidden_params_dict.items():
self.assertEqual(param_value, self.assertEqual(param_value,
@ -3004,18 +3004,18 @@ class DBAPICryptParamsPropsTest(common.HeatTestCase):
self.assertEqual('foo', self.assertEqual('foo',
r_tmpl.environment['parameters']['param1']) r_tmpl.environment['parameters']['param1'])
resources = session.query(models.Resource).all() resources = session.query(models.Resource).all()
self.assertTrue(resources) self.assertNotEqual([], resources)
for resource in resources: for resource in resources:
self.assertEqual('bar1', resource.properties_data['foo1']) self.assertEqual('bar1', resource.properties_data['foo1'])
def encrypt(enc_key=None): def encrypt(enc_key=None):
if enc_key is None: if enc_key is None:
enc_key = cfg.CONF.auth_encryption_key enc_key = cfg.CONF.auth_encryption_key
db_api.db_encrypt_parameters_and_properties( self.assertEqual([], db_api.db_encrypt_parameters_and_properties(
self.ctx, enc_key, batch_size=batch_size) self.ctx, enc_key, batch_size=batch_size))
session = db_api.get_session() session = db_api.get_session()
enc_raw_templates = session.query(models.RawTemplate).all() enc_raw_templates = session.query(models.RawTemplate).all()
self.assertTrue(enc_raw_templates) self.assertNotEqual([], enc_raw_templates)
for enc_tmpl in enc_raw_templates: for enc_tmpl in enc_raw_templates:
for param_name in hidden_params_dict.keys(): for param_name in hidden_params_dict.keys():
self.assertEqual( self.assertEqual(
@ -3029,7 +3029,7 @@ class DBAPICryptParamsPropsTest(common.HeatTestCase):
encrypt_value = enc_tmpl.environment['parameters']['param2'][1] encrypt_value = enc_tmpl.environment['parameters']['param2'][1]
enc_resources = session.query(models.Resource).all() enc_resources = session.query(models.Resource).all()
self.assertTrue(enc_resources) self.assertNotEqual([], enc_resources)
for enc_prop in enc_resources: for enc_prop in enc_resources:
self.assertEqual('cryptography_decrypt_v1', self.assertEqual('cryptography_decrypt_v1',
enc_prop.properties_data['foo1'][0]) enc_prop.properties_data['foo1'][0])
@ -3045,11 +3045,11 @@ class DBAPICryptParamsPropsTest(common.HeatTestCase):
def decrypt(enc_key=None): def decrypt(enc_key=None):
if enc_key is None: if enc_key is None:
enc_key = cfg.CONF.auth_encryption_key enc_key = cfg.CONF.auth_encryption_key
db_api.db_decrypt_parameters_and_properties( self.assertEqual([], db_api.db_decrypt_parameters_and_properties(
self.ctx, enc_key, batch_size=batch_size) self.ctx, enc_key, batch_size=batch_size))
session = db_api.get_session() session = db_api.get_session()
dec_templates = session.query(models.RawTemplate).all() dec_templates = session.query(models.RawTemplate).all()
self.assertTrue(dec_templates) self.assertNotEqual([], dec_templates)
for dec_tmpl in dec_templates: for dec_tmpl in dec_templates:
self.assertNotEqual( self.assertNotEqual(
encrypt_value, encrypt_value,
@ -3073,7 +3073,7 @@ class DBAPICryptParamsPropsTest(common.HeatTestCase):
decrypt_value = dec_tmpl.environment['parameters']['param2'][1] decrypt_value = dec_tmpl.environment['parameters']['param2'][1]
dec_resources = session.query(models.Resource).all() dec_resources = session.query(models.Resource).all()
self.assertTrue(dec_resources) self.assertNotEqual([], dec_resources)
for dec_prop in dec_resources: for dec_prop in dec_resources:
self.assertEqual('bar1', dec_prop.properties_data['foo1']) self.assertEqual('bar1', dec_prop.properties_data['foo1'])
return decrypt_value return decrypt_value
@ -3152,8 +3152,10 @@ class DBAPICryptParamsPropsTest(common.HeatTestCase):
self.assertEqual('', r_tmpls[1].environment) self.assertEqual('', r_tmpls[1].environment)
# Test encryption # Test encryption
db_api.db_encrypt_parameters_and_properties( enc_result = db_api.db_encrypt_parameters_and_properties(
self.ctx, cfg.CONF.auth_encryption_key, batch_size=50) self.ctx, cfg.CONF.auth_encryption_key, batch_size=50)
self.assertEqual(1, len(enc_result))
self.assertIs(AttributeError, type(enc_result[0]))
session = db_api.get_session() session = db_api.get_session()
enc_tmpls = session.query(models.RawTemplate).all() enc_tmpls = session.query(models.RawTemplate).all()
self.assertEqual('', enc_tmpls[1].environment) self.assertEqual('', enc_tmpls[1].environment)
@ -3161,10 +3163,85 @@ class DBAPICryptParamsPropsTest(common.HeatTestCase):
enc_tmpls[2].environment['parameters']['param2'][0]) enc_tmpls[2].environment['parameters']['param2'][0])
# Test decryption # Test decryption
db_api.db_decrypt_parameters_and_properties( dec_result = db_api.db_decrypt_parameters_and_properties(
self.ctx, cfg.CONF.auth_encryption_key, batch_size=50) self.ctx, cfg.CONF.auth_encryption_key, batch_size=50)
self.assertEqual(len(dec_result), 1)
self.assertIs(TypeError, type(dec_result[0]))
session = db_api.get_session() session = db_api.get_session()
dec_tmpls = session.query(models.RawTemplate).all() dec_tmpls = session.query(models.RawTemplate).all()
self.assertEqual('', dec_tmpls[1].environment) self.assertEqual('', dec_tmpls[1].environment)
self.assertEqual('bar', self.assertEqual('bar',
dec_tmpls[2].environment['parameters']['param2']) dec_tmpls[2].environment['parameters']['param2'])
def test_db_encrypt_no_env(self):
template = {
'template': self.t,
'files': {'foo': 'bar'},
'environment': None}
db_api.raw_template_create(self.ctx, template)
self.assertEqual([], db_api.db_encrypt_parameters_and_properties(
self.ctx, cfg.CONF.auth_encryption_key))
def test_db_encrypt_no_env_parameters(self):
template = {
'template': self.t,
'files': {'foo': 'bar'},
'environment': {'encrypted_param_names': ['a']}}
db_api.raw_template_create(self.ctx, template)
self.assertEqual([], db_api.db_encrypt_parameters_and_properties(
self.ctx, cfg.CONF.auth_encryption_key))
def test_db_encrypt_no_properties_data(self):
ctx = utils.dummy_context()
template = self._create_template()
user_creds = create_user_creds(ctx)
stack = create_stack(ctx, template, user_creds)
resources = [create_resource(ctx, stack, name='res1')]
resources[0].properties_data = None
self.assertEqual([], db_api.db_encrypt_parameters_and_properties(
ctx, cfg.CONF.auth_encryption_key))
def test_db_encrypt_non_string_param_type(self):
t = template_format.parse('''
heat_template_version: 2013-05-23
parameters:
param1:
type: string
description: value1.
param2:
type: string
description: value2.
hidden: true
param3:
type: string
description: value3
hidden: true
default: 1234
resources:
a_resource:
type: GenericResourceType
''')
template = {
'template': t,
'files': {},
'environment': {'parameters': {
'param1': 'foo',
'param2': 'bar',
'param3': 12345}}}
db_api.raw_template_create(self.ctx, template)
self.assertEqual([], db_api.db_encrypt_parameters_and_properties(
self.ctx, cfg.CONF.auth_encryption_key))
session = db_api.get_session()
enc_raw_templates = session.query(models.RawTemplate).all()
self.assertNotEqual([], enc_raw_templates)
enc_params = enc_raw_templates[1].environment['parameters']
self.assertEqual([], db_api.db_decrypt_parameters_and_properties(
self.ctx, cfg.CONF.auth_encryption_key, batch_size=50))
session = db_api.get_session()
dec_tmpls = session.query(models.RawTemplate).all()
dec_params = dec_tmpls[1].environment['parameters']
self.assertNotEqual(enc_params['param3'], dec_params['param3'])
self.assertEqual('bar', dec_params['param2'])
self.assertEqual('12345', dec_params['param3'])