Source code for akvo.rsr.forms

# Akvo RSR is covered by the GNU Affero General Public License.
# See more details in the license.txt file located at the root folder of the Akvo RSR module.
# For additional details on the GNU license please see < http://www.gnu.org/licenses/agpl.html >.

"""
Forms and validation code for user registration and updating.

"""

from django import forms
from django.contrib.auth import get_user_model
from django.contrib.auth.forms import PasswordResetForm as PRF, UserCreationForm
from django.contrib.auth.password_validation import validate_password
from django.contrib.sites.shortcuts import get_current_site
from django.core.exceptions import ValidationError
from django.db.models import F, Q
from django.utils.translation import gettext_lazy as _
from django.utils.safestring import mark_safe

from akvo import settings
from akvo.password_policy.error_messages import get_error_message
from akvo.password_policy.rules.character import CharacterRule
from akvo.password_policy.rules.compound import CompoundRule
from akvo.password_policy.rules.length import LengthRule
from akvo.rsr.registration import send_activation_email

PASSWORD_MINIMUM_LENGTH = settings.PASSWORD_MINIMUM_LENGTH

User = get_user_model()


[docs]def password_policy_fallback(password, *_): rules = [ LengthRule(min=PASSWORD_MINIMUM_LENGTH), CharacterRule.letters(min_length=1), CharacterRule.uppercases(min_length=1), CharacterRule.lowercase(min_length=1), CharacterRule.numbers(min_length=1), CharacterRule.symbols(min_length=1), ] validator = CompoundRule(rules) result = validator.validate(password) if not result.is_valid(): errors = [ ValidationError(get_error_message(error.code), code=error.code, params=error.context) for error in result.errors ] raise ValidationError(errors)
[docs]def resolve_password_policy(user): organisation = user.first_organisation() return organisation.password_policy if organisation else None
[docs]class RegisterForm(UserCreationForm): email = forms.EmailField( label=_('Email'), max_length=254, widget=forms.TextInput( attrs={'placeholder': _('Email')} ), ) first_name = forms.CharField( label=_('First name'), max_length=30, widget=forms.TextInput( attrs={'placeholder': _('First name')} ), ) last_name = forms.CharField( label=_('Last name'), max_length=30, widget=forms.TextInput( attrs={'placeholder': _('Last name')} ), )
[docs] class Meta: model = User fields = ('email', 'first_name', 'last_name', 'password1', 'password2')
[docs] def save(self, request): self.instance.username = self.cleaned_data['email'] self.instance.last_login = self.instance.date_joined user = super().save() site = get_current_site(request) send_activation_email(user, site) return user
[docs]class PasswordResetForm(PRF):
[docs] def get_users(self, email): """Given an email, return matching user(s) who should receive a reset. Overriden from the base class to allow users who didn't click on the activation email or invite activation emails to get a new password, by clicking on the forgot password email link. We still ensure that users who have been explicitly deactivated are not able to reset their account by requesting a new password. We check if the user has logged in at least once, to differentiate such users from other users. """ User = get_user_model() users = User.objects.filter(email__iexact=email).filter( # Active users should be allowed to reset passwords Q(is_active=True) # Newly registered users should be allowed to ask for reset # password, even if they didn't activate their account. | Q(last_login=F('date_joined')) ).distinct() return users
[docs]class InvitedUserForm(forms.Form): first_name = forms.CharField( label=_('First name'), max_length=30, widget=forms.TextInput( attrs={'placeholder': _('First name')} ), ) last_name = forms.CharField( label=_('Last name'), max_length=30, widget=forms.TextInput( attrs={'placeholder': _('Last name')} ), ) password1 = forms.CharField( label=_('Password'), widget=forms.PasswordInput( attrs={'placeholder': _('Password')}, render_value=False ) ) password2 = forms.CharField( label=_('Repeat password'), widget=forms.PasswordInput( attrs={'placeholder': _('Repeat password')}, render_value=False ) ) def __init__(self, user, *args, **kwargs): """Add the user to the form.""" super(InvitedUserForm, self).__init__(*args, **kwargs) self.user = user
[docs] def clean_password2(self): password1 = self.cleaned_data.get('password1') password2 = self.cleaned_data.get('password2') if password1 and password2: if password1 != password2: raise forms.ValidationError( _('Passwords do not match. Please enter the same password in both fields.') ) validate_password(password2, self.user) return password2
[docs] def save(self, request): """ Set the user to active and update the user's credentials. """ self.user.is_active = True self.user.first_name = self.cleaned_data['first_name'] self.user.last_name = self.cleaned_data['last_name'] self.user.set_password(self.cleaned_data['password1']) self.user.save()
[docs]class UserAvatarForm(forms.ModelForm): """Form for updating the avatar of a user.""" avatar = forms.ImageField(widget=forms.FileInput(attrs={ 'class': 'input', 'size': '15', }))
[docs] class Meta: model = get_user_model() fields = ('avatar',)
[docs]class CustomLabelModelChoiceField(forms.ModelMultipleChoiceField):
[docs] def label_from_instance(self, obj): if not obj.is_public: return mark_safe('<span class="noCheck">%s <i>(private project)</i></span>' % obj) elif obj.is_published(): return mark_safe('<span class="noCheck">%s</span>' % obj) else: return mark_safe('<span class="noCheck">%s <i>(not published)</i></span>' % obj)