From 93a352d0db778d1f312de1e3ddce5832b7c236a5 Mon Sep 17 00:00:00 2001 From: Marcio Mazza Date: Tue, 31 Oct 2017 18:45:05 -0200 Subject: [PATCH] Adiciona hasher para senhas migradas do zope MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (com utilitário para migrar para o novo formato) --- sapl/hashers.py | 54 ++++++++++++++++++++++++++++++++++++++++++++++++ sapl/settings.py | 5 +++++ test_hashers.py | 11 ++++++++++ 3 files changed, 70 insertions(+) create mode 100644 sapl/hashers.py create mode 100644 test_hashers.py diff --git a/sapl/hashers.py b/sapl/hashers.py new file mode 100644 index 000000000..a514f8f19 --- /dev/null +++ b/sapl/hashers.py @@ -0,0 +1,54 @@ +import base64 +import hashlib + +from django.contrib.auth.hashers import PBKDF2PasswordHasher, make_password +from django.utils.encoding import force_bytes + + +def to_base64(source): + return base64.b64encode(source).decode('utf-8') + + +class ZopeSHA1PasswordHasher(PBKDF2PasswordHasher): + """ + The SHA1 password hashing algorithm used by Zope. + Zope uses `password + salt`, Django has `salt + password`. + Pre encode with SHA1 in this order and PBKDF2 afterwards. + + based on https://www.fourdigits.nl/blog/converting-plone-data-to-django/ + """ + + algorithm = "zope_sha1_pbkdf2" + + def encode(self, password, salt, iterations=None): + assert password is not None + assert salt + password = force_bytes(password) + decoded_salt = base64.b64decode(salt) + + # this is what is stored in zope + hashed = hashlib.sha1(password + decoded_salt).digest() + decoded_salt + hashed = to_base64(hashed) + + # encode again with the standard method + return super().encode(hashed, salt, iterations) + + +def get_salt_from_zope_sha1(data): + intermediate = base64.b64decode(data) + salt = intermediate[20:].strip() + return to_base64(salt) + + +ZOPE_SHA1_PREFIX = '{SSHA}' + + +def zope_encoded_password_to_django(encoded): + if encoded.startswith(ZOPE_SHA1_PREFIX): + data = encoded[len(ZOPE_SHA1_PREFIX):] + salt = get_salt_from_zope_sha1(data) + hasher = ZopeSHA1PasswordHasher() + return super(ZopeSHA1PasswordHasher, hasher).encode(data, salt) + else: + # assume it's a plain password and use the default hashing + return make_password(encoded) diff --git a/sapl/settings.py b/sapl/settings.py index 4598023bd..5c933d57f 100644 --- a/sapl/settings.py +++ b/sapl/settings.py @@ -299,3 +299,8 @@ def excepthook(*args): 'Uncaught exception:', exc_info=args) # sys.excepthook = excepthook + +PASSWORD_HASHERS = [ + 'django.contrib.auth.hashers.PBKDF2PasswordHasher', # default + 'sapl.hashers.ZopeSHA1PasswordHasher', +] diff --git a/test_hashers.py b/test_hashers.py new file mode 100644 index 000000000..c21f13af7 --- /dev/null +++ b/test_hashers.py @@ -0,0 +1,11 @@ +from sapl.hashers import (ZopeSHA1PasswordHasher, + zope_encoded_password_to_django) + + +def test_zope_encoded_password_to_django(): + password = 'saploper' + encoded = '{SSHA}Swzvwt/2lSJfA8KUOl6cRjkpmHLkLkmsKu28' + salt = '5C5JrCrtvA==' + migrated = zope_encoded_password_to_django(encoded) + encoded = ZopeSHA1PasswordHasher().encode(password, salt) + assert migrated == encoded