mirror of
https://github.com/django/django.git
synced 2025-04-25 01:34:38 +00:00
Fixed #35959 -- Displayed password reset button in admin only when user has sufficient permissions.
This change ensures that the "Reset password" button in the admin is shown only when the user has the necessary permission to perform a password change operation. It reuses the password hashing rendering logic in `display_for_field` to show the appropriate read-only widget for users with view-only access.
This commit is contained in:
parent
8a0ad1ebe3
commit
d755a98b84
@ -276,12 +276,6 @@ class AdminReadonlyField:
|
||||
except (AttributeError, ValueError, ObjectDoesNotExist):
|
||||
result_repr = self.empty_value_display
|
||||
else:
|
||||
if field in self.form.fields:
|
||||
widget = self.form[field].field.widget
|
||||
# This isn't elegant but suffices for contrib.auth's
|
||||
# ReadOnlyPasswordHashWidget.
|
||||
if getattr(widget, "read_only", False):
|
||||
return widget.render(field, value)
|
||||
if f is None:
|
||||
if getattr(attr, "boolean", False):
|
||||
result_repr = _boolean_icon(value)
|
||||
|
@ -5,6 +5,8 @@ from collections import defaultdict
|
||||
from functools import reduce
|
||||
from operator import or_
|
||||
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.auth.templatetags.auth import render_password_as_hash
|
||||
from django.core.exceptions import FieldDoesNotExist
|
||||
from django.core.validators import EMPTY_VALUES
|
||||
from django.db import models, router
|
||||
@ -429,7 +431,9 @@ def help_text_for_field(name, model):
|
||||
def display_for_field(value, field, empty_value_display, avoid_link=False):
|
||||
from django.contrib.admin.templatetags.admin_list import _boolean_icon
|
||||
|
||||
if getattr(field, "flatchoices", None):
|
||||
if field.name == "password" and field.model == get_user_model():
|
||||
return render_password_as_hash(value)
|
||||
elif getattr(field, "flatchoices", None):
|
||||
try:
|
||||
return dict(field.flatchoices).get(value, empty_value_display)
|
||||
except TypeError:
|
||||
|
@ -34,7 +34,6 @@ def _unicode_ci_compare(s1, s2):
|
||||
|
||||
class ReadOnlyPasswordHashWidget(forms.Widget):
|
||||
template_name = "auth/widgets/read_only_password_hash.html"
|
||||
read_only = True
|
||||
|
||||
def get_context(self, name, value, attrs):
|
||||
context = super().get_context(name, value, attrs)
|
||||
|
@ -17,9 +17,12 @@ from django.contrib.admin.utils import (
|
||||
lookup_field,
|
||||
quote,
|
||||
)
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib.auth.templatetags.auth import render_password_as_hash
|
||||
from django.core.validators import EMPTY_VALUES
|
||||
from django.db import DEFAULT_DB_ALIAS, models
|
||||
from django.test import SimpleTestCase, TestCase, override_settings
|
||||
from django.test.utils import isolate_apps
|
||||
from django.utils.formats import localize
|
||||
from django.utils.safestring import mark_safe
|
||||
|
||||
@ -238,6 +241,28 @@ class UtilsTests(SimpleTestCase):
|
||||
)
|
||||
self.assertEqual(display_value, "12,345")
|
||||
|
||||
@isolate_apps("admin_utils")
|
||||
def test_display_for_field_password_name_not_user_model(self):
|
||||
class PasswordModel(models.Model):
|
||||
password = models.CharField(max_length=200)
|
||||
|
||||
password_field = PasswordModel._meta.get_field("password")
|
||||
display_value = display_for_field("test", password_field, self.empty_value)
|
||||
self.assertEqual(display_value, "test")
|
||||
|
||||
def test_password_display_for_field_user_model(self):
|
||||
password_field = User._meta.get_field("password")
|
||||
for password in [
|
||||
"invalid",
|
||||
"md5$zjIiKM8EiyfXEGiexlQRw4$a59a82cf344546e7bc09cb5f2246370a",
|
||||
"!b7pk7RNudAXGTNLK6fW5YnBCLVE6UUmeoJJYQHaO",
|
||||
]:
|
||||
with self.subTest(password=password):
|
||||
display_value = display_for_field(
|
||||
password, password_field, self.empty_value
|
||||
)
|
||||
self.assertEqual(display_value, render_password_as_hash(password))
|
||||
|
||||
def test_list_display_for_value(self):
|
||||
display_value = display_for_value([1, 2, 3], self.empty_value)
|
||||
self.assertEqual(display_value, "1, 2, 3")
|
||||
|
@ -1703,7 +1703,7 @@ class ChangelistTests(MessagesTestMixin, AuthViewsTestCase):
|
||||
)
|
||||
algo, salt, hash_string = u.password.split("$")
|
||||
self.assertContains(response, '<div class="readonly">testclient</div>')
|
||||
# ReadOnlyPasswordHashWidget is used to render the field.
|
||||
# The password value is hashed.
|
||||
self.assertContains(
|
||||
response,
|
||||
"<strong>algorithm</strong>: <bdi>%s</bdi>\n\n"
|
||||
@ -1716,6 +1716,10 @@ class ChangelistTests(MessagesTestMixin, AuthViewsTestCase):
|
||||
),
|
||||
html=True,
|
||||
)
|
||||
self.assertNotContains(
|
||||
response,
|
||||
'<a role="button" class="button" href="../password/">Reset password</a>',
|
||||
)
|
||||
# Value in POST data is ignored.
|
||||
data = self.get_user_data(u)
|
||||
data["password"] = "shouldnotchange"
|
||||
|
Loading…
x
Reference in New Issue
Block a user