mirror of
https://github.com/spiral-project/ihatemoney.git
synced 2025-05-01 10:42:24 +02:00
History: also ask for private code to confirm deletion
This is the same idea as deleting a project: deleting history is also a major destructive action. We reuse the same form as for project deletion to ask for the private code and provide CSRF validation.
This commit is contained in:
parent
969029a811
commit
db982572aa
4 changed files with 54 additions and 16 deletions
|
@ -221,7 +221,17 @@ class ProjectForm(EditProjectForm):
|
|||
raise ValidationError(Markup(message))
|
||||
|
||||
|
||||
class DeleteProjectForm(FlaskForm):
|
||||
class DestructiveActionProjectForm(FlaskForm):
|
||||
"""Used for any important "delete" action linked to a project:
|
||||
|
||||
- delete project itself
|
||||
- delete history
|
||||
- delete IP addresses in history
|
||||
- possibly others in the future
|
||||
|
||||
It asks the user to enter the private code to confirm deletion.
|
||||
"""
|
||||
|
||||
password = PasswordField(
|
||||
_("Private code"),
|
||||
description=_("Enter private code to confirm deletion"),
|
||||
|
|
|
@ -125,6 +125,28 @@
|
|||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro delete_project_history(form) %}
|
||||
|
||||
{% include "display_errors.html" %}
|
||||
{{ form.hidden_tag() }}
|
||||
{{ input(form.password, inline=True) }}
|
||||
<div class="actions">
|
||||
<button class="btn btn-danger">{{ _("Confirm deletion") }}</button>
|
||||
</div>
|
||||
|
||||
{% endmacro %}
|
||||
|
||||
{% macro delete_ip_addresses(form) %}
|
||||
|
||||
{% include "display_errors.html" %}
|
||||
{{ form.hidden_tag() }}
|
||||
{{ input(form.password) }}
|
||||
<div class="actions">
|
||||
<button class="btn btn-danger">{{ _("Confirm deletion") }}</button>
|
||||
</div>
|
||||
|
||||
{% endmacro %}
|
||||
|
||||
{% macro add_bill(form, edit=False, title=True) %}
|
||||
|
||||
<fieldset>
|
||||
|
|
|
@ -55,8 +55,7 @@
|
|||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">{{ _("Close") }}</button>
|
||||
<form action="{{ url_for(".strip_ip_addresses") }}" method="post">
|
||||
{{ csrf_form.csrf_token }}
|
||||
<input type="submit" class="btn btn-danger" value="{{ _("Confirm Delete") }}" name="{{ _("Confirm Delete") }}"/>
|
||||
{{ forms.delete_ip_addresses(delete_form) }}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -76,8 +75,7 @@
|
|||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">{{ _("Close") }}</button>
|
||||
<form action="{{ url_for(".erase_history") }}" method="post">
|
||||
{{ csrf_form.csrf_token }}
|
||||
<input type="submit" class="btn btn-danger" value="{{ _("Confirm Delete") }}" name="{{ _("Confirm Delete") }}"/>
|
||||
{{ forms.delete_project_history(delete_form) }}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -39,7 +39,7 @@ from werkzeug.security import check_password_hash, generate_password_hash
|
|||
from ihatemoney.forms import (
|
||||
AdminAuthenticationForm,
|
||||
AuthenticationForm,
|
||||
DeleteProjectForm,
|
||||
DestructiveActionProjectForm,
|
||||
EditProjectForm,
|
||||
EmptyForm,
|
||||
InviteForm,
|
||||
|
@ -402,7 +402,7 @@ def reset_password():
|
|||
@main.route("/<project_id>/edit", methods=["GET", "POST"])
|
||||
def edit_project():
|
||||
edit_form = EditProjectForm(id=g.project.id)
|
||||
delete_form = DeleteProjectForm(id=g.project.id)
|
||||
delete_form = DestructiveActionProjectForm(id=g.project.id)
|
||||
import_form = UploadForm()
|
||||
# Import form
|
||||
if import_form.validate_on_submit():
|
||||
|
@ -517,7 +517,7 @@ def import_project(file, project):
|
|||
|
||||
@main.route("/<project_id>/delete", methods=["POST"])
|
||||
def delete_project():
|
||||
form = DeleteProjectForm(id=g.project.id)
|
||||
form = DestructiveActionProjectForm(id=g.project.id)
|
||||
if form.validate():
|
||||
g.project.remove_project()
|
||||
flash(_("Project successfully deleted"))
|
||||
|
@ -799,11 +799,11 @@ def settle_bill():
|
|||
@main.route("/<project_id>/history")
|
||||
def history():
|
||||
"""Query for the version entries associated with this project."""
|
||||
csrf_form = EmptyForm()
|
||||
history = get_history(g.project, human_readable_names=True)
|
||||
|
||||
any_ip_addresses = any(event["ip"] for event in history)
|
||||
|
||||
delete_form = DestructiveActionProjectForm()
|
||||
return render_template(
|
||||
"history.html",
|
||||
current_view="history",
|
||||
|
@ -812,33 +812,40 @@ def history():
|
|||
LoggingMode=LoggingMode,
|
||||
OperationType=Operation,
|
||||
current_log_pref=g.project.logging_preference,
|
||||
csrf_form=csrf_form,
|
||||
delete_form=delete_form,
|
||||
)
|
||||
|
||||
|
||||
@main.route("/<project_id>/erase_history", methods=["POST"])
|
||||
def erase_history():
|
||||
"""Erase all history entries associated with this project."""
|
||||
# Used for CSRF validation
|
||||
form = EmptyForm()
|
||||
form = DestructiveActionProjectForm(id=g.project.id)
|
||||
if not form.validate():
|
||||
flash(_("CSRF Token: The CSRF token is invalid."), category="danger")
|
||||
flash(
|
||||
_("Error deleting project history: wrong private code or wrong CSRF token"),
|
||||
category="danger",
|
||||
)
|
||||
return redirect(url_for(".history"))
|
||||
|
||||
for query in get_history_queries(g.project):
|
||||
query.delete(synchronize_session="fetch")
|
||||
|
||||
db.session.commit()
|
||||
flash(_("Deleted project history."))
|
||||
return redirect(url_for(".history"))
|
||||
|
||||
|
||||
@main.route("/<project_id>/strip_ip_addresses", methods=["POST"])
|
||||
def strip_ip_addresses():
|
||||
"""Strip ip addresses from history entries associated with this project."""
|
||||
# Used for CSRF validation
|
||||
form = EmptyForm()
|
||||
form = DestructiveActionProjectForm(id=g.project.id)
|
||||
if not form.validate():
|
||||
flash(_("CSRF Token: The CSRF token is invalid."), category="danger")
|
||||
flash(
|
||||
_(
|
||||
"Error deleting recorded IP addresses: wrong private code or wrong CSRF token"
|
||||
),
|
||||
category="danger",
|
||||
)
|
||||
return redirect(url_for(".history"))
|
||||
|
||||
for query in get_history_queries(g.project):
|
||||
|
@ -846,6 +853,7 @@ def strip_ip_addresses():
|
|||
version_object.transaction.remote_addr = None
|
||||
|
||||
db.session.commit()
|
||||
flash(_("Deleted recorded IP addresses in project history."))
|
||||
return redirect(url_for(".history"))
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue