Skip to content

Commit ab35e4c

Browse files
committed
Merge pull request #1180 from adamrp/relax_email_validation-issue-1179
ENH: Bring email validation closer to specs
2 parents f985552 + 2895b30 commit ab35e4c

File tree

2 files changed

+45
-12
lines changed

2 files changed

+45
-12
lines changed

qiita_db/test/test_user.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ def test_validate_email(self):
4343
valid3 = 'w00t@123.456.789.com'
4444
invalid1 = '@stuff.com'
4545
invalid2 = 'asdasdásd@things.com'
46-
invalid3 = 'asdas@com'
46+
invalid3 = '.asdas@com'
4747

4848
self.assertTrue(validate_email(valid1))
4949
self.assertTrue(validate_email(valid2))
@@ -180,9 +180,9 @@ def test_exists(self):
180180
def test_exists_notindb(self):
181181
self.assertFalse(User.exists("notexist@foo.bar"))
182182

183-
def test_exists_invaid_email(self):
183+
def test_exists_invalid_email(self):
184184
with self.assertRaises(IncorrectEmailError):
185-
User.exists("notanemail@badformat")
185+
User.exists("notanemail.@badformat")
186186

187187
def test_get_email(self):
188188
self.assertEqual(self.user.email, 'admin@foo.bar')

qiita_db/user.py

Lines changed: 42 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
# The full license is in the file LICENSE, distributed with this software.
2929
# -----------------------------------------------------------------------------
3030
from __future__ import division
31-
from re import match
31+
from re import sub
3232

3333
from qiita_core.exceptions import (IncorrectEmailError, IncorrectPasswordError,
3434
IncompetentQiitaDeveloperError)
@@ -461,10 +461,18 @@ def validate_email(email):
461461
462462
Notes
463463
-----
464-
A valid email must be of the form "string AT string" where the first string
465-
must be not empty, and consists of [a-zA-Z0-9.+]. The AT is the '@' symbol.
466-
The second string must be not empty, consist of [a-zA-Z0-9.], and is
467-
required to have at least one '.'.
464+
An email address is of the form local-part@domain_part
465+
For our purposes:
466+
467+
- No quoted strings are allowed
468+
- No unicode strings are allowed
469+
- There must be exactly one @ symbol
470+
- Neither local-part nor domain-part can be blank
471+
- The local-part cannot start or end with a dot
472+
- The local-part must be composed of the following characters:
473+
a-zA-Z0-9#_~!$&'()*+,;=:.-
474+
- The domain-part must be a valid hostname, composed of:
475+
a-zA-Z0-9.
468476
469477
Parameters
470478
----------
@@ -476,15 +484,40 @@ def validate_email(email):
476484
bool
477485
Whether or not the email is valid
478486
"""
479-
valid_chars = "a-zA-Z0-9\.\+\-"
480-
pattern = r"[%s]+@[%s]+\.[%s]+" % (valid_chars, valid_chars, valid_chars)
481-
487+
# Do not accept email addresses that have unicode characters
482488
try:
483489
email.encode('ascii')
484490
except UnicodeError:
485491
return False
486492

487-
return True if match(pattern, email) is not None else False
493+
# we are not allowing quoted strings in the email address
494+
if '"' in email:
495+
return False
496+
497+
# Must have exactly 1 @ symbol
498+
if email.count('@') != 1:
499+
return False
500+
501+
local_part, domain_part = email.split('@')
502+
503+
# Neither part can be blank
504+
if not (local_part and domain_part):
505+
return False
506+
507+
# The local part cannot begin or end with a dot
508+
if local_part.startswith('.') or local_part.endswith('.'):
509+
return False
510+
511+
# This is the full set of allowable characters for the local part.
512+
local_valid_chars = "[a-zA-Z0-9#_~!$&'()*+,;=:.-]"
513+
if len(sub(local_valid_chars, '', local_part)):
514+
return False
515+
516+
domain_valid_chars = "[a-zA-Z0-9.]"
517+
if len(sub(domain_valid_chars, '', domain_part)):
518+
return False
519+
520+
return True
488521

489522

490523
def validate_password(password):

0 commit comments

Comments
 (0)