from django.core.exceptions import ValidationError from django.contrib.auth import authenticate, get_user_model from django.contrib.auth.forms import (AuthenticationForm as BaseAuthenticationForm) from django.template.defaultfilters import date as format_date from django.utils.translation import ugettext_lazy as _ from misago.core import forms from misago.users.bans import get_user_ban from misago.users.validators import validate_password class MisagoAuthMixin(object): error_messages = { 'empty_data': _("You have to fill out both fields."), 'invalid_login': _("Your login or password is incorrect."), 'inactive_user': _("You have to activate your account before " "you will be able to sign in."), 'inactive_admin': _("Administrator has to activate your account " "before you will be able to sign in."), } def confirm_user_active(self, user): if user.requires_activation_by_admin: raise ValidationError( self.error_messages['inactive_admin'], code='inactive_admin', ) if user.requires_activation_by_user: raise ValidationError( self.error_messages['inactive_user'], code='inactive_user', ) def confirm_user_not_banned(self, user): self.user_ban = get_user_ban(user) if self.user_ban: if self.user_ban.valid_until: if self.user_ban.user_message: message = _("%(user)s, your account is " "banned until %(date)s for:") else: message = _("%(user)s, your account " "is banned until %(date)s.") date_format = {'date': format_date(self.user_ban.valid_until)} message = message % date_format else: if self.user_ban.user_message: message = _("%(user)s, your account is banned for:") else: message = _("%(user)s, your account is banned.") raise ValidationError( message % {'user': self.user_cache.username}, code='banned', ) class AuthenticationForm(MisagoAuthMixin, forms.Form, BaseAuthenticationForm): """ Base class for authenticating users, Floppy-forms and Misago login field comliant """ username = forms.CharField(label=_("Username or e-mail"), required=False, max_length=254) password = forms.CharField(label=_("Password"), required=False, widget=forms.PasswordInput) def clean(self): username = self.cleaned_data.get('username') password = self.cleaned_data.get('password') if username and password: self.user_cache = authenticate(username=username, password=password) if self.user_cache is None or not self.user_cache.is_active: raise ValidationError( self.error_messages['invalid_login'], code='invalid_login', ) else: self.confirm_login_allowed(self.user_cache) else: raise ValidationError( self.error_messages['empty_data'], code='empty_data', ) return self.cleaned_data def confirm_login_allowed(self, user): self.confirm_user_active(user) self.confirm_user_not_banned(user) class AdminAuthenticationForm(AuthenticationForm): required_css_class = 'required' def __init__(self, *args, **kwargs): self.error_messages.update({ 'not_staff': _("Your account does not have admin privileges.") }) super(AdminAuthenticationForm, self).__init__(*args, **kwargs) def confirm_login_allowed(self, user): if not user.is_staff: raise forms.ValidationError( self.error_messages['not_staff'], code='not_staff', ) class GetUserForm(MisagoAuthMixin, forms.Form): username = forms.CharField(label=_("Username or e-mail")) def clean(self): data = super(GetUserForm, self).clean() credential = data.get('username') if not credential or len(credential) > 250: raise forms.ValidationError(_("You have to fill out form.")) try: User = get_user_model() user = User.objects.get_by_username_or_email(data['username']) self.user_cache = user except User.DoesNotExist: raise forms.ValidationError(_("Invalid username or e-mail.")) self.confirm_allowed(user) return data def confirm_allowed(self, user): raise NotImplementedError("confirm_allowed method must be defined " "by inheriting classes") class ResendActivationForm(GetUserForm): def confirm_allowed(self, user): self.confirm_user_not_banned(user) username_format = {'user': user.username} if not user.requires_activation: message = _("%(user)s, your account is already active.") raise forms.ValidationError(message % username_format) if user.requires_activation_by_admin: message = _("%(user)s, only administrator may activate " "your account.") raise forms.ValidationError(message % username_format) class ResetPasswordForm(GetUserForm): error_messages = { 'inactive_user': _("You have to activate your account before " "you will be able to request new password."), 'inactive_admin': _("Administrator has to activate your account " "before you will be able to request " "new password."), } def confirm_allowed(self, user): self.confirm_user_not_banned(user) self.confirm_user_active(user) class SetNewPasswordForm(MisagoAuthMixin, forms.Form): new_password = forms.CharField(label=_("New password"), widget=forms.PasswordInput) def clean(self): data = super(SetNewPasswordForm, self).clean() new_password = data.get('new_password') if not new_password or len(new_password) > 250: raise forms.ValidationError(_("You have to fill out form.")) validate_password(new_password) return data