forms: Set EmailField max_length to match Django Model.EmailField.

Django's Model.EmailField's default max_length is 254 characters,
while the Form.EmailField's default max length is 320 characters.
The longer valid length for form email fields raises an error
when an email with over 254 characters is validated and the server
attempts to create a preregistration user or realm.

Sets the max length on current form EmailFields to match the max
length on corresponding email fields in the database.

For the form MultiEmailField used on the find account/team page,
we don't need to set the max length to 254, but we don't expect
any emails longer than that to match any existing user accounts.
Adds tests in `zerver/tests/test_signup.py` for form submissions
with long email addresses.
This commit is contained in:
Lauryn Menard
2025-10-09 17:03:25 +02:00
committed by Alex Vandiver
parent fdcfafd13d
commit b42d3e77e7
2 changed files with 42 additions and 3 deletions

View File

@@ -68,6 +68,11 @@ DEACTIVATED_ACCOUNT_ERROR = gettext_lazy(
) )
PASSWORD_TOO_WEAK_ERROR = gettext_lazy("The password is too weak.") PASSWORD_TOO_WEAK_ERROR = gettext_lazy("The password is too weak.")
# Set Form.EmailField to match the default max_length on Model.EmailField,
# can be removed when https://code.djangoproject.com/ticket/35119 is
# completed.
EMAIL_MAX_LENGTH = 254
class OverridableValidationError(ValidationError): class OverridableValidationError(ValidationError):
pass pass
@@ -242,7 +247,7 @@ class ToSForm(forms.Form):
class HomepageForm(forms.Form): class HomepageForm(forms.Form):
email = forms.EmailField() email = forms.EmailField(max_length=EMAIL_MAX_LENGTH)
def __init__(self, *args: Any, **kwargs: Any) -> None: def __init__(self, *args: Any, **kwargs: Any) -> None:
self.realm = kwargs.pop("realm", None) self.realm = kwargs.pop("realm", None)
@@ -321,7 +326,9 @@ class ImportRealmOwnerSelectionForm(forms.Form):
class RealmCreationForm(RealmDetailsForm): class RealmCreationForm(RealmDetailsForm):
# This form determines whether users can create a new realm. # This form determines whether users can create a new realm.
email = forms.EmailField(validators=[email_not_system_bot, email_is_not_disposable]) email = forms.EmailField(
validators=[email_not_system_bot, email_is_not_disposable], max_length=EMAIL_MAX_LENGTH
)
import_from = forms.ChoiceField( import_from = forms.ChoiceField(
choices=PreregistrationRealm.IMPORT_FROM_CHOICES, choices=PreregistrationRealm.IMPORT_FROM_CHOICES,
required=False, required=False,
@@ -539,7 +546,7 @@ def rate_limit_password_reset_form_by_email(email: str) -> None:
class CreateUserForm(forms.Form): class CreateUserForm(forms.Form):
full_name = forms.CharField(max_length=100) full_name = forms.CharField(max_length=100)
email = forms.EmailField() email = forms.EmailField(max_length=EMAIL_MAX_LENGTH)
class OurAuthenticationForm(AuthenticationForm): class OurAuthenticationForm(AuthenticationForm):

View File

@@ -1108,6 +1108,11 @@ class LoginTest(ZulipTestCase):
self.assertEqual(result.status_code, 200) self.assertEqual(result.status_code, 200)
self.assertContains(result, "Enter a valid email address") self.assertContains(result, "Enter a valid email address")
invalid_email = "a" * 260 + "@example.com"
result = self.client_post("/accounts/home/", {"email": invalid_email}, subdomain="zulip")
self.assertEqual(result.status_code, 200)
self.assertContains(result, "Ensure this value has at most 254 characters (it has 272).")
def test_register_deactivated_partway_through(self) -> None: def test_register_deactivated_partway_through(self) -> None:
""" """
If you try to register for a deactivated realm, you get a clear error If you try to register for a deactivated realm, you get a clear error
@@ -4743,6 +4748,33 @@ class TestFindMyTeam(ZulipTestCase):
result = self.client_get("/accounts/find/", {"emails": "invalid"}) result = self.client_get("/accounts/find/", {"emails": "invalid"})
self.assertEqual(result.status_code, 200) self.assertEqual(result.status_code, 200)
def test_find_team_long_email_address(self) -> None:
# Emails over 320 characters are considered invalid.
data = {"emails": "a" * 320 + "@example.com"}
result = self.client_post("/accounts/find/", data)
self.assertEqual(result.status_code, 200)
self.assertIn(b"Enter a valid email", result.content)
from django.core.mail import outbox
self.assert_length(outbox, 0)
# Emails in the database are never over 254 characters,
# but searching for them does not cause an error.
# When https://code.djangoproject.com/ticket/35119 is
# resolved, Django's email validator will return this
# case as invalid, so this test will need to be updated.
data = {"emails": "a" * 260 + "@example.com"}
result = self.client_post("/accounts/find/", data)
self.assertEqual(result.status_code, 200)
content = result.content.decode()
self.assertIn("Emails sent! The addresses entered on", content)
self.assertIn("a@example.com", content)
from django.core.mail import outbox
self.assert_length(outbox, 1)
message = outbox[0]
self.assertIn("Unfortunately, no Zulip Cloud accounts", message.body)
def test_find_team_zero_emails(self) -> None: def test_find_team_zero_emails(self) -> None:
data = {"emails": ""} data = {"emails": ""}
result = self.client_post("/accounts/find/", data) result = self.client_post("/accounts/find/", data)