diff options
Diffstat (limited to 'wqflask/flask_security/forms.py')
-rw-r--r-- | wqflask/flask_security/forms.py | 277 |
1 files changed, 277 insertions, 0 deletions
diff --git a/wqflask/flask_security/forms.py b/wqflask/flask_security/forms.py new file mode 100644 index 00000000..e64e1502 --- /dev/null +++ b/wqflask/flask_security/forms.py @@ -0,0 +1,277 @@ +# -*- coding: utf-8 -*- +""" + flask.ext.security.forms + ~~~~~~~~~~~~~~~~~~~~~~~~ + + Flask-Security forms module + + :copyright: (c) 2012 by Matt Wright. + :license: MIT, see LICENSE for more details. +""" + +import inspect +import urlparse + +import flask_wtf as wtf + +from flask import request, current_app +from flask_wtf import Form as BaseForm, TextField, PasswordField, \ + SubmitField, HiddenField, BooleanField, ValidationError, Field +from flask_login import current_user +from werkzeug.local import LocalProxy + +from .confirmable import requires_confirmation +from .utils import verify_and_update_password, get_message + +# Convenient reference +_datastore = LocalProxy(lambda: current_app.extensions['security'].datastore) + +_default_field_labels = { + 'email': 'Email Address', + 'password': 'Password', + 'remember_me': 'Remember Me', + 'login': 'Login', + 'retype_password': 'Retype Password', + 'register': 'Register', + 'send_confirmation': 'Resend Confirmation Instructions', + 'recover_password': 'Recover Password', + 'reset_password': 'Reset Password', + 'retype_password': 'Retype Password', + 'new_password': 'New Password', + 'change_password': 'Change Password', + 'send_login_link': 'Send Login Link' +} + + +class ValidatorMixin(object): + def __call__(self, form, field): + if self.message and self.message.isupper(): + self.message = get_message(self.message)[0] + return super(ValidatorMixin, self).__call__(form, field) + + +class EqualTo(ValidatorMixin, wtf.EqualTo): + pass + + +class Required(ValidatorMixin, wtf.Required): + pass + + +class Email(ValidatorMixin, wtf.Email): + pass + + +class Length(ValidatorMixin, wtf.Length): + pass + + +email_required = Required(message='EMAIL_NOT_PROVIDED') +email_validator = Email(message='INVALID_EMAIL_ADDRESS') +password_required = Required(message='PASSWORD_NOT_PROVIDED') + + +def get_form_field_label(key): + return _default_field_labels.get(key, '') + + +def unique_user_email(form, field): + if _datastore.find_user(email=field.data) is not None: + msg = get_message('EMAIL_ALREADY_ASSOCIATED', email=field.data)[0] + raise ValidationError(msg) + + +def valid_user_email(form, field): + form.user = _datastore.find_user(email=field.data) + if form.user is None: + raise ValidationError(get_message('USER_DOES_NOT_EXIST')[0]) + + +class Form(BaseForm): + def __init__(self, *args, **kwargs): + if current_app.testing: + self.TIME_LIMIT = None + super(Form, self).__init__(*args, **kwargs) + + +class EmailFormMixin(): + email = TextField(get_form_field_label('email'), + validators=[email_required, + email_validator]) + + +class UserEmailFormMixin(): + user = None + email = TextField(get_form_field_label('email'), + validators=[email_required, + email_validator, + valid_user_email]) + + +class UniqueEmailFormMixin(): + email = TextField(get_form_field_label('email'), + validators=[email_required, + email_validator, + unique_user_email]) + + +class PasswordFormMixin(): + password = PasswordField(get_form_field_label('password'), + validators=[password_required]) + + +class NewPasswordFormMixin(): + password = PasswordField(get_form_field_label('password'), + validators=[password_required, + Length(min=6, max=128)]) + + +class PasswordConfirmFormMixin(): + password_confirm = PasswordField( + get_form_field_label('retype_password'), + validators=[EqualTo('password', message='RETYPE_PASSWORD_MISMATCH')]) + + +class NextFormMixin(): + next = HiddenField() + + def validate_next(self, field): + url_next = urlparse.urlsplit(field.data) + url_base = urlparse.urlsplit(request.host_url) + if url_next.netloc and url_next.netloc != url_base.netloc: + field.data = '' + raise ValidationError(get_message('INVALID_REDIRECT')[0]) + + +class RegisterFormMixin(): + submit = SubmitField(get_form_field_label('register')) + + def to_dict(form): + def is_field_and_user_attr(member): + 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) + + +class SendConfirmationForm(Form, UserEmailFormMixin): + """The default forgot password form""" + + submit = SubmitField(get_form_field_label('send_confirmation')) + + def __init__(self, *args, **kwargs): + super(SendConfirmationForm, self).__init__(*args, **kwargs) + if request.method == 'GET': + self.email.data = request.args.get('email', None) + + def validate(self): + if not super(SendConfirmationForm, self).validate(): + return False + if self.user.confirmed_at is not None: + self.email.errors.append(get_message('ALREADY_CONFIRMED')[0]) + return False + return True + + +class ForgotPasswordForm(Form, UserEmailFormMixin): + """The default forgot password form""" + + submit = SubmitField(get_form_field_label('recover_password')) + + +class PasswordlessLoginForm(Form, UserEmailFormMixin): + """The passwordless login form""" + + submit = SubmitField(get_form_field_label('send_login_link')) + + def __init__(self, *args, **kwargs): + super(PasswordlessLoginForm, self).__init__(*args, **kwargs) + + def validate(self): + if not super(PasswordlessLoginForm, self).validate(): + return False + if not self.user.is_active(): + self.email.errors.append(get_message('DISABLED_ACCOUNT')[0]) + return False + return True + + +class LoginForm(Form, NextFormMixin): + """The default login form""" + + email = TextField(get_form_field_label('email')) + password = PasswordField(get_form_field_label('password')) + remember = BooleanField(get_form_field_label('remember_me')) + submit = SubmitField(get_form_field_label('login')) + + def __init__(self, *args, **kwargs): + super(LoginForm, self).__init__(*args, **kwargs) + + def validate(self): + if not super(LoginForm, self).validate(): + return False + + if self.email.data.strip() == '': + self.email.errors.append(get_message('EMAIL_NOT_PROVIDED')[0]) + return False + + if self.password.data.strip() == '': + self.password.errors.append(get_message('PASSWORD_NOT_PROVIDED')[0]) + return False + + self.user = _datastore.find_user(email=self.email.data) + + if self.user is None: + self.email.errors.append(get_message('USER_DOES_NOT_EXIST')[0]) + return False + if not verify_and_update_password(self.password.data, self.user): + 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]) + return False + if not self.user.is_active(): + self.email.errors.append(get_message('DISABLED_ACCOUNT')[0]) + return False + return True + + +class ConfirmRegisterForm(Form, RegisterFormMixin, + UniqueEmailFormMixin, NewPasswordFormMixin): + pass + + +class RegisterForm(ConfirmRegisterForm, PasswordConfirmFormMixin): + pass + + +class ResetPasswordForm(Form, NewPasswordFormMixin, PasswordConfirmFormMixin): + """The default reset password form""" + + submit = SubmitField(get_form_field_label('reset_password')) + + +class ChangePasswordForm(Form, PasswordFormMixin): + """The default change password form""" + + new_password = PasswordField(get_form_field_label('new_password'), + validators=[password_required, + Length(min=6, max=128)]) + + new_password_confirm = PasswordField(get_form_field_label('retype_password'), + validators=[EqualTo('new_password', message='RETYPE_PASSWORD_MISMATCH')]) + + submit = SubmitField(get_form_field_label('change_password')) + + def validate(self): + if not super(ChangePasswordForm, self).validate(): + return False + + if self.password.data.strip() == '': + self.password.errors.append(get_message('PASSWORD_NOT_PROVIDED')[0]) + 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 |