From b3e12b975064f0873bdb4d2834cae75925387d6b Mon Sep 17 00:00:00 2001 From: Dennis Fink Date: Mon, 10 Aug 2026 23:28:03 +0200 Subject: feat(core): add received webmention admin actions Add authenticated delete and reverify actions to the received webmentions admin view and protect them with CSRF validation. Enable CSRF protection globally while exempting the public Webmention endpoint. --- webmentions_ssg/__init__.py | 3 + webmentions_ssg/forms/__init__.py | 4 ++ webmentions_ssg/templates/received.html | 97 ++++++++++++++++++++++++++++++--- webmentions_ssg/views.py | 72 +++++++++++++++++++++++- 4 files changed, 165 insertions(+), 11 deletions(-) (limited to 'webmentions_ssg') diff --git a/webmentions_ssg/__init__.py b/webmentions_ssg/__init__.py index f66e478..b799678 100644 --- a/webmentions_ssg/__init__.py +++ b/webmentions_ssg/__init__.py @@ -14,6 +14,7 @@ from flask import Flask from flask_bootstrap import Bootstrap5 from flask_migrate import Migrate, upgrade from flask_sqlalchemy import SQLAlchemy +from flask_wtf.csrf import CSRFProtect from sqlalchemy.orm import DeclarativeBase from webmentions_ssg.config import DefaultConfig, DevelopmentConfig, ProductionConfig @@ -28,6 +29,7 @@ class Base(DeclarativeBase): pass +CSRF = CSRFProtect() BOOTSTRAP = Bootstrap5() DATABASE = SQLAlchemy(model_class=Base) MIGRATE = Migrate() @@ -94,6 +96,7 @@ def create_app(config_class: type[DefaultConfig] | None = None) -> Flask: DATABASE.init_app(app) MIGRATE.init_app(app, db=DATABASE) + CSRF.init_app(app) from .auth import AUTH diff --git a/webmentions_ssg/forms/__init__.py b/webmentions_ssg/forms/__init__.py index 0c54999..c386eb1 100644 --- a/webmentions_ssg/forms/__init__.py +++ b/webmentions_ssg/forms/__init__.py @@ -41,3 +41,7 @@ class EndpointForm(FlaskForm): AllowedHostname(), ], ) + + +class AdminActionForm(FlaskForm): + submit = SubmitField("Confirm") diff --git a/webmentions_ssg/templates/received.html b/webmentions_ssg/templates/received.html index ea74557..64f225e 100644 --- a/webmentions_ssg/templates/received.html +++ b/webmentions_ssg/templates/received.html @@ -1,4 +1,5 @@ {% extends "base.html" %} +{% from "bootstrap5/form.html" import render_form %} {% from "bootstrap5/pagination.html" import render_pagination %} {% block content %}
No Webmentions have been received yet.
{% endif %} {% endblock %} +{% block scripts %} + {{ super() }} + +{% endblock %} diff --git a/webmentions_ssg/views.py b/webmentions_ssg/views.py index 5408fa4..b52ae07 100644 --- a/webmentions_ssg/views.py +++ b/webmentions_ssg/views.py @@ -17,8 +17,8 @@ from flask import ( from flask.typing import ResponseReturnValue from flask_login import current_user, login_required, login_user, logout_user +from . import CSRF, forms from . import DATABASE as db -from . import forms from .models import ReceivedWebmention, User from .tasks.receiver import verify_webmention @@ -74,10 +74,78 @@ def received() -> ResponseReturnValue: "received.html", title="Received Webmentions", webmentions=webmentions, + action_form=forms.AdminActionForm(), ) -@root_page.route("/endpoint", methods=["POST"]) +@root_page.post("/received/