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"