{"id": 628025100, "node_id": "MDU6SXNzdWU2MjgwMjUxMDA=", "number": 785, "title": "Datasette secret mechanism - initially for signed cookies", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": {"value": 5512395, "label": "Datasette 0.44"}, "comments": 11, "created_at": "2020-05-31T19:14:52Z", "updated_at": "2020-06-06T00:43:40Z", "closed_at": "2020-06-01T00:18:40Z", "author_association": "OWNER", "pull_request": null, "body": "See comment in https://github.com/simonw/datasette/issues/784#issuecomment-636514974\r\n\r\nDatasette needs to be able to set signed cookies - which means it needs a mechanism for safely handling a signing secret.\r\n\r\nSince Datasette is a long-running process the default behaviour here can be to create a random secret on startup. This means that if the server restarts any signed cookies will be invalidated.\r\n\r\nIf the user wants a persistent secret they'll have to generate it themselves - maybe by setting an environment variable?", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/785/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 628121234, "node_id": "MDU6SXNzdWU2MjgxMjEyMzQ=", "number": 788, "title": " /-/permissions debugging tool", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": {"value": 5512395, "label": "Datasette 0.44"}, "comments": 2, "created_at": "2020-06-01T03:13:47Z", "updated_at": "2020-06-06T00:43:40Z", "closed_at": "2020-06-01T05:01:01Z", "author_association": "OWNER", "pull_request": null, "body": "> Debugging tool idea: `/-/permissions` page which shows you the actor and lets you type in the strings for `action`, `resource_type` and `resource_identifier` - then shows you EVERY plugin hook that would have executed and what it would have said, plus when the chain would have terminated.\r\n>\r\n> Bonus: if you're logged in as the `root` user (or a user that matches some kind of permission check, maybe a check for `permissions_debug`) you get to see a rolling log of the last 30 permission checks and what the results were across the whole of Datasette. This should make figuring out permissions policies a whole lot easier.\r\n\r\n_Originally posted by @simonw in https://github.com/simonw/datasette/issues/699#issuecomment-636576603_", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/788/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 629524205, "node_id": "MDU6SXNzdWU2Mjk1MjQyMDU=", "number": 793, "title": "CSRF protection for /-/messages tool and writable canned queries", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": {"value": 5512395, "label": "Datasette 0.44"}, "comments": 3, "created_at": "2020-06-02T21:22:21Z", "updated_at": "2020-06-06T00:43:41Z", "closed_at": "2020-06-05T19:05:59Z", "author_association": "OWNER", "pull_request": null, "body": "> The `/-/messages` debug tool will need CSRF protection or people will be able to add messages using a hidden form on another website.\r\n_Originally posted by @simonw in https://github.com/simonw/datasette/issues/790#issuecomment-637790860_", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/793/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 631300342, "node_id": "MDExOlB1bGxSZXF1ZXN0NDI4MjEyNDIx", "number": 798, "title": "CSRF protection", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": {"value": 5512395, "label": "Datasette 0.44"}, "comments": 5, "created_at": "2020-06-05T04:22:35Z", "updated_at": "2020-06-06T00:43:41Z", "closed_at": "2020-06-05T19:05:58Z", "author_association": "OWNER", "pull_request": "simonw/datasette/pulls/798", "body": "Refs #793", "repo": {"value": 107914493, "label": "datasette"}, "type": "pull", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/798/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": 0, "state_reason": null} {"id": 628087971, "node_id": "MDU6SXNzdWU2MjgwODc5NzE=", "number": 786, "title": "Documentation page describing Datasette's authentication system", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": {"value": 5512395, "label": "Datasette 0.44"}, "comments": 2, "created_at": "2020-06-01T01:10:06Z", "updated_at": "2020-06-06T19:40:20Z", "closed_at": "2020-06-06T19:40:20Z", "author_association": "OWNER", "pull_request": null, "body": "_Originally posted by @simonw in https://github.com/simonw/datasette/issues/699#issuecomment-636562999_", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/786/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 582526961, "node_id": "MDU6SXNzdWU1ODI1MjY5NjE=", "number": 699, "title": "Authentication (and permissions) as a core concept", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": {"value": 5512395, "label": "Datasette 0.44"}, "comments": 40, "created_at": "2020-03-16T18:48:00Z", "updated_at": "2020-06-06T19:42:11Z", "closed_at": "2020-06-06T19:42:11Z", "author_association": "OWNER", "pull_request": null, "body": "Right now Datasette authentication is provided exclusively by plugins:\r\n\r\n* https://github.com/simonw/datasette-auth-github\r\n* https://github.com/simonw/datasette-auth-existing-cookies\r\n\r\nThis is an all-or-nothing approach: either your Datasette instance requires authentication at the top level or it does not.\r\n\r\nBut... as I build new plugins like https://github.com/simonw/datasette-configure-fts and https://github.com/simonw/datasette-edit-tables I increasingly have individual features which should be reserved for logged-in users while still wanting other parts of Datasette to be open to all.\r\n\r\nThis is too much for plugins to own independently of Datasette core. Datasette needs to ship a single \"user is authenticated\" concept (independent of how users actually sign in) so that different plugins can integrate with it.", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/699/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 582517965, "node_id": "MDU6SXNzdWU1ODI1MTc5NjU=", "number": 698, "title": "Ability for a canned query to write to the database", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": {"value": 5512395, "label": "Datasette 0.44"}, "comments": 26, "created_at": "2020-03-16T18:31:59Z", "updated_at": "2020-06-06T19:43:49Z", "closed_at": "2020-06-06T19:43:48Z", "author_association": "OWNER", "pull_request": null, "body": "Canned queries are currently read-only: https://datasette.readthedocs.io/en/0.38/sql_queries.html#canned-queries\r\n\r\nAdd a `\"write\": true` option to their definition in `metadata.json` which turns them into queries that are submitted via POST and send their queries to the write queue.\r\n\r\nThen they can be used as a really quick way to define a writable interface and JSON API!", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/698/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 632918799, "node_id": "MDU6SXNzdWU2MzI5MTg3OTk=", "number": 808, "title": "Permission check for every view in Datasette (plus docs)", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": {"value": 5512395, "label": "Datasette 0.44"}, "comments": 2, "created_at": "2020-06-07T01:59:23Z", "updated_at": "2020-06-07T05:30:49Z", "closed_at": "2020-06-07T05:30:49Z", "author_association": "OWNER", "pull_request": null, "body": "Every view in Datasette should perform a permission check to see if the current user/actor is allowed to view that page.\r\n\r\nThis permission check will default to allowed, but having this check will allow plugins to lock down access selectively or even to everything in a Datasette instance.", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/808/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 631931408, "node_id": "MDU6SXNzdWU2MzE5MzE0MDg=", "number": 800, "title": "Canned query permissions mechanism", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": {"value": 5512395, "label": "Datasette 0.44"}, "comments": 14, "created_at": "2020-06-05T20:28:21Z", "updated_at": "2020-06-07T16:22:53Z", "closed_at": "2020-06-07T16:22:53Z", "author_association": "OWNER", "pull_request": null, "body": "> Idea: default is anyone can execute a query.\r\n> \r\n> Or you can specify the following:\r\n> \r\n> ```json\r\n> \r\n> {\r\n> \"databases\": {\r\n> \"my-database\": {\r\n> \"queries\": {\r\n> \"add_twitter_handle\": {\r\n> \"sql\": \"insert into twitter_handles (username) values (:username)\",\r\n> \"write\": true,\r\n> \"allow\": {\r\n> \"id\": [\"simon\"],\r\n> \"role\": [\"staff\"]\r\n> }\r\n> }\r\n> }\r\n> }\r\n> }\r\n> }\r\n> ```\r\n> These get matched against the actor JSON. If any of the fields in any of the keys of `\"allow\"` match a key on the actor, the query is allowed.\r\n> \r\n> `\"id\": \"*\"` matches any actor with an `id` key.\r\n\r\n_Originally posted by @simonw in https://github.com/simonw/datasette/issues/698#issuecomment-639784651_", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/800/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 633066114, "node_id": "MDU6SXNzdWU2MzMwNjYxMTQ=", "number": 810, "title": "Refactor permission check for canned query", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": {"value": 5512395, "label": "Datasette 0.44"}, "comments": 1, "created_at": "2020-06-07T05:33:05Z", "updated_at": "2020-06-07T17:03:15Z", "closed_at": "2020-06-07T17:03:15Z", "author_association": "OWNER", "pull_request": null, "body": "This code here (TODO is follow-on from #808).\r\n\r\nhttps://github.com/simonw/datasette/blob/86dec9e8fffd6c4efec928ae9b5713748dec7e74/datasette/views/database.py#L133-L142\r\n\r\nI can improve this with extra code in https://github.com/simonw/datasette/blob/86dec9e8fffd6c4efec928ae9b5713748dec7e74/datasette/default_permissions.py", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/810/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 634783573, "node_id": "MDU6SXNzdWU2MzQ3ODM1NzM=", "number": 816, "title": "Come up with a new example for extra_template_vars plugin", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": {"value": 5512395, "label": "Datasette 0.44"}, "comments": 2, "created_at": "2020-06-08T16:57:59Z", "updated_at": "2020-06-08T19:06:44Z", "closed_at": "2020-06-08T19:06:11Z", "author_association": "OWNER", "pull_request": null, "body": "This example is obsolete, it's from a time before `request.actor` and authentication as a built-in concept (#699):\r\nhttps://github.com/simonw/datasette/blob/0c064c5fe220b7b3d8dcf85b02b4e60452c47232/docs/plugins.rst#L696-L700\r\n\r\nhttps://github.com/simonw/datasette/blob/0c064c5fe220b7b3d8dcf85b02b4e60452c47232/docs/plugins.rst#extra_template_varstemplate-database-table-view_name-request-datasette", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/816/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 628499086, "node_id": "MDU6SXNzdWU2Mjg0OTkwODY=", "number": 790, "title": "\"flash messages\" mechanism", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": {"value": 5512395, "label": "Datasette 0.44"}, "comments": 20, "created_at": "2020-06-01T14:55:44Z", "updated_at": "2020-06-08T19:33:59Z", "closed_at": "2020-06-02T21:14:03Z", "author_association": "OWNER", "pull_request": null, "body": "> Passing `?_success` like this isn't necessarily the best approach. Potential improvements include:\r\n> \r\n> - Signing this message so it can't be tampered with (I could generate a signing secret on startup)\r\n> - Using a cookie with a temporary flash message in it instead\r\n> - Using HTML5 history API to remove the `?_success=` from the URL bar when the user lands on the page\r\n> \r\n> If I add an option to redirect the user to another page after success I may need a mechanism to show a flash message on that page as well, in which case I'll need a general flash message solution that works for any page.\r\n\r\n_Originally posted by @simonw in https://github.com/simonw/datasette/pull/703_", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/790/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 633578769, "node_id": "MDU6SXNzdWU2MzM1Nzg3Njk=", "number": 811, "title": "Support \"allow\" block on root, databases and tables, not just queries", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": {"value": 5512395, "label": "Datasette 0.44"}, "comments": 16, "created_at": "2020-06-07T17:01:09Z", "updated_at": "2020-06-08T19:34:00Z", "closed_at": "2020-06-08T19:32:36Z", "author_association": "OWNER", "pull_request": null, "body": "No reason not to expand the \"allow\" mechanism [described here](https://github.com/simonw/datasette/blob/86dec9e8fffd6c4efec928ae9b5713748dec7e74/docs/authentication.rst#permissions-for-canned-queries) to the root of `metadata.json` plus to databases and tables.\r\n\r\nRefs #810 and #800.\r\n\r\n```json\r\n{\r\n \"databases\": {\r\n \"mydatabase\": {\r\n \"allow\": {\r\n \"id\": [\"root\"]\r\n }\r\n }\r\n }\r\n}\r\n```\r\n\r\nTODO:\r\n\r\n- [x] Instance level\r\n- [x] Database level\r\n- [x] Table level\r\n- [x] Query level\r\n- [x] Affects list of queries\r\n- [x] Affects list of tables on database page\r\n- [x] Affects truncated list of tables on index page\r\n- [x] Affects list of SQL views on database page\r\n- [x] Affects list of databases on index page\r\n- [x] Show \ud83d\udd12 in header on index page for private instances\r\n- [x] Show \ud83d\udd12 in header on private database page\r\n- [x] Show \ud83d\udd12 in header on private table page\r\n- [x] Show \ud83d\udd12 in header on private query page\r\n- [x] Move `assert_permissions_checked()` calls from `test_html.py` to `test_permissions.py`\r\n- [x] Update documentation", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/811/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 634139848, "node_id": "MDU6SXNzdWU2MzQxMzk4NDg=", "number": 813, "title": "Mechanism for specifying allow_sql permission in metadata.json", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": {"value": 5512395, "label": "Datasette 0.44"}, "comments": 6, "created_at": "2020-06-08T04:57:19Z", "updated_at": "2020-06-09T00:09:57Z", "closed_at": "2020-06-09T00:07:19Z", "author_association": "OWNER", "pull_request": null, "body": "Split from #811. It would be useful if finely-grained permissions configured in `metadata.json` could be used to specify if a user is allowed to execute arbitrary SQL queries.\r\n\r\nWe have a permission check call for this already: https://github.com/simonw/datasette/blob/9397d718345c4b35d2a5c55bfcbd1468876b5ab9/datasette/views/database.py#L159\r\n\r\nBut there's currently no way to implement this check without writing a plugin.\r\n\r\nI think a `\"allow_sql\": {...}` block at the database level in `metadata.json` (sibling to the current `\"allow\"` block for that database implemented in #811) would be a good option for this.", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/813/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"} {"id": 626171242, "node_id": "MDU6SXNzdWU2MjYxNzEyNDI=", "number": 777, "title": "Error pages not correctly loading CSS", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": {"value": 5512395, "label": "Datasette 0.44"}, "comments": 4, "created_at": "2020-05-28T02:47:52Z", "updated_at": "2020-06-09T00:35:29Z", "closed_at": "2020-06-09T00:35:29Z", "author_association": "OWNER", "pull_request": null, "body": "e.g. https://latest.datasette.io/fixtures/compound_three_primary_keys.tsv?_size=max\r\n\r\n\r\n\r\nThe HTML starts like this:\r\n\r\n```html\r\n\r\n\r\n
\r\n