Ensure email is case-insensitive

While RFC 5321 alows for the username/mailbox art of the address to be
case-sensitive, it is hightly discouraged.

    The local-part of a mailbox MUST BE treated as case
    sensitive. Therefore, SMTP implementations MUST take care to
    preserve the case of mailbox local-parts. In particular, for
    some hosts, the user "smith" is different from the user
    "Smith". However, exploiting the case sensitivity of mailbox
    local-parts impedes interoperability and is discouraged.
    Mailbox domains follow normal DNS rules and are hence not
    case sensitive.
    -- https://tools.ietf.org/rfc/rfc5321.txt

Signed-off-by: Mike Fiedler <miketheman@gmail.com>
This commit is contained in:
Mike Fiedler 2017-10-01 18:01:55 -04:00
parent f03c173c57
commit 9076715d84
No known key found for this signature in database
GPG Key ID: 5E1134F2FAF158B9
4 changed files with 39 additions and 3 deletions

View File

@ -80,7 +80,7 @@ def forgot_password(org_slug=None):
submitted = False
if request.method == 'POST' and request.form['email']:
submitted = True
email = request.form['email']
email = request.form['email'].lower()
try:
org = current_org._get_current_object()
user = models.User.get_by_email_and_org(email, org)
@ -119,7 +119,7 @@ def login(org_slug=None):
if request.method == 'POST':
try:
org = current_org._get_current_object()
user = models.User.get_by_email_and_org(request.form['email'], org)
user = models.User.get_by_email_and_org(request.form['email'].lower(), org)
if user and user.verify_password(request.form['password']):
remember = ('remember' in request.form)
login_user(user, remember=remember)

View File

@ -30,7 +30,7 @@ class UserListResource(BaseResource):
user = models.User(org=self.current_org,
name=req['name'],
email=req['email'],
email=req['email'].lower(),
group_ids=[self.current_org.default_group.id])
try:

View File

@ -26,6 +26,16 @@ class TestUserListResourcePost(BaseTestCase):
self.assertEqual(rv.json['name'], test_user['name'])
self.assertEqual(rv.json['email'], test_user['email'])
def test_creates_user_case_insensitive_email(self):
admin = self.factory.create_admin()
test_user = {'name': 'User', 'email': 'User@Example.com', 'password': 'test'}
rv = self.make_request('post', '/api/users', data=test_user, user=admin)
self.assertEqual(rv.status_code, 200)
self.assertEqual(rv.json['name'], test_user['name'])
self.assertEqual(rv.json['email'], 'user@example.com')
def test_returns_400_when_email_taken(self):
admin = self.factory.create_admin()
@ -34,6 +44,20 @@ class TestUserListResourcePost(BaseTestCase):
self.assertEqual(rv.status_code, 400)
def test_returns_400_when_email_taken_case_insensitive(self):
admin = self.factory.create_admin()
test_user1 = {'name': 'User', 'email': 'user@example.com', 'password': 'test'}
rv = self.make_request('post', '/api/users', data=test_user1, user=admin)
self.assertEqual(rv.status_code, 200)
self.assertEqual(rv.json['email'], 'user@example.com')
test_user2 = {'name': 'User', 'email': 'user@Example.com', 'password': 'test'}
rv = self.make_request('post', '/api/users', data=test_user2, user=admin)
self.assertEqual(rv.status_code, 400)
class TestUserListGet(BaseTestCase):
def test_returns_users_for_given_org_only(self):

View File

@ -111,6 +111,18 @@ class TestLogin(BaseTestCase):
self.assertEquals(rv.status_code, 302)
login_user_mock.assert_called_with(user, remember=False)
def test_submit_case_insensitive_user_and_password(self):
user = self.factory.user
user.hash_password('password')
self.db.session.add(user)
self.db.session.commit()
with patch('redash.handlers.authentication.login_user') as login_user_mock:
rv = self.client.post('/default/login', data={'email': user.email.upper(), 'password': 'password'})
self.assertEquals(rv.status_code, 302)
login_user_mock.assert_called_with(user, remember=False)
def test_submit_correct_user_and_password_and_remember_me(self):
user = self.factory.user
user.hash_password('password')