Add Digest intrinsic function

Digest intrinsic function takes an algorithm name (e.g. md5, sha512,
etc) and a string value and will resolve to the corresponding hash of
the value.

Usage:

    pwd_hash: { digest: ['sha512', { get_param: raw_password }] }

Implements: blueprint digest-intrinsic-function
Change-Id: I7b5332462013cf8063fadec1654fa9f364da45de
This commit is contained in:
Anderson Mesquita 2015-03-12 13:04:09 +10:00 committed by Angus Salkeld
parent 63c942ba11
commit 33f7883c23
3 changed files with 83 additions and 0 deletions

View File

@ -12,6 +12,7 @@
# under the License.
import collections
import hashlib
import itertools
import six
@ -334,3 +335,44 @@ class Repeat(function.Function):
template = function.resolve(self._template)
return [self._do_replacement(keys, items, template)
for items in itertools.product(*lists)]
class Digest(function.Function):
'''
A function for performing digest operations.
Takes the form::
digest:
- <algorithm>
- <value>
Valid algorithms are the ones provided by natively by hashlib (md5, sha1,
sha224, sha256, sha384, and sha512) or any one provided by OpenSSL.
'''
def validate_usage(self, args):
if not (isinstance(args, list) and
all([isinstance(a, six.string_types) for a in args])):
msg = _('Argument to function "%s" must be a list of strings')
raise TypeError(msg % self.fn_name)
if len(args) != 2:
msg = _('Function "%s" usage: ["<algorithm>", "<value>"]')
raise ValueError(msg % self.fn_name)
if args[0].lower() not in hashlib.algorithms:
msg = _('Algorithm must be one of %s')
raise ValueError(msg % six.text_type(hashlib.algorithms))
def digest(self, algorithm, value):
_hash = hashlib.new(algorithm)
_hash.update(value)
return _hash.hexdigest()
def result(self):
args = function.resolve(self.args)
self.validate_usage(args)
return self.digest(*args)

View File

@ -295,6 +295,7 @@ class HOTemplate20141016(HOTemplate20130523):
class HOTemplate20150430(HOTemplate20141016):
functions = {
'digest': hot_funcs.Digest,
'get_attr': hot_funcs.GetAtt,
'get_file': hot_funcs.GetFile,
'get_param': hot_funcs.GetParam,

View File

@ -671,6 +671,46 @@ class HOTemplateTest(common.HeatTestCase):
'for_each': {'%var%': ['a', 'b', 'c']}}}
self.assertRaises(KeyError, self.resolve, snippet, tmpl)
def test_digest(self):
snippet = {'digest': ['md5', 'foobar']}
snippet_resolved = '3858f62230ac3c915f300c664312c63f'
tmpl = parser.Template(hot_kilo_tpl_empty)
self.assertEqual(snippet_resolved, self.resolve(snippet, tmpl))
def test_digest_invalid_types(self):
tmpl = parser.Template(hot_kilo_tpl_empty)
invalid_snippets = [
{'digest': 'invalid'},
{'digest': {'foo': 'invalid'}},
{'digest': [123]},
]
for snippet in invalid_snippets:
exc = self.assertRaises(TypeError, self.resolve, snippet, tmpl)
self.assertIn('must be a list of strings', six.text_type(exc))
def test_digest_incorrect_number_arguments(self):
tmpl = parser.Template(hot_kilo_tpl_empty)
invalid_snippets = [
{'digest': []},
{'digest': ['foo']},
{'digest': ['md5']},
{'digest': ['md5', 'foo', 'bar']},
]
for snippet in invalid_snippets:
exc = self.assertRaises(ValueError, self.resolve, snippet, tmpl)
self.assertIn('usage: ["<algorithm>", "<value>"]',
six.text_type(exc))
def test_digest_invalid_algorithm(self):
tmpl = parser.Template(hot_kilo_tpl_empty)
snippet = {'digest': ['invalid_algorithm', 'foobar']}
exc = self.assertRaises(ValueError, self.resolve, snippet, tmpl)
self.assertIn('Algorithm must be one of', six.text_type(exc))
def test_prevent_parameters_access(self):
"""
Test that the parameters section can't be accessed using the template