From 5e051bfc30a9333c9978517dc1112102b0422f66 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?P=C4=93teris=20Caune?= <cuu508@gmail.com>
Date: Tue, 20 Aug 2024 10:57:36 +0300
Subject: [PATCH] Fix AJAX views to better handle user logging out

Rather than redirecting to login page, return HTTP 403 Forbidden
---
 CHANGELOG.md                         |  1 +
 hc/front/tests/test_log_events.py    |  4 ++++
 hc/front/tests/test_status.py        |  4 ++++
 hc/front/tests/test_status_single.py |  4 ++++
 hc/front/views.py                    | 18 ++++++++++++------
 static/js/log.js                     |  3 +++
 6 files changed, 28 insertions(+), 6 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 49444f69..48486736 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -15,6 +15,7 @@ All notable changes to this project will be documented in this file.
 
 ### Bug Fixes
 - Fix Check.ping() to lock the check before updating (#1023)
+- Fix AJAX views to better handle user logging out
 
 
 ## v3.4 - 2024-06-20
diff --git a/hc/front/tests/test_log_events.py b/hc/front/tests/test_log_events.py
index eab3d3aa..69fdee7e 100644
--- a/hc/front/tests/test_log_events.py
+++ b/hc/front/tests/test_log_events.py
@@ -57,6 +57,10 @@ class LogTestCase(BaseTestCase):
         self.assertContains(r, "Sent email to alice@example.org")
         self.assertContains(r, "new ➔ down")
 
+    def test_it_returns_403_for_anon_requests(self) -> None:
+        r = self.client.get(self.url())
+        self.assertEqual(r.status_code, 403)
+
     def test_team_access_works(self) -> None:
         # Logging in as bob, not alice. Bob has team access so this
         # should work.
diff --git a/hc/front/tests/test_status.py b/hc/front/tests/test_status.py
index a48e3e89..68545a11 100644
--- a/hc/front/tests/test_status.py
+++ b/hc/front/tests/test_status.py
@@ -26,6 +26,10 @@ class StatusTestCase(BaseTestCase):
         self.assertEqual(detail["status"], "new")
         self.assertIn("Never", detail["last_ping"])
 
+    def test_it_returns_403_for_anon_requests(self) -> None:
+        r = self.client.get(self.url)
+        self.assertEqual(r.status_code, 403)
+
     def test_it_allows_cross_team_access(self) -> None:
         self.client.login(username="bob@example.org", password="password")
         r = self.client.get(self.url)
diff --git a/hc/front/tests/test_status_single.py b/hc/front/tests/test_status_single.py
index 0a16381c..d39b4992 100644
--- a/hc/front/tests/test_status_single.py
+++ b/hc/front/tests/test_status_single.py
@@ -21,6 +21,10 @@ class StatusSingleTestCase(BaseTestCase):
         self.assertTrue("never received a ping" in doc["status_text"])
         self.assertTrue("not received any pings yet" in doc["events"])
 
+    def test_it_returns_403_for_anon_requests(self) -> None:
+        r = self.client.get(self.url)
+        self.assertEqual(r.status_code, 403)
+
     def test_it_returns_events(self) -> None:
         p = Ping.objects.create(owner=self.check, ua="test-user-agent", n=1)
         self.check.status = "up"
diff --git a/hc/front/views.py b/hc/front/views.py
index e529ec11..512ce8d1 100644
--- a/hc/front/views.py
+++ b/hc/front/views.py
@@ -303,8 +303,10 @@ def checks(request: AuthenticatedHttpRequest, code: UUID) -> HttpResponse:
     return render(request, "front/checks.html", ctx)
 
 
-@login_required
-def status(request: AuthenticatedHttpRequest, code: UUID) -> HttpResponse:
+def status(request: HttpRequest, code: UUID) -> HttpResponse:
+    if not request.user.is_authenticated:
+        return HttpResponseForbidden()
+
     project, rw = _get_project_for_user(request, code)
     checks = list(Check.objects.filter(project=project))
 
@@ -1085,8 +1087,10 @@ def copy(request: AuthenticatedHttpRequest, code: UUID) -> HttpResponse:
     return redirect(url + "?copied")
 
 
-@login_required
-def status_single(request: AuthenticatedHttpRequest, code: UUID) -> HttpResponse:
+def status_single(request: HttpRequest, code: UUID) -> HttpResponse:
+    if not request.user.is_authenticated:
+        return HttpResponseForbidden()
+
     check, rw = _get_check_for_user(request, code, preload_owner_profile=True)
 
     status = check.get_status()
@@ -2779,8 +2783,10 @@ def verify_signal_number(request: AuthenticatedHttpRequest) -> HttpResponse:
     return render_result(None)
 
 
-@login_required
-def log_events(request: AuthenticatedHttpRequest, code: UUID) -> HttpResponse:
+def log_events(request: HttpRequest, code: UUID) -> HttpResponse:
+    if not request.user.is_authenticated:
+        return HttpResponseForbidden()
+
     check, rw = _get_check_for_user(request, code, preload_owner_profile=True)
     form = forms.LogFiltersForm(request.GET)
     if not form.is_valid():
diff --git a/static/js/log.js b/static/js/log.js
index 4a36ed43..b579fa1b 100644
--- a/static/js/log.js
+++ b/static/js/log.js
@@ -124,6 +124,9 @@ $(function () {
                 switchDateFormat(dateFormat, tbody.querySelectorAll("tr"));
                 document.getElementById("log").prepend(tbody);
                 updateNumHits();
+            },
+            error: function(data, textStatus, xhr) {
+                activeRequest = null;
             }
         });
     }