From 9cdff1b51c35154864873ee6268dde3987d926f2 Mon Sep 17 00:00:00 2001 From: Helge Jung <hej@c3pb.de> Date: Sat, 21 Dec 2024 00:38:49 +0100 Subject: [PATCH] extend core.utils.resolve_internal_url() to allow any HTTP url This is done to allow internal URLs in RoomLinks without having to handle non-internal URLs in a different way. --- src/core/markdown.py | 2 +- src/core/models/rooms.py | 8 ++------ src/core/tests/utils.py | 12 ++++++++++++ src/core/utils.py | 8 ++++++-- 4 files changed, 21 insertions(+), 9 deletions(-) diff --git a/src/core/markdown.py b/src/core/markdown.py index 06e3365b4..b6011d3c2 100644 --- a/src/core/markdown.py +++ b/src/core/markdown.py @@ -134,7 +134,7 @@ class MyHtmlRenderer(HTMLRenderer): from .utils import resolve_internal_url # attempt resolving an internal URL - if resolved_internal_url := resolve_internal_url(url, fallback_as_is=False): + if resolved_internal_url := resolve_internal_url(url, accept_http_https=False, fallback_as_is=False): url = resolved_internal_url # derive external link (i.e. apply dereferer), if its not an internal or trusted location diff --git a/src/core/models/rooms.py b/src/core/models/rooms.py index 21e0c1ee2..9a4c275f2 100644 --- a/src/core/models/rooms.py +++ b/src/core/models/rooms.py @@ -8,7 +8,6 @@ from django.conf import settings from django.contrib.contenttypes.fields import GenericRelation from django.contrib.postgres.fields import DateTimeRangeField from django.core.exceptions import ValidationError -from django.core.validators import URLValidator from django.db import models from django.db.models import Q, QuerySet from django.utils.text import slugify @@ -21,7 +20,7 @@ from core.models.base_managers import ConferenceManagerMixin from core.models.conference import Conference, ConferenceMember from core.models.shared import BackendMixin from core.models.tags import TagItem -from core.utils import str2bool +from core.utils import resolve_internal_url, str2bool from core.validators import FileSizeValidator, ImageDimensionValidator @@ -483,10 +482,7 @@ class RoomLink(models.Model): def clean(self): if self.link_type in RoomLink.URL_LINKTYPES: - validator = URLValidator() - try: - validator(self.link) - except ValidationError: + if not resolve_internal_url(self.link, fallback_as_is=False): raise ValidationError({'link': _('RoomLink__link__must_be_url')}) def __str__(self): diff --git a/src/core/tests/utils.py b/src/core/tests/utils.py index b4d4ca33b..489713310 100644 --- a/src/core/tests/utils.py +++ b/src/core/tests/utils.py @@ -90,6 +90,18 @@ class InternalUrlTests(TestCase): for check in checks: self.assertEqual(check[1], resolve_internal_url(check[0])) + # combinations of accept_http_https and fallback_as_is with a https URL + self.assertEqual('https://ccc.de/', resolve_internal_url('https://ccc.de/', accept_http_https=True, fallback_as_is=True)) + self.assertEqual('https://ccc.de/', resolve_internal_url('https://ccc.de/', accept_http_https=True, fallback_as_is=False)) + self.assertEqual('https://ccc.de/', resolve_internal_url('https://ccc.de/', accept_http_https=False, fallback_as_is=True)) + self.assertIsNone(resolve_internal_url('https://ccc.de/', accept_http_https=False, fallback_as_is=False)) + + # combinations of accept_http_https and fallback_as_is with an invalid URL + self.assertEqual('foo', resolve_internal_url('foo', accept_http_https=True, fallback_as_is=True)) + self.assertIsNone(resolve_internal_url('foo', accept_http_https=True, fallback_as_is=False)) + self.assertEqual('foo', resolve_internal_url('foo', accept_http_https=False, fallback_as_is=True)) + self.assertIsNone(resolve_internal_url('foo', accept_http_https=False, fallback_as_is=False)) + @override_settings(ADDITIONAL_LINK_PROTOCOLS={'c3nav': 'https://test.c3nav.de/l/VALUE', 'ccc': 'https://ccc.de/?goto=VALUE'}) def test_additional_link_protocols(self): checks = [ diff --git a/src/core/utils.py b/src/core/utils.py index c445d70d3..cd3c151c2 100644 --- a/src/core/utils.py +++ b/src/core/utils.py @@ -156,7 +156,7 @@ def mask_url(url): return urlunparse(masked) -def resolve_internal_url(url: str, fallback_as_is: bool = True) -> str | None: +def resolve_internal_url(url: str, accept_http_https: bool = True, fallback_as_is: bool = True) -> str | None: """ Resolves special URLs like - conference://url_resolver_name @@ -164,7 +164,8 @@ def resolve_internal_url(url: str, fallback_as_is: bool = True) -> str | None: - event://slug - wiki://slug - Regular URLs (i.e. https://...) are returned as-is by default but may be resolved as None (i.e. no match). + Regular URLs (i.e. https://...) are accepted by default, too. + Everything else (unparseable, unknown protocl) are returned as-is by default but may be resolved as None (i.e. no match). """ try: protocol, rhs = url.split('://', maxsplit=1) @@ -175,6 +176,9 @@ def resolve_internal_url(url: str, fallback_as_is: bool = True) -> str | None: remainder = rhs_splitted[0] query_string = '' if len(rhs_splitted) == 1 else rhs_splitted[1] + if accept_http_https and protocol in ['http', 'https']: + return url + try: from core.templatetags.hub_absolute import hub_absolute # pylint: disable=import-outside-toplevel -- GitLab