datasette icon indicating copy to clipboard operation
datasette copied to clipboard

Baked in way of configuring arbitrary permissions in metadata

Open simonw opened this issue 3 years ago • 2 comments

The "allow" block mechanism can already be used to configure various default permissions. When adding permissions to datasette-tiddlywiki I realized it would be good to be able to configure arbitrary permissions such as edit-tiddlywiki there too.

simonw avatar Feb 15 '22 00:02 simonw

I keep running into a need for this. Every time I create a new plugin that defines a new permission I wish there was a clean way to grant that permission to new users without installing some other permissions plugin.

simonw avatar Jul 22 '22 19:07 simonw

I keep shipping plugins that set a special hook just so the root user can try them out.

simonw avatar Jul 22 '22 19:07 simonw

Current design:

{
    "databases": {
        "private": {
            "allow": {
                "id": "*"
            }
        }
    }
}

This can be applied at the instance, database, table or query level within the nested JSON.

https://docs.datasette.io/en/stable/authentication.html#controlling-access-to-specific-databases

It's actually controlling the following permissions:

  • view-instance
  • view-database
  • view-table
  • view-query

There's also a special case for allowing SQL queries,at the instance and database level:

{
    "databases": {
        "mydatabase": {
            "allow_sql": {
                "id": "root"
            }
        }
    }
}

simonw avatar Dec 02 '22 01:12 simonw

So the new mechanism needs to extend that to handle all of the other permissions as well.

The simplest design I can think of is this (here illustrated using YAML):

# instance-level permissions - give every logged in user the debug menu:
permissions:
  debug-menu:
    id: *
databases:
  content:
    # Allow bob to create-table in the content database
    permissions:
      create-table:
        id: bob

simonw avatar Dec 02 '22 02:12 simonw

Should I call this key permissions or something else?

Some options:

  • permissions
  • perms - shorter to type
  • allow - I like the word, but might be confusing to change its meaning since we use it already

simonw avatar Dec 02 '22 04:12 simonw

Also, this is another thing which should live in config.yml rather than being crammed into metadata.yml - but I can fix that when I address:

  • #493

simonw avatar Dec 02 '22 04:12 simonw

Thankfully all of the logic for this already lives in just one place:

https://github.com/simonw/datasette/blob/d7e5e3c9f98d194fdfb12f1ecc60ed5b3afbc464/datasette/default_permissions.py#L23-L59

simonw avatar Dec 02 '22 04:12 simonw

I'm going to write the documentation for this first.

simonw avatar Dec 08 '22 01:12 simonw

What if you want to grant insert-row to a user for ALL tables in a database, or even for all tables in all databases?

You should be able to do that by putting that in the root permissions: block. Need to figure out how the implementation will handle that.

Also: there are some permissions like view-instance or debug-menu for which putting them at the database or table or query level doesn't actually make any sense.

Ideally the implementation would spot those on startup and refuse to start the server, with a helpful error message.

simonw avatar Dec 08 '22 22:12 simonw

First draft of documentation: https://datasette--1938.org.readthedocs.build/en/1938/authentication.html#other-permissions-in-metadata

simonw avatar Dec 08 '22 22:12 simonw

I may need to consult this file to figure out if the permission that is being checked can act at the database/table/instance level:

https://github.com/simonw/datasette/blob/e539c1c024bc62d88df91d9107cbe37e7f0fe55f/datasette/permissions.py#L1-L19

simonw avatar Dec 09 '22 01:12 simonw

A bunch of the work for this just landed - in particular the new scheme is now documented (even though it doesn't work yet):

https://docs.datasette.io/en/latest/authentication.html#other-permissions-in-metadata

simonw avatar Dec 13 '22 02:12 simonw

The implementation for this will go here: https://github.com/simonw/datasette/blob/8bf06a76b51bc9ace7cf72cf0cca8f1da7704ea7/datasette/default_permissions.py#L81-L83

Here's the start of the tests (currently marked as xfail):

https://github.com/simonw/datasette/blob/8bf06a76b51bc9ace7cf72cf0cca8f1da7704ea7/tests/test_permissions.py#L652-L689

simonw avatar Dec 13 '22 02:12 simonw

The thing I'm stuck on at the moment is how to implement it such that an allow block for create-table at the root of the metadata will be checked correctly.

Maybe the algorithm when _resolve_metadata_permissions_blocks(datasette, actor, action, resource) is called should do this:

  1. If a root permission block matching that action exists, test with that
  2. Next, if resource has been passed, check at the database level
  3. If the resource included a table/query, check at that level too

So everything is keyed off the incoming action name.

simonw avatar Dec 13 '22 02:12 simonw