diff options
author | Eli Collins <elic@assurancetechnologies.com> | 2012-04-27 01:51:17 -0400 |
---|---|---|
committer | Eli Collins <elic@assurancetechnologies.com> | 2012-04-27 01:51:17 -0400 |
commit | ed21857d8a616fda97836a519ce353896c9fed69 (patch) | |
tree | 5bb1d08750183191ac49414216fc23913e69ddea | |
parent | 84d84151a162ad4bac5a9261aff94d0076529177 (diff) | |
download | passlib-ed21857d8a616fda97836a519ce353896c9fed69.tar.gz |
added deprecated="auto" support to CryptContext
-rw-r--r-- | CHANGES | 4 | ||||
-rw-r--r-- | docs/lib/passlib.context-options.rst | 3 | ||||
-rw-r--r-- | passlib/context.py | 34 | ||||
-rw-r--r-- | passlib/tests/test_context.py | 32 |
4 files changed, 67 insertions, 6 deletions
@@ -72,6 +72,10 @@ Release History :ref:`min_verify_time <min-verify-time>` has been deprecated, will be ignored in release 1.7, and will be removed in release 1.8. + * The :class:`!CryptContext` option ``deprecated`` can now be set + to the special string ``"auto"``, in which case all schemes + except for the default will be automatically flagged as deprecated. + * The internals of :class:`!CryptContext` have been rewritten drastically. It's methods should now be stricter and more informative about invalid values; and common :class:`!CryptContext` operations diff --git a/docs/lib/passlib.context-options.rst b/docs/lib/passlib.context-options.rst index 52146a4..c354832 100644 --- a/docs/lib/passlib.context-options.rst +++ b/docs/lib/passlib.context-options.rst @@ -53,6 +53,9 @@ of the :class:`!CryptContext` instance itself: This is primarily used by :meth:`CryptContext.hash_needs_update`. If the application does not use this method, this option can be ignored. + If ``deprecated`` is set to ``"auto"``, all schemes which are not the default + will be deprecated. + Example: ``deprecated=["des_crypt"]``. ``default`` diff --git a/passlib/context.py b/passlib/context.py index 57b1646..c41c836 100644 --- a/passlib/context.py +++ b/passlib/context.py @@ -1531,7 +1531,12 @@ class CryptContext(object): for scheme in value: if not isinstance(scheme, str): raise ExpectedTypeError(value, "str", "deprecated element") - if scheme not in schemes: + if scheme in schemes: + continue + elif scheme == "auto": + if len(value) > 1: + raise ValueError("cannot list other schemes if ``deprecated=['auto']`` is used") + else: raise KeyError("deprecated scheme not found " "in policy: %r" % (scheme,)) # TODO: make sure there's at least one non-deprecated scheme. @@ -1721,18 +1726,35 @@ class CryptContext(object): has_cat_options = True # add deprecated flag + # XXX: this logic is now a mess thanks to 'auto' mode. + # a preprocessing pass up in _load() would probably + # simplify this logic quite a bit. dep_map = self._deprecated_schemes if dep_map: deplist = dep_map.get(None) - dep = (deplist is not None and scheme in deplist) + flag = False + if deplist: + if scheme in deplist: + flag = True + elif 'auto' in deplist: + default_scheme = self.default_scheme(None) + if category: + cat_default_scheme = self.default_scheme(category) + if scheme != cat_default_scheme: + flag = True + if default_scheme != cat_default_scheme: + has_cat_options = True + elif scheme != default_scheme: + flag = True if category: deplist = dep_map.get(category) if deplist is not None: - value = (scheme in deplist) - if value != dep: - dep = value + alt_flag = (scheme in deplist or ('auto' in deplist and + scheme != self.default_scheme(category))) + if alt_flag != flag: + flag = alt_flag has_cat_options = True - if dep: + if flag: kwds['deprecated'] = True # add min_verify_time setting diff --git a/passlib/tests/test_context.py b/passlib/tests/test_context.py index 20b6dc6..598457d 100644 --- a/passlib/tests/test_context.py +++ b/passlib/tests/test_context.py @@ -1266,6 +1266,38 @@ sha512_crypt__min_rounds = 45000 self.assertAlmostEqual(elapsed, max_delay, delta=delta) self.consumeWarningList(wlog, ".*verify exceeded min_verify_time") + def test_61_autodeprecate(self): + "test deprecated='auto' is handled correctly" + + def getstate(ctx, category=None): + return [ctx._is_deprecated_scheme(scheme, category) for scheme in ctx.schemes()] + + # correctly reports default + ctx = CryptContext("sha256_crypt,md5_crypt,des_crypt", deprecated="auto") + self.assertEqual(getstate(ctx, None), [False, True, True]) + self.assertEqual(getstate(ctx, "admin"), [False, True, True]) + + # correctly reports changed default + ctx.update(default="md5_crypt") + self.assertEqual(getstate(ctx, None), [True, False, True]) + self.assertEqual(getstate(ctx, "admin"), [True, False, True]) + + # category default is handled correctly + ctx.update(admin__context__default="des_crypt") + self.assertEqual(getstate(ctx, None), [True, False, True]) + self.assertEqual(getstate(ctx, "admin"), [True, True, False]) + + # handles 1 scheme + ctx = CryptContext(["sha256_crypt"], deprecated="auto") + self.assertEqual(getstate(ctx, None), [False]) + self.assertEqual(getstate(ctx, "admin"), [False]) + + # disallow auto & other deprecated schemes at same time. + self.assertRaises(ValueError, CryptContext, "sha256_crypt,md5_crypt", + deprecated="auto,md5_crypt") + self.assertRaises(ValueError, CryptContext, "sha256_crypt,md5_crypt", + deprecated="md5_crypt,auto") + #========================================================= # handler deprecation detectors #========================================================= |