@@ -19,6 +19,7 @@ from django import forms
from django.contrib import messages
from django.contrib.auth import logout
from django.contrib.auth.decorators import login_required
+from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.hashers import make_password
from django.contrib.auth.models import Permission, User
from django.contrib.messages.views import SuccessMessageMixin
@@ -1001,7 +1002,7 @@ class HistoryListView(ListView):
return Revision.objects.all().order_by('-date_created')
-class EditProfileFormView(SuccessMessageMixin, UpdateView):
+class EditProfileFormView(LoginRequiredMixin, SuccessMessageMixin, UpdateView):
form_class = EditProfileForm
@method_decorator(never_cache)
@@ -65,3 +65,12 @@ class TestProfileEmailChange(TestCase):
self.user.refresh_from_db()
self.assertTrue(self.user.is_active)
self.assertEqual(len(mail.outbox), 0)
+
+ def test_profile_requires_login(self):
+ self.client.logout()
+ for method in ('get', 'post'):
+ response = getattr(self.client, method)(reverse('profile'))
+ self.assertEqual(response.status_code, 302, method)
+ self.assertIn(reverse('login'), response['Location'])
+ self.user.refresh_from_db()
+ self.assertEqual(self.user.email, 'old@example.com')
The profile page had no login check of its own, so a visitor who was not logged in got an Internal Server Error (500) for both GET and POST instead of the login page. Require login. AI-Generated: Claude Opus 5.5 Signed-off-by: Tim Orling <tim.orling@konsulko.com> --- layerindex/views.py | 3 ++- tests/test_profile_email.py | 9 +++++++++ 2 files changed, 11 insertions(+), 1 deletion(-)