diff options
Diffstat (limited to 'passlib/ext/django/utils.py')
-rw-r--r-- | passlib/ext/django/utils.py | 37 |
1 files changed, 29 insertions, 8 deletions
diff --git a/passlib/ext/django/utils.py b/passlib/ext/django/utils.py index 3c03637..ab10b6f 100644 --- a/passlib/ext/django/utils.py +++ b/passlib/ext/django/utils.py @@ -119,6 +119,10 @@ def hasher_to_passlib_name(hasher_name): "convert hasher name -> passlib handler name" if hasher_name.startswith(PASSLIB_HASHER_PREFIX): return hasher_name[len(PASSLIB_HASHER_PREFIX):] + if hasher_name == "unsalted_sha1": + # django 1.4.6+ uses a separate hasher for "sha1$$digest" hashes, + # but passlib just reuses the "sha1$salt$digest" handler. + hasher_name = "sha1" for name in list_crypt_handlers(): if name.startswith(DJANGO_PASSLIB_PREFIX) or name in _other_django_hashes: handler = get_crypt_handler(name) @@ -132,13 +136,14 @@ def hasher_to_passlib_name(hasher_name): #============================================================================= # wrapping passlib handlers as django hashers #============================================================================= -_FAKE_SALT = "--fake-salt--" +_GEN_SALT_SIGNAL = "--!!!generate-new-salt!!!--" class _HasherWrapper(object): """helper for wrapping passlib handlers in Hasher-compatible class.""" # filled in by subclass, drives the other methods. passlib_handler = None + iterations = None @classproperty def algorithm(cls): @@ -146,19 +151,22 @@ class _HasherWrapper(object): return PASSLIB_HASHER_PREFIX + cls.passlib_handler.name def salt(self): - # XXX: our encode wrapper generates a new salt each time it's called, - # so just returning a 'no value' flag here. - return _FAKE_SALT + # NOTE: passlib's handler.encrypt() should generate new salt each time, + # so this just returns a special constant which tells + # encode() (below) not to pass a salt keyword along. + return _GEN_SALT_SIGNAL def verify(self, password, encoded): return self.passlib_handler.verify(password, encoded) def encode(self, password, salt=None, iterations=None): kwds = {} - if salt is not None and salt != _FAKE_SALT: + if salt is not None and salt != _GEN_SALT_SIGNAL: kwds['salt'] = salt if iterations is not None: kwds['rounds'] = iterations + elif self.iterations is not None: + kwds['rounds'] = self.iterations return self.passlib_handler.encrypt(password, **kwds) _translate_kwds = dict(checksum="hash", rounds="iterations") @@ -178,10 +186,17 @@ class _HasherWrapper(object): items.append((_(key), value)) return SortedDict(items) + # added in django 1.6 + def must_update(self, encoded): + # TODO: would like to do something useful here, + # but would require access to password context, + # which would mean a serious recoding of this ext. + return False + # cache of hasher wrappers generated by get_passlib_hasher() _hasher_cache = WeakKeyDictionary() -def get_passlib_hasher(handler): +def get_passlib_hasher(handler, algorithm=None): """create *Hasher*-compatible wrapper for specified passlib hash. This takes in the name of a passlib hash (or the handler object itself), @@ -204,8 +219,14 @@ def get_passlib_hasher(handler): handler = get_crypt_handler(handler) if hasattr(handler, "django_name"): # return native hasher instance - # XXX: should cache this too. - return _get_hasher(handler.django_name) + # XXX: should add this to _hasher_cache[] + name = handler.django_name + if name == "sha1" and algorithm == "unsalted_sha1": + # django 1.4.6+ uses a separate hasher for "sha1$$digest" hashes, + # but passlib just reuses the "sha1$salt$digest" handler. + # we want to resolve to correct django hasher. + name = algorithm + return _get_hasher(name) if handler.name == "django_disabled": raise ValueError("can't wrap unusable-password handler") try: |