mirror of
https://github.com/GAM-team/GAM.git
synced 2026-06-29 18:31:38 +00:00
passlib 1.7.1
This commit is contained in:
@@ -7,28 +7,17 @@ import logging; log = logging.getLogger(__name__)
|
||||
import sys
|
||||
# site
|
||||
# pkg
|
||||
from passlib.utils.decor import deprecated_method
|
||||
# local
|
||||
__all__ = [
|
||||
"PasswordHash",
|
||||
]
|
||||
|
||||
#=============================================================================
|
||||
# 2.5-3.2 compatibility helpers
|
||||
# 2/3 compatibility helpers
|
||||
#=============================================================================
|
||||
if sys.version_info >= (2,6):
|
||||
from abc import ABCMeta, abstractmethod, abstractproperty
|
||||
else:
|
||||
# create stub for python 2.5
|
||||
ABCMeta = type
|
||||
def abstractmethod(func):
|
||||
return func
|
||||
# def abstractproperty():
|
||||
# return None
|
||||
|
||||
def create_with_metaclass(meta):
|
||||
def recreate_with_metaclass(meta):
|
||||
"""class decorator that re-creates class using metaclass"""
|
||||
# have to do things this way since abc not present in py25,
|
||||
# and py2/py3 have different ways of doing metaclasses.
|
||||
def builder(cls):
|
||||
if meta is type(cls):
|
||||
return cls
|
||||
@@ -38,6 +27,14 @@ def create_with_metaclass(meta):
|
||||
#=============================================================================
|
||||
# PasswordHash interface
|
||||
#=============================================================================
|
||||
from abc import ABCMeta, abstractmethod, abstractproperty
|
||||
|
||||
# TODO: make this actually use abstractproperty(),
|
||||
# now that we dropped py25, 'abc' is always available.
|
||||
|
||||
# XXX: rename to PasswordHasher?
|
||||
|
||||
@recreate_with_metaclass(ABCMeta)
|
||||
class PasswordHash(object):
|
||||
"""This class describes an abstract interface which all password hashes
|
||||
in Passlib adhere to. Under Python 2.6 and up, this is an actual
|
||||
@@ -56,6 +53,39 @@ class PasswordHash(object):
|
||||
##setting_kwds
|
||||
##context_kwds
|
||||
|
||||
#: flag which indicates this hasher matches a "disabled" hash
|
||||
#: (e.g. unix_disabled, or django_disabled); and doesn't actually
|
||||
#: depend on the provided password.
|
||||
is_disabled = False
|
||||
|
||||
#: Should be None, or a positive integer indicating hash
|
||||
#: doesn't support secrets larger than this value.
|
||||
#: Whether hash throws error or silently truncates secret
|
||||
#: depends on .truncate_error and .truncate_verify_reject flags below.
|
||||
#: NOTE: calls may treat as boolean, since value will never be 0.
|
||||
#: .. versionadded:: 1.7
|
||||
#: .. TODO: passlib 1.8: deprecate/rename this attr to "max_secret_size"?
|
||||
truncate_size = None
|
||||
|
||||
# NOTE: these next two default to the optimistic "ideal",
|
||||
# most hashes in passlib have to default to False
|
||||
# for backward compat and/or expected behavior with existing hashes.
|
||||
|
||||
#: If True, .hash() should throw a :exc:`~passlib.exc.PasswordSizeError` for
|
||||
#: any secrets larger than .truncate_size. Many hashers default to False
|
||||
#: for historical / compatibility purposes, indicating they will silently
|
||||
#: truncate instead. All such hashers SHOULD support changing
|
||||
#: the policy via ``.using(truncate_error=True)``.
|
||||
#: .. versionadded:: 1.7
|
||||
#: .. TODO: passlib 1.8: deprecate/rename this attr to "truncate_hash_error"?
|
||||
truncate_error = True
|
||||
|
||||
#: If True, .verify() should reject secrets larger than max_password_size.
|
||||
#: Many hashers default to False for historical / compatibility purposes,
|
||||
#: indicating they will match on the truncated portion instead.
|
||||
#: .. versionadded:: 1.7.1
|
||||
truncate_verify_reject = True
|
||||
|
||||
#---------------------------------------------------------------
|
||||
# salt information -- if 'salt' in setting_kwds
|
||||
#---------------------------------------------------------------
|
||||
@@ -83,16 +113,104 @@ class PasswordHash(object):
|
||||
#===================================================================
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
def encrypt(cls, secret, **setting_and_context_kwds): # pragma: no cover -- abstract method
|
||||
"""encrypt secret, returning resulting hash"""
|
||||
def hash(cls, secret, # *
|
||||
**setting_and_context_kwds): # pragma: no cover -- abstract method
|
||||
r"""
|
||||
Hash secret, returning result.
|
||||
Should handle generating salt, etc, and should return string
|
||||
containing identifier, salt & other configuration, as well as digest.
|
||||
|
||||
:param \*\*settings_kwds:
|
||||
|
||||
Pass in settings to customize configuration of resulting hash.
|
||||
|
||||
.. deprecated:: 1.7
|
||||
|
||||
Starting with Passlib 1.7, callers should no longer pass settings keywords
|
||||
(e.g. ``rounds`` or ``salt`` directly to :meth:`!hash`); should use
|
||||
``.using(**settings).hash(secret)`` construction instead.
|
||||
|
||||
Support will be removed in Passlib 2.0.
|
||||
|
||||
:param \*\*context_kwds:
|
||||
|
||||
Specific algorithms may require context-specific information (such as the user login).
|
||||
"""
|
||||
# FIXME: need stub for classes that define .encrypt() instead ...
|
||||
# this should call .encrypt(), and check for recursion back to here.
|
||||
raise NotImplementedError("must be implemented by subclass")
|
||||
|
||||
@deprecated_method(deprecated="1.7", removed="2.0", replacement=".hash()")
|
||||
@classmethod
|
||||
def encrypt(cls, *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 cls.hash(*args, **kwds)
|
||||
|
||||
# XXX: could provide default implementation which hands value to
|
||||
# hash(), and then does constant-time comparision on the result
|
||||
# (after making both are same string type)
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
def verify(cls, secret, hash, **context_kwds): # pragma: no cover -- abstract method
|
||||
"""verify secret against hash, returns True/False"""
|
||||
raise NotImplementedError("must be implemented by subclass")
|
||||
|
||||
#===================================================================
|
||||
# configuration
|
||||
#===================================================================
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
def using(cls, relaxed=False, **kwds):
|
||||
"""
|
||||
Return another hasher object (typically a subclass of the current one),
|
||||
which integrates the configuration options specified by ``kwds``.
|
||||
This should *always* return a new object, even if no configuration options are changed.
|
||||
|
||||
.. todo::
|
||||
|
||||
document which options are accepted.
|
||||
|
||||
:returns:
|
||||
typically returns a subclass for most hasher implementations.
|
||||
|
||||
.. todo::
|
||||
|
||||
add this method to main documentation.
|
||||
"""
|
||||
raise NotImplementedError("must be implemented by subclass")
|
||||
|
||||
#===================================================================
|
||||
# migration
|
||||
#===================================================================
|
||||
@classmethod
|
||||
def needs_update(cls, hash, secret=None):
|
||||
"""
|
||||
check if hash's configuration is outside desired bounds,
|
||||
or contains some other internal option which requires
|
||||
updating the password hash.
|
||||
|
||||
:param hash:
|
||||
hash string to examine
|
||||
|
||||
:param secret:
|
||||
optional secret known to have verified against the provided hash.
|
||||
(this is used by some hashes to detect legacy algorithm mistakes).
|
||||
|
||||
:return:
|
||||
whether secret needs re-hashing.
|
||||
|
||||
.. versionadded:: 1.7
|
||||
"""
|
||||
# by default, always report that we don't need update
|
||||
return False
|
||||
|
||||
#===================================================================
|
||||
# additional methods
|
||||
#===================================================================
|
||||
@@ -102,16 +220,41 @@ class PasswordHash(object):
|
||||
"""check if hash belongs to this scheme, returns True/False"""
|
||||
raise NotImplementedError("must be implemented by subclass")
|
||||
|
||||
@deprecated_method(deprecated="1.7", removed="2.0")
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
def genconfig(cls, **setting_kwds): # pragma: no cover -- abstract method
|
||||
"""compile settings into a configuration string for genhash()"""
|
||||
raise NotImplementedError("must be implemented by subclass")
|
||||
"""
|
||||
compile settings into a configuration string for genhash()
|
||||
|
||||
.. deprecated:: 1.7
|
||||
|
||||
As of 1.7, this method is deprecated, and slated for complete removal in Passlib 2.0.
|
||||
|
||||
For all known real-world uses, hashing a constant string
|
||||
should provide equivalent functionality.
|
||||
|
||||
This deprecation may be reversed if a use-case presents itself in the mean time.
|
||||
"""
|
||||
# NOTE: this fallback runs full hash alg, w/ whatever cost param is passed along.
|
||||
# implementations (esp ones w/ variable cost) will want to subclass this
|
||||
# with a constant-time implementation that just renders a config string.
|
||||
if cls.context_kwds:
|
||||
raise NotImplementedError("must be implemented by subclass")
|
||||
return cls.using(**setting_kwds).hash("")
|
||||
|
||||
@deprecated_method(deprecated="1.7", removed="2.0")
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
def genhash(cls, secret, config, **context_kwds): # pragma: no cover -- abstract method
|
||||
"""generated hash for secret, using settings from config/hash string"""
|
||||
def genhash(cls, secret, config, **context):
|
||||
"""
|
||||
generated hash for secret, using settings from config/hash string
|
||||
|
||||
.. deprecated:: 1.7
|
||||
|
||||
As of 1.7, this method is deprecated, and slated for complete removal in Passlib 2.0.
|
||||
|
||||
This deprecation may be reversed if a use-case presents itself in the mean time.
|
||||
"""
|
||||
# XXX: if hashes reliably offered a .parse() method, could make a fallback for this.
|
||||
raise NotImplementedError("must be implemented by subclass")
|
||||
|
||||
#===================================================================
|
||||
@@ -122,39 +265,31 @@ class PasswordHash(object):
|
||||
# they are subject to change between releases,
|
||||
# but are documented here so there's a list of them *somewhere*.
|
||||
|
||||
#---------------------------------------------------------------
|
||||
# extra metdata
|
||||
#---------------------------------------------------------------
|
||||
|
||||
#: this attribute shouldn't be used by hashers themselves,
|
||||
#: it's reserved for the CryptContext to track which hashers are deprecated.
|
||||
#: Note the context will only set this on objects it owns (and generated by .using()),
|
||||
#: and WONT set it on global objects.
|
||||
#: [added in 1.7]
|
||||
#: TODO: document this, or at least the use of testing for
|
||||
#: 'CryptContext().handler().deprecated'
|
||||
deprecated = False
|
||||
|
||||
#: optionally present if hasher corresponds to format built into Django.
|
||||
#: this attribute (if not None) should be the Django 'algorithm' name.
|
||||
#: also indicates to passlib.ext.django that (when installed in django),
|
||||
#: django's native hasher should be used in preference to this one.
|
||||
## django_name
|
||||
|
||||
#---------------------------------------------------------------
|
||||
# checksum information - defined for many hashes
|
||||
#---------------------------------------------------------------
|
||||
## checksum_chars
|
||||
## checksum_size
|
||||
|
||||
#---------------------------------------------------------------
|
||||
# CryptContext flags
|
||||
#---------------------------------------------------------------
|
||||
|
||||
# hack for bsdi_crypt: if True, causes CryptContext to only generate
|
||||
# odd rounds values. assumed False if not defined.
|
||||
## _avoid_even_rounds = False
|
||||
|
||||
##@classmethod
|
||||
##def _bind_needs_update(cls, **setting_kwds):
|
||||
## """return helper to detect hashes that need updating.
|
||||
##
|
||||
## if this method is defined, the CryptContext constructor
|
||||
## will invoke it with the settings specified for the context.
|
||||
## this method should return either ``None``, or a callable
|
||||
## with the signature ``needs_update(hash,secret)->bool``.
|
||||
##
|
||||
## this ``needs_update`` function should return True if the hash
|
||||
## should be re-encrypted, whether due to internal
|
||||
## issues or the specified settings.
|
||||
##
|
||||
## CryptContext will automatically take care of deprecating
|
||||
## hashes with insufficient rounds for classes which define fromstring()
|
||||
## and a rounds attribute - though the requirements for this last
|
||||
## part may change at some point.
|
||||
## """
|
||||
|
||||
#---------------------------------------------------------------
|
||||
# experimental methods
|
||||
#---------------------------------------------------------------
|
||||
@@ -186,7 +321,32 @@ class PasswordHash(object):
|
||||
# eoc
|
||||
#===================================================================
|
||||
|
||||
PasswordHash = create_with_metaclass(ABCMeta)(PasswordHash)
|
||||
class DisabledHash(PasswordHash):
|
||||
"""
|
||||
extended disabled-hash methods; only need be present if .disabled = True
|
||||
"""
|
||||
|
||||
is_disabled = True
|
||||
|
||||
@classmethod
|
||||
def disable(cls, hash=None):
|
||||
"""
|
||||
return string representing a 'disabled' hash;
|
||||
optionally including previously enabled hash
|
||||
(this is up to the individual scheme).
|
||||
"""
|
||||
# default behavior: ignore original hash, return standalone marker
|
||||
return cls.hash("")
|
||||
|
||||
@classmethod
|
||||
def enable(cls, hash):
|
||||
"""
|
||||
given a disabled-hash string,
|
||||
extract previously-enabled hash if one is present,
|
||||
otherwise raises ValueError
|
||||
"""
|
||||
# default behavior: no way to restore original hash
|
||||
raise ValueError("cannot restore original hash")
|
||||
|
||||
#=============================================================================
|
||||
# eof
|
||||
|
||||
Reference in New Issue
Block a user