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 re
import warnings
from pathlib import Path
from urllib.parse import urlsplit, urlunsplit
from django.core.exceptions import ValidationError
from django.utils.deconstruct import deconstructible
from django.utils.deprecation import RemovedInDjango41Warning
from django.utils.encoding import punycode
from django.utils.ipv6 import is_valid_ipv6_address
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)
r'\[([A-f0-9:.]+)\]\Z',
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:
self.message = message
if code is not None:
self.code = code
if whitelist is not None:
self.domain_whitelist = whitelist
if allowlist is not None:
self.domain_allowlist = allowlist
def __call__(self, value):
if not value or '@' not in value:
@ -186,7 +215,7 @@ class EmailValidator:
if not self.user_regex.match(user_part):
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)):
# Try for possible IDN domain-part
try:
@ -215,7 +244,7 @@ class EmailValidator:
def __eq__(self, other):
return (
isinstance(other, EmailValidator) and
(self.domain_whitelist == other.domain_whitelist) and
(self.domain_allowlist == other.domain_allowlist) and
(self.message == other.message) and
(self.code == other.code)
)

View File

@ -21,6 +21,9 @@ details on these changes.
* ``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:
4.0

View File

@ -119,11 +119,11 @@ to, or in lieu of custom ``field.clean()`` methods.
``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 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
@ -136,14 +136,22 @@ to, or in lieu of custom ``field.clean()`` methods.
The error code used by :exc:`~django.core.exceptions.ValidationError`
if validation fails. Defaults to ``"invalid"``.
.. attribute:: whitelist
.. attribute:: allowlist
Whitelist of email domains to allow. By default, a regular expression
(the ``domain_regex`` attribute) is used to validate whatever appears
after the @ sign. However, if that string appears in the whitelist, this
validation is bypassed. If not provided, the default whitelist is
``['localhost']``. Other domains that don't contain a dot won't pass
validation, so you'd need to whitelist them as necessary.
Allowlist of email domains. By default, a regular expression (the
``domain_regex`` attribute) is used to validate whatever appears after
the ``@`` sign. However, if that string appears in the ``allowlist``,
this validation is bypassed. If not provided, the default ``allowlist``
is ``['localhost']``. Other domains that don't contain a dot won't pass
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``
----------------

View File

@ -351,3 +351,9 @@ Miscellaneous
* Using a boolean value in :attr:`.BaseCommand.requires_system_checks` is
deprecated. Use ``'__all__'`` instead of ``True``, and ``[]`` (an empty list)
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
Ai
Alchin
allowlist
alphanumerics
amet
analytics
@ -780,7 +781,6 @@ vertices
viewable
virtualized
Weblog
whitelist
whitespace
whitespaces
whizbang

View File

@ -16,7 +16,8 @@ from django.core.validators import (
validate_ipv4_address, validate_ipv6_address, validate_ipv46_address,
validate_slug, validate_unicode_slug,
)
from django.test import SimpleTestCase
from django.test import SimpleTestCase, ignore_warnings
from django.utils.deprecation import RemovedInDjango41Warning
try:
from PIL import Image # noqa
@ -50,7 +51,7 @@ TEST_DATA = [
(validate_email, 'example@valid-with-hyphens.com', None),
(validate_email, 'test@domain.with.idn.tld.उदाहरण.परीक्षा', 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, 'example@atm.%s' % ('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='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']