# Copyright The IETF Trust 2011-2022, All Rights Reserved # -*- coding: utf-8 -*- import re from unidecode import unidecode from django import forms from django.conf import settings from django.core.exceptions import ValidationError from django.db import models from django.contrib.auth.models import User import debug # pyflakes:ignore from ietf.person.models import Person, Email from ietf.mailinglists.models import Allowlisted from ietf.utils.text import isascii from .widgets import PasswordStrengthInput, PasswordConfirmationInput class RegistrationForm(forms.Form): email = forms.EmailField(label="Your email (lowercase)") def clean_email(self): email = self.cleaned_data.get('email', '') if not email: return email if email.lower() != email: raise forms.ValidationError('The supplied address contained uppercase letters. Please use a lowercase email address.') return email class PasswordForm(forms.Form): password = forms.CharField(widget=PasswordStrengthInput(attrs={'class':'password_strength'})) password_confirmation = forms.CharField(widget=PasswordConfirmationInput( confirm_with='password', attrs={'class':'password_confirmation'}), help_text="Enter the same password as above, for verification.",) def clean_password_confirmation(self): password = self.cleaned_data.get("password", "") password_confirmation = self.cleaned_data["password_confirmation"] if password != password_confirmation: raise forms.ValidationError("The two password fields didn't match.") return password_confirmation def ascii_cleaner(supposedly_ascii): outside_printable_ascii_pattern = r'[^\x20-\x7F]' if re.search(outside_printable_ascii_pattern, supposedly_ascii): raise forms.ValidationError("Only unaccented Latin characters are allowed.") return supposedly_ascii def prevent_at_symbol(name): if "@" in name: raise forms.ValidationError("Please fill in name - this looks like an email address (@ is not allowed in names).") def prevent_system_name(name): name_without_spaces = name.replace(" ", "").replace("\t", "") if "(system)" in name_without_spaces.lower(): raise forms.ValidationError("Please pick another name - this name is reserved.") def prevent_anonymous_name(name): name_without_spaces = name.replace(" ", "").replace("\t", "") if "anonymous" in name_without_spaces.lower(): raise forms.ValidationError("Please pick another name - this name is reserved.") class PersonPasswordForm(forms.ModelForm, PasswordForm): class Meta: model = Person fields = ['name', 'ascii'] def clean_name(self): name = self.cleaned_data.get('name', '') prevent_at_symbol(name) prevent_system_name(name) prevent_anonymous_name(name) return name def clean_ascii(self): ascii = self.cleaned_data.get('ascii', '') if not isascii(ascii): raise forms.ValidationError("Ascii name contains non-ASCII characters.") return ascii def get_person_form(*args, **kwargs): exclude_list = ['time', 'user', 'photo_thumb', 'photo', ] person = kwargs['instance'] roles = person.role_set.all() if not roles: exclude_list += ['biography', 'photo', ] class PersonForm(forms.ModelForm): class Meta: model = Person exclude = exclude_list def __init__(self, *args, **kwargs): super(PersonForm, self).__init__(*args, **kwargs) # blank ascii if it's the same as name self.fields["ascii"].required = self.fields["ascii"].widget.is_required = False self.fields["ascii"].help_text += " " + "Leave blank to use auto-reconstructed Latin version of name." if self.initial.get("ascii") == self.initial.get("name"): self.initial["ascii"] = "" self.fields['pronouns_selectable'] = forms.MultipleChoiceField(label='Pronouns', choices = [(option, option) for option in ["he/him", "she/her", "they/them"]], widget=forms.CheckboxSelectMultiple, required=False) self.fields["pronouns_freetext"].widget.attrs.update( {"aria-label": "Optionally provide your personal pronouns"} ) self.unidecoded_ascii = False if self.data and not self.data.get("ascii", "").strip(): self.data = self.data.copy() name = self.data["name"] reconstructed_name = unidecode(name) self.data["ascii"] = reconstructed_name self.unidecoded_ascii = name != reconstructed_name def clean_name(self): name = self.cleaned_data.get("name") or "" prevent_at_symbol(name) prevent_system_name(name) return name def clean_ascii(self): if self.unidecoded_ascii: raise forms.ValidationError("Name contained non-ASCII characters, and was automatically reconstructed using only Latin characters. Check the result - if you are happy, just hit Submit again.") name = self.cleaned_data.get("ascii") or "" prevent_at_symbol(name) prevent_system_name(name) return ascii_cleaner(name) def clean_ascii_short(self): name = self.cleaned_data.get("ascii_short") or "" prevent_at_symbol(name) prevent_system_name(name) return ascii_cleaner(name) def clean(self): if self.cleaned_data.get("pronouns_selectable") and self.cleaned_data.get("pronouns_freetext"): self.add_error("pronouns_freetext", "Either select from the pronoun checkboxes or provide a custom value, but not both") return PersonForm(*args, **kwargs) class NewEmailForm(forms.Form): new_email = forms.EmailField(label="New email address", required=False) def clean_new_email(self): email = self.cleaned_data.get("new_email", "") for pat in settings.EXCLUDED_PERSONAL_EMAIL_REGEX_PATTERNS: if re.search(pat, email): raise ValidationError("This email address is not valid in a datatracker account") return email class RoleEmailForm(forms.Form): email = forms.ModelChoiceField(label="Role email", queryset=Email.objects.all()) def __init__(self, role, *args, **kwargs): super(RoleEmailForm, self).__init__(*args, **kwargs) f = self.fields["email"] f.label = "%s in %s" % (role.name, role.group.acronym.upper()) f.help_text = "Email to use for %s role in %s" % (role.name, role.group.name) f.queryset = f.queryset.filter(models.Q(person=role.person_id) | models.Q(role=role)).distinct() f.initial = role.email_id f.choices = [(e.pk, e.address if e.active else "({})".format(e.address)) for e in f.queryset] class ResetPasswordForm(forms.Form): username = forms.EmailField(label="Your email (lowercase)") class TestEmailForm(forms.Form): email = forms.EmailField(required=False) class AllowlistForm(forms.ModelForm): class Meta: model = Allowlisted exclude = ['by', 'time' ] from django import forms class ChangePasswordForm(forms.Form): current_password = forms.CharField(widget=forms.PasswordInput) new_password = forms.CharField(widget=PasswordStrengthInput(attrs={'class':'password_strength'})) new_password_confirmation = forms.CharField(widget=PasswordConfirmationInput( confirm_with='new_password', attrs={'class':'password_confirmation'})) def __init__(self, user, data=None): self.user = user super(ChangePasswordForm, self).__init__(data) def clean_current_password(self): password = self.cleaned_data.get('current_password', None) if not self.user.check_password(password): raise ValidationError('Invalid password') return password def clean(self): new_password = self.cleaned_data.get('new_password', None) conf_password = self.cleaned_data.get('new_password_confirmation', None) if not new_password == conf_password: raise ValidationError("The password confirmation is different than the new password") class ChangeUsernameForm(forms.Form): username = forms.ChoiceField(choices=[('-','--------')]) password = forms.CharField(widget=forms.PasswordInput, help_text="Confirm the change with your password") def __init__(self, user, *args, **kwargs): assert isinstance(user, User) super(ChangeUsernameForm, self).__init__(*args, **kwargs) self.user = user emails = user.person.email_set.filter(active=True) choices = [ (email.address, email.address) for email in emails ] self.fields['username'] = forms.ChoiceField(choices=choices) def clean_password(self): password = self.cleaned_data['password'] if not self.user.check_password(password): raise ValidationError('Invalid password') return password def clean_username(self): username = self.cleaned_data['username'] if User.objects.filter(username__iexact=username).exists(): raise ValidationError("A login with that username already exists. Please contact the secretariat to get this resolved.") return username