diff --git a/src/staff/mixins.py b/src/staff/mixins.py index d984f7a2..41163f50 100644 --- a/src/staff/mixins.py +++ b/src/staff/mixins.py @@ -4,7 +4,9 @@ import django_tables2 as tables from django.db import models from django.db.models.query import QuerySet +from django.utils.http import url_has_allowed_host_and_scheme from django.utils.safestring import mark_safe +from django.views.generic import View from genlab_bestilling.models import ( AnalysisOrder, @@ -156,3 +158,43 @@ def order_priority( sorted_by_priority = queryset.order_by(f"{prefix}priority_order") return (sorted_by_priority, True) + + +class SafeRedirectMixin(View): + """Mixin to provide safe redirection after a successful form submission. + This mixin checks for a 'next' parameter in the request and validates it + to ensure it is a safe URL before redirecting. If no valid 'next' URL is found, + it falls back to a method that must be implemented in the view to define + a default redirect URL. + """ + + next_param = "next" + + def get_fallback_url(self) -> str: + msg = "You must override get_fallback_url()" + raise NotImplementedError(msg) + + def has_next_url(self) -> bool: + next_url = self.request.POST.get(self.next_param) or self.request.GET.get( + self.next_param + ) + return bool( + next_url + and url_has_allowed_host_and_scheme( + next_url, + allowed_hosts={self.request.get_host()}, + require_https=self.request.is_secure(), + ) + ) + + def get_next_url(self) -> str: + next_url = self.request.POST.get(self.next_param) or self.request.GET.get( + self.next_param + ) + if next_url and url_has_allowed_host_and_scheme( + next_url, + allowed_hosts={self.request.get_host()}, + require_https=self.request.is_secure(), + ): + return next_url + return self.get_fallback_url() diff --git a/src/staff/templates/staff/components/next_url_input.html b/src/staff/templates/staff/components/next_url_input.html new file mode 100644 index 00000000..7690ffc1 --- /dev/null +++ b/src/staff/templates/staff/components/next_url_input.html @@ -0,0 +1 @@ + diff --git a/src/staff/templates/staff/components/priority_column.html b/src/staff/templates/staff/components/priority_column.html index d46c30d0..0ff60ea2 100644 --- a/src/staff/templates/staff/components/priority_column.html +++ b/src/staff/templates/staff/components/priority_column.html @@ -1,10 +1,12 @@ +{% load next_input %} + {% if record.is_urgent %} {% else %}
{% csrf_token %} - + {% next_url_input %} {% comment %}Outlined flag icon does not work using the tag, so also using the SVG for the filled flag icon for consistency. If it can be fixed in the future it should.{% endcomment %} diff --git a/src/staff/templates/staff/sample_filter.html b/src/staff/templates/staff/sample_filter.html index ccd04396..b2b94506 100644 --- a/src/staff/templates/staff/sample_filter.html +++ b/src/staff/templates/staff/sample_filter.html @@ -1,6 +1,7 @@ {% extends "staff/base_filter.html" %} {% load crispy_forms_tags static %} {% load render_table from django_tables2 %} +{% load next_input %} {% block page-title %}
@@ -33,6 +34,7 @@ {% csrf_token %} + {% next_url_input %}