From 934dbbaf418cc4ea75df0b2ad611c7f0a4f6f150 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20P=C3=A9ters?= Date: Fri, 3 Feb 2017 15:12:17 +0100 Subject: [PATCH] general: add support for substitution variables in URLs (#14622) --- combo/apps/momo/utils.py | 2 +- combo/apps/newsletters/models.py | 17 ++++++++++------- combo/data/models.py | 9 ++++++--- combo/public/views.py | 11 +++++++---- combo/utils.py | 11 +++++++++++ tests/test_public.py | 11 +++++++++++ tests/test_utils.py | 12 ++++++++++-- 7 files changed, 56 insertions(+), 17 deletions(-) diff --git a/combo/apps/momo/utils.py b/combo/apps/momo/utils.py index 7f53433..ce53ccc 100644 --- a/combo/apps/momo/utils.py +++ b/combo/apps/momo/utils.py @@ -87,7 +87,7 @@ def get_page_dict(request, page, manifest): if page.redirect_url: page_dict['external'] = True - page_dict['url'] = page.redirect_url + page_dict['url'] = page.get_redirect_url() if icon_cells: page_dict['icon'] = icon_cells[0].icon diff --git a/combo/apps/newsletters/models.py b/combo/apps/newsletters/models.py index 153c4b0..6e07119 100644 --- a/combo/apps/newsletters/models.py +++ b/combo/apps/newsletters/models.py @@ -29,7 +29,7 @@ from django.utils.http import urlencode from combo.data.models import CellBase from combo.data.library import register_cell_class -from combo.utils import sign_url +from combo.utils import sign_url, get_templated_url from .forms import NewslettersManageForm @@ -120,8 +120,9 @@ class NewslettersCell(CellBase): return filtered def get_newsletters(self, **kwargs): - endpoint = self.url + 'newsletters/' - url = get_signed_url(endpoint, self.url, **kwargs) + url = get_templated_url(self.url) + endpoint = url + 'newsletters/' + url = get_signed_url(endpoint, url, **kwargs) response = requests.get(url) if response.ok: json_response = response.json() @@ -129,8 +130,9 @@ class NewslettersCell(CellBase): return [] def get_subscriptions(self, **kwargs): - endpoint = self.url + 'subscriptions/' - url = get_signed_url(endpoint, self.url, **kwargs) + url = get_templated_url(self.url) + endpoint = url + 'subscriptions/' + url = get_signed_url(endpoint, url, **kwargs) response = requests.get(url) if response.ok: json_response = response.json() @@ -140,9 +142,10 @@ class NewslettersCell(CellBase): def set_subscriptions(self, subscriptions, **kwargs): logger = logging.getLogger(__name__) headers = {'Content-type': 'application/json', 'Accept': 'application/json'} + url = get_templated_url(self.url) try: - endpoint = self.url + 'subscriptions/' - url = get_signed_url(endpoint, self.url, **kwargs) + endpoint = url + 'subscriptions/' + url = get_signed_url(endpoint, url, **kwargs) response = requests.post(url, data=json.dumps(subscriptions), headers=headers) if not response.json()['data']: diff --git a/combo/data/models.py b/combo/data/models.py index fad304a..9dfb6ef 100644 --- a/combo/data/models.py +++ b/combo/data/models.py @@ -258,6 +258,9 @@ class Page(models.Model): ordered_pages = Page.get_as_reordered_flat_hierarchy(cls.objects.all()) return [x.get_serialized_page() for x in ordered_pages] + def get_redirect_url(self): + return utils.get_templated_url(self.redirect_url) + class CellMeta(MediaDefiningClass, ModelBase): pass @@ -605,7 +608,7 @@ class LinkCell(CellBase): context['url'] = self.link_page.get_online_url() context['title'] = self.title or self.link_page.title else: - context['url'] = self.url + context['url'] = utils.get_templated_url(self.url) context['title'] = self.title or self.url if self.anchor: context['url'] += '#' + self.anchor @@ -632,7 +635,7 @@ class FeedCell(CellBase): cache_key = hashlib.md5(self.url).hexdigest() feed_content = cache.get(cache_key) if not feed_content: - feed_response = requests.get(self.url) + feed_response = requests.get(utils.get_templated_url(self.url)) if feed_response.status_code == 200: feed_content = feed_response.content cache.set(cache_key, feed_content, 600) @@ -737,7 +740,7 @@ class ParametersCell(CellBase): if hasattr(self, '_form'): ctx['form'] = self._form ctx['title'] = self.title - ctx['url'] = self.url + ctx['url'] = utils.get_templated_url(self.url) return ctx def get_default_form_class(self): diff --git a/combo/public/views.py b/combo/public/views.py index 31446d4..672039f 100644 --- a/combo/public/views.py +++ b/combo/public/views.py @@ -44,6 +44,7 @@ else: get_idps = lambda: [] from combo.data.models import CellBase, Page, ParentContentCell, TextCell +from combo import utils def login(request, *args, **kwargs): @@ -147,15 +148,17 @@ def skeleton(request): # look in redirect pages after the best match for the source redirect_pages = Page.objects.exclude(redirect_url__isnull=True).exclude(redirect_url='') for page in redirect_pages: - if source.startswith(page.redirect_url): - if selected_page is None or len(page.redirect_url) > len(selected_page.redirect_url): + redirect_url = utils.get_templated_url(page.redirect_url) + if source.startswith(redirect_url): + if selected_page is None or len(redirect_url) > len(selected_page.get_redirect_url()): selected_page = page if selected_page is None: # if there was no page found, look for a domain match netloc = urlparse.urlparse(source).netloc for page in redirect_pages: - if urlparse.urlparse(page.redirect_url).netloc == netloc: + redirect_url = utils.get_templated_url(page.redirect_url) + if urlparse.urlparse(redirect_url).netloc == netloc: selected_page = page break @@ -289,7 +292,7 @@ def publish_page(request, page, status=200, template_name=None): raise PermissionDenied() if page.redirect_url: - return HttpResponseRedirect(page.redirect_url) + return HttpResponseRedirect(page.get_redirect_url()) cells = CellBase.get_cells(page_id=page.id) extend_with_parent_cells(cells) diff --git a/combo/utils.py b/combo/utils.py index 27ff3a2..cc4575c 100644 --- a/combo/utils.py +++ b/combo/utils.py @@ -19,6 +19,7 @@ import base64 import hmac import hashlib import binascii +import re from HTMLParser import HTMLParser import logging @@ -169,6 +170,16 @@ class Requests(RequestsSession): requests = Requests() + +def get_templated_url(url): + template_vars = settings.TEMPLATE_VARS + def repl(matchobj): + if matchobj.group(0)[1:-1] in template_vars: + return template_vars[matchobj.group(0)[1:-1]] + return matchobj.group(0) + return re.sub(r'(\[.*?\])', repl, url) + + # Simple signature scheme for query strings def sign_url(url, key, algo='sha256', timestamp=None, nonce=None): diff --git a/tests/test_public.py b/tests/test_public.py index 307e3fb..b23c6d8 100644 --- a/tests/test_public.py +++ b/tests/test_public.py @@ -4,6 +4,8 @@ import pytest import urllib from django.core.urlresolvers import reverse +from django.test import override_settings + from combo.wsgi import application from combo.data.models import Page, CellBase, TextCell, ParentContentCell, FeedCell @@ -75,6 +77,15 @@ def test_page_redirect(app): resp = app.get('/elsewhere/', status=302) assert resp.location == 'http://example.net' +def test_page_templated_redirect(app): + Page.objects.all().delete() + with override_settings(TEMPLATE_VARS={'test_url': 'http://example.net'}): + page = Page(title='Elsewhere', slug='elsewhere', template_name='standard', + redirect_url='[test_url]') + page.save() + resp = app.get('/elsewhere/', status=302) + assert resp.location == 'http://example.net' + def test_page_private_unlogged(app): Page.objects.all().delete() page = Page(title='Home', slug='index', template_name='standard', public=False) diff --git a/tests/test_utils.py b/tests/test_utils.py index aa9250e..ef23414 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,8 +1,16 @@ -from combo.utils import aes_hex_decrypt, aes_hex_encrypt +from combo.utils import aes_hex_decrypt, aes_hex_encrypt, get_templated_url from django.conf import settings +from django.test import override_settings def test_crypto_url(): invoice_id = '12-1234' key = settings.SECRET_KEY - assert aes_hex_decrypt(key, aes_hex_encrypt(key, invoice_id)) == invoice_id \ No newline at end of file + assert aes_hex_decrypt(key, aes_hex_encrypt(key, invoice_id)) == invoice_id + + +def test_templated_url(): + assert get_templated_url('[test_url]') == '[test_url]' + with override_settings(TEMPLATE_VARS={'test_url': 'http://www.example.net'}): + assert get_templated_url('[test_url]') == 'http://www.example.net' + assert get_templated_url('[test_url]/hello') == 'http://www.example.net/hello' -- 2.11.0