InfoGrab DocsInfoGrab Docs

REST API 구현 가이드

요약

침해된 개인 액세스 토큰(PAT)의 보안 영향을 줄이기 위해, 세분화된 PAT를 사용하면 특정 조직 경계(그룹, 프로젝트, 사용자 또는 인스턴스 수준)에 제한된 세밀한 권한으로 토큰을 생성할 수 있습니다. 세분화된 PAT는 경계와 특정 리소스 권한으로 구성된 세분화된 범위를 통해 세밀한 액세스 제어를 허용합니다.

침해된 개인 액세스 토큰(PAT)의 보안 영향을 줄이기 위해, 세분화된 PAT를 사용하면 특정 조직 경계(그룹, 프로젝트, 사용자 또는 인스턴스 수준)에 제한된 세밀한 권한으로 토큰을 생성할 수 있습니다. 이를 통해 사용자는 토큰에 필요한 권한만 부여하는 최소 권한 원칙을 따를 수 있습니다.

세분화된 PAT는 경계와 특정 리소스 권한으로 구성된 세분화된 범위를 통해 세밀한 액세스 제어를 허용합니다. 세분화된 PAT로 API 요청을 인증할 때, GitLab은 토큰의 권한이 지정된 경계 수준에서 요청된 리소스에 대한 액세스를 포함하는지 검증합니다.

이 문서는 REST API 엔드포인트를 세분화된 PAT 인증과 호환되도록 만들고자 하는 커뮤니티 기여자와 GitLab 개발자를 위해 설계되었습니다.

단계별 구현 가이드#

이 가이드는 REST API 엔드포인트에 세분화된 PAT 인증을 추가하는 방법을 안내합니다. 시작하기 전에 전체에서 사용되는 용어를 이해하기 위해 권한 명명 규칙 문서를 검토하십시오.

Note

이 단계는 REST API 엔드포인트만 다룹니다. GraphQL 쿼리 및 변이에 대한 지원 추가는 GraphQL 구현 가이드를 참조하십시오.

워크플로 개요#

구현은 다음 흐름을 따릅니다:

  1. 1-2단계: 계획 - 엔드포인트 식별 및 권한 설계
  2. 3단계: 원시 권한 생성(YAML 파일)
  3. 4단계: 원시 권한을 할당 가능한 권한으로 번들화(YAML 파일)
  4. 5단계: 엔드포인트에 인증 데코레이터 추가(Ruby 코드)
  5. 6단계: 인증 테스트 작성(Ruby 사양)
  6. 7단계: 로컬 테스트(수동 검증)
  7. 8단계: 문서 검증 및 재생성(Rake 작업)

각 단계에서 생성되는 파일#

각 단계에서 생성하는 파일을 보여주는 빠른 참조:

단계 파일 유형 위치 수량 예시
2 계획 문서 (메모) 권한 이름 식별
3 원시 권한 YAML config/authz/permissions/<resource>/<action>.yml 권한당 1개 config/authz/permissions/job/read.yml
3 원시 권한 리소스 메타데이터 config/authz/permissions/<resource>/.metadata.yml 리소스당 1개 config/authz/permissions/job/.metadata.yml
4 할당 가능한 권한 YAML config/authz/permission_groups/assignable_permissions/<category>/<resource>/<action>.yml 그룹당 1개 config/authz/permission_groups/assignable_permissions/ci_cd/job/run.yml
4(선택) 범주 메타데이터 config/authz/permission_groups/assignable_permissions/<category>/.metadata.yml 범주당 0 또는 1개 config/authz/permission_groups/assignable_permissions/ci_cd/.metadata.yml
4 리소스 메타데이터 config/authz/permission_groups/assignable_permissions/<category>/<resource>/.metadata.yml 리소스당 1개 config/authz/permission_groups/assignable_permissions/ci_cd/job/.metadata.yml
5 Grape 데코레이터 lib/api/<resource>.rb 수정 엔드포인트당 1개 route_setting :authorization 추가됨
6 RSpec 테스트 spec/requests/api/<resource>_spec.rb 수정 엔드포인트당 1개 it_behaves_like 'authorizing...' 추가됨
8 생성된 참조 문서 doc/auth/tokens/fine_grained_access_tokens_rest.md 1 bundle exec rake gitlab:permissions:routes:compile_docs로 재생성

1단계: 리소스에 대한 REST API 엔드포인트 식별#

목표: 작업하는 리소스에 대한 모든 REST API 엔드포인트를 찾습니다.

  1. lib/api/<resource_name>.rb에서 리소스에 대한 API 파일을 찾습니다.

    예시: jobs 리소스의 경우 lib/api/ci/jobs.rb 열기

    팁:

    • 일부 리소스는 여러 API 파일에 걸쳐 엔드포인트가 분산되어 있을 수 있습니다(예: 중첩된 리소스)
    • resources :resource_name do 블록에서 중첩된 엔드포인트를 정의하는 것을 확인하십시오
    • 라우터를 살펴보아 리소스에 대한 엔드포인트의 전체 범위를 이해하십시오
  2. 파일에서 모든 HTTP 메서드/라우트 쌍을 식별합니다. HTTP 동사와 함께 각 엔드포인트를 문서화합니다:

    get ':id/jobs'
    get ':id/jobs/:job_id'
    post ':id/jobs/:job_id/cancel'
    post ':id/jobs/:job_id/retry'
    delete ':id/jobs/:job_id/artifacts'
    
  3. 이미 인증 데코레이터(route_setting :authorization)가 있는 엔드포인트를 확인합니다. 다음을 수행해야 합니다:

    • 데코레이터가 없는 엔드포인트에 데코레이터 추가
    • 불완전하거나 잘못된 권한이 있는 엔드포인트의 데코레이터 업데이트
Note

이러한 엔드포인트는 다음 단계에서 생성할 원시 권한의 기준이 됩니다. 각 고유한 작업(HTTP 동사 + 라우트)에는 일반적으로 자체 권한이 필요합니다.

2단계: 필요한 권한 결정#

목표: GitLab 명명 규칙에 따라 세분화된 권한을 정의합니다.

명명 규칙은 규칙 문서의 권한 명명을 참조하십시오.

엔드포인트에 대한 리소스 이름 결정#

세분화된 PAT 인증을 구현할 때 엔드포인트가 수정하거나 반환하는 내용에 따라 권한 이름을 지정하며, 라우트 구조가 아닙니다.

예시:

  • 엔드포인트 DELETE /projects/:id/jobs/:job_id/artifactsartifacts를 수정 → 권한 이름은 delete_job_artifact
  • 엔드포인트 GET /projects/:id/issuesissues를 반환 → 권한 이름은 read_issue
  • 엔드포인트 POST /projects/:id/jobs/:job_id/canceljob 상태를 수정 → 권한 이름은 cancel_job

일반적인 패턴#

  • 목록 및 표시 작업: 두 작업에 단일 read_resource 권한 사용
    • GET /projects/:id/jobsread_job
    • GET /projects/:id/jobs/:job_idread_job
  • 중첩된 리소스: 권한 이름에 상위 리소스 포함
    • POST /projects/:id/pipeline_schedules/:pipeline_schedule_id/variablescreate_pipeline_schedule_variable
  • 특수 작업: 고유한 작업에 대한 특정 권한 생성
    • 취소, 재시도, 다운로드, 트리거 등 각각 자체 권한 보유
  • 속성 업데이트: 모든 속성을 포함하는 단일 업데이트 권한 사용
    • update_issue는 제목, 설명, 담당자 업데이트 등을 포함
    • update_issue_description, update_issue_title을 만들지 마십시오

3단계: 권한 정의 파일 생성#

목표: 아직 존재하지 않는 경우 각 권한에 대한 YAML 정의 파일을 생성합니다.

bin/permission 명령을 사용하여 원시 권한 YAML 파일을 생성하는 방법은 권한 정의 파일 섹션의 지침을 따르십시오.

4단계: 할당 가능한 권한 생성 또는 업데이트#

목표: 더 간단한 사용자 경험을 위해 원시 권한을 할당 가능한 권한으로 번들링합니다.

가능한 경우 새 할당 가능한 권한을 생성하는 대신 기존 할당 가능한 권한에 원시 권한을 추가하는 것을 선호하십시오. 할당 가능한 권한은 사용자에게 노출됩니다. 새로 추가할 때마다 토큰 생성 UI에 표시되며, 해당 이름은 이를 선택하는 모든 토큰에 대해 데이터베이스에 저장됩니다. 나중에 할당 가능한 권한을 제거하거나 이름을 변경하면 호환성이 깨지는 변경(breaking change)이지만, 기존 할당 가능한 권한 내의 원시 권한은 자유롭게 이름을 변경하거나 이동할 수 있습니다. 원시 권한이 해당 리소스의 기존 할당 가능한 권한과 별도로 사용자가 부여할 수 있어야 하는 기능을 나타내는 경우에만 새 할당 가능한 권한을 생성하십시오. 각 변경 유형의 영향에 대해서는 할당 가능한 권한 유지 관리를 참조하십시오.

할당 가능한 권한 YAML 파일을 생성하거나 업데이트하는 방법은 할당 가능한 권한 문서의 지침을 따르십시오.

5단계: API 엔드포인트에 인증 데코레이터 추가#

각 엔드포인트에 대해 라우트 정의 바로 앞에 route_setting :authorization 데코레이터를 추가합니다:

route_setting :authorization, permissions: :read_job, boundary_type: :project
get ':id/jobs' do
  # 엔드포인트 구현
end

데코레이터 옵션#

옵션 설명
permissions 이 엔드포인트에 필요한 권한(기호 또는 기호 배열)
boundary_type 단일 경계 엔드포인트의 경계 유형: :project, :group, :user 또는 :instance
boundary_param 선택 사항. 경계 식별자를 포함하는 요청 매개변수. 프로젝트의 경우 :project_id(폴백으로 :id), 그룹의 경우 :group_id(폴백으로 :id)가 기본값
boundaries 여러 경계를 지원하는 엔드포인트에 대해 boundary_type 대신 사용(아래 참조)
boundary 표준 매개변수 조회를 통해 경계를 결정할 수 없는 엔드포인트에 대해 boundary_type 대신 사용. 경계 개체를 반환하는 호출 가능한 개체(proc, lambda, 또는 method)
skip_granular_token_authorization 선택 사항. 세분화된 PAT가 특정 권한 없이 엔드포인트에 액세스할 수 있는 이유를 명명하는 기호(예: :public_endpoint)(아래 참조)

사용자 지정 boundary_param 예시:

route_setting :authorization, permissions: :read_job, boundary_type: :project, boundary_param: :project_id
get 'jobs' do
  # 엔드포인트는 params[:id] 대신 params[:project_id]를 사용
end

boundary 사용 예시:

def registry
  ::VirtualRegistries::Packages::Maven::Registry.find(params[:id])
end

route_setting :authorization, permissions: :download_maven_package_file, boundary: -> { registry.group }, boundary_type: :group
get '/api/v4/virtual_registries/packages/maven/:id/*path' do
  # `params`를 통해 경계를 결정할 수 없습니다. 대신 엔드포인트의
  # 매개변수에서 ID를 사용하여 가져온 개체(registry)에서 결정됩니다.
end

엔드포인트당 여러 경계#

일부 엔드포인트는 여러 경계 유형을 지원해야 할 수 있습니다. 예를 들어, 가져오기 엔드포인트는 그룹 네임스페이스로 가져올 때 그룹 수준에서 작동하거나 개인 네임스페이스로 가져올 때 사용자 수준에서 작동할 수 있습니다. 이러한 경우 boundary_type 또는 boundary 대신 boundaries 옵션을 사용합니다:

route_setting :authorization, permissions: :create_bitbucket_import,
  boundaries: [{ boundary_type: :group, boundary_param: :target_namespace }, { boundary_type: :user }]
post 'import/bitbucket' do
  # 엔드포인트 구현
end

여러 경계가 정의된 경우:

  • 배열의 각 경계에는 boundary_type 키가 필요하고 선택적으로 경계 식별자를 포함하는 요청 매개변수를 지정하는 boundary_param 키가 필요합니다
  • 요청 매개변수에서 해결될 수 있는 모든 경계가 고려됩니다(userinstance 경계는 항상 해결됨)
  • 해결된 경계는 우선순위 순서(project > group > user > instance)로 평가되며, 토큰의 범위가 그중 하나에서 필요한 모든 권한을 부여할 때 액세스가 허용됩니다

세분화된 토큰 인증 건너뛰기#

일부 엔드포인트는 인증이 필요하지 않고 공개적으로 액세스 가능하거나 토큰 인증을 구현하지 않습니다. 이러한 엔드포인트에서는 토큰 인증이 건너뛰어지므로 세분화된 권한을 정의하는 것이 의미가 없습니다. 그러나 모든 엔드포인트에 대한 적용 범위 추적을 유지하려면 건너뛰는 이유를 명명하는 기호와 함께 skip_granular_token_authorization 옵션을 사용하십시오:

route_setting :authorization, skip_granular_token_authorization: :public_endpoint
get 'public-endpoint' do
  # 엔드포인트 구현
end

이유는 lib/tasks/gitlab/permissions/routes/skip_reasons.rb에 정의된 키 중 하나여야 합니다. 검증 Rake 작업은 알 수 없는 이유를 거부합니다. 엔드포인트에 적합한 기존 이유가 없으면 사람이 읽을 수 있는 레이블과 함께 새 키를 해당 파일에 추가하십시오.

skip_granular_token_authorization 사용 시기:

  • 검색 또는 메타데이터 엔드포인트를 포함하여 인증이 필요 없는 공개 엔드포인트(:public_endpoint)
  • 개인 액세스 토큰 이외의 수단으로 인증하는 엔드포인트(예: :runner_token_auth, :job_token_auth, 또는 :geo_jwt_auth)
  • 인증이 선택 사항이고 응답이 동일한 엔드포인트

이 데코레이터를 추가하면 권한이 필요하지 않은 엔드포인트도 인증 시스템에 의해 명시적으로 적용됩니다.

중요 사항:

  • 여러 엔드포인트가 동일한 권한을 사용하더라도 모든 엔드포인트에 개별적으로 데코레이터를 추가하십시오
  • 데코레이터는 HTTP 메서드 정의(get, post, put, delete) 바로 앞에 위치해야 합니다
  • YAML 파일에 정의된 정확한 권한 이름(기호)을 사용하십시오
  • 단일 경계 엔드포인트에는 boundary_type 또는 boundary를 사용하고, 다중 경계 엔드포인트에는 boundaries 배열을 사용하십시오
  • skip_granular_token_authorization은 인증되지 않았거나 개인 액세스 토큰 이외의 수단으로 인증하는 엔드포인트에만 사용하십시오. PAT 인증을 허용하는 엔드포인트에서 권한 확인을 우회하는 데 절대 사용하지 마십시오

공개적으로 표시되는 리소스에 대한 접근 허용#

config/authz/roles/public_anonymous.yml에 나열된 권한은 대상 프로젝트 또는 그룹이 공개적으로 표시되고 관련 기능이 활성화된 경우, 일치하는 범위가 없는 세분화된 PAT에도 부여됩니다. 이는 동일한 리소스에 대해 익명 호출자가 가지는 액세스를 반영합니다. Authz::BoundaryPolicyUsers::Anonymous가 기본 프로젝트 또는 그룹에서 동일한 권한을 허용받는 경우 경계에서 해당 권한을 부여합니다.

권한을 이 동작에 옵트인하려면 config/authz/roles/public_anonymous.yml의 해당 경계(project: 또는 group:) 아래에 추가합니다:

project:
  permissions: []
  raw_permissions:
    - read_release
group:
  permissions: []
  raw_permissions:
    - read_subgroup

옵트인 시기:

  • 권한이 공개 프로젝트 또는 그룹의 익명 호출자에게 이미 표시되는 데이터에 대한 읽기 전용 액세스를 나타내는 경우.

  • 권한이 ProjectFeature(예: repository, issues)로 제어되어 기능을 비활성화하면 여전히 액세스가 차단되는 경우.

이 우회는 :project:group 경계로 제한됩니다. :user:instance 경계는 public_anonymous.yml을 참조하지 않습니다.

6단계: 인증 테스트 추가#

목표: 세분화된 PAT 권한이 엔드포인트에서 올바르게 적용되는지 확인합니다.

테스트 파일은 일반적으로 spec/requests/api/<resource>_spec.rb에 있습니다. 찾을 수 없는 경우 관련 사양 파일을 더 찾아봐야 할 수 있습니다.

이 테스트의 목적: 이 테스트는 다음을 확인합니다:

  • 레거시(비세분화된) 개인 액세스 토큰이 엔드포인트에 대한 액세스를 계속 부여함
  • 경계의 최상위 그룹이 세밀한 토큰을 적용하는 경우 레거시 토큰은 액세스가 거부됨
  • 세분화된 PAT에서 필요한 권한이 부여된 사용자는 액세스가 허용됨
  • 필요한 권한이 없는 사용자는 403 Forbidden 응답과 적절한 오류 메시지(insufficient_granular_scope)로 액세스가 거부됨
  • 인증 시스템이 엔드포인트의 권한 요구사항에 대해 세분화된 범위를 올바르게 평가
  • 기능 플래그 granular_personal_access_tokens가 적절하게 적용됨(비활성화될 때 액세스 거부)
  • 공개 리소스의 :project:group 경계에서 공개 리소스 우회는 비멤버 요청을 반영합니다. 즉, 범위 없는 세분화된 PAT는 레거시 비멤버 토큰이 GET 엔드포인트에 액세스할 수 있는 경우에만 정확히 액세스를 허용하며(public_anonymous.yml에서 누락된 권한을 포착함), GET이 아닌 엔드포인트를 통해서는 절대 액세스를 허용하지 않습니다

각 엔드포인트에 공유 예시 추가#

각 엔드포인트에 'authorizing granular token permissions' 공유 예시를 추가합니다. 이것은 인증 동작을 검증하는 재사용 가능한 테스트 도우미입니다:

it_behaves_like 'authorizing granular token permissions', :<permission_name> do
  let(:boundary_object) { <boundary_object> }
  let(:user) { <user> }
  let(:request) do
    <http_method> api("<endpoint_path>", personal_access_token: pat), params: <params_if_needed>
  end
end

공유 예시는 권한 이름 뒤에 다음 키워드 인수를 허용합니다. 이들은 let 정의가 아니라 it_behaves_like의 키워드 인수입니다(do 앞에 위치):

  • expected_success_status: 성공 응답이 :success가 아닌 경우. 일반적인 값은 :created, :accepted, :no_content, :redirect(다운로드 또는 파일 엔드포인트용)입니다.

  • legacy_token_scopes: 엔드포인트가 기본값 %w[api] 이외의 레거시 토큰 범위를 요구하는 경우.

it_behaves_like 'authorizing granular token permissions', :create_release, expected_success_status: :created do

"액세스 부여" 어설션은 실제 성공 응답을 예상하므로, 유효한 params를 제공하고 요청 경로가 참조하는 모든 리소스가 존재하는지 확인하십시오. 이미 엔드포인트를 테스트하는 describe 블록의 let 정의와 설정을 재사용하고, 공유 예시를 해당 블록 안에 배치하십시오.

경계 개체 매핑#

boundary_objectboundary_type과 일치해야 합니다:

경계 유형 경계 개체
:project project
:group group
:user :user
:instance :instance

중요: 경계 개체가 :project 또는 :group인 경우, 인증이 부여되려면 user가 해당 네임스페이스(프로젝트 또는 그룹)의 멤버여야 합니다. 레거시 토큰 컨텍스트가 해당 사용자에 대해 관리자 모드 토큰을 생성하므로, :user:instance 경계의 경우에도 항상 user를 정의하십시오.

7단계: 수동 검증#

목표: 머지 요청을 생성하기 전에 권한이 예상대로 작동하는지 확인하기 위해 로컬 환경에서 구현을 수동으로 테스트합니다.

전체 테스트 스위트를 실행하기 전에 Rails 콘솔에서 엔드포인트와 권한을 테스트하려면 이를 사용하십시오.

설정:

Rails 콘솔에서 사용자에 대한 세분화된 PAT를 생성하고 토큰으로 엔드포인트를 테스트하기 위한 URL을 복사합니다:

# 기능 플래그 활성화
Feature.enable(:granular_personal_access_tokens)

user = User.human.first

# 세분화된 토큰 생성
token = PersonalAccessTokens::CreateService.new(
  current_user: user,
  target_user: user,
  organization_id: user.organization_id,
  params: { expires_at: 1.month.from_now, scopes: ['granular'], granular: true, name: 'gPAT' }
).execute[:personal_access_token]

# 적절한 경계 개체 가져오기(project, group, :user, 또는 :instance)
project = user.projects.first
boundary = Authz::Boundary.for(project)

# 테스트 중인 할당 가능한 권한으로 범위 생성(:read_job을 사용자의 권한으로 대체).
# 범위는 할당 가능한 권한 이름을 저장하며, 요청 시 원시 권한으로 확장됩니다.
scope = Authz::GranularScope.new(namespace: boundary.namespace, access: boundary.access, permissions: [:read_job])

# 토큰에 범위 추가
Authz::GranularScopeService.new(token).add_granular_scopes(scope)

# 토큰으로 API 엔드포인트 URL 복사(엔드포인트로 대체)
IO.popen('pbcopy', 'w') { |f| f.puts "curl \"http://#{Gitlab.host_with_port}/api/v4/projects/#{project.id}/jobs\" --request GET --header \"PRIVATE-TOKEN: #{token.token}\"" }
  • 다른 터미널에 URL을 붙여넣으십시오. 성공해야 합니다.

8단계: 문서 검증 및 재생성#

목표: 모든 권한 정의, 데코레이터 및 테스트가 일관성이 있는지 확인하고 생성된 참조 문서를 업데이트합니다.

  • 세밀한 토큰 참조 문서를 재생성합니다:
bundle exec rake gitlab:permissions:routes:compile_docs

이는 doc/auth/tokens/fine_grained_access_tokens_rest.md를 업데이트합니다. 이 파일을 수동으로 편집하지 마십시오.

  • 권한 검증을 실행합니다:
bundle exec rake gitlab:permissions:validate

이 작업은 Lefthook pre-push 후크로도 실행됩니다. 여러 확인 중에서, 다음과 같은 경우 실패합니다:

엔드포인트에 route_setting :authorization 데코레이터가 없고 건너뛰기 이유도 없는 경우.

  • 데코레이터의 권한에 원시 권한 정의 파일이 없는 경우.

  • 데코레이터의 권한이 어떤 할당 가능한 권한에도 속하지 않는 경우.

  • 데코레이터의 경계 유형이 할당 가능한 권한의 boundaries와 일치하지 않는 경우.

  • 건너뛰기 이유가 lib/tasks/gitlab/permissions/routes/skip_reasons.rb에 정의되지 않은 경우.

  • 엔드포인트의 권한에 인증 테스트가 없는 경우. 각 엔드포인트 선언에는 경계 유형별로 자체 테스트가 필요합니다. 단일 선언에서 생성된 라우트(예: 인스턴스 및 프로젝트 수준 모두에 마운트된 공유 concern)는 하나의 엔드포인트로 계산됩니다.

  • 생성된 참조 문서가 최신 상태가 아닌 경우.

REST API 구현 가이드

GitLab v19.2
원문 보기
요약

침해된 개인 액세스 토큰(PAT)의 보안 영향을 줄이기 위해, 세분화된 PAT를 사용하면 특정 조직 경계(그룹, 프로젝트, 사용자 또는 인스턴스 수준)에 제한된 세밀한 권한으로 토큰을 생성할 수 있습니다. 세분화된 PAT는 경계와 특정 리소스 권한으로 구성된 세분화된 범위를 통해 세밀한 액세스 제어를 허용합니다.

침해된 개인 액세스 토큰(PAT)의 보안 영향을 줄이기 위해, 세분화된 PAT를 사용하면 특정 조직 경계(그룹, 프로젝트, 사용자 또는 인스턴스 수준)에 제한된 세밀한 권한으로 토큰을 생성할 수 있습니다. 이를 통해 사용자는 토큰에 필요한 권한만 부여하는 최소 권한 원칙을 따를 수 있습니다.

세분화된 PAT는 경계와 특정 리소스 권한으로 구성된 세분화된 범위를 통해 세밀한 액세스 제어를 허용합니다. 세분화된 PAT로 API 요청을 인증할 때, GitLab은 토큰의 권한이 지정된 경계 수준에서 요청된 리소스에 대한 액세스를 포함하는지 검증합니다.

이 문서는 REST API 엔드포인트를 세분화된 PAT 인증과 호환되도록 만들고자 하는 커뮤니티 기여자와 GitLab 개발자를 위해 설계되었습니다.

단계별 구현 가이드#

이 가이드는 REST API 엔드포인트에 세분화된 PAT 인증을 추가하는 방법을 안내합니다. 시작하기 전에 전체에서 사용되는 용어를 이해하기 위해 권한 명명 규칙 문서를 검토하십시오.

Note

이 단계는 REST API 엔드포인트만 다룹니다. GraphQL 쿼리 및 변이에 대한 지원 추가는 GraphQL 구현 가이드를 참조하십시오.

워크플로 개요#

구현은 다음 흐름을 따릅니다:

  1. 1-2단계: 계획 - 엔드포인트 식별 및 권한 설계
  2. 3단계: 원시 권한 생성(YAML 파일)
  3. 4단계: 원시 권한을 할당 가능한 권한으로 번들화(YAML 파일)
  4. 5단계: 엔드포인트에 인증 데코레이터 추가(Ruby 코드)
  5. 6단계: 인증 테스트 작성(Ruby 사양)
  6. 7단계: 로컬 테스트(수동 검증)
  7. 8단계: 문서 검증 및 재생성(Rake 작업)

각 단계에서 생성되는 파일#

각 단계에서 생성하는 파일을 보여주는 빠른 참조:

단계 파일 유형 위치 수량 예시
2 계획 문서 (메모) 권한 이름 식별
3 원시 권한 YAML config/authz/permissions/<resource>/<action>.yml 권한당 1개 config/authz/permissions/job/read.yml
3 원시 권한 리소스 메타데이터 config/authz/permissions/<resource>/.metadata.yml 리소스당 1개 config/authz/permissions/job/.metadata.yml
4 할당 가능한 권한 YAML config/authz/permission_groups/assignable_permissions/<category>/<resource>/<action>.yml 그룹당 1개 config/authz/permission_groups/assignable_permissions/ci_cd/job/run.yml
4(선택) 범주 메타데이터 config/authz/permission_groups/assignable_permissions/<category>/.metadata.yml 범주당 0 또는 1개 config/authz/permission_groups/assignable_permissions/ci_cd/.metadata.yml
4 리소스 메타데이터 config/authz/permission_groups/assignable_permissions/<category>/<resource>/.metadata.yml 리소스당 1개 config/authz/permission_groups/assignable_permissions/ci_cd/job/.metadata.yml
5 Grape 데코레이터 lib/api/<resource>.rb 수정 엔드포인트당 1개 route_setting :authorization 추가됨
6 RSpec 테스트 spec/requests/api/<resource>_spec.rb 수정 엔드포인트당 1개 it_behaves_like 'authorizing...' 추가됨
8 생성된 참조 문서 doc/auth/tokens/fine_grained_access_tokens_rest.md 1 bundle exec rake gitlab:permissions:routes:compile_docs로 재생성

1단계: 리소스에 대한 REST API 엔드포인트 식별#

목표: 작업하는 리소스에 대한 모든 REST API 엔드포인트를 찾습니다.

  1. lib/api/<resource_name>.rb에서 리소스에 대한 API 파일을 찾습니다.

    예시: jobs 리소스의 경우 lib/api/ci/jobs.rb 열기

    팁:

    • 일부 리소스는 여러 API 파일에 걸쳐 엔드포인트가 분산되어 있을 수 있습니다(예: 중첩된 리소스)
    • resources :resource_name do 블록에서 중첩된 엔드포인트를 정의하는 것을 확인하십시오
    • 라우터를 살펴보아 리소스에 대한 엔드포인트의 전체 범위를 이해하십시오
  2. 파일에서 모든 HTTP 메서드/라우트 쌍을 식별합니다. HTTP 동사와 함께 각 엔드포인트를 문서화합니다:

    get ':id/jobs'
    get ':id/jobs/:job_id'
    post ':id/jobs/:job_id/cancel'
    post ':id/jobs/:job_id/retry'
    delete ':id/jobs/:job_id/artifacts'
    
  3. 이미 인증 데코레이터(route_setting :authorization)가 있는 엔드포인트를 확인합니다. 다음을 수행해야 합니다:

    • 데코레이터가 없는 엔드포인트에 데코레이터 추가
    • 불완전하거나 잘못된 권한이 있는 엔드포인트의 데코레이터 업데이트
Note

이러한 엔드포인트는 다음 단계에서 생성할 원시 권한의 기준이 됩니다. 각 고유한 작업(HTTP 동사 + 라우트)에는 일반적으로 자체 권한이 필요합니다.

2단계: 필요한 권한 결정#

목표: GitLab 명명 규칙에 따라 세분화된 권한을 정의합니다.

명명 규칙은 규칙 문서의 권한 명명을 참조하십시오.

엔드포인트에 대한 리소스 이름 결정#

세분화된 PAT 인증을 구현할 때 엔드포인트가 수정하거나 반환하는 내용에 따라 권한 이름을 지정하며, 라우트 구조가 아닙니다.

예시:

  • 엔드포인트 DELETE /projects/:id/jobs/:job_id/artifactsartifacts를 수정 → 권한 이름은 delete_job_artifact
  • 엔드포인트 GET /projects/:id/issuesissues를 반환 → 권한 이름은 read_issue
  • 엔드포인트 POST /projects/:id/jobs/:job_id/canceljob 상태를 수정 → 권한 이름은 cancel_job

일반적인 패턴#

  • 목록 및 표시 작업: 두 작업에 단일 read_resource 권한 사용
    • GET /projects/:id/jobsread_job
    • GET /projects/:id/jobs/:job_idread_job
  • 중첩된 리소스: 권한 이름에 상위 리소스 포함
    • POST /projects/:id/pipeline_schedules/:pipeline_schedule_id/variablescreate_pipeline_schedule_variable
  • 특수 작업: 고유한 작업에 대한 특정 권한 생성
    • 취소, 재시도, 다운로드, 트리거 등 각각 자체 권한 보유
  • 속성 업데이트: 모든 속성을 포함하는 단일 업데이트 권한 사용
    • update_issue는 제목, 설명, 담당자 업데이트 등을 포함
    • update_issue_description, update_issue_title을 만들지 마십시오

3단계: 권한 정의 파일 생성#

목표: 아직 존재하지 않는 경우 각 권한에 대한 YAML 정의 파일을 생성합니다.

bin/permission 명령을 사용하여 원시 권한 YAML 파일을 생성하는 방법은 권한 정의 파일 섹션의 지침을 따르십시오.

4단계: 할당 가능한 권한 생성 또는 업데이트#

목표: 더 간단한 사용자 경험을 위해 원시 권한을 할당 가능한 권한으로 번들링합니다.

가능한 경우 새 할당 가능한 권한을 생성하는 대신 기존 할당 가능한 권한에 원시 권한을 추가하는 것을 선호하십시오. 할당 가능한 권한은 사용자에게 노출됩니다. 새로 추가할 때마다 토큰 생성 UI에 표시되며, 해당 이름은 이를 선택하는 모든 토큰에 대해 데이터베이스에 저장됩니다. 나중에 할당 가능한 권한을 제거하거나 이름을 변경하면 호환성이 깨지는 변경(breaking change)이지만, 기존 할당 가능한 권한 내의 원시 권한은 자유롭게 이름을 변경하거나 이동할 수 있습니다. 원시 권한이 해당 리소스의 기존 할당 가능한 권한과 별도로 사용자가 부여할 수 있어야 하는 기능을 나타내는 경우에만 새 할당 가능한 권한을 생성하십시오. 각 변경 유형의 영향에 대해서는 할당 가능한 권한 유지 관리를 참조하십시오.

할당 가능한 권한 YAML 파일을 생성하거나 업데이트하는 방법은 할당 가능한 권한 문서의 지침을 따르십시오.

5단계: API 엔드포인트에 인증 데코레이터 추가#

각 엔드포인트에 대해 라우트 정의 바로 앞에 route_setting :authorization 데코레이터를 추가합니다:

route_setting :authorization, permissions: :read_job, boundary_type: :project
get ':id/jobs' do
  # 엔드포인트 구현
end

데코레이터 옵션#

옵션 설명
permissions 이 엔드포인트에 필요한 권한(기호 또는 기호 배열)
boundary_type 단일 경계 엔드포인트의 경계 유형: :project, :group, :user 또는 :instance
boundary_param 선택 사항. 경계 식별자를 포함하는 요청 매개변수. 프로젝트의 경우 :project_id(폴백으로 :id), 그룹의 경우 :group_id(폴백으로 :id)가 기본값
boundaries 여러 경계를 지원하는 엔드포인트에 대해 boundary_type 대신 사용(아래 참조)
boundary 표준 매개변수 조회를 통해 경계를 결정할 수 없는 엔드포인트에 대해 boundary_type 대신 사용. 경계 개체를 반환하는 호출 가능한 개체(proc, lambda, 또는 method)
skip_granular_token_authorization 선택 사항. 세분화된 PAT가 특정 권한 없이 엔드포인트에 액세스할 수 있는 이유를 명명하는 기호(예: :public_endpoint)(아래 참조)

사용자 지정 boundary_param 예시:

route_setting :authorization, permissions: :read_job, boundary_type: :project, boundary_param: :project_id
get 'jobs' do
  # 엔드포인트는 params[:id] 대신 params[:project_id]를 사용
end

boundary 사용 예시:

def registry
  ::VirtualRegistries::Packages::Maven::Registry.find(params[:id])
end

route_setting :authorization, permissions: :download_maven_package_file, boundary: -> { registry.group }, boundary_type: :group
get '/api/v4/virtual_registries/packages/maven/:id/*path' do
  # `params`를 통해 경계를 결정할 수 없습니다. 대신 엔드포인트의
  # 매개변수에서 ID를 사용하여 가져온 개체(registry)에서 결정됩니다.
end

엔드포인트당 여러 경계#

일부 엔드포인트는 여러 경계 유형을 지원해야 할 수 있습니다. 예를 들어, 가져오기 엔드포인트는 그룹 네임스페이스로 가져올 때 그룹 수준에서 작동하거나 개인 네임스페이스로 가져올 때 사용자 수준에서 작동할 수 있습니다. 이러한 경우 boundary_type 또는 boundary 대신 boundaries 옵션을 사용합니다:

route_setting :authorization, permissions: :create_bitbucket_import,
  boundaries: [{ boundary_type: :group, boundary_param: :target_namespace }, { boundary_type: :user }]
post 'import/bitbucket' do
  # 엔드포인트 구현
end

여러 경계가 정의된 경우:

  • 배열의 각 경계에는 boundary_type 키가 필요하고 선택적으로 경계 식별자를 포함하는 요청 매개변수를 지정하는 boundary_param 키가 필요합니다
  • 요청 매개변수에서 해결될 수 있는 모든 경계가 고려됩니다(userinstance 경계는 항상 해결됨)
  • 해결된 경계는 우선순위 순서(project > group > user > instance)로 평가되며, 토큰의 범위가 그중 하나에서 필요한 모든 권한을 부여할 때 액세스가 허용됩니다

세분화된 토큰 인증 건너뛰기#

일부 엔드포인트는 인증이 필요하지 않고 공개적으로 액세스 가능하거나 토큰 인증을 구현하지 않습니다. 이러한 엔드포인트에서는 토큰 인증이 건너뛰어지므로 세분화된 권한을 정의하는 것이 의미가 없습니다. 그러나 모든 엔드포인트에 대한 적용 범위 추적을 유지하려면 건너뛰는 이유를 명명하는 기호와 함께 skip_granular_token_authorization 옵션을 사용하십시오:

route_setting :authorization, skip_granular_token_authorization: :public_endpoint
get 'public-endpoint' do
  # 엔드포인트 구현
end

이유는 lib/tasks/gitlab/permissions/routes/skip_reasons.rb에 정의된 키 중 하나여야 합니다. 검증 Rake 작업은 알 수 없는 이유를 거부합니다. 엔드포인트에 적합한 기존 이유가 없으면 사람이 읽을 수 있는 레이블과 함께 새 키를 해당 파일에 추가하십시오.

skip_granular_token_authorization 사용 시기:

  • 검색 또는 메타데이터 엔드포인트를 포함하여 인증이 필요 없는 공개 엔드포인트(:public_endpoint)
  • 개인 액세스 토큰 이외의 수단으로 인증하는 엔드포인트(예: :runner_token_auth, :job_token_auth, 또는 :geo_jwt_auth)
  • 인증이 선택 사항이고 응답이 동일한 엔드포인트

이 데코레이터를 추가하면 권한이 필요하지 않은 엔드포인트도 인증 시스템에 의해 명시적으로 적용됩니다.

중요 사항:

  • 여러 엔드포인트가 동일한 권한을 사용하더라도 모든 엔드포인트에 개별적으로 데코레이터를 추가하십시오
  • 데코레이터는 HTTP 메서드 정의(get, post, put, delete) 바로 앞에 위치해야 합니다
  • YAML 파일에 정의된 정확한 권한 이름(기호)을 사용하십시오
  • 단일 경계 엔드포인트에는 boundary_type 또는 boundary를 사용하고, 다중 경계 엔드포인트에는 boundaries 배열을 사용하십시오
  • skip_granular_token_authorization은 인증되지 않았거나 개인 액세스 토큰 이외의 수단으로 인증하는 엔드포인트에만 사용하십시오. PAT 인증을 허용하는 엔드포인트에서 권한 확인을 우회하는 데 절대 사용하지 마십시오

공개적으로 표시되는 리소스에 대한 접근 허용#

config/authz/roles/public_anonymous.yml에 나열된 권한은 대상 프로젝트 또는 그룹이 공개적으로 표시되고 관련 기능이 활성화된 경우, 일치하는 범위가 없는 세분화된 PAT에도 부여됩니다. 이는 동일한 리소스에 대해 익명 호출자가 가지는 액세스를 반영합니다. Authz::BoundaryPolicyUsers::Anonymous가 기본 프로젝트 또는 그룹에서 동일한 권한을 허용받는 경우 경계에서 해당 권한을 부여합니다.

권한을 이 동작에 옵트인하려면 config/authz/roles/public_anonymous.yml의 해당 경계(project: 또는 group:) 아래에 추가합니다:

project:
  permissions: []
  raw_permissions:
    - read_release
group:
  permissions: []
  raw_permissions:
    - read_subgroup

옵트인 시기:

  • 권한이 공개 프로젝트 또는 그룹의 익명 호출자에게 이미 표시되는 데이터에 대한 읽기 전용 액세스를 나타내는 경우.

  • 권한이 ProjectFeature(예: repository, issues)로 제어되어 기능을 비활성화하면 여전히 액세스가 차단되는 경우.

이 우회는 :project:group 경계로 제한됩니다. :user:instance 경계는 public_anonymous.yml을 참조하지 않습니다.

6단계: 인증 테스트 추가#

목표: 세분화된 PAT 권한이 엔드포인트에서 올바르게 적용되는지 확인합니다.

테스트 파일은 일반적으로 spec/requests/api/<resource>_spec.rb에 있습니다. 찾을 수 없는 경우 관련 사양 파일을 더 찾아봐야 할 수 있습니다.

이 테스트의 목적: 이 테스트는 다음을 확인합니다:

  • 레거시(비세분화된) 개인 액세스 토큰이 엔드포인트에 대한 액세스를 계속 부여함
  • 경계의 최상위 그룹이 세밀한 토큰을 적용하는 경우 레거시 토큰은 액세스가 거부됨
  • 세분화된 PAT에서 필요한 권한이 부여된 사용자는 액세스가 허용됨
  • 필요한 권한이 없는 사용자는 403 Forbidden 응답과 적절한 오류 메시지(insufficient_granular_scope)로 액세스가 거부됨
  • 인증 시스템이 엔드포인트의 권한 요구사항에 대해 세분화된 범위를 올바르게 평가
  • 기능 플래그 granular_personal_access_tokens가 적절하게 적용됨(비활성화될 때 액세스 거부)
  • 공개 리소스의 :project:group 경계에서 공개 리소스 우회는 비멤버 요청을 반영합니다. 즉, 범위 없는 세분화된 PAT는 레거시 비멤버 토큰이 GET 엔드포인트에 액세스할 수 있는 경우에만 정확히 액세스를 허용하며(public_anonymous.yml에서 누락된 권한을 포착함), GET이 아닌 엔드포인트를 통해서는 절대 액세스를 허용하지 않습니다

각 엔드포인트에 공유 예시 추가#

각 엔드포인트에 'authorizing granular token permissions' 공유 예시를 추가합니다. 이것은 인증 동작을 검증하는 재사용 가능한 테스트 도우미입니다:

it_behaves_like 'authorizing granular token permissions', :<permission_name> do
  let(:boundary_object) { <boundary_object> }
  let(:user) { <user> }
  let(:request) do
    <http_method> api("<endpoint_path>", personal_access_token: pat), params: <params_if_needed>
  end
end

공유 예시는 권한 이름 뒤에 다음 키워드 인수를 허용합니다. 이들은 let 정의가 아니라 it_behaves_like의 키워드 인수입니다(do 앞에 위치):

  • expected_success_status: 성공 응답이 :success가 아닌 경우. 일반적인 값은 :created, :accepted, :no_content, :redirect(다운로드 또는 파일 엔드포인트용)입니다.

  • legacy_token_scopes: 엔드포인트가 기본값 %w[api] 이외의 레거시 토큰 범위를 요구하는 경우.

it_behaves_like 'authorizing granular token permissions', :create_release, expected_success_status: :created do

"액세스 부여" 어설션은 실제 성공 응답을 예상하므로, 유효한 params를 제공하고 요청 경로가 참조하는 모든 리소스가 존재하는지 확인하십시오. 이미 엔드포인트를 테스트하는 describe 블록의 let 정의와 설정을 재사용하고, 공유 예시를 해당 블록 안에 배치하십시오.

경계 개체 매핑#

boundary_objectboundary_type과 일치해야 합니다:

경계 유형 경계 개체
:project project
:group group
:user :user
:instance :instance

중요: 경계 개체가 :project 또는 :group인 경우, 인증이 부여되려면 user가 해당 네임스페이스(프로젝트 또는 그룹)의 멤버여야 합니다. 레거시 토큰 컨텍스트가 해당 사용자에 대해 관리자 모드 토큰을 생성하므로, :user:instance 경계의 경우에도 항상 user를 정의하십시오.

7단계: 수동 검증#

목표: 머지 요청을 생성하기 전에 권한이 예상대로 작동하는지 확인하기 위해 로컬 환경에서 구현을 수동으로 테스트합니다.

전체 테스트 스위트를 실행하기 전에 Rails 콘솔에서 엔드포인트와 권한을 테스트하려면 이를 사용하십시오.

설정:

Rails 콘솔에서 사용자에 대한 세분화된 PAT를 생성하고 토큰으로 엔드포인트를 테스트하기 위한 URL을 복사합니다:

# 기능 플래그 활성화
Feature.enable(:granular_personal_access_tokens)

user = User.human.first

# 세분화된 토큰 생성
token = PersonalAccessTokens::CreateService.new(
  current_user: user,
  target_user: user,
  organization_id: user.organization_id,
  params: { expires_at: 1.month.from_now, scopes: ['granular'], granular: true, name: 'gPAT' }
).execute[:personal_access_token]

# 적절한 경계 개체 가져오기(project, group, :user, 또는 :instance)
project = user.projects.first
boundary = Authz::Boundary.for(project)

# 테스트 중인 할당 가능한 권한으로 범위 생성(:read_job을 사용자의 권한으로 대체).
# 범위는 할당 가능한 권한 이름을 저장하며, 요청 시 원시 권한으로 확장됩니다.
scope = Authz::GranularScope.new(namespace: boundary.namespace, access: boundary.access, permissions: [:read_job])

# 토큰에 범위 추가
Authz::GranularScopeService.new(token).add_granular_scopes(scope)

# 토큰으로 API 엔드포인트 URL 복사(엔드포인트로 대체)
IO.popen('pbcopy', 'w') { |f| f.puts "curl \"http://#{Gitlab.host_with_port}/api/v4/projects/#{project.id}/jobs\" --request GET --header \"PRIVATE-TOKEN: #{token.token}\"" }
  • 다른 터미널에 URL을 붙여넣으십시오. 성공해야 합니다.

8단계: 문서 검증 및 재생성#

목표: 모든 권한 정의, 데코레이터 및 테스트가 일관성이 있는지 확인하고 생성된 참조 문서를 업데이트합니다.

  • 세밀한 토큰 참조 문서를 재생성합니다:
bundle exec rake gitlab:permissions:routes:compile_docs

이는 doc/auth/tokens/fine_grained_access_tokens_rest.md를 업데이트합니다. 이 파일을 수동으로 편집하지 마십시오.

  • 권한 검증을 실행합니다:
bundle exec rake gitlab:permissions:validate

이 작업은 Lefthook pre-push 후크로도 실행됩니다. 여러 확인 중에서, 다음과 같은 경우 실패합니다:

엔드포인트에 route_setting :authorization 데코레이터가 없고 건너뛰기 이유도 없는 경우.

  • 데코레이터의 권한에 원시 권한 정의 파일이 없는 경우.

  • 데코레이터의 권한이 어떤 할당 가능한 권한에도 속하지 않는 경우.

  • 데코레이터의 경계 유형이 할당 가능한 권한의 boundaries와 일치하지 않는 경우.

  • 건너뛰기 이유가 lib/tasks/gitlab/permissions/routes/skip_reasons.rb에 정의되지 않은 경우.

  • 엔드포인트의 권한에 인증 테스트가 없는 경우. 각 엔드포인트 선언에는 경계 유형별로 자체 테스트가 필요합니다. 단일 선언에서 생성된 라우트(예: 인스턴스 및 프로젝트 수준 모두에 마운트된 공유 concern)는 하나의 엔드포인트로 계산됩니다.

  • 생성된 참조 문서가 최신 상태가 아닌 경우.