GraphQL 認証
作成者は以下の場所で適用できます:
- タイプ
- オブジェクト(
::Types::BaseObject
から始まるすべてのクラス) - 列挙型(
::Types::BaseEnum
から始まるすべてのクラス)
- オブジェクト(
- リゾルバ:
- フィールド・リゾルバ (
::Types::BaseResolver
から始まるすべてのクラス) - 突然変異(
::Types::BaseMutation
から派生するすべてのクラス)
- フィールド・リゾルバ (
- フィールド (
field
DSL メソッドを使用して宣言されたすべてのフィールド)
抽象型 (インターフェースと共用体) には作成者を指定できません。抽象型はメンバ型に委譲します。基本的なスカラー(整数など)には作成者はいません。
作成者の権限システムは、アプリケーションの他の部分と同じDeclarativePolicy
システムを使用しています。
- 単一の値 (
Query.project
など) については、現在認証されているユーザーが認証に失敗した場合、フィールドはnull
に解決されます。 - コレクション(
Project.issues
など)の場合、コレクションは、ユーザーの認証チェックが失敗したオブジェクトを除外するためにフィルタされます。このフィルタリング処理(_再編集とも_呼ばれる)はページ分割の後に行われるため、再編集されたオブジェクトが削除されることにより、ページによっては要求されたページサイズよりも小さくなることがあります。
mutation におけるリソースの作成者も参照してください。
ここで説明するすべての認可スキームの例については、authorization_spec.rb
を参照してください。
タイプ認証
authorize
メソッドに能力を渡すことで、型の作成者を認可します。現在認証されているユーザーが必要な能力を持っていることをチェックすることで、同じ型を持つすべてのフィールドがオーソライズされます。
たとえば、次のような権限付与を行うと、現在認証されているユーザーは、read_project
の能力を持つプロジェクト (Types::ProjectType
を使用するフィールドでプロジェクトが返される場合) のみを表示できるようになります:
module Types
class ProjectType < BaseObject
authorize :read_project
end
end
複数の能力に対して権限を付与することもできます。この場合、すべての能力チェックに合格する必要があります。
たとえば、以下の作成者は、現在認証されているユーザーが、read_project
およびanother_ability
の能力を持っていなければプロジェクトを表示できないようにしています:
module Types
class ProjectType < BaseObject
authorize [:read_project, :another_ability]
end
end
リゾルバ作成者の権限
リゾルバは独自の作成者を持つことができ、それは親オブジェクトにも解決された値にも適用することができます。
:read_list
親オブジェクトに対して権限を与えるリゾルバの例として、Resolvers::BoardListsResolver
があります。
解決されたリソースに対して認可を行う例はResolvers::Ci::ConfigResolver
であり、これは解決された値が:read_pipeline
を満たすことを要求しています。
親に対して認可するためには、リゾルバはauthorizes_object!
で宣言することで、 (これは初期状態ではデフォルト値ではなかったので)_オプトイン_しなければなりません:
module Resolvers
class MyResolver < BaseResolver
authorizes_object!
authorize :some_permission
end
end
解決された値に対して認可を行うには、リゾルバはどこかの時点で認可を適用しなければなりません。通常は#authorized_find!(**args)
を使用します:
module Resolvers
class MyResolver < BaseResolver
authorize :some_permission
def resolve(**args)
authorized_find!(**args) # calls find_object
end
def find_object(id:)
MyThing.find(id)
end
end
end
この2つのアプローチのうち、オブジェクトを認可する方が、 不要なクエリを避けることができるので効率的です。
フィールドの承認
フィールドはauthorize
オプションで作成者を認可することができます。
フィールドの認可は現在のオブジェクトに対してチェックされ、認可はリゾルバの_前に_行われます。フィールドに認可チェックを適用する必要がある場合は、おそらくリゾルバに認可を追加するか、理想的には型に認可を追加することになるでしょう。
たとえば、次の作成者は、認証されたユーザーがsecretName
フィールドを表示するには、プロジェクトの管理者レベルのアクセス権が必要であることを保証します:
module Types
class ProjectType < BaseObject
field :secret_name, ::GraphQL::Types::String, null: true, authorize: :owner_access
end
end
この例では、より高価なクエリを回避するために、フィールド権限 (Ability.allowed?(current_user, :read_transactions, bank_account)
など) を使用します:
module Types
class BankAccountType < BaseObject
field :transactions, ::Types::TransactionType.connection_type, null: true,
authorize: :read_transactions
end
end
フィールドの承認は、以下のような場合に推奨されます:
- 他のフィールドとは異なるレベルのアクセス制御が必要なスカラー・フィールド(文字列、ブーリアン、数値)。
- フィールドの解決を保存し、解決された各オブジェクトに対する個別のポリシー・チェックを回避するために、アクセス・チェックを親に適用できるオブジェクト・フィールドとコレクション・フィールド。
オブジェクトが親プロジェクトのアクセス・レベルと正確に一致しない限り、フィールドの作成者はオブジェクト・レベルのチェックに取って代わることはありません。例えば、イシューは親プロジェクトのアクセスレベルとは無関係に機密扱いにすることができます。したがって、Project.issue
。
複数の能力に対してフィールドを作成することもできます。能力を単一の値としてではなく、配列として渡してください:
module Types
class MyType < BaseObject
field :hidden_field, ::GraphQL::Types::Int,
null: true,
authorize: [:owner_access, :another_ability]
end
end
MyType.hiddenField
、フィールドの作成者は以下のテストを行います:
Ability.allowed?(current_user, :owner_access, object_of_my_type) &&
Ability.allowed?(current_user, :another_ability, object_of_my_type)
タイプ権限とフィールド権限の組み合わせ
作成者の権限は累積されます。言い換えると、現在認証されているユーザーは、フィールドとフィールド・タイプの両方の認証要件をパスする必要があります。
以下の単純化した例では、現在認証されているユーザがissueの作成者を確認するためには、ユーザに関するfirst_permission
、およびissueに関するsecond_permission
の両方が必要です。
class UserType
authorize :first_permission
end
class IssueType
field :author, UserType, authorize: :second_permission
end
UserType
上のオブジェクト権限とIssueType.author
上のフィールド権限の組み合わせは、以下のテストを意味します:
Ability.allowed?(current_user, :second_permission, issue) &&
Ability.allowed?(current_user, :first_permission, issue.author)