graphql-python / graphql-python/graphql-core-legacy

Little help with subscription middleware

未关闭
#246 2 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看
主要语言
Python
星标
372
派生
175
PR 合并指标
30 天内没有已合并 PR

描述

Hi,

I've been playing around with graphql in [this](https://github.com/jamesstidard/Dixtionary-Server) little test server. It's the server side implementation for a pictionary type game. I've managed to get queries, mutations and subscriptions working with python 3 style `defs`, `asyncdefs` and `asyncgenerators`.

I've used middleware on the http query/mutation requests without issue, however, getting middleware to play nice with the subscriptions has proven difficult for me and I was wondering if I could ask for some help.

I have been doing authentication for websocket connections by requiring a token to be provided along with any subscriptions that require auth. However, I'd like to move this to middleware and have the token handed up in connection params in the auth. [This is the branch](https://github.com/jamesstidard/Dixtionary-Server/tree/subscription-middleware-auth) I [stash the connection_params](https://github.com/jamesstidard/Dixtionary-Server/blob/subscription-middleware-auth/dixtionary/extensions/graphql.py#L12) and [pass them into the context](https://github.com/jamesstidard/Dixtionary-Server/blob/subscription-middleware-auth/dixtionary/extensions/graphql.py#L30).

In that same spot I [add a auth middleware](https://github.com/jamesstidard/Dixtionary-Server/blob/subscription-middleware-auth/dixtionary/extensions/graphql.py#L27) to handle authenticating websocket connections via those `connection_params`. Wrapping in a `MiddlewareManager` as suggested by @dfee in [this issue](https://github.com/graphql-python/graphql-core/issues/149#issuecomment-353307642).

The implantation of the `authorize_ws` middleware looks like this:

```python
async def _authorize_ws_subscription(next, root, info, **args):
token = info.context["connection_params"].get("authorization")
await _authorize(token=token, info=info)

async for msg in next(root, info, **args):
yield msg

async def _authorize_ws_query_mutation(next, root, info, **args):
token = info.context["connection_params"].get("authorization")
await _authorize(token=token, info=info)

result = next(root, info, **args)
if inspect.isawaitable(result):
return await result
else:
return result

def authorize_ws(next, root, info, **args):
if info.operation.operation == 'subscription':
return _authorize_ws_subscription(next, root, info, **args)
else:
return _authorize_ws_query_mutation(next, root, info, **args
```

However, this `info.operation.operation == 'subscription'` check does not appear to be sufficient for determining how to handle the request and results in my subscription websocket spitting out stringified rx objects for values e.g.:

```json
{
"id": "6",
"type": "start",
"payload": {
"variables": {
"uuid": ""
},
"extensions": {},
"operationName": "roomDeleted",
"query": "subscription roomDeleted($uuid: String!) {\n roomDeleted(uuids: [$uuid]) {\n uuid\n __typename\n }\n}\n"
}
}
```

What it appears to be is that not all subscription operations expect a `asyncgen` as some subscription calls are for `attr_resolve` and not the base subscription itself. Anyway, putting in this hacky line fixes it... (where my base Subscription graphene.ObjectType is named Subscription). Though of course this then skips authentication for those calls.

```python
def authorize_ws(next, root, info, **args):
if info.operation.operation == 'subscription':
if not str(next).startswith('Subscription.'):
return next(root, info, **args)
return _authorize_ws_subscription(next, root, info, **args)
else:
return _authorize_ws_query_mutation(next, root, info, **args)
```

Would appreciate any pointers. Sorry for posting links to a codebase instead of a smaller snippet, I'm not sure however where in the chain I'm doing things incorrectly (as maybe it's my resolvers that are written incorrectly for the middleware and not vice-versa).

Here's a few anchors around the codebase:

Query: [dixtionary.model.query.Query](https://github.com/jamesstidard/Dixtionary-Server/blob/subscription-middleware-auth/dixtionary/model/query.py#L179)
Mutation: [dixtionary.model.mutations.Mutation](https://github.com/jamesstidard/Dixtionary-Server/blob/subscription-middleware-auth/dixtionary/model/mutations.py#L161)
Subscription: [dixtionary.model.subscriptions.Subscription](https://github.com/jamesstidard/Dixtionary-Server/blob/subscription-middleware-auth/dixtionary/model/subscriptions.py#L85)
Middleware: [dixtionary.middleware.authentication.authorize_ws](https://github.com/jamesstidard/Dixtionary-Server/blob/subscription-middleware-auth/dixtionary/middleware/authentication.py#L73)
WsLibSubscriptionServer Monkey-patching: [dixtionary.extensions.graphql](https://github.com/jamesstidard/Dixtionary-Server/blob/subscription-middleware-auth/dixtionary/extensions/graphql.py#L45)

Thanks to anyone that indulges such a long post, and Sorry.

贡献指南

这个仓库没有索引到贡献指南

评估

这个 Issue 还没有评估数据。

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。