Skip to content

fix the CSRF token on delete files and directories#2829

Open
samialfattani wants to merge 2 commits into
pallets-eco:masterfrom
samialfattani:fix/csrf-fileadmin
Open

fix the CSRF token on delete files and directories#2829
samialfattani wants to merge 2 commits into
pallets-eco:masterfrom
samialfattani:fix/csrf-fileadmin

Conversation

@samialfattani

@samialfattani samialfattani commented Mar 11, 2026

Copy link
Copy Markdown
Contributor

CSRF token is not generated in the file/list.html page for both file and directory delete <form>. This PR uses csrf_token() to generate the token and put them in a hidden field to be submitted within the delete action.

Why this PR: it supports CSRF in file/list.html if the user useed flask_wtf.csrf.CSRFProtect

Test: This PR adds many test cases that covers all pages of FileAdmin() including list, rename, upload, ...etc. also there are some tests that include the csrf_token in the post request and assert the response to be 200 not 400.

@samialfattani samialfattani marked this pull request as ready for review March 18, 2026 17:53
samialfattani added a commit to samialfattani/flask-admin that referenced this pull request Apr 1, 2026
- fix the CSRF token on delete files and directories pallets-eco#2829
@samialfattani samialfattani mentioned this pull request Apr 1, 2026
9 tasks

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this edit?

Comment thread flask_admin/tests/fileadmin/test_fileadmin_s3.py
Comment thread flask_admin/tests/fileadmin/test_fileadmin.py Outdated
@samialfattani

Copy link
Copy Markdown
Contributor Author

could any one review this ? i think everything is ready here

Comment thread flask_admin/templates/bootstrap4/admin/file/list.html
use SecureForm

fix

fix

fix

fix

fix

fix Azure initial files

fix

fix

fix

fix

add comment

Co-authored-by: Copilot <copilot@github.com>
@ElLorans

Copy link
Copy Markdown
Contributor

I still do not understand what is happening here:

{% if delete_form.csrf_token is defined and delete_form.csrf_token %}
  {{ delete_form.csrf_token }}
{% elif csrf_token is defined and csrf_token %}
  <input name="csrf_token" type="hidden" value="{{ csrf_token() }}"/>
{% endif %}

is this coming from another part of the repo? Sorry for being dense

@samialfattani

samialfattani commented Jun 15, 2026

Copy link
Copy Markdown
Contributor Author

I still do not understand what is happening here:

{% if delete_form.csrf_token is defined and delete_form.csrf_token %}
  {{ delete_form.csrf_token }}
{% elif csrf_token is defined and csrf_token %}
  <input name="csrf_token" type="hidden" value="{{ csrf_token() }}"/>
{% endif %}

is this coming from another part of the repo? Sorry for being dense

it supports CSRF if the user used flask_wtf.csrf.CSRFProtect, without this PR, the CSRF will fail when the user tries to delete a folder or file and it will shows 400. here is how to replicate the error:

from flask import Flask
from flask_wtf import CSRFProtect
from flask_admin import Admin
from flask_admin.contrib.fileadmin import FileAdmin
from flask_sqlalchemy import SQLAlchemy
from flask_babel import Babel

app = Flask(__name__)
app.config["SECRET_KEY"] = "secret"

babel = Babel(app)

csrf = CSRFProtect(app)

admin = Admin(app,)


@app.route("/")
def index():
    return '<a href="/admin/">Click me to get to Admin!</a>'


class MyFileAdmin(FileAdmin):
    editable_extensions = ["txt", "html", "js", "css"]


if __name__ == "__main__":

    admin.add_view(MyFileAdmin("files/", name="Local Files", category="Menu"))

    app.run(debug=True)

you will find the same logic is applied in :

{% if admin_view.is_editable(c) %}
{% set form = list_forms[get_pk_value(row)] %}
{% if form.csrf_token is defined and form.csrf_token %}
{{ form[c](pk=get_pk_value(row), display_value=get_value(row, c), csrf=form.csrf_token._value()) }}
{% elif csrf_token is defined and csrf_token %}
{{ form[c](pk=get_pk_value(row), display_value=get_value(row, c), csrf=csrf_token()) }}
{% else %}
{{ form[c](pk=get_pk_value(row), display_value=get_value(row, c)) }}
{% endif %}
{% else %}
{{ get_value(row, c) }}
{% endif %}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants