html_url,issue_url,id,node_id,user,created_at,updated_at,author_association,body,reactions,issue,performed_via_github_app https://github.com/simonw/datasette/issues/1855#issuecomment-1347761892,https://api.github.com/repos/simonw/datasette/issues/1855,1347761892,IC_kwDOBm6k_c5QVTbk,9599,2022-12-13T05:14:25Z,2022-12-13T05:14:25Z,OWNER,New documentation: https://docs.datasette.io/en/latest/authentication.html#restricting-the-actions-that-a-token-can-perform,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1423336089, https://github.com/simonw/datasette/issues/1855#issuecomment-1347759522,https://api.github.com/repos/simonw/datasette/issues/1855,1347759522,IC_kwDOBm6k_c5QVS2i,9599,2022-12-13T05:11:43Z,2022-12-13T05:11:43Z,OWNER,"Decided to do the `/-/create-token` UI in a separate ticket: - #1947","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1423336089, https://github.com/simonw/datasette/issues/1855#issuecomment-1347731288,https://api.github.com/repos/simonw/datasette/issues/1855,1347731288,IC_kwDOBm6k_c5QVL9Y,9599,2022-12-13T04:24:50Z,2022-12-13T04:24:50Z,OWNER,For the tests for `datasette create-token` it would be useful if `datasette --get` had a mechanism for sending an `Authorization: Bearer X` header.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1423336089, https://github.com/simonw/datasette/issues/1855#issuecomment-1347726302,https://api.github.com/repos/simonw/datasette/issues/1855,1347726302,IC_kwDOBm6k_c5QVKve,9599,2022-12-13T04:16:26Z,2022-12-13T04:16:26Z,OWNER,I'm going to move this code into `datasette/cli.py` - it's a bit unexpected having it live in `default_permissions.py` like this (I couldn't find the code when I went looking for it earlier).,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1423336089, https://github.com/simonw/datasette/issues/1855#issuecomment-1347707683,https://api.github.com/repos/simonw/datasette/issues/1855,1347707683,IC_kwDOBm6k_c5QVGMj,9599,2022-12-13T03:55:35Z,2022-12-13T04:15:27Z,OWNER,"Help looks like this: ``` Usage: datasette create-token [OPTIONS] ID Create a signed API token for the specified actor ID Example: datasette create-token root --secret mysecret To only allow create-table: datasette create-token root --secret mysecret \ --all create-table Or to only allow insert-row against a specific table: datasette create-token root --secret myscret \ --resource mydb mytable insert-row Restricted actions can be specified multiple times using multiple --all, --database, and --resource options. Add --debug to see a decoded version of the token. Options: --secret TEXT Secret used for signing the API tokens [required] -e, --expires-after INTEGER Token should expire after this many seconds -a, --all ACTION Restrict token to this action -d, --database DB ACTION Restrict token to this action on this database -r, --resource DB RESOURCE ACTION Restrict token to this action on this database resource (a table, SQL view or named query) --debug Show decoded token --plugins-dir DIRECTORY Path to directory containing custom plugins --help Show this message and exit. ```","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1423336089, https://github.com/simonw/datasette/issues/1855#issuecomment-1347695728,https://api.github.com/repos/simonw/datasette/issues/1855,1347695728,IC_kwDOBm6k_c5QVDRw,9599,2022-12-13T03:30:09Z,2022-12-13T03:30:09Z,OWNER,"I just noticed this in the existing code: https://github.com/simonw/datasette/blob/c5d30b58a1cd1c66bbddcf3561db005543ecaf25/datasette/default_permissions.py#L195-L203 Hard-coding those action names should not be necessary any more, especially now we have `datasette.permissions` for looking up metadata about the permissions.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1423336089, https://github.com/simonw/datasette/issues/1855#issuecomment-1347694871,https://api.github.com/repos/simonw/datasette/issues/1855,1347694871,IC_kwDOBm6k_c5QVDEX,9599,2022-12-13T03:28:15Z,2022-12-13T03:28:15Z,OWNER,"Initial prototype of the `create-token` command changes: ```diff diff --git a/datasette/default_permissions.py b/datasette/default_permissions.py index 406dae40..bbe1247e 100644 --- a/datasette/default_permissions.py +++ b/datasette/default_permissions.py @@ -278,17 +278,55 @@ def register_commands(cli): help=""Token should expire after this many seconds"", type=int, ) + @click.option( + ""alls"", + ""-a"", + ""--all"", + type=str, + multiple=True, + help=""Restrict token to this permission"", + ) + @click.option( + ""databases"", + ""-d"", + ""--database"", + type=(str, str), + multiple=True, + help=""Restrict token to this permission on this database"", + ) + @click.option( + ""resources"", + ""-r"", + ""--resource"", + type=(str, str, str), + multiple=True, + help=""Restrict token to this permission on this database resource (a table, SQL view or named query)"", + ) @click.option( ""--debug"", help=""Show decoded token"", is_flag=True, ) - def create_token(id, secret, expires_after, debug): + def create_token(id, secret, expires_after, alls, databases, resources, debug): ""Create a signed API token for the specified actor ID"" ds = Datasette(secret=secret) bits = {""a"": id, ""token"": ""dstok"", ""t"": int(time.time())} if expires_after: bits[""d""] = expires_after + if alls or databases or resources: + bits[""_r""] = {} + if alls: + bits[""_r""][""a""] = list(alls) + if databases: + bits[""_r""][""d""] = {} + for database, action in databases: + bits[""_r""][""d""].setdefault(database, []).append(action) + if resources: + bits[""_r""][""r""] = {} + for database, table, action in resources: + bits[""_r""][""r""].setdefault(database, {}).setdefault( + table, [] + ).append(action) token = ds.sign(bits, namespace=""token"") click.echo(""dstok_{}"".format(token)) if debug: ``` Still needs tests, plus I'd like it to use abbreviations if available to keep the token length shorter.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1423336089, https://github.com/simonw/datasette/issues/1855#issuecomment-1347693620,https://api.github.com/repos/simonw/datasette/issues/1855,1347693620,IC_kwDOBm6k_c5QVCw0,9599,2022-12-13T03:25:41Z,2022-12-13T03:25:41Z,OWNER,"I'm going to rename ""t"" in the magic format to ""r"" for resource.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1423336089, https://github.com/simonw/datasette/issues/1855#issuecomment-1347675456,https://api.github.com/repos/simonw/datasette/issues/1855,1347675456,IC_kwDOBm6k_c5QU-VA,9599,2022-12-13T02:57:46Z,2022-12-13T02:57:46Z,OWNER,"I was going to have the CLI command throw an error if you attempt to use a permission that isn't registered with Datasette, but then I remembered that one of the uses for the CLI tool is to create signed tokens that will work against other Datasette instances (via the `--secret` option) that might have different plugins installed that register different permission names. So I might have it output warnings instead.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1423336089, https://github.com/simonw/datasette/issues/1855#issuecomment-1313148519,https://api.github.com/repos/simonw/datasette/issues/1855,1313148519,IC_kwDOBm6k_c5ORQ5n,9599,2022-11-14T06:13:43Z,2022-12-13T02:46:51Z,OWNER,"The `datasette create-token` command will need to be able to do this too. Right now that command looks like this: ``` % datasette create-token --help Usage: datasette create-token [OPTIONS] ID Create a signed API token for the specified actor ID Options: --secret TEXT Secret used for signing the API tokens [required] -e, --expires-after INTEGER Token should expire after this many seconds --debug Show decoded token --help Show this message and exit. ``` ``` % datasette create-token root --secret sec --debug -e 445 dstok_eyJhIjoicm9vdCIsInRva2VuIjoiZHN0b2siLCJ0IjoxNjY4NDA2MjEzLCJkIjo0NDV9.Hd6qRli6xRKkOIRQgZkPO5iN1wM Decoded: { ""a"": ""root"", ""token"": ""dstok"", ""t"": 1668406213, ""d"": 445 } ``` (The `--debug` bit adds the decoded token.) Syntax for adding ""insert row"" for everything, ""update row"" for all in the ""data"" database and ""delete row"" just for the docs / titles table: ``` datasette create-token root --secret sec \ --all insert-row \ --database data update-row \ --table docs titles delete-row ``` The `ir` / `ur` / `dr` options would work too. To add multiple permissions use these options multiple times: ``` datasette create-token root --secret sec \ --all insert-row \ --all delete-row ``` Short versions: `-a` and `-d` and `-t`. UPDATE: I have decided to use the term `resource` in the user-facing elements of this feature instead of `table`, since that can refer to a SQL view and a canned query as well. So `--resource` and `-r`, not `-t`.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1423336089, https://github.com/simonw/datasette/issues/1855#issuecomment-1347669087,https://api.github.com/repos/simonw/datasette/issues/1855,1347669087,IC_kwDOBm6k_c5QU8xf,9599,2022-12-13T02:45:15Z,2022-12-13T02:45:15Z,OWNER,The hardest piece here is the UI. I'm going to implement the CLI command first.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1423336089,