GraphQLで各操作に権限を設定する方法
gqlgen でGraphQLエンドポイントを実装している場合、handler.Server に AroundOperations (オペレーション以外にもいくつかある)ミドルウェアが用意されていて、これをHTTPハンドラで差し込むミドルウェアのように利用できる。
関数には context.Context が渡されてくるので、ここから必要な値を取り出すといい。OperationContext はGraphQLの具体的なクエリ情報を扱う。
- pkg.go.dev | github.com/99designs/gqlgen/graphql
- pkg.go.dev | github.com/99designs/gqlgen/graphql/handler
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.Operation … query や mutation など
- opCtx.Operation.Directives … GraphQLのディレクティブ
なのでデフォルトでは opCtx.Operation.Operation をみて判断しておき、一部のクエリやミューテーションだけディレクティブを使って変更するのが無難ではないか。
テストを書く
Section titled “テストを書く”テストは愚直に構造体を埋めていけばいい。もっと良い方法があるかもしれないが、ひとまずこれで動作する。
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)}