summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEli Collins <elic@assurancetechnologies.com>2019-11-12 13:46:59 -0500
committerEli Collins <elic@assurancetechnologies.com>2019-11-12 13:46:59 -0500
commit346f6994e4ddb30fbb85f922e2745cb9f58a217d (patch)
tree68eb4c2912f01806ffcf6ffaf9000f733e2b54c9
parent1d9f7f3b058fbaf78c3c7895ad51cb8fb99dbc0c (diff)
downloadpasslib-346f6994e4ddb30fbb85f922e2745cb9f58a217d.tar.gz
passlib.utils.handlers: split _sanitize() helper out as separate mask_value() function;
adjusted UTs
-rw-r--r--passlib/tests/utils.py16
-rw-r--r--passlib/utils/handlers.py49
2 files changed, 48 insertions, 17 deletions
diff --git a/passlib/tests/utils.py b/passlib/tests/utils.py
index a62006f..f509a9d 100644
--- a/passlib/tests/utils.py
+++ b/passlib/tests/utils.py
@@ -2529,10 +2529,24 @@ class HandlerCase(TestCase):
for key in ("salt", "checksum"):
if key in result3:
self.assertNotEqual(result3[key], correct3[key])
- self.assertTrue(result3[key].endswith("**"), "%r is masked" % result3[key])
+ self.assert_is_masked(result3[key])
correct3[key] = result3[key]
self.assertEqual(result3, correct3)
+ def assert_is_masked(self, value):
+ """
+ check value properly masked by :func:`passlib.utils.mask_value`
+ """
+ if value is None:
+ return
+ self.assertIsInstance(value, unicode)
+ # assumes mask_value() defaults will never show more than <show> chars (4);
+ # and show nothing if size less than 1/<pct> (8).
+ ref = value if len(value) < 8 else value[4:]
+ if set(ref) == set(["*"]):
+ return True
+ raise self.fail("value not masked: %r" % value)
+
def test_71_parsehash_results(self):
"""
parsehash() -- known outputs
diff --git a/passlib/utils/handlers.py b/passlib/utils/handlers.py
index bb6f224..4c3b4b7 100644
--- a/passlib/utils/handlers.py
+++ b/passlib/utils/handlers.py
@@ -315,6 +315,38 @@ def render_mc3(ident, rounds, salt, checksum, sep=u("$"), rounds_base=10):
parts = [ident, rounds, sep, salt]
return uascii_to_str(join_unicode(parts))
+
+def mask_value(value, show=4, pct=0.125, char=u"*"):
+ """
+ helper to mask contents of sensitive field.
+
+ :param value:
+ raw value (str, bytes, etc)
+
+ :param show:
+ max # of characters to remain visible
+
+ :param pct:
+ don't show more than this % of input.
+
+ :param char:
+ character to use for masking
+
+ :rtype: str | None
+ """
+ if value is None:
+ return None
+ if not isinstance(value, unicode):
+ if isinstance(value, bytes):
+ from passlib.utils.binary import ab64_encode
+ value = ab64_encode(value).decode("ascii")
+ else:
+ value = unicode(value)
+ size = len(value)
+ show = min(show, int(size * pct))
+ return value[:show] + char * (size - show)
+
+
#=============================================================================
# parameter helpers
#=============================================================================
@@ -831,21 +863,6 @@ class GenericHandler(MinimalHandler):
"""
return tuple(key for key in cls.setting_kwds if key not in cls._unparsed_settings)
- # XXX: make this a global function?
- @staticmethod
- def _sanitize(value, char=u("*")):
- """default method to obscure sensitive fields"""
- if value is None:
- return None
- if isinstance(value, bytes):
- from passlib.utils.binary import ab64_encode
- value = ab64_encode(value).decode("ascii")
- elif not isinstance(value, unicode):
- value = unicode(value)
- size = len(value)
- clip = min(4, size//8)
- return value[:clip] + char * (size-clip)
-
@classmethod
def parsehash(cls, hash, checksum=True, sanitize=False):
"""[experimental method] parse hash into dictionary of settings.
@@ -880,7 +897,7 @@ class GenericHandler(MinimalHandler):
kwds['checksum'] = self.checksum
if sanitize:
if sanitize is True:
- sanitize = cls._sanitize
+ sanitize = mask_value
for key in cls._unsafe_settings:
if key in kwds:
kwds[key] = sanitize(kwds[key])