From e3366e3544a2f0ec66fb129a1022be01d0683792 Mon Sep 17 00:00:00 2001
From: Julian Rother <julian@cccv.de>
Date: Mon, 14 Feb 2022 22:36:45 +0100
Subject: [PATCH] Enable foreign key support for SQLite

---
 uffd/__init__.py |  4 +++-
 uffd/database.py | 19 ++++++++++++++++++-
 2 files changed, 21 insertions(+), 2 deletions(-)

diff --git a/uffd/__init__.py b/uffd/__init__.py
index 55fa8360..611f107c 100644
--- a/uffd/__init__.py
+++ b/uffd/__init__.py
@@ -13,7 +13,7 @@ except ImportError:
 from werkzeug.exceptions import InternalServerError, Forbidden
 from flask_migrate import Migrate
 
-from uffd.database import db, SQLAlchemyJSON
+from uffd.database import db, SQLAlchemyJSON, customize_db_engine
 from uffd.template_helper import register_template_helper
 from uffd.navbar import setup_navbar
 from uffd.secure_redirect import secure_local_redirect
@@ -79,6 +79,8 @@ def create_app(test_config=None): # pylint: disable=too-many-locals,too-many-sta
 
 	db.init_app(app)
 	Migrate(app, db, render_as_batch=True, directory=os.path.join(app.root_path, 'migrations'))
+	with app.app_context():
+		customize_db_engine(db.engine)
 
 	for module in [user, selfservice, role, mail, session, csrf, mfa, oauth2, services, rolemod, api, signup, invite]:
 		for bp in module.bp:
diff --git a/uffd/database.py b/uffd/database.py
index 681581e1..68800c74 100644
--- a/uffd/database.py
+++ b/uffd/database.py
@@ -1,6 +1,6 @@
 from collections import OrderedDict
 
-from sqlalchemy import MetaData
+from sqlalchemy import MetaData, event
 from flask_sqlalchemy import SQLAlchemy
 from flask.json import JSONEncoder
 
@@ -15,6 +15,23 @@ metadata = MetaData(naming_convention=convention)
 
 db = SQLAlchemy(metadata=metadata)
 
+def enable_sqlite_foreign_key_support(dbapi_connection, connection_record):
+	# pylint: disable=unused-argument
+	cursor = dbapi_connection.cursor()
+	cursor.execute('PRAGMA foreign_keys=ON')
+	cursor.close()
+
+# We want to enable SQLite foreign key support for app and test code, but not
+# for migrations.
+# The common way to add the handler to the Engine class (so it applies to all
+# instances) would also affect the migrations. With flask_sqlalchemy v2.4 and
+# newer we could overwrite SQLAlchemy.create_engine and add our handler there.
+# However Debian Buster and Bullseye ship v2.1, so we do this here and call
+# this function in create_app.
+def customize_db_engine(engine):
+	if engine.name == 'sqlite':
+		event.listen(engine, 'connect', enable_sqlite_foreign_key_support)
+
 class SQLAlchemyJSON(JSONEncoder):
 	def default(self, o):
 		if isinstance(o, db.Model):
-- 
GitLab