summaryrefslogtreecommitdiff
path: root/passlib/context.py
diff options
context:
space:
mode:
authorEli Collins <elic@assurancetechnologies.com>2016-06-29 10:53:29 -0400
committerEli Collins <elic@assurancetechnologies.com>2016-06-29 10:53:29 -0400
commit165283cc679059ed16dbf38897bbe07f7ed163c3 (patch)
tree6c2da8650350a84862df2406e7d9c82967be3f1d /passlib/context.py
parentea62ff70d48b8d30f17b6f3cd7187158fc6999b5 (diff)
downloadpasslib-165283cc679059ed16dbf38897bbe07f7ed163c3.tar.gz
CryptContext: improved handling of global options to be passed to handlers
* now have whitelist of allowed global settings which will be passed to all hashes. all other bare options will be passed to CryptContext itself. * this makes the "all" scheme warnings a bit cleaner -- using the scheme explicitly will now ALWAYS create a warning, and any non-whitelisted global hash setting will also create a warning. * get_scheme_options_with_flag() now filters out any global settings which don't apply to a particular handler; simplifies a HasRounds workaround previously in _create_record(), and makes things more rugged.
Diffstat (limited to 'passlib/context.py')
-rw-r--r--passlib/context.py73
1 files changed, 46 insertions, 27 deletions
diff --git a/passlib/context.py b/passlib/context.py
index b2759f9..c0f656c 100644
--- a/passlib/context.py
+++ b/passlib/context.py
@@ -11,11 +11,14 @@ from warnings import warn
# pkg
from passlib.exc import ExpectedStringError, ExpectedTypeError, PasslibConfigWarning
from passlib.registry import get_crypt_handler, _validate_handler_name
-from passlib.utils import handlers as uh, to_bytes, deprecated_method, \
- to_unicode, splitcomma
-from passlib.utils.compat import iteritems, num_types, \
- PY2, PY3, unicode, SafeConfigParser, \
- NativeStringIO, BytesIO, unicode_or_bytes_types, native_string_types
+from passlib.utils import (handlers as uh, to_bytes, deprecated_method,
+ to_unicode, splitcomma, memoized_property,
+ )
+from passlib.utils.compat import (iteritems, num_types,
+ PY2, PY3, unicode, SafeConfigParser,
+ NativeStringIO, BytesIO,
+ unicode_or_bytes_types, native_string_types,
+ )
# local
__all__ = [
'CryptContext',
@@ -67,8 +70,8 @@ def _always_needs_update(hash, secret=None):
"""
return True
-#: list of keys allowed under wildcard "all" scheme w/o a security warning
-_global_safe_options = ["vary_rounds"]
+#: list of keys allowed under wildcard "all" scheme w/o a security warning.
+_global_settings = set(["vary_rounds"])
#=============================================================================
# crypt policy
@@ -678,18 +681,24 @@ class _CryptConfig(object):
# load source config into internal storage
for (cat, scheme, key), value in iteritems(source):
categories.add(cat)
+ explicit_scheme = scheme
+ if not cat and not scheme and key in _global_settings:
+ # going forward, not using "<cat>__all__<key>" format. instead...
+ # whitelisting set of keys which should be passed to (all) schemes,
+ # rather than passed to the CryptContext itself
+ scheme = "all"
if scheme:
# normalize scheme option
key, value = norm_scheme_option(key, value)
+ # e.g. things like "min_rounds" should never be set cross-scheme
+ if scheme == "all" and key not in _global_settings:
+ warn("The '%s' option should be configured per-algorithm, and not set "
+ "globally in the context" % (key,), PasslibConfigWarning)
+
# this scheme is going away in 2.0;
# but most keys deserve an extra warning since it impacts security.
- if scheme == "all":
- if key not in _global_safe_options:
- # e.g. things like "min_rounds" should never be set cross-scheme
- warn("The '%s' option should be configured per-algorithm, and not set "
- "globally using the 'all' scheme" % (key,), PasslibConfigWarning)
-
+ if explicit_scheme == "all":
warn("The 'all' scheme is deprecated as of Passlib 1.7, "
"and will be removed in Passlib 2.0; Please configure "
"options on a per-algorithm basis.", DeprecationWarning)
@@ -818,6 +827,18 @@ class _CryptConfig(object):
except KeyError:
return default
+ def get_base_handler(self, scheme):
+ return self.handlers[self.schemes.index(scheme)]
+
+ @staticmethod
+ def expand_settings(handler):
+ setting_kwds = handler.setting_kwds
+ if 'rounds' in handler.setting_kwds:
+ # XXX: historically this extras won't be listed in setting_kwds
+ setting_kwds += uh.HasRounds.using_rounds_kwds
+ return setting_kwds
+
+ # NOTE: this is only used by _get_record_options_with_flag()...
def get_scheme_options_with_flag(self, scheme, category):
"""return composite dict of all options set for scheme.
includes options inherited from 'all' and from default category.
@@ -834,6 +855,14 @@ class _CryptConfig(object):
defkwds = kwds.copy() # <-- used to detect category-specific options
kwds.update(get_optionmap("all", category))
+ # filter out global settings not supported by handler
+ allowed_settings = self.expand_settings(self.get_base_handler(scheme))
+ for key in set(kwds).difference(allowed_settings):
+ kwds.pop(key)
+ if category:
+ for key in set(defkwds).difference(allowed_settings):
+ defkwds.pop(key)
+
# add in default options for scheme
other = get_optionmap(scheme, None)
kwds.update(other)
@@ -936,17 +965,15 @@ class _CryptConfig(object):
# so CryptContext throws error immediately rather than later.
self._record_lists = {}
records = self._records = {}
- context_kwds = self.context_kwds = set()
+ all_context_kwds = self.context_kwds = set()
get_options = self._get_record_options_with_flag
- categories = self.categories
+ categories = (None,) + self.categories
for handler in self.handlers:
scheme = handler.name
- context_kwds.update(handler.context_kwds)
- kwds, _ = get_options(scheme, None)
- records[scheme, None] = self._create_record(handler, **kwds)
+ all_context_kwds.update(handler.context_kwds)
for cat in categories:
kwds, has_cat_options = get_options(scheme, cat)
- if has_cat_options:
+ if cat is None or has_cat_options:
records[scheme, cat] = self._create_record(handler, cat, **kwds)
# NOTE: if handler has no category-specific opts, get_record()
# will automatically use the default category's record.
@@ -955,14 +982,6 @@ class _CryptConfig(object):
@staticmethod
def _create_record(handler, category=None, deprecated=False, **settings):
- # historically, configs may specify generic default rounds.
- # stripping those out for hashes w/o a rounds parameter,
- # but need to discourage this situation in the future.
- # TODO: once 'all' prefix support is removed (Passlib 2.0), this can be stripped out
- if 'rounds' not in handler.setting_kwds:
- for key in uh.HasRounds.using_rounds_kwds:
- settings.pop(key, None)
-
# create custom handler if needed.
try:
# XXX: relaxed=True is mostly here to retain backwards-compat behavior.