diff options
author | Amrith Kumar <amrith@tesora.com> | 2015-12-14 15:25:53 -0500 |
---|---|---|
committer | Amrith Kumar <amrith@tesora.com> | 2016-01-05 14:04:09 -0500 |
commit | 077fee4d439a6ecfa95e4e5626e4c5a3f9e7a72d (patch) | |
tree | 377f3656742c6e24c832005b78488139f1aa34d3 /oslo_utils/strutils.py | |
parent | e46a46ba90741987f1147afc56876e3d0d27e8a2 (diff) | |
download | oslo-utils-077fee4d439a6ecfa95e4e5626e4c5a3f9e7a72d.tar.gz |
Add a mechanism to mask passwords in dictionaries
The widely used mask_password() function will mask passwords in
strings using a list of keys and patterns. This change extends it to
work on dictionaries as well. This allows one to directly invoke
mask_dict_passwords() on a dictionary and this will have the
effect of masking not only the strings (values) but also mask the
values if the keys in the dictionary are part of the list of
sanitization keys.
If the dictionary contains nested dictionaries, those will be
recursively masked as well.
Change-Id: I7ebafdeb671da36e0fdc9d6983a17ac5481b6f28
Closes-Bug: 1526041
Diffstat (limited to 'oslo_utils/strutils.py')
-rw-r--r-- | oslo_utils/strutils.py | 73 |
1 files changed, 73 insertions, 0 deletions
diff --git a/oslo_utils/strutils.py b/oslo_utils/strutils.py index 342b398..af55d9d 100644 --- a/oslo_utils/strutils.py +++ b/oslo_utils/strutils.py @@ -287,6 +287,79 @@ def mask_password(message, secret="***"): # nosec return message +def mask_dict_password(dictionary, secret="***"): # nosec + """Replace password with *secret* in a dictionary recursively. + + :param dictionary: The dictionary which includes secret information. + :param secret: value with which to replace secret information. + :returns: The dictionary with string substitutions. + + A dictionary (which may contain nested dictionaries) contains + information (such as passwords) which should not be revealed, and + this function helps detect and replace those with the 'secret' + provided (or '***' if none is provided). + + Substitution is performed in one of three situations: + + If the key is something that is considered to be indicative of a + secret, then the corresponding value is replaced with the secret + provided (or '***' if none is provided). + + If a value in the dictionary is a string, then it is masked + using the mask_password() function. + + Finally, if a value is a dictionary, this function will + recursively mask that dictionary as well. + + For example: + + >>> mask_dict_password({'password': 'd81juxmEW_', + >>> 'user': 'admin', + >>> 'home-dir': '/home/admin'}, + >>> '???') + {'password': '???', 'user': 'admin', 'home-dir': '/home/admin'} + + For example (the value is masked using mask_password()) + + >>> mask_dict_password({'password': '--password d81juxmEW_', + >>> 'user': 'admin', + >>> 'home-dir': '/home/admin'}, + >>> '???') + {'password': '--password ???', 'user': 'admin', + 'home-dir': '/home/admin'} + + + For example (a nested dictionary is masked): + + >>> mask_dict_password({"nested": {'password': 'd81juxmEW_', + >>> 'user': 'admin', + >>> 'home': '/home/admin'}}, + >>> '???') + {"nested": {'password': '???', 'user': 'admin', 'home': '/home/admin'}} + + .. versionadded:: 3.4 + + """ + + if not isinstance(dictionary, dict): + raise TypeError("Expected a dictionary, got %s instead." + % type(dictionary)) + + out = {} + + for k, v in dictionary.items(): + if isinstance(v, dict): + v = mask_dict_password(v, secret=secret) + elif k in _SANITIZE_KEYS: + v = secret + elif isinstance(v, six.string_types): + v = mask_password(v, secret=secret) + + out[k] = v + + return out + + def is_int_like(val): """Check if a value looks like an integer with base 10. |