diff --git a/django/contrib/auth/forms.py b/django/contrib/auth/forms.py index cd02887027..7b0b3833b8 100644 --- a/django/contrib/auth/forms.py +++ b/django/contrib/auth/forms.py @@ -3,7 +3,7 @@ import unicodedata from django import forms from django.contrib.auth import authenticate, get_user_model, password_validation -from django.contrib.auth.hashers import UNUSABLE_PASSWORD_PREFIX, identify_hasher +from django.contrib.auth.hashers import UNUSABLE_PASSWORD_PREFIX from django.contrib.auth.models import User from django.contrib.auth.tokens import default_token_generator from django.contrib.sites.shortcuts import get_current_site @@ -13,7 +13,6 @@ from django.template import loader from django.utils.encoding import force_bytes from django.utils.http import urlsafe_base64_encode from django.utils.text import capfirst -from django.utils.translation import gettext from django.utils.translation import gettext_lazy as _ from django.views.decorators.debug import sensitive_variables @@ -40,24 +39,6 @@ class ReadOnlyPasswordHashWidget(forms.Widget): def get_context(self, name, value, attrs): context = super().get_context(name, value, attrs) usable_password = value and not value.startswith(UNUSABLE_PASSWORD_PREFIX) - summary = [] - if usable_password: - try: - hasher = identify_hasher(value) - except ValueError: - summary.append( - { - "label": gettext( - "Invalid password format or unknown hashing algorithm." - ) - } - ) - else: - for key, value_ in hasher.safe_summary(value).items(): - summary.append({"label": gettext(key), "value": value_}) - else: - summary.append({"label": gettext("No password set.")}) - context["summary"] = summary context["button_label"] = ( _("Reset password") if usable_password else _("Set password") ) diff --git a/django/contrib/auth/templates/auth/widgets/read_only_password_hash.html b/django/contrib/auth/templates/auth/widgets/read_only_password_hash.html index b48204b7eb..102ceb30eb 100644 --- a/django/contrib/auth/templates/auth/widgets/read_only_password_hash.html +++ b/django/contrib/auth/templates/auth/widgets/read_only_password_hash.html @@ -1,8 +1,5 @@ +{% load auth %}
- {% for entry in summary %} - {{ entry.label }}{% if entry.value %}: {{ entry.value }}{% endif %} - {% endfor %} -
+ {% render_password_as_hash widget.value %}{}
", gettext("No password set.")) + try: + hasher = identify_hasher(value) + hashed_summary = hasher.safe_summary(value) + except ValueError: + return format_html( + "{}
", + gettext("Invalid password format or unknown hashing algorithm."), + ) + items = [(gettext(key), val) for key, val in hashed_summary.items()] + return format_html( + "{}
", + format_html_join(" ", "{}: {}", items), + ) diff --git a/tests/auth_tests/test_forms.py b/tests/auth_tests/test_forms.py index 0f8b48286a..df91f100f5 100644 --- a/tests/auth_tests/test_forms.py +++ b/tests/auth_tests/test_forms.py @@ -1445,6 +1445,29 @@ class ReadOnlyPasswordHashTest(SimpleTestCase): "", ) + def test_render_no_password(self): + widget = ReadOnlyPasswordHashWidget() + self.assertHTMLEqual( + widget.render("name", None, {}), + "No password set.
" + 'Set password' + "
" + "Invalid password format or unknown hashing algorithm." + '
algorithm: pbkdf2_sha256 " + "iterations: 100000 " + "salt: a6Pucb****** " + "hash: WmCkn9**************************************" + "
" + ) + self.assertEqual(render_password_as_hash(value), hashed_html) + + def test_invalid_password(self): + expected = ( + "Invalid password format or unknown hashing algorithm." + "
" + ) + for value in ["pbkdf2_sh", "md5$password", "invalid", "testhash$password"]: + with self.subTest(value=value): + self.assertEqual(render_password_as_hash(value), expected) + + def test_no_password(self): + expected = "No password set.
" + for value in ["", None, make_password(None)]: + with self.subTest(value=value): + self.assertEqual(render_password_as_hash(value), expected)