From 47dbedba818a9371797024c80503bb75c1321002 Mon Sep 17 00:00:00 2001 From: Jennifer Richards Date: Thu, 29 Feb 2024 16:08:32 -0400 Subject: [PATCH 1/5] feat: Limit initial email addr to valid usernames --- ietf/ietfauth/forms.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/ietf/ietfauth/forms.py b/ietf/ietfauth/forms.py index 9b8ee22e0b..a4e731be2c 100644 --- a/ietf/ietfauth/forms.py +++ b/ietf/ietfauth/forms.py @@ -10,6 +10,7 @@ from django.core.exceptions import ValidationError from django.db import models from django.contrib.auth.models import User +from django.contrib.auth.validators import ASCIIUsernameValidator import debug # pyflakes:ignore @@ -20,8 +21,21 @@ from .widgets import PasswordStrengthInput, PasswordConfirmationInput +validate_username_email = ASCIIUsernameValidator( + message=( + "This value may contain only unaccented lowercase a-z " + "and uppercase A-Z letters, numbers, and @/./+/-/_ characters." + ) +) + + +class UsernameEmailField(forms.EmailField): + """Email that is suitable for a username""" + default_validators = forms.EmailField.default_validators + [validate_username_email] + + class RegistrationForm(forms.Form): - email = forms.EmailField(label="Your email (lowercase)") + email = UsernameEmailField(label="Your email (lowercase)") def clean_email(self): email = self.cleaned_data.get('email', '') From b02fac9bb16ec852c689f89dbd5d7958dbf02ae9 Mon Sep 17 00:00:00 2001 From: Jennifer Richards Date: Thu, 29 Feb 2024 16:09:40 -0400 Subject: [PATCH 2/5] feat: Only allow setting username to valid emails --- ietf/ietfauth/forms.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/ietf/ietfauth/forms.py b/ietf/ietfauth/forms.py index a4e731be2c..d289c79bce 100644 --- a/ietf/ietfauth/forms.py +++ b/ietf/ietfauth/forms.py @@ -244,10 +244,19 @@ 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 ] + choices = [(email.address, email.address) for email in self._allowed_emails(user)] self.fields['username'] = forms.ChoiceField(choices=choices) + @staticmethod + def _allowed_emails(user): + for email in user.person.email_set.filter(active=True): + try: + validate_username_email(email.address) + except ValidationError: + pass + else: + yield email + def clean_password(self): password = self.cleaned_data['password'] if not self.user.check_password(password): From a970583c6d570718a34ae6cdb382a65e76355fc8 Mon Sep 17 00:00:00 2001 From: Jennifer Richards Date: Thu, 29 Feb 2024 16:52:16 -0400 Subject: [PATCH 3/5] chore: Describe email address limitations --- ietf/templates/registration/change_username.html | 3 +++ ietf/templates/registration/create.html | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/ietf/templates/registration/change_username.html b/ietf/templates/registration/change_username.html index 159a373031..b73af57ac6 100644 --- a/ietf/templates/registration/change_username.html +++ b/ietf/templates/registration/change_username.html @@ -13,6 +13,9 @@

Change username

email address, then please first edit your profile to add that email address to the active email addresses for your account. + Usernames are restricted to alphanumeric characters and the characters + "@", ".", "+", "-", and "_". Email addresses with other characters may + not be chosen as a username.
{% csrf_token %} diff --git a/ietf/templates/registration/create.html b/ietf/templates/registration/create.html index 142f7450c8..639a847855 100644 --- a/ietf/templates/registration/create.html +++ b/ietf/templates/registration/create.html @@ -33,7 +33,9 @@

Account creation

- Otherwise, please enter your email address in order to create your datatracker account. + Otherwise, please enter your email address in order to create your datatracker account. This email + address will be used as your datatracker username and must consist only of alphanumeric characters + and the symbols "@", ".", "+", "-", and "_".

{% csrf_token %} From f1f7688553c0b781632845a309fdbba814ae1a84 Mon Sep 17 00:00:00 2001 From: Jennifer Richards Date: Fri, 1 Mar 2024 10:37:15 -0400 Subject: [PATCH 4/5] fix: better validate_username_email message --- ietf/ietfauth/forms.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ietf/ietfauth/forms.py b/ietf/ietfauth/forms.py index d289c79bce..9bd14401f9 100644 --- a/ietf/ietfauth/forms.py +++ b/ietf/ietfauth/forms.py @@ -23,8 +23,8 @@ validate_username_email = ASCIIUsernameValidator( message=( - "This value may contain only unaccented lowercase a-z " - "and uppercase A-Z letters, numbers, and @/./+/-/_ characters." + 'This value may contain only unaccented lowercase letters (a-z), ' + 'digits (0-9), and the special characters "@", ".", "+", "-", and "_".' ) ) From ef95c4e416ef8d247b5789dfbce51bb963000a34 Mon Sep 17 00:00:00 2001 From: Jennifer Richards Date: Fri, 1 Mar 2024 10:53:24 -0400 Subject: [PATCH 5/5] fix: Apply case validation more thoroughly --- ietf/ietfauth/forms.py | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/ietf/ietfauth/forms.py b/ietf/ietfauth/forms.py index 9bd14401f9..b36153318f 100644 --- a/ietf/ietfauth/forms.py +++ b/ietf/ietfauth/forms.py @@ -21,12 +21,26 @@ from .widgets import PasswordStrengthInput, PasswordConfirmationInput -validate_username_email = ASCIIUsernameValidator( +class UsernameEmailValidator(ASCIIUsernameValidator): message=( 'This value may contain only unaccented lowercase letters (a-z), ' 'digits (0-9), and the special characters "@", ".", "+", "-", and "_".' ) -) + + def __call__(self, value): + if value.lower() != value: + raise forms.ValidationError( + ( + "The supplied address contained uppercase letters. " + "Please use a lowercase email address." + ), + code="invalid_case", + params={"value": value}, + ) + super().__call__(value) + + +validate_username_email = UsernameEmailValidator() class UsernameEmailField(forms.EmailField): @@ -37,14 +51,6 @@ class UsernameEmailField(forms.EmailField): class RegistrationForm(forms.Form): email = UsernameEmailField(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'}))