Label expand permission check respects cascade, closes #2178

This commit is contained in:
Simon Willison 2023-09-07 16:28:30 -07:00
commit c26370485a
2 changed files with 57 additions and 17 deletions

View file

@ -950,13 +950,19 @@ class Datasette:
except IndexError: except IndexError:
return {} return {}
# Ensure user has permission to view the referenced table # Ensure user has permission to view the referenced table
if not await self.permission_allowed( other_table = fk["other_table"]
actor=actor, other_column = fk["other_column"]
action="view-table", visible, _ = await self.check_visibility(
resource=(database, fk["other_table"]), actor,
): permissions=[
("view-table", (database, other_table)),
("view-database", database),
"view-instance",
],
)
if not visible:
return {} return {}
label_column = await db.label_column_for_table(fk["other_table"]) label_column = await db.label_column_for_table(other_table)
if not label_column: if not label_column:
return {(fk["column"], value): str(value) for value in values} return {(fk["column"], value): str(value) for value in values}
labeled_fks = {} labeled_fks = {}
@ -965,9 +971,9 @@ class Datasette:
from {other_table} from {other_table}
where {other_column} in ({placeholders}) where {other_column} in ({placeholders})
""".format( """.format(
other_column=escape_sqlite(fk["other_column"]), other_column=escape_sqlite(other_column),
label_column=escape_sqlite(label_column), label_column=escape_sqlite(label_column),
other_table=escape_sqlite(fk["other_table"]), other_table=escape_sqlite(other_table),
placeholders=", ".join(["?"] * len(set(values))), placeholders=", ".join(["?"] * len(set(values))),
) )
try: try:

View file

@ -1207,9 +1207,11 @@ async def test_format_of_binary_links(size, title, length_bytes):
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_foreign_key_labels_obey_permissions(): @pytest.mark.parametrize(
ds = Datasette( "metadata",
metadata={ (
# Blocked at table level
{
"databases": { "databases": {
"foreign_key_labels": { "foreign_key_labels": {
"tables": { "tables": {
@ -1218,15 +1220,47 @@ async def test_foreign_key_labels_obey_permissions():
} }
} }
} }
} },
) # Blocked at database level
{
"databases": {
"foreign_key_labels": {
# Only root can view this database
"allow": {"id": "root"},
"tables": {
# But table b is visible to everyone
"b": {"allow": True},
},
}
}
},
# Blocked at the instance level
{
"allow": {"id": "root"},
"databases": {
"foreign_key_labels": {
"tables": {
# Table b is visible to everyone
"b": {"allow": True},
}
}
},
},
),
)
async def test_foreign_key_labels_obey_permissions(metadata):
ds = Datasette(metadata=metadata)
db = ds.add_memory_database("foreign_key_labels") db = ds.add_memory_database("foreign_key_labels")
await db.execute_write("create table a(id integer primary key, name text)")
await db.execute_write("insert into a (id, name) values (1, 'hello')")
await db.execute_write( await db.execute_write(
"create table b(id integer primary key, name text, a_id integer references a(id))" "create table if not exists a(id integer primary key, name text)"
)
await db.execute_write("insert or replace into a (id, name) values (1, 'hello')")
await db.execute_write(
"create table if not exists b(id integer primary key, name text, a_id integer references a(id))"
)
await db.execute_write(
"insert or replace into b (id, name, a_id) values (1, 'world', 1)"
) )
await db.execute_write("insert into b (id, name, a_id) values (1, 'world', 1)")
# Anonymous user can see table b but not table a # Anonymous user can see table b but not table a
blah = await ds.client.get("/foreign_key_labels.json") blah = await ds.client.get("/foreign_key_labels.json")
anon_a = await ds.client.get("/foreign_key_labels/a.json?_labels=on") anon_a = await ds.client.get("/foreign_key_labels/a.json?_labels=on")