Repository: ietf-tools/datatracker

Issue:
password reset views conflate user and request.user
`type_defect`   |    by rjsparks@nostrum.com

___


As a result, the user is given the mistaken impression that they are logged in after after a reset. Confusion ensues.

The view should be modified to describe what account is being acted upon in the view body proper, and the menubar should reflect not being logged in throughout.

___
_Issue migrated from trac:3568 at 2022-03-04 09:30:51 +0000_
password reset views conflate user and request.user
`type_defect`   |    by rjsparks@nostrum.com

___


As a result, the user is given the mistaken impression that they are logged in after after a reset. Confusion ensues.

The view should be modified to describe what account is being acted upon in the view body proper, and the menubar should reflect not being logged in throughout.

___
_Issue migrated from trac:3568 at 2022-03-04 09:30:51 +0000_


Discussion:

Resolving patch:
diff --git a/ietf/ietfauth/views.py b/ietf/ietfauth/views.py
--- a/ietf/ietfauth/views.py
+++ b/ietf/ietfauth/views.py
@@ -55,7 +55,7 @@
 from django.core.exceptions import ObjectDoesNotExist, ValidationError
 from django.urls import reverse as urlreverse
 from django.utils.safestring import mark_safe
-from django.http import Http404, HttpResponseRedirect  #, HttpResponse, 
+from django.http import Http404, HttpResponseRedirect, HttpResponseForbidden
 from django.shortcuts import render, redirect, get_object_or_404
 from django.utils.encoding import force_bytes
 
@@ -303,7 +303,6 @@ def profile(request):
         person_form = get_person_form(instance=person)
 
     return render(request, 'registration/edit_profile.html', {
-        'user': request.user,
         'person': person,
         'person_form': person_form,
         'roles': roles,
@@ -462,7 +461,11 @@ def confirm_password_reset(request, auth):
         raise Http404("Invalid or expired auth")
 
     user = get_object_or_404(User, username=username, password__endswith=password, last_login=last_login)
-
+    if request.user.is_authenticated and request.user != user:
+        return HttpResponseForbidden(
+            f'This password reset link is not for the signed-in user. '
+            f'Please <a href="{urlreverse("django.contrib.auth.views.logout")}">sign out</a> and try again.'
+        )
     success = False
     if request.method == 'POST':
         form = PasswordForm(request.POST)
@@ -483,7 +486,7 @@ def confirm_password_reset(request, auth):
     hasher = getattr(hlib, hashername)
     return render(request, 'registration/change_password.html', {
         'form': form,
-        'user': user,
+        'update_user': user,
         'success': success,
         'hasher': hasher,
     })
@@ -647,7 +650,6 @@ def change_password(request):
     hasher = getattr(hlib, hashername)
     return render(request, 'registration/change_password.html', {
         'form': form,
-        'user': user,
         'success': success,
         'hasher': hasher,
     })
@@ -685,10 +687,7 @@ def change_username(request):
     else:
         form = ChangeUsernameForm(request.user)
 
-    return render(request, 'registration/change_username.html', {
-        'form': form,
-        'user': user,
-    })
+    return render(request, 'registration/change_username.html', {'form': form})
 
 
 


Test patch:
diff --git a/ietf/ietfauth/tests.py b/ietf/ietfauth/tests.py
--- a/ietf/ietfauth/tests.py
+++ b/ietf/ietfauth/tests.py
@@ -37,7 +37,7 @@
 from ietf.mailinglists.models import Subscribed
 from ietf.meeting.factories import MeetingFactory
 from ietf.nomcom.factories import NomComFactory
-from ietf.person.factories import PersonFactory, EmailFactory
+from ietf.person.factories import PersonFactory, EmailFactory, UserFactory
 from ietf.person.models import Person, Email, PersonalApiKey
 from ietf.review.factories import ReviewRequestFactory, ReviewAssignmentFactory
 from ietf.review.models import ReviewWish, UnavailablePeriod
@@ -433,10 +433,20 @@ def test_reset_password(self):
         self.assertEqual(r.status_code, 200)
         self.assertEqual(len(outbox), 1)
 
-        # go to change password page
+        # goto change password page, logged in as someone else
         confirm_url = self.extract_confirm_url(outbox[-1])
+        other_user = UserFactory()
+        self.client.login(username=other_user.username, password=other_user.username + '+password')
+        r = self.client.get(confirm_url)
+        self.assertEqual(r.status_code, 403)
+
+        # sign out and go back to change password page
+        self.client.logout()
         r = self.client.get(confirm_url)
         self.assertEqual(r.status_code, 200)
+        q = PyQuery(r.content)
+        self.assertNotIn(user.username, q('.nav').text(),
+                         'user should not appear signed in while resetting password')
 
         # password mismatch
         r = self.client.post(confirm_url, { 'password': 'secret', 'password_confirmation': 'nosecret' })