GraphQL 인가
인가는 다음 위치에 적용할 수 있습니다.
- 유형:
- 객체 (
::Types::BaseObject
에서 파생된 모든 클래스) - 열거형 (
::Types::BaseEnum
에서 파생된 모든 클래스)
- 객체 (
- 리졸버:
- 필드 리졸버 (
::Types::BaseResolver
에서 파생된 모든 클래스) - 뮤테이션 (
::Types::BaseMutation
에서 파생된 모든 클래스)
- 필드 리졸버 (
- 필드 (
field
DSL 메서드를 사용하여 선언된 모든 필드)
인가는 추상 유형(인터페이스 및 연합)에 대해 지정할 수 없습니다. 추상 유형은 멤버 유형으로 위임됩니다. 기본 내장 스칼라(예: 정수)에는 인가가 없습니다.
당사의 인가 시스템은 응용 프로그램의 나머지 부분에서 사용되는 것과 같은 DeclarativePolicy
시스템을 사용합니다.
- 단일 값에 대해(예:
Query.project
) 현재 인증된 사용자가 인가에 실패하면 해당 필드는null
로 해결됩니다. - 컬렉션(예:
Project.issues
)의 경우 사용자의 인가 검사가 실패한 개체가 제외된 컬렉션으로 필터링됩니다. 이 필터링 프로세스(또는 _레드액션_으로도 알려짐)는 페이징 후에 발생하므로 레드액션된 객체가 제거되어 요청된 페이지 크기보다 작을 수 있습니다.
변이에 대한 리소스 인가에 대해서는 뮤테이션에서 리소스에 대한 인가를 참조하세요.
여기서 설명된 모든 인가 체계의 예제는 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
리졸버 인가
리졸버에는 자체 인가가 적용될 수 있으며, 부모 개체 또는 해결된 값에 적용될 수 있습니다.
부모를 기준으로 인가를 적용하는 리졸버의 예로는 Resolvers::BoardListsResolver
가 있으며, 이는 실행되기 전에 부모가 :read_list
를 충족해야 합니다.
해결된 리소스를 기준으로 인가를 적용하는 예로는 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) # find_object를 호출합니다
end
def find_object(id:)
MyThing.find(id)
end
end
end
이 두 가지 접근 방식 중에서, 객체를 인가하는 것이 더 효율적입니다. 왜냐하면 이를 통해 불필요한 쿼리를 회피할 수 있기 때문입니다.
필드 인가
필드에는 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)
유형 및 필드 인가 결합
인가는 누적됩니다. 즉, 현재 인증된 사용자는 필드와 필드의 유형 각각의 인가 요구 사항을 통과해야 할 수도 있습니다.
다음 단순화된 예에서 현재 인증된 사용자는 문제의 작성자를 볼 권한이 UserType 에 대해 first_permission
및 IssueType.author에 대해 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)