From bee25f58cbeafb5aba3648a7e4f516632bc7d81d Mon Sep 17 00:00:00 2001 From: Simon Willison Date: Wed, 18 Mar 2026 11:47:13 -0700 Subject: [PATCH] set-column-types permission, refs #2671 --- datasette/default_actions.py | 6 ++++++ docs/authentication.rst | 16 +++++++++++++++- tests/test_auth.py | 19 +++++++++++++++++++ tests/test_internals_datasette.py | 9 ++++++++- tests/test_permissions.py | 16 ++++++++++++++++ 5 files changed, 64 insertions(+), 2 deletions(-) diff --git a/datasette/default_actions.py b/datasette/default_actions.py index 87d98fac..216d0046 100644 --- a/datasette/default_actions.py +++ b/datasette/default_actions.py @@ -85,6 +85,12 @@ def register_actions(): description="Alter tables", resource_class=TableResource, ), + Action( + name="set-column-types", + abbr="sct", + description="Set column types", + resource_class=TableResource, + ), Action( name="drop-table", abbr="dt", diff --git a/docs/authentication.rst b/docs/authentication.rst index 1b949f9a..90fd26ce 100644 --- a/docs/authentication.rst +++ b/docs/authentication.rst @@ -33,7 +33,7 @@ The one exception is the "root" account, which you can sign into while using Dat The ``--root`` flag is designed for local development and testing. When you start Datasette with ``--root``, the root user automatically receives every permission, including: * All view permissions (``view-instance``, ``view-database``, ``view-table``, etc.) -* All write permissions (``insert-row``, ``update-row``, ``delete-row``, ``create-table``, ``alter-table``, ``drop-table``) +* All write permissions (``insert-row``, ``update-row``, ``delete-row``, ``create-table``, ``alter-table``, ``set-column-types``, ``drop-table``) * Debug permissions (``permissions-debug``, ``debug-menu``) * Any custom permissions defined by plugins @@ -886,6 +886,8 @@ To grant ``create-table`` to the user with ``id`` of ``editor`` for the ``docs`` } .. [[[end]]] +Other table-scoped write permissions, including ``set-column-types``, can be configured in the same place. + And for ``insert-row`` against the ``reports`` table in that ``docs`` database: .. [[[cog @@ -1343,6 +1345,18 @@ alter-table Actor is allowed to alter a database table. +``resource`` - ``datasette.resources.TableResource(database, table)`` + ``database`` is the name of the database (string) + + ``table`` is the name of the table (string) + +.. _actions_set_column_types: + +set-column-types +---------------- + +Actor is allowed to set assigned column types for columns in a table. + ``resource`` - ``datasette.resources.TableResource(database, table)`` ``database`` is the name of the database (string) diff --git a/tests/test_auth.py b/tests/test_auth.py index 1e1cd622..cb77c7a2 100644 --- a/tests/test_auth.py +++ b/tests/test_auth.py @@ -191,6 +191,7 @@ def test_auth_create_token( "all:view-query", "database:fixtures:drop-table", "resource:fixtures:foreign_key_references:insert-row", + "resource:fixtures:facetable:set-column-types", } ) # Now try actually creating one @@ -427,6 +428,15 @@ async def test_root_with_root_enabled_gets_all_permissions(ds_client): is True ) + assert ( + await ds_client.ds.allowed( + action="set-column-types", + resource=TableResource("fixtures", "facetable"), + actor=root_actor, + ) + is True + ) + assert ( await ds_client.ds.allowed( action="drop-table", @@ -491,3 +501,12 @@ async def test_root_without_root_enabled_no_special_permissions(ds_client): ) is not True ), "Root without root_enabled should not automatically get drop-table" + + assert ( + await ds_client.ds.allowed( + action="set-column-types", + resource=TableResource("fixtures", "facetable"), + actor=root_actor, + ) + is not True + ), "Root without root_enabled should not automatically get set-column-types" diff --git a/tests/test_internals_datasette.py b/tests/test_internals_datasette.py index b378a158..008fa7cd 100644 --- a/tests/test_internals_datasette.py +++ b/tests/test_internals_datasette.py @@ -164,7 +164,14 @@ def test_datasette_error_if_string_not_list(tmpdir): @pytest.mark.asyncio async def test_get_action(ds_client): ds = ds_client.ds - for name_or_abbr in ("vi", "view-instance", "vt", "view-table"): + for name_or_abbr in ( + "vi", + "view-instance", + "vt", + "view-table", + "sct", + "set-column-types", + ): action = ds.get_action(name_or_abbr) if "-" in name_or_abbr: assert action.name == name_or_abbr diff --git a/tests/test_permissions.py b/tests/test_permissions.py index be8969d7..4db89a0e 100644 --- a/tests/test_permissions.py +++ b/tests/test_permissions.py @@ -831,6 +831,22 @@ PermConfigTestCase = collections.namedtuple( resource=("perms_ds_one", "t1"), expected_result=True, ), + # set-column-types on specific table + PermConfigTestCase( + config={ + "databases": { + "perms_ds_one": { + "tables": { + "t1": {"permissions": {"set-column-types": {"id": "user"}}} + } + } + } + }, + actor={"id": "user"}, + action="set-column-types", + resource=("perms_ds_one", "t1"), + expected_result=True, + ), # insert-row on database PermConfigTestCase( config={