From af7e8fc94929753281737d7cca5e107bfe509013 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C4=93teris=20Caune?= Date: Fri, 6 Aug 2021 13:54:12 +0300 Subject: [PATCH] Fix the login view to handle already authenticated users If an already authenticated user visits /accounts/login/, Healthchecks will now redirect them to their dashboard instead of showing the login form. --- hc/accounts/tests/test_login.py | 10 ++++++++++ hc/accounts/views.py | 7 +++++++ 2 files changed, 17 insertions(+) diff --git a/hc/accounts/tests/test_login.py b/hc/accounts/tests/test_login.py index fc966999..b7c27a35 100644 --- a/hc/accounts/tests/test_login.py +++ b/hc/accounts/tests/test_login.py @@ -11,6 +11,16 @@ class LoginTestCase(BaseTestCase): super().setUp() self.checks_url = f"/projects/{self.project.code}/checks/" + def test_it_shows_form(self): + r = self.client.get("/accounts/login/") + self.assertContains(r, "Email Me a Link") + + def test_it_redirects_authenticated_get(self): + self.client.login(username="alice@example.org", password="password") + + r = self.client.get("/accounts/login/") + self.assertRedirects(r, self.checks_url) + def test_it_sends_link(self): form = {"identity": "alice@example.org"} diff --git a/hc/accounts/views.py b/hc/accounts/views.py index e9a9b77d..7a7fba64 100644 --- a/hc/accounts/views.py +++ b/hc/accounts/views.py @@ -164,6 +164,9 @@ def login(request): response.set_cookie("auto-login", "1", max_age=300, httponly=True) return response + if request.user.is_authenticated: + return _redirect_after_login(request) + bad_link = request.session.pop("bad_link", None) ctx = { "page": "login", @@ -868,11 +871,15 @@ def login_totp(request): totp = pyotp.totp.TOTP(user.profile.totp) if request.method == "POST": + # To guard against brute-forcing TOTP codes, we allow + # 96 attempts per user per 24h. if not TokenBucket.authorize_totp_attempt(user): return render(request, "try_later.html") form = forms.TotpForm(totp, request.POST) if form.is_valid(): + # We blacklist an used TOTP code for 90 seconds, + # so an attacker cannot reuse a stolen code. if not TokenBucket.authorize_totp_code(user, form.cleaned_data["code"]): return render(request, "try_later.html")