diff --git a/flask_security/core.py b/flask_security/core.py index b413c74..6ae5ba4 100644 --- a/flask_security/core.py +++ b/flask_security/core.py @@ -97,7 +97,8 @@ _default_messages = { 'DISABLED_ACCOUNT': ('Account is disabled.', 'error'), 'PASSWORDLESS_LOGIN_SUCCESSFUL': ('You have successfuly logged in.', 'success'), 'PASSWORD_RESET': ('You successfully reset your password and you have been logged in automatically.', 'success'), - 'PASSWORD_CHANGE': ('You successfully changes your password.', 'success'), + 'PASSWORD_CHANGE': ('You successfully changed your password.', 'success'), + 'INVALID_PASSWORD': ('Invalid password', 'error'), 'LOGIN': ('Please log in to access this page.', 'info'), 'REFRESH': ('Please reauthenticate to access this page.', 'info') } @@ -294,6 +295,9 @@ class _SecurityState(object): def reset_password_context_processor(self, fn): self._add_ctx_processor('reset_password', fn) + def change_password_context_processor(self, fn): + self._add_ctx_processor('change_password', fn) + def send_confirmation_context_processor(self, fn): self._add_ctx_processor('send_confirmation', fn) @@ -323,8 +327,8 @@ class Security(object): def init_app(self, app, datastore=None, register_blueprint=True, login_form=None, confirm_register_form=None, register_form=None, forgot_password_form=None, - reset_password_form=None, send_confirmation_form=None, - passwordless_login_form=None): + reset_password_form=None, change_password_form=None, + send_confirmation_form=None, passwordless_login_form=None): """Initializes the Flask-Security extension for the specified application and datastore implentation. @@ -348,6 +352,7 @@ class Security(object): register_form=register_form, forgot_password_form=forgot_password_form, reset_password_form=reset_password_form, + change_password_form=change_password_form, send_confirmation_form=send_confirmation_form, passwordless_login_form=passwordless_login_form) diff --git a/flask_security/forms.py b/flask_security/forms.py index 9234322..4b2808b 100644 --- a/flask_security/forms.py +++ b/flask_security/forms.py @@ -15,10 +15,11 @@ from flask import request, current_app from flask.ext.wtf import Form as BaseForm, TextField, PasswordField, \ SubmitField, HiddenField, Required, BooleanField, EqualTo, Email, \ ValidationError, Length, Field +from flask.ext.login import current_user from werkzeug.local import LocalProxy from .confirmable import requires_confirmation -from .utils import verify_and_update_password, get_message +from .utils import verify_password, verify_and_update_password, get_message # Convenient reference _datastore = LocalProxy(lambda: current_app.extensions['security'].datastore) @@ -83,6 +84,7 @@ class NewPasswordFormMixin(): validators=[password_required, Length(min=6, max=128)]) + class PasswordConfirmFormMixin(): password_confirm = PasswordField("Retype Password", validators=[EqualTo('password', message="Passwords do not match")]) @@ -97,7 +99,8 @@ class RegisterFormMixin(): def to_dict(form): def is_field_and_user_attr(member): - return isinstance(member, Field) and hasattr(_datastore.user_model, member.name) + return isinstance(member, Field) and \ + hasattr(_datastore.user_model, member.name) fields = inspect.getmembers(form, is_field_and_user_attr) return dict((key, value.data) for key, value in fields) @@ -172,7 +175,7 @@ class LoginForm(Form, NextFormMixin): self.email.errors.append('Specified user does not exist') return False if not verify_and_update_password(self.password.data, self.user): - self.password.errors.append('Invalid password') + self.password.errors.append(get_message('INVALID_PASSWORD')[0]) return False if requires_confirmation(self.user): self.email.errors.append(get_message('CONFIRMATION_REQUIRED')[0]) @@ -197,11 +200,25 @@ class ResetPasswordForm(Form, NewPasswordFormMixin, PasswordConfirmFormMixin): submit = SubmitField("Reset Password") -class ChangePasswordForm(Form, PasswordFormMixin, PasswordConfirmFormMixin): +class ChangePasswordForm(Form, PasswordFormMixin): """The default Change password form""" new_password = PasswordField("New Password", validators=[password_required, Length(min=6, max=128)]) + new_password_confirm = PasswordField("Retype Password", + validators=[EqualTo('new_password', message="Passwords do not match")]) + submit = SubmitField("Change Password") + + def validate(self): + super(ChangePasswordForm, self).validate() + + if self.password.data.strip() == '': + self.password.errors.append('Password not provided') + return False + if not verify_and_update_password(self.password.data, current_user): + self.password.errors.append(get_message('INVALID_PASSWORD')[0]) + return False + return True diff --git a/flask_security/templates/security/_menu.html b/flask_security/templates/security/_menu.html index d50b5a4..272cd22 100644 --- a/flask_security/templates/security/_menu.html +++ b/flask_security/templates/security/_menu.html @@ -5,6 +5,9 @@ {% if security.registerable %}