こんにちは、Tochiです。 相変わらず技術に苦戦している日々が続いています。
今日はGraphQLを触っていたときに「An object of type 〇〇〇 was hidden due to permissions」というエラーに遭遇した話をアウトプットしていきます。
前提
前提に、サーバーサイドではgraphql-ruby
を使っています。
はじめに
わたしは最初このエラーに怖気付きました。
というのも、単純にlocalhost:8080/graphiql
でクエリを投げたときは正常に値が返ってきていて、
実際のアプリケーションではエラーが発生しているという状態だったので、長年?の勘で「これは苦労しそうだ」と思い若干怖気付いてしましました。。。
が、1つづつ紐解いていくとシンプルな知識不足だったことがわかりました。。。
どんなエラーなんだろう
まず、「An object of type 〇〇〇 was hidden due to permissions」というエラーはどこで発生しているのか気になりました。 今回、説明のため「An object of type Memberships was hidden due to permissions」というエラーが出たと仮定します。
実はこのエラー、pundit
というgemで管理されたポリシーに違反していたことで起きていました。
github.com
このgemを使うと、app/policies
配下に各モデルファイルに対応したポリシーを定義することができます。
例えば、以下のコードは、ユーザーが管理者である場合、または投稿が非公開である場合に投稿の更新を許可する例です。
class PostPolicy attr_reader :user, :post def initialize(user, post) @user = user @post = post end def update? user.admin? || !post.published? end end
こんな感じでポリシーに違反していると、Pundit::NotAuthorizedError
というエラーが発生します。
これはapp_schema.rb
などで捕捉することできます。
rescue_from(Pundit::NotAuthorizedError) do |e| GraphQL::ExecutionError.new(e, extensions: { code: 'AUTHORIZED_ERROR' }) end
はやくも結論....
今回はMembership
がどうとかいっているので、とりあえずmembership_policy.rb
をみてみると、、
# frozen_string_literal: true # TODO: 再整理、再実装をする class MembershipPolicy < ApplicationPolicy def show? user.memberships.where(organization: record.organization).exists? end end
すいません....どうも要求したmembershipsが自分が属する組織外だったため 今回のエラーが出たようです。
これがひとまず結論になります。
ちょっとまって、何に混乱したんだ自分は。
一旦答えがわかったところで、自分が混乱してしまったことを整理していきます。 まずシンプルに混乱したのは以下のコード。
module Types class MembershipType < Types::BaseObject global_id_field :id # 省略 class << self def authorized?(object, context) super && Pundit.policy(context[:current_user], object).show? end end
なんか、このへんのコードが関係しているのでは?と思い、睨めっこしてました。 そう、ここで混乱したのです。混乱ポイントとしては、、、、
- ここの
authorized?
が、なんなのかわかっていなかったこと。 authorized?
の中でPunditを呼び出しているので、なんとなくathorized?
メソッドもPundit
のメソッドなのかと思ってしまったということ。
1に関してはドキュメント通り、オブジェクトの認可を行っている部分になります。
When you implement this method to return false, the query will be halted, for example: ドキュメント引用 そして、その認可のロジックを
Pundit
で処理している感じです。
Pundit.policy(context[:current_user], object).show?
の一文はこちらのドキュメントを見ればなるほどとなるはずです。
まとめ
GraphQLの認可はざっくり、、
- 〇〇_type.rb内の
authorized?
の判定で決まる 2.(弊社の場合だと)判定のロジックはPunditに渡す - Policyファイルの内容が検証される
と言った感じですかね
知識不足って本当辛い。。。。そして恥ずかしい。。。