Skip to content

Commit d632322

Browse files
committed
Merge pull request #150 from Tivix/pr/141
Ability to login using e-mail (without allauth)
2 parents 37dda5d + 54eb54a commit d632322

File tree

3 files changed

+100
-23
lines changed

3 files changed

+100
-23
lines changed

docs/api_endpoints.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ Basic
77
- /rest-auth/login/ (POST)
88

99
- username (string)
10+
- email (string)
1011
- password (string)
1112

1213

rest_auth/serializers.py

Lines changed: 53 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -19,43 +19,73 @@ class LoginSerializer(serializers.Serializer):
1919
email = serializers.EmailField(required=False, allow_blank=True)
2020
password = serializers.CharField(style={'input_type': 'password'})
2121

22+
def _validate_email(self, email, password):
23+
user = None
24+
25+
if email and password:
26+
user = authenticate(email=email, password=password)
27+
else:
28+
msg = _('Must include "email" and "password".')
29+
raise exceptions.ValidationError(msg)
30+
31+
return user
32+
33+
def _validate_username(self, username, password):
34+
user = None
35+
36+
if username and password:
37+
user = authenticate(username=username, password=password)
38+
else:
39+
msg = _('Must include "username" and "password".')
40+
raise exceptions.ValidationError(msg)
41+
42+
return user
43+
44+
def _validate_username_email(self, username, email, password):
45+
user = None
46+
47+
if email and password:
48+
user = authenticate(email=email, password=password)
49+
elif username and password:
50+
user = authenticate(username=username, password=password)
51+
else:
52+
msg = _('Must include either "username" or "email" and "password".')
53+
raise exceptions.ValidationError(msg)
54+
55+
return user
56+
2257
def validate(self, attrs):
2358
username = attrs.get('username')
2459
email = attrs.get('email')
2560
password = attrs.get('password')
2661

62+
user = None
63+
2764
if 'allauth' in settings.INSTALLED_APPS:
2865
from allauth.account import app_settings
66+
2967
# Authentication through email
3068
if app_settings.AUTHENTICATION_METHOD == app_settings.AuthenticationMethod.EMAIL:
31-
if email and password:
32-
user = authenticate(email=email, password=password)
33-
else:
34-
msg = _('Must include "email" and "password".')
35-
raise exceptions.ValidationError(msg)
69+
user = self._validate_email(email, password)
70+
3671
# Authentication through username
37-
elif app_settings.AUTHENTICATION_METHOD == app_settings.AuthenticationMethod.USERNAME:
38-
if username and password:
39-
user = authenticate(username=username, password=password)
40-
else:
41-
msg = _('Must include "username" and "password".')
42-
raise exceptions.ValidationError(msg)
72+
if app_settings.AUTHENTICATION_METHOD == app_settings.AuthenticationMethod.USERNAME:
73+
user = self._validate_username(username, password)
74+
4375
# Authentication through either username or email
4476
else:
45-
if email and password:
46-
user = authenticate(email=email, password=password)
47-
elif username and password:
48-
user = authenticate(username=username, password=password)
49-
else:
50-
msg = _('Must include either "username" or "email" and "password".')
51-
raise exceptions.ValidationError(msg)
52-
53-
elif username and password:
54-
user = authenticate(username=username, password=password)
77+
user = self._validate_username_email(username, email, password)
5578

5679
else:
57-
msg = _('Must include "username" and "password".')
58-
raise exceptions.ValidationError(msg)
80+
# Authentication without using allauth
81+
if email:
82+
try:
83+
username = UserModel.objects.get(email__iexact=email).username
84+
except UserModel.DoesNotExist:
85+
pass
86+
87+
if username:
88+
user = self._validate_username_email(username, '', password)
5989

6090
# Did we get back an active user?
6191
if user:

rest_auth/tests/test_api.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from django.test import TestCase
33
from django.contrib.auth import get_user_model
44
from django.core import mail
5+
from django.conf import settings
56
from django.test.utils import override_settings
67
from django.utils.encoding import force_text
78

@@ -90,6 +91,51 @@ def test_login(self):
9091
# test empty payload
9192
self.post(self.login_url, data={}, status_code=400)
9293

94+
def test_login_by_email(self):
95+
# starting test without allauth app
96+
settings.INSTALLED_APPS.remove('allauth')
97+
98+
payload = {
99+
"email": self.EMAIL.lower(),
100+
"password": self.PASS
101+
}
102+
# there is no users in db so it should throw error (400)
103+
self.post(self.login_url, data=payload, status_code=400)
104+
105+
self.post(self.password_change_url, status_code=403)
106+
107+
# create user
108+
user = get_user_model().objects.create_user(self.USERNAME, self.EMAIL, self.PASS)
109+
110+
# test auth by email
111+
self.post(self.login_url, data=payload, status_code=200)
112+
self.assertEqual('key' in self.response.json.keys(), True)
113+
self.token = self.response.json['key']
114+
115+
# test auth by email in different case
116+
payload = {
117+
"email": self.EMAIL.upper(),
118+
"password": self.PASS
119+
}
120+
self.post(self.login_url, data=payload, status_code=200)
121+
self.assertEqual('key' in self.response.json.keys(), True)
122+
self.token = self.response.json['key']
123+
124+
# test inactive user
125+
user.is_active = False
126+
user.save()
127+
self.post(self.login_url, data=payload, status_code=400)
128+
129+
# test wrong email/password
130+
payload = {
131+
"email": 't' + self.EMAIL,
132+
"password": self.PASS
133+
}
134+
self.post(self.login_url, data=payload, status_code=400)
135+
136+
# test empty payload
137+
self.post(self.login_url, data={}, status_code=400)
138+
93139
def test_password_change(self):
94140
login_payload = {
95141
"username": self.USERNAME,

0 commit comments

Comments
 (0)