diff options
Diffstat (limited to 'passlib/context.py')
-rw-r--r-- | passlib/context.py | 749 |
1 files changed, 54 insertions, 695 deletions
diff --git a/passlib/context.py b/passlib/context.py index bc3cbf5..21865a0 100644 --- a/passlib/context.py +++ b/passlib/context.py @@ -2,8 +2,9 @@ #============================================================================= # imports #============================================================================= -from __future__ import with_statement # core +from configparser import ConfigParser +from io import StringIO import re import logging; log = logging.getLogger(__name__) import threading @@ -19,17 +20,14 @@ from passlib.utils import (handlers as uh, to_bytes, as_bool, timer, rng, getrandstr, ) from passlib.utils.binary import BASE64_CHARS -from passlib.utils.compat import (iteritems, num_types, irange, - PY2, PY3, unicode, SafeConfigParser, - NativeStringIO, BytesIO, - unicode_or_bytes_types, native_string_types, +from passlib.utils.compat import (num_types, + unicode_or_bytes, ) from passlib.utils.decor import deprecated_method, memoized_property # local __all__ = [ 'CryptContext', 'LazyCryptContext', - 'CryptPolicy', ] #============================================================================= @@ -80,508 +78,6 @@ def _always_needs_update(hash, secret=None): _global_settings = set(["truncate_error", "vary_rounds"]) #============================================================================= -# crypt policy -#============================================================================= -_preamble = ("The CryptPolicy class has been deprecated as of " - "Passlib 1.6, and will be removed in Passlib 1.8. ") - -class CryptPolicy(object): - """ - .. deprecated:: 1.6 - This class has been deprecated, and will be removed in Passlib 1.8. - All of its functionality has been rolled into :class:`CryptContext`. - - This class previously stored the configuration options for the - CryptContext class. In the interest of interface simplification, - all of this class' functionality has been rolled into the CryptContext - class itself. - The documentation for this class is now focused on documenting how to - migrate to the new api. Additionally, where possible, the deprecation - warnings issued by the CryptPolicy methods will list the replacement call - that should be used. - - Constructors - ============ - CryptPolicy objects can be constructed directly using any of - the keywords accepted by :class:`CryptContext`. Direct uses of the - :class:`!CryptPolicy` constructor should either pass the keywords - directly into the CryptContext constructor, or to :meth:`CryptContext.update` - if the policy object was being used to update an existing context object. - - In addition to passing in keywords directly, - CryptPolicy objects can be constructed by the following methods: - - .. automethod:: from_path - .. automethod:: from_string - .. automethod:: from_source - .. automethod:: from_sources - .. automethod:: replace - - Introspection - ============= - All of the informational methods provided by this class have been deprecated - by identical or similar methods in the :class:`CryptContext` class: - - .. automethod:: has_schemes - .. automethod:: schemes - .. automethod:: iter_handlers - .. automethod:: get_handler - .. automethod:: get_options - .. automethod:: handler_is_deprecated - .. automethod:: get_min_verify_time - - Exporting - ========= - .. automethod:: iter_config - .. automethod:: to_dict - .. automethod:: to_file - .. automethod:: to_string - - .. note:: - CryptPolicy are immutable. - Use the :meth:`replace` method to mutate existing instances. - - .. deprecated:: 1.6 - """ - #=================================================================== - # class methods - #=================================================================== - @classmethod - def from_path(cls, path, section="passlib", encoding="utf-8"): - """create a CryptPolicy instance from a local file. - - .. deprecated:: 1.6 - - Creating a new CryptContext from a file, which was previously done via - ``CryptContext(policy=CryptPolicy.from_path(path))``, can now be - done via ``CryptContext.from_path(path)``. - See :meth:`CryptContext.from_path` for details. - - Updating an existing CryptContext from a file, which was previously done - ``context.policy = CryptPolicy.from_path(path)``, can now be - done via ``context.load_path(path)``. - See :meth:`CryptContext.load_path` for details. - """ - warn(_preamble + - "Instead of ``CryptPolicy.from_path(path)``, " - "use ``CryptContext.from_path(path)`` " - " or ``context.load_path(path)`` for an existing CryptContext.", - DeprecationWarning, stacklevel=2) - return cls(_internal_context=CryptContext.from_path(path, section, - encoding)) - - @classmethod - def from_string(cls, source, section="passlib", encoding="utf-8"): - """create a CryptPolicy instance from a string. - - .. deprecated:: 1.6 - - Creating a new CryptContext from a string, which was previously done via - ``CryptContext(policy=CryptPolicy.from_string(data))``, can now be - done via ``CryptContext.from_string(data)``. - See :meth:`CryptContext.from_string` for details. - - Updating an existing CryptContext from a string, which was previously done - ``context.policy = CryptPolicy.from_string(data)``, can now be - done via ``context.load(data)``. - See :meth:`CryptContext.load` for details. - """ - warn(_preamble + - "Instead of ``CryptPolicy.from_string(source)``, " - "use ``CryptContext.from_string(source)`` or " - "``context.load(source)`` for an existing CryptContext.", - DeprecationWarning, stacklevel=2) - return cls(_internal_context=CryptContext.from_string(source, section, - encoding)) - - @classmethod - def from_source(cls, source, _warn=True): - """create a CryptPolicy instance from some source. - - this method autodetects the source type, and invokes - the appropriate constructor automatically. it attempts - to detect whether the source is a configuration string, a filepath, - a dictionary, or an existing CryptPolicy instance. - - .. deprecated:: 1.6 - - Create a new CryptContext, which could previously be done via - ``CryptContext(policy=CryptPolicy.from_source(source))``, should - now be done using an explicit method: the :class:`CryptContext` - constructor itself, :meth:`CryptContext.from_path`, - or :meth:`CryptContext.from_string`. - - Updating an existing CryptContext, which could previously be done via - ``context.policy = CryptPolicy.from_source(source)``, should - now be done using an explicit method: :meth:`CryptContext.update`, - or :meth:`CryptContext.load`. - """ - if _warn: - warn(_preamble + - "Instead of ``CryptPolicy.from_source()``, " - "use ``CryptContext.from_string(path)`` " - " or ``CryptContext.from_path(source)``, as appropriate.", - DeprecationWarning, stacklevel=2) - if isinstance(source, CryptPolicy): - return source - elif isinstance(source, dict): - return cls(_internal_context=CryptContext(**source)) - elif not isinstance(source, (bytes,unicode)): - raise TypeError("source must be CryptPolicy, dict, config string, " - "or file path: %r" % (type(source),)) - elif any(c in source for c in "\n\r\t") or not source.strip(" \t./;:"): - return cls(_internal_context=CryptContext.from_string(source)) - else: - return cls(_internal_context=CryptContext.from_path(source)) - - @classmethod - def from_sources(cls, sources, _warn=True): - """create a CryptPolicy instance by merging multiple sources. - - each source is interpreted as by :meth:`from_source`, - and the results are merged together. - - .. deprecated:: 1.6 - Instead of using this method to merge multiple policies together, - a :class:`CryptContext` instance should be created, and then - the multiple sources merged together via :meth:`CryptContext.load`. - """ - if _warn: - warn(_preamble + - "Instead of ``CryptPolicy.from_sources()``, " - "use the various CryptContext constructors " - " followed by ``context.update()``.", - DeprecationWarning, stacklevel=2) - if len(sources) == 0: - raise ValueError("no sources specified") - if len(sources) == 1: - return cls.from_source(sources[0], _warn=False) - kwds = {} - for source in sources: - kwds.update(cls.from_source(source, _warn=False)._context.to_dict(resolve=True)) - return cls(_internal_context=CryptContext(**kwds)) - - def replace(self, *args, **kwds): - """create a new CryptPolicy, optionally updating parts of the - existing configuration. - - .. deprecated:: 1.6 - Callers of this method should :meth:`CryptContext.update` or - :meth:`CryptContext.copy` instead. - """ - if self._stub_policy: - warn(_preamble + # pragma: no cover -- deprecated & unused - "Instead of ``context.policy.replace()``, " - "use ``context.update()`` or ``context.copy()``.", - DeprecationWarning, stacklevel=2) - else: - warn(_preamble + - "Instead of ``CryptPolicy().replace()``, " - "create a CryptContext instance and " - "use ``context.update()`` or ``context.copy()``.", - DeprecationWarning, stacklevel=2) - sources = [ self ] - if args: - sources.extend(args) - if kwds: - sources.append(kwds) - return CryptPolicy.from_sources(sources, _warn=False) - - #=================================================================== - # instance attrs - #=================================================================== - - # internal CryptContext we're wrapping to handle everything - # until this class is removed. - _context = None - - # flag indicating this is wrapper generated by the CryptContext.policy - # attribute, rather than one created independantly by the application. - _stub_policy = False - - #=================================================================== - # init - #=================================================================== - def __init__(self, *args, **kwds): - context = kwds.pop("_internal_context", None) - if context: - assert isinstance(context, CryptContext) - self._context = context - self._stub_policy = kwds.pop("_stub_policy", False) - assert not (args or kwds), "unexpected args: %r %r" % (args,kwds) - else: - if args: - if len(args) != 1: - raise TypeError("only one positional argument accepted") - if kwds: - raise TypeError("cannot specify positional arg and kwds") - kwds = args[0] - warn(_preamble + - "Instead of constructing a CryptPolicy instance, " - "create a CryptContext directly, or use ``context.update()`` " - "and ``context.load()`` to reconfigure existing CryptContext " - "instances.", - DeprecationWarning, stacklevel=2) - self._context = CryptContext(**kwds) - - #=================================================================== - # public interface for examining options - #=================================================================== - def has_schemes(self): - """return True if policy defines *any* schemes for use. - - .. deprecated:: 1.6 - applications should use ``bool(context.schemes())`` instead. - see :meth:`CryptContext.schemes`. - """ - if self._stub_policy: - warn(_preamble + # pragma: no cover -- deprecated & unused - "Instead of ``context.policy.has_schemes()``, " - "use ``bool(context.schemes())``.", - DeprecationWarning, stacklevel=2) - else: - warn(_preamble + - "Instead of ``CryptPolicy().has_schemes()``, " - "create a CryptContext instance and " - "use ``bool(context.schemes())``.", - DeprecationWarning, stacklevel=2) - return bool(self._context.schemes()) - - def iter_handlers(self): - """return iterator over handlers defined in policy. - - .. deprecated:: 1.6 - applications should use ``context.schemes(resolve=True))`` instead. - see :meth:`CryptContext.schemes`. - """ - if self._stub_policy: - warn(_preamble + - "Instead of ``context.policy.iter_handlers()``, " - "use ``context.schemes(resolve=True)``.", - DeprecationWarning, stacklevel=2) - else: - warn(_preamble + - "Instead of ``CryptPolicy().iter_handlers()``, " - "create a CryptContext instance and " - "use ``context.schemes(resolve=True)``.", - DeprecationWarning, stacklevel=2) - return self._context.schemes(resolve=True, unconfigured=True) - - def schemes(self, resolve=False): - """return list of schemes defined in policy. - - .. deprecated:: 1.6 - applications should use :meth:`CryptContext.schemes` instead. - """ - if self._stub_policy: - warn(_preamble + # pragma: no cover -- deprecated & unused - "Instead of ``context.policy.schemes()``, " - "use ``context.schemes()``.", - DeprecationWarning, stacklevel=2) - else: - warn(_preamble + - "Instead of ``CryptPolicy().schemes()``, " - "create a CryptContext instance and " - "use ``context.schemes()``.", - DeprecationWarning, stacklevel=2) - return list(self._context.schemes(resolve=resolve, unconfigured=True)) - - def get_handler(self, name=None, category=None, required=False): - """return handler as specified by name, or default handler. - - .. deprecated:: 1.6 - applications should use :meth:`CryptContext.handler` instead, - though note that the ``required`` keyword has been removed, - and the new method will always act as if ``required=True``. - """ - if self._stub_policy: - warn(_preamble + - "Instead of ``context.policy.get_handler()``, " - "use ``context.handler()``.", - DeprecationWarning, stacklevel=2) - else: - warn(_preamble + - "Instead of ``CryptPolicy().get_handler()``, " - "create a CryptContext instance and " - "use ``context.handler()``.", - DeprecationWarning, stacklevel=2) - # CryptContext.handler() doesn't support required=False, - # so wrapping it in try/except - try: - return self._context.handler(name, category, unconfigured=True) - except KeyError: - if required: - raise - else: - return None - - def get_min_verify_time(self, category=None): - """get min_verify_time setting for policy. - - .. deprecated:: 1.6 - min_verify_time option will be removed entirely in passlib 1.8 - - .. versionchanged:: 1.7 - this method now always returns the value automatically - calculated by :meth:`CryptContext.min_verify_time`, - any value specified by policy is ignored. - """ - warn("get_min_verify_time() and min_verify_time option is deprecated and ignored, " - "and will be removed in Passlib 1.8", DeprecationWarning, - stacklevel=2) - return 0 - - def get_options(self, name, category=None): - """return dictionary of options specific to a given handler. - - .. deprecated:: 1.6 - this method has no direct replacement in the 1.6 api, as there - is not a clearly defined use-case. however, examining the output of - :meth:`CryptContext.to_dict` should serve as the closest alternative. - """ - # XXX: might make a public replacement, but need more study of the use cases. - if self._stub_policy: - warn(_preamble + # pragma: no cover -- deprecated & unused - "``context.policy.get_options()`` will no longer be available.", - DeprecationWarning, stacklevel=2) - else: - warn(_preamble + - "``CryptPolicy().get_options()`` will no longer be available.", - DeprecationWarning, stacklevel=2) - if hasattr(name, "name"): - name = name.name - return self._context._config._get_record_options_with_flag(name, category)[0] - - def handler_is_deprecated(self, name, category=None): - """check if handler has been deprecated by policy. - - .. deprecated:: 1.6 - this method has no direct replacement in the 1.6 api, as there - is not a clearly defined use-case. however, examining the output of - :meth:`CryptContext.to_dict` should serve as the closest alternative. - """ - # XXX: might make a public replacement, but need more study of the use cases. - if self._stub_policy: - warn(_preamble + - "``context.policy.handler_is_deprecated()`` will no longer be available.", - DeprecationWarning, stacklevel=2) - else: - warn(_preamble + - "``CryptPolicy().handler_is_deprecated()`` will no longer be available.", - DeprecationWarning, stacklevel=2) - if hasattr(name, "name"): - name = name.name - return self._context.handler(name, category).deprecated - - #=================================================================== - # serialization - #=================================================================== - - def iter_config(self, ini=False, resolve=False): - """iterate over key/value pairs representing the policy object. - - .. deprecated:: 1.6 - applications should use :meth:`CryptContext.to_dict` instead. - """ - if self._stub_policy: - warn(_preamble + # pragma: no cover -- deprecated & unused - "Instead of ``context.policy.iter_config()``, " - "use ``context.to_dict().items()``.", - DeprecationWarning, stacklevel=2) - else: - warn(_preamble + - "Instead of ``CryptPolicy().iter_config()``, " - "create a CryptContext instance and " - "use ``context.to_dict().items()``.", - DeprecationWarning, stacklevel=2) - # hacked code that renders keys & values in manner that approximates - # old behavior. context.to_dict() is much cleaner. - context = self._context - if ini: - def render_key(key): - return context._render_config_key(key).replace("__", ".") - def render_value(value): - if isinstance(value, (list,tuple)): - value = ", ".join(value) - return value - resolve = False - else: - render_key = context._render_config_key - render_value = lambda value: value - return ( - (render_key(key), render_value(value)) - for key, value in context._config.iter_config(resolve) - ) - - def to_dict(self, resolve=False): - """export policy object as dictionary of options. - - .. deprecated:: 1.6 - applications should use :meth:`CryptContext.to_dict` instead. - """ - if self._stub_policy: - warn(_preamble + - "Instead of ``context.policy.to_dict()``, " - "use ``context.to_dict()``.", - DeprecationWarning, stacklevel=2) - else: - warn(_preamble + - "Instead of ``CryptPolicy().to_dict()``, " - "create a CryptContext instance and " - "use ``context.to_dict()``.", - DeprecationWarning, stacklevel=2) - return self._context.to_dict(resolve) - - def to_file(self, stream, section="passlib"): # pragma: no cover -- deprecated & unused - """export policy to file. - - .. deprecated:: 1.6 - applications should use :meth:`CryptContext.to_string` instead, - and then write the output to a file as desired. - """ - if self._stub_policy: - warn(_preamble + - "Instead of ``context.policy.to_file(stream)``, " - "use ``stream.write(context.to_string())``.", - DeprecationWarning, stacklevel=2) - else: - warn(_preamble + - "Instead of ``CryptPolicy().to_file(stream)``, " - "create a CryptContext instance and " - "use ``stream.write(context.to_string())``.", - DeprecationWarning, stacklevel=2) - out = self._context.to_string(section=section) - if PY2: - out = out.encode("utf-8") - stream.write(out) - - def to_string(self, section="passlib", encoding=None): - """export policy to file. - - .. deprecated:: 1.6 - applications should use :meth:`CryptContext.to_string` instead. - """ - if self._stub_policy: - warn(_preamble + # pragma: no cover -- deprecated & unused - "Instead of ``context.policy.to_string()``, " - "use ``context.to_string()``.", - DeprecationWarning, stacklevel=2) - else: - warn(_preamble + - "Instead of ``CryptPolicy().to_string()``, " - "create a CryptContext instance and " - "use ``context.to_string()``.", - DeprecationWarning, stacklevel=2) - out = self._context.to_string(section=section) - if encoding: - out = out.encode(encoding) - return out - - #=================================================================== - # eoc - #=================================================================== - -#============================================================================= # _CryptConfig helper class #============================================================================= class _CryptConfig(object): @@ -641,7 +137,7 @@ class _CryptConfig(object): """initialize .handlers and .schemes attributes""" handlers = [] schemes = [] - if isinstance(data, native_string_types): + if isinstance(data, str): data = splitcomma(data) for elem in data or (): # resolve elem -> handler & scheme @@ -649,7 +145,7 @@ class _CryptConfig(object): handler = elem scheme = handler.name _validate_handler_name(scheme) - elif isinstance(elem, native_string_types): + elif isinstance(elem, str): handler = get_crypt_handler(elem) scheme = handler.name else: @@ -687,7 +183,7 @@ class _CryptConfig(object): categories = set() # load source config into internal storage - for (cat, scheme, key), value in iteritems(source): + for (cat, scheme, key), value in source.items(): categories.add(cat) explicit_scheme = scheme if not cat and not scheme and key in _global_settings: @@ -732,8 +228,6 @@ class _CryptConfig(object): raise KeyError("'schemes' context option is not allowed " "per category") key, value = norm_context_option(cat, key, value) - if key == "min_verify_time": # ignored in 1.7, to be removed in 1.8 - continue # store in context_options # map structure: context_options[key][category] = value @@ -754,7 +248,7 @@ class _CryptConfig(object): raise KeyError("%r option not allowed in CryptContext " "configuration" % (key,)) # coerce strings for certain fields (e.g. min_rounds uses ints) - if isinstance(value, native_string_types): + if isinstance(value, str): func = _coerce_scheme_options.get(key) if func: value = func(value) @@ -765,12 +259,12 @@ class _CryptConfig(object): if key == "default": if hasattr(value, "name"): value = value.name - elif not isinstance(value, native_string_types): + elif not isinstance(value, str): raise ExpectedTypeError(value, "str", "default") if schemes and value not in schemes: raise KeyError("default scheme not found in policy") elif key == "deprecated": - if isinstance(value, native_string_types): + if isinstance(value, str): value = splitcomma(value) elif not isinstance(value, (list,tuple)): raise ExpectedTypeError(value, "str or seq", "deprecated") @@ -783,19 +277,11 @@ class _CryptConfig(object): elif schemes: # make sure list of deprecated schemes is subset of configured schemes for scheme in value: - if not isinstance(scheme, native_string_types): + if not isinstance(scheme, str): raise ExpectedTypeError(value, "str", "deprecated element") if scheme not in schemes: raise KeyError("deprecated scheme not found " "in policy: %r" % (scheme,)) - elif key == "min_verify_time": - warn("'min_verify_time' was deprecated in Passlib 1.6, is " - "ignored in 1.7, and will be removed in 1.8", - DeprecationWarning) - elif key == "harden_verify": - warn("'harden_verify' is deprecated & ignored as of Passlib 1.7.1, " - " and will be removed in 1.8", - DeprecationWarning) elif key != "schemes": raise KeyError("unknown CryptContext keyword: %r" % (key,)) return key, value @@ -1059,12 +545,9 @@ class _CryptConfig(object): pass # type check - if category is not None and not isinstance(category, native_string_types): - if PY2 and isinstance(category, unicode): - # for compatibility with unicode-centric py2 apps - return self.get_record(scheme, category.encode("utf-8")) + if category is not None and not isinstance(category, str): raise ExpectedTypeError(category, "str or None", "category") - if scheme is not None and not isinstance(scheme, native_string_types): + if scheme is not None and not isinstance(scheme, str): raise ExpectedTypeError(scheme, "str or None", "scheme") # if scheme=None, @@ -1118,7 +601,7 @@ class _CryptConfig(object): # unique identifiers will work properly in a CryptContext. # XXX: if all handlers have a unique prefix (e.g. all are MCF / LDAP), # could use dict-lookup to speed up this search. - if not isinstance(hash, unicode_or_bytes_types): + if not isinstance(hash, unicode_or_bytes): raise ExpectedStringError(hash, "hash") # type check of category - handled by _get_record_list() for record in self._get_record_list(category): @@ -1255,7 +738,7 @@ class CryptContext(object): def from_string(cls, source, section="passlib", encoding="utf-8"): """create new CryptContext instance from an INI-formatted string. - :type source: unicode or bytes + :type source: str or bytes :arg source: string containing INI-formatted content. @@ -1284,8 +767,8 @@ class CryptContext(object): .. seealso:: :meth:`to_string`, the inverse of this constructor. """ - if not isinstance(source, unicode_or_bytes_types): - raise ExpectedTypeError(source, "unicode or bytes", "source") + if not isinstance(source, unicode_or_bytes): + raise ExpectedTypeError(source, "str or bytes", "source") self = cls(_autoload=False) self.load(source, section=section, encoding=encoding) return self @@ -1346,7 +829,7 @@ class CryptContext(object): .. versionadded:: 1.6 This method was previously named :meth:`!replace`. That alias - has been deprecated, and will be removed in Passlib 1.8. + was removed in Passlib 1.8. .. seealso:: :meth:`update` """ @@ -1365,100 +848,39 @@ class CryptContext(object): """ return self.copy(**kwds) - def replace(self, **kwds): - """deprecated alias of :meth:`copy`""" - warn("CryptContext().replace() has been deprecated in Passlib 1.6, " - "and will be removed in Passlib 1.8, " - "it has been renamed to CryptContext().copy()", - DeprecationWarning, stacklevel=2) - return self.copy(**kwds) - #=================================================================== # init #=================================================================== def __init__(self, schemes=None, # keyword only... - policy=_UNSET, # <-- deprecated _autoload=True, **kwds): # XXX: add ability to make flag certain contexts as immutable, # e.g. the builtin passlib ones? # XXX: add a name or import path for the contexts, to help out repr? if schemes is not None: kwds['schemes'] = schemes - if policy is not _UNSET: - warn("The CryptContext ``policy`` keyword has been deprecated as of Passlib 1.6, " - "and will be removed in Passlib 1.8; please use " - "``CryptContext.from_string()` or " - "``CryptContext.from_path()`` instead.", - DeprecationWarning) - if policy is None: - self.load(kwds) - elif isinstance(policy, CryptPolicy): - self.load(policy._context) - self.update(kwds) - else: - raise TypeError("policy must be a CryptPolicy instance") - elif _autoload: + if _autoload: self.load(kwds) else: assert not kwds, "_autoload=False and kwds are mutually exclusive" # XXX: would this be useful? ##def __str__(self): - ## if PY3: - ## return self.to_string() - ## else: - ## return self.to_string().encode("utf-8") + ## return self.to_string() def __repr__(self): return "<CryptContext at 0x%0x>" % id(self) #=================================================================== - # deprecated policy object - #=================================================================== - def _get_policy(self): - # The CryptPolicy class has been deprecated, so to support any - # legacy accesses, we create a stub policy object so .policy attr - # will continue to work. - # - # the code waits until app accesses a specific policy object attribute - # before issuing deprecation warning, so developer gets method-specific - # suggestion for how to upgrade. - - # NOTE: making a copy of the context so the policy acts like a snapshot, - # to retain the pre-1.6 behavior. - return CryptPolicy(_internal_context=self.copy(), _stub_policy=True) - - def _set_policy(self, policy): - warn("The CryptPolicy class and the ``context.policy`` attribute have " - "been deprecated as of Passlib 1.6, and will be removed in " - "Passlib 1.8; please use the ``context.load()`` and " - "``context.update()`` methods instead.", - DeprecationWarning, stacklevel=2) - if isinstance(policy, CryptPolicy): - self.load(policy._context) - else: - raise TypeError("expected CryptPolicy instance") - - policy = property(_get_policy, _set_policy, - doc="[deprecated] returns CryptPolicy instance " - "tied to this CryptContext") - - #=================================================================== # loading / updating configuration #=================================================================== @staticmethod def _parse_ini_stream(stream, section, filename): """helper read INI from stream, extract passlib section as dict""" - # NOTE: this expects a unicode stream under py3, - # and a utf-8 bytes stream under py2, - # allowing the resulting dict to always use native strings. - p = SafeConfigParser() - if PY3: - # python 3.2 deprecated readfp in favor of read_file - p.read_file(stream, filename) - else: - p.readfp(stream, filename) + # NOTE: this expects a unicode stream, + # and resulting dict will always use native strings. + p = ConfigParser() + p.read_file(stream, filename) # XXX: could change load() to accept list of items, # and skip intermediate dict creation return dict(p.items(section)) @@ -1474,22 +896,9 @@ class CryptContext(object): .. versionadded:: 1.6 """ - def helper(stream): + with open(path, "rt", encoding=encoding) as stream: kwds = self._parse_ini_stream(stream, section, path) - return self.load(kwds, update=update) - if PY3: - # decode to unicode, which load() expected under py3 - with open(path, "rt", encoding=encoding) as stream: - return helper(stream) - elif encoding in ["utf-8", "ascii"]: - # keep as utf-8 bytes, which load() expects under py2 - with open(path, "rb") as stream: - return helper(stream) - else: - # transcode to utf-8 bytes - with open(path, "rb") as fh: - tmp = fh.read().decode(encoding).encode("utf-8") - return helper(BytesIO(tmp)) + return self.load(kwds, update=update) def load(self, source, update=False, section="passlib", encoding="utf-8"): """Load new configuration into CryptContext, replacing existing config. @@ -1503,7 +912,7 @@ class CryptContext(object): the key/value pairs will be interpreted the same keywords for the :class:`CryptContext` class constructor. - * a :class:`!unicode` or :class:`!bytes` string + * a :class:`!str` or :class:`!bytes` string this will be interpreted as an INI-formatted file, and appropriate key/value pairs will be loaded from @@ -1556,13 +965,9 @@ class CryptContext(object): # autodetect source type, convert to dict #----------------------------------------------------------- parse_keys = True - if isinstance(source, unicode_or_bytes_types): - if PY3: - source = to_unicode(source, encoding, param="source") - else: - source = to_bytes(source, "utf-8", source_encoding=encoding, - param="source") - source = self._parse_ini_stream(NativeStringIO(source), section, + if isinstance(source, unicode_or_bytes): + source = to_unicode(source, encoding, param="source") + source = self._parse_ini_stream(StringIO(source), section, "<string passed to CryptContext.load()>") elif isinstance(source, CryptContext): # extract dict directly from config, so it can be merged later @@ -1580,8 +985,7 @@ class CryptContext(object): #----------------------------------------------------------- if parse_keys: parse = self._parse_config_key - source = dict((parse(key), value) - for key, value in iteritems(source)) + source = dict((parse(key), value) for key, value in source.items()) if update and self._config is not None: # if updating, do nothing if source is empty, if not source: @@ -1610,7 +1014,7 @@ class CryptContext(object): def _parse_config_key(ckey): """helper used to parse ``cat__scheme__option`` keys into a tuple""" # split string into 1-3 parts - assert isinstance(ckey, native_string_types) + assert isinstance(ckey, str) parts = ckey.replace(".", "__").split("__") count = len(parts) if count == 1: @@ -1669,8 +1073,7 @@ class CryptContext(object): ## return ## ## def strip_items(target, filter): - ## keys = [key for key,value in iteritems(target) - ## if filter(key,value)] + ## keys = [key for key,value in target.items() if filter(key,value)] ## for key in keys: ## del target[key] ## @@ -1695,7 +1098,7 @@ class CryptContext(object): ## strip_items(deprecated, lambda k,v: k and v==cur) ## ## # remove redundant category options. - ## for scheme, config in iteritems(scheme_options): + ## for scheme, config in scheme_options.items(): ## if None in config: ## cur = config[None] ## strip_items(config, lambda k,v: k and v==cur) @@ -1881,7 +1284,7 @@ class CryptContext(object): else: value = str(value) - assert isinstance(value, native_string_types), \ + assert isinstance(value, str), \ "expected string for key: %r %r" % (key, value) # escape any percent signs. @@ -1958,9 +1361,9 @@ class CryptContext(object): .. seealso:: the :ref:`context-serialization-example` example in the tutorial. """ - parser = SafeConfigParser() + parser = ConfigParser() self._write_to_parser(parser, section) - buf = NativeStringIO() + buf = StringIO() parser.write(buf) unregistered = self._get_unregistered_handlers() if unregistered: @@ -1968,10 +1371,7 @@ class CryptContext(object): "# NOTE: the %s handler(s) are not registered with Passlib,\n" "# this string may not correctly reproduce the current configuration.\n\n" ) % ", ".join(repr(handler.name) for handler in unregistered)) - out = buf.getvalue() - if not PY3: - out = out.decode("utf-8") - return out + return buf.getvalue() # XXX: is this useful enough to enable? ##def write_to_path(self, path, section="passlib", update=False): @@ -1987,23 +1387,6 @@ class CryptContext(object): ## fh.close() #=================================================================== - # verify() hardening - # NOTE: this entire feature has been disabled. - # all contents of this section are NOOPs as of 1.7.1, - # and will be removed in 1.8. - #=================================================================== - - mvt_estimate_max_samples = 20 - mvt_estimate_min_samples = 10 - mvt_estimate_max_time = 2 - mvt_estimate_resolution = 0.01 - harden_verify = None - min_verify_time = 0 - - def reset_min_verify_time(self): - self._reset_dummy_verify() - - #=================================================================== # password hash api #=================================================================== @@ -2023,7 +1406,7 @@ class CryptContext(object): def _get_or_identify_record(self, hash, scheme=None, category=None): """return record based on scheme, or failing that, by identifying hash""" if scheme: - if not isinstance(hash, unicode_or_bytes_types): + if not isinstance(hash, unicode_or_bytes): raise ExpectedStringError(hash, "hash") return self._get_record(scheme, category) else: @@ -2060,7 +1443,7 @@ class CryptContext(object): If so, the password should be re-hashed using :meth:`hash` Otherwise, it will return ``False``. - :type hash: unicode or bytes + :type hash: str or bytes :arg hash: The hash string to examine. @@ -2084,7 +1467,7 @@ class CryptContext(object): be used when determining if the hash needs to be updated (e.g. is below the minimum rounds). - :type secret: unicode, bytes, or None + :type secret: str, bytes, or None :param secret: Optional secret associated with the provided ``hash``. This is not required, or even currently used for anything... @@ -2166,7 +1549,7 @@ class CryptContext(object): All registered algorithms will be checked, from first to last, and whichever one positively identifies the hash first will be returned. - :type hash: unicode or bytes + :type hash: str or bytes :arg hash: The hash string to test. @@ -2204,7 +1587,7 @@ class CryptContext(object): def hash(self, secret, scheme=None, category=None, **kwds): """run secret through selected algorithm, returning resulting hash. - :type secret: unicode or bytes + :type secret: str or bytes :arg secret: the password to hash. @@ -2243,7 +1626,7 @@ class CryptContext(object): .. seealso:: the :ref:`context-basic-example` example in the tutorial """ - # XXX: could insert normalization to preferred unicode encoding here + # XXX: could insert normalization to preferred str encoding here if scheme is not None: # TODO: offer replacement alternative. # ``context.handler(scheme).hash()`` would work, @@ -2277,11 +1660,11 @@ class CryptContext(object): (limited to the schemes configured for this context). It will then check whether the password verifies against the hash. - :type secret: unicode or bytes + :type secret: str or bytes :arg secret: the secret to verify - :type hash: unicode or bytes + :type hash: str or bytes :arg hash: hash string to compare to @@ -2357,11 +1740,11 @@ class CryptContext(object): which wish to update deprecated hashes, and this call takes care of all 3 steps efficiently. - :type secret: unicode or bytes + :type secret: str or bytes :arg secret: the secret to verify - :type secret: unicode or bytes + :type secret: str or bytes :arg hash: hash string to compare to. @@ -2458,7 +1841,7 @@ class CryptContext(object): """ type(self)._dummy_hash.clear_cache(self) - def dummy_verify(self, elapsed=0): + def dummy_verify(self): """ Helper that applications can call when user wasn't found, in order to simulate time it would take to hash a password. @@ -2466,12 +1849,6 @@ class CryptContext(object): Runs verify() against a dummy hash, to simulate verification of a real account password. - :param elapsed: - - .. deprecated:: 1.7.1 - - this option is ignored, and will be removed in passlib 1.8. - .. versionadded:: 1.7 """ self.verify(self._dummy_secret, self._dummy_hash) @@ -2567,12 +1944,6 @@ class LazyCryptContext(CryptContext): .. versionadded:: 1.6 - :param create_policy: - - .. deprecated:: 1.6 - This option will be removed in Passlib 1.8, - applications should use ``onload`` instead. - :param kwds: All additional keywords are passed to CryptContext; @@ -2596,11 +1967,9 @@ class LazyCryptContext(CryptContext): """ _lazy_kwds = None - # NOTE: the way this class works changed in 1.6. - # previously it just called _lazy_init() when ``.policy`` was - # first accessed. now that is done whenever any of the public - # attributes are accessed, and the class itself is changed - # to a regular CryptContext, to remove the overhead once it's unneeded. + # NOTE: the way this class works is that whenever any of the public + # attributes are accessed, _lazy_init() is invoked, the class itself is changed + # to a regular CryptContext (to remove the overhead once it's unneeded) def __init__(self, schemes=None, **kwds): if schemes is not None: @@ -2609,21 +1978,11 @@ class LazyCryptContext(CryptContext): def _lazy_init(self): kwds = self._lazy_kwds - if 'create_policy' in kwds: - warn("The CryptPolicy class, and LazyCryptContext's " - "``create_policy`` keyword have been deprecated as of " - "Passlib 1.6, and will be removed in Passlib 1.8; " - "please use the ``onload`` keyword instead.", - DeprecationWarning) - create_policy = kwds.pop("create_policy") - result = create_policy(**kwds) - policy = CryptPolicy.from_source(result, _warn=False) - kwds = policy._context.to_dict() - elif 'onload' in kwds: + if 'onload' in kwds: onload = kwds.pop("onload") kwds = onload(**kwds) del self._lazy_kwds - super(LazyCryptContext, self).__init__(**kwds) + super().__init__(**kwds) self.__class__ = CryptContext def __getattribute__(self, attr): |