issue_comments

40 rows where issue = 582526961 sorted by updated_at descending

View and edit SQL

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

user

author_association

issue

  • Authentication (and permissions) as a core concept · 40
id html_url issue_url node_id user created_at updated_at ▲ author_association body reactions issue
640108763 https://github.com/simonw/datasette/issues/699#issuecomment-640108763 https://api.github.com/repos/simonw/datasette/issues/699 MDEyOklzc3VlQ29tbWVudDY0MDEwODc2Mw== simonw 9599 2020-06-06T19:42:11Z 2020-06-06T19:42:11Z OWNER

I landed canned query writes. This feature can now be considered complete: https://datasette.readthedocs.io/en/latest/authentication.html

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Authentication (and permissions) as a core concept 582526961
640106668 https://github.com/simonw/datasette/issues/699#issuecomment-640106668 https://api.github.com/repos/simonw/datasette/issues/699 MDEyOklzc3VlQ29tbWVudDY0MDEwNjY2OA== simonw 9599 2020-06-06T19:22:36Z 2020-06-06T19:22:36Z OWNER

The canned queries feature is gaining permissions support in #800.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Authentication (and permissions) as a core concept 582526961
637819025 https://github.com/simonw/datasette/issues/699#issuecomment-637819025 https://api.github.com/repos/simonw/datasette/issues/699 MDEyOklzc3VlQ29tbWVudDYzNzgxOTAyNQ== simonw 9599 2020-06-02T21:34:31Z 2020-06-02T21:34:31Z OWNER

I can close this issue once I've expanded out this page of documentation https://datasette.readthedocs.io/en/latest/authentication.html - and published at least one plugin and/or feature that takes advantage of this new mechanism.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Authentication (and permissions) as a core concept 582526961
636576603 https://github.com/simonw/datasette/issues/699#issuecomment-636576603 https://api.github.com/repos/simonw/datasette/issues/699 MDEyOklzc3VlQ29tbWVudDYzNjU3NjYwMw== simonw 9599 2020-06-01T02:13:26Z 2020-06-01T03:13:31Z OWNER

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.

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.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Authentication (and permissions) as a core concept 582526961
636576252 https://github.com/simonw/datasette/issues/699#issuecomment-636576252 https://api.github.com/repos/simonw/datasette/issues/699 MDEyOklzc3VlQ29tbWVudDYzNjU3NjI1Mg== simonw 9599 2020-06-01T02:11:40Z 2020-06-01T02:11:40Z OWNER

Plugin idea: datasette-allow-all - really simple plugin which just says "yes" to every permission check.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Authentication (and permissions) as a core concept 582526961
636566616 https://github.com/simonw/datasette/issues/699#issuecomment-636566616 https://api.github.com/repos/simonw/datasette/issues/699 MDEyOklzc3VlQ29tbWVudDYzNjU2NjYxNg== simonw 9599 2020-06-01T01:23:48Z 2020-06-01T01:23:48Z OWNER

https://latest.datasette.io/-/actor is now live (it returns null because there's no current way to sign into the latest.datasette.io site - not even with a fake ds_actor cookie because there's no way to know what that site's random secret is).

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Authentication (and permissions) as a core concept 582526961
636566433 https://github.com/simonw/datasette/issues/699#issuecomment-636566433 https://api.github.com/repos/simonw/datasette/issues/699 MDEyOklzc3VlQ29tbWVudDYzNjU2NjQzMw== simonw 9599 2020-06-01T01:22:59Z 2020-06-01T01:22:59Z OWNER

Some next steps:

  • Try out a branch of datasette-auth-github that builds on these new plugin hooks
  • Build a datasette-api-tokens plugin which implements Authorization: bearer xxx token support for API access
  • Maybe prototype up a datasette-user-accounts plugin which supports username/password accounts and allows an admin user to create/delete them
  • Do more work on writable canned queries in #698 and see what they look like if they take advantage of the permissions hook (to restrict some to only allowing authenticated users)
{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Authentication (and permissions) as a core concept 582526961
636565610 https://github.com/simonw/datasette/issues/699#issuecomment-636565610 https://api.github.com/repos/simonw/datasette/issues/699 MDEyOklzc3VlQ29tbWVudDYzNjU2NTYxMA== simonw 9599 2020-06-01T01:19:45Z 2020-06-01T01:19:45Z OWNER

I rebased in #783 so all of this is on master now.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Authentication (and permissions) as a core concept 582526961
636562999 https://github.com/simonw/datasette/issues/699#issuecomment-636562999 https://api.github.com/repos/simonw/datasette/issues/699 MDEyOklzc3VlQ29tbWVudDYzNjU2Mjk5OQ== simonw 9599 2020-06-01T01:09:47Z 2020-06-01T01:09:47Z OWNER

I should add an entire page to the documentation describing Datasette authentication.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Authentication (and permissions) as a core concept 582526961
636562658 https://github.com/simonw/datasette/issues/699#issuecomment-636562658 https://api.github.com/repos/simonw/datasette/issues/699 MDEyOklzc3VlQ29tbWVudDYzNjU2MjY1OA== simonw 9599 2020-06-01T01:08:20Z 2020-06-01T01:08:54Z OWNER

OK, the implementation in PR #783 is in a good state now - it implements the new plugin hooks with tests and documentation, plus it implements this:

$ datasette . --root
http://127.0.0.1:8001/-/auth-token?token=3ca9ee460a6451142389351d19b147bce27d2a785dfb6b5a74f82211be1ede49
...

That URL, when clicked, will set a cookie for the {"id": "root"} user. The cookie is respected and used to populate scope["actor"].

I'm going to merge that pull request and continue working on this stuff on master.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Authentication (and permissions) as a core concept 582526961
636510761 https://github.com/simonw/datasette/issues/699#issuecomment-636510761 https://api.github.com/repos/simonw/datasette/issues/699 MDEyOklzc3VlQ29tbWVudDYzNjUxMDc2MQ== simonw 9599 2020-05-31T18:38:30Z 2020-05-31T18:38:30Z OWNER

I quite like root - it supports the idea that best practice is to NOT do things as the root account, but to use a plugin to set up separate accounts for different purposes.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Authentication (and permissions) as a core concept 582526961
636510647 https://github.com/simonw/datasette/issues/699#issuecomment-636510647 https://api.github.com/repos/simonw/datasette/issues/699 MDEyOklzc3VlQ29tbWVudDYzNjUxMDY0Nw== simonw 9599 2020-05-31T18:37:39Z 2020-05-31T18:37:39Z OWNER

Maybe the default single account should be called something other than admin? The problem with admin is that it sounds like more of a role - in larger installations one can expect multiple admins. root may be better since there's clearly only one root account. Bit of a technical term though.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Authentication (and permissions) as a core concept 582526961
636510398 https://github.com/simonw/datasette/issues/699#issuecomment-636510398 https://api.github.com/repos/simonw/datasette/issues/699 MDEyOklzc3VlQ29tbWVudDYzNjUxMDM5OA== simonw 9599 2020-05-31T18:35:57Z 2020-05-31T18:36:05Z OWNER

Again I will use exploratory prototyping to inform a decision on the minimum subset design for the actor dictionary.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Authentication (and permissions) as a core concept 582526961
636510303 https://github.com/simonw/datasette/issues/699#issuecomment-636510303 https://api.github.com/repos/simonw/datasette/issues/699 MDEyOklzc3VlQ29tbWVudDYzNjUxMDMwMw== simonw 9599 2020-05-31T18:35:17Z 2020-05-31T18:35:17Z OWNER

Keeping the structure of the actor dictionary completely undefined doesn't make sense if Datasette is going to ship with a default authentication mechanism for admin users.

I'm going to define a small set of required keys for the actor dictionary, and enforce them in code.

But which keys? I feel I need a unique key representing the identity of the actor, plus a key that can be displayed in the "You are logged in as X" navigation. Maybe these are the same key?

So the single required key could be id. Problem is: is that a string or an integer? Some use-cases may call for an integer, which matches to how SQLite auto incrementing primary keys work. admin is a string.

Maybe id is required, name is optional - but if name is present then the "You are logged in as..." uses that in preference to id. id has to be a string, and if you want to store integer IDs in your database you need to remember to convert them to a string in your actor_from_request implementation.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Authentication (and permissions) as a core concept 582526961
636498913 https://github.com/simonw/datasette/issues/699#issuecomment-636498913 https://api.github.com/repos/simonw/datasette/issues/699 MDEyOklzc3VlQ29tbWVudDYzNjQ5ODkxMw== simonw 9599 2020-05-31T17:04:50Z 2020-05-31T17:06:40Z OWNER

This also means some writable canned queries can allow writes from unauthenticated users (for stuff like feedback forms), while others can require an authenticated user - all with core Datasette without any plugins needed.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Authentication (and permissions) as a core concept 582526961
636499075 https://github.com/simonw/datasette/issues/699#issuecomment-636499075 https://api.github.com/repos/simonw/datasette/issues/699 MDEyOklzc3VlQ29tbWVudDYzNjQ5OTA3NQ== simonw 9599 2020-05-31T17:06:09Z 2020-05-31T17:06:09Z OWNER

I believe that this plugin hook design is flexible enough that role-based permissions could be built on top of it as a separate plugin.

Would be good to check that with a proof of concept though.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Authentication (and permissions) as a core concept 582526961
636498770 https://github.com/simonw/datasette/issues/699#issuecomment-636498770 https://api.github.com/repos/simonw/datasette/issues/699 MDEyOklzc3VlQ29tbWVudDYzNjQ5ODc3MA== simonw 9599 2020-05-31T17:03:38Z 2020-05-31T17:03:38Z OWNER

I'm going to draw the line here: default Datasette supports authentication but only for a single user account ("admin"). Plugins can then add support for multiple user accounts, social auth, SSO etc.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Authentication (and permissions) as a core concept 582526961
636495124 https://github.com/simonw/datasette/issues/699#issuecomment-636495124 https://api.github.com/repos/simonw/datasette/issues/699 MDEyOklzc3VlQ29tbWVudDYzNjQ5NTEyNA== simonw 9599 2020-05-31T16:36:08Z 2020-05-31T16:36:08Z OWNER

HTTP Basic auth would be a good default option. No need to build a custom login UI for it.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Authentication (and permissions) as a core concept 582526961
636495005 https://github.com/simonw/datasette/issues/699#issuecomment-636495005 https://api.github.com/repos/simonw/datasette/issues/699 MDEyOklzc3VlQ29tbWVudDYzNjQ5NTAwNQ== simonw 9599 2020-05-31T16:35:10Z 2020-05-31T16:35:26Z OWNER

I think I want to keep full username/password authentication against a database table as a plugin. I'll experiment with Jupyter-style URLs as a starting point.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Authentication (and permissions) as a core concept 582526961
636494374 https://github.com/simonw/datasette/issues/699#issuecomment-636494374 https://api.github.com/repos/simonw/datasette/issues/699 MDEyOklzc3VlQ29tbWVudDYzNjQ5NDM3NA== simonw 9599 2020-05-31T16:29:48Z 2020-05-31T16:29:48Z OWNER

If Datasette were to support authentication out-of-the-box, without plugins (which makes more sense with writable canned queries, #698) what would that look like?

Some options:

  • Jupyter notebook style: output a magic URL on the console with a one-time token to authenticate the user as an "admin"
  • Really simple password authentication - via an environment variable perhaps?
  • SQL based authentication: I was going to do this as a plugin, but maybe it should be default? A way of configuring a SQL query which can be used to authenticate a user based on their username and password.
{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Authentication (and permissions) as a core concept 582526961
636395263 https://github.com/simonw/datasette/issues/699#issuecomment-636395263 https://api.github.com/repos/simonw/datasette/issues/699 MDEyOklzc3VlQ29tbWVudDYzNjM5NTI2Mw== simonw 9599 2020-05-30T22:54:09Z 2020-05-30T22:54:09Z OWNER

Idea: add a /-/actor.json special page which JSON dumps out the current request.scope["actor"] - so you can easily test how your request has been authenticated.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Authentication (and permissions) as a core concept 582526961
636393204 https://github.com/simonw/datasette/issues/699#issuecomment-636393204 https://api.github.com/repos/simonw/datasette/issues/699 MDEyOklzc3VlQ29tbWVudDYzNjM5MzIwNA== simonw 9599 2020-05-30T22:29:44Z 2020-05-30T22:30:15Z OWNER

Robust testing of permissions is really important. I should think about utilities I may be able to add to Datasette's unit testing tools that make it as easy as possible to confirm which permission checks were carried out on a specific HTTP request.

That way I can set a good example that any Datasette plugin which makes permission checks can follow.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Authentication (and permissions) as a core concept 582526961
636392850 https://github.com/simonw/datasette/issues/699#issuecomment-636392850 https://api.github.com/repos/simonw/datasette/issues/699 MDEyOklzc3VlQ29tbWVudDYzNjM5Mjg1MA== simonw 9599 2020-05-30T22:25:19Z 2020-05-30T22:25:19Z OWNER

The branch is now usable! Next step: write some experimental plugins that exercise some real authentication use-cases with it.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Authentication (and permissions) as a core concept 582526961
636391331 https://github.com/simonw/datasette/issues/699#issuecomment-636391331 https://api.github.com/repos/simonw/datasette/issues/699 MDEyOklzc3VlQ29tbWVudDYzNjM5MTMzMQ== simonw 9599 2020-05-30T22:08:21Z 2020-05-30T22:08:21Z OWNER

I'm going to add an awaitable utility method to the Datasette class for checking permissions:

await datasette.permission_allowed(actor, action, resource_type, resource_identifier)

The second two arguments will be optional.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Authentication (and permissions) as a core concept 582526961
636388288 https://github.com/simonw/datasette/issues/699#issuecomment-636388288 https://api.github.com/repos/simonw/datasette/issues/699 MDEyOklzc3VlQ29tbWVudDYzNjM4ODI4OA== simonw 9599 2020-05-30T21:34:50Z 2020-05-30T21:34:50Z OWNER

Debugging permissions is going to be important. Optional tooling that supports the following would be useful:

  • Log every check to permission_allowed to the console - optionally with tracebacks showing where in the code the check was made
  • Log every check to the https://latest.datasette.io/?_trace=1 output
  • A tool that shows you exactly what permissions the current authenticated user/entity has
  • A tool showing all available permissions

That last one is tricky if permissions are just strings that might be passed to permission_allowed - so maybe there needs to be a plugin hook that lets plugins register their permissions, such that they can be introspected later on? A register_permission_actions() hook that returns a list of permission action strings (or objects of some sort) perhaps.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Authentication (and permissions) as a core concept 582526961
636379067 https://github.com/simonw/datasette/issues/699#issuecomment-636379067 https://api.github.com/repos/simonw/datasette/issues/699 MDEyOklzc3VlQ29tbWVudDYzNjM3OTA2Nw== simonw 9599 2020-05-30T20:12:47Z 2020-05-30T20:40:42Z OWNER

I could bake some permission checks into default Datasette, which are all treated as allow by default but can then be locked down by plugins. Maybe the following:

permission_allowed(request.actor, "execute-sql", "database", "name-of-database")

Checks that current user can execute arbitrary SQL queries against a specific database (or use the ?_where= feature). Equivalent to current allow_sql setting.

permission_allowed(request.actor, "download-database", "database", "name-of-database")

Can the user download the database file? Like allow_download.

Maybe one for allow_csv_stream too.

Having a permission check (defaulting to True) on every single "view" would be useful:

  • view_index
  • view_database
  • view_table
  • view_row
  • view_query
  • view_special (for /-/versions and so on)
{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Authentication (and permissions) as a core concept 582526961
636381732 https://github.com/simonw/datasette/issues/699#issuecomment-636381732 https://api.github.com/repos/simonw/datasette/issues/699 MDEyOklzc3VlQ29tbWVudDYzNjM4MTczMg== simonw 9599 2020-05-30T20:32:11Z 2020-05-30T20:39:11Z OWNER

I started sketching this out in the authentication branch. Here's the documentation so far: https://github.com/simonw/datasette/blob/8871c20/docs/plugins.rst#actor_from_requestdatasette-request

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Authentication (and permissions) as a core concept 582526961
636376209 https://github.com/simonw/datasette/issues/699#issuecomment-636376209 https://api.github.com/repos/simonw/datasette/issues/699 MDEyOklzc3VlQ29tbWVudDYzNjM3NjIwOQ== simonw 9599 2020-05-30T19:53:28Z 2020-05-30T20:09:10Z OWNER

I think there are two hooks here:

actor_from_request(datasette, request) - returns None or a dictionary.

  • datasette is a Datasette instance - useful for things like reading plugin configuration or executing queries
  • request is a Request object - which means ASGI scope can be accessed as request.scope

A non-None value means the request is authenticated in some way. The shape of that dictionary is entirely undefined.

The second hook is for checking permissions. It can look something like this:

permission_allowed(actor, action, resource_type, resource_identifier)

  • actor = the dictionary that was returned by actor_from_scope
  • action = a string representing the action to be performed, e.g. edit-schema
  • resource_type = a string representing the type of resource being acted on, e.g. table
  • resource_identifier = a string (or maybe tuple?) representing the specific resource, e.g. the table name

I don't know if Datasette should provide default implementations of these hooks. It may be that leaving them completely up to plugins is the way to go.

I think I need to prototype this quickly to start feeling for how well it might work.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Authentication (and permissions) as a core concept 582526961
636376893 https://github.com/simonw/datasette/issues/699#issuecomment-636376893 https://api.github.com/repos/simonw/datasette/issues/699 MDEyOklzc3VlQ29tbWVudDYzNjM3Njg5Mw== simonw 9599 2020-05-30T19:57:54Z 2020-05-30T20:09:05Z OWNER

auth_from_scope(datasette, scope) needs to be able to return an awaitable which is then awaited - so it can execute database queries.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Authentication (and permissions) as a core concept 582526961
636376974 https://github.com/simonw/datasette/issues/699#issuecomment-636376974 https://api.github.com/repos/simonw/datasette/issues/699 MDEyOklzc3VlQ29tbWVudDYzNjM3Njk3NA== simonw 9599 2020-05-30T19:58:40Z 2020-05-30T20:08:59Z OWNER

Maybe call that actor_from_request(datasette, request) instead.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Authentication (and permissions) as a core concept 582526961
636378228 https://github.com/simonw/datasette/issues/699#issuecomment-636378228 https://api.github.com/repos/simonw/datasette/issues/699 MDEyOklzc3VlQ29tbWVudDYzNjM3ODIyOA== simonw 9599 2020-05-30T20:07:25Z 2020-05-30T20:07:25Z OWNER

I like "actor" better than "entity" to mean "the user or API key that is authenticated for this request".

I'm going to use "resource" instead of "subject" - updating the design comment again.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Authentication (and permissions) as a core concept 582526961
636378121 https://github.com/simonw/datasette/issues/699#issuecomment-636378121 https://api.github.com/repos/simonw/datasette/issues/699 MDEyOklzc3VlQ29tbWVudDYzNjM3ODEyMQ== simonw 9599 2020-05-30T20:06:47Z 2020-05-30T20:06:47Z OWNER

In AWS IAM world the following terminology is used: https://aws.amazon.com/iam/features/manage-permissions/

Permissions are granted to IAM entities (users, groups, and roles) [...]

To assign permissions to a user, group, role, or resource, you create a policy that lets you specify:

  • Actions – Which AWS service actions you allow. For example, you might allow a user to call the Amazon S3 ListBucket action. Any actions that you don't explicitly allow are denied.
  • Resources – Which AWS resources you allow the action on. For example, what Amazon S3 buckets will you allow the user to perform the ListBucket action on? Users cannot access any resources that you do not explicitly grant permissions to.
  • Effect – Whether to allow or deny access. Because access is denied by default, you typically write policies where the effect is to allow.
  • Conditions – Which conditions must be present for the policy to take effect. For example, you might allow access only to the specific S3 buckets if the user is connecting from a specific IP range or has used multi-factor authentication at login.
{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Authentication (and permissions) as a core concept 582526961
636377755 https://github.com/simonw/datasette/issues/699#issuecomment-636377755 https://api.github.com/repos/simonw/datasette/issues/699 MDEyOklzc3VlQ29tbWVudDYzNjM3Nzc1NQ== simonw 9599 2020-05-30T20:04:23Z 2020-05-30T20:04:23Z OWNER

My usage of the term subject here to mean "the thing I am checking I have permission to interact with, e.g. a database table" may be misleading. https://stackoverflow.com/questions/4989063/what-is-the-meaning-and-difference-between-subject-user-and-principal for example shows that JAAS (Java Authentication and Authorization Service) defines subject as "The purpose of the Subject is to represent the authenticated user".

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Authentication (and permissions) as a core concept 582526961
636377235 https://github.com/simonw/datasette/issues/699#issuecomment-636377235 https://api.github.com/repos/simonw/datasette/issues/699 MDEyOklzc3VlQ29tbWVudDYzNjM3NzIzNQ== simonw 9599 2020-05-30T20:00:42Z 2020-05-30T20:01:35Z OWNER

I'm changing auth to actor and updating the above design comment.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Authentication (and permissions) as a core concept 582526961
626991001 https://github.com/simonw/datasette/issues/699#issuecomment-626991001 https://api.github.com/repos/simonw/datasette/issues/699 MDEyOklzc3VlQ29tbWVudDYyNjk5MTAwMQ== zeluspudding 8431341 2020-05-11T22:06:34Z 2020-05-11T22:06:34Z NONE

Very nice! Thank you for sharing that :+1: :) Will try it out!

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Authentication (and permissions) as a core concept 582526961
626945281 https://github.com/simonw/datasette/issues/699#issuecomment-626945281 https://api.github.com/repos/simonw/datasette/issues/699 MDEyOklzc3VlQ29tbWVudDYyNjk0NTI4MQ== simonw 9599 2020-05-11T20:32:33Z 2020-05-11T20:32:33Z OWNER

I did have a bit of trouble with this one-off plugin getting it to load in the correct order - since I need authentication to work if EITHER the one-off plugin spots a token or my datasette-auth-github plugin authenticates the user.

That's why I want authentication as a core Datasette concept - so plugins like these can easily play together in a predictable manner.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Authentication (and permissions) as a core concept 582526961
626943809 https://github.com/simonw/datasette/issues/699#issuecomment-626943809 https://api.github.com/repos/simonw/datasette/issues/699 MDEyOklzc3VlQ29tbWVudDYyNjk0MzgwOQ== simonw 9599 2020-05-11T20:30:07Z 2020-05-11T20:31:18Z OWNER

I implemented bearer tokens in a private project of mine as a one-off plugin. I'm going to extract that out into a installable plugin soon. For the moment, my plugins/token_auth.py file looks like this:

from datasette import hookimpl
import secrets


class TokenAuth:
    def __init__(
        self, app, secret, auth,
    ):
        self.app = app
        self.secret = secret
        self.auth = auth

    async def __call__(self, scope, receive, send):
        if scope.get("type") != "http":
            return await self.app(scope, receive, send)

        authorization = dict(scope.get("headers") or {}).get(b"authorization") or b""
        expected = "Bearer {}".format(self.secret).encode("utf8")

        if secrets.compare_digest(authorization, expected):
            scope = dict(scope, auth=self.auth)

        return await self.app(scope, receive, send)


@hookimpl(trylast=True)
def asgi_wrapper(datasette):
    config = datasette.plugin_config("token-auth") or {}
    secret = config.get("secret")
    auth = config.get("auth")

    def wrap_with_asgi_auth(app):
        return TokenAuth(app, secret=secret, auth=auth,)

    return wrap_with_asgi_auth

Then I have the following in metadata.json:

{
    "plugins": {
        "token-auth": {
            "auth": {
                "name": "token-bot"
            },
            "secret": {
                "$env": "TOKEN_SECRET"
            }
        }
    }
}

And a TOKEN_SECRET environment variable.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Authentication (and permissions) as a core concept 582526961
626807487 https://github.com/simonw/datasette/issues/699#issuecomment-626807487 https://api.github.com/repos/simonw/datasette/issues/699 MDEyOklzc3VlQ29tbWVudDYyNjgwNzQ4Nw== zeluspudding 8431341 2020-05-11T16:23:57Z 2020-05-11T16:24:59Z NONE

Authorization: bearer xxx auth for API keys is a plus plus for me. Looked into just adding this into your Flask logic but learned this project doesn't use flask. Interesting 🤔

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Authentication (and permissions) as a core concept 582526961
611880250 https://github.com/simonw/datasette/issues/699#issuecomment-611880250 https://api.github.com/repos/simonw/datasette/issues/699 MDEyOklzc3VlQ29tbWVudDYxMTg4MDI1MA== simonw 9599 2020-04-10T05:08:54Z 2020-04-10T05:08:54Z OWNER

So maybe this is all handled by plugin hooks?

auth_from_scope(datasette, scope) would be the main one - for deciding if a user should be authenticated based on data from the scope.

How would a permissions hook work though?

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Authentication (and permissions) as a core concept 582526961
611879821 https://github.com/simonw/datasette/issues/699#issuecomment-611879821 https://api.github.com/repos/simonw/datasette/issues/699 MDEyOklzc3VlQ29tbWVudDYxMTg3OTgyMQ== simonw 9599 2020-04-10T05:06:56Z 2020-04-10T05:06:56Z OWNER

Another problem this would solve: if you want multiple authentication mechanisms - GitHub auth for users, Authorization: bearer xxx auth for API keys - the order in which they run might end up mattering.

I dealt with this a bit in https://github.com/simonw/datasette-auth-github/issues/59

But having an authentication plugin hook - where playing get to decide if a user should be authenticated based on the incoming ASGI scopes - would be neater.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Authentication (and permissions) as a core concept 582526961

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])
);
CREATE INDEX [idx_issue_comments_issue]
                ON [issue_comments] ([issue]);
CREATE INDEX [idx_issue_comments_user]
                ON [issue_comments] ([user]);
Powered by Datasette · Query took 28.139ms · About: github-to-sqlite