Fix: use PAPERLESS_URL if set for pw reset emails (#5902)

This commit is contained in:
shamoon 2024-02-26 13:41:25 -08:00 committed by GitHub
parent 86811d0733
commit 16f4552e0e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 136 additions and 43 deletions

View File

@ -253,7 +253,8 @@ permissions can be granted to limit access to certain parts of the UI (and corre
### Password reset
In order to enable the password reset feature you will need to setup an SMTP backend, see
[`PAPERLESS_EMAIL_HOST`](configuration.md#PAPERLESS_EMAIL_HOST)
[`PAPERLESS_EMAIL_HOST`](configuration.md#PAPERLESS_EMAIL_HOST). If your installation does not have
[`PAPERLESS_URL`](configuration.md#PAPERLESS_URL) set, the reset link included in emails will use the server host.
## Workflows

View File

@ -7,4 +7,5 @@ def settings(request):
or django_settings.EMAIL_HOST_USER != "",
"DISABLE_REGULAR_LOGIN": django_settings.DISABLE_REGULAR_LOGIN,
"ACCOUNT_ALLOW_SIGNUPS": django_settings.ACCOUNT_ALLOW_SIGNUPS,
"domain": getattr(django_settings, "PAPERLESS_URL", request.get_host()),
}

View File

@ -0,0 +1,7 @@
{% load i18n %}{% autoescape off %}{% blocktrans with site_name="Paperless-ngx" %}Hello from {{ site_name }}!{% endblocktrans %}
{% block content %}{% endblock content %}
{% blocktrans with site_name="Paperless-ngx" site_domain=settings.domain %}Thank you for using {{ site_name }}!
{{ site_domain }}{% endblocktrans %}
{% endautoescape %}

View File

@ -2,7 +2,7 @@ msgid ""
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-02-26 00:10-0800\n"
"POT-Creation-Date: 2024-02-26 13:34-0800\n"
"PO-Revision-Date: 2022-02-17 04:17\n"
"Last-Translator: \n"
"Language-Team: English\n"
@ -786,6 +786,18 @@ msgstr ""
msgid "Invalid variable detected."
msgstr ""
#: documents/templates/account/email/base_message.txt:1
#, python-format
msgid "Hello from %(site_name)s!"
msgstr ""
#: documents/templates/account/email/base_message.txt:5
#, python-format
msgid ""
"Thank you for using %(site_name)s!\n"
"%(site_domain)s"
msgstr ""
#: documents/templates/account/login.html:5
msgid "Paperless-ngx sign in"
msgstr ""
@ -1138,131 +1150,131 @@ msgstr ""
msgid "paperless application settings"
msgstr ""
#: paperless/settings.py:644
#: paperless/settings.py:656
msgid "English (US)"
msgstr ""
#: paperless/settings.py:645
#: paperless/settings.py:657
msgid "Arabic"
msgstr ""
#: paperless/settings.py:646
#: paperless/settings.py:658
msgid "Afrikaans"
msgstr ""
#: paperless/settings.py:647
#: paperless/settings.py:659
msgid "Belarusian"
msgstr ""
#: paperless/settings.py:648
#: paperless/settings.py:660
msgid "Bulgarian"
msgstr ""
#: paperless/settings.py:649
#: paperless/settings.py:661
msgid "Catalan"
msgstr ""
#: paperless/settings.py:650
#: paperless/settings.py:662
msgid "Czech"
msgstr ""
#: paperless/settings.py:651
#: paperless/settings.py:663
msgid "Danish"
msgstr ""
#: paperless/settings.py:652
#: paperless/settings.py:664
msgid "German"
msgstr ""
#: paperless/settings.py:653
#: paperless/settings.py:665
msgid "Greek"
msgstr ""
#: paperless/settings.py:654
#: paperless/settings.py:666
msgid "English (GB)"
msgstr ""
#: paperless/settings.py:655
#: paperless/settings.py:667
msgid "Spanish"
msgstr ""
#: paperless/settings.py:656
#: paperless/settings.py:668
msgid "Finnish"
msgstr ""
#: paperless/settings.py:657
#: paperless/settings.py:669
msgid "French"
msgstr ""
#: paperless/settings.py:658
#: paperless/settings.py:670
msgid "Hungarian"
msgstr ""
#: paperless/settings.py:659
#: paperless/settings.py:671
msgid "Italian"
msgstr ""
#: paperless/settings.py:660
#: paperless/settings.py:672
msgid "Japanese"
msgstr ""
#: paperless/settings.py:661
#: paperless/settings.py:673
msgid "Luxembourgish"
msgstr ""
#: paperless/settings.py:662
#: paperless/settings.py:674
msgid "Norwegian"
msgstr ""
#: paperless/settings.py:663
#: paperless/settings.py:675
msgid "Dutch"
msgstr ""
#: paperless/settings.py:664
#: paperless/settings.py:676
msgid "Polish"
msgstr ""
#: paperless/settings.py:665
#: paperless/settings.py:677
msgid "Portuguese (Brazil)"
msgstr ""
#: paperless/settings.py:666
#: paperless/settings.py:678
msgid "Portuguese"
msgstr ""
#: paperless/settings.py:667
#: paperless/settings.py:679
msgid "Romanian"
msgstr ""
#: paperless/settings.py:668
#: paperless/settings.py:680
msgid "Russian"
msgstr ""
#: paperless/settings.py:669
#: paperless/settings.py:681
msgid "Slovak"
msgstr ""
#: paperless/settings.py:670
#: paperless/settings.py:682
msgid "Slovenian"
msgstr ""
#: paperless/settings.py:671
#: paperless/settings.py:683
msgid "Serbian"
msgstr ""
#: paperless/settings.py:672
#: paperless/settings.py:684
msgid "Swedish"
msgstr ""
#: paperless/settings.py:673
#: paperless/settings.py:685
msgid "Turkish"
msgstr ""
#: paperless/settings.py:674
#: paperless/settings.py:686
msgid "Ukrainian"
msgstr ""
#: paperless/settings.py:675
#: paperless/settings.py:687
msgid "Chinese Simplified"
msgstr ""

View File

@ -1,3 +1,5 @@
from urllib.parse import quote
from allauth.account.adapter import DefaultAccountAdapter
from allauth.core import context
from allauth.socialaccount.adapter import DefaultSocialAccountAdapter
@ -45,6 +47,20 @@ class CustomAccountAdapter(DefaultAccountAdapter):
return url_has_allowed_host_and_scheme(url, allowed_hosts=allowed_hosts)
def get_reset_password_from_key_url(self, key):
"""
Return the URL to reset a password e.g. in reset email.
"""
if settings.PAPERLESS_URL is None:
return super().get_reset_password_from_key_url(key)
else:
path = reverse(
"account_reset_password_from_key",
kwargs={"uidb36": "UID", "key": "KEY"},
)
path = path.replace("UID-KEY", quote(key))
return settings.PAPERLESS_URL + path
class CustomSocialAccountAdapter(DefaultSocialAccountAdapter):
def is_open_for_signup(self, request, sociallogin):

View File

@ -437,6 +437,8 @@ SOCIALACCOUNT_PROVIDERS = json.loads(
os.getenv("PAPERLESS_SOCIALACCOUNT_PROVIDERS", "{}"),
)
ACCOUNT_EMAIL_SUBJECT_PREFIX = "[Paperless-ngx] "
DISABLE_REGULAR_LOGIN = __get_boolean("PAPERLESS_DISABLE_REGULAR_LOGIN")
AUTO_LOGIN_USERNAME = os.getenv("PAPERLESS_AUTO_LOGIN_USERNAME")
@ -498,18 +500,23 @@ if DEBUG:
CORS_ALLOWED_ORIGINS.append("http://localhost:4200")
ALLOWED_HOSTS = __get_list("PAPERLESS_ALLOWED_HOSTS", ["*"])
_paperless_url = os.getenv("PAPERLESS_URL")
if _paperless_url:
_paperless_uri = urlparse(_paperless_url)
CSRF_TRUSTED_ORIGINS.append(_paperless_url)
CORS_ALLOWED_ORIGINS.append(_paperless_url)
if ["*"] != ALLOWED_HOSTS:
# always allow localhost. Necessary e.g. for healthcheck in docker.
ALLOWED_HOSTS.append("localhost")
if _paperless_url:
ALLOWED_HOSTS.append(_paperless_uri.hostname)
def _parse_paperless_url():
global CSRF_TRUSTED_ORIGINS, CORS_ALLOWED_ORIGINS, ALLOWED_HOSTS
url = os.getenv("PAPERLESS_URL")
if url:
CSRF_TRUSTED_ORIGINS.append(url)
CORS_ALLOWED_ORIGINS.append(url)
ALLOWED_HOSTS.append(urlparse(url).hostname)
return url
PAPERLESS_URL = _parse_paperless_url()
# For use with trusted proxies
TRUSTED_PROXIES = __get_list("PAPERLESS_TRUSTED_PROXIES")
@ -1126,3 +1133,6 @@ DEFAULT_FROM_EMAIL: Final[str] = os.getenv("PAPERLESS_EMAIL_FROM", EMAIL_HOST_US
EMAIL_USE_TLS: Final[bool] = __get_boolean("PAPERLESS_EMAIL_USE_TLS")
EMAIL_USE_SSL: Final[bool] = __get_boolean("PAPERLESS_EMAIL_USE_SSL")
EMAIL_SUBJECT_PREFIX: Final[str] = "[Paperless-ngx] "
if DEBUG: # pragma: no cover
EMAIL_BACKEND = "django.core.mail.backends.filebased.EmailBackend"
EMAIL_FILE_PATH = BASE_DIR / "sent_emails"

View File

@ -61,6 +61,27 @@ class TestCustomAccountAdapter(TestCase):
with self.assertRaises(ValidationError):
adapter.pre_authenticate(request)
def test_get_reset_password_from_key_url(self):
request = HttpRequest()
request.get_host = mock.Mock(return_value="foo.org")
with context.request_context(request):
adapter = get_adapter()
# Test when PAPERLESS_URL is None
expected_url = f"https://foo.org{reverse('account_reset_password_from_key', kwargs={'uidb36': 'UID', 'key': 'KEY'})}"
self.assertEqual(
adapter.get_reset_password_from_key_url("UID-KEY"),
expected_url,
)
# Test when PAPERLESS_URL is not None
with override_settings(PAPERLESS_URL="https://bar.com"):
expected_url = f"https://bar.com{reverse('account_reset_password_from_key', kwargs={'uidb36': 'UID', 'key': 'KEY'})}"
self.assertEqual(
adapter.get_reset_password_from_key_url("UID-KEY"),
expected_url,
)
class TestCustomSocialAccountAdapter(TestCase):
def test_is_open_for_signup(self):

View File

@ -8,6 +8,7 @@ from celery.schedules import crontab
from paperless.settings import _parse_beat_schedule
from paperless.settings import _parse_db_settings
from paperless.settings import _parse_ignore_dates
from paperless.settings import _parse_paperless_url
from paperless.settings import _parse_redis_url
from paperless.settings import default_threads_per_worker
@ -349,3 +350,27 @@ class TestDBSettings(TestCase):
},
databases["sqlite"]["OPTIONS"],
)
class TestPaperlessURLSettings(TestCase):
def test_paperless_url(self):
"""
GIVEN:
- PAPERLESS_URL is set
WHEN:
- The URL is parsed
THEN:
- The URL is returned and present in related settings
"""
with mock.patch.dict(
os.environ,
{
"PAPERLESS_URL": "https://example.com",
},
):
url = _parse_paperless_url()
self.assertEqual("https://example.com", url)
from django.conf import settings
self.assertIn(url, settings.CSRF_TRUSTED_ORIGINS)
self.assertIn(url, settings.CORS_ALLOWED_ORIGINS)