Opened 7 weeks ago

Closed 7 weeks ago

Last modified 7 weeks ago

#36541 closed Cleanup/optimization (worksforme)

Using the `string_if_invalid` template configuration breaks the password reset button in the `UserAdmin`

Reported by: Drew Winstel Owned by:
Component: contrib.auth Version: 5.2
Severity: Normal Keywords:
Cc: Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

Steps to replicate:

  1. Create any basic app (polls is fine) and add path("admin/", admin.site.urls) to your urlconf.
  2. Set this template configuration in your settings.py:
    TEMPLATES = [
        {
            "BACKEND": "django.template.backends.django.DjangoTemplates",
            "OPTIONS": {
                "string_if_invalid": "INVALID EXPRESSION: %s",
            },
        },
    ]
    
  3. Navigate to the user detail view in the admin for any user
  4. Observe that the Reset password button renders to HTML as <a class="button" href="INVALID EXPRESSION: password_url">Reset password</a> which returns a 404 if you click on the button

This is because the default template for the password reset button looks for the password_url template context, which isn't set at all by default. However, when you have string_if_invalid set, password_url resolves to the fallback string, preventing the default filter from returning the correct value.

There are two workarounds:

  1. Find a way to inject a password_url into your context that gets set in the context
  2. Override the read_only_password_hash.html template locally to hard-code the link to point to ../password/

Change History (2)

comment:1 by Natalia Bidart, 7 weeks ago

Component: Uncategorizedcontrib.auth
Resolution: worksforme
Status: newclosed
Type: UncategorizedCleanup/optimization

Hello Drew! Thank you for taking the time to create this ticket. I see your point and I agree that not having a password_url defined is an issue we could/should improve. But I have not been able to reproduce what you see. See my two attempts below:

  • I have defined a simple TEMPLATES settings like this:
    TEMPLATES = [
        {
            "BACKEND": "django.template.backends.django.DjangoTemplates",
            "OPTIONS": {
                "string_if_invalid": "INVALID EXPRESSION: %s",
                "loaders": [
                    "django.template.loaders.app_directories.Loader",
                ],
                "context_processors": [
                    "django.template.context_processors.request",
                    "django.contrib.auth.context_processors.auth",
                    "django.contrib.messages.context_processors.messages",
                ]
            },
        },
    ]
    

And while I do see the string_if_invalid used in my app templates, it's not used in the admin templates. I debugged for a while and I see that the engine being used to render the admin has a slightly different definition of the one used for my site templates. Could you please the exact setting definition that you are using?

  • I also wrote this test which is passing for me (i.e. no INVALID STRING is printed in the password URL):
    • tests/auth_tests/test_views.py

      diff --git a/tests/auth_tests/test_views.py b/tests/auth_tests/test_views.py
      index 0a4f7d28c8..ea1632d82d 100644
      a b class ChangelistTests(MessagesTestMixin, AuthViewsTestCase):  
      16171617        self.admin.refresh_from_db()
      16181618        self.assertIs(self.admin.has_usable_password(), False)
      16191619
       1620    @override_settings(TEMPLATES=[
       1621        {
       1622            "BACKEND": "django.template.backends.django.DjangoTemplates",
       1623            "OPTIONS": {
       1624                "string_if_invalid": "INVALID STRING: %s",
       1625                "loaders": [
       1626                    "django.template.loaders.app_directories.Loader",
       1627                ],
       1628            },
       1629        },
       1630    ])
       1631    def test_user_with_usable_password_change_password_string_if_invalid(self):
       1632        user_change_url = reverse(
       1633            "auth_test_admin:auth_user_change", args=(self.admin.pk,)
       1634        )
       1635        response = self.client.get(user_change_url)
       1636        # Test the link inside password field help_text.
       1637        expected = '<a role="button" class="button" href="../password/">Reset password</a>'
       1638        self.assertContains(response, expected, html=True)
       1639

I'll close as worksforme for now, but please reopen when you can provide further details or a way to reproduce. Thanks again!

comment:2 by Drew Winstel, 7 weeks ago

Thanks, Natalia! I'll do some more digging and see if I can get a full repro case up.

Note: See TracTickets for help on using tickets.
Back to Top