From 961b43a42ecf7587e37f4018ad40ab4af249c678 Mon Sep 17 00:00:00 2001 From: catborise Date: Wed, 21 Oct 2026 20:24:20 +0300 Subject: [PATCH] fix(accounts): a signed-in user is sent on from the login page The login page showed its sign-in card to a user who was already signed in, with the navbar squeezed beside it. Both login views, with and without OTP, now send that user to the page in next, or to the default page when next is missing or points elsewhere. Every permission check answers 403 instead of redirecting to the login, so this cannot loop. The page test renders the login page signed out. Co-Authored-By: Claude Opus 5.5 (1M context) --- accounts/tests/test_otp_login.py | 6 ++++++ accounts/tests/test_views.py | 8 ++++++++ accounts/urls.py | 1 + accounts/views.py | 2 ++ webvirtcloud/tests/test_pages.py | 5 +++++ 5 files changed, 22 insertions(+) diff --git a/accounts/tests/test_otp_login.py b/accounts/tests/test_otp_login.py index 98f3cdd3..c072350b 100644 --- a/accounts/tests/test_otp_login.py +++ b/accounts/tests/test_otp_login.py @@ -141,3 +141,9 @@ class OTPLoginHTTPTestCase(TestCase): device = TOTPDevice.objects.create(user=self.user, confirmed=True) response = self._post(otp_token=wrong_token(device)) self.assertEqual(response.status_code, 200) + + def test_a_signed_in_user_is_sent_on_from_the_login_page(self): + from django.urls import reverse + self.client.force_login(self.user) + response = self.client.get(f"{reverse('accounts:login')}?next={reverse('computes')}") + self.assertRedirects(response, reverse("computes"), fetch_redirect_response=False) diff --git a/accounts/tests/test_views.py b/accounts/tests/test_views.py index d79ed15c..0d9d6547 100644 --- a/accounts/tests/test_views.py +++ b/accounts/tests/test_views.py @@ -79,6 +79,14 @@ class AccountsTestCase(TestCase): response = client.post(reverse("accounts:logout")) self.assertRedirects(response, reverse("accounts:login")) + def test_a_signed_in_user_is_sent_on_from_the_login_page(self): + self.client.force_login(self.test_user) + login = reverse("accounts:login") + response = self.client.get(f"{login}?next={reverse('computes')}") + self.assertRedirects(response, reverse("computes"), fetch_redirect_response=False) + response = self.client.get(f"{login}?next=https://example.com/") + self.assertRedirects(response, settings.LOGIN_REDIRECT_URL, fetch_redirect_response=False) + def test_change_password(self): self.client.force_login(self.test_user) diff --git a/accounts/urls.py b/accounts/urls.py index 306741ac..90801c2e 100644 --- a/accounts/urls.py +++ b/accounts/urls.py @@ -45,6 +45,7 @@ if settings.OTP_ENABLED: LoginView.as_view( template_name="accounts/otp_login.html", authentication_form=SingleDeviceOTPForm, + redirect_authenticated_user=True, ), name="login", ), diff --git a/accounts/views.py b/accounts/views.py index 80a539ee..fcdccd36 100644 --- a/accounts/views.py +++ b/accounts/views.py @@ -29,6 +29,8 @@ from django.contrib.auth.views import LoginView from logs.views import addlogmsg class CustomLoginView(LoginView): + redirect_authenticated_user = True + def form_valid(self, form): username = form.cleaned_data['username'] addlogmsg(username, "-", "-", "Logged In") diff --git a/webvirtcloud/tests/test_pages.py b/webvirtcloud/tests/test_pages.py index b13979f6..a78db717 100644 --- a/webvirtcloud/tests/test_pages.py +++ b/webvirtcloud/tests/test_pages.py @@ -55,6 +55,11 @@ class PageTests(TestCase): self.assertEqual(response.status_code, 200) checked.append("network") yield "network", html.fromstring(response.content) + # a signed-in user is sent on from the login page + response = self.client_class().get(reverse("accounts:login")) + self.assertEqual(response.status_code, 200) + checked.append("accounts:login") + yield "accounts:login", html.fromstring(response.content) self.assertTrue({"index", "overview", "instances:instance", "nwfilter"} <= set(checked), checked) self.assertIn("instances:create_instance", checked)