datasette-permissions-sql/datasette_permissions_sql/__init__.py
from datasette import hookimplimport json@hookimpldef permission_allowed(datasette, actor, action, resource):actor = actor or {}async def inner():params = {"action": action}if resource:if len(resource) == 2:resource_1, resource_2 = resourceelse:resource_1, resource_2 = resource, Noneparams["resource_1"] = resource_1params["resource_2"] = resource_2for key, value in actor.items():if isinstance(value, (dict, list, tuple)):value = json.dumps(value, default=repr)params["actor_{}".format(key)] = valuefor rule in datasette.plugin_config("datasette-permissions-sql") or []:sql = rule["sql"]rule_action = rule.get("action")rule_resource = rule.get("resource")if isinstance(rule_resource, list):rule_resource = tuple(rule_resource)fallback = rule.get("fallback")# Optionally match on action/resourceif rule_action is not None and rule_action != action:continueif rule_resource is not None and rule_resource != resource:continue# Execute the SQLif actor:db = datasette.get_database(rule.get("database"))result = await db.execute(sql, params)row = result.first()else:row = Noneif row is None:if not fallback:# Explicit denyreturn Falseelse:# Fallback mode: try next rule in the loopcontinueif len(row) == 1 and str(row[0]) == "-1":# Explicit denyreturn Falseelse:return Truereturn inner