GraphQL 인가

인가는 다음 위치에 적용할 수 있습니다.

  • 유형:
    • 객체 (::Types::BaseObject에서 파생된 모든 클래스)
    • 열거형 (::Types::BaseEnum에서 파생된 모든 클래스)
  • 리졸버:
    • 필드 리졸버 (::Types::BaseResolver에서 파생된 모든 클래스)
    • 뮤테이션 (::Types::BaseMutation에서 파생된 모든 클래스)
  • 필드 (field DSL 메서드를 사용하여 선언된 모든 필드)

인가는 추상 유형(인터페이스 및 연합)에 대해 지정할 수 없습니다. 추상 유형은 멤버 유형으로 위임됩니다. 기본 내장 스칼라(예: 정수)에는 인가가 없습니다.

당사의 인가 시스템은 응용 프로그램의 나머지 부분에서 사용되는 것과 같은 DeclarativePolicy 시스템을 사용합니다.

  • 단일 값에 대해(예: Query.project) 현재 인증된 사용자가 인가에 실패하면 해당 필드는 null로 해결됩니다.
  • 컬렉션(예: Project.issues)의 경우 사용자의 인가 검사가 실패한 개체가 제외된 컬렉션으로 필터링됩니다. 이 필터링 프로세스(또는 _레드액션_으로도 알려짐)는 페이징 후에 발생하므로 레드액션된 객체가 제거되어 요청된 페이지 크기보다 작을 수 있습니다.

변이에 대한 리소스 인가에 대해서는 뮤테이션에서 리소스에 대한 인가를 참조하세요.

note
현재 인증된 사용자가 볼 수 있는 것만으로 기존의 검색기를 통해 로드하는 것이 최선의 방법입니다. 이로써 데이터베이스 쿼리를 최소화하고 로드된 레코드의 불필요한 인가 검사를 회피할 수 있습니다. 또한 민감한 리소스의 존재를 노출할 수 있는 짧은 페이지와 같은 상황을 피할 수 있습니다.

여기서 설명된 모든 인가 체계의 예제는 authorization_spec.rb를 참조하세요.

유형 인가

authorize 메서드에 권한을 전달하여 유형을 인가할 수 있습니다. 동일한 유형의 모든 필드는 현재 인증된 사용자가 필요한 권한을 가지고 있는지 확인하여 인가됩니다.

예를 들어, 다음 인가는 현재 인증된 사용자가 read_project 능력을 가진 프로젝트만 볼 수 있도록 보장합니다(프로젝트가 Types::ProjectType을 사용하는 필드에서 반환되는 한):

module Types
  class ProjectType < BaseObject
    authorize :read_project
  end
end

여러 권한에 대해 인가할 수도 있으며, 이 경우 모든 권한 검사가 통과되어야 합니다.

예를 들어, 다음 인가는 현재 인증된 사용자가 프로젝트를 볼 권한을 얻으려면 read_projectanother_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_permissionIssueType.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)