Refs #31670 -- Renamed whitelist argument and attribute of EmailValidator.

This commit is contained in:
David Smith 2020-06-17 07:35:09 +01:00 committed by Mariusz Felisiak
parent 26a413507a
commit 27c09043da
6 changed files with 104 additions and 18 deletions

View File

@ -1,10 +1,12 @@
import ipaddress import ipaddress
import re import re
import warnings
from pathlib import Path from pathlib import Path
from urllib.parse import urlsplit, urlunsplit from urllib.parse import urlsplit, urlunsplit
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.deprecation import RemovedInDjango41Warning
from django.utils.encoding import punycode 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
@ -167,15 +169,42 @@ class EmailValidator:
# literal form, ipv4 or ipv6 address (SMTP 4.1.3) # literal form, ipv4 or ipv6 address (SMTP 4.1.3)
r'\[([A-f0-9:.]+)\]\Z', r'\[([A-f0-9:.]+)\]\Z',
re.IGNORECASE) re.IGNORECASE)
domain_whitelist = ['localhost'] domain_allowlist = ['localhost']
def __init__(self, message=None, code=None, whitelist=None): @property
def domain_whitelist(self):
warnings.warn(
'The domain_whitelist attribute is deprecated in favor of '
'domain_allowlist.',
RemovedInDjango41Warning,
stacklevel=2,
)
return self.domain_allowlist
@domain_whitelist.setter
def domain_whitelist(self, allowlist):
warnings.warn(
'The domain_whitelist attribute is deprecated in favor of '
'domain_allowlist.',
RemovedInDjango41Warning,
stacklevel=2,
)
self.domain_allowlist = allowlist
def __init__(self, message=None, code=None, allowlist=None, *, whitelist=None):
if whitelist is not None:
allowlist = whitelist
warnings.warn(
'The whitelist argument is deprecated in favor of allowlist.',
RemovedInDjango41Warning,
stacklevel=2,
)
if message is not None: if message is not None:
self.message = message self.message = message
if code is not None: if code is not None:
self.code = code self.code = code
if whitelist is not None: if allowlist is not None:
self.domain_whitelist = whitelist self.domain_allowlist = allowlist
def __call__(self, value): def __call__(self, value):
if not value or '@' not in value: if not value or '@' not in value:
@ -186,7 +215,7 @@ class EmailValidator:
if not self.user_regex.match(user_part): if not self.user_regex.match(user_part):
raise ValidationError(self.message, code=self.code) raise ValidationError(self.message, code=self.code)
if (domain_part not in self.domain_whitelist and if (domain_part not in self.domain_allowlist and
not self.validate_domain_part(domain_part)): not self.validate_domain_part(domain_part)):
# Try for possible IDN domain-part # Try for possible IDN domain-part
try: try:
@ -215,7 +244,7 @@ class EmailValidator:
def __eq__(self, other): def __eq__(self, other):
return ( return (
isinstance(other, EmailValidator) and isinstance(other, EmailValidator) and
(self.domain_whitelist == other.domain_whitelist) and (self.domain_allowlist == other.domain_allowlist) and
(self.message == other.message) and (self.message == other.message) and
(self.code == other.code) (self.code == other.code)
) )

View File

@ -21,6 +21,9 @@ details on these changes.
* ``BaseCommand.requires_system_checks`` won't support boolean values. * ``BaseCommand.requires_system_checks`` won't support boolean values.
* The ``whitelist`` argument and ``domain_whitelist`` attribute of
``django.core.validators.EmailValidator`` will be removed.
.. _deprecation-removed-in-4.0: .. _deprecation-removed-in-4.0:
4.0 4.0

View File

@ -119,11 +119,11 @@ to, or in lieu of custom ``field.clean()`` methods.
``EmailValidator`` ``EmailValidator``
------------------ ------------------
.. class:: EmailValidator(message=None, code=None, whitelist=None) .. class:: EmailValidator(message=None, code=None, allowlist=None)
:param message: If not ``None``, overrides :attr:`.message`. :param message: If not ``None``, overrides :attr:`.message`.
:param code: If not ``None``, overrides :attr:`code`. :param code: If not ``None``, overrides :attr:`code`.
:param whitelist: If not ``None``, overrides :attr:`whitelist`. :param allowlist: If not ``None``, overrides :attr:`allowlist`.
.. attribute:: message .. attribute:: message
@ -136,14 +136,22 @@ to, or in lieu of custom ``field.clean()`` methods.
The error code used by :exc:`~django.core.exceptions.ValidationError` The error code used by :exc:`~django.core.exceptions.ValidationError`
if validation fails. Defaults to ``"invalid"``. if validation fails. Defaults to ``"invalid"``.
.. attribute:: whitelist .. attribute:: allowlist
Whitelist of email domains to allow. By default, a regular expression Allowlist of email domains. By default, a regular expression (the
(the ``domain_regex`` attribute) is used to validate whatever appears ``domain_regex`` attribute) is used to validate whatever appears after
after the @ sign. However, if that string appears in the whitelist, this the ``@`` sign. However, if that string appears in the ``allowlist``,
validation is bypassed. If not provided, the default whitelist is this validation is bypassed. If not provided, the default ``allowlist``
``['localhost']``. Other domains that don't contain a dot won't pass is ``['localhost']``. Other domains that don't contain a dot won't pass
validation, so you'd need to whitelist them as necessary. validation, so you'd need to add them to the ``allowlist`` as
necessary.
.. deprecated:: 3.2
The ``whitelist`` parameter is deprecated. Use :attr:`allowlist`
instead.
The undocumented ``domain_whitelist`` attribute is deprecated. Use
``domain_allowlist`` instead.
``URLValidator`` ``URLValidator``
---------------- ----------------

View File

@ -351,3 +351,9 @@ Miscellaneous
* Using a boolean value in :attr:`.BaseCommand.requires_system_checks` is * Using a boolean value in :attr:`.BaseCommand.requires_system_checks` is
deprecated. Use ``'__all__'`` instead of ``True``, and ``[]`` (an empty list) deprecated. Use ``'__all__'`` instead of ``True``, and ``[]`` (an empty list)
instead of ``False``. instead of ``False``.
* The ``whitelist`` argument and ``domain_whitelist`` attribute of
:class:`~django.core.validators.EmailValidator` are deprecated. Use
``allowlist`` instead of ``whitelist``, and ``domain_allowlist`` instead of
``domain_whitelist``. You may need to rename ``whitelist`` in existing
migrations.

View File

@ -10,6 +10,7 @@ affordances
aggregator aggregator
Ai Ai
Alchin Alchin
allowlist
alphanumerics alphanumerics
amet amet
analytics analytics
@ -780,7 +781,6 @@ vertices
viewable viewable
virtualized virtualized
Weblog Weblog
whitelist
whitespace whitespace
whitespaces whitespaces
whizbang whizbang

View File

@ -16,7 +16,8 @@ from django.core.validators import (
validate_ipv4_address, validate_ipv6_address, validate_ipv46_address, validate_ipv4_address, validate_ipv6_address, validate_ipv46_address,
validate_slug, validate_unicode_slug, validate_slug, validate_unicode_slug,
) )
from django.test import SimpleTestCase from django.test import SimpleTestCase, ignore_warnings
from django.utils.deprecation import RemovedInDjango41Warning
try: try:
from PIL import Image # noqa from PIL import Image # noqa
@ -50,7 +51,7 @@ TEST_DATA = [
(validate_email, 'example@valid-with-hyphens.com', None), (validate_email, 'example@valid-with-hyphens.com', None),
(validate_email, 'test@domain.with.idn.tld.उदाहरण.परीक्षा', None), (validate_email, 'test@domain.with.idn.tld.उदाहरण.परीक्षा', None),
(validate_email, 'email@localhost', None), (validate_email, 'email@localhost', None),
(EmailValidator(whitelist=['localdomain']), 'email@localdomain', None), (EmailValidator(allowlist=['localdomain']), 'email@localdomain', None),
(validate_email, '"test@test"@example.com', None), (validate_email, '"test@test"@example.com', None),
(validate_email, 'example@atm.%s' % ('a' * 63), None), (validate_email, 'example@atm.%s' % ('a' * 63), None),
(validate_email, 'example@%s.atm' % ('a' * 63), None), (validate_email, 'example@%s.atm' % ('a' * 63), None),
@ -510,3 +511,42 @@ class TestValidatorEquality(TestCase):
ProhibitNullCharactersValidator(message='message', code='code1'), ProhibitNullCharactersValidator(message='message', code='code1'),
ProhibitNullCharactersValidator(message='message', code='code2') ProhibitNullCharactersValidator(message='message', code='code2')
) )
class DeprecationTests(SimpleTestCase):
@ignore_warnings(category=RemovedInDjango41Warning)
def test_whitelist(self):
validator = EmailValidator(whitelist=['localdomain'])
self.assertEqual(validator.domain_allowlist, ['localdomain'])
self.assertIsNone(validator('email@localdomain'))
self.assertEqual(validator.domain_allowlist, validator.domain_whitelist)
def test_whitelist_warning(self):
msg = "The whitelist argument is deprecated in favor of allowlist."
with self.assertRaisesMessage(RemovedInDjango41Warning, msg):
EmailValidator(whitelist='localdomain')
@ignore_warnings(category=RemovedInDjango41Warning)
def test_domain_whitelist(self):
validator = EmailValidator()
validator.domain_whitelist = ['mydomain']
self.assertEqual(validator.domain_allowlist, ['mydomain'])
self.assertEqual(validator.domain_allowlist, validator.domain_whitelist)
def test_domain_whitelist_access_warning(self):
validator = EmailValidator()
msg = (
'The domain_whitelist attribute is deprecated in favor of '
'domain_allowlist.'
)
with self.assertRaisesMessage(RemovedInDjango41Warning, msg):
validator.domain_whitelist
def test_domain_whitelist_set_warning(self):
validator = EmailValidator()
msg = (
'The domain_whitelist attribute is deprecated in favor of '
'domain_allowlist.'
)
with self.assertRaisesMessage(RemovedInDjango41Warning, msg):
validator.domain_whitelist = ['mydomain']