mirror of
https://github.com/simonw/datasette.git
synced 2025-12-10 16:51:24 +01:00
Foreign key label expanding respects table permissions, closes #2178
This commit is contained in:
parent
2200abfa17
commit
dbfad6d220
4 changed files with 63 additions and 3 deletions
|
|
@ -935,7 +935,7 @@ class Datasette:
|
||||||
log_sql_errors=log_sql_errors,
|
log_sql_errors=log_sql_errors,
|
||||||
)
|
)
|
||||||
|
|
||||||
async def expand_foreign_keys(self, database, table, column, values):
|
async def expand_foreign_keys(self, actor, database, table, column, values):
|
||||||
"""Returns dict mapping (column, value) -> label"""
|
"""Returns dict mapping (column, value) -> label"""
|
||||||
labeled_fks = {}
|
labeled_fks = {}
|
||||||
db = self.databases[database]
|
db = self.databases[database]
|
||||||
|
|
@ -949,6 +949,13 @@ class Datasette:
|
||||||
][0]
|
][0]
|
||||||
except IndexError:
|
except IndexError:
|
||||||
return {}
|
return {}
|
||||||
|
# Ensure user has permission to view the referenced table
|
||||||
|
if not await self.permission_allowed(
|
||||||
|
actor=actor,
|
||||||
|
action="view-table",
|
||||||
|
resource=(database, fk["other_table"]),
|
||||||
|
):
|
||||||
|
return {}
|
||||||
label_column = await db.label_column_for_table(fk["other_table"])
|
label_column = await db.label_column_for_table(fk["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}
|
||||||
|
|
|
||||||
|
|
@ -253,7 +253,7 @@ class ColumnFacet(Facet):
|
||||||
# Attempt to expand foreign keys into labels
|
# Attempt to expand foreign keys into labels
|
||||||
values = [row["value"] for row in facet_rows]
|
values = [row["value"] for row in facet_rows]
|
||||||
expanded = await self.ds.expand_foreign_keys(
|
expanded = await self.ds.expand_foreign_keys(
|
||||||
self.database, self.table, column, values
|
self.request.actor, self.database, self.table, column, values
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
expanded = {}
|
expanded = {}
|
||||||
|
|
|
||||||
|
|
@ -1144,7 +1144,7 @@ async def table_view_data(
|
||||||
# Expand them
|
# Expand them
|
||||||
expanded_labels.update(
|
expanded_labels.update(
|
||||||
await datasette.expand_foreign_keys(
|
await datasette.expand_foreign_keys(
|
||||||
database_name, table_name, column, values
|
request.actor, database_name, table_name, column, values
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
if expanded_labels:
|
if expanded_labels:
|
||||||
|
|
|
||||||
|
|
@ -1204,3 +1204,56 @@ async def test_format_of_binary_links(size, title, length_bytes):
|
||||||
sql_response = await ds.client.get("/{}".format(db_name), params={"sql": sql})
|
sql_response = await ds.client.get("/{}".format(db_name), params={"sql": sql})
|
||||||
assert sql_response.status_code == 200
|
assert sql_response.status_code == 200
|
||||||
assert expected in sql_response.text
|
assert expected in sql_response.text
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_foreign_key_labels_obey_permissions():
|
||||||
|
ds = Datasette(
|
||||||
|
metadata={
|
||||||
|
"databases": {
|
||||||
|
"foreign_key_labels": {
|
||||||
|
"tables": {
|
||||||
|
# Table a is only visible to root
|
||||||
|
"a": {"allow": {"id": "root"}},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
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(
|
||||||
|
"create table b(id integer primary key, name text, a_id integer references a(id))"
|
||||||
|
)
|
||||||
|
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
|
||||||
|
blah = await ds.client.get("/foreign_key_labels.json")
|
||||||
|
anon_a = await ds.client.get("/foreign_key_labels/a.json?_labels=on")
|
||||||
|
assert anon_a.status_code == 403
|
||||||
|
anon_b = await ds.client.get("/foreign_key_labels/b.json?_labels=on")
|
||||||
|
assert anon_b.status_code == 200
|
||||||
|
# root user can see both
|
||||||
|
cookies = {"ds_actor": ds.sign({"a": {"id": "root"}}, "actor")}
|
||||||
|
root_a = await ds.client.get(
|
||||||
|
"/foreign_key_labels/a.json?_labels=on", cookies=cookies
|
||||||
|
)
|
||||||
|
assert root_a.status_code == 200
|
||||||
|
root_b = await ds.client.get(
|
||||||
|
"/foreign_key_labels/b.json?_labels=on", cookies=cookies
|
||||||
|
)
|
||||||
|
assert root_b.status_code == 200
|
||||||
|
# Labels should have been expanded for root
|
||||||
|
assert root_b.json() == {
|
||||||
|
"ok": True,
|
||||||
|
"next": None,
|
||||||
|
"rows": [{"id": 1, "name": "world", "a_id": {"value": 1, "label": "hello"}}],
|
||||||
|
"truncated": False,
|
||||||
|
}
|
||||||
|
# But not for anon
|
||||||
|
assert anon_b.json() == {
|
||||||
|
"ok": True,
|
||||||
|
"next": None,
|
||||||
|
"rows": [{"id": 1, "name": "world", "a_id": 1}],
|
||||||
|
"truncated": False,
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue