커스텀 롤

Ultimate 고객은 커스텀 롤을 생성하고 지정된 능력을 할당함으로써 해당 롤을 정의할 수 있습니다.

예를 들어, 사용자는 “엔지니어” 역할을 만들어 코드 읽기병합 요청 관리자 능력을 부여할 수 있지만, 이슈 관리자와 같은 능력을 부여하지 않을 수 있습니다.

이 문맥에서 “권한”과 “능력”이라는 용어는 종종 서로 교환되어 사용됩니다.

  • “능력”은 사용자가 수행할 수 있는 작업입니다. 이것들은 선언적 정책 능력과 일치하며 ee/app/policies/*의 정책 클래스에 포함됩니다.
  • “권한”은 사용자 기준 문서에서 능력을 참조하는 방식입니다(insert-your-URL-here). 사용자 기준 문서에는 수동으로 생성되므로 문서에 나열된 권한과 정책 클래스에 정의된 능력 간에 1:1 대응이 반드시 있지는 않습니다.

커스텀 롤 대 기본 롤

GitLab 15.9 이전에는 GitLab은 권한 시스템으로 기본 롤만 가지고 있었습니다. 이 시스템에서 특정 능력에 정적으로 할당된 몇 가지 기본 롤이 있습니다. 이러한 기본 롤은 고객이 사용자화할 수 없습니다.

커스텀 롤을 사용하면 고객은 특정 사용자 그룹에 어떤 능력을 할당할지를 결정할 수 있습니다. 예를 들어:

  • 기본 롤 시스템에서 취약점 읽기는 개발자 역할에만 허용됩니다.
  • 커스텀 롤 시스템에서 고객은 이 능력을 기본 역할 중 아무 역할을 기반으로 새로운 커스텀 롤에 할당할 수 있습니다.

기본 롤과 마찬가지로 커스텀 롤은 그룹 계층 구조 내에서 상속됩니다. 사용자가 그룹에 대해 커스텀 롤을 가지고 있는 경우, 해당 사용자는 해당 그룹의 프로젝트나 하위 그룹에 대해서도 커스텀 롤을 가집니다.

기술적 개요

  • 개별 커스텀 롤은 member_roles 테이블 (MemberRole 모델)에 저장됩니다.
  • member_roles 레코드는 namespace_id 외래 키를 통해 최상위 그룹과 연관되어 있습니다 (하위 그룹이 아님).
  • 그룹 또는 프로젝트 멤버십(members 레코드)은 member_role_id 외래 키를 통해 커스텀 롤과 연관되어 있습니다.
  • 그룹 또는 프로젝트 멤버십은 해당 그룹이나 프로젝트의 최상위 그룹에 정의된 커스텀 롤과 연관짓습니다.
  • member_roles 테이블에는 개별 권한과 base_access_level 값이 포함되어 있습니다.
  • base_access_level유효한 액세스 레벨이어야 합니다. base_access_level은 커스텀 롤에 포함되는 능력을 결정합니다. 예를 들어, base_access_level10이면 커스텀 롤은 기본 게스트 역할이 수신하는 모든 능력과 read_code와 같이 어떤 속성이 true로 설정되는 경우 추가 능력을 포함합니다.
  • 커스텀 롤은 base_access_level에 대한 추가 능력을 활성화할 수 있지만 권한을 비활성화할 수는 없습니다. 결과적으로 커스텀 롤은 “추가적”입니다. 이 선택의 근거는 이 코멘트에서 확인할 수 있습니다.
  • 커스텀 롤 능력은 프로젝트 레벨과 그룹 레벨에서 지원됩니다.

능력 리팩터링

기존 능력 확인

능력은 종종 하나의 엔드포인트나 웹 요청에 대해 여러 위치에서 확인됩니다. 따라서 특정 엔드포인트에 대해 실행되는 권한 확인 목록을 찾는 것이 어려울 수 있습니다.

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

이는 실행하는 모든 specs의 요청에서 확인된 능력에 대한 정보를 출력합니다. 출력에는 권한 확인이 이뤄진 코드 라인도 포함됩니다. 호출자 정보는 특히 메타프로그래밍을 사용하는 경우 어려운 경우가 있기 때문에 도움이 됩니다.

예를 들어:

# spec 실행 예시

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!'"]

리팩터링 중에 권한 확인에 대한 자세한 내용을 알아보기 위해 이 설정을 사용하세요. 기본 브랜치에서는 어떤 specs에 대해서도 이 설정을 유지하지 않아야 합니다.

개별 능력 로직 이해

능력에 대한 참조는 상황과 다른 능력을 참조하는 조건 및 규칙에 의존하여 DeclarativePolicy 클래스에 여러 번 나타날 수 있습니다. 결과적으로 특정 능력에 대한 정확한 조건을 알기가 어려울 수 있습니다.

DeclarativePolicy는 각 정책 클래스에 대한 ability_map을 제공하는데, 이는 특정 능력에 대한 모든 규칙을 배열로 모아줍니다.

예시:

> GroupPolicy.ability_map.map.select { |k,v| k == :read_group_member }
=> {:read_group_member=>[[:enable, #<Rule can?(:read_group)>], [:prevent, #<Rule ~can_read_group_member>]]}

> GroupPolicy.ability_map.map.select { |k,v| k == :read_group }
=> {:read_group=>
  [[:enable, #<Rule public_group>],
   [:enable, #<Rule logged_in_viewable>],
   [:enable, #<Rule guest>],
   [:enable, #<Rule admin>],
   [:enable, #<Rule has_projects>],
   [:enable, #<Rule read_package_registry_deploy_token>],
   [:enable, #<Rule write_package_registry_deploy_token>],
   [:prevent, #<Rule all?(~public_group, ~admin, user_banned_from_group)>],
   [:enable, #<Rule auditor>],
   [:prevent, #<Rule needs_new_sso_session>],
   [:prevent, #<Rule all?(ip_enforcement_prevents_access, ~owner, ~auditor)>]]}

DeclarativePolicy에는 또한 특정 객체 및 액터에 대한 논리 트리를 이해하기 위해 사용할 수 있는 debug 메서드가 있습니다. 출력은 ability_map의 규칙 목록과 유사합니다. 그러나 DeclarativePolicy는 능력을 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 logged_in_viewable ((@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))

=> #<DeclarativePolicy::Runner::State:0x000000015c665050
 @called_conditions=
  #<Set: {
   "/dp/condition/GroupPolicy/public_group/Group:139",
   "/dp/condition/GroupPolicy/logged_in_viewable/User:83,Group:139",
   "/dp/condition/BasePolicy/admin/User:83",
   "/dp/condition/BasePolicy/auditor/User:83",
   "/dp/condition/GroupPolicy/user_banned_from_group/User:83,Group:139",
   "/dp/condition/GroupPolicy/needs_new_sso_session/User:83,Group:139",
   "/dp/condition/GroupPolicy/guest/User:83,Group:139",
   "/dp/condition/GroupPolicy/has_projects/User:83,Group:139",
   "/dp/condition/GroupPolicy/read_package_registry_deploy_token/User:83,Group:139",
   "/dp/condition/GroupPolicy/write_package_registry_deploy_token/User:83,Group:139"}>,
 @enabled=false,
 @prevented=true>

능력 통합

사용자 정의 역할에 추가된 모든 기능은 최소한의 능력이 있어야 합니다. 대부분의 기능에는 read_*admin_*만 있으면 충분합니다. 다음을 모두 통합해야 합니다.

  • read_* 아래에 있는 뷰 관련된 능력. 예를 들어, 목록 또는 상세 보기.
  • admin_* 아래에 객체 업데이트가 포함됩니다. 예를 들어, 객체 업데이트, 담당자 추가 또는 닫기와 같은 객체. 보통 admin_을 활성화하는 역할은 read_ 능력도 활성화되어야 합니다. 이는 MemberRole 모델의 ALL_CUSTOMIZABLE_PERMISSIONS 해시의 requirement 옵션에 정의됩니다.

추가 능력이 필요한 기능이 있을 수 있지만 최소화하세요. 언제든지 인증 및 허가 그룹의 회원들에게 의견이나 도움을 요청할 수 있습니다.

여기서 작업을 시작해야 합니다. 작업 중인 기능에 대한 모든 능력을 가져와서 필요한 경우 그 능력을 read_, admin_ 또는 추가 능력으로 통합해야 합니다.

GroupPolicyProjectPolicy 클래스의 많은 불필요한 정책이 있습니다. 이러한 정책 클래스를 통합하기 위한 epic이 있습니다. 이러한 클래스에서 유사한 권한을 발견하면 동일한 이름을 갖도록 리팩터링하는 것을 고려하세요.

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

사용자 정의 역할에 능력 추가하는 방법

기존 능력을 추가할 때, 아래 단계를 완료하기 전에 별도의 병합 요청에서 기능을 위한 능력 리팩터링 & 통합을 고려하세요.

단계 1. 구성 파일 생성

  • ./ee/bin/custom-ability <ABILITY_NAME>을 실행하여 새 능력을 위한 구성 파일을 생성합니다.
  • 이를 통해 ee/config/custom_abilities에 YAML 파일이 생성되며 다음 스키마를 따릅니다:
필드 필수 설명
name 사용자 정의 능력을 설명하는 고유한 소문자 및 밑줄 이름. 파일 이름과 일치해야 합니다.
title 사용자 정의 능력의 사람이 읽기 쉬운 제목
description 사용자 정의 능력에 대한 사람이 읽기 쉬운 설명
feature_category 기능 카테고리의 이름. 예를 들어, vulnerability_management.
introduced_by_issue 이 사용자 정의 능력의 추가를 제안한 이슈 URL
introduced_by_mr 이 사용자 정의 능력을 추가한 MR URL
milestone 이 사용자 정의 능력이 추가된 마일스톤
group_ability 이 능력을 그룹 수준에서 확인하는지 여부를 나타내는 부울 값
project_ability 이 능력을 프로젝트 수준에서 확인하는지 여부를 나타내는 부울 값
requirements 아니요 이 능력이 종속된 사용자 정의 권한 목록. 예를 들어 admin_vulnerabilityread_vulnerability에 의존합니다. 없으면 []을 입력하세요.
available_from_access_level 아니요 해당 능력의 기본 액세스 수준, 적용 가능한 경우. 능력의 기본 액세스 수준을 결정하는 데 도움이 될 개별 능력의 논리 이해 섹션을 참조하세요. 이것은 참고용으로만 제공되며 사용자 정의 역할의 작동에는 영향을 미치지 않습니다.

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

  • bundle exec rails generate gitlab:custom_roles:code --ability <ABILITY_NAME>을 실행하여 권한 유효성 검사 스키마 파일을 업데이트하고 빈 spec 파일을 만듭니다.

단계 3: 정책 업데이트

  • 능력을 그룹 수준에서 확인하는 경우, 능력을 활성화하기 위해 GroupPolicy에 규칙을 추가하세요.
  • 예를 들어: 추가하려는 능력이 read_dependency인 경우, ee/app/policies/ee/group_policy.rb의 업데이트는 다음과 같습니다:
rule { custom_role_enables_read_dependency }.enable(:read_dependency)
  • 마찬가지로, 능력이 프로젝트 수준에서 확인되는 경우, 능력을 활성화하기 위해 ProjectPolicy에 규칙을 추가하세요.
  • 예를 들어: 추가하려는 능력이 read_dependency인 경우, ee/app/policies/ee/project_policy.rb의 업데이트는 다음과 같습니다:
rule { custom_role_enables_read_dependency }.enable(:read_dependency)
  • 모든 능력이 두 수준에서 활성화되어야 하는 것은 아니며, admin_terraform_state은 프로젝트의 terraform 상태를 관리할 수 있도록 허용합니다. 그것은 그룹 수준이 아닌 프로젝트 수준에서만 활성화되어야 하므로, ee/app/policies/ee/project_policy.rb에만 구성해야 합니다.

단계 4: 검증

  • GITLAB_SIMULATE_SAAS=1로 SaaS 모드가 활성화되었는지 확인하세요.
  • 소유자인 모든 그룹에서 ‘Settings -> Roles and permissions’로 이동하세요.
  • 새 역할을 선택하고 방금 만든 사용자 정의 능력을 가진 사용자 정의 역할을 생성하세요.
  • 그룹의 Manage -> Members 페이지로 이동하여 새로 생성된 사용자 정의 역할에 멤버를 할당하세요.
  • 다음으로 해당 멤버로서 로그인하고 해당 사용자 정의 능력을 위한 페이지에 액세스할 수 있는지 확인하세요.

단계 5: 스펙 추가

  • MemberRoles 팩토리에 능력을 특성으로 추가하세요. ee/spec/factories/member_roles.rb.
  • 사용자가 사용자 정의 능력이 할당된 후 컨트롤러, REST API 엔드포인트 및 GraphQL API 엔드포인트에 성공적으로 액세스할 수 있는지 확인하기 위해 ee/spec/requests/custom_roles/<ABILITY_NAME>/request_spec.rb에 테스트를 추가하세요.
  • 아래는 일반적으로 Rails 컨트롤러 엔드포인트를 테스트하는 데 필요한 설정 예입니다.
  let_it_be(:user) { create(:user) }
  let_it_be(:project) { create(:project, :repository, :in_group) }
  let_it_be(:role) { create(:member_role, :guest, namespace: project.group, custom_permission: true) }
  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 뮤테이션을 테스트하는 데 필요한 일반적인 설정 예입니다.
  let_it_be(:user) { create(:user) }
  let_it_be(:project) { create(:project, :repository, :in_group) }
  let_it_be(:role) { create(:member_role, :guest, namespace: project.group, custom_permission: true) }
  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

단계 6: 문서 업데이트

다음을 수행하여 문서를 업데이트하려면 GitLab 문서 기여 페이지를 따르세요:

  • bundle exec rake gitlab:custom_roles:compile_docs를 실행하여 사용자 정의 권한 목록을 업데이트하세요.
  • bundle exec rake gitlab:graphql:compile_docs를 실행하여 GraphQL 문서를 업데이트하세요.

특권 상승 고려 사항

기본 역할에는 일반적으로 해당 artifact와 상호 작용할 때 해당 기본 역할에 해당하는 artifact의 생성 또는 관리를 허용하는 권한이 있습니다. 예를 들어, ‘Developer’가 프로젝트용 엑세스 토큰을 생성하는 경우 해당 자격 증명에 ‘Developer’ 액세스가 부여됩니다. 새로운 사용자 정의 권한이 생성됨에 따라 GitLab artifact와 상호 작용할 때 특권이 상승할 가능성이 있으며 적절한 안전장치 또는 기본 역할 확인이 추가되어야 합니다.

Seat 소비

Guest 역할을 가진 새로운 사용자가 CUSTOMIZABLE_PERMISSIONS_EXEMPT_FROM_CONSUMING_SEAT 배열에 포함되지 않은 능력을 활성화하는 멤버 역할에 추가되면 Seat이 소비됩니다. 우리는 “고급” 능력을 가진 게스트 사용자에 대해 고급 고객에게 청구합니다. 이는 SaaS의 요금 납부 대상 사용자에만 해당됩니다(네임스페이스 구독에 포함된 요금 납부 대상 사용자). 이 주제에 대한 자세한 내용은 이 이슈에서 찾을 수 있습니다.

모듈러 정책

GitLab 모듈러 모놀리스 설계 문서를 지원하기 위해 인가 그룹Create:IDE 그룹협력하고 있습니다. POC가 구현되면 결과가 토론되고 인가 그룹에서 모듈러 정책의 설계를 앞으로 진행할 결정을 내리게 될 것입니다.