From e1971467d7fcbd65c3974de7befc571d80a919fa Mon Sep 17 00:00:00 2001 From: Brock Date: Fri, 6 Dec 2024 16:52:26 -0500 Subject: [PATCH] Fixed #35959 -- Checked for change permission to show "password" field. Removed "password" from UserAdmin fieldset for users without change permission, omitting ReadOnlyPasswordHashField in UserChangeForm. Thanks to Sarah Boyce for help with implementation! Co-authored-by: Sarah Boyce --- django/contrib/auth/admin.py | 17 ++++++++++++++++- tests/auth_tests/test_views.py | 7 +++++-- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/django/contrib/auth/admin.py b/django/contrib/auth/admin.py index e977d3ded5..026e1536a7 100644 --- a/django/contrib/auth/admin.py +++ b/django/contrib/auth/admin.py @@ -1,3 +1,5 @@ +import copy + from django.conf import settings from django.contrib import admin, messages from django.contrib.admin.options import IS_POPUP_VAR @@ -82,10 +84,23 @@ class UserAdmin(admin.ModelAdmin): "user_permissions", ) + @staticmethod + def _remove_fields_from_fieldsets(fieldsets, fields): + fieldset_without_fields = [] + for fieldset_name, fieldset in copy.deepcopy(fieldsets): + fieldset["fields"] = [f for f in fieldset["fields"] if f not in fields] + fieldset_without_fields.append((fieldset_name, fieldset)) + return fieldset_without_fields + def get_fieldsets(self, request, obj=None): if not obj: return self.add_fieldsets - return super().get_fieldsets(request, obj) + fieldsets = super().get_fieldsets(request, obj) + if not self.has_change_permission(request, obj): + return self._remove_fields_from_fieldsets( + fieldsets=fieldsets, fields=["password"] + ) + return fieldsets def get_form(self, request, obj=None, **kwargs): """ diff --git a/tests/auth_tests/test_views.py b/tests/auth_tests/test_views.py index 98fdfe79b7..d1c5a0978d 100644 --- a/tests/auth_tests/test_views.py +++ b/tests/auth_tests/test_views.py @@ -1680,7 +1680,7 @@ class ChangelistTests(MessagesTestMixin, AuthViewsTestCase): (_request, user), _kwargs = has_change_permission.call_args self.assertEqual(user.pk, self.admin.pk) - def test_view_user_password_is_readonly(self): + def test_password_not_viewable_for_user_without_change_permission(self): u = User.objects.get(username="testclient") u.is_superuser = False u.save() @@ -1692,7 +1692,7 @@ class ChangelistTests(MessagesTestMixin, AuthViewsTestCase): algo, salt, hash_string = u.password.split("$") self.assertContains(response, '
testclient
') # ReadOnlyPasswordHashWidget is used to render the field. - self.assertContains( + self.assertNotContains( response, "algorithm: %s\n\n" "salt: %s********************\n\n" @@ -1704,6 +1704,9 @@ class ChangelistTests(MessagesTestMixin, AuthViewsTestCase): ), html=True, ) + self.assertNotContains( + response, 'Reset password' + ) # Value in POST data is ignored. data = self.get_user_data(u) data["password"] = "shouldnotchange"