LoginSignup
8
4

More than 3 years have passed since last update.

GraphQL-Rubyの認可についてまとめる

Last updated at Posted at 2021-04-29

はじめに

最近、GraphQL-Rubyを使って認可を実装する機会があったのでまとめてみます。
用語的には認証と認可がありますが、今回は認可を扱います。(GraphQLは一般的には認証を扱ってません)

resolverやmutationに認可をつける

まずは、resolvermutationに対しての認可に利用できるメソッドについてまとめます。
https://graphql-ruby.org/mutations/mutation_authorization.html

ready?

ready?メソッドは、以下のようにresolverやmutationで使用できます。ready?はmutation実行前に自動的に実行されます。戻り値がfalseの場合はnullが返されますが、例のようにGraphQL Errorを発生させた方がベターでしょう。

mutation.rb
class Mutations::PromoteEmployee < Mutations::BaseMutation
  def ready?(**args) # argsはmutationの引数
    if !context[:current_user].admin?
      raise GraphQL::ExecutionError, "Only admins can run this mutation"
    else
      true
    end
  end

  def resolver(**args)
    # mutation実行
  end
end

authorized?

mutationにはauthorized?メソッドも用意されています。実はこのメソッドはready?メソッドとほとんど同じ動きをします。上のready?authorized?に書き換えても同様に動作します。唯一の違いはargumentsをロードするかどうかです。
argumentsをロードするとは、以下のようにargumentを定義した場合にargumentのemployee_idからloadsで定義されているEmployeeオブジェクトを自動的にDBから取得してくれることを指します(多分)。

mutation.rb
argument :employee_id, ID, required: true, loads: Types::Employee

上記の場合だと、引数で指定したidのemployeeオブジェクトが取得されます。故にauthorized?メソッドを利用すると、以下のようにも書くことができます。

mutation.rb
argument :employee_id, ID, required: true, loads: Types::Employee

def authorized?(employee:)
  context[:current_user].manager_of?(employee)
end

オブジェクトタイプに認可をつける

GraphQL-Rubyでは、関連を持つオブジェクト同士は、クライアント側で自由に取得できます。すると、本来は権限が必要なリソースにもアクセスできてしまうことがあります。それを防ぐためにオブジェクトタイプごとに認可を設定する方法をまとめます。

visible?を使う

https://graphql-ruby.org/authorization/visibility.html
visible?メソッドはその名の通り、スキーマ自体がユーザから見えなくなります。例えば、実験的な機能を特定のユーザのみに開放するみたいなユースケースが考えられます。
以下の例では、visible?メソッドの戻り値がfalseなら、graphqlエラーが返ることになります

secret_type.rb
  class secretType < BaseObject
    def self.visible?(context)
     context[:current_user].admin?
    end

    field :hoge, Hoge, null: true
  end

authorized?を使う

https://graphql-ruby.org/authorization/authorization.html
1番、認可っぽいのはこのauthorized?メソッドでしょうか。名前からしてそうですね。
以下の例では、current_userがobject.userと等しい場合は、Objectが返され、等しくない場合は、nullが返ります。

secret_type.rb
  class secretType < BaseObject
    def self.authorized?(object, context)
     context[:current_user] === object.user
    end

    field :hoge, Hoge, null: true
  end

また、authorized?メソッドがfalseの場合にエラーを返したい場合は、schemaのトップレベルにエラーを定義することができます。

my_schema.rb
class MySchema < GraphQL::Schema
  def self.unauthorized_object(error)
    raise GraphQL::ExecutionError, "An object of type #{error.type.graphql_name} was hidden due to permissions"
  end
end

scopeを使う

https://graphql-ruby.org/authorization/scoping.html
authorized?メソッドは、そのオブジェクトを返すか、返さないかの二択でしたが、ユーザによって返すオブジェクトを制限したい場合もあると思います。その場合はscopeメソッドが便利です。
以下の場合は、adminユーザならば、オブジェクトを全て返し、それ以外はpublishedなproductを返します。

product_type.rb
  class prodcutType < BaseObject
      def self.scope_items(items, context)
        context[:current_user].admin? items : items.published
      end
  end

このscopeを適用するかどうかは、以下のようにfieldの引数で定義できます。

field :products, [Types::Product] scope: true

戻り値が配列の場合は、デフォルトでtrueになっており、値の場合はfalseになっています。

まとめ

以上、GraphQL-Rubyの認可についてまとめました。Graphqlの認可は結構ややこしいので、しっかり整理していきたいです!

8
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
4