summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEli Collins <elic@assurancetechnologies.com>2012-04-27 01:51:17 -0400
committerEli Collins <elic@assurancetechnologies.com>2012-04-27 01:51:17 -0400
commited21857d8a616fda97836a519ce353896c9fed69 (patch)
tree5bb1d08750183191ac49414216fc23913e69ddea
parent84d84151a162ad4bac5a9261aff94d0076529177 (diff)
downloadpasslib-ed21857d8a616fda97836a519ce353896c9fed69.tar.gz
added deprecated="auto" support to CryptContext
-rw-r--r--CHANGES4
-rw-r--r--docs/lib/passlib.context-options.rst3
-rw-r--r--passlib/context.py34
-rw-r--r--passlib/tests/test_context.py32
4 files changed, 67 insertions, 6 deletions
diff --git a/CHANGES b/CHANGES
index 7cfab56..a9b3d23 100644
--- a/CHANGES
+++ b/CHANGES
@@ -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
#=========================================================