InfoGrab DocsInfoGrab Docs

커스텀 역할 개발 가이드라인

요약

Ultimate 고객은 커스텀 역할을 생성하고, 특정 권한(ability)을 지정하여 해당 역할을 정의할 수 있습니다. 예를 들어, 사용자는 read code와 admin merge requests 권한을 가지지만 admin issues와 같은 권한은 없는 "Engineer" 역할을 만들 수 있습니다.

Ultimate 고객은 커스텀 역할을 생성하고, 특정 권한(ability)을 지정하여 해당 역할을 정의할 수 있습니다.

예를 들어, 사용자는 read codeadmin merge requests 권한을 가지지만 admin issues와 같은 권한은 없는 "Engineer" 역할을 만들 수 있습니다.

이 맥락에서 "permission"과 "ability"라는 용어는 종종 혼용됩니다.

  • "Ability"는 사용자가 수행할 수 있는 작업입니다. 이는 Declarative Policy abilities에 매핑되며, ee/app/policies/*의 Policy 클래스에 존재합니다.

  • "Permission"은 사용자 대상 문서에서 ability를 지칭하는 방식입니다. 권한 문서는 수동으로 생성되므로, 문서에 나열된 권한과 Policy 클래스에 정의된 ability가 반드시 1:1로 매핑되지는 않습니다.

커스텀 역할 vs 기본 역할#

GitLab 15.9 이하에서 GitLab은 권한 시스템으로 기본 역할만 사용했습니다. 이 시스템에는 특정 권한(ability)에 정적으로 할당된 몇 가지 사전 정의된 역할이 있습니다. 이러한 기본 역할은 고객이 사용자 정의할 수 없습니다.

커스텀 역할을 사용하면 고객이 특정 사용자 그룹에 어떤 권한(ability)을 부여할지 결정할 수 있습니다. 예를 들어:

  • 기본 역할 시스템에서 취약점 읽기는 Developer 권한으로 제한됩니다.

  • 커스텀 역할 시스템에서 고객은 이 권한(ability)을 임의의 기본 역할을 기반으로 하는 새 커스텀 역할에 할당할 수 있습니다.

기본 역할과 마찬가지로, 커스텀 역할도 그룹 계층 내에서 상속됩니다. 사용자가 그룹에 대한 커스텀 역할을 가지고 있으면, 해당 사용자는 그룹 내의 모든 프로젝트나 하위 그룹에 대해서도 커스텀 역할을 가집니다.

기술 개요#

  • 개별 커스텀 역할은 member_roles 테이블(MemberRole 모델)에 저장됩니다.

  • member_roles 레코드는 namespace_id 외래 키를 통해 최상위 그룹(하위 그룹 아님)과 연결됩니다.

  • 그룹 또는 프로젝트 멤버십(members 레코드)은 member_role_id 외래 키를 통해 커스텀 역할과 연결됩니다.

  • 그룹 또는 프로젝트 멤버십은 해당 그룹 또는 프로젝트의 루트 수준 그룹에 정의된 임의의 커스텀 역할과 연결될 수 있습니다.

  • member_roles 테이블에는 개별 권한과 base_access_level 값이 포함됩니다.

  • base_access_level은 유효한 액세스 수준이어야 합니다.

가능한 값: 0 (액세스 없음), 5 (최소 액세스), 10 (Guest), 15 (Planner), 20 (Reporter), 25 (Security Manager), 30 (Developer), 40 (Maintainer), 50 (Owner), 60 (Admin).

  • base_access_level은 커스텀 역할에 포함되는 권한(ability)을 결정합니다. 예를 들어, base_access_level10이면, 커스텀 역할은 기본 Guest 권한이 받는 모든 권한(ability)에 더해, member_roles 레코드에서 read_code와 같은 속성을 true로 설정하여 활성화된 추가 권한(ability)을 포함합니다.

  • 커스텀 역할은 base_access_level에 대해 추가 권한(ability)을 활성화할 수 있지만 권한을 비활성화할 수는 없습니다. 따라서 커스텀 역할은 "추가만 가능(additive only)"합니다. 이 선택의 근거는 이 코멘트에 있습니다.

  • 커스텀 역할 권한(ability)은 프로젝트 수준과 그룹 수준에서 지원됩니다.

권한(ability) 리팩토링#

기존 권한(ability) 검사 찾기#

권한(ability)은 단일 엔드포인트 또는 웹 요청에 대해 여러 위치에서 검사되는 경우가 많습니다. 따라서 특정 엔드포인트에 대해 실행되는 인가 검사 목록을 찾기 어려울 수 있습니다.

이를 돕기 위해 로컬에서 GITLAB_DEBUG_POLICIES=true를 설정할 수 있습니다.

이 설정은 실행하는 모든 스펙에서 발생하는 요청에서 어떤 권한(ability)이 검사되는지에 대한 정보를 출력합니다. 출력에는 인가 검사가 이루어진 코드 라인도 포함됩니다. 호출자 정보는 메타프로그래밍이 사용된 경우에 특히 유용합니다. 해당 경우는 ability 이름 문자열로 grep하기 어렵기 때문입니다.

예시:

# 스펙 실행 예시

GITLAB_DEBUG_POLICIES=true bundle exec rspec spec/controllers/groups_controller_spec.rb:162

# 스펙 실행 시 권한 디버그 출력; 여러 정책 검사가 실행되면 모두 디버그 출력에 포함됩니다.

POLICY CHECK DEBUG -> policy: GlobalPolicy, ability: create_group, called_from: ["/gitlab/app/controllers/application_controller.rb:245:in `can?'", "/gitlab/app/controllers/groups_controller.rb:255:in `authorize_create_group!'"]

이 설정을 사용하여 리팩토링 중 인가 검사에 대해 더 많이 파악하세요. 기본 브랜치의 어떤 스펙에서도 이 설정을 활성화된 상태로 유지해서는 안 됩니다.

개별 권한(ability)의 로직 이해#

ability에 대한 참조는 DeclarativePolicy 클래스에 여러 번 나타날 수 있으며, 다른 ability를 참조하는 조건과 규칙에 의존합니다. 따라서 특정 ability에 적용되는 조건이 정확히 무엇인지 파악하기 어려울 수 있습니다.

DeclarativePolicy는 각 정책 클래스에 대해 ability_map을 제공하며, 이를 통해 ability에 대한 모든 규칙을 배열로 가져올 수 있습니다.

예시:

> GroupPolicy.ability_map.map.select { |k,v| k == :read_group_member }
=> {:read_group_member=>[[:enable, #], [:prevent, #]]}

> GroupPolicy.ability_map.map.select { |k,v| k == :read_group }
=> {:read_group=>
  [[:enable, #],
   [:enable, #],
   [:enable, #],
   [:enable, #],
   [:enable, #],
   [:enable, #],
   [:prevent, #],
   [:enable, #],
   [:prevent, #],
   [:prevent, #]]}

DeclarativePolicy는 특정 객체와 액터에 대한 로직 트리를 이해하는 데 사용할 수 있는 debug 메서드도 제공합니다. 출력은 ability_map의 규칙 목록과 유사합니다. 하지만 DeclarativePolicy는 ability를 prevent한 후에는 규칙 평가를 중지하므로, 모든 조건이 호출되지 않을 수 있습니다.

예시:

policy = GroupPolicy.new(User.last,  Group.last)
policy.debug(:read_group)

- [0] enable when public_group ((@custom_guest_user1 : Group/139))
- [0] enable when admin ((@custom_guest_user1 : Group/139))
- [0] enable when auditor ((@custom_guest_user1 : Group/139))
- [14] prevent when all?(~public_group, ~admin, user_banned_from_group) ((@custom_guest_user1 : Group/139))
- [14] prevent when needs_new_sso_session ((@custom_guest_user1 : Group/139))
- [16] enable when guest ((@custom_guest_user1 : Group/139))
- [16] enable when has_projects ((@custom_guest_user1 : Group/139))
- [16] enable when read_package_registry_deploy_token ((@custom_guest_user1 : Group/139))
- [16] enable when write_package_registry_deploy_token ((@custom_guest_user1 : Group/139))
  [21] prevent when all?(ip_enforcement_prevents_access, ~owner, ~auditor) ((@custom_guest_user1 : Group/139))

=> #,
 @enabled=false,
 @prevented=true>

권한(ability) 통합#

커스텀 역할에 추가되는 모든 기능은 최소한의 권한(ability)을 가져야 합니다. 대부분의 기능에서 read_*admin_*만 있으면 충분합니다. 다음을 모두 통합해야 합니다:

  • 보기 관련 권한(ability)은 read_* 아래에. 예: 목록 또는 상세 보기.

  • 객체 업데이트는 admin_* 아래에. 예: 객체 업데이트, 담당자 추가 또는 해당 객체 닫기. 일반적으로 admin_을 활성화하는 역할은 read_ 권한(ability)도 활성화되어 있어야 합니다. 이는 MemberRole 모델의 ALL_CUSTOMIZABLE_PERMISSIONS 해시의 requirement 옵션에 정의됩니다.

추가 권한(ability)이 필요한 기능이 있을 수 있지만, 이를 최소화하도록 노력하세요. 언제든지 Authentication and Authorization 그룹 멤버에게 의견이나 도움을 요청할 수 있습니다.

여기서부터 작업을 시작해야 합니다. 작업 중인 기능의 모든 권한(ability)을 파악하고, 해당 권한(ability)을 read_, admin_, 또는 필요한 경우 추가 권한(ability)으로 통합하세요.

GroupPolicyProjectPolicy 클래스의 많은 권한(ability)에는 중복된 정책이 많이 있습니다. 이러한 Policy 클래스를 통합하기 위한 에픽이 있습니다. 이러한 클래스에서 유사한 권한을 발견하면, 동일한 이름을 갖도록 리팩토링하는 것을 고려하세요.

예를 들어, GroupPolicyread_group_security_dashboard라는 ability가 있고 ProjectPolicyread_project_security_dashboard라는 ability가 있다고 가정합니다. 두 가지 모두 사용자 정의 가능하게 만들고 싶다면, member_roles 테이블에 각 ability에 대한 행을 추가하는 대신 이름을 read_security_dashboard로 변경하고 member_roles 테이블에 read_security_dashboard를 추가하는 것을 고려하세요. 상위 그룹에서 read_security_dashboard를 활성화하면 커스텀 역할이 그룹 보안 대시보드와 해당 그룹의 각 프로젝트에 대한 프로젝트 보안 대시보드에 액세스할 수 있습니다. 특정 프로젝트에서 동일한 권한을 활성화하면 해당 프로젝트의 보안 대시보드에 액세스할 수 있습니다.

커스텀 역할에 권한(ability) 지원 추가 방법#

기존 ability를 추가하는 경우, 아래 단계를 완료하기 전에 별도의 머지 리퀘스트에서 먼저 해당 기능의 ability를 리팩토링 및 통합하는 것을 고려하세요.

1단계: 구성 파일 생성#

  • ./ee/bin/custom-ability 을 실행하여 새 ability에 대한 구성 파일을 생성합니다.

  • 이렇게 하면 ee/config/custom_abilities에 다음 스키마를 따르는 YAML 파일이 생성됩니다:

필드 필수 여부 설명
name 커스텀 ability를 설명하는 고유하고, 소문자이며 밑줄로 구분된 이름. 파일명과 일치해야 합니다.
title 커스텀 ability의 사람이 읽을 수 있는 제목.
description 커스텀 ability의 사람이 읽을 수 있는 설명.
feature_category 기능 카테고리 이름. 예: vulnerability_management.
introduced_by_issue 이 커스텀 ability의 추가를 제안한 이슈 URL.
introduced_by_mr 이 커스텀 ability를 추가한 MR URL.
milestone 이 커스텀 ability가 추가된 마일스톤.
admin_ability 아니오 이 ability가 관리자 수준에서 검사되는지 여부를 나타내는 불리언 값.
group_ability 이 ability가 그룹 수준에서 검사되는지 여부를 나타내는 불리언 값.
enabled_for_group_access_levels group_ability = true인 경우 그룹에서 이 커스텀 ability에 이미 액세스할 수 있는 액세스 수준의 배열. 개별 ability의 로직 이해 섹션을 참조하여 ability의 기본 액세스 수준을 결정하는 데 도움을 받으세요. 이는 정보용이며 커스텀 역할 동작에는 영향을 미치지 않습니다.
project_ability 이 ability가 프로젝트 수준에서 검사되는지 여부를 나타내는 불리언 값.
enabled_for_project_access_levels project_ability = true인 경우 프로젝트에서 이 커스텀 ability에 이미 액세스할 수 있는 액세스 수준의 배열. 개별 ability의 로직 이해 섹션을 참조하여 ability의 기본 액세스 수준을 결정하는 데 도움을 받으세요. 이는 정보용이며 커스텀 역할 동작에는 영향을 미치지 않습니다.
requirements 아니오 이 ability가 의존하는 커스텀 권한 목록. 예를 들어 admin_vulnerability는 read_vulnerability에 의존합니다. 없으면 []를 입력하세요.
project_permissions project_ability = true인 경우 이 커스텀 ability가 프로젝트 수준에서 부여될 때 활성화할 권한 목록. 이러한 권한은 정책 규칙을 수동으로 추가할 필요 없이 ProjectPolicy에 자동으로 적용됩니다.
group_permissions group_ability = true인 경우 이 커스텀 ability가 그룹 수준에서 부여될 때 활성화할 권한 목록. 이러한 권한은 정책 규칙을 수동으로 추가할 필요 없이 GroupPolicy에 자동으로 적용됩니다.

2단계: 스펙 파일 생성 및 유효성 검사 스키마 업데이트#

  • bundle exec rails generate gitlab:custom_roles:code --ability 을 실행합니다. 이렇게 하면 권한 유효성 검사 스키마 파일이 업데이트되고 빈 스펙 파일이 생성됩니다.

3단계: ability를 작업 중(WIP)으로 표시 (선택 사항)#

ability가 여러 MR에 걸쳐 구현되는 경우, 출시 준비가 완료될 때까지 공개 API 표면에 나타나지 않도록 작업 중으로 표시합니다. ability YAML에 wip: true를 추가하세요:

# ee/config/custom_abilities/read_dependency.yml
---
name: read_dependency
wip: true
# ... other fields

WIP ability는 부팅 시 로드된 권한 세트에서 제외됩니다. REST 요청 파라미터, REST 응답 본문, 또는 GraphQL enum에 나타나지 않습니다. 해당 ability가 설정된 상태로 역할을 생성하거나 조회할 수 없습니다.

로컬에서 ability를 작업하려면 Rails를 시작하기 전에 GITLAB_LOAD_WIP_CUSTOM_ABILITIES 환경 변수를 설정하세요:

export GITLAB_LOAD_WIP_CUSTOM_ABILITIES=true

스펙은 spec_helper.rb를 통해 기본적으로 이 변수가 설정된 상태로 실행되므로, 모든 테스트에서 WIP ability를 볼 수 있습니다. 프로덕션과 유사한 게이팅 동작을 검증해야 하는 테스트는 :disable_wip_custom_abilities RSpec 메타데이터를 사용하여 활성화할 수 있습니다.

ability 출시 준비가 완료되면 wip: 키를 제거하세요.

4단계: YAML 구성 파일에서 권한 정의#

권한은 project_permissionsgroup_permissions 필드를 사용하여 YAML 구성 파일에 선언적으로 정의됩니다. 시스템은 이러한 정의에서 정책 규칙을 자동으로 생성합니다. ProjectPolicy 또는 GroupPolicy를 수동으로 업데이트할 필요가 없습니다.

예를 들어, read_dependency 커스텀 ability를 추가하는 경우 YAML 파일에 다음을 포함해야 합니다:

# ee/config/custom_abilities/read_dependency.yml
---
name: read_dependency
group_ability: true
project_ability: true
project_permissions:
  - read_dependency
group_permissions:
  - read_dependency
# ... other fields

시작 시 시스템은 이러한 YAML 파일을 읽고 GroupPolicyProjectPolicy 모두에서 각 ability에 대한 custom_role_enables_<ability> 조건과 해당 규칙을 동적으로 생성합니다. read_dependency를 포함하는 커스텀 역할을 가진 사용자가 프로젝트 또는 그룹에 액세스하면, 시스템은 각각 project_permissions 또는 group_permissions에 나열된 권한을 자동으로 활성화합니다.

커스텀 ability는 여러 권한을 활성화할 수 있습니다. 예를 들어, 배포 토큰을 관리하는 ability는 그룹과 프로젝트 수준에서 서로 다른 권한을 활성화합니다:

# ee/config/custom_abilities/manage_deploy_tokens.yml
group_permissions:
  - manage_deploy_tokens
  - read_deploy_token
  - create_deploy_token
  - destroy_deploy_token
project_permissions:
  - manage_deploy_tokens
  - read_deploy_token
  - create_deploy_token
  - destroy_deploy_token

모든 ability가 두 수준 모두에서 활성화될 필요는 없습니다. 예를 들어, admin_terraform_state는 사용자가 프로젝트의 Terraform 상태를 관리할 수 있도록 합니다. project_permissions만 정의하면 되고, group_permissions는 필요하지 않습니다.

5단계: 역할 액세스 확인#

  • GITLAB_SIMULATE_SAAS=1로 SaaS 모드가 활성화되어 있는지 확인합니다.

  • Owner인 그룹으로 이동한 후 설정 -> 역할 및 권한으로 이동합니다.

  • 새 역할을 선택하고 방금 만든 권한으로 커스텀 역할을 생성합니다.

  • 그룹의 관리 -> 멤버 페이지로 이동하여 멤버를 새로 생성한 커스텀 역할에 할당합니다.

  • 다음으로 해당 멤버로 로그인하여 커스텀 ability가 의도한 페이지에 액세스할 수 있는지 확인합니다.

6단계: 고급 검색에 대한 영향 평가#

커스텀 역할은 ability가 고급 검색에 의해 인덱싱되는 데이터에 영향을 미치는 경우 고급 검색 기능에 영향을 줄 수 있습니다.

  • 고급 검색을 활성화하고 인스턴스를 인덱싱합니다

  • 커스텀 역할이 할당된 멤버로 그룹에 로그인합니다

  • 검색 또는 이동...으로 이동하여 전체 검색을 수행합니다. 검색어를 입력하고 모든 GitLab에서 검색하도록 선택합니다.

  • 사용자가 커스텀 역할의 영향을 받는 데이터를 검색할 수 있는지 확인합니다

  • 그룹 페이지로 이동한 후 검색 또는 이동...으로 이동하여 그룹 검색을 수행합니다. 검색어를 입력하고 그룹에서 검색하도록 선택합니다.

  • 사용자가 커스텀 역할의 영향을 받는 데이터를 검색할 수 있는지 확인합니다

  • 필요한 경우 검색 인가를 업데이트합니다

7단계: 스펙 추가#

  • ee/spec/factories/member_roles.rbMemberRoles 팩토리에 ability를 트레이트로 추가합니다.

  • ee/spec/requests/custom_roles//request_spec.rb에 테스트를 추가하여 사용자가 커스텀 ability를 할당받은 후 컨트롤러, REST API 엔드포인트 및 GraphQL API 엔드포인트에 성공적으로 액세스할 수 있는지 확인합니다.

  • 아래는 Rails 컨트롤러 엔드포인트를 테스트하는 데 필요한 일반적인 설정의 예시입니다.

  let_it_be(:user) { create(:user) }
  let_it_be(:project) { create(:project, :repository, :in_group) }
  let_it_be(:role) { create(:member_role, :guest, :custom_permission, namespace: project.group) }
  let_it_be(:membership) { create(:project_member, :guest, member_role: role, user: user, project: project) }

  before do
    stub_licensed_features(custom_roles: true)

    sign_in(user)
  end

  describe MyController do
    describe '#show' do
      it 'allows access' do
        get my_controller_path(project)

        expect(response).to have_gitlab_http_status(:ok)
        expect(response).to render_template(:show)
      end
    end
  end
  • 아래는 GraphQL mutation을 테스트하는 데 필요한 일반적인 설정의 예시입니다.
  let_it_be(:user) { create(:user) }
  let_it_be(:project) { create(:project, :repository, :in_group) }
  let_it_be(:role) { create(:member_role, :guest, :custom_permission, namespace: project.group) }
  let_it_be(:membership) { create(:project_member, :guest, member_role: role, user: user, project: project) }

  before do
    stub_licensed_features(custom_roles: true)

    sign_in(user)
  end

  describe MyMutation do
    include GraphqlHelpers

    describe '#show' do
      let(:mutation) { graphql_mutation(:my_mutation) }

      it_behaves_like 'a working graphql query'
    end
  end
  • ProjectPolicy 및/또는 GroupPolicy에 테스트를 추가합니다. 아래는 ProjectPolicy 관련 변경 사항을 테스트하는 예시입니다.
  context 'for a member role with read_dependency true' do
    let(:member_role_abilities) { { read_dependency: true } }
    let(:allowed_abilities) { [:read_dependency] }

    it_behaves_like 'custom roles abilities'
  end

8단계: 문서 업데이트#

GitLab 문서에 기여하기 페이지를 따라 다음과 같이 문서를 변경합니다:

  • bundle exec rake gitlab:custom_roles:compile_docs를 실행하여 커스텀 ability 목록을 업데이트합니다

  • bundle exec rake gitlab:graphql:compile_docs를 실행하여 GraphQL 문서를 업데이트합니다

권한 에스컬레이션 고려 사항#

기본 역할은 일반적으로 해당 기본 역할과 상호 작용할 때 기본 역할에 해당하는 아티팩트의 생성 또는 관리를 허용하는 권한을 가지고 있습니다. 예를 들어, Developer가 프로젝트에 대한 액세스 토큰을 생성하면 해당 자격 증명에 Developer 액세스가 인코딩되어 생성됩니다. 새로운 커스텀 권한이 생성될 때 GitLab 아티팩트와 상호 작용 시 권한이 상승될 위험이 있을 수 있음을 명심하는 것이 중요하며, 적절한 안전장치 또는 기본 역할 검사를 추가해야 합니다.

시트 소비#

CUSTOMIZABLE_PERMISSIONS_EXEMPT_FROM_CONSUMING_SEAT 배열에 없는 ability의 활성화를 포함하는 멤버 역할에 Guest 권한을 가진 새 사용자가 추가되면, 시트가 소비됩니다. 이는 단순히 "상승된" 권한(ability)을 가진 게스트 사용자에 대해 Ultimate 고객에게 요금을 부과하고자 하는 것입니다. 이는 SaaS의 청구 가능 사용자(네임스페이스 구독에 포함되는 청구 가능 사용자)에게만 적용됩니다. 이 주제에 대한 자세한 내용은 이 이슈에서 확인할 수 있습니다.

모듈식 정책#

GitLab 모듈식 모놀리스 설계 문서를 지원하기 위해 Authorization 그룹Create:IDE 그룹협력하고 있습니다. POC가 구현되면 결과가 논의되고, Authorization 그룹이 앞으로의 정책 모듈식 설계에 대한 결정을 내릴 것입니다.

커스텀 역할 개발 가이드라인

GitLab v19.1
원문 보기
요약

Ultimate 고객은 커스텀 역할을 생성하고, 특정 권한(ability)을 지정하여 해당 역할을 정의할 수 있습니다. 예를 들어, 사용자는 read code와 admin merge requests 권한을 가지지만 admin issues와 같은 권한은 없는 "Engineer" 역할을 만들 수 있습니다.

Ultimate 고객은 커스텀 역할을 생성하고, 특정 권한(ability)을 지정하여 해당 역할을 정의할 수 있습니다.

예를 들어, 사용자는 read codeadmin merge requests 권한을 가지지만 admin issues와 같은 권한은 없는 "Engineer" 역할을 만들 수 있습니다.

이 맥락에서 "permission"과 "ability"라는 용어는 종종 혼용됩니다.

  • "Ability"는 사용자가 수행할 수 있는 작업입니다. 이는 Declarative Policy abilities에 매핑되며, ee/app/policies/*의 Policy 클래스에 존재합니다.

  • "Permission"은 사용자 대상 문서에서 ability를 지칭하는 방식입니다. 권한 문서는 수동으로 생성되므로, 문서에 나열된 권한과 Policy 클래스에 정의된 ability가 반드시 1:1로 매핑되지는 않습니다.

커스텀 역할 vs 기본 역할#

GitLab 15.9 이하에서 GitLab은 권한 시스템으로 기본 역할만 사용했습니다. 이 시스템에는 특정 권한(ability)에 정적으로 할당된 몇 가지 사전 정의된 역할이 있습니다. 이러한 기본 역할은 고객이 사용자 정의할 수 없습니다.

커스텀 역할을 사용하면 고객이 특정 사용자 그룹에 어떤 권한(ability)을 부여할지 결정할 수 있습니다. 예를 들어:

  • 기본 역할 시스템에서 취약점 읽기는 Developer 권한으로 제한됩니다.

  • 커스텀 역할 시스템에서 고객은 이 권한(ability)을 임의의 기본 역할을 기반으로 하는 새 커스텀 역할에 할당할 수 있습니다.

기본 역할과 마찬가지로, 커스텀 역할도 그룹 계층 내에서 상속됩니다. 사용자가 그룹에 대한 커스텀 역할을 가지고 있으면, 해당 사용자는 그룹 내의 모든 프로젝트나 하위 그룹에 대해서도 커스텀 역할을 가집니다.

기술 개요#

  • 개별 커스텀 역할은 member_roles 테이블(MemberRole 모델)에 저장됩니다.

  • member_roles 레코드는 namespace_id 외래 키를 통해 최상위 그룹(하위 그룹 아님)과 연결됩니다.

  • 그룹 또는 프로젝트 멤버십(members 레코드)은 member_role_id 외래 키를 통해 커스텀 역할과 연결됩니다.

  • 그룹 또는 프로젝트 멤버십은 해당 그룹 또는 프로젝트의 루트 수준 그룹에 정의된 임의의 커스텀 역할과 연결될 수 있습니다.

  • member_roles 테이블에는 개별 권한과 base_access_level 값이 포함됩니다.

  • base_access_level은 유효한 액세스 수준이어야 합니다.

가능한 값: 0 (액세스 없음), 5 (최소 액세스), 10 (Guest), 15 (Planner), 20 (Reporter), 25 (Security Manager), 30 (Developer), 40 (Maintainer), 50 (Owner), 60 (Admin).

  • base_access_level은 커스텀 역할에 포함되는 권한(ability)을 결정합니다. 예를 들어, base_access_level10이면, 커스텀 역할은 기본 Guest 권한이 받는 모든 권한(ability)에 더해, member_roles 레코드에서 read_code와 같은 속성을 true로 설정하여 활성화된 추가 권한(ability)을 포함합니다.

  • 커스텀 역할은 base_access_level에 대해 추가 권한(ability)을 활성화할 수 있지만 권한을 비활성화할 수는 없습니다. 따라서 커스텀 역할은 "추가만 가능(additive only)"합니다. 이 선택의 근거는 이 코멘트에 있습니다.

  • 커스텀 역할 권한(ability)은 프로젝트 수준과 그룹 수준에서 지원됩니다.

권한(ability) 리팩토링#

기존 권한(ability) 검사 찾기#

권한(ability)은 단일 엔드포인트 또는 웹 요청에 대해 여러 위치에서 검사되는 경우가 많습니다. 따라서 특정 엔드포인트에 대해 실행되는 인가 검사 목록을 찾기 어려울 수 있습니다.

이를 돕기 위해 로컬에서 GITLAB_DEBUG_POLICIES=true를 설정할 수 있습니다.

이 설정은 실행하는 모든 스펙에서 발생하는 요청에서 어떤 권한(ability)이 검사되는지에 대한 정보를 출력합니다. 출력에는 인가 검사가 이루어진 코드 라인도 포함됩니다. 호출자 정보는 메타프로그래밍이 사용된 경우에 특히 유용합니다. 해당 경우는 ability 이름 문자열로 grep하기 어렵기 때문입니다.

예시:

# 스펙 실행 예시

GITLAB_DEBUG_POLICIES=true bundle exec rspec spec/controllers/groups_controller_spec.rb:162

# 스펙 실행 시 권한 디버그 출력; 여러 정책 검사가 실행되면 모두 디버그 출력에 포함됩니다.

POLICY CHECK DEBUG -> policy: GlobalPolicy, ability: create_group, called_from: ["/gitlab/app/controllers/application_controller.rb:245:in `can?'", "/gitlab/app/controllers/groups_controller.rb:255:in `authorize_create_group!'"]

이 설정을 사용하여 리팩토링 중 인가 검사에 대해 더 많이 파악하세요. 기본 브랜치의 어떤 스펙에서도 이 설정을 활성화된 상태로 유지해서는 안 됩니다.

개별 권한(ability)의 로직 이해#

ability에 대한 참조는 DeclarativePolicy 클래스에 여러 번 나타날 수 있으며, 다른 ability를 참조하는 조건과 규칙에 의존합니다. 따라서 특정 ability에 적용되는 조건이 정확히 무엇인지 파악하기 어려울 수 있습니다.

DeclarativePolicy는 각 정책 클래스에 대해 ability_map을 제공하며, 이를 통해 ability에 대한 모든 규칙을 배열로 가져올 수 있습니다.

예시:

> GroupPolicy.ability_map.map.select { |k,v| k == :read_group_member }
=> {:read_group_member=>[[:enable, #], [:prevent, #]]}

> GroupPolicy.ability_map.map.select { |k,v| k == :read_group }
=> {:read_group=>
  [[:enable, #],
   [:enable, #],
   [:enable, #],
   [:enable, #],
   [:enable, #],
   [:enable, #],
   [:prevent, #],
   [:enable, #],
   [:prevent, #],
   [:prevent, #]]}

DeclarativePolicy는 특정 객체와 액터에 대한 로직 트리를 이해하는 데 사용할 수 있는 debug 메서드도 제공합니다. 출력은 ability_map의 규칙 목록과 유사합니다. 하지만 DeclarativePolicy는 ability를 prevent한 후에는 규칙 평가를 중지하므로, 모든 조건이 호출되지 않을 수 있습니다.

예시:

policy = GroupPolicy.new(User.last,  Group.last)
policy.debug(:read_group)

- [0] enable when public_group ((@custom_guest_user1 : Group/139))
- [0] enable when admin ((@custom_guest_user1 : Group/139))
- [0] enable when auditor ((@custom_guest_user1 : Group/139))
- [14] prevent when all?(~public_group, ~admin, user_banned_from_group) ((@custom_guest_user1 : Group/139))
- [14] prevent when needs_new_sso_session ((@custom_guest_user1 : Group/139))
- [16] enable when guest ((@custom_guest_user1 : Group/139))
- [16] enable when has_projects ((@custom_guest_user1 : Group/139))
- [16] enable when read_package_registry_deploy_token ((@custom_guest_user1 : Group/139))
- [16] enable when write_package_registry_deploy_token ((@custom_guest_user1 : Group/139))
  [21] prevent when all?(ip_enforcement_prevents_access, ~owner, ~auditor) ((@custom_guest_user1 : Group/139))

=> #,
 @enabled=false,
 @prevented=true>

권한(ability) 통합#

커스텀 역할에 추가되는 모든 기능은 최소한의 권한(ability)을 가져야 합니다. 대부분의 기능에서 read_*admin_*만 있으면 충분합니다. 다음을 모두 통합해야 합니다:

  • 보기 관련 권한(ability)은 read_* 아래에. 예: 목록 또는 상세 보기.

  • 객체 업데이트는 admin_* 아래에. 예: 객체 업데이트, 담당자 추가 또는 해당 객체 닫기. 일반적으로 admin_을 활성화하는 역할은 read_ 권한(ability)도 활성화되어 있어야 합니다. 이는 MemberRole 모델의 ALL_CUSTOMIZABLE_PERMISSIONS 해시의 requirement 옵션에 정의됩니다.

추가 권한(ability)이 필요한 기능이 있을 수 있지만, 이를 최소화하도록 노력하세요. 언제든지 Authentication and Authorization 그룹 멤버에게 의견이나 도움을 요청할 수 있습니다.

여기서부터 작업을 시작해야 합니다. 작업 중인 기능의 모든 권한(ability)을 파악하고, 해당 권한(ability)을 read_, admin_, 또는 필요한 경우 추가 권한(ability)으로 통합하세요.

GroupPolicyProjectPolicy 클래스의 많은 권한(ability)에는 중복된 정책이 많이 있습니다. 이러한 Policy 클래스를 통합하기 위한 에픽이 있습니다. 이러한 클래스에서 유사한 권한을 발견하면, 동일한 이름을 갖도록 리팩토링하는 것을 고려하세요.

예를 들어, GroupPolicyread_group_security_dashboard라는 ability가 있고 ProjectPolicyread_project_security_dashboard라는 ability가 있다고 가정합니다. 두 가지 모두 사용자 정의 가능하게 만들고 싶다면, member_roles 테이블에 각 ability에 대한 행을 추가하는 대신 이름을 read_security_dashboard로 변경하고 member_roles 테이블에 read_security_dashboard를 추가하는 것을 고려하세요. 상위 그룹에서 read_security_dashboard를 활성화하면 커스텀 역할이 그룹 보안 대시보드와 해당 그룹의 각 프로젝트에 대한 프로젝트 보안 대시보드에 액세스할 수 있습니다. 특정 프로젝트에서 동일한 권한을 활성화하면 해당 프로젝트의 보안 대시보드에 액세스할 수 있습니다.

커스텀 역할에 권한(ability) 지원 추가 방법#

기존 ability를 추가하는 경우, 아래 단계를 완료하기 전에 별도의 머지 리퀘스트에서 먼저 해당 기능의 ability를 리팩토링 및 통합하는 것을 고려하세요.

1단계: 구성 파일 생성#

  • ./ee/bin/custom-ability 을 실행하여 새 ability에 대한 구성 파일을 생성합니다.

  • 이렇게 하면 ee/config/custom_abilities에 다음 스키마를 따르는 YAML 파일이 생성됩니다:

필드 필수 여부 설명
name 커스텀 ability를 설명하는 고유하고, 소문자이며 밑줄로 구분된 이름. 파일명과 일치해야 합니다.
title 커스텀 ability의 사람이 읽을 수 있는 제목.
description 커스텀 ability의 사람이 읽을 수 있는 설명.
feature_category 기능 카테고리 이름. 예: vulnerability_management.
introduced_by_issue 이 커스텀 ability의 추가를 제안한 이슈 URL.
introduced_by_mr 이 커스텀 ability를 추가한 MR URL.
milestone 이 커스텀 ability가 추가된 마일스톤.
admin_ability 아니오 이 ability가 관리자 수준에서 검사되는지 여부를 나타내는 불리언 값.
group_ability 이 ability가 그룹 수준에서 검사되는지 여부를 나타내는 불리언 값.
enabled_for_group_access_levels group_ability = true인 경우 그룹에서 이 커스텀 ability에 이미 액세스할 수 있는 액세스 수준의 배열. 개별 ability의 로직 이해 섹션을 참조하여 ability의 기본 액세스 수준을 결정하는 데 도움을 받으세요. 이는 정보용이며 커스텀 역할 동작에는 영향을 미치지 않습니다.
project_ability 이 ability가 프로젝트 수준에서 검사되는지 여부를 나타내는 불리언 값.
enabled_for_project_access_levels project_ability = true인 경우 프로젝트에서 이 커스텀 ability에 이미 액세스할 수 있는 액세스 수준의 배열. 개별 ability의 로직 이해 섹션을 참조하여 ability의 기본 액세스 수준을 결정하는 데 도움을 받으세요. 이는 정보용이며 커스텀 역할 동작에는 영향을 미치지 않습니다.
requirements 아니오 이 ability가 의존하는 커스텀 권한 목록. 예를 들어 admin_vulnerability는 read_vulnerability에 의존합니다. 없으면 []를 입력하세요.
project_permissions project_ability = true인 경우 이 커스텀 ability가 프로젝트 수준에서 부여될 때 활성화할 권한 목록. 이러한 권한은 정책 규칙을 수동으로 추가할 필요 없이 ProjectPolicy에 자동으로 적용됩니다.
group_permissions group_ability = true인 경우 이 커스텀 ability가 그룹 수준에서 부여될 때 활성화할 권한 목록. 이러한 권한은 정책 규칙을 수동으로 추가할 필요 없이 GroupPolicy에 자동으로 적용됩니다.

2단계: 스펙 파일 생성 및 유효성 검사 스키마 업데이트#

  • bundle exec rails generate gitlab:custom_roles:code --ability 을 실행합니다. 이렇게 하면 권한 유효성 검사 스키마 파일이 업데이트되고 빈 스펙 파일이 생성됩니다.

3단계: ability를 작업 중(WIP)으로 표시 (선택 사항)#

ability가 여러 MR에 걸쳐 구현되는 경우, 출시 준비가 완료될 때까지 공개 API 표면에 나타나지 않도록 작업 중으로 표시합니다. ability YAML에 wip: true를 추가하세요:

# ee/config/custom_abilities/read_dependency.yml
---
name: read_dependency
wip: true
# ... other fields

WIP ability는 부팅 시 로드된 권한 세트에서 제외됩니다. REST 요청 파라미터, REST 응답 본문, 또는 GraphQL enum에 나타나지 않습니다. 해당 ability가 설정된 상태로 역할을 생성하거나 조회할 수 없습니다.

로컬에서 ability를 작업하려면 Rails를 시작하기 전에 GITLAB_LOAD_WIP_CUSTOM_ABILITIES 환경 변수를 설정하세요:

export GITLAB_LOAD_WIP_CUSTOM_ABILITIES=true

스펙은 spec_helper.rb를 통해 기본적으로 이 변수가 설정된 상태로 실행되므로, 모든 테스트에서 WIP ability를 볼 수 있습니다. 프로덕션과 유사한 게이팅 동작을 검증해야 하는 테스트는 :disable_wip_custom_abilities RSpec 메타데이터를 사용하여 활성화할 수 있습니다.

ability 출시 준비가 완료되면 wip: 키를 제거하세요.

4단계: YAML 구성 파일에서 권한 정의#

권한은 project_permissionsgroup_permissions 필드를 사용하여 YAML 구성 파일에 선언적으로 정의됩니다. 시스템은 이러한 정의에서 정책 규칙을 자동으로 생성합니다. ProjectPolicy 또는 GroupPolicy를 수동으로 업데이트할 필요가 없습니다.

예를 들어, read_dependency 커스텀 ability를 추가하는 경우 YAML 파일에 다음을 포함해야 합니다:

# ee/config/custom_abilities/read_dependency.yml
---
name: read_dependency
group_ability: true
project_ability: true
project_permissions:
  - read_dependency
group_permissions:
  - read_dependency
# ... other fields

시작 시 시스템은 이러한 YAML 파일을 읽고 GroupPolicyProjectPolicy 모두에서 각 ability에 대한 custom_role_enables_<ability> 조건과 해당 규칙을 동적으로 생성합니다. read_dependency를 포함하는 커스텀 역할을 가진 사용자가 프로젝트 또는 그룹에 액세스하면, 시스템은 각각 project_permissions 또는 group_permissions에 나열된 권한을 자동으로 활성화합니다.

커스텀 ability는 여러 권한을 활성화할 수 있습니다. 예를 들어, 배포 토큰을 관리하는 ability는 그룹과 프로젝트 수준에서 서로 다른 권한을 활성화합니다:

# ee/config/custom_abilities/manage_deploy_tokens.yml
group_permissions:
  - manage_deploy_tokens
  - read_deploy_token
  - create_deploy_token
  - destroy_deploy_token
project_permissions:
  - manage_deploy_tokens
  - read_deploy_token
  - create_deploy_token
  - destroy_deploy_token

모든 ability가 두 수준 모두에서 활성화될 필요는 없습니다. 예를 들어, admin_terraform_state는 사용자가 프로젝트의 Terraform 상태를 관리할 수 있도록 합니다. project_permissions만 정의하면 되고, group_permissions는 필요하지 않습니다.

5단계: 역할 액세스 확인#

  • GITLAB_SIMULATE_SAAS=1로 SaaS 모드가 활성화되어 있는지 확인합니다.

  • Owner인 그룹으로 이동한 후 설정 -> 역할 및 권한으로 이동합니다.

  • 새 역할을 선택하고 방금 만든 권한으로 커스텀 역할을 생성합니다.

  • 그룹의 관리 -> 멤버 페이지로 이동하여 멤버를 새로 생성한 커스텀 역할에 할당합니다.

  • 다음으로 해당 멤버로 로그인하여 커스텀 ability가 의도한 페이지에 액세스할 수 있는지 확인합니다.

6단계: 고급 검색에 대한 영향 평가#

커스텀 역할은 ability가 고급 검색에 의해 인덱싱되는 데이터에 영향을 미치는 경우 고급 검색 기능에 영향을 줄 수 있습니다.

  • 고급 검색을 활성화하고 인스턴스를 인덱싱합니다

  • 커스텀 역할이 할당된 멤버로 그룹에 로그인합니다

  • 검색 또는 이동...으로 이동하여 전체 검색을 수행합니다. 검색어를 입력하고 모든 GitLab에서 검색하도록 선택합니다.

  • 사용자가 커스텀 역할의 영향을 받는 데이터를 검색할 수 있는지 확인합니다

  • 그룹 페이지로 이동한 후 검색 또는 이동...으로 이동하여 그룹 검색을 수행합니다. 검색어를 입력하고 그룹에서 검색하도록 선택합니다.

  • 사용자가 커스텀 역할의 영향을 받는 데이터를 검색할 수 있는지 확인합니다

  • 필요한 경우 검색 인가를 업데이트합니다

7단계: 스펙 추가#

  • ee/spec/factories/member_roles.rbMemberRoles 팩토리에 ability를 트레이트로 추가합니다.

  • ee/spec/requests/custom_roles//request_spec.rb에 테스트를 추가하여 사용자가 커스텀 ability를 할당받은 후 컨트롤러, REST API 엔드포인트 및 GraphQL API 엔드포인트에 성공적으로 액세스할 수 있는지 확인합니다.

  • 아래는 Rails 컨트롤러 엔드포인트를 테스트하는 데 필요한 일반적인 설정의 예시입니다.

  let_it_be(:user) { create(:user) }
  let_it_be(:project) { create(:project, :repository, :in_group) }
  let_it_be(:role) { create(:member_role, :guest, :custom_permission, namespace: project.group) }
  let_it_be(:membership) { create(:project_member, :guest, member_role: role, user: user, project: project) }

  before do
    stub_licensed_features(custom_roles: true)

    sign_in(user)
  end

  describe MyController do
    describe '#show' do
      it 'allows access' do
        get my_controller_path(project)

        expect(response).to have_gitlab_http_status(:ok)
        expect(response).to render_template(:show)
      end
    end
  end
  • 아래는 GraphQL mutation을 테스트하는 데 필요한 일반적인 설정의 예시입니다.
  let_it_be(:user) { create(:user) }
  let_it_be(:project) { create(:project, :repository, :in_group) }
  let_it_be(:role) { create(:member_role, :guest, :custom_permission, namespace: project.group) }
  let_it_be(:membership) { create(:project_member, :guest, member_role: role, user: user, project: project) }

  before do
    stub_licensed_features(custom_roles: true)

    sign_in(user)
  end

  describe MyMutation do
    include GraphqlHelpers

    describe '#show' do
      let(:mutation) { graphql_mutation(:my_mutation) }

      it_behaves_like 'a working graphql query'
    end
  end
  • ProjectPolicy 및/또는 GroupPolicy에 테스트를 추가합니다. 아래는 ProjectPolicy 관련 변경 사항을 테스트하는 예시입니다.
  context 'for a member role with read_dependency true' do
    let(:member_role_abilities) { { read_dependency: true } }
    let(:allowed_abilities) { [:read_dependency] }

    it_behaves_like 'custom roles abilities'
  end

8단계: 문서 업데이트#

GitLab 문서에 기여하기 페이지를 따라 다음과 같이 문서를 변경합니다:

  • bundle exec rake gitlab:custom_roles:compile_docs를 실행하여 커스텀 ability 목록을 업데이트합니다

  • bundle exec rake gitlab:graphql:compile_docs를 실행하여 GraphQL 문서를 업데이트합니다

권한 에스컬레이션 고려 사항#

기본 역할은 일반적으로 해당 기본 역할과 상호 작용할 때 기본 역할에 해당하는 아티팩트의 생성 또는 관리를 허용하는 권한을 가지고 있습니다. 예를 들어, Developer가 프로젝트에 대한 액세스 토큰을 생성하면 해당 자격 증명에 Developer 액세스가 인코딩되어 생성됩니다. 새로운 커스텀 권한이 생성될 때 GitLab 아티팩트와 상호 작용 시 권한이 상승될 위험이 있을 수 있음을 명심하는 것이 중요하며, 적절한 안전장치 또는 기본 역할 검사를 추가해야 합니다.

시트 소비#

CUSTOMIZABLE_PERMISSIONS_EXEMPT_FROM_CONSUMING_SEAT 배열에 없는 ability의 활성화를 포함하는 멤버 역할에 Guest 권한을 가진 새 사용자가 추가되면, 시트가 소비됩니다. 이는 단순히 "상승된" 권한(ability)을 가진 게스트 사용자에 대해 Ultimate 고객에게 요금을 부과하고자 하는 것입니다. 이는 SaaS의 청구 가능 사용자(네임스페이스 구독에 포함되는 청구 가능 사용자)에게만 적용됩니다. 이 주제에 대한 자세한 내용은 이 이슈에서 확인할 수 있습니다.

모듈식 정책#

GitLab 모듈식 모놀리스 설계 문서를 지원하기 위해 Authorization 그룹Create:IDE 그룹협력하고 있습니다. POC가 구현되면 결과가 논의되고, Authorization 그룹이 앞으로의 정책 모듈식 설계에 대한 결정을 내릴 것입니다.