simonw / datasette

An open source multi-tool for exploring and publishing data
https://datasette.io
Apache License 2.0
9.59k stars 691 forks source link

Method for datasette.client() to forward on authentication #1020

Open simonw opened 4 years ago

simonw commented 4 years ago

I stumbled into this while working on Dogsheep Beta: the requests it re-dispatched through TableView did not carry authentication cookies, and since this was against a private instance they were thus denied.

https://github.com/dogsheep/dogsheep-beta/blob/bed9df2b3ef68189e2e445427721a28f4e9b4887/dogsheep_beta/__init__.py#L223-L231

This made me think that datasette.client.get() (which Dogsheep Beta will start using shortly) could benefit from some kind of utility mechanism for passing through the cookies and general authenticated state from the current request.

simonw commented 4 years ago

One option: add an optional request=... parameter which can be passed the current request, and will use that to populate the mock request with the exception of the bits that are passed explicitly (like the path):

response = await datasette.client.get("/db/table.json", request=request)
simonw commented 4 years ago

Maybe these internal requests should have some kind of flag that lets the underlying code tell that it's being called internally.

One option: add a x-internal: 1 request header - and ensure that any requests from outside Datasette have that header stripped.

simonw commented 4 years ago

I should also verify (and probably unit-test) that things like the ?_trace=1 mechanism work across the internal request boundary.

/-/permissions appears to work across this boundary, but again a test would be useful confirmation.

simonw commented 4 years ago

It turns out this works just fine:

response = await datasette.client.get(path, cookies=request.cookies)

So I don't need a mechanism for this. I'm going to add this to the documentation instead.

simonw commented 4 years ago

... unless I want to support authentication mechanisms that work based on incoming IP address instead, in which case there's an argument for copying more over from the incoming request.

Tailscale is a good example of a system where authentication based on IP address can actually work well, so this is worth doing. Also, there might be authentication mechanisms which work by setting a custom header on the incoming request (not to mention the Authorization header).

simonw commented 4 years ago

I'm having trouble coming up with the syntax for this. Here's one option:

response = await datasette.client.get(path, request=request)

But this feels confusing to me. We're not using the request= argument as a request - we're using it as a source of some default request values (the cookies and incoming headers, but not the path).

We're essentially combining that request with the other arguments passed to .get().