Pass only the return parameter to form actions

Five templates built their form action with url_for(..., **request.args),
so every query parameter ended up in the action. This included Flask's
special parameters (?_external=1 gave an absolute http:// action in
production) and the route parameters (?user_id=... or ?alias_id=...
caused a TypeError and HTTP 500).

The only parameter needed is return=detail, which sends the user back to
the detail page after the POST. ReturnToMixin reads it, passes on only
the known value and puts it into the template context as return_to.
BackToUsersMixin and BackToAliasesMixin build on it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
finnandClaude Opus 5.5 committed 2026-10-21 19:24:37 +00:00
1 parent 53081dd553
commit 91eaef7f93
8 files changed
+30 -9

No files matched your search

+4 -2
View File
@@ -4,6 +4,8 @@ from max.db import get_alias_with_user_by_id
from max.exceptions import NotFound from max.exceptions import NotFound
from max.views import BaseTemplateGetView from max.views import BaseTemplateGetView
from .return_to_mixin import RETURN_DETAIL, ReturnToMixin
class FetchAliasMixin: class FetchAliasMixin:
def before_handler(self): def before_handler(self):
@@ -21,9 +23,9 @@ class FetchAliasTemplateGetView(FetchAliasMixin, BaseTemplateGetView):
return context return context
class BackToAliasesMixin: class BackToAliasesMixin(ReturnToMixin):
def get_back_text_and_url(self): def get_back_text_and_url(self):
if self.request.args.get("return") == "detail": if self.get_return_to() == RETURN_DETAIL:
return self.user.email, url_for("alias-detail", alias_id=self.alias.id) return self.user.email, url_for("alias-detail", alias_id=self.alias.id)
else: else:
return self.user.email, url_for("user-detail", user_id=self.user.id) return self.user.email, url_for("user-detail", user_id=self.user.id)
+4 -2
View File
@@ -5,6 +5,8 @@ from max.exceptions import NotFound
from max.translations import t from max.translations import t
from max.views import BaseTemplateGetView from max.views import BaseTemplateGetView
from .return_to_mixin import RETURN_DETAIL, ReturnToMixin
class FetchUserMixin: class FetchUserMixin:
def before_handler(self): def before_handler(self):
@@ -20,9 +22,9 @@ class FetchUserTemplateGetView(FetchUserMixin, BaseTemplateGetView):
return context return context
class BackToUsersMixin: class BackToUsersMixin(ReturnToMixin):
def get_back_text_and_url(self): def get_back_text_and_url(self):
if self.request.args.get("return") == "detail": if self.get_return_to() == RETURN_DETAIL:
return self.user.email, url_for("user-detail", user_id=self.user.id) return self.user.email, url_for("user-detail", user_id=self.user.id)
else: else:
return t("User list"), url_for("user-list") return t("User list"), url_for("user-list")
+17
View File
@@ -0,0 +1,17 @@
RETURN_DETAIL = "detail"
class ReturnToMixin:
"""
Reads the `return` query parameter, which tells where to go back to
"""
def get_return_to(self):
if self.request.args.get("return") == RETURN_DETAIL:
return RETURN_DETAIL
return None
def get_context(self):
context = super().get_context()
context["return_to"] = self.get_return_to()
return context
+1 -1
View File
@@ -4,7 +4,7 @@
{% include "alerts.html" %} {% include "alerts.html" %}
<form action="{{ url_for('alias-delete', alias_id=alias.id, **request.args) }}" method="post"> <form action="{{ url_for('alias-delete', alias_id=alias.id, return=return_to) }}" method="post">
<button type="submit" class="pure-button button-error">{{ t("Delete") }}</button> <button type="submit" class="pure-button button-error">{{ t("Delete") }}</button>
<a href="{{ back_url }}" class="pure-button">{{ t("Cancel") }}</a> <a href="{{ back_url }}" class="pure-button">{{ t("Cancel") }}</a>
{% include "csrf.html" %} {% include "csrf.html" %}
+1 -1
View File
@@ -4,7 +4,7 @@
{% include "alerts.html" %} {% include "alerts.html" %}
<form id="form" class="pure-form pure-form-stacked" action="{{ url_for('alias-edit-note', alias_id=alias.id, **request.args) }}" method="post"> <form id="form" class="pure-form pure-form-stacked" action="{{ url_for('alias-edit-note', alias_id=alias.id, return=return_to) }}" method="post">
<label for="note">{{ t("Note") }}</label> <label for="note">{{ t("Note") }}</label>
<textarea id="note" name="note" form="form">{{ alias.note }}</textarea> <textarea id="note" name="note" form="form">{{ alias.note }}</textarea>
+1 -1
View File
@@ -4,7 +4,7 @@
{% include "alerts.html" %} {% include "alerts.html" %}
<form class="pure-form pure-form-aligned" action="{{ url_for('user-change-password', user_id=user.id, **request.args) }}" method="post"> <form class="pure-form pure-form-aligned" action="{{ url_for('user-change-password', user_id=user.id, return=return_to) }}" method="post">
<div class="pure-control-group"> <div class="pure-control-group">
<label for="password1">{{ t("Password") }}</label> <label for="password1">{{ t("Password") }}</label>
<input id="password1" type="password" name="password1" placeholder="{{ t("New password") }}"> <input id="password1" type="password" name="password1" placeholder="{{ t("New password") }}">
+1 -1
View File
@@ -4,7 +4,7 @@
{% include "alerts.html" %} {% include "alerts.html" %}
<form action="{{ url_for('user-delete', user_id=user.id, **request.args) }}" method="post"> <form action="{{ url_for('user-delete', user_id=user.id, return=return_to) }}" method="post">
<button type="submit" class="pure-button button-error">{{ t("Delete") }}</button> <button type="submit" class="pure-button button-error">{{ t("Delete") }}</button>
<a href="{{ back_url }}" class="pure-button">{{ t("Cancel") }}</a> <a href="{{ back_url }}" class="pure-button">{{ t("Cancel") }}</a>
{% include "csrf.html" %} {% include "csrf.html" %}
+1 -1
View File
@@ -4,7 +4,7 @@
{% include "alerts.html" %} {% include "alerts.html" %}
<form id="form" class="pure-form pure-form-stacked" action="{{ url_for('user-edit-note', user_id=user.id, **request.args) }}" method="post"> <form id="form" class="pure-form pure-form-stacked" action="{{ url_for('user-edit-note', user_id=user.id, return=return_to) }}" method="post">
<label for="note">{{ t("Note") }}</label> <label for="note">{{ t("Note") }}</label>
<textarea id="note" name="note" form="form">{{ user.note }}</textarea> <textarea id="note" name="note" form="form">{{ user.note }}</textarea>