summaryrefslogtreecommitdiff
path: root/passlib/context.py
diff options
context:
space:
mode:
Diffstat (limited to 'passlib/context.py')
-rw-r--r--passlib/context.py749
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):