home / github

Menu
  • Search all tables
  • GraphQL API

issue_comments

Table actions
  • GraphQL API for issue_comments

26 rows where author_association = "OWNER" and "created_at" is on date 2022-10-24 sorted by updated_at descending

✎ View and edit SQL

This data as json, CSV (advanced)

Suggested facets: issue_url, created_at (date), updated_at (date)

issue 8

  • Default API token authentication mechanism 7
  • Write API in Datasette core 5
  • Table/database that is private due to inherited permissions does not show padlock 3
  • Private database page should show padlock on every table 3
  • check_visibility can now take multiple permissions into account 2
  • Both _local_metadata and _metadata_local? 2
  • NoneType' object has no attribute 'actor' 2
  • API to insert a single record into an existing table 2

user 1

  • simonw 26

author_association 1

  • OWNER · 26 ✖
id html_url issue_url node_id user created_at updated_at ▲ author_association body reactions issue performed_via_github_app
1289712350 https://github.com/simonw/datasette/issues/1851#issuecomment-1289712350 https://api.github.com/repos/simonw/datasette/issues/1851 IC_kwDOBm6k_c5M33Le simonw 9599 2022-10-24T22:28:39Z 2022-10-27T23:18:48Z OWNER

API design: (**UPDATE: this was later changed to POST /db/table/-/insert)

POST /db/table Authorization: Bearer xxx Content-Type: application/json { "row": { "id": 1, "name": "New record" } } Returns: 201 Created { "row": { "id": 1, "name": "New record" } } You can omit optional fields in the input, including the ID field. The returned object will always include all fields - and will even include rowid if your object doesn't have a primary key of its own.

I decided to use "row" as the key in both request and response, to preserve space for other future keys - one that tells you that the table has been created, for example.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
API to insert a single record into an existing table 1421544654  
1289776707 https://github.com/simonw/datasette/issues/1852#issuecomment-1289776707 https://api.github.com/repos/simonw/datasette/issues/1852 IC_kwDOBm6k_c5M4G5D simonw 9599 2022-10-24T23:29:03Z 2022-10-24T23:29:03Z OWNER

I'm going to implement the first version of this token mechanism using permissions that exist already. Right now that's:

https://docs.datasette.io/en/0.62/authentication.html#built-in-permissions

Here are the shortcuts I'll use for them:

  • view-instance - vi
  • view-database - vd
  • view-database-download - vdd
  • view-table - vt
  • view-query - vq
  • execute-sql - es
{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Default API token authentication mechanism 1421552095  
1289775162 https://github.com/simonw/datasette/issues/1852#issuecomment-1289775162 https://api.github.com/repos/simonw/datasette/issues/1852 IC_kwDOBm6k_c5M4Gg6 simonw 9599 2022-10-24T23:27:00Z 2022-10-24T23:27:00Z OWNER

Might be neat for API tokens to be signed with an additional secret than can be rotated independently of DATASETTE_SECRET itself, in order to invalidate all tokens without needing to invalidate logged in users too.

But again, I don't want to implement something like that until I see an actual need for it.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Default API token authentication mechanism 1421552095  
1289774183 https://github.com/simonw/datasette/issues/1852#issuecomment-1289774183 https://api.github.com/repos/simonw/datasette/issues/1852 IC_kwDOBm6k_c5M4GRn simonw 9599 2022-10-24T23:25:52Z 2022-10-24T23:25:52Z OWNER

... also, maybe there should be a UI (perhaps on that page) for resetting the Datasette secret? Useful for emergency invalidation of all tokens.

No, I'm not going to build that unless someone asks for it. Restarting the server with a fresh secret should be easy enough.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Default API token authentication mechanism 1421552095  
1289773634 https://github.com/simonw/datasette/issues/1852#issuecomment-1289773634 https://api.github.com/repos/simonw/datasette/issues/1852 IC_kwDOBm6k_c5M4GJC simonw 9599 2022-10-24T23:25:06Z 2022-10-24T23:25:06Z OWNER

If you start Datasette without providing a DATASETTE_SECRET environment variable of --secret option it creates a random signing secret that only lasts for the lifetime of the server.

This means any signed API tokens you create will stop working if the server restarts.

I think the /-/create-token UI should know when this happens and show a warning message about it, to avoid confusion.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Default API token authentication mechanism 1421552095  
1289766513 https://github.com/simonw/datasette/issues/1852#issuecomment-1289766513 https://api.github.com/repos/simonw/datasette/issues/1852 IC_kwDOBm6k_c5M4EZx simonw 9599 2022-10-24T23:16:00Z 2022-10-24T23:16:00Z OWNER

Here's what that example looks like signed: python from datasette.app import Datasette ds = Datasette() ds.sign('{"t":{"a":["ir","ur","dr"],"d":{"fixtures":["ir","ur","dr"]},"t":{"fixtures":{"searchable":["ir"]}}}}') .eJxTqo5RKolRsgJSiUAqOkYpsyhGSSdGqRRCpQCpWBANUZOWWVFSWpRajFNprQ7cPCS1QF5xamJRckZiUk4qQm9sLRAoAQCC8yph.O0Gaej6-VOLbbtPq7xU6T77jEO0 That's 129 characters.

Note that Datasette doesn't have its own mechanism for signing things for a specific duration yet: https://docs.datasette.io/en/stable/internals.html#sign-value-namespace-default

So I'll need to add a "e": 1666739744 field with the UTC timestamp at which the token should expire.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Default API token authentication mechanism 1421552095  
1289733483 https://github.com/simonw/datasette/issues/1852#issuecomment-1289733483 https://api.github.com/repos/simonw/datasette/issues/1852 IC_kwDOBm6k_c5M38Vr simonw 9599 2022-10-24T22:54:37Z 2022-10-24T23:12:10Z OWNER

Token design concept: json { "t": { "a": ["ir", "ur", "dr"], "d": { "fixtures": ["ir", "ur", "dr"] }, "t": { "fixtures": { "searchable": ["ir"] } } } } That JSON would be minified and signed.

Minified version of the above looks like this (101 characters):

{"t":{"a":["ir","ur","dr"],"d":{"fixtures":["ir","ur","dr"]},"t":{"fixtures":{"searchable":["ir"]}}}}

The "t" key shows this is a token that as a default API key.

"a" means "all" - these are permissions that have been granted on all tables and databases.

"d" means "databases" - this is a way to set permissions for all tables in a specific database.

"t" means "tables" - this lets you set permissions at a finely grained table level.

Then the permissions themselves are two character codes which are shortened versions - so:

  • ir = insert-row
  • ur = update-row
  • dr = delete-row
{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Default API token authentication mechanism 1421552095  
1289718660 https://github.com/simonw/datasette/issues/1852#issuecomment-1289718660 https://api.github.com/repos/simonw/datasette/issues/1852 IC_kwDOBm6k_c5M34uE simonw 9599 2022-10-24T22:35:01Z 2022-10-24T22:35:01Z OWNER

Maybe these tokens can be restricted to specific databases and tables when they are first created?

Since they're signed tokens, I could bundle a bunch of extra stuff in them - this token is allowed to do these permissions against these tables/rows for example.

General wisdom seems to be that 8KB is a sensible maximum length for this kind of token, which is easily long enough to fit in a bunch of database / table / permissions.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Default API token authentication mechanism 1421552095  
1289713513 https://github.com/simonw/datasette/issues/1851#issuecomment-1289713513 https://api.github.com/repos/simonw/datasette/issues/1851 IC_kwDOBm6k_c5M33dp simonw 9599 2022-10-24T22:29:58Z 2022-10-24T22:30:15Z OWNER

Interesting open question: how should validation errors (if any) be returned?

The two forms of validation I can think of at first are:

  • Missing keys which are marked as not null in the schema
  • Keys that do not match to existing columns (if you didn't pass "alter": true, an option I am considering)
{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
API to insert a single record into an existing table 1421544654  
1289707357 https://github.com/simonw/datasette/issues/1850#issuecomment-1289707357 https://api.github.com/repos/simonw/datasette/issues/1850 IC_kwDOBm6k_c5M319d simonw 9599 2022-10-24T22:23:12Z 2022-10-24T22:23:12Z OWNER

Here's the implementation of datasette-auth-tokens: https://github.com/simonw/datasette-auth-tokens/blob/main/datasette_auth_tokens/init.py

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Write API in Datasette core 1421529723  
1289706439 https://github.com/simonw/datasette/issues/1850#issuecomment-1289706439 https://api.github.com/repos/simonw/datasette/issues/1850 IC_kwDOBm6k_c5M31vH simonw 9599 2022-10-24T22:22:17Z 2022-10-24T22:22:17Z OWNER

API authentication will be via Authorization: Bearer XXX request headers.

I'm inclined to add a default token mechanism to Datasette based on tokens that are signed with the DATASETTE_SECRET. Maybe the root user can access /-/create-token which provides a UI for generating a time-limited signed token? Could also have a datasette create-token command for creating such tokens at the command-line.

Plugins can then define alternative ways of creating tokens, such as the existing https://datasette.io/plugins/datasette-auth-tokens plugin.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Write API in Datasette core 1421529723  
1289703432 https://github.com/simonw/datasette/issues/1850#issuecomment-1289703432 https://api.github.com/repos/simonw/datasette/issues/1850 IC_kwDOBm6k_c5M31AI simonw 9599 2022-10-24T22:19:48Z 2022-10-24T22:19:48Z OWNER

It may turn out that it makes sense to also add a UI for these actions as part of this project. That's still to be determined.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Write API in Datasette core 1421529723  
1289702146 https://github.com/simonw/datasette/issues/1850#issuecomment-1289702146 https://api.github.com/repos/simonw/datasette/issues/1850 IC_kwDOBm6k_c5M30sC simonw 9599 2022-10-24T22:19:04Z 2022-10-24T22:19:04Z OWNER

This is going to need a whole bunch of new permissions.

To review: the existing set of permissions are listed here: https://docs.datasette.io/en/0.62/authentication.html#built-in-permissions

  • view-instance
  • view-database
  • view-database-download
  • view-table
  • view-query
  • execute-sql
  • permissions-debug
  • debug-menu

I'm going to reuse database terminology for the new permissions. So first draft of those is:

  • insert-row
  • update-row
  • delete-row
  • create-table
  • drop-table
  • alter-table
{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Write API in Datasette core 1421529723  
1289696171 https://github.com/simonw/datasette/issues/1850#issuecomment-1289696171 https://api.github.com/repos/simonw/datasette/issues/1850 IC_kwDOBm6k_c5M3zOr simonw 9599 2022-10-24T22:15:57Z 2022-10-24T22:15:57Z OWNER

I'm going to treat this as a bit of a research spike, at least until I like the direction it is going enough to commit to it.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Write API in Datasette core 1421529723  
1288384907 https://github.com/simonw/datasette/issues/1849#issuecomment-1288384907 https://api.github.com/repos/simonw/datasette/issues/1849 IC_kwDOBm6k_c5MyzGL simonw 9599 2022-10-24T04:04:02Z 2022-10-24T04:04:02Z OWNER

Refs: - #1831

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
NoneType' object has no attribute 'actor' 1420174670  
1288384098 https://github.com/simonw/datasette/issues/1849#issuecomment-1288384098 https://api.github.com/repos/simonw/datasette/issues/1849 IC_kwDOBm6k_c5Myy5i simonw 9599 2022-10-24T04:03:09Z 2022-10-24T04:03:09Z OWNER

Looks like the new breadcrumbs code can't handle the case where request is None.

Need a test that demonstrates this too.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
NoneType' object has no attribute 'actor' 1420174670  
1288340476 https://github.com/simonw/datasette/issues/1848#issuecomment-1288340476 https://api.github.com/repos/simonw/datasette/issues/1848 IC_kwDOBm6k_c5MyoP8 simonw 9599 2022-10-24T02:50:29Z 2022-10-24T02:50:29Z OWNER

https://latest.datasette.io/_internal now looks like this:

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Private database page should show padlock on every table 1420090659  
1288330238 https://github.com/simonw/datasette/issues/1848#issuecomment-1288330238 https://api.github.com/repos/simonw/datasette/issues/1848 IC_kwDOBm6k_c5Mylv- simonw 9599 2022-10-24T02:34:41Z 2022-10-24T02:34:41Z OWNER

Tested my fix with this metadata.yml: yaml databases: fixtures: allow: id: root tables: 123_starts_with_digits: allow: true Signed in as root I saw this - showing that the 123_starts_with_digits table is public:

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Private database page should show padlock on every table 1420090659  
1288327467 https://github.com/simonw/datasette/issues/1848#issuecomment-1288327467 https://api.github.com/repos/simonw/datasette/issues/1848 IC_kwDOBm6k_c5MylEr simonw 9599 2022-10-24T02:30:48Z 2022-10-24T02:31:04Z OWNER

Here's the code at fault: https://github.com/simonw/datasette/blob/78dad236df730212aa7172f885fd8ec575f0d3ad/datasette/views/database.py#L67-L116

Those checks aren't doing the new cascading permissions thing added in #1829 which means they can't tell that an anonymous user would not be able to se those tbles and queries and views.

Should do something like this instead:

python view_visible, view_private = await self.ds.check_visibility( request.actor, permissions=[ ("view-table", (database, view_name)), ("view-database", database), "view-instance", ], )

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Private database page should show padlock on every table 1420090659  
1288321630 https://github.com/simonw/datasette/issues/1829#issuecomment-1288321630 https://api.github.com/repos/simonw/datasette/issues/1829 IC_kwDOBm6k_c5Myjpe simonw 9599 2022-10-24T02:22:49Z 2022-10-24T02:23:46Z OWNER

Visit https://latest.datasette.io/login-as-root and then:

https://latest.datasette.io/

https://latest.datasette.io/_internal/columns

https://latest.datasette.io/_internal/columns/_internal,columns,cid

https://latest.datasette.io/_internal/from_hook

That's all as it should be.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Table/database that is private due to inherited permissions does not show padlock 1396948693  
1288320411 https://github.com/simonw/datasette/issues/1829#issuecomment-1288320411 https://api.github.com/repos/simonw/datasette/issues/1829 IC_kwDOBm6k_c5MyjWb simonw 9599 2022-10-24T02:21:19Z 2022-10-24T02:21:19Z OWNER

Updated docs for check_visibility(): https://docs.datasette.io/en/latest/internals.html#await-check-visibility-actor-action-none-resource-none-permissions-none

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Table/database that is private due to inherited permissions does not show padlock 1396948693  
1288311852 https://github.com/simonw/datasette/pull/1842#issuecomment-1288311852 https://api.github.com/repos/simonw/datasette/issues/1842 IC_kwDOBm6k_c5MyhQs simonw 9599 2022-10-24T02:11:12Z 2022-10-24T02:11:12Z OWNER

I'm going to construct a metadata.yml which makes various databases and tables visible or invisible, then browse them using the root user.

block-instance.yml:

yaml allow: id: root block-database.yml: yaml databases: fixtures: allow: id: root block-table.yml: yaml databases: fixtures: tables: searchable: allow: id: root block-query.yml: yaml databases: fixtures: queries: two: sql: select 1 + 1 allow: id: root https://gist.github.com/simonw/2d007ebe43de46d44499c77a2a291756 - checkout that Gist to get all four.

I manually tested all four scenarios with root and non-root users and confirmed that they worked correctly and padlocks were shown in the right places.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
check_visibility can now take multiple permissions into account 1408561039  
1288308945 https://github.com/simonw/datasette/issues/1829#issuecomment-1288308945 https://api.github.com/repos/simonw/datasette/issues/1829 IC_kwDOBm6k_c5MygjR simonw 9599 2022-10-24T02:07:50Z 2022-10-24T02:07:50Z OWNER

Useful test: if you sign in as root to https://latest.datasette.io/_internal/columns/_internal,columns,database_name you can see there's no padlock icon on that page or on https://latest.datasette.io/_internal/columns - fixing this bug should fix that.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Table/database that is private due to inherited permissions does not show padlock 1396948693  
1288304224 https://github.com/simonw/datasette/pull/1842#issuecomment-1288304224 https://api.github.com/repos/simonw/datasette/issues/1842 IC_kwDOBm6k_c5MyfZg simonw 9599 2022-10-24T02:00:14Z 2022-10-24T02:00:14Z OWNER

I need to do one last round of manual testing before I merge this.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
check_visibility can now take multiple permissions into account 1408561039  
1288296235 https://github.com/simonw/datasette/issues/1847#issuecomment-1288296235 https://api.github.com/repos/simonw/datasette/issues/1847 IC_kwDOBm6k_c5Mydcr simonw 9599 2022-10-24T01:45:56Z 2022-10-24T01:45:56Z OWNER

This bug here: https://github.com/simonw/datasette/blob/85d5d2762c13d2b5a8bd9c5ec81c77fe6577121f/tests/test_permissions.py#L485

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Both _local_metadata and _metadata_local? 1420055377  
1288295713 https://github.com/simonw/datasette/issues/1847#issuecomment-1288295713 https://api.github.com/repos/simonw/datasette/issues/1847 IC_kwDOBm6k_c5MydUh simonw 9599 2022-10-24T01:45:13Z 2022-10-24T01:45:13Z OWNER

Turns out that was a bug I had introduced while working on that test, and it was the reason I was blocked on finishing work on: - #1829

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Both _local_metadata and _metadata_local? 1420055377  

Advanced export

JSON shape: default, array, newline-delimited, object

CSV options:

CREATE TABLE [issue_comments] (
   [html_url] TEXT,
   [issue_url] TEXT,
   [id] INTEGER PRIMARY KEY,
   [node_id] TEXT,
   [user] INTEGER REFERENCES [users]([id]),
   [created_at] TEXT,
   [updated_at] TEXT,
   [author_association] TEXT,
   [body] TEXT,
   [reactions] TEXT,
   [issue] INTEGER REFERENCES [issues]([id])
, [performed_via_github_app] TEXT);
CREATE INDEX [idx_issue_comments_issue]
                ON [issue_comments] ([issue]);
CREATE INDEX [idx_issue_comments_user]
                ON [issue_comments] ([user]);
Powered by Datasette · Queries took 591.708ms · About: github-to-sqlite