forked from ietf-tools/datatracker
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathforms.py
More file actions
250 lines (187 loc) · 9.55 KB
/
forms.py
File metadata and controls
250 lines (187 loc) · 9.55 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
# Copyright The IETF Trust 2011-2022, All Rights Reserved
# -*- coding: utf-8 -*-
import re
from unidecode import unidecode
from django import forms
from django.contrib.auth.models import User
from django.contrib.auth import password_validation
from django.core.exceptions import ValidationError
from django.db import models
from ietf.person.models import Person, Email
from ietf.mailinglists.models import Allowlisted
from ietf.utils.text import isascii
from .password_validation import StrongPasswordValidator
from .validators import prevent_at_symbol, prevent_system_name, prevent_anonymous_name, is_allowed_address
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 PasswordStrengthField(forms.CharField):
widget = PasswordStrengthInput(
attrs={
"class": "password_strength",
"data-disable-strength-enforcement": "", # usually removed in init
}
)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for pwval in password_validation.get_default_password_validators():
if isinstance(pwval, password_validation.MinimumLengthValidator):
self.widget.attrs["minlength"] = pwval.min_length
elif isinstance(pwval, StrongPasswordValidator):
self.widget.attrs.pop(
"data-disable-strength-enforcement", None
)
class PasswordForm(forms.Form):
password = PasswordStrengthField()
password_confirmation = forms.CharField(widget=PasswordConfirmationInput(
confirm_with='password',
attrs={'class':'password_confirmation'}),
help_text="Enter the same password as above, for verification.",)
def __init__(self, *args, user=None, **kwargs):
# user is a kw-only argument to avoid interfering with the signature
# when this class is mixed with ModelForm in PersonPasswordForm
self.user = user
super().__init__(*args, **kwargs)
def clean_password_confirmation(self):
# clean fields here rather than a clean() method so validation is
# still enforced in PersonPasswordForm without having to override its
# clean() method
password = self.cleaned_data.get("password")
password_confirmation = self.cleaned_data.get("password_confirmation")
if password != password_confirmation:
raise ValidationError(
"The password confirmation is different than the new password"
)
try:
password_validation.validate_password(password_confirmation, self.user)
except ValidationError as err:
self.add_error("password", err)
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
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, validators=[is_allowed_address])
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 <i>%s</i> 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' ]
class ChangePasswordForm(PasswordForm):
current_password = forms.CharField(widget=forms.PasswordInput)
field_order = ["current_password", "password", "password_confirmation"]
def __init__(self, user, *args, **kwargs):
# user arg is optional in superclass, but required for this form
super().__init__(*args, user=user, **kwargs)
def clean_current_password(self):
# n.b., password = None is handled by check_password and results in a failed check
password = self.cleaned_data.get("current_password", None)
if not self.user.check_password(password):
raise ValidationError("Invalid password")
return 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