diff options
author | Eli Collins <elic@assurancetechnologies.com> | 2019-11-12 13:46:59 -0500 |
---|---|---|
committer | Eli Collins <elic@assurancetechnologies.com> | 2019-11-12 13:46:59 -0500 |
commit | 346f6994e4ddb30fbb85f922e2745cb9f58a217d (patch) | |
tree | 68eb4c2912f01806ffcf6ffaf9000f733e2b54c9 | |
parent | 1d9f7f3b058fbaf78c3c7895ad51cb8fb99dbc0c (diff) | |
download | passlib-346f6994e4ddb30fbb85f922e2745cb9f58a217d.tar.gz |
passlib.utils.handlers: split _sanitize() helper out as separate mask_value() function;
adjusted UTs
-rw-r--r-- | passlib/tests/utils.py | 16 | ||||
-rw-r--r-- | passlib/utils/handlers.py | 49 |
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]) |