emails/validators: miscellaneous adjustments (#32435)

* address preferred mx server
 * set connection timeout
 * do not raise validation error on temporary failures
 * add an option to bypass smtp validation
 * catch all dns exceptions
This commit is contained in:
Christophe Siraut 2019-04-18 09:33:34 +02:00
parent 133bd51350
commit 4a5b60a95a
3 changed files with 16 additions and 9 deletions

View File

@ -23,14 +23,14 @@ from django.utils.translation import ugettext_lazy as _
def validate_email_address(value):
if not settings.HOBO_VALIDATE_EMAIL_WITH_SMTP:
return
email_domain = value.split('@')[-1]
try:
mx_server = dns.resolver.query(email_domain, 'MX')[0].exchange.to_text()
except dns.resolver.NXDOMAIN as e:
mx_server = sorted(dns.resolver.query(email_domain, 'MX'), key=lambda rdata: rdata.preference)[0].exchange.to_text()
except dns.exception.DNSException as e:
raise ValidationError(_('Error: %s') % str(e))
except dns.resolver.NoAnswer as e:
raise ValidationError(_('Error: %s') % str(e))
smtp = smtplib.SMTP(timeout=30)
smtp = smtplib.SMTP(timeout=10)
try:
smtp.connect(mx_server)
except socket.error as e:
@ -41,11 +41,9 @@ def validate_email_address(value):
raise ValidationError(_('Error while connecting to %(server)s: %(msg)s') % {'server': mx_server, 'msg': msg})
smtp.mail('')
status, msg = smtp.rcpt(value)
if status == 250:
smtp.quit()
return
smtp.quit()
raise ValidationError(_('Email address not found on %s') % mx_server)
if status // 100 == 5:
raise ValidationError(_('Email address not found on %s') % mx_server)
def validate_email_spf(value, strict=False):

View File

@ -26,6 +26,7 @@ DEBUG = True
ALLOWED_HOSTS = []
HOBO_VALIDATE_EMAIL_WITH_SMTP = True
ALLOWED_SPF_RECORDS = []
# Application definition

View File

@ -22,6 +22,9 @@ def dns_resolver(monkeypatch):
mx.exchange = mock.create_autospec(name.Name)
mx.exchange.to_text = mock.MagicMock()
mx.exchange.to_text.return_value = 'localhost:10025'
mx.preference = mock.create_autospec(name.Name)
mx.preference.to_text = mock.MagicMock()
mx.preference.to_text.return_value = 10
return [mx]
if kind == 'TXT':
txt = mock.create_autospec(TXT)
@ -86,6 +89,11 @@ def test_validate_email_address_socket_error(dns_resolver, monkeypatch):
assert 'Error while connecting' in str(e.value)
def test_validate_email_address_bypass(settings):
settings.HOBO_VALIDATE_EMAIL_WITH_SMTP = False
assert validate_email_address('foo') == None
def test_invalid_address(client, admin_user):
client.post('/login/', {'username': 'admin', 'password': 'password'})
response = client.post('/emails/', {'default_from_email': 'foobar'})