diff --git a/tests/test_email.py b/tests/test_email.py index 70fbb23e..149ca197 100644 --- a/tests/test_email.py +++ b/tests/test_email.py @@ -39,3 +39,32 @@ def test_returns_true_on_valid_email(value, whitelist): ]) def test_returns_failed_validation_on_invalid_email(value): assert isinstance(email(value), ValidationFailure) + + +@pytest.mark.parametrize(('value', 'whitelist'), [ + ('email@here.com', ['foo.here.com']), + ('weirder-email@here.and.there.com', ['here.and.there.org']), + ('email@[127.0.0.1]', ['[127.0.0.2]']), + ('example@valid-----hyphens.com', ['-----hyphens.com']), + ('example@valid-with-hyphens.com', ['hyphens.com']), + ('email@localhost', ['localdomain']), + ('"test@test"@example.com', ['example.org', 'example.net', 'foo.org']), + ('"\\\011"@here.com', ['there.com']), +]) +def test_returns_failed_validation_on_bad_whitelist(value, whitelist): + assert isinstance(email(value, whitelist=whitelist, only_whitelist=True), + ValidationFailure) + + +@pytest.mark.parametrize(('value', 'whitelist'), [ + ('email@here.com', ['here.com']), + ('weirder-email@here.and.there.com', ['here.and.there.com']), + ('email@[127.0.0.1]', ['[127.0.0.1]']), + ('example@valid-----hyphens.com', ['valid-----hyphens.com']), + ('example@valid-with-hyphens.com', ['valid-with-hyphens.com']), + ('email@localhost', ['localdomain', 'localhost']), + ('"test@test"@example.com', ['example.org', 'example.com', 'foo.org']), + ('"\\\011"@here.com', ['here.com', 'test.org', 'zeta', 100]), +]) +def test_returns_true_on_valid_email_and_only_whitelist(value, whitelist): + assert email(value, whitelist=whitelist, only_whitelist=True) diff --git a/validators/email.py b/validators/email.py index 6c36c2e8..d92b5837 100644 --- a/validators/email.py +++ b/validators/email.py @@ -23,7 +23,7 @@ @validator -def email(value, whitelist=None): +def email(value, whitelist=None, only_whitelist=False): """ Validate an email address. @@ -46,27 +46,34 @@ def email(value, whitelist=None): :param value: value to validate :param whitelist: domain names to whitelist + :param only_whitelist: specify whether to fail non whitelisted domains :copyright: (c) Django Software Foundation and individual contributors. :license: BSD """ + if not value or '@' not in value: + return False if whitelist is None: whitelist = domain_whitelist - if not value or '@' not in value: - return False - user_part, domain_part = value.rsplit('@', 1) if not user_regex.match(user_part): return False - if domain_part not in whitelist and not domain_regex.match(domain_part): + # skip domain regex check if user specified it explicitly in whitelist + if only_whitelist: + return domain_part in whitelist + + # check if domain is good + if not (domain_regex.match(domain_part) or domain_part in whitelist): # Try for possible IDN domain-part try: domain_part = domain_part.encode('idna').decode('ascii') return domain_regex.match(domain_part) except UnicodeError: return False + + # No whitelist and email passes user_regex and domain passes domain_regex. return True