diff options
Diffstat (limited to 'passlib/context.py')
| -rw-r--r-- | passlib/context.py | 62 |
1 files changed, 60 insertions, 2 deletions
diff --git a/passlib/context.py b/passlib/context.py index 9454b77..1d81b12 100644 --- a/passlib/context.py +++ b/passlib/context.py @@ -4,6 +4,7 @@ #========================================================= from __future__ import with_statement #core +from functools import update_wrapper import inspect import re import hashlib @@ -23,7 +24,7 @@ except ImportError: from passlib.registry import get_crypt_handler, _validate_handler_name from passlib.utils import to_bytes, to_unicode, bytes, \ is_crypt_handler, rng, \ - PasslibPolicyWarning, timer + PasslibPolicyWarning, timer, saslprep from passlib.utils.compat import is_mapping, iteritems, num_types, \ PY3, PY_MIN_32, unicode, bytes from passlib.utils.compat.aliases import SafeConfigParser, StringIO, BytesIO @@ -759,6 +760,11 @@ default_policy = _load_default_policy() #========================================================= # helpers for CryptContext #========================================================= +_passprep_funcs = dict( + saslprep=saslprep, + raw=lambda s: s, +) + class _CryptRecord(object): """wraps a handler and automatically applies various options. @@ -801,7 +807,7 @@ class _CryptRecord(object): #================================================================ def __init__(self, handler, category=None, deprecated=False, min_rounds=None, max_rounds=None, default_rounds=None, - vary_rounds=None, min_verify_time=None, + vary_rounds=None, min_verify_time=None, passprep=None, **settings): self.handler = handler self.category = category @@ -815,6 +821,9 @@ class _CryptRecord(object): self.identify = handler.identify self.genhash = handler.genhash + # let stringprep code wrap genhash/encrypt if needed + self._compile_passprep(passprep) + @property def scheme(self): return self.handler.name @@ -1082,6 +1091,55 @@ class _CryptRecord(object): _hash_needs_update = None #================================================================ + # password stringprep + #================================================================ + def _compile_passprep(self, value): + # NOTE: all of this code assumes secret uses utf-8 encoding if bytes. + if not value: + return + self._stringprep = value + names = _splitcomma(value) + if names == ["raw"]: + return + funcs = [_passprep_funcs[name] for name in names] + + first = funcs[0] + def wrap(orig): + def wrapper(secret, *args, **kwds): + if isinstance(secret, bytes): + secret = secret.decode("utf-8") + return orig(first(secret), *args, **kwds) + update_wrapper(wrapper, orig) + wrapper._wrapped = orig + return wrapper + + # wrap genhash & encrypt so secret is prep'd + self.genhash = wrap(self.genhash) + self.encrypt = wrap(self.encrypt) + + # wrap verify so secret is prep'd + if len(funcs) == 1: + self.verify = wrap(self.verify) + else: + # if multiple fallback prep functions, + # try to verify with each of them. + verify = self.verify + def wrapper(secret, *args, **kwds): + if isinstance(secret, bytes): + secret = secret.decode("utf-8") + seen = set() + for prep in funcs: + tmp = prep(secret) + if tmp not in seen: + if verify(tmp, *args, **kwds): + return True + seen.add(tmp) + return False + update_wrapper(wrapper, verify) + wrapper._wrapped = verify + self.verify = wrapper + + #================================================================ # eoc #================================================================ |
