InfoGrab DocsInfoGrab Docs

할당 가능한 권한

요약

할당 가능한 권한은 하나 이상의 원시 권한을 사용자에게 보이는 권한 그룹으로 묶습니다. config/authz/permission_groups/assignable_permissions/<category>/<resource>/<action>.yml 경로에 새 YAML 파일을 직접 만듭니다.

할당 가능한 권한#

할당 가능한 권한은 하나 이상의 원시 권한을 사용자에게 보이는 권한 그룹으로 묶습니다. 이를 사용해 사용자에게 제시되는 세분화 수준을 조정할 수 있으며, 권한을 세밀하게 묶을지(예: read issue와 read snippet 권한을 각각 분리) 더 넓게 묶을지(예: 모든 read work item 권한을 함께) 제품 그룹이 결정할 수 있습니다. 이렇게 하면 코드 수준에서는 세분화된 제어를 유지하면서 UI에서는 사용자가 다루기 쉬운 경험을 제공합니다.

할당 가능한 권한 파일 만들기#

config/authz/permission_groups/assignable_permissions/<category>/<resource>/<action>.yml 경로에 새 YAML 파일을 직접 만듭니다.

---
name: run_job
description: Grants the ability to run jobs
permissions:
  - play_job
  - retry_job
boundaries:
  - group
  - project

할당 가능한 권한 파일 필드#

필드 설명
name 할당 가능한 권한의 고유 식별자
description 할당 가능한 권한이 부여하는 내용을 사람이 읽을 수 있게 적은 설명
permissions 이 할당 가능한 권한에 포함된 원시 권한 배열(원시 권한 정의 파일로 이미 존재해야 함)
boundaries 할당 가능한 권한이 적용되는 조직 수준 목록
available_for 이 권한을 사용할 수 있는 소비자로, granular_access_token(세분화된 PAT), role(표준 및 커스텀 역할) 또는 둘 다입니다. granular_access_token을 선언하는 할당 가능한 권한은 그 원시 권한 중 하나 이상이 REST 권한 부여 데코레이터나 GraphQL 세분화 스코프 지시자에서 참조되어야 합니다.
assignable_when 선택 사항입니다. 토큰 생성 UI에서 해당 권한이 제시되려면 현재 사용자가 충족해야 하는 조건입니다. 조건부 할당 가능한 권한을 참고합니다.
deprecated 선택 사항입니다. true로 설정하면 UI에서 할당 가능한 권한을 숨겨 사용자가 새 토큰을 만들 때 더 이상 선택할 수 없습니다. 이미 이 권한을 가진 기존 토큰은 계속 동작합니다. 이름 변경 마이그레이션 중이거나 권한을 단계적으로 제거할 때 사용합니다.

디렉터리 구조 이해#

디렉터리 구조는 <category>/<resource>/<action>.yml의 세 단계를 사용합니다.

메타데이터 파일이 필요한 경우#

파일 필요한 경우 목적
카테고리 .metadata.yml 선택 사항 폴더 이름 표시 재정의(예: ci_cd → "Ci Cd" 대신 "CI/CD")
리소스 .metadata.yml 선택 사항 생성된 리소스 설명이나 대문자화된 리소스 이름 재정의
Note

<category>/<resource>/<action>.yml의 할당 가능한 권한 YAML 파일은 항상 필수이며 메타데이터 파일이 아닙니다. 권한 번들을 정의하는 주 구성 파일입니다.

카테고리 수준: <category> 하위 폴더는 할당 가능한 권한이 묶이는 UI에 표시되는 카테고리 이름을 나타냅니다. 폴더 이름은 표시될 때 대문자화됩니다(예: project_management는 "Project Management" 가 됩니다). 이 카테고리 이름은 권한 선택 UI에 표시되어 사용자가 기능 영역별로 권한을 정리하고 찾는 데 도움이 됩니다.

대문자화가 잘못된 표시 이름을 만들 때만 카테고리 폴더에 .metadata.yml 파일을 만듭니다. 예를 들어 대문자화가 잘 되지 않는 약어나 축약어가 이에 해당합니다.

---
name: "CI/CD"

카테고리 수준 메타데이터 예시:

  • 폴더: project_management → 메타데이터 없음: "Project Management" 로 표시
  • 폴더: ci_cd → 메타데이터 없음: "Ci Cd" 로 표시(잘못됨)
  • 폴더: ci_cd → .metadata.yml 재정의 적용: "CI/CD" 로 표시(올바름)

리소스 수준: 기본값을 재정의하려면 config/authz/permission_groups/assignable_permissions/<category>/<resource>/.metadata.yml에 .metadata.yml 파일을 선택적으로 만듭니다.

---
description: "Grants the ability to <actions> SSH keys."
name: "SSH Key"

기본적으로 리소스 이름은 디렉터리 이름에서(대문자화하여) 파생되고, 설명은 리소스의 액션에서 생성됩니다(예: "Grants the ability to assign, create, and read runners.").

필드:

  • description(선택 사항) - 생성된 리소스 설명을 재정의합니다. <actions> 보간을 포함해야 하며, 이 보간은 런타임에 리소스의 YAML 파일에 있는 액션 목록을 알파벳순으로 정렬한 값으로 치환됩니다. <actions>를 사용하면 명사를 원하는 대로 바꾸면서도 액션 목록을 자동으로 동기화 상태로 유지합니다(예: "Grants the ability to <actions> CI variables.").
  • name(선택 사항) - 표시용으로 대문자화된 리소스 이름을 재정의합니다. 대문자화가 제대로 동작하지 않는 약어나 특수한 형식에 사용합니다(예: 자동 대문자화된 이름 대신 name: "SSH Key")

UI 예시:

다음 스크린샷은 카테고리와 리소스 메타데이터가 권한 선택 UI에 표시되는 방식을 보여 줍니다.

리소스 메타데이터를 보여 주는 권한 선택 UI

이 예시에서 각 항목은 다음과 같습니다.

  • CI/CD - 폴더 이름에서 가져온 카테고리 이름이며, 카테고리 .metadata.yml로 재정의할 수 있습니다
  • CI Config - 폴더 이름에서 가져온 리소스 이름이며, 리소스 .metadata.yml로 재정의할 수 있습니다
  • 그 아래 설명은 리소스 설명으로, 리소스 .metadata.yml 파일에서 재정의하거나 리소스의 액션에서 생성됩니다

리소스 .metadata.yml을 만들어야 하는 경우:

  • 리소스 이름에 대문자화가 제대로 되지 않는 약어나 브랜드 이름이 포함된 경우(예: ssh_key → "SSH Key" 가 아니라 "Ssh Key")
  • 생성된 설명에 별도의 명사가 필요한 경우(예: "variables" 대신 "CI variables", "codes" 대신 "code")
  • 리소스에 문장으로 쓰면 어색한 특이한 액션 이름이 있는 경우(예: renew_secret)
  • 디렉터리 이름이 셀 수 없거나 복수형이 어색한 경우(예: "code" → "codes", "metadata" → "metadatas")

디렉터리 이름이 대문자화와 복수형 처리가 올바르게 되는 경우(대부분의 리소스)에는 메타데이터 파일이 필요하지 않습니다.

경계 결정#

boundaries 필드는 이 할당 가능한 권한을 어떤 조직 수준에서 지원하는지 지정합니다. 묶인 원시 권한을 적용할 수 있는 위치를 기준으로 선택합니다. 최소 권한 원칙에 따라 권한이 실제로 적용되는 경계만 포함합니다.

경계 유형:

  • project - 프로젝트와 프로젝트 수준 리소스에 적용되는 권한(이슈 관리, 파이프라인 생성, 리포지터리 설정 업데이트)
    • 원시 권한이 /projects/:id/... 같은 프로젝트 엔드포인트에서 동작하면 포함합니다
  • group - 그룹과 그룹 수준 리소스에 적용되는 권한(그룹 멤버 관리, 그룹 설정, 그룹 소유 프로젝트)
    • 원시 권한이 /groups/:id/... 같은 그룹 엔드포인트에서 동작하면 포함합니다
  • user - 사용자 수준 리소스에 적용되는 권한(개인 프로필, 개인 설정, 사용자 소유 리소스)
    • 원시 권한이 /users/:id/... 같은 사용자 엔드포인트나 개인 네임스페이스 작업에서 동작하면 포함합니다
  • instance - GitLab 인스턴스 수준에서 적용되는 권한(스니펫 읽기, 감사 로그 보기, 시스템 설정 관리 같은 작업)
    • 드물게 사용합니다. 일반적으로 관리자용 권한에만 해당합니다

경계 선택: API 파일의 엔드포인트 라우트 또는 보호하려는 GraphQL 유형과 뮤테이션을 검토합니다. 엔드포인트가 /projects/:id/... 같은 패턴을 따르면 project를 포함합니다. 엔드포인트가 /groups/:id/...를 따르면 group을 포함합니다. GraphQL은 지시자에 선언된 boundary_type을 확인합니다. 엔드포인트가 실제로 지원하는 경계만 포함합니다.

조건부 할당 가능한 권한#

일부 엔드포인트는 특정 사용자만 사용할 수 있습니다. 예를 들어 인스턴스 수준 감사 이벤트 엔드포인트는 관리자 액세스가 필요하지만, 같은 리소스의 그룹 수준 및 프로젝트 수준 엔드포인트는 그렇지 않습니다. 이런 권한을 토큰 생성 UI에서 모든 사용자에게 노출하면 도움이 되지 않고 화면만 복잡해집니다.

토큰 생성 UI에서 해당 권한이 제시되려면 사용자가 충족해야 하는 조건은 선택 항목인 assignable_when 필드로 선언합니다.

---
name: read_audit_event
description: Grants the ability to read audit events
boundaries:
  - group
  - project
  - instance
available_for:
  - granular_access_token
permissions:
  - read_audit_event
assignable_when:
  - condition: admin
    boundaries:
      - instance

각 항목은 condition 하나와, 선택적으로 그 조건이 적용되는 boundaries를 선언합니다. boundaries를 생략하면 해당 조건이 그 권한의 모든 경계에 적용됩니다. 같은 경계에 적용되는 항목이 여러 개이면 모두 충족해야 합니다. assignable_when에 나열된 모든 경계는 해당 권한의 boundaries 필드에도 있어야 합니다.

사용할 수 있는 조건:

조건 권한이 제시되는 경우
admin 현재 사용자가 인스턴스 관리자이거나 커스텀 관리자 역할을 통해 Admin area에 접근할 수 있는 경우입니다.
gitlab_team_member 현재 사용자가 GitLab 팀 멤버인 경우입니다.
saas 인스턴스가 GitLab.com 인 경우입니다.
self_managed 인스턴스가 GitLab.com 이 아닌 경우입니다.

assignable_when의 효과:

  • 토큰 생성 UI는 현재 사용자가 조건을 충족하는 경계에서만 해당 권한을 제시합니다. 위 예시에서 일반 사용자는 그룹과 프로젝트에 대해 read_audit_event를 선택할 수 있지만, 인스턴스 전체 영역에는 그 권한이 나타나지 않습니다. 관리자에게는 모든 영역에 표시됩니다.
  • 토큰 생성, 유효성 검사, 문서 생성, 런타임 권한 부여는 영향을 받지 않습니다. API는 조건과 무관하게 해당 권한이 포함된 세분화된 스코프를 허용하며, 엔드포인트 자체 검사에서 사용자가 거부되면 해당 엔드포인트 호출은 403 Forbidden으로 실패합니다. 이미 그 권한을 가진 기존 토큰은 소유자가 더 이상 조건을 충족하지 않더라도 계속 동작합니다.
  • REST 엔드포인트와 GraphQL 유형, 뮤테이션, 필드는 권한 부여 데코레이터 또는 지시자에 assignable_when을 붙여 같은 조건을 선언합니다.

유효성 검사 태스크는 세분화된 액세스 토큰에서 사용할 수 있는 할당 가능한 권한의 각 경계에 대해, YAML 조건이 해당 경계에서 그 권한을 사용하는 모든 REST 엔드포인트와 GraphQL 유형, 뮤테이션, 필드가 공유하는 조건과 같은지 확인합니다. 엔드포인트나 지시자가 사용하지 않는 경계에는 조건을 선언할 수 없습니다. lib/tasks/gitlab/permissions/assignable/validate_task.rb의 GRANULAR_TOKEN_NON_API_CONSUMERS에 나열된 권한은 그 소비자에 태그를 붙일 수 없으므로 예외입니다. 엔드포인트나 지시자에 태그를 붙일 때는 같은 머지 리퀘스트에서 YAML 파일에도 대응하는 조건을 선언하며, 그 반대도 마찬가지입니다. 태그가 없는 코드와 조건이 없는 권한은 항상 일치하므로, 태그가 없는 기존 코드는 그대로 유효성 검사를 통과합니다.

Warning

assignable_when은 보안 통제 수단이 아닙니다. 토큰 생성 UI가 어떤 권한을 제시할지만 제어합니다. 엔드포인트는 요청 시점에 같은 조건을 그대로 적용해야 합니다(예: authenticated_as_admin! 사용).

중요한 제약 사항#

  • 할당 가능한 권한에 포함된 각 원시 권한은 이미 존재해야 합니다(원시 권한 정의 파일로 생성)
  • 할당 가능한 권한에 할당된 원시 권한만 토큰 권한 부여에 사용할 수 있습니다
  • 관련된 할당 가능한 권한 전반에 걸쳐 일관된 이름을 사용합니다

할당 가능한 권한 유효성 검사#

할당 가능한 권한 유효성 검사는 git push를 실행할 때 Lefthook의 pre-push 훅이 자동으로 실행합니다. 직접 실행할 수도 있습니다.

bundle exec rake gitlab:permissions:validate

유효성 검사 태스크는 다음 제약 사항을 적용합니다.

  • 할당 가능한 권한은 정확히 config/authz/permission_groups/assignable_permissions/<category>/<resource>/<action>.yml에 있어야 합니다
  • 기본 경로와 마지막 파일 이름 사이에 추가 디렉터리를 둘 수 없습니다
  • 각 REST API 라우트의 boundary_type과 각 GraphQL 지시자의 boundary_type은 할당 가능한 권한의 boundaries 필드에 있는 경계 중 최소 하나와 일치해야 합니다(예: 라우트나 지시자가 boundary_type: :project를 선언하면 할당 가능한 권한의 경계에 project가 포함되어야 합니다)
  • 각 경계의 YAML assignable_when 조건은 그 경계에서 해당 권한을 사용하는 REST 엔드포인트와 GraphQL 선언의 태그와 일치해야 합니다(위의 조건부 할당 가능한 권한 참고)

할당 가능한 권한 유지 관리#

할당 가능한 권한은 시간이 지나면서 변경이 필요할 수 있습니다. 이 절에서는 흔한 변경 시나리오와 그 영향을 다룹니다.

토큰이 권한을 해석하는 방법#

변경하기 전에 토큰이 권한을 어떻게 저장하고 해석하는지 이해해야 합니다.

토큰은 원시 권한이 아니라 할당 가능한 권한 이름을 데이터베이스에 저장합니다. 요청 시점에 시스템은 현재 YAML 정의를 사용해 이 이름을 원시 권한으로 동적으로 해석합니다. 따라서 YAML 파일 변경은 마이그레이션 없이도 기존 토큰 전체에 즉시 적용됩니다.

이 동작은 app/models/authz/granular_scope.rb에 구현되어 있습니다.

scope
  .pluck(Arel.sql('DISTINCT jsonb_array_elements_text(permissions)'))
  .flat_map { |p| ::Authz::PermissionGroups::Assignable.get(p)&.permissions }
  .compact.map(&:to_sym)

Assignable.get(p)가 현재 YAML 정의에서 저장된 이름을 찾지 못하면 nil을 반환하고 해당 권한은 조용히 무시됩니다. 할당 가능한 권한의 이름을 바꾸거나 제거할 때 문제가 생기는 근본 원인이 바로 이것입니다.

할당 가능한 권한 추가#

새 할당 가능한 권한을 추가하는 작업은 안전합니다. 새 YAML 파일은 자동으로 인식되어 세분화된 스코프를 만들 때 UI에 표시됩니다. 기존 토큰은 영향을 받지 않습니다.

Note

할당 가능한 권한에 포함된 원시 권한 가운데 API 권한 부여에 사용되는 것이 하나도 없으면, 토큰을 만드는 사용자에게는 그 권한을 추가한 효과가 나타나지 않습니다. available_for: granular_access_token을 선언한 할당 가능한 권한이 이런 상태이면 gitlab:permissions:validate Rake 태스크가 실패합니다. API 권한 부여 밖에서 소비되는 원시 권한(예: download_code는 Git 작업에 사용)은 lib/tasks/gitlab/permissions/assignable/validate_task.rb의 GRANULAR_TOKEN_NON_API_CONSUMERS를 통해 예외 처리됩니다.

할당 가능한 권한 제거#

할당 가능한 권한을 제거하는 작업은 호환성을 깨는 변경입니다. 제거된 이름에 대해 Assignable.get(p)가 nil을 반환하므로, 그 할당 가능한 권한으로 만든 토큰은 포함된 원시 권한이 부여하던 API 액세스를 모두 잃습니다.

기반 API 기능도 함께 제거하는 경우에만 할당 가능한 권한을 제거합니다.

할당 가능한 권한 이름 변경#

할당 가능한 권한의 이름을 바꾸는 작업은 호환성을 깨는 변경입니다. 저장된 이름이 더 이상 어떤 YAML 정의와도 일치하지 않으므로, 이전 이름으로 만든 토큰은 액세스를 잃습니다.

이 작업에는 세 단계가 필요합니다.

  1. 다음 내용을 담은 머지 리퀘스트를 만듭니다.

    • 새 할당 가능한 권한 YAML 파일을 추가합니다.
    • 데이터베이스에 저장된 이름을 갱신하도록 rename_granular_scope_permission 배포 후 배치 백그라운드 마이그레이션을 추가합니다.
    • 이전 할당 가능한 권한을 더 이상 사용하지 않음으로 표시합니다.
  2. 이후 마일스톤에서 배치 백그라운드 마이그레이션을 마무리하여 남은 행이 업그레이드 중에 동기적으로 마이그레이션되도록 합니다.

  3. 마이그레이션이 마무리된 뒤에는 더 이상 사용하지 않는 권한을 제거하는 후속 머지 리퀘스트를 만듭니다. Rake 태스크로 대상 파일을 찾아 삭제합니다.

    bundle exec rake gitlab:permissions:assignable:cleanup_deprecated
    

    이 태스크는 이전 마일스톤에서 이름 변경 마이그레이션이 마무리된 모든 폐기 권한 파일을 삭제합니다. 삭제한 경로를 각각 출력하며, 대상이 없으면 아무것도 출력하지 않고 종료합니다.

이름 변경 마이그레이션 만들기

설명이 담긴 이름으로 마이그레이션 스캐폴드를 생성합니다.

bundle exec rails g batched_background_migration rename_granular_scope_permission_<description> \
  --table-name=granular_scopes --feature-category=permissions

생성된 배포 후 마이그레이션 내용을 다음으로 바꿉니다.

# frozen_string_literal: true

class QueueRenameGranularScopePermissionDescription < Gitlab::Database::Migration[2.3]
  milestone '<milestone>'
  restrict_gitlab_migration gitlab_schema: :gitlab_main_org

  MIGRATION = 'RenameGranularScopePermissionDescription'

  def up
    queue_batched_background_migration(
      MIGRATION,
      :granular_scopes,
      :id
    )
  end

  def down
    delete_batched_background_migration(MIGRATION, :granular_scopes, :id, [])
  end
end

생성된 백그라운드 마이그레이션 내용을 다음으로 바꿉니다.

# frozen_string_literal: true

module Gitlab
  module BackgroundMigration
    class RenameGranularScopePermissionDescription < BatchedMigrationJob
      include Gitlab::Database::MigrationHelpers::GranularScopePermissions

      RENAMES = {
        'old_name_one' => 'new_name_one',
        'old_name_two' => %w[new_name_two_a new_name_two_b]
      }.freeze

      feature_category :permissions
    end
  end
end

이름 변경 내용과 대상 릴리스에 맞게 RENAMES 해시, 클래스 이름, milestone을 갱신합니다. RENAMES의 값은 문자열(단순 이름 변경)이거나 배열(권한 하나를 여러 개로 분할)일 수 있습니다. 이름을 하나만 바꾸면 항목 하나를 사용하고, 같은 배치 처리에서 여러 이름을 바꾸려면 항목을 여러 개 사용합니다.

할당 가능한 권한에 원시 권한 추가#

기존 할당 가능한 권한에 원시 권한을 추가하면 그 할당 가능한 권한으로 이전에 만든 토큰의 액세스 범위가 넓어집니다.

해석이 동적으로 이루어지므로 새 원시 권한은 즉시 적용됩니다. 최소 권한 원칙에 어긋나 보일 수 있지만, 각 원시 권한은 할당 가능한 권한 하나에만 속할 수 있다는 유효성 검사 때문에 그 새 기능은 다른 어떤 권한으로도 사용할 수 없었을 것입니다. 사용자도 같은 리소스의 새 기능을 해당 할당 가능한 권한이 포함하리라고 기대할 가능성이 큽니다.

새 API 엔드포인트 지원을 추가할 때만 원시 권한을 추가합니다. 할당 가능한 권한의 YAML 파일에 있는 permissions 배열에 원시 권한을 추가합니다.

할당 가능한 권한에서 원시 권한 제거#

할당 가능한 권한에서 원시 권한을 제거하는 작업은 호환성을 깨는 변경입니다. 해당 할당 가능한 권한을 가진 토큰은 제거된 원시 권한이 부여하던 액세스를 즉시 잃습니다.

이 영향은 rename_granular_scope_permission 마이그레이션으로 완화할 수 있습니다. 이전 권한에서 제거된 원시 권한을 뺀 권한과, 옮겨진 원시 권한을 포함하는 새 할당 가능한 권한의 조합으로 이전 할당 가능한 권한을 대체하는 방식입니다.

Note

새 할당 가능한 권한에 옮겨진 권한 외에 다른 원시 권한이 포함되어 있으면 이 방식으로 액세스 범위가 넓어질 수 있으니 유의합니다.

엔드포인트 또는 지시자의 경계 유형 변경#

REST API route_setting 이나 GraphQL authorize_granular_token 지시자의 boundary_type을 바꾸면 기존 토큰에 호환성을 깨는 변경이 될 수 있습니다.

할당 가능한 권한의 boundaries 필드는 그 원시 권한을 사용하는 엔드포인트와 지시자가 선언한 모든 boundary_type 값의 합집합을 포함해야 합니다. 할당 가능한 권한의 경계를 직접 바꾸지는 않습니다. 엔드포인트가 boundary_type을 추가하거나 변경할 때, 또는 원시 권한이 할당 가능한 권한에 추가되거나 제거될 때 그 결과로 바뀝니다. 불일치는 Lefthook의 pre-push 유효성 검사가 잡아냅니다.

토큰은 세분화된 스코프를 경계(네임스페이스)와 할당 가능한 권한의 조합으로 저장합니다. 엔드포인트의 boundary_type 이 바뀌면 권한 부여 검사는 새 경계를 기준으로 토큰의 스코프를 평가합니다. 이전 경계의 스코프로 만든 토큰은 더 이상 일치하지 않을 수 있습니다.

project와 group 사이의 변경은 안전합니다. 프로젝트는 그룹에 속하므로 그룹 경계의 세분화된 스코프를 가진 토큰은 그 그룹 안의 프로젝트도 포함하며, 프로젝트 경계의 스코프는 그룹 엔드포인트의 영향을 받지 않습니다.

user 나 instance로, 또는 그로부터의 변경(예: project에서 instance로)은 호환성을 깨는 변경입니다. 해당 권한에 대해 프로젝트 경계의 세분화된 스코프로 만든 토큰은 더 이상 액세스할 수 없습니다. 토큰 소유자는 새 경계에서 스코프를 새로 만들어야 합니다.

API 권한 부여에 사용되는 원시 권한 이름 변경#

원시 권한의 이름을 바꿔도 UI 나 기존 토큰에는 영향이 없습니다. 토큰은 원시 권한 이름이 아니라 할당 가능한 권한 이름을 저장하므로, 원시 권한 이름 변경에는 YAML 파일 갱신만 필요합니다.

  • 원시 권한 정의 파일(config/authz/permissions/<resource>/<action>.yml)
  • 그 권한을 참조하는 모든 할당 가능한 권한 YAML 파일

데이터베이스 마이그레이션은 필요하지 않습니다.

할당 가능한 권한

GitLab v19.5
원문 보기

요약

할당 가능한 권한은 하나 이상의 원시 권한을 사용자에게 보이는 권한 그룹으로 묶습니다. config/authz/permission_groups/assignable_permissions/&#x3C;category>/&#x3C;resource>/&#x3C;action>.yml 경로에 새 YAML 파일을 직접 만듭니다.

할당 가능한 권한#

할당 가능한 권한은 하나 이상의 원시 권한을 사용자에게 보이는 권한 그룹으로 묶습니다. 이를 사용해 사용자에게 제시되는 세분화 수준을 조정할 수 있으며, 권한을 세밀하게 묶을지(예: read issue와 read snippet 권한을 각각 분리) 더 넓게 묶을지(예: 모든 read work item 권한을 함께) 제품 그룹이 결정할 수 있습니다. 이렇게 하면 코드 수준에서는 세분화된 제어를 유지하면서 UI에서는 사용자가 다루기 쉬운 경험을 제공합니다.

할당 가능한 권한 파일 만들기#

config/authz/permission_groups/assignable_permissions/<category>/<resource>/<action>.yml 경로에 새 YAML 파일을 직접 만듭니다.

---
name: run_job
description: Grants the ability to run jobs
permissions:
  - play_job
  - retry_job
boundaries:
  - group
  - project

할당 가능한 권한 파일 필드#

필드 설명
name 할당 가능한 권한의 고유 식별자
description 할당 가능한 권한이 부여하는 내용을 사람이 읽을 수 있게 적은 설명
permissions 이 할당 가능한 권한에 포함된 원시 권한 배열(원시 권한 정의 파일로 이미 존재해야 함)
boundaries 할당 가능한 권한이 적용되는 조직 수준 목록
available_for 이 권한을 사용할 수 있는 소비자로, granular_access_token(세분화된 PAT), role(표준 및 커스텀 역할) 또는 둘 다입니다. granular_access_token을 선언하는 할당 가능한 권한은 그 원시 권한 중 하나 이상이 REST 권한 부여 데코레이터나 GraphQL 세분화 스코프 지시자에서 참조되어야 합니다.
assignable_when 선택 사항입니다. 토큰 생성 UI에서 해당 권한이 제시되려면 현재 사용자가 충족해야 하는 조건입니다. 조건부 할당 가능한 권한을 참고합니다.
deprecated 선택 사항입니다. true로 설정하면 UI에서 할당 가능한 권한을 숨겨 사용자가 새 토큰을 만들 때 더 이상 선택할 수 없습니다. 이미 이 권한을 가진 기존 토큰은 계속 동작합니다. 이름 변경 마이그레이션 중이거나 권한을 단계적으로 제거할 때 사용합니다.

디렉터리 구조 이해#

디렉터리 구조는 <category>/<resource>/<action>.yml의 세 단계를 사용합니다.

메타데이터 파일이 필요한 경우#

파일 필요한 경우 목적
카테고리 .metadata.yml 선택 사항 폴더 이름 표시 재정의(예: ci_cd → "Ci Cd" 대신 "CI/CD")
리소스 .metadata.yml 선택 사항 생성된 리소스 설명이나 대문자화된 리소스 이름 재정의
Note

<category>/<resource>/<action>.yml의 할당 가능한 권한 YAML 파일은 항상 필수이며 메타데이터 파일이 아닙니다. 권한 번들을 정의하는 주 구성 파일입니다.

카테고리 수준: <category> 하위 폴더는 할당 가능한 권한이 묶이는 UI에 표시되는 카테고리 이름을 나타냅니다. 폴더 이름은 표시될 때 대문자화됩니다(예: project_management는 "Project Management" 가 됩니다). 이 카테고리 이름은 권한 선택 UI에 표시되어 사용자가 기능 영역별로 권한을 정리하고 찾는 데 도움이 됩니다.

대문자화가 잘못된 표시 이름을 만들 때만 카테고리 폴더에 .metadata.yml 파일을 만듭니다. 예를 들어 대문자화가 잘 되지 않는 약어나 축약어가 이에 해당합니다.

---
name: "CI/CD"

카테고리 수준 메타데이터 예시:

  • 폴더: project_management → 메타데이터 없음: "Project Management" 로 표시
  • 폴더: ci_cd → 메타데이터 없음: "Ci Cd" 로 표시(잘못됨)
  • 폴더: ci_cd → .metadata.yml 재정의 적용: "CI/CD" 로 표시(올바름)

리소스 수준: 기본값을 재정의하려면 config/authz/permission_groups/assignable_permissions/<category>/<resource>/.metadata.yml에 .metadata.yml 파일을 선택적으로 만듭니다.

---
description: "Grants the ability to <actions> SSH keys."
name: "SSH Key"

기본적으로 리소스 이름은 디렉터리 이름에서(대문자화하여) 파생되고, 설명은 리소스의 액션에서 생성됩니다(예: "Grants the ability to assign, create, and read runners.").

필드:

  • description(선택 사항) - 생성된 리소스 설명을 재정의합니다. <actions> 보간을 포함해야 하며, 이 보간은 런타임에 리소스의 YAML 파일에 있는 액션 목록을 알파벳순으로 정렬한 값으로 치환됩니다. <actions>를 사용하면 명사를 원하는 대로 바꾸면서도 액션 목록을 자동으로 동기화 상태로 유지합니다(예: "Grants the ability to <actions> CI variables.").
  • name(선택 사항) - 표시용으로 대문자화된 리소스 이름을 재정의합니다. 대문자화가 제대로 동작하지 않는 약어나 특수한 형식에 사용합니다(예: 자동 대문자화된 이름 대신 name: "SSH Key")

UI 예시:

다음 스크린샷은 카테고리와 리소스 메타데이터가 권한 선택 UI에 표시되는 방식을 보여 줍니다.

리소스 메타데이터를 보여 주는 권한 선택 UI

이 예시에서 각 항목은 다음과 같습니다.

  • CI/CD - 폴더 이름에서 가져온 카테고리 이름이며, 카테고리 .metadata.yml로 재정의할 수 있습니다
  • CI Config - 폴더 이름에서 가져온 리소스 이름이며, 리소스 .metadata.yml로 재정의할 수 있습니다
  • 그 아래 설명은 리소스 설명으로, 리소스 .metadata.yml 파일에서 재정의하거나 리소스의 액션에서 생성됩니다

리소스 .metadata.yml을 만들어야 하는 경우:

  • 리소스 이름에 대문자화가 제대로 되지 않는 약어나 브랜드 이름이 포함된 경우(예: ssh_key → "SSH Key" 가 아니라 "Ssh Key")
  • 생성된 설명에 별도의 명사가 필요한 경우(예: "variables" 대신 "CI variables", "codes" 대신 "code")
  • 리소스에 문장으로 쓰면 어색한 특이한 액션 이름이 있는 경우(예: renew_secret)
  • 디렉터리 이름이 셀 수 없거나 복수형이 어색한 경우(예: "code" → "codes", "metadata" → "metadatas")

디렉터리 이름이 대문자화와 복수형 처리가 올바르게 되는 경우(대부분의 리소스)에는 메타데이터 파일이 필요하지 않습니다.

경계 결정#

boundaries 필드는 이 할당 가능한 권한을 어떤 조직 수준에서 지원하는지 지정합니다. 묶인 원시 권한을 적용할 수 있는 위치를 기준으로 선택합니다. 최소 권한 원칙에 따라 권한이 실제로 적용되는 경계만 포함합니다.

경계 유형:

  • project - 프로젝트와 프로젝트 수준 리소스에 적용되는 권한(이슈 관리, 파이프라인 생성, 리포지터리 설정 업데이트)
    • 원시 권한이 /projects/:id/... 같은 프로젝트 엔드포인트에서 동작하면 포함합니다
  • group - 그룹과 그룹 수준 리소스에 적용되는 권한(그룹 멤버 관리, 그룹 설정, 그룹 소유 프로젝트)
    • 원시 권한이 /groups/:id/... 같은 그룹 엔드포인트에서 동작하면 포함합니다
  • user - 사용자 수준 리소스에 적용되는 권한(개인 프로필, 개인 설정, 사용자 소유 리소스)
    • 원시 권한이 /users/:id/... 같은 사용자 엔드포인트나 개인 네임스페이스 작업에서 동작하면 포함합니다
  • instance - GitLab 인스턴스 수준에서 적용되는 권한(스니펫 읽기, 감사 로그 보기, 시스템 설정 관리 같은 작업)
    • 드물게 사용합니다. 일반적으로 관리자용 권한에만 해당합니다

경계 선택: API 파일의 엔드포인트 라우트 또는 보호하려는 GraphQL 유형과 뮤테이션을 검토합니다. 엔드포인트가 /projects/:id/... 같은 패턴을 따르면 project를 포함합니다. 엔드포인트가 /groups/:id/...를 따르면 group을 포함합니다. GraphQL은 지시자에 선언된 boundary_type을 확인합니다. 엔드포인트가 실제로 지원하는 경계만 포함합니다.

조건부 할당 가능한 권한#

일부 엔드포인트는 특정 사용자만 사용할 수 있습니다. 예를 들어 인스턴스 수준 감사 이벤트 엔드포인트는 관리자 액세스가 필요하지만, 같은 리소스의 그룹 수준 및 프로젝트 수준 엔드포인트는 그렇지 않습니다. 이런 권한을 토큰 생성 UI에서 모든 사용자에게 노출하면 도움이 되지 않고 화면만 복잡해집니다.

토큰 생성 UI에서 해당 권한이 제시되려면 사용자가 충족해야 하는 조건은 선택 항목인 assignable_when 필드로 선언합니다.

---
name: read_audit_event
description: Grants the ability to read audit events
boundaries:
  - group
  - project
  - instance
available_for:
  - granular_access_token
permissions:
  - read_audit_event
assignable_when:
  - condition: admin
    boundaries:
      - instance

각 항목은 condition 하나와, 선택적으로 그 조건이 적용되는 boundaries를 선언합니다. boundaries를 생략하면 해당 조건이 그 권한의 모든 경계에 적용됩니다. 같은 경계에 적용되는 항목이 여러 개이면 모두 충족해야 합니다. assignable_when에 나열된 모든 경계는 해당 권한의 boundaries 필드에도 있어야 합니다.

사용할 수 있는 조건:

조건 권한이 제시되는 경우
admin 현재 사용자가 인스턴스 관리자이거나 커스텀 관리자 역할을 통해 Admin area에 접근할 수 있는 경우입니다.
gitlab_team_member 현재 사용자가 GitLab 팀 멤버인 경우입니다.
saas 인스턴스가 GitLab.com 인 경우입니다.
self_managed 인스턴스가 GitLab.com 이 아닌 경우입니다.

assignable_when의 효과:

  • 토큰 생성 UI는 현재 사용자가 조건을 충족하는 경계에서만 해당 권한을 제시합니다. 위 예시에서 일반 사용자는 그룹과 프로젝트에 대해 read_audit_event를 선택할 수 있지만, 인스턴스 전체 영역에는 그 권한이 나타나지 않습니다. 관리자에게는 모든 영역에 표시됩니다.
  • 토큰 생성, 유효성 검사, 문서 생성, 런타임 권한 부여는 영향을 받지 않습니다. API는 조건과 무관하게 해당 권한이 포함된 세분화된 스코프를 허용하며, 엔드포인트 자체 검사에서 사용자가 거부되면 해당 엔드포인트 호출은 403 Forbidden으로 실패합니다. 이미 그 권한을 가진 기존 토큰은 소유자가 더 이상 조건을 충족하지 않더라도 계속 동작합니다.
  • REST 엔드포인트와 GraphQL 유형, 뮤테이션, 필드는 권한 부여 데코레이터 또는 지시자에 assignable_when을 붙여 같은 조건을 선언합니다.

유효성 검사 태스크는 세분화된 액세스 토큰에서 사용할 수 있는 할당 가능한 권한의 각 경계에 대해, YAML 조건이 해당 경계에서 그 권한을 사용하는 모든 REST 엔드포인트와 GraphQL 유형, 뮤테이션, 필드가 공유하는 조건과 같은지 확인합니다. 엔드포인트나 지시자가 사용하지 않는 경계에는 조건을 선언할 수 없습니다. lib/tasks/gitlab/permissions/assignable/validate_task.rb의 GRANULAR_TOKEN_NON_API_CONSUMERS에 나열된 권한은 그 소비자에 태그를 붙일 수 없으므로 예외입니다. 엔드포인트나 지시자에 태그를 붙일 때는 같은 머지 리퀘스트에서 YAML 파일에도 대응하는 조건을 선언하며, 그 반대도 마찬가지입니다. 태그가 없는 코드와 조건이 없는 권한은 항상 일치하므로, 태그가 없는 기존 코드는 그대로 유효성 검사를 통과합니다.

Warning

assignable_when은 보안 통제 수단이 아닙니다. 토큰 생성 UI가 어떤 권한을 제시할지만 제어합니다. 엔드포인트는 요청 시점에 같은 조건을 그대로 적용해야 합니다(예: authenticated_as_admin! 사용).

중요한 제약 사항#

  • 할당 가능한 권한에 포함된 각 원시 권한은 이미 존재해야 합니다(원시 권한 정의 파일로 생성)
  • 할당 가능한 권한에 할당된 원시 권한만 토큰 권한 부여에 사용할 수 있습니다
  • 관련된 할당 가능한 권한 전반에 걸쳐 일관된 이름을 사용합니다

할당 가능한 권한 유효성 검사#

할당 가능한 권한 유효성 검사는 git push를 실행할 때 Lefthook의 pre-push 훅이 자동으로 실행합니다. 직접 실행할 수도 있습니다.

bundle exec rake gitlab:permissions:validate

유효성 검사 태스크는 다음 제약 사항을 적용합니다.

  • 할당 가능한 권한은 정확히 config/authz/permission_groups/assignable_permissions/<category>/<resource>/<action>.yml에 있어야 합니다
  • 기본 경로와 마지막 파일 이름 사이에 추가 디렉터리를 둘 수 없습니다
  • 각 REST API 라우트의 boundary_type과 각 GraphQL 지시자의 boundary_type은 할당 가능한 권한의 boundaries 필드에 있는 경계 중 최소 하나와 일치해야 합니다(예: 라우트나 지시자가 boundary_type: :project를 선언하면 할당 가능한 권한의 경계에 project가 포함되어야 합니다)
  • 각 경계의 YAML assignable_when 조건은 그 경계에서 해당 권한을 사용하는 REST 엔드포인트와 GraphQL 선언의 태그와 일치해야 합니다(위의 조건부 할당 가능한 권한 참고)

할당 가능한 권한 유지 관리#

할당 가능한 권한은 시간이 지나면서 변경이 필요할 수 있습니다. 이 절에서는 흔한 변경 시나리오와 그 영향을 다룹니다.

토큰이 권한을 해석하는 방법#

변경하기 전에 토큰이 권한을 어떻게 저장하고 해석하는지 이해해야 합니다.

토큰은 원시 권한이 아니라 할당 가능한 권한 이름을 데이터베이스에 저장합니다. 요청 시점에 시스템은 현재 YAML 정의를 사용해 이 이름을 원시 권한으로 동적으로 해석합니다. 따라서 YAML 파일 변경은 마이그레이션 없이도 기존 토큰 전체에 즉시 적용됩니다.

이 동작은 app/models/authz/granular_scope.rb에 구현되어 있습니다.

scope
  .pluck(Arel.sql('DISTINCT jsonb_array_elements_text(permissions)'))
  .flat_map { |p| ::Authz::PermissionGroups::Assignable.get(p)&.permissions }
  .compact.map(&:to_sym)

Assignable.get(p)가 현재 YAML 정의에서 저장된 이름을 찾지 못하면 nil을 반환하고 해당 권한은 조용히 무시됩니다. 할당 가능한 권한의 이름을 바꾸거나 제거할 때 문제가 생기는 근본 원인이 바로 이것입니다.

할당 가능한 권한 추가#

새 할당 가능한 권한을 추가하는 작업은 안전합니다. 새 YAML 파일은 자동으로 인식되어 세분화된 스코프를 만들 때 UI에 표시됩니다. 기존 토큰은 영향을 받지 않습니다.

Note

할당 가능한 권한에 포함된 원시 권한 가운데 API 권한 부여에 사용되는 것이 하나도 없으면, 토큰을 만드는 사용자에게는 그 권한을 추가한 효과가 나타나지 않습니다. available_for: granular_access_token을 선언한 할당 가능한 권한이 이런 상태이면 gitlab:permissions:validate Rake 태스크가 실패합니다. API 권한 부여 밖에서 소비되는 원시 권한(예: download_code는 Git 작업에 사용)은 lib/tasks/gitlab/permissions/assignable/validate_task.rb의 GRANULAR_TOKEN_NON_API_CONSUMERS를 통해 예외 처리됩니다.

할당 가능한 권한 제거#

할당 가능한 권한을 제거하는 작업은 호환성을 깨는 변경입니다. 제거된 이름에 대해 Assignable.get(p)가 nil을 반환하므로, 그 할당 가능한 권한으로 만든 토큰은 포함된 원시 권한이 부여하던 API 액세스를 모두 잃습니다.

기반 API 기능도 함께 제거하는 경우에만 할당 가능한 권한을 제거합니다.

할당 가능한 권한 이름 변경#

할당 가능한 권한의 이름을 바꾸는 작업은 호환성을 깨는 변경입니다. 저장된 이름이 더 이상 어떤 YAML 정의와도 일치하지 않으므로, 이전 이름으로 만든 토큰은 액세스를 잃습니다.

이 작업에는 세 단계가 필요합니다.

  1. 다음 내용을 담은 머지 리퀘스트를 만듭니다.

    • 새 할당 가능한 권한 YAML 파일을 추가합니다.
    • 데이터베이스에 저장된 이름을 갱신하도록 rename_granular_scope_permission 배포 후 배치 백그라운드 마이그레이션을 추가합니다.
    • 이전 할당 가능한 권한을 더 이상 사용하지 않음으로 표시합니다.
  2. 이후 마일스톤에서 배치 백그라운드 마이그레이션을 마무리하여 남은 행이 업그레이드 중에 동기적으로 마이그레이션되도록 합니다.

  3. 마이그레이션이 마무리된 뒤에는 더 이상 사용하지 않는 권한을 제거하는 후속 머지 리퀘스트를 만듭니다. Rake 태스크로 대상 파일을 찾아 삭제합니다.

    bundle exec rake gitlab:permissions:assignable:cleanup_deprecated
    

    이 태스크는 이전 마일스톤에서 이름 변경 마이그레이션이 마무리된 모든 폐기 권한 파일을 삭제합니다. 삭제한 경로를 각각 출력하며, 대상이 없으면 아무것도 출력하지 않고 종료합니다.

이름 변경 마이그레이션 만들기

설명이 담긴 이름으로 마이그레이션 스캐폴드를 생성합니다.

bundle exec rails g batched_background_migration rename_granular_scope_permission_<description> \
  --table-name=granular_scopes --feature-category=permissions

생성된 배포 후 마이그레이션 내용을 다음으로 바꿉니다.

# frozen_string_literal: true

class QueueRenameGranularScopePermissionDescription < Gitlab::Database::Migration[2.3]
  milestone '<milestone>'
  restrict_gitlab_migration gitlab_schema: :gitlab_main_org

  MIGRATION = 'RenameGranularScopePermissionDescription'

  def up
    queue_batched_background_migration(
      MIGRATION,
      :granular_scopes,
      :id
    )
  end

  def down
    delete_batched_background_migration(MIGRATION, :granular_scopes, :id, [])
  end
end

생성된 백그라운드 마이그레이션 내용을 다음으로 바꿉니다.

# frozen_string_literal: true

module Gitlab
  module BackgroundMigration
    class RenameGranularScopePermissionDescription < BatchedMigrationJob
      include Gitlab::Database::MigrationHelpers::GranularScopePermissions

      RENAMES = {
        'old_name_one' => 'new_name_one',
        'old_name_two' => %w[new_name_two_a new_name_two_b]
      }.freeze

      feature_category :permissions
    end
  end
end

이름 변경 내용과 대상 릴리스에 맞게 RENAMES 해시, 클래스 이름, milestone을 갱신합니다. RENAMES의 값은 문자열(단순 이름 변경)이거나 배열(권한 하나를 여러 개로 분할)일 수 있습니다. 이름을 하나만 바꾸면 항목 하나를 사용하고, 같은 배치 처리에서 여러 이름을 바꾸려면 항목을 여러 개 사용합니다.

할당 가능한 권한에 원시 권한 추가#

기존 할당 가능한 권한에 원시 권한을 추가하면 그 할당 가능한 권한으로 이전에 만든 토큰의 액세스 범위가 넓어집니다.

해석이 동적으로 이루어지므로 새 원시 권한은 즉시 적용됩니다. 최소 권한 원칙에 어긋나 보일 수 있지만, 각 원시 권한은 할당 가능한 권한 하나에만 속할 수 있다는 유효성 검사 때문에 그 새 기능은 다른 어떤 권한으로도 사용할 수 없었을 것입니다. 사용자도 같은 리소스의 새 기능을 해당 할당 가능한 권한이 포함하리라고 기대할 가능성이 큽니다.

새 API 엔드포인트 지원을 추가할 때만 원시 권한을 추가합니다. 할당 가능한 권한의 YAML 파일에 있는 permissions 배열에 원시 권한을 추가합니다.

할당 가능한 권한에서 원시 권한 제거#

할당 가능한 권한에서 원시 권한을 제거하는 작업은 호환성을 깨는 변경입니다. 해당 할당 가능한 권한을 가진 토큰은 제거된 원시 권한이 부여하던 액세스를 즉시 잃습니다.

이 영향은 rename_granular_scope_permission 마이그레이션으로 완화할 수 있습니다. 이전 권한에서 제거된 원시 권한을 뺀 권한과, 옮겨진 원시 권한을 포함하는 새 할당 가능한 권한의 조합으로 이전 할당 가능한 권한을 대체하는 방식입니다.

Note

새 할당 가능한 권한에 옮겨진 권한 외에 다른 원시 권한이 포함되어 있으면 이 방식으로 액세스 범위가 넓어질 수 있으니 유의합니다.

엔드포인트 또는 지시자의 경계 유형 변경#

REST API route_setting 이나 GraphQL authorize_granular_token 지시자의 boundary_type을 바꾸면 기존 토큰에 호환성을 깨는 변경이 될 수 있습니다.

할당 가능한 권한의 boundaries 필드는 그 원시 권한을 사용하는 엔드포인트와 지시자가 선언한 모든 boundary_type 값의 합집합을 포함해야 합니다. 할당 가능한 권한의 경계를 직접 바꾸지는 않습니다. 엔드포인트가 boundary_type을 추가하거나 변경할 때, 또는 원시 권한이 할당 가능한 권한에 추가되거나 제거될 때 그 결과로 바뀝니다. 불일치는 Lefthook의 pre-push 유효성 검사가 잡아냅니다.

토큰은 세분화된 스코프를 경계(네임스페이스)와 할당 가능한 권한의 조합으로 저장합니다. 엔드포인트의 boundary_type 이 바뀌면 권한 부여 검사는 새 경계를 기준으로 토큰의 스코프를 평가합니다. 이전 경계의 스코프로 만든 토큰은 더 이상 일치하지 않을 수 있습니다.

project와 group 사이의 변경은 안전합니다. 프로젝트는 그룹에 속하므로 그룹 경계의 세분화된 스코프를 가진 토큰은 그 그룹 안의 프로젝트도 포함하며, 프로젝트 경계의 스코프는 그룹 엔드포인트의 영향을 받지 않습니다.

user 나 instance로, 또는 그로부터의 변경(예: project에서 instance로)은 호환성을 깨는 변경입니다. 해당 권한에 대해 프로젝트 경계의 세분화된 스코프로 만든 토큰은 더 이상 액세스할 수 없습니다. 토큰 소유자는 새 경계에서 스코프를 새로 만들어야 합니다.

API 권한 부여에 사용되는 원시 권한 이름 변경#

원시 권한의 이름을 바꿔도 UI 나 기존 토큰에는 영향이 없습니다. 토큰은 원시 권한 이름이 아니라 할당 가능한 권한 이름을 저장하므로, 원시 권한 이름 변경에는 YAML 파일 갱신만 필요합니다.

  • 원시 권한 정의 파일(config/authz/permissions/<resource>/<action>.yml)
  • 그 권한을 참조하는 모든 할당 가능한 권한 YAML 파일

데이터베이스 마이그레이션은 필요하지 않습니다.