Skip to content

Commit 1932809

Browse files
committed
Make email addresses case insensitive
1 parent 06ffebf commit 1932809

File tree

8 files changed

+100
-15
lines changed

8 files changed

+100
-15
lines changed

.travis.yml

+13-5
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,23 @@ python:
44
- "3.6"
55
- "3.7"
66
env:
7-
- DJANGO=20
8-
- DJANGO=21
9-
- DJANGO=22
10-
- DJANGO=master
7+
- DJANGO=20-sqlite
8+
- DJANGO=21-sqlite
9+
- DJANGO=22-sqlite
10+
- DJANGO=master-sqlite
11+
- DJANGO=20-pg
12+
- DJANGO=21-pg
13+
- DJANGO=22-pg
14+
- DJANGO=master-pg
15+
16+
addons:
17+
postgresql: "10"
1118

1219
matrix:
1320
fast_finish: true
1421
allow_failures:
15-
- env: DJANGO=master
22+
- env: DJANGO=master-sqlite
23+
- env: DJANGO=master-pg
1624

1725
install: pip install tox-travis codecov
1826
before_script:
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
from django.db import migrations
2+
3+
try:
4+
from django.contrib.postgres.fields import CIEmailField
5+
except ImportError:
6+
CIEmailField = None
7+
else:
8+
from django.contrib.postgres.operations import CITextExtension
9+
10+
11+
def _operations():
12+
if CIEmailField:
13+
yield CITextExtension()
14+
yield migrations.AlterField(
15+
model_name='emailuser',
16+
name='email',
17+
field=CIEmailField(
18+
db_index=True, max_length=254, unique=True, verbose_name='email address'
19+
),
20+
)
21+
else:
22+
yield migrations.RunSQL(
23+
sql=(
24+
'CREATE UNIQUE INDEX mailauth_user_emailuser_email_upper_idx'
25+
' ON mailauth_user_emailuser (UPPER("email"));',
26+
)
27+
,
28+
reverse_sql=(
29+
'DROP INDEX mailauth_user_emailuser_email_upper_idx;',
30+
)
31+
)
32+
33+
34+
class Migration(migrations.Migration):
35+
dependencies = [
36+
('mailauth_user', '0002_emailuser_session_salt'),
37+
]
38+
39+
operations = list(_operations())

mailauth/contrib/user/models.py

+6-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@
44
from django.utils.crypto import get_random_string, salted_hmac
55
from django.utils.translation import ugettext_lazy as _
66

7+
try:
8+
from django.contrib.postgres.fields import CIEmailField
9+
except ImportError:
10+
from django.db.models import EmailField as CIEmailField
11+
712

813
class EmailUserManager(BaseUserManager):
914
use_in_migrations = True
@@ -37,7 +42,7 @@ class AbstractEmailUser(AbstractUser):
3742
USERNAME_FIELD = 'email'
3843
REQUIRED_FIELDS = []
3944

40-
email = models.EmailField(_('email address'), unique=True, db_index=True)
45+
email = CIEmailField(_('email address'), unique=True, db_index=True)
4146
username = None
4247
password = None
4348
session_salt = models.CharField(

mailauth/forms.py

+6-3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from django.contrib.auth import get_user_model
33
from django.contrib.sites.shortcuts import get_current_site
44
from django.core.mail import EmailMultiAlternatives
5+
from django.db import connection
56
from django.template import TemplateDoesNotExist, loader
67
from django.urls import reverse
78
from django.utils.encoding import iri_to_uri
@@ -105,9 +106,11 @@ def __init__(self, request, *args, **kwargs):
105106
self.fields[self.field_name] = field
106107

107108
def get_users(self, email=None):
108-
return get_user_model()._default_manager.filter(
109-
**{self.field_name: email}
110-
).iterator()
109+
if connection.vendor == 'postgresql':
110+
query = {self.field_name: email}
111+
else:
112+
query = {'%s__iexact' % self.field_name: email}
113+
return get_user_model()._default_manager.filter(**query).iterator()
111114

112115
def save(self):
113116
"""

setup.cfg

+5-1
Original file line numberDiff line numberDiff line change
@@ -57,15 +57,19 @@ addopts = --cov=mailauth --cov-report xml --cov-report term-missing --tb=short
5757
DJANGO_SETTINGS_MODULE = tests.testapp.settings
5858

5959
[tox:tox]
60-
envlist = py{36,37}-dj{22,21,20,master},docs
60+
envlist = py{36,37}-dj{22,11,master}-{sqlite,pg},docs
61+
depencies = psycopg2-binary
6162

6263
[testenv]
6364
passenv=CI
65+
setenv =
66+
pg: DB=pg
6467
deps =
6568
dj20: https://github.com/django/django/archive/stable/2.0.x.tar.gz#egg=django
6669
dj21: https://github.com/django/django/archive/stable/2.1.x.tar.gz#egg=django
6770
dj22: https://github.com/django/django/archive/stable/2.2.x.tar.gz#egg=django
6871
djmaster: https://github.com/django/django/archive/master.tar.gz#egg=django
72+
pg: psycopg2-binary
6973
commands = python setup.py test
7074

7175
[testenv:docs]

tests/test_forms.py

+5
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,8 @@ class MyEmailLoginForm(EmailLoginForm):
1717

1818
MyEmailLoginForm(request=None).send_mail('spiderman@avengers.com', {})
1919
assert mail.outbox[-1].alternatives
20+
21+
def test_get_users(self, db, user):
22+
assert list(EmailLoginForm(request=None).get_users('spiderman@avengers.com'))
23+
assert list(EmailLoginForm(request=None).get_users('SpiderMan@Avengers.com'))
24+
assert not list(EmailLoginForm(request=None).get_users('SpiderMan@dc.com'))

tests/test_models.py

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import pytest
2+
from django.db import IntegrityError
3+
4+
from mailauth.contrib.user import models
5+
6+
7+
class TestEmailUser:
8+
9+
def test_email__ci_unique(self, db):
10+
models.EmailUser.objects.create_user('IronMan@avengers.com')
11+
with pytest.raises(IntegrityError):
12+
models.EmailUser.objects.create_user('ironman@avengers.com')

tests/testapp/settings.py

+14-5
Original file line numberDiff line numberDiff line change
@@ -84,12 +84,21 @@
8484
# Database
8585
# https://docs.djangoproject.com/en/2.2/ref/settings/#databases
8686

87-
DATABASES = {
88-
'default': {
89-
'ENGINE': 'django.db.backends.sqlite3',
90-
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
87+
DB = os.getenv('DB', 'sqlite')
88+
if DB == 'sqlite':
89+
DATABASES = {
90+
'default': {
91+
'ENGINE': 'django.db.backends.sqlite3',
92+
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
93+
}
94+
}
95+
elif DB == 'pg':
96+
DATABASES = {
97+
'default': {
98+
'ENGINE': 'django.db.backends.postgresql',
99+
'NAME': 'django-test',
100+
}
91101
}
92-
}
93102

94103

95104
LANGUAGE_CODE = 'en-us'

0 commit comments

Comments
 (0)