From a1c007599f89e4b493947cc5c2c3968c47ff52d9 Mon Sep 17 00:00:00 2001 From: Christophe Simonis Date: Tue, 8 Jan 2013 00:15:21 +0100 Subject: [PATCH 1/3] allow change of hash scheme --- flask_security/core.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/flask_security/core.py b/flask_security/core.py index c29de69..6b58b3d 100644 --- a/flask_security/core.py +++ b/flask_security/core.py @@ -94,6 +94,16 @@ _default_messages = { 'REFRESH': ('Please reauthenticate to access this page.', 'info') } +_allowed_password_hash_schemes = [ + 'bcrypt', + 'des_crypt', + 'pbkdf2_sha256', + 'pbkdf2_sha512', + 'sha256_crypt', + 'sha512_crypt', + # And always last one... + 'plaintext' +] def _user_loader(user_id): return _security.datastore.find_user(id=user_id) @@ -149,7 +159,10 @@ def _get_principal(app): def _get_pwd_context(app): pw_hash = cv('PASSWORD_HASH', app=app) - return CryptContext(schemes=[pw_hash], default=pw_hash) + if pw_hash not in _allowed_password_hash_schemes: + allowed = ', '.join(_allowed_password_hash_schemes[:-1]) + ' and ' + _allowed_password_hash_schemes[-1] + raise ValueError("Invalid hash scheme %r. Allowed values are %s" % (pw_hash, allowed)) + return CryptContext(schemes=_allowed_password_hash_schemes, default=pw_hash) def _get_serializer(app, name): From d0497fc8860a818cec2a52ec469329e75b3c592e Mon Sep 17 00:00:00 2001 From: Christophe Simonis Date: Tue, 8 Jan 2013 00:49:20 +0100 Subject: [PATCH 2/3] update password automatically --- flask_security/decorators.py | 3 ++- flask_security/forms.py | 2 +- flask_security/utils.py | 8 ++++++-- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/flask_security/decorators.py b/flask_security/decorators.py index 32e3409..e7ddad5 100644 --- a/flask_security/decorators.py +++ b/flask_security/decorators.py @@ -67,7 +67,8 @@ def _check_http_auth(): auth = request.authorization or dict(username=None, password=None) user = _security.datastore.find_user(email=auth.username) - if user and utils.verify_password(auth.password, user.password): + if user and utils.verify_password(auth.password, user): + _security.datastore.commit() app = current_app._get_current_object() identity_changed.send(app, identity=Identity(user.id)) return True diff --git a/flask_security/forms.py b/flask_security/forms.py index dc99118..12bf3c9 100644 --- a/flask_security/forms.py +++ b/flask_security/forms.py @@ -162,7 +162,7 @@ class LoginForm(Form, NextFormMixin): if self.user is None: self.email.errors.append('Specified user does not exist') return False - if not verify_password(self.password.data, self.user.password): + if not verify_password(self.password.data, self.user): self.password.errors.append('Invalid password') return False if requires_confirmation(self.user): diff --git a/flask_security/utils.py b/flask_security/utils.py index 03becf6..40f427a 100644 --- a/flask_security/utils.py +++ b/flask_security/utils.py @@ -82,8 +82,12 @@ def get_hmac(password): return base64.b64encode(h.digest()) -def verify_password(password, password_hash): - return _pwd_context.verify(get_hmac(password), password_hash) +def verify_password(password, user): + verify, new_password = _pwd_context.verify_and_update(get_hmac(password), user.password) + if verify and new_password: + user.password = new_password + _datastore.put(user) + return verify def encrypt_password(password): From a89b76d648761d0ff5641fdd5f84aad92b92cac1 Mon Sep 17 00:00:00 2001 From: Christophe Simonis Date: Tue, 8 Jan 2013 01:01:02 +0100 Subject: [PATCH 3/3] do not break API. add a new function to verify and update password --- flask_security/decorators.py | 2 +- flask_security/forms.py | 4 ++-- flask_security/utils.py | 12 ++++++++---- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/flask_security/decorators.py b/flask_security/decorators.py index e7ddad5..5787679 100644 --- a/flask_security/decorators.py +++ b/flask_security/decorators.py @@ -67,7 +67,7 @@ def _check_http_auth(): auth = request.authorization or dict(username=None, password=None) user = _security.datastore.find_user(email=auth.username) - if user and utils.verify_password(auth.password, user): + if user and utils.verify_and_update_password(auth.password, user): _security.datastore.commit() app = current_app._get_current_object() identity_changed.send(app, identity=Identity(user.id)) diff --git a/flask_security/forms.py b/flask_security/forms.py index 12bf3c9..3539f63 100644 --- a/flask_security/forms.py +++ b/flask_security/forms.py @@ -16,7 +16,7 @@ from flask.ext.wtf import Form as BaseForm, TextField, PasswordField, \ from werkzeug.local import LocalProxy from .confirmable import requires_confirmation -from .utils import verify_password, get_message +from .utils import verify_and_update_password, get_message # Convenient reference _datastore = LocalProxy(lambda: current_app.extensions['security'].datastore) @@ -162,7 +162,7 @@ class LoginForm(Form, NextFormMixin): if self.user is None: self.email.errors.append('Specified user does not exist') return False - if not verify_password(self.password.data, self.user): + if not verify_and_update_password(self.password.data, self.user): self.password.errors.append('Invalid password') return False if requires_confirmation(self.user): diff --git a/flask_security/utils.py b/flask_security/utils.py index 40f427a..5531ec0 100644 --- a/flask_security/utils.py +++ b/flask_security/utils.py @@ -82,12 +82,16 @@ def get_hmac(password): return base64.b64encode(h.digest()) -def verify_password(password, user): - verify, new_password = _pwd_context.verify_and_update(get_hmac(password), user.password) - if verify and new_password: +def verify_password(password, password_hash): + return _pwd_context.verify(get_hmac(password), password_hash) + + +def verify_and_update_password(password, user): + verified, new_password = _pwd_context.verify_and_update(get_hmac(password), user.password) + if verified and new_password: user.password = new_password _datastore.put(user) - return verify + return verified def encrypt_password(password):