mirror of
https://github.com/django/django.git
synced 2025-06-08 04:59:13 +00:00
Fixed #36014 -- Supported international domains in EmailValidator.
This commit is contained in:
parent
53e21eebf2
commit
c068f000be
@ -6,7 +6,6 @@ from urllib.parse import urlsplit
|
|||||||
|
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django.utils.deconstruct import deconstructible
|
from django.utils.deconstruct import deconstructible
|
||||||
from django.utils.encoding import punycode
|
|
||||||
from django.utils.ipv6 import is_valid_ipv6_address
|
from django.utils.ipv6 import is_valid_ipv6_address
|
||||||
from django.utils.regex_helper import _lazy_re_compile
|
from django.utils.regex_helper import _lazy_re_compile
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
@ -76,14 +75,14 @@ class DomainNameValidator(RegexValidator):
|
|||||||
# Max length for domain name labels is 63 characters per RFC 1034 sec. 3.1.
|
# Max length for domain name labels is 63 characters per RFC 1034 sec. 3.1.
|
||||||
domain_re = r"(?:\.(?!-)[a-z" + ul + r"0-9-]{1,63}(?<!-))*"
|
domain_re = r"(?:\.(?!-)[a-z" + ul + r"0-9-]{1,63}(?<!-))*"
|
||||||
# Top-level domain.
|
# Top-level domain.
|
||||||
tld_re = (
|
tld_no_fqdn_re = (
|
||||||
r"\." # dot
|
r"\." # dot
|
||||||
r"(?!-)" # can't start with a dash
|
r"(?!-)" # can't start with a dash
|
||||||
r"(?:[a-z" + ul + "-]{2,63}" # domain label
|
r"(?:[a-z" + ul + "-]{2,63}" # domain label
|
||||||
r"|xn--[a-z0-9]{1,59})" # or punycode label
|
r"|xn--[a-z0-9]{1,59})" # or punycode label
|
||||||
r"(?<!-)" # can't end with a dash
|
r"(?<!-)" # can't end with a dash
|
||||||
r"\.?" # may have a trailing dot
|
|
||||||
)
|
)
|
||||||
|
tld_re = tld_no_fqdn_re + r"\.?"
|
||||||
ascii_only_hostname_re = r"[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?"
|
ascii_only_hostname_re = r"[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?"
|
||||||
ascii_only_domain_re = r"(?:\.(?!-)[a-zA-Z0-9-]{1,63}(?<!-))*"
|
ascii_only_domain_re = r"(?:\.(?!-)[a-zA-Z0-9-]{1,63}(?<!-))*"
|
||||||
ascii_only_tld_re = (
|
ascii_only_tld_re = (
|
||||||
@ -210,6 +209,10 @@ def validate_integer(value):
|
|||||||
class EmailValidator:
|
class EmailValidator:
|
||||||
message = _("Enter a valid email address.")
|
message = _("Enter a valid email address.")
|
||||||
code = "invalid"
|
code = "invalid"
|
||||||
|
hostname_re = DomainNameValidator.hostname_re
|
||||||
|
domain_re = DomainNameValidator.domain_re
|
||||||
|
tld_no_fqdn_re = DomainNameValidator.tld_no_fqdn_re
|
||||||
|
|
||||||
user_regex = _lazy_re_compile(
|
user_regex = _lazy_re_compile(
|
||||||
# dot-atom
|
# dot-atom
|
||||||
r"(^[-!#$%&'*+/=?^_`{}|~0-9A-Z]+(\.[-!#$%&'*+/=?^_`{}|~0-9A-Z]+)*\Z"
|
r"(^[-!#$%&'*+/=?^_`{}|~0-9A-Z]+(\.[-!#$%&'*+/=?^_`{}|~0-9A-Z]+)*\Z"
|
||||||
@ -219,8 +222,7 @@ class EmailValidator:
|
|||||||
re.IGNORECASE,
|
re.IGNORECASE,
|
||||||
)
|
)
|
||||||
domain_regex = _lazy_re_compile(
|
domain_regex = _lazy_re_compile(
|
||||||
# max length for domain name labels is 63 characters per RFC 1034
|
r"^" + hostname_re + domain_re + tld_no_fqdn_re + r"\Z",
|
||||||
r"((?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+)(?:[A-Z0-9-]{2,63}(?<!-))\Z",
|
|
||||||
re.IGNORECASE,
|
re.IGNORECASE,
|
||||||
)
|
)
|
||||||
literal_regex = _lazy_re_compile(
|
literal_regex = _lazy_re_compile(
|
||||||
@ -252,14 +254,6 @@ class EmailValidator:
|
|||||||
if domain_part not in self.domain_allowlist and not self.validate_domain_part(
|
if domain_part not in self.domain_allowlist and not self.validate_domain_part(
|
||||||
domain_part
|
domain_part
|
||||||
):
|
):
|
||||||
# Try for possible IDN domain-part
|
|
||||||
try:
|
|
||||||
domain_part = punycode(domain_part)
|
|
||||||
except UnicodeError:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
if self.validate_domain_part(domain_part):
|
|
||||||
return
|
|
||||||
raise ValidationError(self.message, code=self.code, params={"value": value})
|
raise ValidationError(self.message, code=self.code, params={"value": value})
|
||||||
|
|
||||||
def validate_domain_part(self, domain_part):
|
def validate_domain_part(self, domain_part):
|
||||||
|
@ -31,7 +31,8 @@ class EmailFieldTest(FormFieldAssertionsMixin, SimpleTestCase):
|
|||||||
# Check for runaway regex security problem. This will take a long time
|
# Check for runaway regex security problem. This will take a long time
|
||||||
# if the security fix isn't in place.
|
# if the security fix isn't in place.
|
||||||
addr = "viewx3dtextx26qx3d@yahoo.comx26latlngx3d15854521645943074058"
|
addr = "viewx3dtextx26qx3d@yahoo.comx26latlngx3d15854521645943074058"
|
||||||
self.assertEqual(addr, f.clean(addr))
|
with self.assertRaisesMessage(ValidationError, "Enter a valid email address."):
|
||||||
|
f.clean(addr)
|
||||||
|
|
||||||
def test_emailfield_not_required(self):
|
def test_emailfield_not_required(self):
|
||||||
f = EmailField(required=False)
|
f = EmailField(required=False)
|
||||||
|
@ -319,6 +319,20 @@ TEST_DATA = [
|
|||||||
(validate_email, "example@inv-.alid-.com", ValidationError),
|
(validate_email, "example@inv-.alid-.com", ValidationError),
|
||||||
(validate_email, "example@inv-.-alid.com", ValidationError),
|
(validate_email, "example@inv-.-alid.com", ValidationError),
|
||||||
(validate_email, 'test@example.com\n\n<script src="x.js">', ValidationError),
|
(validate_email, 'test@example.com\n\n<script src="x.js">', ValidationError),
|
||||||
|
(validate_email, "email@xn--4ca9at.com", None),
|
||||||
|
(validate_email, "email@öäü.com", None),
|
||||||
|
(validate_email, "email@עִתוֹן.example.il", None),
|
||||||
|
(validate_email, "email@މިހާރު.example.mv", None),
|
||||||
|
(validate_email, "email@漢字.example.com", None),
|
||||||
|
(validate_email, "editor@މިހާރު.example.mv", None),
|
||||||
|
(validate_email, "@domain.com", ValidationError),
|
||||||
|
(validate_email, "email.domain.com", ValidationError),
|
||||||
|
(validate_email, "email@domain@domain.com", ValidationError),
|
||||||
|
(validate_email, "email@domain..com", ValidationError),
|
||||||
|
(validate_email, "email@.domain.com", ValidationError),
|
||||||
|
(validate_email, "email@-domain.com", ValidationError),
|
||||||
|
(validate_email, "email@domain-.com", ValidationError),
|
||||||
|
(validate_email, "email@domain.com-", ValidationError),
|
||||||
# Quoted-string format (CR not allowed)
|
# Quoted-string format (CR not allowed)
|
||||||
(validate_email, '"\\\011"@here.com', None),
|
(validate_email, '"\\\011"@here.com', None),
|
||||||
(validate_email, '"\\\012"@here.com', ValidationError),
|
(validate_email, '"\\\012"@here.com', ValidationError),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user