From afc099848763cb17f9d3f143b2a238bafd181ef8 Mon Sep 17 00:00:00 2001 From: Sidney Richards Date: Thu, 17 Oct 2024 15:06:35 +0200 Subject: [PATCH] fixup! [#2816] Split user/password auth from token verification --- src/open_inwoner/accounts/backends.py | 67 ++++++++++--------- .../accounts/tests/test_backends.py | 4 +- 2 files changed, 37 insertions(+), 34 deletions(-) diff --git a/src/open_inwoner/accounts/backends.py b/src/open_inwoner/accounts/backends.py index e60b115f57..b979e1c6bc 100644 --- a/src/open_inwoner/accounts/backends.py +++ b/src/open_inwoner/accounts/backends.py @@ -27,29 +27,31 @@ class UserModelEmailBackend(ModelBackend): """ def authenticate(self, request, username=None, password=None): + if not username or not password: + return + User = get_user_model() - if username and password: - try: - user = User.objects.get( - email__iexact=username, - login_type=LoginTypeChoices.default, - ) - if check_password( - password, user.password - ) and self.user_can_authenticate(user): - return user - except User.MultipleObjectsReturned: - # Found multiple users with this email (shouldn't happen if we added checks) - # Run the default password hasher once to reduce the timing - # difference between an existing and a nonexistent user (#20760). - User().set_password(password) - return None - except User.DoesNotExist: - # No user was found, return None - triggers default login failed - # Run the default password hasher once to reduce the timing - # difference between an existing and a nonexistent user (#20760). - User().set_password(password) - return None + try: + user = User.objects.get( + email__iexact=username, + login_type=LoginTypeChoices.default, + ) + if check_password(password, user.password) and self.user_can_authenticate( + user + ): + return user + except User.MultipleObjectsReturned: + # Found multiple users with this email (shouldn't happen if we added checks) + # Run the default password hasher once to reduce the timing + # difference between an existing and a nonexistent user (#20760). + User().set_password(password) + return None + except User.DoesNotExist: + # No user was found, return None - triggers default login failed + # Run the default password hasher once to reduce the timing + # difference between an existing and a nonexistent user (#20760). + User().set_password(password) + return None class Verify2FATokenBackend(BaseBackend): @@ -58,17 +60,18 @@ class Verify2FATokenBackend(BaseBackend): """ def authenticate(self, request, *, user=None, token=None): - # 2FA with sms verification - if user and token: - accepted, drift = accept_totp( - key=user.seed, - response=token, - period=getattr(settings, "ACCOUNTS_USER_TOKEN_EXPIRE_TIME", 300), - ) - if not accepted: - return None + if not user or not token: + return - return user + accepted, drift = accept_totp( + key=user.seed, + response=token, + period=getattr(settings, "ACCOUNTS_USER_TOKEN_EXPIRE_TIME", 300), + ) + if not accepted: + return None + + return user class CustomAxesBackend(AxesBackend): diff --git a/src/open_inwoner/accounts/tests/test_backends.py b/src/open_inwoner/accounts/tests/test_backends.py index 2b3c5e8aad..46ccde4b4b 100644 --- a/src/open_inwoner/accounts/tests/test_backends.py +++ b/src/open_inwoner/accounts/tests/test_backends.py @@ -87,7 +87,7 @@ class UserModelEmailBackendTestCase(TestCase): def setUpTestData(cls): super().setUpTestData() - cls.password = "keepitsecert" + cls.password = "keepitsecret" cls.user = UserFactory( login_type=LoginTypeChoices.default, password=cls.password ) @@ -142,6 +142,7 @@ def test_missing_username_and_or_password_returns_none(self): "open_inwoner.accounts.backends.Verify2FATokenBackend", ] ) +@freeze_time("2023-05-22 12:05:01") class Verify2FATokenBackendTestCase(TestCase): @classmethod def setUpTestData(cls): @@ -151,7 +152,6 @@ def setUpTestData(cls): cls.expires_in = getattr(settings, "ACCOUNTS_USER_TOKEN_EXPIRE_TIME", 300) cls.make_token = lambda: totp(cls.user.seed, period=cls.expires_in) - @freeze_time("2023-05-22 12:05:01") def test_valid_token_and_user_returns_user(self): request = RequestFactory().get(reverse("verify_token"))