datasette / datasette-enrichments

Tools for running enrichments against data stored in Datasette
https://enrichments.datasette.io
Apache License 2.0
16 stars 0 forks source link

Doesn't work on authenticated Datasette instances #13

Closed simonw closed 7 months ago

simonw commented 7 months ago

Tried this plugin on Datasette Cloud and got this error:

CleanShot 2023-11-16 at 12 19 20@2x

The problem is here:

https://github.com/datasette/datasette-enrichments/blob/96cbf510115b3ab738360f88a6e7e8070575a171/datasette_enrichments/views.py#L38-L42

https://github.com/datasette/datasette-enrichments/blob/96cbf510115b3ab738360f88a6e7e8070575a171/datasette_enrichments/views.py#L87-L91

https://github.com/datasette/datasette-enrichments/blob/96cbf510115b3ab738360f88a6e7e8070575a171/datasette_enrichments/__init__.py#L79C1-L79C1

Those calls to datasette.client.get() don't attempt to pass through authentication information, so a Datasette instance with extra authentication plugins installed refuses them.

simonw commented 7 months ago

Options:

  1. Pass through cookies and potentially other authentication headers too. I'll try this first.
  2. Modify Datasette itself to make it easier to call datasette.client.get() etc while passing through authentication credentials.
simonw commented 7 months ago

I wrote this function:

async def get_with_auth(datasette, request, *args, **kwargs):
    cookies = kwargs.pop("cookies") or {}
    headers = kwargs.pop("headers") or {}
    # Copy across cookies from request
    for key, value in request.cookies.items():
        if key not in cookies:
            cookies[key] = value
    # Also the authorization header, if set
    if "authorization" in request.headers:
        headers["authorization"] = request.headers["authorization"]
    kwargs["cookies"] = cookies
    kwargs["headers"] = headers
    return await datasette.client.get(*args, **kwargs)

But it's not going to work! Because of this code:

https://github.com/datasette/datasette-enrichments/blob/96cbf510115b3ab738360f88a6e7e8070575a171/datasette_enrichments/__init__.py#L70-L133

Neither of those methods have access to the request object - because enrichments are designed to run in-process but outside of the user's request.

Authentication is considered when a user first queues up an enrichment, but after that point the code runs independently and needs to be able to fetch new rows without adding extra authentication headers.

Options for solving this:

Or... there may be one more option. I could define my own permission_allowed() hook in this plugin which uses some kind of internal criteria unique to this plugin, such that calls to datasette.client.get() can complete here without having to intefere with other aspects of Datasette's existing auth system.

I'm going to experiment with that path first.

simonw commented 7 months ago

That seems to work!

simonw commented 7 months ago

Got this working on Datasette Cloud now.