[extensions-web/wip/ne0sight] auth: allow logging in with both login or email



commit 62053a42e0a912ca31679114d0a83c735b8e3d7d
Author: Yuri Konotopov <ykonotopov gnome org>
Date:   Sun Nov 24 21:05:06 2019 +0400

    auth: allow logging in with both login or email

 po/extensions-web.pot       | 24 ++++++++++++++----------
 sweettooth/auth/backends.py | 29 +++++++++++++++++++++++++++++
 sweettooth/auth/forms.py    | 11 +++++++++--
 sweettooth/auth/tests.py    | 42 +++++++++++++++++++++++++++++++++---------
 sweettooth/settings.py      |  2 ++
 5 files changed, 87 insertions(+), 21 deletions(-)
---
diff --git a/po/extensions-web.pot b/po/extensions-web.pot
index 85450eb..62b4732 100644
--- a/po/extensions-web.pot
+++ b/po/extensions-web.pot
@@ -9,36 +9,40 @@ msgid ""
 msgstr ""
 "Project-Id-Version: 1.0\n"
 "Report-Msgid-Bugs-To: ykonotopov gnome org\n"
-"POT-Creation-Date: 2019-01-22 14:52+0000\n"
+"POT-Creation-Date: 2019-11-24 17:14+0000\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=utf-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 
-#: sweettooth/auth/forms.py:39 extensions-web-domain-django:1
+#: sweettooth/auth/forms.py:26 extensions-web-domain-django:1
+msgid "Username or email"
+msgstr ""
+
+#: sweettooth/auth/forms.py:46 extensions-web-domain-django:1
 msgid "Username"
 msgstr ""
 
-#: sweettooth/auth/forms.py:40 extensions-web-domain-django:1
+#: sweettooth/auth/forms.py:47 extensions-web-domain-django:1
 msgid "Required. 30 characters or fewer. Letters, digits and @/./+/-/_ only."
 msgstr ""
 
-#: sweettooth/auth/forms.py:41 extensions-web-domain-django:1
+#: sweettooth/auth/forms.py:48 extensions-web-domain-django:1
 msgid "This value may contain only letters, numbers and @/./+/-/_ characters."
 msgstr ""
 
-#: sweettooth/auth/forms.py:43 extensions-web-domain-django:1
+#: sweettooth/auth/forms.py:50 extensions-web-domain-django:1
 msgid "Email"
 msgstr ""
 
-#: sweettooth/auth/forms.py:44 extensions-web-domain-django:1
+#: sweettooth/auth/forms.py:51 extensions-web-domain-django:1
 msgid "Password"
 msgstr ""
 
-#: sweettooth/auth/forms.py:45 extensions-web-domain-django:1
+#: sweettooth/auth/forms.py:52 extensions-web-domain-django:1
 msgid "Password confirmation"
 msgstr ""
 
-#: sweettooth/auth/forms.py:46 extensions-web-domain-django:1
+#: sweettooth/auth/forms.py:53 extensions-web-domain-django:1
 msgid "Enter the same password as above, for verification."
 msgstr ""
 
@@ -54,13 +58,13 @@ msgstr ""
 msgid "Log in"
 msgstr ""
 
-#: sweettooth/auth/templates/registration/login.html:37
+#: sweettooth/auth/templates/registration/login.html:36
 #: sweettooth/auth/templates/registration/login_popup_form.html:17
 #: extensions-web-domain-django:1
 msgid "Don't have an account?"
 msgstr ""
 
-#: sweettooth/auth/templates/registration/login.html:38
+#: sweettooth/auth/templates/registration/login.html:37
 #: sweettooth/auth/templates/registration/login_popup_form.html:19
 #: extensions-web-domain-django:1
 msgid "Register"
diff --git a/sweettooth/auth/backends.py b/sweettooth/auth/backends.py
new file mode 100644
index 0000000..2486572
--- /dev/null
+++ b/sweettooth/auth/backends.py
@@ -0,0 +1,29 @@
+"""
+    GNOME Shell extensions repository
+    Copyright (C) 2019  Yuri Konotopov <ykonotopov gnome org>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+"""
+
+from django.contrib.auth.backends import ModelBackend, UserModel
+from django.contrib.auth.models import User
+
+class LoginEmailAuthentication(ModelBackend):
+    def authenticate(self, request, username=None, password=None, **kwargs):
+        user = super().authenticate(request, username=username, password=password, **kwargs)
+        if user is not None:
+            return user
+
+        if username is not None:
+            try:
+                user = UserModel.objects.get(email=username)
+            except UserModel.DoesNotExist:
+                # Run the default password hasher once to reduce the timing
+                # difference between an existing and a nonexistent user (#20760).
+                UserModel().set_password(password)
+            else:
+                if user.check_password(password) and self.user_can_authenticate(user):
+                    return user
diff --git a/sweettooth/auth/forms.py b/sweettooth/auth/forms.py
index a240654..902c396 100644
--- a/sweettooth/auth/forms.py
+++ b/sweettooth/auth/forms.py
@@ -20,6 +20,12 @@ class AutoFocusForm(object):
             self.fields[field].widget.attrs['autofocus'] = 'autofocus'
             break
 
+class LoginOrEmailAuthenticationForm(object):
+    def __init__(self, *a, **kw):
+        super().__init__(*a, **kw)
+        self.fields['username'].label = _('Username or email')
+
+
 class InlineForm(object):
     def __init__(self, *a, **kw):
         super().__init__(*a, **kw)
@@ -28,10 +34,11 @@ class InlineForm(object):
             field.widget.attrs['class'] = 'form-control'
 
 class InlineAuthenticationForm(PlainOutputForm, AutoFocusForm,
-                               InlineForm, auth_forms.AuthenticationForm):
+                               InlineForm, LoginOrEmailAuthenticationForm, auth_forms.AuthenticationForm):
     pass
 
-class AuthenticationForm(AutoFocusForm, auth_forms.AuthenticationForm):
+class AuthenticationForm(LoginOrEmailAuthenticationForm, AutoFocusForm,
+                        auth_forms.AuthenticationForm):
     pass
 
 class RegistrationForm(RegistrationFormUniqueEmail):
diff --git a/sweettooth/auth/tests.py b/sweettooth/auth/tests.py
index 627b71d..623d7f0 100644
--- a/sweettooth/auth/tests.py
+++ b/sweettooth/auth/tests.py
@@ -9,6 +9,11 @@ User = get_user_model()
 
 # registration/tests/test_forms.py
 class AuthTests(TestCase):
+    registration_data = {
+        User.USERNAME_FIELD: 'bob',
+        'email': 'bob example com',
+        'password': 'mysecretpassword'
+    }
     valid_data = {
         User.USERNAME_FIELD: 'alice',
         'email': 'alice example com',
@@ -16,15 +21,19 @@ class AuthTests(TestCase):
         'password2': 'swordfish',
     }
 
-    def test_email_uniqueness(self):
-        User.objects.create(
-            username='bob',
-            email=self.valid_data['email'],
-            password=self.valid_data['password1']
+    @classmethod
+    def setUp(cls):
+        User.objects.create_user(
+            username=cls.registration_data[User.USERNAME_FIELD],
+            email=cls.registration_data['email'],
+            password=cls.registration_data['password']
         )
 
+    def test_email_uniqueness(self):
+        data = self.valid_data.copy()
+        data.update(email = self.registration_data['email'])
         form = AutoFocusRegistrationForm(
-            data=self.valid_data.copy()
+            data=data
         )
         self.assertFalse(form.is_valid())
         self.assertEqual(
@@ -32,9 +41,24 @@ class AuthTests(TestCase):
             [text_type(validators.DUPLICATE_EMAIL)]
         )
 
-        data = self.valid_data.copy()
-        data.update(email='bob example com')
         form = AutoFocusRegistrationForm(
-            data=data
+            data=self.valid_data.copy()
         )
         self.assertTrue(form.is_valid())
+
+    def test_auth_username_email(self):
+        self.assertTrue(self.client.login(
+            username=self.registration_data[User.USERNAME_FIELD],
+            password=self.registration_data['password']))
+
+        self.assertTrue(self.client.login(
+            username=self.registration_data['email'],
+            password=self.registration_data['password']))
+
+        self.assertFalse(self.client.login(
+            username=self.registration_data[User.USERNAME_FIELD],
+            password=self.valid_data['password1']))
+
+        self.assertFalse(self.client.login(
+            username=self.registration_data['email'],
+            password=self.valid_data['password1']))
diff --git a/sweettooth/settings.py b/sweettooth/settings.py
index 08341a5..7bce914 100644
--- a/sweettooth/settings.py
+++ b/sweettooth/settings.py
@@ -67,6 +67,8 @@ MIDDLEWARE = (
     'django.contrib.messages.middleware.MessageMiddleware',
 )
 
+AUTHENTICATION_BACKENDS = ['sweettooth.auth.backends.LoginEmailAuthentication']
+
 SECURE_BROWSER_XSS_FILTER = True
 SECURE_CONTENT_TYPE_NOSNIFF = True
 X_FRAME_OPTIONS = 'DENY'


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]