Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Doesn't work with authenticated Datasette instances #97

Open
simonw opened this issue Sep 7, 2023 · 5 comments
Open

Doesn't work with authenticated Datasette instances #97

simonw opened this issue Sep 7, 2023 · 5 comments
Labels
bug Something isn't working

Comments

@simonw
Copy link
Owner

simonw commented Sep 7, 2023

The GraphQL explorer at least doesn't - you get this error:

CleanShot 2023-09-07 at 10 07 27@2x

Interesting that the API introspection DOES work, but the actual query execution does not.

@simonw simonw added the bug Something isn't working label Sep 7, 2023
@simonw
Copy link
Owner Author

simonw commented Sep 7, 2023

I set that instance up with https://datasette.io/datasette-auth-tokens and this config:

plugins:
  datasette-auth-tokens:
    tokens:
    - token: secret-token
      actor:
        id: bot

allow:
  id:
  - bot
  - root

@simonw
Copy link
Owner Author

simonw commented Sep 7, 2023

Made this change:

diff --git a/datasette_graphql/utils.py b/datasette_graphql/utils.py
index 9eeb358..2218e8a 100644
--- a/datasette_graphql/utils.py
+++ b/datasette_graphql/utils.py
@@ -594,7 +594,10 @@ def make_table_resolver(
                     path_with_query_string,
                 )
 
-        data = (await datasette.client.get(path_with_query_string)).json()
+        response = await datasette.client.get(path_with_query_string)
+        if response.status_code != 200:
+            raise Exception(str(response.status_code) + response.text)
+        data = response.json()
         # If any cells are $base64, decode them into bytes objects
         for row in data["rows"]:
             for key, value in row.items():

And now:

You do not have permission to view this table

CleanShot 2023-09-07 at 10 10 32@2x

@simonw
Copy link
Owner Author

simonw commented Sep 7, 2023

Fixing this is actually quite hard. The datasette.client.get() call is going to need access to the cookies and Authorization header from the incoming request object - but getting that object to it requires digging through a few levels of abstraction.

The main problem is that it's part of the schema which is created in here:

async def schema_for_database(datasette, database=None):
db = datasette.get_database(database)
hidden_tables = await db.hidden_table_names()

Specifically this bit:

to_add.append(
(
"resolve_{}".format(meta.graphql_name),
make_table_resolver(
datasette, db.name, table, table_classes, table_metadata
),
)
)

And the whole point of schema_for_database is that it can be cached once and used for multiple requests!

@simonw
Copy link
Owner Author

simonw commented Sep 7, 2023

Maybe the answer lies here:

context = {
"time_started": time.monotonic(),
"time_limit_ms": config.get("time_limit_ms") or DEFAULT_TIME_LIMIT_MS,
"num_queries_executed": 0,
"num_queries_limit": config.get("num_queries_limit")
or DEFAULT_NUM_QUERIES_LIMIT,
}
result = await schema.execute_async(
query,
operation_name=operation_name,
variable_values=variables or {},
context_value=context,
)
response = {"data": result.data}

I could inject the request information into that context.

@simonw
Copy link
Owner Author

simonw commented Sep 7, 2023

OK, this seems to fix things:

diff --git a/datasette_graphql/__init__.py b/datasette_graphql/__init__.py
index d02262f..ac18ce6 100644
--- a/datasette_graphql/__init__.py
+++ b/datasette_graphql/__init__.py
@@ -113,6 +113,7 @@ async def view_graphql(request, datasette):
         "num_queries_executed": 0,
         "num_queries_limit": config.get("num_queries_limit")
         or DEFAULT_NUM_QUERIES_LIMIT,
+        "request": request,  # For authentication headers
     }
 
     result = await schema.execute_async(
diff --git a/datasette_graphql/utils.py b/datasette_graphql/utils.py
index 9eeb358..c346ded 100644
--- a/datasette_graphql/utils.py
+++ b/datasette_graphql/utils.py
@@ -594,7 +594,15 @@ def make_table_resolver(
                     path_with_query_string,
                 )
 
-        data = (await datasette.client.get(path_with_query_string)).json()
+        headers = context["request"].headers
+        cookies = context["request"].cookies
+
+        response = await datasette.client.get(
+            path_with_query_string, headers=headers, cookies=cookies
+        )
+        if response.status_code != 200:
+            raise Exception(str(response.status_code) + response.text)
+        data = response.json()
         # If any cells are $base64, decode them into bytes objects
         for row in data["rows"]:
             for key, value in row.items():

I need to do a bunch of testing around this though. Including checking that the schema isn't being linked to users who should not be able to view it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

1 participant