mirror of
https://github.com/django/django.git
synced 2025-06-04 02:59:13 +00:00
Refs #35959 -- Added render_password_as_hash auth template tag for password rendering.
This commit is contained in:
parent
d469db978e
commit
8a0ad1ebe3
@ -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")
|
||||
)
|
||||
|
@ -1,8 +1,5 @@
|
||||
{% load auth %}
|
||||
<div{% include 'django/forms/widgets/attrs.html' %}>
|
||||
<p>
|
||||
{% for entry in summary %}
|
||||
<strong>{{ entry.label }}</strong>{% if entry.value %}: <bdi>{{ entry.value }}</bdi>{% endif %}
|
||||
{% endfor %}
|
||||
</p>
|
||||
{% render_password_as_hash widget.value %}
|
||||
<p><a role="button" class="button" href="{{ password_url|default:"../password/" }}">{{ button_label }}</a></p>
|
||||
</div>
|
||||
|
0
django/contrib/auth/templatetags/__init__.py
Normal file
0
django/contrib/auth/templatetags/__init__.py
Normal file
25
django/contrib/auth/templatetags/auth.py
Normal file
25
django/contrib/auth/templatetags/auth.py
Normal file
@ -0,0 +1,25 @@
|
||||
from django.contrib.auth.hashers import UNUSABLE_PASSWORD_PREFIX, identify_hasher
|
||||
from django.template import Library
|
||||
from django.utils.html import format_html, format_html_join
|
||||
from django.utils.translation import gettext
|
||||
|
||||
register = Library()
|
||||
|
||||
|
||||
@register.simple_tag
|
||||
def render_password_as_hash(value):
|
||||
if not value or value.startswith(UNUSABLE_PASSWORD_PREFIX):
|
||||
return format_html("<p><strong>{}</strong></p>", gettext("No password set."))
|
||||
try:
|
||||
hasher = identify_hasher(value)
|
||||
hashed_summary = hasher.safe_summary(value)
|
||||
except ValueError:
|
||||
return format_html(
|
||||
"<p><strong>{}</strong></p>",
|
||||
gettext("Invalid password format or unknown hashing algorithm."),
|
||||
)
|
||||
items = [(gettext(key), val) for key, val in hashed_summary.items()]
|
||||
return format_html(
|
||||
"<p>{}</p>",
|
||||
format_html_join(" ", "<strong>{}</strong>: <bdi>{}</bdi>", items),
|
||||
)
|
@ -1445,6 +1445,29 @@ class ReadOnlyPasswordHashTest(SimpleTestCase):
|
||||
"</div>",
|
||||
)
|
||||
|
||||
def test_render_no_password(self):
|
||||
widget = ReadOnlyPasswordHashWidget()
|
||||
self.assertHTMLEqual(
|
||||
widget.render("name", None, {}),
|
||||
"<div><p><strong>No password set.</p><p>"
|
||||
'<a role="button" class="button" href="../password/">Set password</a>'
|
||||
"</p></div>",
|
||||
)
|
||||
|
||||
@override_settings(
|
||||
PASSWORD_HASHERS=["django.contrib.auth.hashers.PBKDF2PasswordHasher"]
|
||||
)
|
||||
def test_render_invalid_password_format(self):
|
||||
widget = ReadOnlyPasswordHashWidget()
|
||||
value = "pbkdf2_sh"
|
||||
self.assertHTMLEqual(
|
||||
widget.render("name", value, {}),
|
||||
"<div><p>"
|
||||
"<strong>Invalid password format or unknown hashing algorithm.</strong>"
|
||||
'</p><p><a role="button" class="button" href="../password/">Reset password'
|
||||
"</a></p></div>",
|
||||
)
|
||||
|
||||
def test_readonly_field_has_changed(self):
|
||||
field = ReadOnlyPasswordHashField()
|
||||
self.assertIs(field.disabled, True)
|
||||
|
37
tests/auth_tests/test_templatetags.py
Normal file
37
tests/auth_tests/test_templatetags.py
Normal file
@ -0,0 +1,37 @@
|
||||
from django.contrib.auth.hashers import make_password
|
||||
from django.contrib.auth.templatetags.auth import render_password_as_hash
|
||||
from django.test import SimpleTestCase, override_settings
|
||||
|
||||
|
||||
class RenderPasswordAsHashTests(SimpleTestCase):
|
||||
@override_settings(
|
||||
PASSWORD_HASHERS=["django.contrib.auth.hashers.PBKDF2PasswordHasher"]
|
||||
)
|
||||
def test_valid_password(self):
|
||||
value = (
|
||||
"pbkdf2_sha256$100000$a6Pucb1qSFcD$WmCkn9Hqidj48NVe5x0FEM6A9YiOqQcl/83m2Z5u"
|
||||
"dm0="
|
||||
)
|
||||
hashed_html = (
|
||||
"<p><strong>algorithm</strong>: <bdi>pbkdf2_sha256</bdi> "
|
||||
"<strong>iterations</strong>: <bdi>100000</bdi> "
|
||||
"<strong>salt</strong>: <bdi>a6Pucb******</bdi> "
|
||||
"<strong>hash</strong>: <bdi>WmCkn9**************************************"
|
||||
"</bdi></p>"
|
||||
)
|
||||
self.assertEqual(render_password_as_hash(value), hashed_html)
|
||||
|
||||
def test_invalid_password(self):
|
||||
expected = (
|
||||
"<p><strong>Invalid password format or unknown hashing algorithm.</strong>"
|
||||
"</p>"
|
||||
)
|
||||
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 = "<p><strong>No password set.</strong></p>"
|
||||
for value in ["", None, make_password(None)]:
|
||||
with self.subTest(value=value):
|
||||
self.assertEqual(render_password_as_hash(value), expected)
|
Loading…
x
Reference in New Issue
Block a user