mirror of
https://github.com/spiral-project/ihatemoney.git
synced 2025-04-29 09:52:36 +02:00
Rework project deletion to add CSRF validation
It requires reworking the user interface, but it's probably for the best.
This commit is contained in:
parent
109d7fca17
commit
2bb6f2b6a7
5 changed files with 61 additions and 13 deletions
|
@ -221,6 +221,26 @@ class ProjectForm(EditProjectForm):
|
|||
raise ValidationError(Markup(message))
|
||||
|
||||
|
||||
class DeleteProjectForm(FlaskForm):
|
||||
password = PasswordField(
|
||||
_("Private code"),
|
||||
description=_("Enter private code to confirm deletion"),
|
||||
validators=[DataRequired()],
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
# Same trick as EditProjectForm: we need to know the project ID
|
||||
self.id = SimpleNamespace(data=kwargs.pop("id", ""))
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def validate_password(form, field):
|
||||
project = Project.query.get(form.id.data)
|
||||
if project is None:
|
||||
raise ValidationError(_("Unknown error"))
|
||||
if not check_password_hash(project.password, form.password.data):
|
||||
raise ValidationError(_("Invalid private code."))
|
||||
|
||||
|
||||
class AuthenticationForm(FlaskForm):
|
||||
id = StringField(_("Project identifier"), validators=[DataRequired()])
|
||||
password = PasswordField(_("Private code"), validators=[DataRequired()])
|
||||
|
|
|
@ -303,11 +303,6 @@ footer .footer-left {
|
|||
color: #fff;
|
||||
}
|
||||
|
||||
.confirm,
|
||||
.confirm:hover {
|
||||
color: red;
|
||||
}
|
||||
|
||||
.bill-actions {
|
||||
padding-top: 10px;
|
||||
text-align: center;
|
||||
|
|
|
@ -1,9 +1,17 @@
|
|||
{% extends "layout.html" %}
|
||||
|
||||
{% block js %}
|
||||
$('#delete-project').click(function ()
|
||||
{
|
||||
$(this).html("<a style='color:red; ' href='{{ url_for('.delete_project') }}' >{{_("you sure?")}}</a>");
|
||||
|
||||
$('#delete-project').click(function(){
|
||||
var link = $(this).find('button');
|
||||
link.click(function(){
|
||||
if ($(this).hasClass("confirm")){
|
||||
return true;
|
||||
}
|
||||
$(this).html("{{_("Are you sure?")}}");
|
||||
$(this).addClass("confirm");
|
||||
return false;
|
||||
});
|
||||
});
|
||||
|
||||
$('.custom-file-input').on('change', function(event) {
|
||||
|
@ -21,6 +29,10 @@
|
|||
{{ forms.edit_project(edit_form) }}
|
||||
</form>
|
||||
|
||||
<h2>{{ _("Delete project") }}</h2>
|
||||
<form id="delete-project" class="form-horizontal" action="{{ url_for(".delete_project") }}" method="post" >
|
||||
{{ forms.delete_project(delete_form) }}
|
||||
</form>
|
||||
|
||||
<h2>{{ _("Import JSON") }}</h2>
|
||||
<form class="form-horizontal" method="post" enctype="multipart/form-data">
|
||||
|
|
|
@ -100,7 +100,18 @@
|
|||
{{ input(form.default_currency) }}
|
||||
<div class="actions">
|
||||
<button class="btn btn-primary">{{ _("Edit the project") }}</button>
|
||||
<a id="delete-project" style="color:red; margin-left:10px; cursor:pointer; ">{{ _("delete") }}</a>
|
||||
</div>
|
||||
|
||||
{% endmacro %}
|
||||
|
||||
{% macro delete_project(form) %}
|
||||
|
||||
{% include "display_errors.html" %}
|
||||
<p><strong>{{ _("This will remove all bills and participants in this project!") }}</strong></p>
|
||||
{{ form.hidden_tag() }}
|
||||
{{ input(form.password) }}
|
||||
<div class="actions">
|
||||
<button class="btn btn-danger">{{ _("Delete project") }}</button>
|
||||
</div>
|
||||
|
||||
{% endmacro %}
|
||||
|
|
|
@ -39,6 +39,7 @@ from werkzeug.security import check_password_hash, generate_password_hash
|
|||
from ihatemoney.forms import (
|
||||
AdminAuthenticationForm,
|
||||
AuthenticationForm,
|
||||
DeleteProjectForm,
|
||||
EditProjectForm,
|
||||
EmptyForm,
|
||||
InviteForm,
|
||||
|
@ -401,6 +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)
|
||||
import_form = UploadForm()
|
||||
# Import form
|
||||
if import_form.validate_on_submit():
|
||||
|
@ -434,6 +436,7 @@ def edit_project():
|
|||
return render_template(
|
||||
"edit_project.html",
|
||||
edit_form=edit_form,
|
||||
delete_form=delete_form,
|
||||
import_form=import_form,
|
||||
current_view="edit_project",
|
||||
)
|
||||
|
@ -512,11 +515,18 @@ def import_project(file, project):
|
|||
db.session.commit()
|
||||
|
||||
|
||||
@main.route("/<project_id>/delete")
|
||||
@main.route("/<project_id>/delete", methods=["POST"])
|
||||
def delete_project():
|
||||
form = DeleteProjectForm(id=g.project.id)
|
||||
if form.validate():
|
||||
g.project.remove_project()
|
||||
flash(_("Project successfully deleted"))
|
||||
|
||||
return redirect(url_for(".home"))
|
||||
else:
|
||||
flash(
|
||||
_("Error deleting project: wrong private code or wrong CSRF token"),
|
||||
category="danger",
|
||||
)
|
||||
return redirect(request.headers.get("Referer") or url_for(".home"))
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue