Configure login password-reset link via PASSWORD_RESET_URL#18564
Configure login password-reset link via PASSWORD_RESET_URL#18564baltenaxis wants to merge 3 commits intoWeblateOrg:mainfrom
Conversation
for more information, see https://pre-commit.ci
There was a problem hiding this comment.
Pull request overview
Adds a new configurable setting to control where the “Forgot your password?” link on the login page points, primarily to support external identity providers (e.g., LDAP/SAML/OAuth) in deployments where Weblate’s internal password reset is not applicable.
Changes:
- Introduces
PASSWORD_RESET_URL(with backward-compat fallback toEXTERNAL_PASSWORD_RESET_URL) and wires it into the login view/template. - Adds Docker env var support via
WEBLATE_PASSWORD_RESET_URL(falling back toWEBLATE_EXTERNAL_PASSWORD_RESET_URL). - Documents the setting/env var and adds tests for the externally-configured reset link.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| weblate/templates/accounts/login.html | Uses reset_url when set; otherwise falls back to the internal password_reset route. |
| weblate/settings_example.py | Adds PASSWORD_RESET_URL = None to the example settings. |
| weblate/settings_docker.py | Reads PASSWORD_RESET_URL from WEBLATE_PASSWORD_RESET_URL with legacy env var fallback. |
| weblate/accounts/views.py | Adds reset_url/can_reset context logic to support external reset links. |
| weblate/accounts/tests/test_views.py | Adds tests around presence/absence of the reset link based on settings. |
| docs/admin/install/docker.rst | Documents WEBLATE_PASSWORD_RESET_URL. |
| docs/admin/config.rst | Documents the new PASSWORD_RESET_URL setting. |
| docs/admin/auth.rst | Updates LDAP example to show PASSWORD_RESET_URL usage. |
| @override_settings( | ||
| AUTHENTICATION_BACKENDS=( | ||
| "django.contrib.auth.backends.ModelBackend", | ||
| "weblate.accounts.auth.WeblateUserBackend", | ||
| ), | ||
| REGISTRATION_OPEN=False, | ||
| PASSWORD_RESET_URL="https://id.example.net/password-reset", | ||
| ) | ||
| def test_login_password_reset_url(self) -> None: | ||
| response = self.client.get(reverse("login")) | ||
| self.assertContains(response, 'href="https://id.example.net/password-reset"') | ||
|
|
||
| @override_settings( | ||
| AUTHENTICATION_BACKENDS=( | ||
| "django.contrib.auth.backends.ModelBackend", | ||
| "weblate.accounts.auth.WeblateUserBackend", | ||
| ), | ||
| REGISTRATION_OPEN=False, | ||
| PASSWORD_RESET_URL=None, | ||
| ) |
There was a problem hiding this comment.
The tests override AUTHENTICATION_BACKENDS using override_settings, but social_core caches loaded backends globally; other tests in the repo use social_core_override_settings to clear this cache when changing AUTHENTICATION_BACKENDS. These new tests can become order-dependent/flaky unless they also clear social_core.backends.utils.BACKENDSCACHE (e.g., switch to social_core_override_settings or reset the cache in setup/teardown).
| def test_login_password_reset_url(self) -> None: | ||
| response = self.client.get(reverse("login")) | ||
| self.assertContains(response, 'href="https://id.example.net/password-reset"') | ||
|
|
||
| @override_settings( | ||
| AUTHENTICATION_BACKENDS=( | ||
| "django.contrib.auth.backends.ModelBackend", | ||
| "weblate.accounts.auth.WeblateUserBackend", | ||
| ), | ||
| REGISTRATION_OPEN=False, | ||
| PASSWORD_RESET_URL=None, | ||
| ) | ||
| def test_login_without_configured_password_reset_url(self) -> None: | ||
| response = self.client.get(reverse("login")) | ||
| self.assertNotContains(response, reverse("password_reset")) | ||
|
|
There was a problem hiding this comment.
Current tests cover (a) external PASSWORD_RESET_URL displayed when e-mail auth is disabled and (b) no reset link when neither internal nor external reset is available. The PR description also promises a fallback to the internal password_reset when PASSWORD_RESET_URL is not set, but there is no test asserting that behavior when social_core.backends.email.EmailAuth is enabled. Please add a test that verifies the login page includes the internal reset URL when e-mail auth is available and PASSWORD_RESET_URL is unset.
| .. setting:: PASSWORD_RESET_URL | ||
|
|
||
| PASSWORD_RESET_URL | ||
| ------------------ | ||
|
|
||
| URL for password reset when authentication is handled by an external identity provider | ||
| such as LDAP, SAML, or OAuth. | ||
|
|
||
| When set, :guilabel:`Forgot your password?` on the sign-in page links to this URL | ||
| instead of Weblate's built-in password reset page. | ||
|
|
There was a problem hiding this comment.
The new PASSWORD_RESET_URL setting documentation is missing the usual .. versionadded:: directive used for other settings in this file. Also, docs/admin/config.rst appears to keep settings sections roughly ordered; consider placing this entry near the other PASSWORD_* settings to keep the reference easy to navigate.
| URL for password reset when authentication is handled by an external identity provider | ||
| such as LDAP, SAML, or OAuth. | ||
|
|
||
| When set, :guilabel:`Forgot your password?` on the sign-in page links to this URL | ||
| instead of Weblate's built-in password reset page. | ||
|
|
There was a problem hiding this comment.
This introduces user-visible behavior/a new setting; the repo’s changelog (docs/changes.rst) typically gets an entry for such changes. Please add a short note under the appropriate rubric so administrators notice PASSWORD_RESET_URL in release notes.
nijel
left a comment
There was a problem hiding this comment.
What is the reason for EXTERNAL_PASSWORD_RESET_URL? It seems just a duplicate for the same setting. Also please see the copilot feedback, I think all of that is valid.
| auth_backends = get_auth_keys() | ||
| reset_url = getattr(settings, "PASSWORD_RESET_URL", None) or getattr( | ||
| settings, "EXTERNAL_PASSWORD_RESET_URL", None | ||
| ) |
There was a problem hiding this comment.
Please include PASSWORD_RESET_URL in WeblateAccountsConf, so that it can be accessed directly from the settings.
| context["login_backends"] = [x for x in sorted(auth_backends) if x != "email"] | ||
| context["can_reset"] = self.has_email_auth | ||
| context["can_reset"] = self.has_email_auth or bool(reset_url) | ||
| context["reset_url"] = reset_url |
There was a problem hiding this comment.
This might be better to pass using the existing context processor in weblate/trans/context_processors.py.
Motivation
Fix issue #10101 by allowing LDAP/external-auth deployments to send users to the correct password reset page.
What this PR changes
PASSWORD_RESET_URLfor the login page reset link.EXTERNAL_PASSWORD_RESET_URL.password_resetwhen not set.WEBLATE_PASSWORD_RESET_URL), and LDAP example.Testing
weblate.accounts.tests.test_viewsfor reset-link behavior.