From 352ded6c40b97d5217bb1c585fe358a918942d6f Mon Sep 17 00:00:00 2001 From: Jamie Fox <134952460+FoxJamie16@users.noreply.github.com> Date: Tue, 12 Nov 2024 13:36:13 +0000 Subject: [PATCH 1/9] retrieve csrf token in the same way as inline feedback --- domestic/static/javascript/hcsat-feedback-form.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/domestic/static/javascript/hcsat-feedback-form.js b/domestic/static/javascript/hcsat-feedback-form.js index f23e3480af..aedbab4b8f 100644 --- a/domestic/static/javascript/hcsat-feedback-form.js +++ b/domestic/static/javascript/hcsat-feedback-form.js @@ -31,10 +31,12 @@ class CsatFormHandler { this.resetForm(); } try { + const csrfToken = document.querySelector('[name=csrfmiddlewaretoken]').value + console.log('csrfToken:',csrfToken) const response = await fetch(`${url}?js_enabled=True`, { method: 'POST', headers: { - 'X-CSRFToken': formData.get('csrfmiddlewaretoken'), + 'X-CSRFToken': csrfToken, 'Accept': 'application/json', 'X-Requested-With': 'XMLHttpRequest', }, From 50aec6220050c40a11efd70443d77cbe230fd71c Mon Sep 17 00:00:00 2001 From: Jamie Fox <134952460+FoxJamie16@users.noreply.github.com> Date: Tue, 12 Nov 2024 14:37:29 +0000 Subject: [PATCH 2/9] cache_control no cache added to post --- domestic/static/javascript/hcsat-feedback-form.js | 1 + 1 file changed, 1 insertion(+) diff --git a/domestic/static/javascript/hcsat-feedback-form.js b/domestic/static/javascript/hcsat-feedback-form.js index aedbab4b8f..2395c2629d 100644 --- a/domestic/static/javascript/hcsat-feedback-form.js +++ b/domestic/static/javascript/hcsat-feedback-form.js @@ -36,6 +36,7 @@ class CsatFormHandler { const response = await fetch(`${url}?js_enabled=True`, { method: 'POST', headers: { + 'cache_control': 'no-cache', 'X-CSRFToken': csrfToken, 'Accept': 'application/json', 'X-Requested-With': 'XMLHttpRequest', From 27ae90ec42f97de181571b2834de96b807d278ba Mon Sep 17 00:00:00 2001 From: Jamie Fox <134952460+FoxJamie16@users.noreply.github.com> Date: Tue, 12 Nov 2024 14:42:19 +0000 Subject: [PATCH 3/9] revert last change to confirm if wagtail cache is causing error --- domestic/static/javascript/hcsat-feedback-form.js | 1 - 1 file changed, 1 deletion(-) diff --git a/domestic/static/javascript/hcsat-feedback-form.js b/domestic/static/javascript/hcsat-feedback-form.js index 2395c2629d..aedbab4b8f 100644 --- a/domestic/static/javascript/hcsat-feedback-form.js +++ b/domestic/static/javascript/hcsat-feedback-form.js @@ -36,7 +36,6 @@ class CsatFormHandler { const response = await fetch(`${url}?js_enabled=True`, { method: 'POST', headers: { - 'cache_control': 'no-cache', 'X-CSRFToken': csrfToken, 'Accept': 'application/json', 'X-Requested-With': 'XMLHttpRequest', From fe74957b96260c9491aa0cbb347bd745fbf4c9fd Mon Sep 17 00:00:00 2001 From: Jamie Fox <134952460+FoxJamie16@users.noreply.github.com> Date: Tue, 12 Nov 2024 15:06:09 +0000 Subject: [PATCH 4/9] wagtail cache is the issue, re-adding cache-control to post --- domestic/static/javascript/hcsat-feedback-form.js | 1 + 1 file changed, 1 insertion(+) diff --git a/domestic/static/javascript/hcsat-feedback-form.js b/domestic/static/javascript/hcsat-feedback-form.js index aedbab4b8f..2395c2629d 100644 --- a/domestic/static/javascript/hcsat-feedback-form.js +++ b/domestic/static/javascript/hcsat-feedback-form.js @@ -36,6 +36,7 @@ class CsatFormHandler { const response = await fetch(`${url}?js_enabled=True`, { method: 'POST', headers: { + 'cache_control': 'no-cache', 'X-CSRFToken': csrfToken, 'Accept': 'application/json', 'X-Requested-With': 'XMLHttpRequest', From 43f19dadc64b832f53c13e7f37e9faa4019c5b13 Mon Sep 17 00:00:00 2001 From: Jamie Fox <134952460+FoxJamie16@users.noreply.github.com> Date: Tue, 12 Nov 2024 15:43:48 +0000 Subject: [PATCH 5/9] no_cache added to learn article page view --- core/models.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/core/models.py b/core/models.py index 5ea88182cd..a43b93f9e4 100644 --- a/core/models.py +++ b/core/models.py @@ -764,7 +764,12 @@ def serve(self, request): return self._redirect_to_parent_module() -class DetailPage(settings.FEATURE_DEA_V2 and CMSGenericPageAnonymous or CMSGenericPage, mixins.HCSATMixin): +from wagtailcache.cache import WagtailCacheMixin + + +class DetailPage( + settings.FEATURE_DEA_V2 and CMSGenericPageAnonymous or CMSGenericPage, mixins.HCSATMixin, WagtailCacheMixin +): estimated_read_duration = models.DurationField(null=True, blank=True) parent_page_types = [ 'core.CuratedListPage', # TEMPORARY: remove after topics refactor migration has run @@ -778,6 +783,9 @@ class Meta: verbose_name = 'Detail page' verbose_name_plural = 'Detail pages' + def cache_control(self): + return 'no-cache' + ################ # Content fields ################ From 22f1fb02ea6c3c4a0add4f9bf195fcf0f9ec9c9e Mon Sep 17 00:00:00 2001 From: Jamie Fox <134952460+FoxJamie16@users.noreply.github.com> Date: Wed, 13 Nov 2024 12:09:05 +0000 Subject: [PATCH 6/9] added clear_cache wagtail tag for hcsat template fragment in learn article page --- core/templatetags/clear_cache.py | 50 ++++++++++++++++++++++++++ learn/templates/learn/detail_page.html | 5 +++ 2 files changed, 55 insertions(+) create mode 100644 core/templatetags/clear_cache.py diff --git a/core/templatetags/clear_cache.py b/core/templatetags/clear_cache.py new file mode 100644 index 0000000000..705ad57e1d --- /dev/null +++ b/core/templatetags/clear_cache.py @@ -0,0 +1,50 @@ +from django import template +from django.core.cache import cache +from urllib.parse import quote +from hashlib import md5 + +register = template.Library() + + +class ClearCacheNode(template.Node): + def __init__(self, fragment_name, vary_on): + self.fragment_name = fragment_name + self.vary_on = vary_on + + def render(self, context): + # Build a unicode key for this fragment and all vary-on's. + args = md5( + u':'.join([quote(template.Variable('request').resolve(var, context)) for var in self.vary_on]).encode( + 'utf-8' + ) + ) + cache_key = 'template.cache.%s.%s' % (self.fragment_name, args.hexdigest()) + cache.delete(cache_key) + return '' + + +@register.simple_tag +def clear_cache(parser, token): + """ + This will clear the cache for a template fragment + + Usage:: + + {% load clearcache %} + {% clearcache [fragment_name] %} + + This tag also supports varying by a list of arguments:: + + {% load clearcache %} + {% clearcache [fragment_name] [var1] [var2] .. %} + + The set of arguments must be the same as the original cache tag (except for expire_time). + """ + try: + tokens = token.split_contents() + except ValueError: + raise template.TemplateSyntaxError('%r tag requires at least one argument' % token.contents.split()[0]) + return ClearCacheNode(tokens[1], tokens[2:]) + + +register.tag('clear_cache', clear_cache) diff --git a/learn/templates/learn/detail_page.html b/learn/templates/learn/detail_page.html index 6393bc9977..7b12dacd8f 100644 --- a/learn/templates/learn/detail_page.html +++ b/learn/templates/learn/detail_page.html @@ -94,6 +94,9 @@

Explore the topic

{% endif %}
{% include 'learn/promo.html' with bg_class='great-bg-light-blue' %}
{% block body_inline_feedback %}{% endblock %} +{% load clear_cache %} +{% clear_cache hcsat %} +{% block hcsat %} {% if not csat_complete %}
@@ -103,6 +106,8 @@

Explore the topic

{% endif %} {% endblock %} +{% clear_cache hcsat %} +{% endblock %} {% block body_js %} {{ block.super }} From 62e838e24c405103f144357b1da415c0764c8b4c Mon Sep 17 00:00:00 2001 From: Jamie Fox <134952460+FoxJamie16@users.noreply.github.com> Date: Wed, 13 Nov 2024 16:50:54 +0000 Subject: [PATCH 7/9] csrf token generator created but not used, csrf protect decorator added to serve which I think will fix --- core/models.py | 9 +++------ core/urls.py | 1 + core/views.py | 15 +++++++++++++++ domestic/static/javascript/hcsat-feedback-form.js | 12 ++++++++++-- 4 files changed, 29 insertions(+), 8 deletions(-) diff --git a/core/models.py b/core/models.py index a43b93f9e4..bbf0abbcd5 100644 --- a/core/models.py +++ b/core/models.py @@ -73,6 +73,8 @@ SECTION_SLUGS as EXPORTPLAN_SLUGS, SECTIONS as EXPORTPLAN_URL_MAP, ) +from django.utils.decorators import method_decorator +from django.views.decorators.csrf import csrf_protect # If we make a Redirect appear as a Snippet, we can sync it via Wagtail-Transfer register_snippet(Redirect) @@ -764,9 +766,6 @@ def serve(self, request): return self._redirect_to_parent_module() -from wagtailcache.cache import WagtailCacheMixin - - class DetailPage( settings.FEATURE_DEA_V2 and CMSGenericPageAnonymous or CMSGenericPage, mixins.HCSATMixin, WagtailCacheMixin ): @@ -783,9 +782,6 @@ class Meta: verbose_name = 'Detail page' verbose_name_plural = 'Detail pages' - def cache_control(self): - return 'no-cache' - ################ # Content fields ################ @@ -1102,6 +1098,7 @@ def form_valid(self, form, request): return JsonResponse({'pk': hcsat.pk}) return HttpResponseRedirect(self.get_success_url(request)) + @method_decorator(csrf_protect, name='post') def serve(self, request, *args, **kwargs): self.handle_page_view(request) diff --git a/core/urls.py b/core/urls.py index d63872c83a..1281bc0db7 100644 --- a/core/urls.py +++ b/core/urls.py @@ -175,6 +175,7 @@ def anonymous_user_required(function): name='campaign-site', ), path('api/signed-url/', views.SignedURLView.as_view(), name='signed-url'), + path('api/getcsrftoken/', views.CSRFView.as_view(), name='csrftoken'), # WHEN ADDING TO THIS LIST CONSIDER WHETHER YOU SHOULD ALSO ADD THE URL NAME # TO core.views.StaticViewSitemap ] diff --git a/core/views.py b/core/views.py index efb0f57665..81fa51ed29 100644 --- a/core/views.py +++ b/core/views.py @@ -55,6 +55,8 @@ from domestic.models import DomesticDashboard, TopicLandingPage from export_academy.models import Event from sso.views import SSOBusinessUserLogoutView +from django.middleware.csrf import get_token +from rest_framework.views import APIView logger = logging.getLogger(__name__) @@ -966,3 +968,16 @@ def get_context_data(self, **kwargs): ukea_events=ukea_events, market_guide=market_guide, ) + + +@method_decorator(never_cache, name='dispatch') +class CSRFView(APIView): + permission_classes = [] + authentication_classes = [] + + def _get_token(self, request): + token = get_token(request) + return JsonResponse(status=200, data={'csrftoken': token}) + + def dispatch(self, request, *args, **kwargs): + return self._get_token(request) diff --git a/domestic/static/javascript/hcsat-feedback-form.js b/domestic/static/javascript/hcsat-feedback-form.js index 2395c2629d..76135df3be 100644 --- a/domestic/static/javascript/hcsat-feedback-form.js +++ b/domestic/static/javascript/hcsat-feedback-form.js @@ -31,13 +31,21 @@ class CsatFormHandler { this.resetForm(); } try { - const csrfToken = document.querySelector('[name=csrfmiddlewaretoken]').value + const oldCsrfToken = document.querySelector('[name=csrfmiddlewaretoken]').value + const csrfTokenFetch = await fetch('/api/getcsrftoken/', { + method: 'GET', + headers: { + 'cache_control': 'no-cache', + } + }) + const csrfTokenJson = await csrfTokenFetch.json() + const csrfToken = csrfTokenJson.csrftoken console.log('csrfToken:',csrfToken) const response = await fetch(`${url}?js_enabled=True`, { method: 'POST', headers: { 'cache_control': 'no-cache', - 'X-CSRFToken': csrfToken, + 'X-CSRFToken': oldCsrfToken, 'Accept': 'application/json', 'X-Requested-With': 'XMLHttpRequest', }, From 90c6077977d4a07cb9b5d151196f8da5464ef7fb Mon Sep 17 00:00:00 2001 From: Jamie Fox <134952460+FoxJamie16@users.noreply.github.com> Date: Wed, 13 Nov 2024 16:58:23 +0000 Subject: [PATCH 8/9] syntax fix on csrf token generator --- core/views.py | 1 + 1 file changed, 1 insertion(+) diff --git a/core/views.py b/core/views.py index 81fa51ed29..b234975114 100644 --- a/core/views.py +++ b/core/views.py @@ -55,6 +55,7 @@ from domestic.models import DomesticDashboard, TopicLandingPage from export_academy.models import Event from sso.views import SSOBusinessUserLogoutView +from django.views.decorators.cache import never_cache from django.middleware.csrf import get_token from rest_framework.views import APIView From 83f55d65ede0764c3c4d8e57750bdd906203ad20 Mon Sep 17 00:00:00 2001 From: Jamie Fox <134952460+FoxJamie16@users.noreply.github.com> Date: Fri, 22 Nov 2024 09:14:04 +0000 Subject: [PATCH 9/9] more snippet caching attempts but none working so far --- core/templatetags/clear_cache.py | 15 ++++++++++++++- learn/templates/learn/detail_page.html | 3 +++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/core/templatetags/clear_cache.py b/core/templatetags/clear_cache.py index 705ad57e1d..95ec0abf95 100644 --- a/core/templatetags/clear_cache.py +++ b/core/templatetags/clear_cache.py @@ -18,8 +18,20 @@ def render(self, context): 'utf-8' ) ) + from django.core.cache.utils import make_template_fragment_key + cache_key = 'template.cache.%s.%s' % (self.fragment_name, args.hexdigest()) - cache.delete(cache_key) + print('clear-cache key', cache_key) + from django.core.cache import cache + + key = make_template_fragment_key('hcsat') + + result2 = cache.delete(key) + result1 = cache.delete_many(keys=cache.keys('*hcsat*')) + result = cache.delete(cache_key) + print('result', result) + print('result1', result1) + print('result2', result2) return '' @@ -42,6 +54,7 @@ def clear_cache(parser, token): """ try: tokens = token.split_contents() + print('tokens', tokens) except ValueError: raise template.TemplateSyntaxError('%r tag requires at least one argument' % token.contents.split()[0]) return ClearCacheNode(tokens[1], tokens[2:]) diff --git a/learn/templates/learn/detail_page.html b/learn/templates/learn/detail_page.html index 7b12dacd8f..d9f02ce571 100644 --- a/learn/templates/learn/detail_page.html +++ b/learn/templates/learn/detail_page.html @@ -96,6 +96,8 @@

Explore the topic

{% block body_inline_feedback %}{% endblock %} {% load clear_cache %} {% clear_cache hcsat %} +{% load cache %} +{% cache 500 hcsat %} {% block hcsat %} {% if not csat_complete %}
Explore the topic
{% endif %} {% endblock %} +{% endcache %} {% clear_cache hcsat %} {% endblock %} {% block body_js %}