You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					2638 lines
				
				107 KiB
			
		
		
			
		
	
	
					2638 lines
				
				107 KiB
			| 
								 
											3 years ago
										 
									 | 
							
								"""passlib.context - CryptContext implementation"""
							 | 
						||
| 
								 | 
							
								#=============================================================================
							 | 
						||
| 
								 | 
							
								# imports
							 | 
						||
| 
								 | 
							
								#=============================================================================
							 | 
						||
| 
								 | 
							
								from __future__ import with_statement
							 | 
						||
| 
								 | 
							
								# core
							 | 
						||
| 
								 | 
							
								import re
							 | 
						||
| 
								 | 
							
								import logging; log = logging.getLogger(__name__)
							 | 
						||
| 
								 | 
							
								import threading
							 | 
						||
| 
								 | 
							
								import time
							 | 
						||
| 
								 | 
							
								from warnings import warn
							 | 
						||
| 
								 | 
							
								# site
							 | 
						||
| 
								 | 
							
								# pkg
							 | 
						||
| 
								 | 
							
								from passlib import exc
							 | 
						||
| 
								 | 
							
								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,
							 | 
						||
| 
								 | 
							
								                           to_unicode, splitcomma,
							 | 
						||
| 
								 | 
							
								                           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.decor import deprecated_method, memoized_property
							 | 
						||
| 
								 | 
							
								# local
							 | 
						||
| 
								 | 
							
								__all__ = [
							 | 
						||
| 
								 | 
							
								    'CryptContext',
							 | 
						||
| 
								 | 
							
								    'LazyCryptContext',
							 | 
						||
| 
								 | 
							
								    'CryptPolicy',
							 | 
						||
| 
								 | 
							
								]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#=============================================================================
							 | 
						||
| 
								 | 
							
								# support
							 | 
						||
| 
								 | 
							
								#=============================================================================
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								# private object to detect unset params
							 | 
						||
| 
								 | 
							
								_UNSET = object()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def _coerce_vary_rounds(value):
							 | 
						||
| 
								 | 
							
								    """parse vary_rounds string to percent as [0,1) float, or integer"""
							 | 
						||
| 
								 | 
							
								    if value.endswith("%"):
							 | 
						||
| 
								 | 
							
								        # XXX: deprecate this in favor of raw float?
							 | 
						||
| 
								 | 
							
								        return float(value.rstrip("%"))*.01
							 | 
						||
| 
								 | 
							
								    try:
							 | 
						||
| 
								 | 
							
								        return int(value)
							 | 
						||
| 
								 | 
							
								    except ValueError:
							 | 
						||
| 
								 | 
							
								        return float(value)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								# set of options which aren't allowed to be set via policy
							 | 
						||
| 
								 | 
							
								_forbidden_scheme_options = set(["salt"])
							 | 
						||
| 
								 | 
							
								    # 'salt' - not allowed since a fixed salt would defeat the purpose.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								# dict containing funcs used to coerce strings to correct type for scheme option keys.
							 | 
						||
| 
								 | 
							
								# NOTE: this isn't really needed any longer, since Handler.using() handles the actual parsing.
							 | 
						||
| 
								 | 
							
								#       keeping this around for now, though, since it makes context.to_dict() output cleaner.
							 | 
						||
| 
								 | 
							
								_coerce_scheme_options = dict(
							 | 
						||
| 
								 | 
							
								    min_rounds=int,
							 | 
						||
| 
								 | 
							
								    max_rounds=int,
							 | 
						||
| 
								 | 
							
								    default_rounds=int,
							 | 
						||
| 
								 | 
							
								    vary_rounds=_coerce_vary_rounds,
							 | 
						||
| 
								 | 
							
								    salt_size=int,
							 | 
						||
| 
								 | 
							
								)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def _is_handler_registered(handler):
							 | 
						||
| 
								 | 
							
								    """detect if handler is registered or a custom handler"""
							 | 
						||
| 
								 | 
							
								    return get_crypt_handler(handler.name, None) is handler
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								@staticmethod
							 | 
						||
| 
								 | 
							
								def _always_needs_update(hash, secret=None):
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    dummy function patched into handler.needs_update() by _CryptConfig
							 | 
						||
| 
								 | 
							
								    when hash alg has been deprecated for context.
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    return True
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#: list of keys allowed under wildcard "all" scheme w/o a security warning.
							 | 
						||
| 
								 | 
							
								_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):
							 | 
						||
| 
								 | 
							
								    """parses, validates, and stores CryptContext config
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    this is a helper used internally by CryptContext to handle
							 | 
						||
| 
								 | 
							
								    parsing, validation, and serialization of its config options.
							 | 
						||
| 
								 | 
							
								    split out from the main class, but not made public since
							 | 
						||
| 
								 | 
							
								    that just complicates interface too much (c.f. CryptPolicy)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    :arg source: config as dict mapping ``(cat,scheme,option) -> value``
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    #===================================================================
							 | 
						||
| 
								 | 
							
								    # instance attrs
							 | 
						||
| 
								 | 
							
								    #===================================================================
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # triple-nested dict which maps scheme -> category -> key -> value,
							 | 
						||
| 
								 | 
							
								    # storing all hash-specific options
							 | 
						||
| 
								 | 
							
								    _scheme_options = None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # double-nested dict which maps key -> category -> value
							 | 
						||
| 
								 | 
							
								    # storing all CryptContext options
							 | 
						||
| 
								 | 
							
								    _context_options = None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # tuple of handler objects
							 | 
						||
| 
								 | 
							
								    handlers = None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # tuple of scheme objects in same order as handlers
							 | 
						||
| 
								 | 
							
								    schemes = None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # tuple of categories in alphabetical order (not including None)
							 | 
						||
| 
								 | 
							
								    categories = None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # set of all context keywords used by active schemes
							 | 
						||
| 
								 | 
							
								    context_kwds = None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # dict mapping category -> default scheme
							 | 
						||
| 
								 | 
							
								    _default_schemes = None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # dict mapping (scheme, category) -> custom handler
							 | 
						||
| 
								 | 
							
								    _records = None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # dict mapping category -> list of custom handler instances for that category,
							 | 
						||
| 
								 | 
							
								    # in order of schemes(). populated on demand by _get_record_list()
							 | 
						||
| 
								 | 
							
								    _record_lists = None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    #===================================================================
							 | 
						||
| 
								 | 
							
								    # constructor
							 | 
						||
| 
								 | 
							
								    #===================================================================
							 | 
						||
| 
								 | 
							
								    def __init__(self, source):
							 | 
						||
| 
								 | 
							
								        self._init_scheme_list(source.get((None,None,"schemes")))
							 | 
						||
| 
								 | 
							
								        self._init_options(source)
							 | 
						||
| 
								 | 
							
								        self._init_default_schemes()
							 | 
						||
| 
								 | 
							
								        self._init_records()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _init_scheme_list(self, data):
							 | 
						||
| 
								 | 
							
								        """initialize .handlers and .schemes attributes"""
							 | 
						||
| 
								 | 
							
								        handlers  = []
							 | 
						||
| 
								 | 
							
								        schemes = []
							 | 
						||
| 
								 | 
							
								        if isinstance(data, native_string_types):
							 | 
						||
| 
								 | 
							
								            data = splitcomma(data)
							 | 
						||
| 
								 | 
							
								        for elem in data or ():
							 | 
						||
| 
								 | 
							
								            # resolve elem -> handler & scheme
							 | 
						||
| 
								 | 
							
								            if hasattr(elem, "name"):
							 | 
						||
| 
								 | 
							
								                handler = elem
							 | 
						||
| 
								 | 
							
								                scheme = handler.name
							 | 
						||
| 
								 | 
							
								                _validate_handler_name(scheme)
							 | 
						||
| 
								 | 
							
								            elif isinstance(elem, native_string_types):
							 | 
						||
| 
								 | 
							
								                handler = get_crypt_handler(elem)
							 | 
						||
| 
								 | 
							
								                scheme = handler.name
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                raise TypeError("scheme must be name or CryptHandler, "
							 | 
						||
| 
								 | 
							
								                                "not %r" % type(elem))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            # check scheme name isn't already in use
							 | 
						||
| 
								 | 
							
								            if scheme in schemes:
							 | 
						||
| 
								 | 
							
								                raise KeyError("multiple handlers with same name: %r" %
							 | 
						||
| 
								 | 
							
								                               (scheme,))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            # add to handler list
							 | 
						||
| 
								 | 
							
								            handlers.append(handler)
							 | 
						||
| 
								 | 
							
								            schemes.append(scheme)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self.handlers = tuple(handlers)
							 | 
						||
| 
								 | 
							
								        self.schemes = tuple(schemes)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    #===================================================================
							 | 
						||
| 
								 | 
							
								    # lowlevel options
							 | 
						||
| 
								 | 
							
								    #===================================================================
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    #---------------------------------------------------------------
							 | 
						||
| 
								 | 
							
								    # init lowlevel option storage
							 | 
						||
| 
								 | 
							
								    #---------------------------------------------------------------
							 | 
						||
| 
								 | 
							
								    def _init_options(self, source):
							 | 
						||
| 
								 | 
							
								        """load config dict into internal representation,
							 | 
						||
| 
								 | 
							
								        and init .categories attr
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        # prepare dicts & locals
							 | 
						||
| 
								 | 
							
								        norm_scheme_option = self._norm_scheme_option
							 | 
						||
| 
								 | 
							
								        norm_context_option = self._norm_context_option
							 | 
						||
| 
								 | 
							
								        self._scheme_options = scheme_options = {}
							 | 
						||
| 
								 | 
							
								        self._context_options = context_options = {}
							 | 
						||
| 
								 | 
							
								        categories = set()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # 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
							 | 
						||
| 
								 | 
							
								                # this will be fatal under 2.0.
							 | 
						||
| 
								 | 
							
								                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; This will be an error in Passlib 2.0" %
							 | 
						||
| 
								 | 
							
								                         (key,), PasslibConfigWarning)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                # this scheme is going away in 2.0;
							 | 
						||
| 
								 | 
							
								                # but most keys deserve an extra warning since it impacts security.
							 | 
						||
| 
								 | 
							
								                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)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                # store in scheme_options
							 | 
						||
| 
								 | 
							
								                # map structure: scheme_options[scheme][category][key] = value
							 | 
						||
| 
								 | 
							
								                try:
							 | 
						||
| 
								 | 
							
								                    category_map = scheme_options[scheme]
							 | 
						||
| 
								 | 
							
								                except KeyError:
							 | 
						||
| 
								 | 
							
								                    scheme_options[scheme] = {cat: {key: value}}
							 | 
						||
| 
								 | 
							
								                else:
							 | 
						||
| 
								 | 
							
								                    try:
							 | 
						||
| 
								 | 
							
								                        option_map = category_map[cat]
							 | 
						||
| 
								 | 
							
								                    except KeyError:
							 | 
						||
| 
								 | 
							
								                        category_map[cat] = {key: value}
							 | 
						||
| 
								 | 
							
								                    else:
							 | 
						||
| 
								 | 
							
								                        option_map[key] = value
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                # normalize context option
							 | 
						||
| 
								 | 
							
								                if cat and key == "schemes":
							 | 
						||
| 
								 | 
							
								                    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
							 | 
						||
| 
								 | 
							
								                try:
							 | 
						||
| 
								 | 
							
								                    category_map = context_options[key]
							 | 
						||
| 
								 | 
							
								                except KeyError:
							 | 
						||
| 
								 | 
							
								                    context_options[key] = {cat: value}
							 | 
						||
| 
								 | 
							
								                else:
							 | 
						||
| 
								 | 
							
								                    category_map[cat] = value
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # store list of configured categories
							 | 
						||
| 
								 | 
							
								        categories.discard(None)
							 | 
						||
| 
								 | 
							
								        self.categories = tuple(sorted(categories))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _norm_scheme_option(self, key, value):
							 | 
						||
| 
								 | 
							
								        # check for invalid options
							 | 
						||
| 
								 | 
							
								        if key in _forbidden_scheme_options:
							 | 
						||
| 
								 | 
							
								            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):
							 | 
						||
| 
								 | 
							
								            func = _coerce_scheme_options.get(key)
							 | 
						||
| 
								 | 
							
								            if func:
							 | 
						||
| 
								 | 
							
								                value = func(value)
							 | 
						||
| 
								 | 
							
								        return key, value
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _norm_context_option(self, cat, key, value):
							 | 
						||
| 
								 | 
							
								        schemes = self.schemes
							 | 
						||
| 
								 | 
							
								        if key == "default":
							 | 
						||
| 
								 | 
							
								            if hasattr(value, "name"):
							 | 
						||
| 
								 | 
							
								                value = value.name
							 | 
						||
| 
								 | 
							
								            elif not isinstance(value, native_string_types):
							 | 
						||
| 
								 | 
							
								                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):
							 | 
						||
| 
								 | 
							
								                value = splitcomma(value)
							 | 
						||
| 
								 | 
							
								            elif not isinstance(value, (list,tuple)):
							 | 
						||
| 
								 | 
							
								                raise ExpectedTypeError(value, "str or seq", "deprecated")
							 | 
						||
| 
								 | 
							
								            if 'auto' in value:
							 | 
						||
| 
								 | 
							
								                # XXX: have any statements been made about when this is default?
							 | 
						||
| 
								 | 
							
								                #      should do it in 1.8 at latest.
							 | 
						||
| 
								 | 
							
								                if len(value) > 1:
							 | 
						||
| 
								 | 
							
								                    raise ValueError("cannot list other schemes if "
							 | 
						||
| 
								 | 
							
								                                     "``deprecated=['auto']`` is used")
							 | 
						||
| 
								 | 
							
								            elif schemes:
							 | 
						||
| 
								 | 
							
								                # make sure list of deprecated schemes is subset of configured schemes
							 | 
						||
| 
								 | 
							
								                for scheme in value:
							 | 
						||
| 
								 | 
							
								                    if not isinstance(scheme, native_string_types):
							 | 
						||
| 
								 | 
							
								                        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
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    #---------------------------------------------------------------
							 | 
						||
| 
								 | 
							
								    # reading context options
							 | 
						||
| 
								 | 
							
								    #---------------------------------------------------------------
							 | 
						||
| 
								 | 
							
								    def get_context_optionmap(self, key, _default={}):
							 | 
						||
| 
								 | 
							
								        """return dict mapping category->value for specific context option.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        .. warning:: treat return value as readonly!
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        return self._context_options.get(key, _default)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def get_context_option_with_flag(self, category, key):
							 | 
						||
| 
								 | 
							
								        """return value of specific option, handling category inheritance.
							 | 
						||
| 
								 | 
							
								        also returns flag indicating whether value is category-specific.
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        try:
							 | 
						||
| 
								 | 
							
								            category_map = self._context_options[key]
							 | 
						||
| 
								 | 
							
								        except KeyError:
							 | 
						||
| 
								 | 
							
								            return None, False
							 | 
						||
| 
								 | 
							
								        value = category_map.get(None)
							 | 
						||
| 
								 | 
							
								        if category:
							 | 
						||
| 
								 | 
							
								            try:
							 | 
						||
| 
								 | 
							
								                alt = category_map[category]
							 | 
						||
| 
								 | 
							
								            except KeyError:
							 | 
						||
| 
								 | 
							
								                pass
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                if value is None or alt != value:
							 | 
						||
| 
								 | 
							
								                    return alt, True
							 | 
						||
| 
								 | 
							
								        return value, False
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    #---------------------------------------------------------------
							 | 
						||
| 
								 | 
							
								    # reading scheme options
							 | 
						||
| 
								 | 
							
								    #---------------------------------------------------------------
							 | 
						||
| 
								 | 
							
								    def _get_scheme_optionmap(self, scheme, category, default={}):
							 | 
						||
| 
								 | 
							
								        """return all options for (scheme,category) combination
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        .. warning:: treat return value as readonly!
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        try:
							 | 
						||
| 
								 | 
							
								            return self._scheme_options[scheme][category]
							 | 
						||
| 
								 | 
							
								        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.
							 | 
						||
| 
								 | 
							
								        result can be modified.
							 | 
						||
| 
								 | 
							
								        returns (kwds, has_cat_specific_options)
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        # start out with copy of global options
							 | 
						||
| 
								 | 
							
								        get_optionmap = self._get_scheme_optionmap
							 | 
						||
| 
								 | 
							
								        kwds = get_optionmap("all", None).copy()
							 | 
						||
| 
								 | 
							
								        has_cat_options = False
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # add in category-specific global options
							 | 
						||
| 
								 | 
							
								        if category:
							 | 
						||
| 
								 | 
							
								            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)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # load category-specific options for scheme
							 | 
						||
| 
								 | 
							
								        if category:
							 | 
						||
| 
								 | 
							
								            defkwds.update(other)
							 | 
						||
| 
								 | 
							
								            kwds.update(get_optionmap(scheme, category))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            # compare default category options to see if there's anything
							 | 
						||
| 
								 | 
							
								            # category-specific
							 | 
						||
| 
								 | 
							
								            if kwds != defkwds:
							 | 
						||
| 
								 | 
							
								                has_cat_options = True
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return kwds, has_cat_options
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    #===================================================================
							 | 
						||
| 
								 | 
							
								    # deprecated & default schemes
							 | 
						||
| 
								 | 
							
								    #===================================================================
							 | 
						||
| 
								 | 
							
								    def _init_default_schemes(self):
							 | 
						||
| 
								 | 
							
								        """initialize maps containing default scheme for each category.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        have to do this after _init_options(), since the default scheme
							 | 
						||
| 
								 | 
							
								        is affected by the list of deprecated schemes.
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        # init maps & locals
							 | 
						||
| 
								 | 
							
								        get_optionmap = self.get_context_optionmap
							 | 
						||
| 
								 | 
							
								        default_map = self._default_schemes = get_optionmap("default").copy()
							 | 
						||
| 
								 | 
							
								        dep_map = get_optionmap("deprecated")
							 | 
						||
| 
								 | 
							
								        schemes = self.schemes
							 | 
						||
| 
								 | 
							
								        if not schemes:
							 | 
						||
| 
								 | 
							
								            return
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # figure out default scheme
							 | 
						||
| 
								 | 
							
								        deps = dep_map.get(None) or ()
							 | 
						||
| 
								 | 
							
								        default = default_map.get(None)
							 | 
						||
| 
								 | 
							
								        if not default:
							 | 
						||
| 
								 | 
							
								            for scheme in schemes:
							 | 
						||
| 
								 | 
							
								                if scheme not in deps:
							 | 
						||
| 
								 | 
							
								                    default_map[None] = scheme
							 | 
						||
| 
								 | 
							
								                    break
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                raise ValueError("must have at least one non-deprecated scheme")
							 | 
						||
| 
								 | 
							
								        elif default in deps:
							 | 
						||
| 
								 | 
							
								            raise ValueError("default scheme cannot be deprecated")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # figure out per-category default schemes,
							 | 
						||
| 
								 | 
							
								        for cat in self.categories:
							 | 
						||
| 
								 | 
							
								            cdeps = dep_map.get(cat, deps)
							 | 
						||
| 
								 | 
							
								            cdefault = default_map.get(cat, default)
							 | 
						||
| 
								 | 
							
								            if not cdefault:
							 | 
						||
| 
								 | 
							
								                for scheme in schemes:
							 | 
						||
| 
								 | 
							
								                    if scheme not in cdeps:
							 | 
						||
| 
								 | 
							
								                        default_map[cat] = scheme
							 | 
						||
| 
								 | 
							
								                        break
							 | 
						||
| 
								 | 
							
								                else:
							 | 
						||
| 
								 | 
							
								                    raise ValueError("must have at least one non-deprecated "
							 | 
						||
| 
								 | 
							
								                                     "scheme for %r category" % cat)
							 | 
						||
| 
								 | 
							
								            elif cdefault in cdeps:
							 | 
						||
| 
								 | 
							
								                raise ValueError("default scheme for %r category "
							 | 
						||
| 
								 | 
							
								                                 "cannot be deprecated" % cat)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def default_scheme(self, category):
							 | 
						||
| 
								 | 
							
								        """return default scheme for specific category"""
							 | 
						||
| 
								 | 
							
								        defaults = self._default_schemes
							 | 
						||
| 
								 | 
							
								        try:
							 | 
						||
| 
								 | 
							
								            return defaults[category]
							 | 
						||
| 
								 | 
							
								        except KeyError:
							 | 
						||
| 
								 | 
							
								            pass
							 | 
						||
| 
								 | 
							
								        if not self.schemes:
							 | 
						||
| 
								 | 
							
								            raise KeyError("no hash schemes configured for this "
							 | 
						||
| 
								 | 
							
								                           "CryptContext instance")
							 | 
						||
| 
								 | 
							
								        return defaults[None]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def is_deprecated_with_flag(self, scheme, category):
							 | 
						||
| 
								 | 
							
								        """is scheme deprecated under particular category?"""
							 | 
						||
| 
								 | 
							
								        depmap = self.get_context_optionmap("deprecated")
							 | 
						||
| 
								 | 
							
								        def test(cat):
							 | 
						||
| 
								 | 
							
								            source = depmap.get(cat, depmap.get(None))
							 | 
						||
| 
								 | 
							
								            if source is None:
							 | 
						||
| 
								 | 
							
								                return None
							 | 
						||
| 
								 | 
							
								            elif 'auto' in source:
							 | 
						||
| 
								 | 
							
								                return scheme != self.default_scheme(cat)
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                return scheme in source
							 | 
						||
| 
								 | 
							
								        value = test(None) or False
							 | 
						||
| 
								 | 
							
								        if category:
							 | 
						||
| 
								 | 
							
								            alt = test(category)
							 | 
						||
| 
								 | 
							
								            if alt is not None and value != alt:
							 | 
						||
| 
								 | 
							
								                return alt, True
							 | 
						||
| 
								 | 
							
								        return value, False
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    #===================================================================
							 | 
						||
| 
								 | 
							
								    # CryptRecord objects
							 | 
						||
| 
								 | 
							
								    #===================================================================
							 | 
						||
| 
								 | 
							
								    def _init_records(self):
							 | 
						||
| 
								 | 
							
								        # NOTE: this step handles final validation of settings,
							 | 
						||
| 
								 | 
							
								        #       checking for violations against handler's internal invariants.
							 | 
						||
| 
								 | 
							
								        #       this is why we create all the records now,
							 | 
						||
| 
								 | 
							
								        #       so CryptContext throws error immediately rather than later.
							 | 
						||
| 
								 | 
							
								        self._record_lists = {}
							 | 
						||
| 
								 | 
							
								        records = self._records = {}
							 | 
						||
| 
								 | 
							
								        all_context_kwds = self.context_kwds = set()
							 | 
						||
| 
								 | 
							
								        get_options = self._get_record_options_with_flag
							 | 
						||
| 
								 | 
							
								        categories = (None,) + self.categories
							 | 
						||
| 
								 | 
							
								        for handler in self.handlers:
							 | 
						||
| 
								 | 
							
								            scheme = handler.name
							 | 
						||
| 
								 | 
							
								            all_context_kwds.update(handler.context_kwds)
							 | 
						||
| 
								 | 
							
								            for cat in categories:
							 | 
						||
| 
								 | 
							
								                kwds, has_cat_options = get_options(scheme, cat)
							 | 
						||
| 
								 | 
							
								                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.
							 | 
						||
| 
								 | 
							
								        # NOTE: default records for specific category stored under the
							 | 
						||
| 
								 | 
							
								        # key (None,category); these are populated on-demand by get_record().
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @staticmethod
							 | 
						||
| 
								 | 
							
								    def _create_record(handler, category=None, deprecated=False, **settings):
							 | 
						||
| 
								 | 
							
								        # create custom handler if needed.
							 | 
						||
| 
								 | 
							
								        try:
							 | 
						||
| 
								 | 
							
								            # XXX: relaxed=True is mostly here to retain backwards-compat behavior.
							 | 
						||
| 
								 | 
							
								            #      could make this optional flag in future.
							 | 
						||
| 
								 | 
							
								            subcls = handler.using(relaxed=True, **settings)
							 | 
						||
| 
								 | 
							
								        except TypeError as err:
							 | 
						||
| 
								 | 
							
								            m = re.match(r".* unexpected keyword argument '(.*)'$", str(err))
							 | 
						||
| 
								 | 
							
								            if m and m.group(1) in settings:
							 | 
						||
| 
								 | 
							
								                # translate into KeyError, for backwards compat.
							 | 
						||
| 
								 | 
							
								                # XXX: push this down to GenericHandler.using() implementation?
							 | 
						||
| 
								 | 
							
								                key = m.group(1)
							 | 
						||
| 
								 | 
							
								                raise KeyError("keyword not supported by %s handler: %r" %
							 | 
						||
| 
								 | 
							
								                               (handler.name, key))
							 | 
						||
| 
								 | 
							
								            raise
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # using private attrs to store some extra metadata in custom handler
							 | 
						||
| 
								 | 
							
								        assert subcls is not handler, "expected unique variant of handler"
							 | 
						||
| 
								 | 
							
								        ##subcls._Context__category = category
							 | 
						||
| 
								 | 
							
								        subcls._Context__orig_handler = handler
							 | 
						||
| 
								 | 
							
								        subcls.deprecated = deprecated  # attr reserved for this purpose
							 | 
						||
| 
								 | 
							
								        return subcls
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _get_record_options_with_flag(self, scheme, category):
							 | 
						||
| 
								 | 
							
								        """return composite dict of options for given scheme + category.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        this is currently a private method, though some variant
							 | 
						||
| 
								 | 
							
								        of its output may eventually be made public.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        given a scheme & category, it returns two things:
							 | 
						||
| 
								 | 
							
								        a set of all the keyword options to pass to :meth:`_create_record`,
							 | 
						||
| 
								 | 
							
								        and a bool flag indicating whether any of these options
							 | 
						||
| 
								 | 
							
								        were specific to the named category. if this flag is false,
							 | 
						||
| 
								 | 
							
								        the options are identical to the options for the default category.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        the options dict includes all the scheme-specific settings,
							 | 
						||
| 
								 | 
							
								        as well as optional *deprecated* keyword.
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        # get scheme options
							 | 
						||
| 
								 | 
							
								        kwds, has_cat_options = self.get_scheme_options_with_flag(scheme, category)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # throw in deprecated flag
							 | 
						||
| 
								 | 
							
								        value, not_inherited = self.is_deprecated_with_flag(scheme, category)
							 | 
						||
| 
								 | 
							
								        if value:
							 | 
						||
| 
								 | 
							
								            kwds['deprecated'] = True
							 | 
						||
| 
								 | 
							
								        if not_inherited:
							 | 
						||
| 
								 | 
							
								            has_cat_options = True
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return kwds, has_cat_options
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def get_record(self, scheme, category):
							 | 
						||
| 
								 | 
							
								        """return record for specific scheme & category (cached)"""
							 | 
						||
| 
								 | 
							
								        # NOTE: this is part of the critical path shared by
							 | 
						||
| 
								 | 
							
								        #       all of CryptContext's PasswordHash methods,
							 | 
						||
| 
								 | 
							
								        #       hence all the caching and error checking.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # quick lookup in cache
							 | 
						||
| 
								 | 
							
								        try:
							 | 
						||
| 
								 | 
							
								            return self._records[scheme, category]
							 | 
						||
| 
								 | 
							
								        except KeyError:
							 | 
						||
| 
								 | 
							
								            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"))
							 | 
						||
| 
								 | 
							
								            raise ExpectedTypeError(category, "str or None", "category")
							 | 
						||
| 
								 | 
							
								        if scheme is not None and not isinstance(scheme, native_string_types):
							 | 
						||
| 
								 | 
							
								            raise ExpectedTypeError(scheme, "str or None", "scheme")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # if scheme=None,
							 | 
						||
| 
								 | 
							
								        # use record for category's default scheme, and cache result.
							 | 
						||
| 
								 | 
							
								        if not scheme:
							 | 
						||
| 
								 | 
							
								            default = self.default_scheme(category)
							 | 
						||
| 
								 | 
							
								            assert default
							 | 
						||
| 
								 | 
							
								            record = self._records[None, category] = self.get_record(default,
							 | 
						||
| 
								 | 
							
								                                                                      category)
							 | 
						||
| 
								 | 
							
								            return record
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # if no record for (scheme, category),
							 | 
						||
| 
								 | 
							
								        # use record for (scheme, None), and cache result.
							 | 
						||
| 
								 | 
							
								        if category:
							 | 
						||
| 
								 | 
							
								            try:
							 | 
						||
| 
								 | 
							
								                cache = self._records
							 | 
						||
| 
								 | 
							
								                record = cache[scheme, category] = cache[scheme, None]
							 | 
						||
| 
								 | 
							
								                return record
							 | 
						||
| 
								 | 
							
								            except KeyError:
							 | 
						||
| 
								 | 
							
								                pass
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # scheme not found in configuration for default category
							 | 
						||
| 
								 | 
							
								        raise KeyError("crypt algorithm not found in policy: %r" % (scheme,))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _get_record_list(self, category=None):
							 | 
						||
| 
								 | 
							
								        """return list of records for category (cached)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        this is an internal helper used only by identify_record()
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        # type check of category - handled by _get_record()
							 | 
						||
| 
								 | 
							
								        # quick lookup in cache
							 | 
						||
| 
								 | 
							
								        try:
							 | 
						||
| 
								 | 
							
								            return self._record_lists[category]
							 | 
						||
| 
								 | 
							
								        except KeyError:
							 | 
						||
| 
								 | 
							
								            pass
							 | 
						||
| 
								 | 
							
								        # cache miss - build list from scratch
							 | 
						||
| 
								 | 
							
								        value = self._record_lists[category] = [
							 | 
						||
| 
								 | 
							
								            self.get_record(scheme, category)
							 | 
						||
| 
								 | 
							
								            for scheme in self.schemes
							 | 
						||
| 
								 | 
							
								            ]
							 | 
						||
| 
								 | 
							
								        return value
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def identify_record(self, hash, category, required=True):
							 | 
						||
| 
								 | 
							
								        """internal helper to identify appropriate custom handler for hash"""
							 | 
						||
| 
								 | 
							
								        # NOTE: this is part of the critical path shared by
							 | 
						||
| 
								 | 
							
								        #       all of CryptContext's PasswordHash methods,
							 | 
						||
| 
								 | 
							
								        #       hence all the caching and error checking.
							 | 
						||
| 
								 | 
							
								        # FIXME: if multiple hashes could match (e.g. lmhash vs nthash)
							 | 
						||
| 
								 | 
							
								        #        this will only return first match. might want to do something
							 | 
						||
| 
								 | 
							
								        #        about this in future, but for now only hashes with
							 | 
						||
| 
								 | 
							
								        #        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):
							 | 
						||
| 
								 | 
							
								            raise ExpectedStringError(hash, "hash")
							 | 
						||
| 
								 | 
							
								        # type check of category - handled by _get_record_list()
							 | 
						||
| 
								 | 
							
								        for record in self._get_record_list(category):
							 | 
						||
| 
								 | 
							
								            if record.identify(hash):
							 | 
						||
| 
								 | 
							
								                return record
							 | 
						||
| 
								 | 
							
								        if not required:
							 | 
						||
| 
								 | 
							
								            return None
							 | 
						||
| 
								 | 
							
								        elif not self.schemes:
							 | 
						||
| 
								 | 
							
								            raise KeyError("no crypt algorithms supported")
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            raise exc.UnknownHashError("hash could not be identified")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @memoized_property
							 | 
						||
| 
								 | 
							
								    def disabled_record(self):
							 | 
						||
| 
								 | 
							
								        for record in self._get_record_list(None):
							 | 
						||
| 
								 | 
							
								            if record.is_disabled:
							 | 
						||
| 
								 | 
							
								                return record
							 | 
						||
| 
								 | 
							
								        raise RuntimeError("no disabled hasher present "
							 | 
						||
| 
								 | 
							
								                           "(perhaps add 'unix_disabled' to list of schemes?)")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    #===================================================================
							 | 
						||
| 
								 | 
							
								    # serialization
							 | 
						||
| 
								 | 
							
								    #===================================================================
							 | 
						||
| 
								 | 
							
								    def iter_config(self, resolve=False):
							 | 
						||
| 
								 | 
							
								        """regenerate original config.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        this is an iterator which yields ``(cat,scheme,option),value`` items,
							 | 
						||
| 
								 | 
							
								        in the order they generally appear inside an INI file.
							 | 
						||
| 
								 | 
							
								        if interpreted as a dictionary, it should match the original
							 | 
						||
| 
								 | 
							
								        keywords passed to the CryptContext (aside from any canonization).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        it's mainly used as the internal backend for most of the public
							 | 
						||
| 
								 | 
							
								        serialization methods.
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        # grab various bits of data
							 | 
						||
| 
								 | 
							
								        scheme_options = self._scheme_options
							 | 
						||
| 
								 | 
							
								        context_options = self._context_options
							 | 
						||
| 
								 | 
							
								        scheme_keys = sorted(scheme_options)
							 | 
						||
| 
								 | 
							
								        context_keys = sorted(context_options)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # write loaded schemes (may differ from 'schemes' local var)
							 | 
						||
| 
								 | 
							
								        if 'schemes' in context_keys:
							 | 
						||
| 
								 | 
							
								            context_keys.remove("schemes")
							 | 
						||
| 
								 | 
							
								        value = self.handlers if resolve else self.schemes
							 | 
						||
| 
								 | 
							
								        if value:
							 | 
						||
| 
								 | 
							
								            yield (None, None, "schemes"), list(value)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # then run through config for each user category
							 | 
						||
| 
								 | 
							
								        for cat in (None,) + self.categories:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            # write context options
							 | 
						||
| 
								 | 
							
								            for key in context_keys:
							 | 
						||
| 
								 | 
							
								                try:
							 | 
						||
| 
								 | 
							
								                    value = context_options[key][cat]
							 | 
						||
| 
								 | 
							
								                except KeyError:
							 | 
						||
| 
								 | 
							
								                    pass
							 | 
						||
| 
								 | 
							
								                else:
							 | 
						||
| 
								 | 
							
								                    if isinstance(value, list):
							 | 
						||
| 
								 | 
							
								                        value = list(value)
							 | 
						||
| 
								 | 
							
								                    yield (cat, None, key), value
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            # write per-scheme options for all schemes.
							 | 
						||
| 
								 | 
							
								            for scheme in scheme_keys:
							 | 
						||
| 
								 | 
							
								                try:
							 | 
						||
| 
								 | 
							
								                    kwds = scheme_options[scheme][cat]
							 | 
						||
| 
								 | 
							
								                except KeyError:
							 | 
						||
| 
								 | 
							
								                    pass
							 | 
						||
| 
								 | 
							
								                else:
							 | 
						||
| 
								 | 
							
								                    for key in sorted(kwds):
							 | 
						||
| 
								 | 
							
								                        yield (cat, scheme, key), kwds[key]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    #===================================================================
							 | 
						||
| 
								 | 
							
								    # eoc
							 | 
						||
| 
								 | 
							
								    #===================================================================
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#=============================================================================
							 | 
						||
| 
								 | 
							
								# main CryptContext class
							 | 
						||
| 
								 | 
							
								#=============================================================================
							 | 
						||
| 
								 | 
							
								class CryptContext(object):
							 | 
						||
| 
								 | 
							
								    """Helper for hashing & verifying passwords using multiple algorithms.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Instances of this class allow applications to choose a specific
							 | 
						||
| 
								 | 
							
								    set of hash algorithms which they wish to support, set limits and defaults
							 | 
						||
| 
								 | 
							
								    for the rounds and salt sizes those algorithms should use, flag
							 | 
						||
| 
								 | 
							
								    which algorithms should be deprecated, and automatically handle
							 | 
						||
| 
								 | 
							
								    migrating users to stronger hashes when they log in.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Basic usage::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        >>> ctx = CryptContext(schemes=[...])
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    See the Passlib online documentation for details and full documentation.
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    # FIXME: altering the configuration of this object isn't threadsafe,
							 | 
						||
| 
								 | 
							
								    # but is generally only done during application init, so not a major
							 | 
						||
| 
								 | 
							
								    # issue (just yet).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # XXX: would like some way to restrict the categories that are allowed,
							 | 
						||
| 
								 | 
							
								    # to restrict what the app OR the config can use.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # XXX: add wrap/unwrap callback hooks so app can mutate hash format?
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # XXX: add method for detecting and warning user about schemes
							 | 
						||
| 
								 | 
							
								    #      which don't have any good distinguishing marks?
							 | 
						||
| 
								 | 
							
								    #      or greedy ones (unix_disabled, plaintext) which are not listed at the end?
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    #===================================================================
							 | 
						||
| 
								 | 
							
								    # instance attrs
							 | 
						||
| 
								 | 
							
								    #===================================================================
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # _CryptConfig instance holding current parsed config
							 | 
						||
| 
								 | 
							
								    _config = None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # copy of _config methods, stored in CryptContext instance for speed.
							 | 
						||
| 
								 | 
							
								    _get_record = None
							 | 
						||
| 
								 | 
							
								    _identify_record = None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    #===================================================================
							 | 
						||
| 
								 | 
							
								    # secondary constructors
							 | 
						||
| 
								 | 
							
								    #===================================================================
							 | 
						||
| 
								 | 
							
								    @classmethod
							 | 
						||
| 
								 | 
							
								    def _norm_source(cls, source):
							 | 
						||
| 
								 | 
							
								        """internal helper - accepts string, dict, or context"""
							 | 
						||
| 
								 | 
							
								        if isinstance(source, dict):
							 | 
						||
| 
								 | 
							
								            return cls(**source)
							 | 
						||
| 
								 | 
							
								        elif isinstance(source, cls):
							 | 
						||
| 
								 | 
							
								            return source
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            self = cls()
							 | 
						||
| 
								 | 
							
								            self.load(source)
							 | 
						||
| 
								 | 
							
								            return self
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @classmethod
							 | 
						||
| 
								 | 
							
								    def from_string(cls, source, section="passlib", encoding="utf-8"):
							 | 
						||
| 
								 | 
							
								        """create new CryptContext instance from an INI-formatted string.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :type source: unicode or bytes
							 | 
						||
| 
								 | 
							
								        :arg source:
							 | 
						||
| 
								 | 
							
								            string containing INI-formatted content.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :type section: str
							 | 
						||
| 
								 | 
							
								        :param section:
							 | 
						||
| 
								 | 
							
								            option name of section to read from, defaults to ``"passlib"``.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :type encoding: str
							 | 
						||
| 
								 | 
							
								        :arg encoding:
							 | 
						||
| 
								 | 
							
								            optional encoding used when source is bytes, defaults to ``"utf-8"``.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :returns:
							 | 
						||
| 
								 | 
							
								            new :class:`CryptContext` instance, configured based on the
							 | 
						||
| 
								 | 
							
								            parameters in the *source* string.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        Usage example::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            >>> from passlib.context import CryptContext
							 | 
						||
| 
								 | 
							
								            >>> context = CryptContext.from_string('''
							 | 
						||
| 
								 | 
							
								            ... [passlib]
							 | 
						||
| 
								 | 
							
								            ... schemes = sha256_crypt, des_crypt
							 | 
						||
| 
								 | 
							
								            ... sha256_crypt__default_rounds = 30000
							 | 
						||
| 
								 | 
							
								            ... ''')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        .. versionadded:: 1.6
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        .. seealso:: :meth:`to_string`, the inverse of this constructor.
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        if not isinstance(source, unicode_or_bytes_types):
							 | 
						||
| 
								 | 
							
								            raise ExpectedTypeError(source, "unicode or bytes", "source")
							 | 
						||
| 
								 | 
							
								        self = cls(_autoload=False)
							 | 
						||
| 
								 | 
							
								        self.load(source, section=section, encoding=encoding)
							 | 
						||
| 
								 | 
							
								        return self
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @classmethod
							 | 
						||
| 
								 | 
							
								    def from_path(cls, path, section="passlib", encoding="utf-8"):
							 | 
						||
| 
								 | 
							
								        """create new CryptContext instance from an INI-formatted file.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        this functions exactly the same as :meth:`from_string`,
							 | 
						||
| 
								 | 
							
								        except that it loads from a local file.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :type path: str
							 | 
						||
| 
								 | 
							
								        :arg path:
							 | 
						||
| 
								 | 
							
								            path to local file containing INI-formatted config.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :type section: str
							 | 
						||
| 
								 | 
							
								        :param section:
							 | 
						||
| 
								 | 
							
								            option name of section to read from, defaults to ``"passlib"``.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :type encoding: str
							 | 
						||
| 
								 | 
							
								        :arg encoding:
							 | 
						||
| 
								 | 
							
								            encoding used to load file, defaults to ``"utf-8"``.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :returns:
							 | 
						||
| 
								 | 
							
								            new CryptContext instance, configured based on the parameters
							 | 
						||
| 
								 | 
							
								            stored in the file *path*.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        .. versionadded:: 1.6
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        .. seealso:: :meth:`from_string` for an equivalent usage example.
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        self = cls(_autoload=False)
							 | 
						||
| 
								 | 
							
								        self.load_path(path, section=section, encoding=encoding)
							 | 
						||
| 
								 | 
							
								        return self
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def copy(self, **kwds):
							 | 
						||
| 
								 | 
							
								        """Return copy of existing CryptContext instance.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        This function returns a new CryptContext instance whose configuration
							 | 
						||
| 
								 | 
							
								        is exactly the same as the original, with the exception that any keywords
							 | 
						||
| 
								 | 
							
								        passed in will take precedence over the original settings.
							 | 
						||
| 
								 | 
							
								        As an example::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            >>> from passlib.context import CryptContext
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            >>> # given an existing context...
							 | 
						||
| 
								 | 
							
								            >>> ctx1 = CryptContext(["sha256_crypt", "md5_crypt"])
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            >>> # copy can be used to make a clone, and update
							 | 
						||
| 
								 | 
							
								            >>> # some of the settings at the same time...
							 | 
						||
| 
								 | 
							
								            >>> ctx2 = custom_app_context.copy(default="md5_crypt")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            >>> # and the original will be unaffected by the change
							 | 
						||
| 
								 | 
							
								            >>> ctx1.default_scheme()
							 | 
						||
| 
								 | 
							
								            "sha256_crypt"
							 | 
						||
| 
								 | 
							
								            >>> ctx2.default_scheme()
							 | 
						||
| 
								 | 
							
								            "md5_crypt"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        .. versionadded:: 1.6
							 | 
						||
| 
								 | 
							
								            This method was previously named :meth:`!replace`. That alias
							 | 
						||
| 
								 | 
							
								            has been deprecated, and will be removed in Passlib 1.8.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        .. seealso:: :meth:`update`
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        # XXX: it would be faster to store ref to self._config,
							 | 
						||
| 
								 | 
							
								        #      but don't want to share config objects til sure
							 | 
						||
| 
								 | 
							
								        #      can rely on them being immutable.
							 | 
						||
| 
								 | 
							
								        other = CryptContext(_autoload=False)
							 | 
						||
| 
								 | 
							
								        other.load(self)
							 | 
						||
| 
								 | 
							
								        if kwds:
							 | 
						||
| 
								 | 
							
								            other.load(kwds, update=True)
							 | 
						||
| 
								 | 
							
								        return other
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def using(self, **kwds):
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        alias for :meth:`copy`, to match PasswordHash.using()
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        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:
							 | 
						||
| 
								 | 
							
								            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")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    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)
							 | 
						||
| 
								 | 
							
								        # XXX: could change load() to accept list of items,
							 | 
						||
| 
								 | 
							
								        #      and skip intermediate dict creation
							 | 
						||
| 
								 | 
							
								        return dict(p.items(section))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def load_path(self, path, update=False, section="passlib", encoding="utf-8"):
							 | 
						||
| 
								 | 
							
								        """Load new configuration into CryptContext from a local file.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        This function is a wrapper for :meth:`load` which
							 | 
						||
| 
								 | 
							
								        loads a configuration string from the local file *path*,
							 | 
						||
| 
								 | 
							
								        instead of an in-memory source. Its behavior and options
							 | 
						||
| 
								 | 
							
								        are otherwise identical to :meth:`!load` when provided with
							 | 
						||
| 
								 | 
							
								        an INI-formatted string.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        .. versionadded:: 1.6
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        def helper(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))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def load(self, source, update=False, section="passlib", encoding="utf-8"):
							 | 
						||
| 
								 | 
							
								        """Load new configuration into CryptContext, replacing existing config.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :arg source:
							 | 
						||
| 
								 | 
							
								            source of new configuration to load.
							 | 
						||
| 
								 | 
							
								            this value can be a number of different types:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            * a :class:`!dict` object, or compatible Mapping
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                the key/value pairs will be interpreted the same
							 | 
						||
| 
								 | 
							
								                keywords for the :class:`CryptContext` class constructor.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            * a :class:`!unicode` or :class:`!bytes` string
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                this will be interpreted as an INI-formatted file,
							 | 
						||
| 
								 | 
							
								                and appropriate key/value pairs will be loaded from
							 | 
						||
| 
								 | 
							
								                the specified *section*.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            * another :class:`!CryptContext` object.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                this will export a snapshot of its configuration
							 | 
						||
| 
								 | 
							
								                using :meth:`to_dict`.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :type update: bool
							 | 
						||
| 
								 | 
							
								        :param update:
							 | 
						||
| 
								 | 
							
								            By default, :meth:`load` will replace the existing configuration
							 | 
						||
| 
								 | 
							
								            entirely. If ``update=True``, it will preserve any existing
							 | 
						||
| 
								 | 
							
								            configuration options that are not overridden by the new source,
							 | 
						||
| 
								 | 
							
								            much like the :meth:`update` method.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :type section: str
							 | 
						||
| 
								 | 
							
								        :param section:
							 | 
						||
| 
								 | 
							
								            When parsing an INI-formatted string, :meth:`load` will look for
							 | 
						||
| 
								 | 
							
								            a section named ``"passlib"``. This option allows an alternate
							 | 
						||
| 
								 | 
							
								            section name to be used. Ignored when loading from a dictionary.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :type encoding: str
							 | 
						||
| 
								 | 
							
								        :param encoding:
							 | 
						||
| 
								 | 
							
								            Encoding to use when **source** is bytes.
							 | 
						||
| 
								 | 
							
								            Defaults to ``"utf-8"``. Ignored when loading from a dictionary.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            .. deprecated:: 1.8
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                This keyword, and support for bytes input, will be dropped in Passlib 2.0
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :raises TypeError:
							 | 
						||
| 
								 | 
							
								            * If the source cannot be identified.
							 | 
						||
| 
								 | 
							
								            * If an unknown / malformed keyword is encountered.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :raises ValueError:
							 | 
						||
| 
								 | 
							
								            If an invalid keyword value is encountered.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        .. note::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            If an error occurs during a :meth:`!load` call, the :class:`!CryptContext`
							 | 
						||
| 
								 | 
							
								            instance will be restored to the configuration it was in before
							 | 
						||
| 
								 | 
							
								            the :meth:`!load` call was made; this is to ensure it is
							 | 
						||
| 
								 | 
							
								            *never* left in an inconsistent state due to a load error.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        .. versionadded:: 1.6
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        #-----------------------------------------------------------
							 | 
						||
| 
								 | 
							
								        # 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,
							 | 
						||
| 
								 | 
							
								                                            "<string passed to CryptContext.load()>")
							 | 
						||
| 
								 | 
							
								        elif isinstance(source, CryptContext):
							 | 
						||
| 
								 | 
							
								            # extract dict directly from config, so it can be merged later
							 | 
						||
| 
								 | 
							
								            source = dict(source._config.iter_config(resolve=True))
							 | 
						||
| 
								 | 
							
								            parse_keys = False
							 | 
						||
| 
								 | 
							
								        elif not hasattr(source, "items"):
							 | 
						||
| 
								 | 
							
								            # mappings are left alone, otherwise throw an error.
							 | 
						||
| 
								 | 
							
								            raise ExpectedTypeError(source, "string or dict", "source")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # XXX: add support for other iterable types, e.g. sequence of pairs?
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        #-----------------------------------------------------------
							 | 
						||
| 
								 | 
							
								        # parse dict keys into (category, scheme, option) format,
							 | 
						||
| 
								 | 
							
								        # and merge with existing configuration if needed.
							 | 
						||
| 
								 | 
							
								        #-----------------------------------------------------------
							 | 
						||
| 
								 | 
							
								        if parse_keys:
							 | 
						||
| 
								 | 
							
								            parse = self._parse_config_key
							 | 
						||
| 
								 | 
							
								            source = dict((parse(key), value)
							 | 
						||
| 
								 | 
							
								                          for key, value in iteritems(source))
							 | 
						||
| 
								 | 
							
								        if update and self._config is not None:
							 | 
						||
| 
								 | 
							
								            # if updating, do nothing if source is empty,
							 | 
						||
| 
								 | 
							
								            if not source:
							 | 
						||
| 
								 | 
							
								                return
							 | 
						||
| 
								 | 
							
								            # otherwise overlay source on top of existing config
							 | 
						||
| 
								 | 
							
								            tmp = source
							 | 
						||
| 
								 | 
							
								            source = dict(self._config.iter_config(resolve=True))
							 | 
						||
| 
								 | 
							
								            source.update(tmp)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        #-----------------------------------------------------------
							 | 
						||
| 
								 | 
							
								        # compile into _CryptConfig instance, and update state
							 | 
						||
| 
								 | 
							
								        #-----------------------------------------------------------
							 | 
						||
| 
								 | 
							
								        config = _CryptConfig(source)
							 | 
						||
| 
								 | 
							
								        self._config = config
							 | 
						||
| 
								 | 
							
								        self._reset_dummy_verify()
							 | 
						||
| 
								 | 
							
								        self._get_record = config.get_record
							 | 
						||
| 
								 | 
							
								        self._identify_record = config.identify_record
							 | 
						||
| 
								 | 
							
								        if config.context_kwds:
							 | 
						||
| 
								 | 
							
								            # (re-)enable method for this instance (in case ELSE clause below ran last load).
							 | 
						||
| 
								 | 
							
								            self.__dict__.pop("_strip_unused_context_kwds", None)
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            # disable method for this instance, it's not needed.
							 | 
						||
| 
								 | 
							
								            self._strip_unused_context_kwds = None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @staticmethod
							 | 
						||
| 
								 | 
							
								    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)
							 | 
						||
| 
								 | 
							
								        parts = ckey.replace(".", "__").split("__")
							 | 
						||
| 
								 | 
							
								        count = len(parts)
							 | 
						||
| 
								 | 
							
								        if count == 1:
							 | 
						||
| 
								 | 
							
								            cat, scheme, key = None, None, parts[0]
							 | 
						||
| 
								 | 
							
								        elif count == 2:
							 | 
						||
| 
								 | 
							
								            cat = None
							 | 
						||
| 
								 | 
							
								            scheme, key = parts
							 | 
						||
| 
								 | 
							
								        elif count == 3:
							 | 
						||
| 
								 | 
							
								            cat, scheme, key = parts
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            raise TypeError("keys must have less than 3 separators: %r" %
							 | 
						||
| 
								 | 
							
								                            (ckey,))
							 | 
						||
| 
								 | 
							
								        # validate & normalize the parts
							 | 
						||
| 
								 | 
							
								        if cat == "default":
							 | 
						||
| 
								 | 
							
								            cat = None
							 | 
						||
| 
								 | 
							
								        elif not cat and cat is not None:
							 | 
						||
| 
								 | 
							
								            raise TypeError("empty category: %r" % ckey)
							 | 
						||
| 
								 | 
							
								        if scheme == "context":
							 | 
						||
| 
								 | 
							
								            scheme = None
							 | 
						||
| 
								 | 
							
								        elif not scheme and scheme is not None:
							 | 
						||
| 
								 | 
							
								            raise TypeError("empty scheme: %r" % ckey)
							 | 
						||
| 
								 | 
							
								        if not key:
							 | 
						||
| 
								 | 
							
								            raise TypeError("empty option: %r" % ckey)
							 | 
						||
| 
								 | 
							
								        return cat, scheme, key
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def update(self, *args, **kwds):
							 | 
						||
| 
								 | 
							
								        """Helper for quickly changing configuration.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        This acts much like the :meth:`!dict.update` method:
							 | 
						||
| 
								 | 
							
								        it updates the context's configuration,
							 | 
						||
| 
								 | 
							
								        replacing the original value(s) for the specified keys,
							 | 
						||
| 
								 | 
							
								        and preserving the rest.
							 | 
						||
| 
								 | 
							
								        It accepts any :ref:`keyword <context-options>`
							 | 
						||
| 
								 | 
							
								        accepted by the :class:`!CryptContext` constructor.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        .. versionadded:: 1.6
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        .. seealso:: :meth:`copy`
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        if args:
							 | 
						||
| 
								 | 
							
								            if len(args) > 1:
							 | 
						||
| 
								 | 
							
								                raise TypeError("expected at most one positional argument")
							 | 
						||
| 
								 | 
							
								            if kwds:
							 | 
						||
| 
								 | 
							
								                raise TypeError("positional arg and keywords mutually exclusive")
							 | 
						||
| 
								 | 
							
								            self.load(args[0], update=True)
							 | 
						||
| 
								 | 
							
								        elif kwds:
							 | 
						||
| 
								 | 
							
								            self.load(kwds, update=True)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # XXX: make this public? even just as flag to load?
							 | 
						||
| 
								 | 
							
								    # FIXME: this function suffered some bitrot in 1.6.1,
							 | 
						||
| 
								 | 
							
								    #        will need to be updated before works again.
							 | 
						||
| 
								 | 
							
								    ##def _simplify(self):
							 | 
						||
| 
								 | 
							
								    ##    "helper to remove redundant/unused options"
							 | 
						||
| 
								 | 
							
								    ##    # don't do anything if no schemes are defined
							 | 
						||
| 
								 | 
							
								    ##    if not self._schemes:
							 | 
						||
| 
								 | 
							
								    ##        return
							 | 
						||
| 
								 | 
							
								    ##
							 | 
						||
| 
								 | 
							
								    ##    def strip_items(target, filter):
							 | 
						||
| 
								 | 
							
								    ##        keys = [key for key,value in iteritems(target)
							 | 
						||
| 
								 | 
							
								    ##                if filter(key,value)]
							 | 
						||
| 
								 | 
							
								    ##        for key in keys:
							 | 
						||
| 
								 | 
							
								    ##            del target[key]
							 | 
						||
| 
								 | 
							
								    ##
							 | 
						||
| 
								 | 
							
								    ##    # remove redundant default.
							 | 
						||
| 
								 | 
							
								    ##    defaults = self._default_schemes
							 | 
						||
| 
								 | 
							
								    ##    if defaults.get(None) == self._schemes[0]:
							 | 
						||
| 
								 | 
							
								    ##        del defaults[None]
							 | 
						||
| 
								 | 
							
								    ##
							 | 
						||
| 
								 | 
							
								    ##    # remove options for unused schemes.
							 | 
						||
| 
								 | 
							
								    ##    scheme_options = self._scheme_options
							 | 
						||
| 
								 | 
							
								    ##    schemes = self._schemes + ("all",)
							 | 
						||
| 
								 | 
							
								    ##    strip_items(scheme_options, lambda k,v: k not in schemes)
							 | 
						||
| 
								 | 
							
								    ##
							 | 
						||
| 
								 | 
							
								    ##    # remove rendundant cat defaults.
							 | 
						||
| 
								 | 
							
								    ##    cur = self.default_scheme()
							 | 
						||
| 
								 | 
							
								    ##    strip_items(defaults, lambda k,v: k and v==cur)
							 | 
						||
| 
								 | 
							
								    ##
							 | 
						||
| 
								 | 
							
								    ##    # remove redundant category deprecations.
							 | 
						||
| 
								 | 
							
								    ##    # TODO: this should work w/ 'auto', but needs closer inspection
							 | 
						||
| 
								 | 
							
								    ##    deprecated = self._deprecated_schemes
							 | 
						||
| 
								 | 
							
								    ##    cur = self._deprecated_schemes.get(None)
							 | 
						||
| 
								 | 
							
								    ##    strip_items(deprecated, lambda k,v: k and v==cur)
							 | 
						||
| 
								 | 
							
								    ##
							 | 
						||
| 
								 | 
							
								    ##    # remove redundant category options.
							 | 
						||
| 
								 | 
							
								    ##    for scheme, config in iteritems(scheme_options):
							 | 
						||
| 
								 | 
							
								    ##        if None in config:
							 | 
						||
| 
								 | 
							
								    ##            cur = config[None]
							 | 
						||
| 
								 | 
							
								    ##            strip_items(config, lambda k,v: k and v==cur)
							 | 
						||
| 
								 | 
							
								    ##
							 | 
						||
| 
								 | 
							
								    ##    # XXX: anything else?
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    #===================================================================
							 | 
						||
| 
								 | 
							
								    # reading configuration
							 | 
						||
| 
								 | 
							
								    #===================================================================
							 | 
						||
| 
								 | 
							
								    def schemes(self, resolve=False, category=None, unconfigured=False):
							 | 
						||
| 
								 | 
							
								        """return schemes loaded into this CryptContext instance.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :type resolve: bool
							 | 
						||
| 
								 | 
							
								        :arg resolve:
							 | 
						||
| 
								 | 
							
								            if ``True``, will return a tuple of :class:`~passlib.ifc.PasswordHash`
							 | 
						||
| 
								 | 
							
								            objects instead of their names.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :returns:
							 | 
						||
| 
								 | 
							
								            returns tuple of the schemes configured for this context
							 | 
						||
| 
								 | 
							
								            via the *schemes* option.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        .. versionadded:: 1.6
							 | 
						||
| 
								 | 
							
								            This was previously available as ``CryptContext().policy.schemes()``
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        .. seealso:: the :ref:`schemes <context-schemes-option>` option for usage example.
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        # XXX: should resolv return records rather than handlers?
							 | 
						||
| 
								 | 
							
								        #      or deprecate resolve keyword completely?
							 | 
						||
| 
								 | 
							
								        #      offering up a .hashers Mapping in v1.8 would be great.
							 | 
						||
| 
								 | 
							
								        # NOTE: supporting 'category' and 'unconfigured' kwds as of 1.7
							 | 
						||
| 
								 | 
							
								        #       just to pass through to .handler(), but not documenting them...
							 | 
						||
| 
								 | 
							
								        #       may not need to put them to use.
							 | 
						||
| 
								 | 
							
								        schemes = self._config.schemes
							 | 
						||
| 
								 | 
							
								        if resolve:
							 | 
						||
| 
								 | 
							
								            return tuple(self.handler(scheme, category, unconfigured=unconfigured)
							 | 
						||
| 
								 | 
							
								                         for scheme in schemes)
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            return schemes
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def default_scheme(self, category=None, resolve=False, unconfigured=False):
							 | 
						||
| 
								 | 
							
								        """return name of scheme that :meth:`hash` will use by default.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :type resolve: bool
							 | 
						||
| 
								 | 
							
								        :arg resolve:
							 | 
						||
| 
								 | 
							
								            if ``True``, will return a :class:`~passlib.ifc.PasswordHash`
							 | 
						||
| 
								 | 
							
								            object instead of the name.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :type category: str or None
							 | 
						||
| 
								 | 
							
								        :param category:
							 | 
						||
| 
								 | 
							
								            Optional :ref:`user category <user-categories>`.
							 | 
						||
| 
								 | 
							
								            If specified, this will return the catgory-specific default scheme instead.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :returns:
							 | 
						||
| 
								 | 
							
								            name of the default scheme.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        .. seealso:: the :ref:`default <context-default-option>` option for usage example.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        .. versionadded:: 1.6
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        .. versionchanged:: 1.7
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            This now returns a hasher configured with any CryptContext-specific
							 | 
						||
| 
								 | 
							
								            options (custom rounds settings, etc).  Previously this returned
							 | 
						||
| 
								 | 
							
								            the base hasher from :mod:`passlib.hash`.
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        # XXX: deprecate this in favor of .handler() or whatever it's replaced with?
							 | 
						||
| 
								 | 
							
								        # NOTE: supporting 'unconfigured' kwds as of 1.7
							 | 
						||
| 
								 | 
							
								        #       just to pass through to .handler(), but not documenting them...
							 | 
						||
| 
								 | 
							
								        #       may not need to put them to use.
							 | 
						||
| 
								 | 
							
								        hasher = self.handler(None, category, unconfigured=unconfigured)
							 | 
						||
| 
								 | 
							
								        return hasher if resolve else hasher.name
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # XXX: need to decide if exposing this would be useful in any way
							 | 
						||
| 
								 | 
							
								    ##def categories(self):
							 | 
						||
| 
								 | 
							
								    ##    """return user-categories with algorithm-specific options in this CryptContext.
							 | 
						||
| 
								 | 
							
								    ##
							 | 
						||
| 
								 | 
							
								    ##    this will always return a tuple.
							 | 
						||
| 
								 | 
							
								    ##    if no categories besides the default category have been configured,
							 | 
						||
| 
								 | 
							
								    ##    the tuple will be empty.
							 | 
						||
| 
								 | 
							
								    ##    """
							 | 
						||
| 
								 | 
							
								    ##    return self._config.categories
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # XXX: need to decide if exposing this would be useful to applications
							 | 
						||
| 
								 | 
							
								    # in any meaningful way that isn't already served by to_dict()
							 | 
						||
| 
								 | 
							
								    ##def options(self, scheme, category=None):
							 | 
						||
| 
								 | 
							
								    ##    kwds, percat = self._config.get_options(scheme, category)
							 | 
						||
| 
								 | 
							
								    ##    return kwds
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def handler(self, scheme=None, category=None, unconfigured=False):
							 | 
						||
| 
								 | 
							
								        """helper to resolve name of scheme -> :class:`~passlib.ifc.PasswordHash` object used by scheme.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :arg scheme:
							 | 
						||
| 
								 | 
							
								            This should identify the scheme to lookup.
							 | 
						||
| 
								 | 
							
								            If omitted or set to ``None``, this will return the handler
							 | 
						||
| 
								 | 
							
								            for the default scheme.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :arg category:
							 | 
						||
| 
								 | 
							
								            If a user category is specified, and no scheme is provided,
							 | 
						||
| 
								 | 
							
								            it will use the default for that category.
							 | 
						||
| 
								 | 
							
								            Otherwise this parameter is ignored.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :param unconfigured:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            By default, this returns a handler object whose .hash()
							 | 
						||
| 
								 | 
							
								            and .needs_update() methods will honor the configured
							 | 
						||
| 
								 | 
							
								            provided by CryptContext.   See ``unconfigured=True``
							 | 
						||
| 
								 | 
							
								            to get the underlying handler from before any context-specific
							 | 
						||
| 
								 | 
							
								            configuration was applied.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :raises KeyError:
							 | 
						||
| 
								 | 
							
								            If the scheme does not exist OR is not being used within this context.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :returns:
							 | 
						||
| 
								 | 
							
								            :class:`~passlib.ifc.PasswordHash` object used to implement
							 | 
						||
| 
								 | 
							
								            the named scheme within this context (this will usually
							 | 
						||
| 
								 | 
							
								            be one of the objects from :mod:`passlib.hash`)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        .. versionadded:: 1.6
							 | 
						||
| 
								 | 
							
								            This was previously available as ``CryptContext().policy.get_handler()``
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        .. versionchanged:: 1.7
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            This now returns a hasher configured with any CryptContext-specific
							 | 
						||
| 
								 | 
							
								            options (custom rounds settings, etc).  Previously this returned
							 | 
						||
| 
								 | 
							
								            the base hasher from :mod:`passlib.hash`.
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        try:
							 | 
						||
| 
								 | 
							
								            hasher = self._get_record(scheme, category)
							 | 
						||
| 
								 | 
							
								            if unconfigured:
							 | 
						||
| 
								 | 
							
								                return hasher._Context__orig_handler
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                return hasher
							 | 
						||
| 
								 | 
							
								        except KeyError:
							 | 
						||
| 
								 | 
							
								            pass
							 | 
						||
| 
								 | 
							
								        if self._config.handlers:
							 | 
						||
| 
								 | 
							
								            raise KeyError("crypt algorithm not found in this "
							 | 
						||
| 
								 | 
							
								                           "CryptContext instance: %r" % (scheme,))
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            raise KeyError("no crypt algorithms loaded in this "
							 | 
						||
| 
								 | 
							
								                           "CryptContext instance")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _get_unregistered_handlers(self):
							 | 
						||
| 
								 | 
							
								        """check if any handlers in this context aren't in the global registry"""
							 | 
						||
| 
								 | 
							
								        return tuple(handler for handler in self._config.handlers
							 | 
						||
| 
								 | 
							
								                     if not _is_handler_registered(handler))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @property
							 | 
						||
| 
								 | 
							
								    def context_kwds(self):
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        return :class:`!set` containing union of all :ref:`contextual keywords <context-keywords>`
							 | 
						||
| 
								 | 
							
								        supported by the handlers in this context.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        .. versionadded:: 1.6.6
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        return self._config.context_kwds
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    #===================================================================
							 | 
						||
| 
								 | 
							
								    # exporting config
							 | 
						||
| 
								 | 
							
								    #===================================================================
							 | 
						||
| 
								 | 
							
								    @staticmethod
							 | 
						||
| 
								 | 
							
								    def _render_config_key(key):
							 | 
						||
| 
								 | 
							
								        """convert 3-part config key to single string"""
							 | 
						||
| 
								 | 
							
								        cat, scheme, option = key
							 | 
						||
| 
								 | 
							
								        if cat:
							 | 
						||
| 
								 | 
							
								            return "%s__%s__%s" % (cat, scheme or "context", option)
							 | 
						||
| 
								 | 
							
								        elif scheme:
							 | 
						||
| 
								 | 
							
								            return "%s__%s" % (scheme, option)
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            return option
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @staticmethod
							 | 
						||
| 
								 | 
							
								    def _render_ini_value(key, value):
							 | 
						||
| 
								 | 
							
								        """render value to string suitable for INI file"""
							 | 
						||
| 
								 | 
							
								        # convert lists to comma separated lists
							 | 
						||
| 
								 | 
							
								        # (mainly 'schemes' & 'deprecated')
							 | 
						||
| 
								 | 
							
								        if isinstance(value, (list,tuple)):
							 | 
						||
| 
								 | 
							
								            value = ", ".join(value)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # convert numbers to strings
							 | 
						||
| 
								 | 
							
								        elif isinstance(value, num_types):
							 | 
						||
| 
								 | 
							
								            if isinstance(value, float) and key[2] == "vary_rounds":
							 | 
						||
| 
								 | 
							
								                value = ("%.2f" % value).rstrip("0") if value else "0"
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                value = str(value)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        assert isinstance(value, native_string_types), \
							 | 
						||
| 
								 | 
							
								               "expected string for key: %r %r" % (key, value)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # escape any percent signs.
							 | 
						||
| 
								 | 
							
								        return value.replace("%", "%%")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def to_dict(self, resolve=False):
							 | 
						||
| 
								 | 
							
								        """Return current configuration as a dictionary.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :type resolve: bool
							 | 
						||
| 
								 | 
							
								        :arg resolve:
							 | 
						||
| 
								 | 
							
								            if ``True``, the ``schemes`` key will contain a list of
							 | 
						||
| 
								 | 
							
								            a :class:`~passlib.ifc.PasswordHash` objects instead of just
							 | 
						||
| 
								 | 
							
								            their names.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        This method dumps the current configuration of the CryptContext
							 | 
						||
| 
								 | 
							
								        instance. The key/value pairs should be in the format accepted
							 | 
						||
| 
								 | 
							
								        by the :class:`!CryptContext` class constructor, in fact
							 | 
						||
| 
								 | 
							
								        ``CryptContext(**myctx.to_dict())`` will create an exact copy of ``myctx``.
							 | 
						||
| 
								 | 
							
								        As an example::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            >>> # you can dump the configuration of any crypt context...
							 | 
						||
| 
								 | 
							
								            >>> from passlib.apps import ldap_nocrypt_context
							 | 
						||
| 
								 | 
							
								            >>> ldap_nocrypt_context.to_dict()
							 | 
						||
| 
								 | 
							
								            {'schemes': ['ldap_salted_sha1',
							 | 
						||
| 
								 | 
							
								            'ldap_salted_md5',
							 | 
						||
| 
								 | 
							
								            'ldap_sha1',
							 | 
						||
| 
								 | 
							
								            'ldap_md5',
							 | 
						||
| 
								 | 
							
								            'ldap_plaintext']}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        .. versionadded:: 1.6
							 | 
						||
| 
								 | 
							
								            This was previously available as ``CryptContext().policy.to_dict()``
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        .. seealso:: the :ref:`context-serialization-example` example in the tutorial.
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        # XXX: should resolve default to conditional behavior
							 | 
						||
| 
								 | 
							
								        # based on presence of unregistered handlers?
							 | 
						||
| 
								 | 
							
								        render_key = self._render_config_key
							 | 
						||
| 
								 | 
							
								        return dict((render_key(key), value)
							 | 
						||
| 
								 | 
							
								                    for key, value in self._config.iter_config(resolve))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _write_to_parser(self, parser, section):
							 | 
						||
| 
								 | 
							
								        """helper to write to ConfigParser instance"""
							 | 
						||
| 
								 | 
							
								        render_key = self._render_config_key
							 | 
						||
| 
								 | 
							
								        render_value = self._render_ini_value
							 | 
						||
| 
								 | 
							
								        parser.add_section(section)
							 | 
						||
| 
								 | 
							
								        for k,v in self._config.iter_config():
							 | 
						||
| 
								 | 
							
								            v = render_value(k, v)
							 | 
						||
| 
								 | 
							
								            k = render_key(k)
							 | 
						||
| 
								 | 
							
								            parser.set(section, k, v)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def to_string(self, section="passlib"):
							 | 
						||
| 
								 | 
							
								        """serialize to INI format and return as unicode string.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :param section:
							 | 
						||
| 
								 | 
							
								            name of INI section to output, defaults to ``"passlib"``.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :returns:
							 | 
						||
| 
								 | 
							
								            CryptContext configuration, serialized to a INI unicode string.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        This function acts exactly like :meth:`to_dict`, except that it
							 | 
						||
| 
								 | 
							
								        serializes all the contents into a single human-readable string,
							 | 
						||
| 
								 | 
							
								        which can be hand edited, and/or stored in a file. The
							 | 
						||
| 
								 | 
							
								        output of this method is accepted by :meth:`from_string`,
							 | 
						||
| 
								 | 
							
								        :meth:`from_path`, and :meth:`load`. As an example::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            >>> # you can dump the configuration of any crypt context...
							 | 
						||
| 
								 | 
							
								            >>> from passlib.apps import ldap_nocrypt_context
							 | 
						||
| 
								 | 
							
								            >>> print ldap_nocrypt_context.to_string()
							 | 
						||
| 
								 | 
							
								            [passlib]
							 | 
						||
| 
								 | 
							
								            schemes = ldap_salted_sha1, ldap_salted_md5, ldap_sha1, ldap_md5, ldap_plaintext
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        .. versionadded:: 1.6
							 | 
						||
| 
								 | 
							
								            This was previously available as ``CryptContext().policy.to_string()``
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        .. seealso:: the :ref:`context-serialization-example` example in the tutorial.
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        parser = SafeConfigParser()
							 | 
						||
| 
								 | 
							
								        self._write_to_parser(parser, section)
							 | 
						||
| 
								 | 
							
								        buf = NativeStringIO()
							 | 
						||
| 
								 | 
							
								        parser.write(buf)
							 | 
						||
| 
								 | 
							
								        unregistered = self._get_unregistered_handlers()
							 | 
						||
| 
								 | 
							
								        if unregistered:
							 | 
						||
| 
								 | 
							
								            buf.write((
							 | 
						||
| 
								 | 
							
								                "# 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
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # XXX: is this useful enough to enable?
							 | 
						||
| 
								 | 
							
								    ##def write_to_path(self, path, section="passlib", update=False):
							 | 
						||
| 
								 | 
							
								    ##    "write to INI file"
							 | 
						||
| 
								 | 
							
								    ##    parser = ConfigParser()
							 | 
						||
| 
								 | 
							
								    ##    if update and os.path.exists(path):
							 | 
						||
| 
								 | 
							
								    ##        if not parser.read([path]):
							 | 
						||
| 
								 | 
							
								    ##            raise EnvironmentError("failed to read existing file")
							 | 
						||
| 
								 | 
							
								    ##        parser.remove_section(section)
							 | 
						||
| 
								 | 
							
								    ##    self._write_to_parser(parser, section)
							 | 
						||
| 
								 | 
							
								    ##    fh = file(path, "w")
							 | 
						||
| 
								 | 
							
								    ##    parser.write(fh)
							 | 
						||
| 
								 | 
							
								    ##    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
							 | 
						||
| 
								 | 
							
								    #===================================================================
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # NOTE: all the following methods do is look up the appropriate
							 | 
						||
| 
								 | 
							
								    #       custom handler for a given (scheme,category) combination,
							 | 
						||
| 
								 | 
							
								    #       and hand off the real work to the handler itself,
							 | 
						||
| 
								 | 
							
								    #       which is optimized for the specific (scheme,category) configuration.
							 | 
						||
| 
								 | 
							
								    #
							 | 
						||
| 
								 | 
							
								    #       The custom handlers are cached inside the _CryptConfig
							 | 
						||
| 
								 | 
							
								    #       instance stored in self._config, and are retrieved
							 | 
						||
| 
								 | 
							
								    #       via get_record() and identify_record().
							 | 
						||
| 
								 | 
							
								    #
							 | 
						||
| 
								 | 
							
								    #       _get_record() and _identify_record() are references
							 | 
						||
| 
								 | 
							
								    #       to _config methods of the same name,
							 | 
						||
| 
								 | 
							
								    #       stored in CryptContext for speed.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    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):
							 | 
						||
| 
								 | 
							
								                raise ExpectedStringError(hash, "hash")
							 | 
						||
| 
								 | 
							
								            return self._get_record(scheme, category)
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            # hash typecheck handled by identify_record()
							 | 
						||
| 
								 | 
							
								            return self._identify_record(hash, category)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _strip_unused_context_kwds(self, kwds, record):
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        helper which removes any context keywords from **kwds**
							 | 
						||
| 
								 | 
							
								        that are known to be used by another scheme in this context,
							 | 
						||
| 
								 | 
							
								        but are NOT supported by handler specified by **record**.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        .. note::
							 | 
						||
| 
								 | 
							
								            as optimization, load() will set this method to None on a per-instance basis
							 | 
						||
| 
								 | 
							
								            if there are no context kwds.
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        if not kwds:
							 | 
						||
| 
								 | 
							
								            return
							 | 
						||
| 
								 | 
							
								        unused_kwds = self._config.context_kwds.difference(record.context_kwds)
							 | 
						||
| 
								 | 
							
								        for key in unused_kwds:
							 | 
						||
| 
								 | 
							
								            kwds.pop(key, None)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def needs_update(self, hash, scheme=None, category=None, secret=None):
							 | 
						||
| 
								 | 
							
								        """Check if hash needs to be replaced for some reason,
							 | 
						||
| 
								 | 
							
								        in which case the secret should be re-hashed.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        This function is the core of CryptContext's support for hash migration:
							 | 
						||
| 
								 | 
							
								        This function takes in a hash string, and checks the scheme,
							 | 
						||
| 
								 | 
							
								        number of rounds, and other properties against the current policy.
							 | 
						||
| 
								 | 
							
								        It returns ``True`` if the hash is using a deprecated scheme,
							 | 
						||
| 
								 | 
							
								        or is otherwise outside of the bounds specified by the policy
							 | 
						||
| 
								 | 
							
								        (e.g. the number of rounds is lower than :ref:`min_rounds <context-min-rounds-option>`
							 | 
						||
| 
								 | 
							
								        configuration for that algorithm).
							 | 
						||
| 
								 | 
							
								        If so, the password should be re-hashed using :meth:`hash`
							 | 
						||
| 
								 | 
							
								        Otherwise, it will return ``False``.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :type hash: unicode or bytes
							 | 
						||
| 
								 | 
							
								        :arg hash:
							 | 
						||
| 
								 | 
							
								            The hash string to examine.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :type scheme: str or None
							 | 
						||
| 
								 | 
							
								        :param scheme:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            Optional scheme to use. Scheme must be one of the ones
							 | 
						||
| 
								 | 
							
								            configured for this context (see the
							 | 
						||
| 
								 | 
							
								            :ref:`schemes <context-schemes-option>` option).
							 | 
						||
| 
								 | 
							
								            If no scheme is specified, it will be identified
							 | 
						||
| 
								 | 
							
								            based on the value of *hash*.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            .. deprecated:: 1.7
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                Support for this keyword is deprecated, and will be removed in Passlib 2.0.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :type category: str or None
							 | 
						||
| 
								 | 
							
								        :param category:
							 | 
						||
| 
								 | 
							
								            Optional :ref:`user category <user-categories>`.
							 | 
						||
| 
								 | 
							
								            If specified, this will cause any category-specific defaults to
							 | 
						||
| 
								 | 
							
								            be used when determining if the hash needs to be updated
							 | 
						||
| 
								 | 
							
								            (e.g. is below the minimum rounds).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :type secret: unicode, bytes, or None
							 | 
						||
| 
								 | 
							
								        :param secret:
							 | 
						||
| 
								 | 
							
								            Optional secret associated with the provided ``hash``.
							 | 
						||
| 
								 | 
							
								            This is not required, or even currently used for anything...
							 | 
						||
| 
								 | 
							
								            it's for forward-compatibility with any future
							 | 
						||
| 
								 | 
							
								            update checks that might need this information.
							 | 
						||
| 
								 | 
							
								            If provided, Passlib assumes the secret has already been
							 | 
						||
| 
								 | 
							
								            verified successfully against the hash.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            .. versionadded:: 1.6
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :returns: ``True`` if hash should be replaced, otherwise ``False``.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :raises ValueError:
							 | 
						||
| 
								 | 
							
								            If the hash did not match any of the configured :meth:`schemes`.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        .. versionadded:: 1.6
							 | 
						||
| 
								 | 
							
								            This method was previously named :meth:`hash_needs_update`.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        .. seealso:: the :ref:`context-migration-example` example in the tutorial.
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        if scheme is not None:
							 | 
						||
| 
								 | 
							
								            # TODO: offer replacement alternative.
							 | 
						||
| 
								 | 
							
								            #       ``context.handler(scheme).needs_update()`` would work,
							 | 
						||
| 
								 | 
							
								            #       but may deprecate .handler() in passlib 1.8.
							 | 
						||
| 
								 | 
							
								            warn("CryptContext.needs_update(): 'scheme' keyword is deprecated as of "
							 | 
						||
| 
								 | 
							
								                 "Passlib 1.7, and will be removed in Passlib 2.0",
							 | 
						||
| 
								 | 
							
								                 DeprecationWarning)
							 | 
						||
| 
								 | 
							
								        record = self._get_or_identify_record(hash, scheme, category)
							 | 
						||
| 
								 | 
							
								        return record.deprecated or record.needs_update(hash, secret=secret)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @deprecated_method(deprecated="1.6", removed="2.0", replacement="CryptContext.needs_update()")
							 | 
						||
| 
								 | 
							
								    def hash_needs_update(self, hash, scheme=None, category=None):
							 | 
						||
| 
								 | 
							
								        """Legacy alias for :meth:`needs_update`.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        .. deprecated:: 1.6
							 | 
						||
| 
								 | 
							
								            This method was renamed to :meth:`!needs_update` in version 1.6.
							 | 
						||
| 
								 | 
							
								            This alias will be removed in version 2.0, and should only
							 | 
						||
| 
								 | 
							
								            be used for compatibility with Passlib 1.3 - 1.5.
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        return self.needs_update(hash, scheme, category)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @deprecated_method(deprecated="1.7", removed="2.0")
							 | 
						||
| 
								 | 
							
								    def genconfig(self, scheme=None, category=None, **settings):
							 | 
						||
| 
								 | 
							
								        """Generate a config string for specified scheme.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        .. deprecated:: 1.7
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            This method will be removed in version 2.0, and should only
							 | 
						||
| 
								 | 
							
								            be used for compatibility with Passlib 1.3 - 1.6.
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        record = self._get_record(scheme, category)
							 | 
						||
| 
								 | 
							
								        strip_unused = self._strip_unused_context_kwds
							 | 
						||
| 
								 | 
							
								        if strip_unused:
							 | 
						||
| 
								 | 
							
								            strip_unused(settings, record)
							 | 
						||
| 
								 | 
							
								        return record.genconfig(**settings)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @deprecated_method(deprecated="1.7", removed="2.0")
							 | 
						||
| 
								 | 
							
								    def genhash(self, secret, config, scheme=None, category=None, **kwds):
							 | 
						||
| 
								 | 
							
								        """Generate hash for the specified secret using another hash.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        .. deprecated:: 1.7
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            This method will be removed in version 2.0, and should only
							 | 
						||
| 
								 | 
							
								            be used for compatibility with Passlib 1.3 - 1.6.
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        record = self._get_or_identify_record(config, scheme, category)
							 | 
						||
| 
								 | 
							
								        strip_unused = self._strip_unused_context_kwds
							 | 
						||
| 
								 | 
							
								        if strip_unused:
							 | 
						||
| 
								 | 
							
								            strip_unused(kwds, record)
							 | 
						||
| 
								 | 
							
								        return record.genhash(secret, config, **kwds)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def identify(self, hash, category=None, resolve=False, required=False,
							 | 
						||
| 
								 | 
							
								                 unconfigured=False):
							 | 
						||
| 
								 | 
							
								        """Attempt to identify which algorithm the hash belongs to.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        Note that this will only consider the algorithms
							 | 
						||
| 
								 | 
							
								        currently configured for this context
							 | 
						||
| 
								 | 
							
								        (see the :ref:`schemes <context-schemes-option>` option).
							 | 
						||
| 
								 | 
							
								        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
							 | 
						||
| 
								 | 
							
								        :arg hash:
							 | 
						||
| 
								 | 
							
								            The hash string to test.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :type category: str or None
							 | 
						||
| 
								 | 
							
								        :param category:
							 | 
						||
| 
								 | 
							
								            Optional :ref:`user category <user-categories>`.
							 | 
						||
| 
								 | 
							
								            Ignored by this function, this parameter
							 | 
						||
| 
								 | 
							
								            is provided for symmetry with the other methods.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :type resolve: bool
							 | 
						||
| 
								 | 
							
								        :param resolve:
							 | 
						||
| 
								 | 
							
								            If ``True``, returns the hash handler itself,
							 | 
						||
| 
								 | 
							
								            instead of the name of the hash.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :type required: bool
							 | 
						||
| 
								 | 
							
								        :param required:
							 | 
						||
| 
								 | 
							
								            If ``True``, this will raise a ValueError if the hash
							 | 
						||
| 
								 | 
							
								            cannot be identified, instead of returning ``None``.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :returns:
							 | 
						||
| 
								 | 
							
								            The handler which first identifies the hash,
							 | 
						||
| 
								 | 
							
								            or ``None`` if none of the algorithms identify the hash.
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        record = self._identify_record(hash, category, required)
							 | 
						||
| 
								 | 
							
								        if record is None:
							 | 
						||
| 
								 | 
							
								            return None
							 | 
						||
| 
								 | 
							
								        elif resolve:
							 | 
						||
| 
								 | 
							
								            if unconfigured:
							 | 
						||
| 
								 | 
							
								                return record._Context__orig_handler
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                return record
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            return record.name
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def hash(self, secret, scheme=None, category=None, **kwds):
							 | 
						||
| 
								 | 
							
								        """run secret through selected algorithm, returning resulting hash.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :type secret: unicode or bytes
							 | 
						||
| 
								 | 
							
								        :arg secret:
							 | 
						||
| 
								 | 
							
								            the password to hash.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :type scheme: str or None
							 | 
						||
| 
								 | 
							
								        :param scheme:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            Optional scheme to use. Scheme must be one of the ones
							 | 
						||
| 
								 | 
							
								            configured for this context (see the
							 | 
						||
| 
								 | 
							
								            :ref:`schemes <context-schemes-option>` option).
							 | 
						||
| 
								 | 
							
								            If no scheme is specified, the configured default
							 | 
						||
| 
								 | 
							
								            will be used.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            .. deprecated:: 1.7
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                Support for this keyword is deprecated, and will be removed in Passlib 2.0.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :type category: str or None
							 | 
						||
| 
								 | 
							
								        :param category:
							 | 
						||
| 
								 | 
							
								            Optional :ref:`user category <user-categories>`.
							 | 
						||
| 
								 | 
							
								            If specified, this will cause any category-specific defaults to
							 | 
						||
| 
								 | 
							
								            be used when hashing the password (e.g. different default scheme,
							 | 
						||
| 
								 | 
							
								            different default rounds values, etc).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :param \\*\\*kwds:
							 | 
						||
| 
								 | 
							
								            All other keyword options are passed to the selected algorithm's
							 | 
						||
| 
								 | 
							
								            :meth:`PasswordHash.hash() <passlib.ifc.PasswordHash.hash>` method.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :returns:
							 | 
						||
| 
								 | 
							
								            The secret as encoded by the specified algorithm and options.
							 | 
						||
| 
								 | 
							
								            The return value will always be a :class:`!str`.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :raises TypeError, ValueError:
							 | 
						||
| 
								 | 
							
								            * If any of the arguments have an invalid type or value.
							 | 
						||
| 
								 | 
							
								              This includes any keywords passed to the underlying hash's
							 | 
						||
| 
								 | 
							
								              :meth:`PasswordHash.hash() <passlib.ifc.PasswordHash.hash>` method.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        .. seealso:: the :ref:`context-basic-example` example in the tutorial
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        # XXX: could insert normalization to preferred unicode encoding here
							 | 
						||
| 
								 | 
							
								        if scheme is not None:
							 | 
						||
| 
								 | 
							
								            # TODO: offer replacement alternative.
							 | 
						||
| 
								 | 
							
								            #       ``context.handler(scheme).hash()`` would work,
							 | 
						||
| 
								 | 
							
								            #       but may deprecate .handler() in passlib 1.8.
							 | 
						||
| 
								 | 
							
								            warn("CryptContext.hash(): 'scheme' keyword is deprecated as of "
							 | 
						||
| 
								 | 
							
								                 "Passlib 1.7, and will be removed in Passlib 2.0",
							 | 
						||
| 
								 | 
							
								                 DeprecationWarning)
							 | 
						||
| 
								 | 
							
								        record = self._get_record(scheme, category)
							 | 
						||
| 
								 | 
							
								        strip_unused = self._strip_unused_context_kwds
							 | 
						||
| 
								 | 
							
								        if strip_unused:
							 | 
						||
| 
								 | 
							
								            strip_unused(kwds, record)
							 | 
						||
| 
								 | 
							
								        return record.hash(secret, **kwds)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @deprecated_method(deprecated="1.7", removed="2.0", replacement="CryptContext.hash()")
							 | 
						||
| 
								 | 
							
								    def encrypt(self, *args, **kwds):
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        Legacy alias for :meth:`hash`.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        .. deprecated:: 1.7
							 | 
						||
| 
								 | 
							
								            This method was renamed to :meth:`!hash` in version 1.7.
							 | 
						||
| 
								 | 
							
								            This alias will be removed in version 2.0, and should only
							 | 
						||
| 
								 | 
							
								            be used for compatibility with Passlib 1.3 - 1.6.
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        return self.hash(*args, **kwds)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def verify(self, secret, hash, scheme=None, category=None, **kwds):
							 | 
						||
| 
								 | 
							
								        """verify secret against an existing hash.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        If no scheme is specified, this will attempt to identify
							 | 
						||
| 
								 | 
							
								        the scheme based on the contents of the provided hash
							 | 
						||
| 
								 | 
							
								        (limited to the schemes configured for this context).
							 | 
						||
| 
								 | 
							
								        It will then check whether the password verifies against the hash.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :type secret: unicode or bytes
							 | 
						||
| 
								 | 
							
								        :arg secret:
							 | 
						||
| 
								 | 
							
								            the secret to verify
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :type hash: unicode or bytes
							 | 
						||
| 
								 | 
							
								        :arg hash:
							 | 
						||
| 
								 | 
							
								            hash string to compare to
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if ``None`` is passed in, this will be treated as "never verifying"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :type scheme: str
							 | 
						||
| 
								 | 
							
								        :param scheme:
							 | 
						||
| 
								 | 
							
								            Optionally force context to use specific scheme.
							 | 
						||
| 
								 | 
							
								            This is usually not needed, as most hashes can be unambiguously
							 | 
						||
| 
								 | 
							
								            identified. Scheme must be one of the ones configured
							 | 
						||
| 
								 | 
							
								            for this context
							 | 
						||
| 
								 | 
							
								            (see the :ref:`schemes <context-schemes-option>` option).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            .. deprecated:: 1.7
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                Support for this keyword is deprecated, and will be removed in Passlib 2.0.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :type category: str or None
							 | 
						||
| 
								 | 
							
								        :param category:
							 | 
						||
| 
								 | 
							
								            Optional :ref:`user category <user-categories>` string.
							 | 
						||
| 
								 | 
							
								            This is mainly used when generating new hashes, it has little
							 | 
						||
| 
								 | 
							
								            effect when verifying; this keyword is mainly provided for symmetry.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :param \\*\\*kwds:
							 | 
						||
| 
								 | 
							
								            All additional keywords are passed to the appropriate handler,
							 | 
						||
| 
								 | 
							
								            and should match its :attr:`~passlib.ifc.PasswordHash.context_kwds`.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :returns:
							 | 
						||
| 
								 | 
							
								            ``True`` if the password matched the hash, else ``False``.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :raises ValueError:
							 | 
						||
| 
								 | 
							
								            * if the hash did not match any of the configured :meth:`schemes`.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            * if any of the arguments have an invalid value (this includes
							 | 
						||
| 
								 | 
							
								              any keywords passed to the underlying hash's
							 | 
						||
| 
								 | 
							
								              :meth:`PasswordHash.verify() <passlib.ifc.PasswordHash.verify>` method).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :raises TypeError:
							 | 
						||
| 
								 | 
							
								            * if any of the arguments have an invalid type (this includes
							 | 
						||
| 
								 | 
							
								              any keywords passed to the underlying hash's
							 | 
						||
| 
								 | 
							
								              :meth:`PasswordHash.verify() <passlib.ifc.PasswordHash.verify>` method).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        .. seealso:: the :ref:`context-basic-example` example in the tutorial
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        # XXX: could insert normalization to preferred unicode encoding here
							 | 
						||
| 
								 | 
							
								        # XXX: what about supporting a setter() callback ala django 1.4 ?
							 | 
						||
| 
								 | 
							
								        if scheme is not None:
							 | 
						||
| 
								 | 
							
								            # TODO: offer replacement alternative.
							 | 
						||
| 
								 | 
							
								            #       ``context.handler(scheme).verify()`` would work,
							 | 
						||
| 
								 | 
							
								            #       but may deprecate .handler() in passlib 1.8.
							 | 
						||
| 
								 | 
							
								            warn("CryptContext.verify(): 'scheme' keyword is deprecated as of "
							 | 
						||
| 
								 | 
							
								                 "Passlib 1.7, and will be removed in Passlib 2.0",
							 | 
						||
| 
								 | 
							
								                 DeprecationWarning)
							 | 
						||
| 
								 | 
							
								        if hash is None:
							 | 
						||
| 
								 | 
							
								            # convenience feature -- let apps pass in hash=None when user
							 | 
						||
| 
								 | 
							
								            # isn't found / has no hash; useful because it invokes dummy_verify()
							 | 
						||
| 
								 | 
							
								            self.dummy_verify()
							 | 
						||
| 
								 | 
							
								            return False
							 | 
						||
| 
								 | 
							
								        record = self._get_or_identify_record(hash, scheme, category)
							 | 
						||
| 
								 | 
							
								        strip_unused = self._strip_unused_context_kwds
							 | 
						||
| 
								 | 
							
								        if strip_unused:
							 | 
						||
| 
								 | 
							
								            strip_unused(kwds, record)
							 | 
						||
| 
								 | 
							
								        return record.verify(secret, hash, **kwds)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def verify_and_update(self, secret, hash, scheme=None, category=None, **kwds):
							 | 
						||
| 
								 | 
							
								        """verify password and re-hash the password if needed, all in a single call.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        This is a convenience method which takes care of all the following:
							 | 
						||
| 
								 | 
							
								        first it verifies the password (:meth:`~CryptContext.verify`), if this is successfull
							 | 
						||
| 
								 | 
							
								        it checks if the hash needs updating (:meth:`~CryptContext.needs_update`), and if so,
							 | 
						||
| 
								 | 
							
								        re-hashes the password (:meth:`~CryptContext.hash`), returning the replacement hash.
							 | 
						||
| 
								 | 
							
								        This series of steps is a very common task for applications
							 | 
						||
| 
								 | 
							
								        which wish to update deprecated hashes, and this call takes
							 | 
						||
| 
								 | 
							
								        care of all 3 steps efficiently.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :type secret: unicode or bytes
							 | 
						||
| 
								 | 
							
								        :arg secret:
							 | 
						||
| 
								 | 
							
								            the secret to verify
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :type secret: unicode or bytes
							 | 
						||
| 
								 | 
							
								        :arg hash:
							 | 
						||
| 
								 | 
							
								            hash string to compare to.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if ``None`` is passed in, this will be treated as "never verifying"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :type scheme: str
							 | 
						||
| 
								 | 
							
								        :param scheme:
							 | 
						||
| 
								 | 
							
								            Optionally force context to use specific scheme.
							 | 
						||
| 
								 | 
							
								            This is usually not needed, as most hashes can be unambiguously
							 | 
						||
| 
								 | 
							
								            identified. Scheme must be one of the ones configured
							 | 
						||
| 
								 | 
							
								            for this context
							 | 
						||
| 
								 | 
							
								            (see the :ref:`schemes <context-schemes-option>` option).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            .. deprecated:: 1.7
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                Support for this keyword is deprecated, and will be removed in Passlib 2.0.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :type category: str or None
							 | 
						||
| 
								 | 
							
								        :param category:
							 | 
						||
| 
								 | 
							
								            Optional :ref:`user category <user-categories>`.
							 | 
						||
| 
								 | 
							
								            If specified, this will cause any category-specific defaults to
							 | 
						||
| 
								 | 
							
								            be used if the password has to be re-hashed.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :param \\*\\*kwds:
							 | 
						||
| 
								 | 
							
								            all additional keywords are passed to the appropriate handler,
							 | 
						||
| 
								 | 
							
								            and should match that hash's
							 | 
						||
| 
								 | 
							
								            :attr:`PasswordHash.context_kwds <passlib.ifc.PasswordHash.context_kwds>`.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :returns:
							 | 
						||
| 
								 | 
							
								            This function returns a tuple containing two elements:
							 | 
						||
| 
								 | 
							
								            ``(verified, replacement_hash)``. The first is a boolean
							 | 
						||
| 
								 | 
							
								            flag indicating whether the password verified,
							 | 
						||
| 
								 | 
							
								            and the second an optional replacement hash.
							 | 
						||
| 
								 | 
							
								            The tuple will always match one of the following 3 cases:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            * ``(False, None)`` indicates the secret failed to verify.
							 | 
						||
| 
								 | 
							
								            * ``(True, None)`` indicates the secret verified correctly,
							 | 
						||
| 
								 | 
							
								              and the hash does not need updating.
							 | 
						||
| 
								 | 
							
								            * ``(True, str)`` indicates the secret verified correctly,
							 | 
						||
| 
								 | 
							
								              but the current hash needs to be updated. The :class:`!str`
							 | 
						||
| 
								 | 
							
								              will be the freshly generated hash, to replace the old one.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :raises TypeError, ValueError:
							 | 
						||
| 
								 | 
							
								            For the same reasons as :meth:`verify`.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        .. seealso:: the :ref:`context-migration-example` example in the tutorial.
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        # XXX: could insert normalization to preferred unicode encoding here.
							 | 
						||
| 
								 | 
							
								        if scheme is not None:
							 | 
						||
| 
								 | 
							
								            warn("CryptContext.verify(): 'scheme' keyword is deprecated as of "
							 | 
						||
| 
								 | 
							
								                 "Passlib 1.7, and will be removed in Passlib 2.0",
							 | 
						||
| 
								 | 
							
								                 DeprecationWarning)
							 | 
						||
| 
								 | 
							
								        if hash is None:
							 | 
						||
| 
								 | 
							
								            # convenience feature -- let apps pass in hash=None when user
							 | 
						||
| 
								 | 
							
								            # isn't found / has no hash; useful because it invokes dummy_verify()
							 | 
						||
| 
								 | 
							
								            self.dummy_verify()
							 | 
						||
| 
								 | 
							
								            return False, None
							 | 
						||
| 
								 | 
							
								        record = self._get_or_identify_record(hash, scheme, category)
							 | 
						||
| 
								 | 
							
								        strip_unused = self._strip_unused_context_kwds
							 | 
						||
| 
								 | 
							
								        if strip_unused and kwds:
							 | 
						||
| 
								 | 
							
								            clean_kwds = kwds.copy()
							 | 
						||
| 
								 | 
							
								            strip_unused(clean_kwds, record)
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            clean_kwds = kwds
							 | 
						||
| 
								 | 
							
								        # XXX: if record is default scheme, could extend PasswordHash
							 | 
						||
| 
								 | 
							
								        #      api to combine verify & needs_update to single call,
							 | 
						||
| 
								 | 
							
								        #      potentially saving some round-trip parsing.
							 | 
						||
| 
								 | 
							
								        #      but might make these codepaths more complex...
							 | 
						||
| 
								 | 
							
								        if not record.verify(secret, hash, **clean_kwds):
							 | 
						||
| 
								 | 
							
								            return False, None
							 | 
						||
| 
								 | 
							
								        elif record.deprecated or record.needs_update(hash, secret=secret):
							 | 
						||
| 
								 | 
							
								            # NOTE: we re-hash with default scheme, not current one.
							 | 
						||
| 
								 | 
							
								            return True, self.hash(secret, category=category, **kwds)
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            return True, None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    #===================================================================
							 | 
						||
| 
								 | 
							
								    # missing-user helper
							 | 
						||
| 
								 | 
							
								    #===================================================================
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    #: secret used for dummy_verify()
							 | 
						||
| 
								 | 
							
								    _dummy_secret = "too many secrets"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @memoized_property
							 | 
						||
| 
								 | 
							
								    def _dummy_hash(self):
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        precalculated hash for dummy_verify() to use
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        return self.hash(self._dummy_secret)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _reset_dummy_verify(self):
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        flush memoized values used by dummy_verify()
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        type(self)._dummy_hash.clear_cache(self)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def dummy_verify(self, elapsed=0):
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        Helper that applications can call when user wasn't found,
							 | 
						||
| 
								 | 
							
								        in order to simulate time it would take to hash a password.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        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)
							 | 
						||
| 
								 | 
							
								        return False
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    #===================================================================
							 | 
						||
| 
								 | 
							
								    # disabled hash support
							 | 
						||
| 
								 | 
							
								    #===================================================================
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def is_enabled(self, hash):
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        test if hash represents a usuable password --
							 | 
						||
| 
								 | 
							
								        i.e. does not represent an unusuable password such as ``"!"``,
							 | 
						||
| 
								 | 
							
								        which is recognized by the :class:`~passlib.hash.unix_disabled` hash.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :raises ValueError:
							 | 
						||
| 
								 | 
							
								            if the hash is not recognized
							 | 
						||
| 
								 | 
							
								            (typically solved by adding ``unix_disabled`` to the list of schemes).
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        return not self._identify_record(hash, None).is_disabled
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def disable(self, hash=None):
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        return a string to disable logins for user,
							 | 
						||
| 
								 | 
							
								        usually by returning a non-verifying string such as ``"!"``.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :param hash:
							 | 
						||
| 
								 | 
							
								            Callers can optionally provide the account's existing hash.
							 | 
						||
| 
								 | 
							
								            Some disabled handlers (such as :class:`!unix_disabled`)
							 | 
						||
| 
								 | 
							
								            will encode this into the returned value,
							 | 
						||
| 
								 | 
							
								            so that it can be recovered via :meth:`enable`.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :raises RuntimeError:
							 | 
						||
| 
								 | 
							
								            if this function is called w/o a disabled hasher
							 | 
						||
| 
								 | 
							
								            (such as :class:`~passlib.hash.unix_disabled`) included
							 | 
						||
| 
								 | 
							
								            in the list of schemes.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :returns:
							 | 
						||
| 
								 | 
							
								            hash string which will be recognized as valid by the context,
							 | 
						||
| 
								 | 
							
								            but is guaranteed to not validate against *any* password.
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        record = self._config.disabled_record
							 | 
						||
| 
								 | 
							
								        assert record.is_disabled
							 | 
						||
| 
								 | 
							
								        return record.disable(hash)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def enable(self, hash):
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        inverse of :meth:`disable` --
							 | 
						||
| 
								 | 
							
								        attempts to recover original hash which was converted
							 | 
						||
| 
								 | 
							
								        by a :meth:`!disable` call into a disabled hash --
							 | 
						||
| 
								 | 
							
								        thus restoring the user's original password.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :raises ValueError:
							 | 
						||
| 
								 | 
							
								            if original hash not present, or if the disabled handler doesn't
							 | 
						||
| 
								 | 
							
								            support encoding the original hash (e.g. ``django_disabled``)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :returns:
							 | 
						||
| 
								 | 
							
								            the original hash.
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        record = self._identify_record(hash, None)
							 | 
						||
| 
								 | 
							
								        if record.is_disabled:
							 | 
						||
| 
								 | 
							
								            # XXX: should we throw error if result can't be identified by context?
							 | 
						||
| 
								 | 
							
								            return record.enable(hash)
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            # hash wasn't a disabled hash, so return unchanged
							 | 
						||
| 
								 | 
							
								            return hash
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    #===================================================================
							 | 
						||
| 
								 | 
							
								    # eoc
							 | 
						||
| 
								 | 
							
								    #===================================================================
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class LazyCryptContext(CryptContext):
							 | 
						||
| 
								 | 
							
								    """CryptContext subclass which doesn't load handlers until needed.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    This is a subclass of CryptContext which takes in a set of arguments
							 | 
						||
| 
								 | 
							
								    exactly like CryptContext, but won't import any handlers
							 | 
						||
| 
								 | 
							
								    (or even parse its arguments) until
							 | 
						||
| 
								 | 
							
								    the first time one of its methods is accessed.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    :arg schemes:
							 | 
						||
| 
								 | 
							
								        The first positional argument can be a list of schemes, or omitted,
							 | 
						||
| 
								 | 
							
								        just like CryptContext.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    :param onload:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        If a callable is passed in via this keyword,
							 | 
						||
| 
								 | 
							
								        it will be invoked at lazy-load time
							 | 
						||
| 
								 | 
							
								        with the following signature:
							 | 
						||
| 
								 | 
							
								        ``onload(**kwds) -> kwds``;
							 | 
						||
| 
								 | 
							
								        where ``kwds`` is all the additional kwds passed to LazyCryptContext.
							 | 
						||
| 
								 | 
							
								        It should perform any additional deferred initialization,
							 | 
						||
| 
								 | 
							
								        and return the final dict of options to be passed to 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;
							 | 
						||
| 
								 | 
							
								        or to the *onload* function (if provided).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    This is mainly used internally by modules such as :mod:`passlib.apps`,
							 | 
						||
| 
								 | 
							
								    which define a large number of contexts, but only a few of them will be needed
							 | 
						||
| 
								 | 
							
								    at any one time. Use of this class saves the memory needed to import
							 | 
						||
| 
								 | 
							
								    the specified handlers until the context instance is actually accessed.
							 | 
						||
| 
								 | 
							
								    As well, it allows constructing a context at *module-init* time,
							 | 
						||
| 
								 | 
							
								    but using :func:`!onload()` to provide dynamic configuration
							 | 
						||
| 
								 | 
							
								    at *application-run* time.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    .. note:: 
							 | 
						||
| 
								 | 
							
								        This class is only useful if you're referencing handler objects by name,
							 | 
						||
| 
								 | 
							
								        and don't want them imported until runtime. If you want to have the config
							 | 
						||
| 
								 | 
							
								        validated before your application runs, or are passing in already-imported
							 | 
						||
| 
								 | 
							
								        handler instances, you should use :class:`CryptContext` instead.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    .. versionadded:: 1.4
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    _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.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def __init__(self, schemes=None, **kwds):
							 | 
						||
| 
								 | 
							
								        if schemes is not None:
							 | 
						||
| 
								 | 
							
								            kwds['schemes'] = schemes
							 | 
						||
| 
								 | 
							
								        self._lazy_kwds = kwds
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    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:
							 | 
						||
| 
								 | 
							
								            onload = kwds.pop("onload")
							 | 
						||
| 
								 | 
							
								            kwds = onload(**kwds)
							 | 
						||
| 
								 | 
							
								        del self._lazy_kwds
							 | 
						||
| 
								 | 
							
								        super(LazyCryptContext, self).__init__(**kwds)
							 | 
						||
| 
								 | 
							
								        self.__class__ = CryptContext
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def __getattribute__(self, attr):
							 | 
						||
| 
								 | 
							
								        if (not attr.startswith("_") or attr.startswith("__")) and \
							 | 
						||
| 
								 | 
							
								            self._lazy_kwds is not None:
							 | 
						||
| 
								 | 
							
								                self._lazy_init()
							 | 
						||
| 
								 | 
							
								        return object.__getattribute__(self, attr)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#=============================================================================
							 | 
						||
| 
								 | 
							
								# eof
							 | 
						||
| 
								 | 
							
								#=============================================================================
							 |