Skip to content

GraphQLで各操作に権限を設定する方法

gqlgen でGraphQLエンドポイントを実装している場合、handler.ServerAroundOperations (オペレーション以外にもいくつかある)ミドルウェアが用意されていて、これをHTTPハンドラで差し込むミドルウェアのように利用できる。

関数には context.Context が渡されてくるので、ここから必要な値を取り出すといい。OperationContext はGraphQLの具体的なクエリ情報を扱う。

import (
"context"
"github.com/99designs/gqlgen/graphql"
)
func PermitUserOperation(ctx context.Context, next graphql.OperationHandler) graphql.ResponseHandler {
opCtx := graphql.GetOperationContext(ctx)
if ctx != nil {
return graphql.OneShot(graphql.ErrorResponse(ctx, "the session didn't have the operationContext"))
}
return next(ctx)
}

このとき、特定のクエリだけ挙動を切り替えたい場合も考えられる。判定には以下の値が利用しやすいだろう。

  • opCtx.OperationName … たぶん下と同じ
  • opCtx.Operation.Name … オペレーション名
  • opCtx.Operation.Operationquerymutation など
  • opCtx.Operation.DirectivesGraphQLのディレクティブ

なのでデフォルトでは opCtx.Operation.Operation をみて判断しておき、一部のクエリやミューテーションだけディレクティブを使って変更するのが無難ではないか。

テストは愚直に構造体を埋めていけばいい。もっと良い方法があるかもしれないが、ひとまずこれで動作する。

import (
"context"
"testing"
"github.com/99designs/gqlgen/graphql"
"github.com/vektah/gqlparser/v2/ast"
)
func TestMiddleware(t *testing.T) {
oc := &graphql.OperationContext{
OperationName: "GetServiceMap",
Operation: &ast.OperationDefinition{
Operation: ast.Query,
Name: "GetServiceMap",
Directives: []*ast.Directive{
{Name: "deprecated"},
},
},
}
ctx = graphql.WithOperationContext(t.Context(), oc)
handle := PermitUserOperation(ctx, func(ctx context.Context) graphql.ResponseHandler {
return graphql.OneShot(&graphql.Response{})
})
res := handle(ctx)
}