{"html_url": "https://github.com/simonw/datasette/issues/1855#issuecomment-1313148519", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1855", "id": 1313148519, "node_id": "IC_kwDOBm6k_c5ORQ5n", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-11-14T06:13:43Z", "updated_at": "2022-12-13T02:46:51Z", "author_association": "OWNER", "body": "The `datasette create-token` command will need to be able to do this too.\r\n\r\nRight now that command looks like this:\r\n```\r\n% datasette create-token --help\r\nUsage: datasette create-token [OPTIONS] ID\r\n\r\n Create a signed API token for the specified actor ID\r\n\r\nOptions:\r\n --secret TEXT Secret used for signing the API tokens\r\n [required]\r\n -e, --expires-after INTEGER Token should expire after this many seconds\r\n --debug Show decoded token\r\n --help Show this message and exit.\r\n```\r\n```\r\n% datasette create-token root --secret sec --debug -e 445\r\ndstok_eyJhIjoicm9vdCIsInRva2VuIjoiZHN0b2siLCJ0IjoxNjY4NDA2MjEzLCJkIjo0NDV9.Hd6qRli6xRKkOIRQgZkPO5iN1wM\r\n\r\nDecoded:\r\n\r\n{\r\n \"a\": \"root\",\r\n \"token\": \"dstok\",\r\n \"t\": 1668406213,\r\n \"d\": 445\r\n}\r\n```\r\n(The `--debug` bit adds the decoded token.)\r\n\r\nSyntax for adding \"insert row\" for everything, \"update row\" for all in the \"data\" database and \"delete row\" just for the docs / titles table:\r\n```\r\ndatasette create-token root --secret sec \\\r\n --all insert-row \\\r\n --database data update-row \\\r\n --table docs titles delete-row\r\n```\r\nThe `ir` / `ur` / `dr` options would work too. To add multiple permissions use these options multiple times:\r\n```\r\ndatasette create-token root --secret sec \\\r\n --all insert-row \\\r\n --all delete-row\r\n```\r\nShort versions: `-a` and `-d` and `-t`.\r\n\r\nUPDATE: 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.\r\n\r\nSo `--resource` and `-r`, not `-t`.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1423336089, "label": "`datasette create-token` ability to create tokens with a reduced set of permissions"}, "performed_via_github_app": null}
{"html_url": "https://github.com/simonw/datasette/pull/1940#issuecomment-1347616055", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1940", "id": 1347616055, "node_id": "IC_kwDOBm6k_c5QUv03", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-12-13T01:27:03Z", "updated_at": "2022-12-13T01:27:03Z", "author_association": "OWNER", "body": "I'm going to revert that last commit, see if I can get the tests running again and then apply the changes a line at a time to figure out which ones broke things.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1486011362, "label": "register_permissions() plugin hook"}, "performed_via_github_app": null}
{"html_url": "https://github.com/simonw/datasette/pull/1940#issuecomment-1347620733", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1940", "id": 1347620733, "node_id": "IC_kwDOBm6k_c5QUw99", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-12-13T01:33:06Z", "updated_at": "2022-12-13T01:33:06Z", "author_association": "OWNER", "body": "It's this change which triggers the failures:\r\n```diff\r\ndiff --git a/datasette/app.py b/datasette/app.py\r\nindex 760063d5..defa9688 100644\r\n--- a/datasette/app.py\r\n+++ b/datasette/app.py\r\n@@ -707,9 +707,12 @@ class Datasette:\r\n )\r\n return crumbs\r\n \r\n- async def permission_allowed(self, actor, action, resource=None, default=False):\r\n+ async def permission_allowed(self, actor, action, resource=None, default=None):\r\n \"\"\"Check permissions using the permissions_allowed plugin hook\"\"\"\r\n result = None\r\n+ # Use default from registered permission, if available\r\n+ if default is None and action in self.permissions:\r\n+ default = self.permissions[action].default\r\n for check in pm.hook.permission_allowed(\r\n datasette=self,\r\n actor=actor,\r\n```", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1486011362, "label": "register_permissions() plugin hook"}, "performed_via_github_app": null}
{"html_url": "https://github.com/simonw/datasette/pull/1940#issuecomment-1347632350", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1940", "id": 1347632350, "node_id": "IC_kwDOBm6k_c5QUzze", "user": {"value": 22429695, "label": "codecov[bot]"}, "created_at": "2022-12-13T01:48:40Z", "updated_at": "2022-12-13T02:00:52Z", "author_association": "NONE", "body": "# [Codecov](https://codecov.io/gh/simonw/datasette/pull/1940?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Simon+Willison) Report\nBase: **92.00**% // Head: **92.03**% // Increases project coverage by **`+0.02%`** :tada:\n> Coverage data is based on head [(`a1317ab`)](https://codecov.io/gh/simonw/datasette/pull/1940?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Simon+Willison) compared to base [(`e539c1c`)](https://codecov.io/gh/simonw/datasette/commit/e539c1c024bc62d88df91d9107cbe37e7f0fe55f?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Simon+Willison).\n> Patch coverage: 100.00% of modified lines in pull request are covered.\n\n> :exclamation: Current head a1317ab differs from pull request most recent head 94e5c75. Consider uploading reports for the commit 94e5c75 to get more accurate results\n\nAdditional details and impacted files\n\n\n```diff\n@@ Coverage Diff @@\n## main #1940 +/- ##\n==========================================\n+ Coverage 92.00% 92.03% +0.02% \n==========================================\n Files 38 38 \n Lines 5378 5396 +18 \n==========================================\n+ Hits 4948 4966 +18 \n Misses 430 430 \n```\n\n\n| [Impacted Files](https://codecov.io/gh/simonw/datasette/pull/1940?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Simon+Willison) | Coverage \u0394 | |\n|---|---|---|\n| [datasette/permissions.py](https://codecov.io/gh/simonw/datasette/pull/1940/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Simon+Willison#diff-ZGF0YXNldHRlL3Blcm1pc3Npb25zLnB5) | `100.00% <\u00f8> (\u00f8)` | |\n| [datasette/views/database.py](https://codecov.io/gh/simonw/datasette/pull/1940/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Simon+Willison#diff-ZGF0YXNldHRlL3ZpZXdzL2RhdGFiYXNlLnB5) | `96.26% <\u00f8> (\u00f8)` | |\n| [datasette/views/index.py](https://codecov.io/gh/simonw/datasette/pull/1940/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Simon+Willison#diff-ZGF0YXNldHRlL3ZpZXdzL2luZGV4LnB5) | `96.49% <\u00f8> (\u00f8)` | |\n| [datasette/views/special.py](https://codecov.io/gh/simonw/datasette/pull/1940/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Simon+Willison#diff-ZGF0YXNldHRlL3ZpZXdzL3NwZWNpYWwucHk=) | `79.20% <\u00f8> (-0.21%)` | :arrow_down: |\n| [datasette/views/table.py](https://codecov.io/gh/simonw/datasette/pull/1940/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Simon+Willison#diff-ZGF0YXNldHRlL3ZpZXdzL3RhYmxlLnB5) | `92.57% <\u00f8> (\u00f8)` | |\n| [datasette/\\_\\_init\\_\\_.py](https://codecov.io/gh/simonw/datasette/pull/1940/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Simon+Willison#diff-ZGF0YXNldHRlL19faW5pdF9fLnB5) | `100.00% <100.00%> (\u00f8)` | |\n| [datasette/app.py](https://codecov.io/gh/simonw/datasette/pull/1940/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Simon+Willison#diff-ZGF0YXNldHRlL2FwcC5weQ==) | `94.47% <100.00%> (+0.04%)` | :arrow_up: |\n| [datasette/default\\_permissions.py](https://codecov.io/gh/simonw/datasette/pull/1940/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Simon+Willison#diff-ZGF0YXNldHRlL2RlZmF1bHRfcGVybWlzc2lvbnMucHk=) | `95.20% <100.00%> (+0.39%)` | :arrow_up: |\n| [datasette/hookspecs.py](https://codecov.io/gh/simonw/datasette/pull/1940/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Simon+Willison#diff-ZGF0YXNldHRlL2hvb2tzcGVjcy5weQ==) | `100.00% <100.00%> (\u00f8)` | |\n\nHelp us with your feedback. Take ten seconds to tell us [how you rate us](https://about.codecov.io/nps?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Simon+Willison). Have a feature suggestion? [Share it here.](https://app.codecov.io/gh/feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Simon+Willison)\n\n\n\n[:umbrella: View full report at Codecov](https://codecov.io/gh/simonw/datasette/pull/1940?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Simon+Willison). \n:loudspeaker: Do you have feedback about the report comment? [Let us know in this issue](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Simon+Willison).\n", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1486011362, "label": "register_permissions() plugin hook"}, "performed_via_github_app": null}
{"html_url": "https://github.com/simonw/datasette/pull/1940#issuecomment-1347634128", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1940", "id": 1347634128, "node_id": "IC_kwDOBm6k_c5QU0PQ", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-12-13T01:51:56Z", "updated_at": "2022-12-13T01:51:56Z", "author_association": "OWNER", "body": "Actually one last thing: I said that the error would only occur if the permissions differed in some way.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1486011362, "label": "register_permissions() plugin hook"}, "performed_via_github_app": null}
{"html_url": "https://github.com/simonw/datasette/pull/1940#issuecomment-1347640542", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1940", "id": 1347640542, "node_id": "IC_kwDOBm6k_c5QU1ze", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-12-13T02:02:10Z", "updated_at": "2022-12-13T02:02:10Z", "author_association": "OWNER", "body": "This PR ended up bundling part of the implementation of:\r\n- #1636\r\n\r\nI'm going to be bad an NOT untangle that from this before I merge it.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1486011362, "label": "register_permissions() plugin hook"}, "performed_via_github_app": null}
{"html_url": "https://github.com/simonw/datasette/issues/1943#issuecomment-1347645615", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1943", "id": 1347645615, "node_id": "IC_kwDOBm6k_c5QU3Cv", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-12-13T02:06:47Z", "updated_at": "2022-12-13T02:06:47Z", "author_association": "OWNER", "body": "This URL is already used for the https://latest.datasette.io/-/permissions tool - but it could include a block on that page that tells you what permissions are available.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1490576818, "label": "`/-/permissions` should list available permissions"}, "performed_via_github_app": null}
{"html_url": "https://github.com/simonw/datasette/issues/1939#issuecomment-1347646516", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1939", "id": 1347646516, "node_id": "IC_kwDOBm6k_c5QU3Q0", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-12-13T02:07:50Z", "updated_at": "2022-12-13T02:07:50Z", "author_association": "OWNER", "body": "Documentation for the new hook: https://docs.datasette.io/en/latest/plugin_hooks.html#register-permissions-datasette", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1485757511, "label": "register_permissions(datasette) plugin hook"}, "performed_via_github_app": null}
{"html_url": "https://github.com/simonw/datasette/issues/1636#issuecomment-1347647298", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1636", "id": 1347647298, "node_id": "IC_kwDOBm6k_c5QU3dC", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-12-13T02:08:46Z", "updated_at": "2022-12-13T02:08:46Z", "author_association": "OWNER", "body": "A bunch of the work for this just landed - in particular the new scheme is now documented (even though it doesn't work yet):\r\n\r\nhttps://docs.datasette.io/en/latest/authentication.html#other-permissions-in-metadata", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1138008042, "label": "\"permissions\" propery in metadata for configuring arbitrary permissions"}, "performed_via_github_app": null}
{"html_url": "https://github.com/simonw/datasette/issues/1636#issuecomment-1347648326", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1636", "id": 1347648326, "node_id": "IC_kwDOBm6k_c5QU3tG", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-12-13T02:10:02Z", "updated_at": "2022-12-13T02:10:02Z", "author_association": "OWNER", "body": "The implementation for this will go here: https://github.com/simonw/datasette/blob/8bf06a76b51bc9ace7cf72cf0cca8f1da7704ea7/datasette/default_permissions.py#L81-L83\r\n\r\nHere's the start of the tests (currently marked as `xfail`):\r\n\r\nhttps://github.com/simonw/datasette/blob/8bf06a76b51bc9ace7cf72cf0cca8f1da7704ea7/tests/test_permissions.py#L652-L689", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1138008042, "label": "\"permissions\" propery in metadata for configuring arbitrary permissions"}, "performed_via_github_app": null}
{"html_url": "https://github.com/simonw/datasette/issues/1636#issuecomment-1347655074", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1636", "id": 1347655074, "node_id": "IC_kwDOBm6k_c5QU5Wi", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-12-13T02:21:04Z", "updated_at": "2022-12-13T02:21:23Z", "author_association": "OWNER", "body": "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.\r\n\r\nMaybe the algorithm when `_resolve_metadata_permissions_blocks(datasette, actor, action, resource)` is called should do this:\r\n\r\n1. If a root permission block matching that action exists, test with that\r\n2. Next, if resource has been passed, check at the database level\r\n3. If the resource included a table/query, check at that level too\r\n\r\nSo everything is keyed off the incoming `action` name.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1138008042, "label": "\"permissions\" propery in metadata for configuring arbitrary permissions"}, "performed_via_github_app": null}
{"html_url": "https://github.com/simonw/datasette/issues/1855#issuecomment-1347669087", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1855", "id": 1347669087, "node_id": "IC_kwDOBm6k_c5QU8xf", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-12-13T02:45:15Z", "updated_at": "2022-12-13T02:45:15Z", "author_association": "OWNER", "body": "The hardest piece here is the UI. I'm going to implement the CLI command first.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1423336089, "label": "`datasette create-token` ability to create tokens with a reduced set of permissions"}, "performed_via_github_app": null}
{"html_url": "https://github.com/simonw/datasette/issues/1855#issuecomment-1347675456", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1855", "id": 1347675456, "node_id": "IC_kwDOBm6k_c5QU-VA", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-12-13T02:57:46Z", "updated_at": "2022-12-13T02:57:46Z", "author_association": "OWNER", "body": "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.\r\n\r\nSo I might have it output warnings instead.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1423336089, "label": "`datasette create-token` ability to create tokens with a reduced set of permissions"}, "performed_via_github_app": null}
{"html_url": "https://github.com/simonw/datasette/issues/1855#issuecomment-1347693620", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1855", "id": 1347693620, "node_id": "IC_kwDOBm6k_c5QVCw0", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-12-13T03:25:41Z", "updated_at": "2022-12-13T03:25:41Z", "author_association": "OWNER", "body": "I'm going to rename \"t\" in the magic format to \"r\" for resource.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1423336089, "label": "`datasette create-token` ability to create tokens with a reduced set of permissions"}, "performed_via_github_app": null}
{"html_url": "https://github.com/simonw/datasette/issues/1855#issuecomment-1347694871", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1855", "id": 1347694871, "node_id": "IC_kwDOBm6k_c5QVDEX", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-12-13T03:28:15Z", "updated_at": "2022-12-13T03:28:15Z", "author_association": "OWNER", "body": "Initial prototype of the `create-token` command changes:\r\n\r\n```diff\r\ndiff --git a/datasette/default_permissions.py b/datasette/default_permissions.py\r\nindex 406dae40..bbe1247e 100644\r\n--- a/datasette/default_permissions.py\r\n+++ b/datasette/default_permissions.py\r\n@@ -278,17 +278,55 @@ def register_commands(cli):\r\n help=\"Token should expire after this many seconds\",\r\n type=int,\r\n )\r\n+ @click.option(\r\n+ \"alls\",\r\n+ \"-a\",\r\n+ \"--all\",\r\n+ type=str,\r\n+ multiple=True,\r\n+ help=\"Restrict token to this permission\",\r\n+ )\r\n+ @click.option(\r\n+ \"databases\",\r\n+ \"-d\",\r\n+ \"--database\",\r\n+ type=(str, str),\r\n+ multiple=True,\r\n+ help=\"Restrict token to this permission on this database\",\r\n+ )\r\n+ @click.option(\r\n+ \"resources\",\r\n+ \"-r\",\r\n+ \"--resource\",\r\n+ type=(str, str, str),\r\n+ multiple=True,\r\n+ help=\"Restrict token to this permission on this database resource (a table, SQL view or named query)\",\r\n+ )\r\n @click.option(\r\n \"--debug\",\r\n help=\"Show decoded token\",\r\n is_flag=True,\r\n )\r\n- def create_token(id, secret, expires_after, debug):\r\n+ def create_token(id, secret, expires_after, alls, databases, resources, debug):\r\n \"Create a signed API token for the specified actor ID\"\r\n ds = Datasette(secret=secret)\r\n bits = {\"a\": id, \"token\": \"dstok\", \"t\": int(time.time())}\r\n if expires_after:\r\n bits[\"d\"] = expires_after\r\n+ if alls or databases or resources:\r\n+ bits[\"_r\"] = {}\r\n+ if alls:\r\n+ bits[\"_r\"][\"a\"] = list(alls)\r\n+ if databases:\r\n+ bits[\"_r\"][\"d\"] = {}\r\n+ for database, action in databases:\r\n+ bits[\"_r\"][\"d\"].setdefault(database, []).append(action)\r\n+ if resources:\r\n+ bits[\"_r\"][\"r\"] = {}\r\n+ for database, table, action in resources:\r\n+ bits[\"_r\"][\"r\"].setdefault(database, {}).setdefault(\r\n+ table, []\r\n+ ).append(action)\r\n token = ds.sign(bits, namespace=\"token\")\r\n click.echo(\"dstok_{}\".format(token))\r\n if debug:\r\n```\r\nStill needs tests, plus I'd like it to use abbreviations if available to keep the token length shorter.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1423336089, "label": "`datasette create-token` ability to create tokens with a reduced set of permissions"}, "performed_via_github_app": null}
{"html_url": "https://github.com/simonw/datasette/issues/1855#issuecomment-1347695728", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1855", "id": 1347695728, "node_id": "IC_kwDOBm6k_c5QVDRw", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-12-13T03:30:09Z", "updated_at": "2022-12-13T03:30:09Z", "author_association": "OWNER", "body": "I just noticed this in the existing code:\r\n\r\nhttps://github.com/simonw/datasette/blob/c5d30b58a1cd1c66bbddcf3561db005543ecaf25/datasette/default_permissions.py#L195-L203\r\n\r\nHard-coding those action names should not be necessary any more, especially now we have `datasette.permissions` for looking up metadata about the permissions.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1423336089, "label": "`datasette create-token` ability to create tokens with a reduced set of permissions"}, "performed_via_github_app": null}
{"html_url": "https://github.com/simonw/datasette/issues/1855#issuecomment-1347707683", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1855", "id": 1347707683, "node_id": "IC_kwDOBm6k_c5QVGMj", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-12-13T03:55:35Z", "updated_at": "2022-12-13T04:15:27Z", "author_association": "OWNER", "body": "Help looks like this:\r\n\r\n```\r\nUsage: datasette create-token [OPTIONS] ID\r\n\r\n Create a signed API token for the specified actor ID\r\n\r\n Example:\r\n\r\n datasette create-token root --secret mysecret\r\n\r\n To only allow create-table:\r\n\r\n datasette create-token root --secret mysecret \\\r\n --all create-table\r\n\r\n Or to only allow insert-row against a specific table:\r\n\r\n datasette create-token root --secret myscret \\\r\n --resource mydb mytable insert-row\r\n\r\n Restricted actions can be specified multiple times using multiple --all,\r\n --database, and --resource options.\r\n\r\n Add --debug to see a decoded version of the token.\r\n\r\nOptions:\r\n --secret TEXT Secret used for signing the API tokens\r\n [required]\r\n -e, --expires-after INTEGER Token should expire after this many seconds\r\n -a, --all ACTION Restrict token to this action\r\n -d, --database DB ACTION Restrict token to this action on this\r\n database\r\n -r, --resource DB RESOURCE ACTION\r\n Restrict token to this action on this\r\n database resource (a table, SQL view or\r\n named query)\r\n --debug Show decoded token\r\n --plugins-dir DIRECTORY Path to directory containing custom plugins\r\n --help Show this message and exit.\r\n```", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1423336089, "label": "`datasette create-token` ability to create tokens with a reduced set of permissions"}, "performed_via_github_app": null}
{"html_url": "https://github.com/simonw/datasette/issues/1855#issuecomment-1347726302", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1855", "id": 1347726302, "node_id": "IC_kwDOBm6k_c5QVKve", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-12-13T04:16:26Z", "updated_at": "2022-12-13T04:16:26Z", "author_association": "OWNER", "body": "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).", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1423336089, "label": "`datasette create-token` ability to create tokens with a reduced set of permissions"}, "performed_via_github_app": null}
{"html_url": "https://github.com/simonw/datasette/issues/1855#issuecomment-1347731288", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1855", "id": 1347731288, "node_id": "IC_kwDOBm6k_c5QVL9Y", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-12-13T04:24:50Z", "updated_at": "2022-12-13T04:24:50Z", "author_association": "OWNER", "body": "For the tests for `datasette create-token` it would be useful if `datasette --get` had a mechanism for sending an `Authorization: Bearer X` header.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1423336089, "label": "`datasette create-token` ability to create tokens with a reduced set of permissions"}, "performed_via_github_app": null}
{"html_url": "https://github.com/simonw/datasette/issues/1946#issuecomment-1347732039", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1946", "id": 1347732039, "node_id": "IC_kwDOBm6k_c5QVMJH", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-12-13T04:26:20Z", "updated_at": "2022-12-13T04:26:20Z", "author_association": "OWNER", "body": "Two options:\r\n\r\n- `--header \"Authorization: Bearer XXX\"` which can be used to send any headers\r\n- `--token XXX` to specify the token, which is then sent using that header\r\n\r\nI like the second option more, simply because there are currently no other headers that affect how Datasette works. `--token` feels obvious and easy to use.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1493339206, "label": "`datasette --get` mechanism for sending tokens"}, "performed_via_github_app": null}
{"html_url": "https://github.com/simonw/datasette/issues/1946#issuecomment-1347733217", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1946", "id": 1347733217, "node_id": "IC_kwDOBm6k_c5QVMbh", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-12-13T04:28:45Z", "updated_at": "2022-12-13T04:28:45Z", "author_association": "OWNER", "body": "Demo of the new feature:\r\n```\r\n% datasette create-token --secret s root\r\ndstok_eyJhIjoicm9vdCIsInRva2VuIjoiZHN0b2siLCJ0IjoxNjcwOTA1NjgwfQ.pqSWOwCSNp678hEWl9l5o7m1GaM\r\n% datasette --get /-/actor.json\r\n{\"actor\": null}\r\n% DATASETTE_SECRET=s datasette --get /-/actor.json --token dstok_eyJhIjoicm9vdCIsInRva2VuIjoiZHN0b2siLCJ0IjoxNjcwOTA1NjgwfQ.pqSWOwCSNp678hEWl9l5o7m1GaM\r\n{\"actor\": {\"id\": \"root\", \"token\": \"dstok\"}}\r\n```", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1493339206, "label": "`datasette --get` mechanism for sending tokens"}, "performed_via_github_app": null}
{"html_url": "https://github.com/simonw/datasette/issues/1855#issuecomment-1347759522", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1855", "id": 1347759522, "node_id": "IC_kwDOBm6k_c5QVS2i", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-12-13T05:11:43Z", "updated_at": "2022-12-13T05:11:43Z", "author_association": "OWNER", "body": "Decided to do the `/-/create-token` UI in a separate ticket:\r\n- #1947", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1423336089, "label": "`datasette create-token` ability to create tokens with a reduced set of permissions"}, "performed_via_github_app": null}
{"html_url": "https://github.com/simonw/datasette/issues/1947#issuecomment-1347760109", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1947", "id": 1347760109, "node_id": "IC_kwDOBm6k_c5QVS_t", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-12-13T05:12:00Z", "updated_at": "2022-12-13T05:12:00Z", "author_association": "OWNER", "body": "For the UI: I think I'm going to dump a whole bunch of form elements on the page (so you can set up to 3 of each category of limit without any JavaScript), then add JavaScript that hides all but one of the options and gives you a \"add another\" widget that adds multiple more.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1493390939, "label": "UI to create reduced scope tokens from the `/-/create-token` page"}, "performed_via_github_app": null}
{"html_url": "https://github.com/simonw/datasette/issues/1855#issuecomment-1347761892", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1855", "id": 1347761892, "node_id": "IC_kwDOBm6k_c5QVTbk", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-12-13T05:14:25Z", "updated_at": "2022-12-13T05:14:25Z", "author_association": "OWNER", "body": "New documentation: https://docs.datasette.io/en/latest/authentication.html#restricting-the-actions-that-a-token-can-perform", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1423336089, "label": "`datasette create-token` ability to create tokens with a reduced set of permissions"}, "performed_via_github_app": null}
{"html_url": "https://github.com/simonw/datasette/issues/1948#issuecomment-1347766530", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1948", "id": 1347766530, "node_id": "IC_kwDOBm6k_c5QVUkC", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-12-13T05:22:19Z", "updated_at": "2022-12-13T05:22:19Z", "author_association": "OWNER", "body": "I tested:\r\n\r\n```\r\n{\"id\": \"root\", \"_r\": {\"a\": \"view-table\"}}\r\n```", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1493404423, "label": "500 error on permission debug page when testing actors with _r"}, "performed_via_github_app": null}
{"html_url": "https://github.com/simonw/datasette/pull/1938#issuecomment-1347767048", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1938", "id": 1347767048, "node_id": "IC_kwDOBm6k_c5QVUsI", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-12-13T05:23:18Z", "updated_at": "2022-12-13T05:23:18Z", "author_association": "OWNER", "body": "I landed this already:\r\n- #1636 ", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1485488236, "label": "\"permissions\" blocks in metadata.json/yaml"}, "performed_via_github_app": null}
{"html_url": "https://github.com/simonw/datasette/issues/1947#issuecomment-1347768328", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1947", "id": 1347768328, "node_id": "IC_kwDOBm6k_c5QVVAI", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-12-13T05:25:31Z", "updated_at": "2022-12-13T22:25:46Z", "author_association": "OWNER", "body": "https://latest.datasette.io/-/create-token currently looks like this:\n\n\n![Image](https://user-images.githubusercontent.com/9599/207458002-7c46940b-22c0-45d3-a668-ec7f1082588c.png)\n\n\nAs a reminder, the CLI options that this needs to provide an alternative to are:\n\nhttps://github.com/simonw/datasette/blob/d4b98d3924dec625a99236e65b1b169ff957381f/docs/cli-reference.rst#L619-L638", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1493390939, "label": "UI to create reduced scope tokens from the `/-/create-token` page"}, "performed_via_github_app": null}
{"html_url": "https://github.com/simonw/datasette/issues/1947#issuecomment-1347768549", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1947", "id": 1347768549, "node_id": "IC_kwDOBm6k_c5QVVDl", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-12-13T05:25:56Z", "updated_at": "2022-12-13T22:29:12Z", "author_association": "OWNER", "body": "- [x] I should add a `--database` example to that help text.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1493390939, "label": "UI to create reduced scope tokens from the `/-/create-token` page"}, "performed_via_github_app": null}
{"html_url": "https://github.com/simonw/datasette/issues/1937#issuecomment-1347770871", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1937", "id": 1347770871, "node_id": "IC_kwDOBm6k_c5QVVn3", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-12-13T05:30:43Z", "updated_at": "2022-12-13T05:30:43Z", "author_association": "OWNER", "body": "Also you should need `update-row` permission to use the `\"replace\": true` option - I should add that rule to `/-/insert` add well.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1483320357, "label": "/db/-/create API should require insert-rows permission to use row: or rows: option"}, "performed_via_github_app": null}
{"html_url": "https://github.com/simonw/datasette/issues/1947#issuecomment-1347775760", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1947", "id": 1347775760, "node_id": "IC_kwDOBm6k_c5QVW0Q", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-12-13T05:38:47Z", "updated_at": "2022-12-13T05:38:47Z", "author_association": "OWNER", "body": "I'm going to hide the options for reducing the scope of the token inside a details/summary element.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1493390939, "label": "UI to create reduced scope tokens from the `/-/create-token` page"}, "performed_via_github_app": null}
{"html_url": "https://github.com/simonw/datasette/issues/1914#issuecomment-1347801679", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1914", "id": 1347801679, "node_id": "IC_kwDOBm6k_c5QVdJP", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-12-13T06:15:54Z", "updated_at": "2022-12-13T06:15:54Z", "author_association": "OWNER", "body": "Should make sure that every API that returns an object as the top level (that's almost all of them) includes `\"ok\": true` to indicate no errors.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1468689139, "label": "Finalize design of JSON for Datasette 1.0"}, "performed_via_github_app": null}
{"html_url": "https://github.com/simonw/datasette/issues/1950#issuecomment-1349855620", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1950", "id": 1349855620, "node_id": "IC_kwDOBm6k_c5QdSmE", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-12-13T22:08:50Z", "updated_at": "2022-12-13T22:08:50Z", "author_association": "OWNER", "body": "https://github.com/simonw/datasette/blob/d4b98d3924dec625a99236e65b1b169ff957381f/datasette/views/table.py#L392-L400", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1495241162, "label": "Bad ?_sort returns a 500 error, should be a 400"}, "performed_via_github_app": null}
{"html_url": "https://github.com/simonw/datasette/issues/1950#issuecomment-1349864950", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1950", "id": 1349864950, "node_id": "IC_kwDOBm6k_c5QdU32", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-12-13T22:11:15Z", "updated_at": "2022-12-13T22:11:15Z", "author_association": "OWNER", "body": "Most places I use that exception at the moment set their own non-500 status error:\n```\ndatasette % rg DatasetteError\ndatasette/handle_exception.py\n7:from .views.base import DatasetteError\n33: elif isinstance(exception, DatasetteError):\n\ndatasette/filters.py\n2:from datasette.views.base import DatasetteError\n22: raise DatasetteError(\"_where= is not allowed\", status=403)\n141: raise DatasetteError(\n\ndatasette/views/table.py\n34:from .base import BaseView, DataView, DatasetteError, ureg, _error\n178: raise DatasetteError(\n192: raise DatasetteError(\n390: raise DatasetteError(\"Cannot use _sort and _sort_desc at the same time\")\n394: raise DatasetteError(f\"Cannot sort table by {sort}\")\n400: raise DatasetteError(f\"Cannot sort table by {sort_desc}\")\n\ndatasette/views/base.py\n39:class DatasetteError(Exception):\n219: raise DatasetteError(str(e), title=\"Invalid SQL\", status=400)\n222: raise DatasetteError(str(e))\n224: except DatasetteError:\n382: raise DatasetteError(\n402: raise DatasetteError(str(e), title=\"Invalid SQL\", status=400)\n405: raise DatasetteError(str(e))\n407: except DatasetteError:\n\ndatasette/views/table2.py\n28:from .base import DataView, DatasetteError, ureg\n296: raise DatasetteError(\n310: raise DatasetteError(\n472: raise DatasetteError(\"Cannot use _sort and _sort_desc at the same time\")\n476: raise DatasetteError(f\"Cannot sort table by {sort}\")\n482: raise DatasetteError(f\"Cannot sort table by {sort_desc}\")\n\ndatasette/views/database.py\n31:from .base import BaseView, DatasetteError, DataView, _error\n188: raise DatasetteError(\"Invalid database\", status=404)\n190: raise DatasetteError(\"Cannot download in-memory databases\", status=404)\n194: raise DatasetteError(\"Cannot download database\", status=404)\n```", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1495241162, "label": "Bad ?_sort returns a 500 error, should be a 400"}, "performed_via_github_app": null}
{"html_url": "https://github.com/simonw/datasette/issues/1947#issuecomment-1349972480", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1947", "id": 1349972480, "node_id": "IC_kwDOBm6k_c5QdvIA", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-12-13T22:58:51Z", "updated_at": "2022-12-13T22:58:51Z", "author_association": "OWNER", "body": "I'm experimenting with a `