From 2f977217cb95534f8688454a4aa37e52e2d51516 Mon Sep 17 00:00:00 2001 From: truffle Date: Sat, 30 May 2026 19:13:56 +0000 Subject: [PATCH] Fix /-/check 500 error on query-scoped actions Closes #2756 --- datasette/views/special.py | 8 +++++--- tests/test_permissions.py | 28 ++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/datasette/views/special.py b/datasette/views/special.py index 6c82983c18..48e35b179f 100644 --- a/datasette/views/special.py +++ b/datasette/views/special.py @@ -497,11 +497,13 @@ async def _check_permission_for_actor(ds, action, parent, child, actor): if action_obj.resource_class is None: resource_obj = None elif action_obj.takes_parent and action_obj.takes_child: - # Child-level resource (e.g., TableResource, QueryResource) - resource_obj = action_obj.resource_class(database=parent, table=child) + # Child-level resource (e.g., TableResource, QueryResource). Pass + # positional args so we work with subclasses whose second parameter + # is named `table`, `query`, or anything else. + resource_obj = action_obj.resource_class(parent, child) elif action_obj.takes_parent: # Parent-level resource (e.g., DatabaseResource) - resource_obj = action_obj.resource_class(database=parent) + resource_obj = action_obj.resource_class(parent) else: # This shouldn't happen given validation in Action.__post_init__ return {"error": f"Invalid action configuration: {action}"}, 500 diff --git a/tests/test_permissions.py b/tests/test_permissions.py index e5e7543216..10ecb7d65d 100644 --- a/tests/test_permissions.py +++ b/tests/test_permissions.py @@ -1733,6 +1733,34 @@ async def test_permission_check_view_requires_debug_permission(): assert data["allowed"] is True +@pytest.mark.asyncio +@pytest.mark.parametrize( + "action,child", + [ + ("view-query", "myquery"), + ("update-query", "myquery"), + ("delete-query", "myquery"), + ], +) +async def test_permission_check_view_query_scoped_action(action, child): + """Test that /-/check works for query-scoped actions (issue #2756).""" + ds = Datasette() + ds.root_enabled = True + root_token = await ds.create_token("root", handler="signed") + response = await ds.client.get( + f"/-/check.json?action={action}&parent=mydb&child={child}", + headers={"Authorization": f"Bearer {root_token}"}, + ) + assert response.status_code == 200 + data = response.json() + assert data["action"] == action + assert data["resource"] == { + "parent": "mydb", + "child": child, + "path": f"/mydb/{child}", + } + + @pytest.mark.asyncio async def test_root_allow_block_with_table_restricted_actor(): """