アクションポリシーを使用したGraphSQL APIのパーミッションの公開


今日は、私は私の人生の誕生日の記念日Rails Appsで- 1年の試行錯誤、ミスとヒット、WTF - Sとの完全な記念日です.

オープンソース中毒者として、私は特にGraphqlで働くのが好きでした:それはむしろ若い技術です、そして、したがって、貢献(少なくとも、Ruby Worldで)と実験の機会がたくさんあります.
そして今、私はそのような実験の結果を提示している action_policy-graphql gem , どちらが接着するかGraphQL Ruby and Action Policy 認証ライブラリ.

待機、アクションポリシーは、IST DAS *だった?


この記事を書いている間、私はRammsteinを聞いています🎸)
Graphqlの作業を始めたのと同じ時間に、新しいRuby認証ライブラリを発表しました.Action Policy , 世界へ.
アクションポリシーは、私はここ数年で取り組んできた複数のプロジェクトから抽出されています.それはイデオロギー的に似ているPundit (最初はそれの上に構築された)が、ボックスの追加機能の束を提供しています(そして非常に異なるアーキテクチャを内部に持っている).
これらの機能の一つは、認証チェックが失敗した理由について追加のコンテキストを提供する機能ですfailure reasons トラッキング
この機能は長い間、暗い馬でした、我々がほとんど我々がGraphqlで働き始めたまで、我々はほとんど(ほとんどデバッグ目的のために)使用しました-それは、醜いアヒルが美しい白鳥に変わった時です.

To learn more about Action Policy and its features check out the slides from the most recent talk I gave at Seattle.rb early this year: https://speakerdeck.com/palkan/seattle-dot-rb-2019-a-denial.


認可とグラフ


Authorization is an act of giving someone official permission to do something (not to be confused with authentication).


我々のコードで尋ねるたびに、「それをするのを許されるユーザーですか?」我々は認可の行為を行う.
GraphSQLを扱うとき、この質問はより具体的なものに分けることができます.
  • ユーザーは、オブジェクト(特定のタイプの、または、グラフの特定のノードの)と対話するのを許されますか?
  • ユーザーはこの突然変異を実行したり、このサブスクリプションを購読するか?
  • 別の関連する側面は、データスコープである.フィルタコレクションは、すべての単一の項目をチェックする代わりに、ユーザのパーミッションに従っている.
    The graphql 宝石基礎authorization support を通じて#authorized? フックとscoping support を通じて#scope_items フック:それは最初に十分に良いことができますが、ボイラー板の量のためによくスケールしません.
    だからこそ私は開発を始めました action_policy-graphql gem –フィールド認証とスコープのためのより良いAPIを提供するにはGraphQL Pro for pundit and cancancan ). このようにAPIを説明します.
    # field authorization example (`authorize: true`)
    field :home, Home, null: false, authorize: true do
      argument :id, ID, required: true
    end
    
    # data scoping using policies (`authorized_scope: true`)
    field :events, EventType.connection_type, null: false, authorized_scope: true
    
    この実装はfield extensions 内部的には、それは上に構築するよりも柔軟になった#authorized? and #scope_items フック.
    問題は解決されているようでした:アクセス許可をチェックして、例外を上げるためにAPIで方針クラスで定義された認可とスコーピング規則を使用するのは楽になりました.後者は例外を上げるのですが、パーミッションの欠如についてユーザーに知らせる最善の方法ではありませんでした.
    これらの例外を防ぎ、フロントエンドクライアントに対してどのような動作が許可されているかを説明します.

    クライアントにその権利について知らせる


    の問題pushing current user's permissions クライアント(フロントエンド/モバイルアプリケーション)への情報は、新しいされていませんそれは、Grapqlが生まれる前に、何年も存在しました.
    問題は、クライアントがどのアクションをユーザーに許可されているかを確認する方法(および表示/非表示特定のボタン/リンク/コントロール)に翻訳することができます?
    どのように解決するには?
    たとえば、承認モデル(ロール、パーミッションセット)だけを通過し、承認規則クライアント側を実装(すなわち複製)するようにしてください.そのような重複は、足で自分を撃つ最も簡単な方法です.

    You should rely on a single source of authorization truth.


    そして、この単一のソースのソースは、通常、サーバーをチェックします.
    したがって、我々のポリシー規則をクライアント互換形式に変換する必要があります.例えば、JSONオブジェクトです.
    ユーザーのJSONオブジェクトへのすべての可能な認証ルールをダンプするのは良いアイデアではありません.うまくいけば、GraphSQLの利点の一つは、必要なデータだけを要求する能力です.
    だから、我々は追加の簡単なアイデアを始めたcanDoSmth 我々のGraphSQLタイプへのブールフィールド
    class EventType < Types::BaseType
      # include Action Policy helpers (`authorize!`, `allowed_to?`)
      include ActionPolicy::Behaviour
    
      field :can_destroy, Boolean, null: false
    
      def can_destroy
        # checks the EventPolicy#destroy? rule for the object
        allowed_to?(:destroy?, object, context: {user: context[:current_user]})
      end
    end
    
    クライアントは、イベントを削除することができるかどうかをサーバに問い合わせることができます.
    query {
      event(id: $id) {
        canDestroy # true | false
      }
    }
    
    また、マクロを追加して認証フィールドを定義して、ボイラー板を減らすことができました.
    class EventType < Types::BaseType
      include ActionPolicy::Behaviour
    
      expose_authorization_rules :destroy?, :edit?, :rsvp?
    end
    
    「まだそこにいるのですか?」いいえ.

    クライアントがより多くのコンテキスト


    ほとんどの場合、行動が許されているか否かを知っていることがわかったtrue or false ) 十分ではありません:また、ユーザーに通知を表示する必要があります(または“なぜ?”質問に答えてください).そして、いくつかの状況では、このメッセージを私たちがこの行動を禁止した理由によって異なります.
    ユーザーがイベントにRSVPを許可するかどうかをチェックする規則の簡単な例を考えます.
    def rsvp?
      allowed_to?(:show?) &&  # => User should have an access to this event
      rsvp_open? &&           # => RSVP should be open
      seats_available?        # => There must be seats left
    end
    
    私たちは最後の2つのチェックに最も興味を持っています(ユーザーがイベントへのアクセスを持たないならば、それはRSVPへの許可を求めるべきではありません).
    RSVPが閉じられるとき、我々は「RSVPはこのイベントのために閉じられました」メッセージを見せたいです;これ以上の席が利用できない場合は
    どのように、我々は我々の方針からこの情報を得ることができますか?使用failure reasons 機能性!
    我々のルールを少し変更する必要があります.
    def rsvp?
      allowed_to?(:show?) &&
      # wrapping method call into a `check?` method
      # tracks the failure of this check (i.e. when it returns false) 
      check?(:rsvp_open?) &&
      check?(:seats_available?)
    end
    
    次に、認証チェック結果から追加のコンテキストを取得できます.
    policy = EventPolicy.new(record: event, user: user)
    # first, apply the rule
    policy.apply(:rsvp?)
    # now we can access the result object and its reasons
    # for example, details hash contains the checks names grouped
    # by a policy
    policy.result.reasons.details #=> {event: [:rsvp_open?]} or {event: [:seats_available?]}
    
    待つ?詳細ハッシュ?識別子?我々は人間に読めるメッセージが必要です!
    よろしい.ここでアクションポリシーとi18n integration .
    メッセージをlocaleファイルに追加しましょう.
    en:
      action_policy:
        policy:
          event:
            rsvp?: "You cannot RSVP to this event"
            rsvp_opened?: "RSVP has been closed for this event"
            seats_available?: "This event is sold out"
    
    ローカライズされたメッセージにアクセスするには、#full_messages 結果オブジェクトについて:
    policy.result.reasons.full_messages #=> ["RSVP has been closed for this event"]
    
    # to access the top-level (rule) message
    policy.result.message #=> "You cannot RSVP to this event"
    
    今すぐGraphSQLに戻ってexpose_authorization_rules マクロを使用してフィールドを定義するAuthorizationResult 種類:

    にはreasons フィールドオブアFailureReasons 種類:

    クライアントの視点から、次のようになります.
    query {
      event(id: $id) {
        canDestroy {
          value # true | false
          message # top-level message
          reasons {
            details # JSON-encoded failure details
            fullMessages # human-readable messages
          }
        }
      }
    }
    
    このアプローチの長所は何ですか.
  • 認証情報を取得するための標準APIがあります
  • 認証を行うための単一の抽象レイヤを持っています(ポリシークラス)
  • スキーマへの認証ルールの追加は簡単です(そして、アクションポリシーのおかげで)ですtestability , テストも簡単です.
  • そして、すべての機能を使用するときに無料で得る action_policy-graphql gem !
    記事を読むhttps://evilmartians.com/chronicles !