テックキャンプ卒の弱々エンジニア日記

エンジニアとして働く中での学びをちょっとでも記録していきます。

GraphQLで「An object of type 〇〇〇 was hidden due to permissions」というエラーが出た件について

こんにちは、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

なんか、このへんのコードが関係しているのでは?と思い、睨めっこしてました。 そう、ここで混乱したのです。混乱ポイントとしては、、、、

  1. ここのauthorized?が、なんなのかわかっていなかったこと。
  2. 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?

の一文はこちらのドキュメントを見ればなるほどとなるはずです。

github.com

まとめ

GraphQLの認可はざっくり、、

  1. 〇〇_type.rb内のauthorized?の判定で決まる 2.(弊社の場合だと)判定のロジックはPunditに渡す
  2. Policyファイルの内容が検証される

と言った感じですかね

知識不足って本当辛い。。。。そして恥ずかしい。。。