Filter foreign-key helper targets by view permission

This commit is contained in:
Simon Willison 2026-09-08 21:12:33 -07:00
commit c01e95f3bd
2 changed files with 44 additions and 3 deletions

View file

@ -27,6 +27,11 @@ from datasette.utils import (
table_column_details,
)
from datasette.utils.asgi import NotFound, PayloadTooLarge, Response
from datasette.utils.permissions import (
SKIP_PERMISSION_CHECKS,
gather_permission_sql_from_hooks,
resolve_permissions_with_candidates,
)
from datasette.utils.sqlite import sqlite_hidden_table_names
from .base import BaseView
@ -122,6 +127,30 @@ def _public_foreign_key_target(target):
}
async def _filter_visible_foreign_key_targets(datasette, actor, database_name, targets):
if not targets:
return []
permission_sqls = await gather_permission_sql_from_hooks(
datasette=datasette,
actor=actor,
action="view-table",
)
if permission_sqls is SKIP_PERMISSION_CHECKS:
return targets
candidate_tables = list(dict.fromkeys(target["fk_table"] for target in targets))
permission_rows = await resolve_permissions_with_candidates(
datasette.get_internal_database(),
actor,
permission_sqls,
[(database_name, table_name) for table_name in candidate_tables],
"view-table",
)
visible_tables = {row["child"] for row in permission_rows if bool(row["allow"])}
return [target for target in targets if target["fk_table"] in visible_tables]
def _singular(name):
if name.endswith("ies") and len(name) > 3:
return name[:-3] + "y"
@ -1014,6 +1043,9 @@ class DatabaseForeignKeyTargetsView(BaseView):
for target in (await db.execute(FOREIGN_KEY_TARGETS_SQL)).dicts()
if target["fk_table"] not in hidden_tables
]
targets = await _filter_visible_foreign_key_targets(
self.ds, request.actor, database_name, targets
)
return Response.json(
{
"ok": True,
@ -1052,6 +1084,15 @@ class TableForeignKeySuggestionsView(BaseView):
source_columns, targets, current_by_column = await db.execute_fn(
lambda conn: _foreign_key_suggestion_metadata(conn, table_name)
)
targets = await _filter_visible_foreign_key_targets(
self.ds, request.actor, database_name, targets
)
visible_target_tables = {target["fk_table"] for target in targets}
current_by_column = {
column: current
for column, current in current_by_column.items()
if current["fk_table"] in visible_target_tables
}
columns = []
options_by_column = {}