InfoGrab DocsInfoGrab Docs

권한 규칙

요약

새 권한은 반드시 필요할 때만 도입합니다. 권한을 도입해야 하는 예는 admin_project처럼 권한 범위가 매우 넓은 경우입니다. 새 권한은 최소 권한 원칙을 뒷받침해야 합니다. 권한은 역할 정의 YAML 파일(기본 역할용), 커스텀 어빌리티 YAML 파일(커스텀 역할용), 할당 가능 권한 그룹(세분화된 PAT 스코프 지정용)에서 참조됩니다.

새로운 권한 도입#

새 권한은 반드시 필요할 때만 도입합니다. 항상 기존 권한을 먼저 사용해 봅니다. 예를 들어 이미 read_issue가 있고 두 권한이 요구하는 접근 수준이 같다면 read_issue_description 권한은 필요하지 않습니다. 일반적인 지침으로, 주체와 행위가 같다면 권한을 재사용할 수 있습니다. 앞의 예에서 주체는 issue 이고 행위는 read 입니다. 사용자가 읽을 수 있는 이슈의 속성마다 새 권한을 만들 필요는 없습니다.

권한을 도입해야 하는 예는 admin_project처럼 권한 범위가 매우 넓은 경우입니다. 이 경우 권한이 모호하며 프로젝트 메인테이너에게 부여됩니다. 이론상 이 권한은 CI/CD 변수 관리 기능이 메인테이너에게 부여되므로 프로젝트에서 CI/CD 변수를 관리하는 접근을 제어하는 데 쓸 수 있습니다. 다만 범위가 넓은 권한을 사용하면 권한 검사만 보아서는 무엇을 인가하는지 분명하지 않습니다. 또한 admin_cicd_variable 이나 manage_cicd_variable 같은 권한은 인가되는 행위가 여러 가지임을 함의하므로 피해야 합니다. 대신 행위는 create_cicd_variable 이나 read_cicd_variable처럼 구체적이어야 합니다. 세분화된 권한을 구현하면 커스텀 역할에서 최소 권한 원칙을 지킬 수 있고 표준 역할에도 훨씬 세밀한 선택지를 제공합니다.

새 권한은 최소 권한 원칙을 뒷받침해야 합니다. 단일 리소스에 대한 하나의 명확히 정의된 행위에 필요한 접근만 부여해야 하며 그 이상은 안 됩니다. 제안된 권한이 둘 이상의 행위에 대한 접근을 부여하거나 서로 무관한 기능을 묶는다면 별개의 권한으로 분리합니다. 권한을 이만큼 좁게 한정할 수 없다면 도입하기 전에 설계를 다시 검토합니다.

권한은 역할 정의 YAML 파일(기본 역할용), 커스텀 어빌리티 YAML 파일(커스텀 역할용), 할당 가능 권한 그룹(세분화된 PAT 스코프 지정용)에서 참조됩니다.

권한 명명#

모든 권한이 action_resource(_subresource)라는 일관된 패턴을 따르도록 하는 것이 목표입니다. 이 지침은 Assignable Permissions와 Raw Permissions 모두에 적용되지만, Assignable Permissions는 외부에 공개되므로 더 엄격하게 지켜야 합니다.

선호 행위(Actions)#

새 권한을 도입한다면 다음 행위 중 하나를 사용하는 것이 좋습니다.

행위 기능 예시
create 새 객체를 생성합니다 create_issue
read 객체를 조회하거나 검색합니다 read_project
update 기존 객체를 수정합니다 update_merge_request
delete 객체를 제거합니다 delete_issue

이 행위 집합이 제한적이며 모든 기능에 적용되지는 않는다는 점을 인지하고 있습니다. 상황에 따라 이 집합 밖의 행위도 허용되지만 Authorization 팀의 승인이 필요합니다.

허용되지 않는 행위(Actions)#

다음 행위 패턴은 권한 카탈로그에 도입해서는 안 되는 사례입니다.

행위 허용되지 않는 이유
admin 범위가 불명확한 광범위하고 정의되지 않은 권한을 함의합니다
change update와 중복됩니다
configure update와 중복됩니다
destroy 도메인 행위가 아니라 구현 시맨틱을 반영합니다. delete를 사용합니다
edit update와 중복됩니다
list read 시맨틱이 모호합니다. read를 사용합니다
manage 여러 CRUD 작업을 하나의 모호한 권한으로 묶습니다
modify update와 중복됩니다
set update와 중복됩니다
view read 시맨틱이 모호합니다. read를 사용합니다
write create, update, delete 작업을 모두 포함하므로, 사용자가 create 나 update 권한만 필요한 상황에서 실수로 delete 접근을 받는 보안 사고로 이어지는 의도치 않은 권한 상승을 일으킵니다. create, update, delete처럼 구체적인 행위를 사용합니다

이러한 행위를 쓰는 권한이 보이더라도, 이는 대체로 지금의 규칙이 정해지기 전에 도입된 것이며 현재 지침에 맞게 차차 리팩터링될 예정입니다.

새로운 행위를 도입해야 하는 경우#

선호 행위 집합 밖에도 사용자에게 안전하고 직관적인 권한 모델을 제공하는 데 필요한 행위가 있습니다.

새 행위는 다음과 같은 경우에 도입할 수 있습니다.

  1. 해당 행위가 GitLab 도메인 언어에 이미 존재하는 고유한 라이프사이클이나 상태 전환을 나타내는 경우. 예를 들어 archive_project 나 protect_branch는 GitLab 도메인 언어에 이미 자리 잡고 있어 사용자가 이해하고 기대하는 구체적인 행위를 나타냅니다.
  2. 해당 행위가 GitLab 도메인 언어에 속한 리소스 사이의 관계를 변경하는 경우. 예를 들어 transfer_project 나 move_issue는 리소스와 상위 네임스페이스 사이의 관계를 바꾸는 구체적인 행위를 나타냅니다.
  3. 해당 행위가 영향이 크거나 되돌릴 수 없으며 고유한 도메인 의미를 갖는 경우. 예를 들어 purge_maven_virtual_registry_cache는 되돌릴 수 없고 소프트웨어 업계 전반의 캐시 논의에서 확립된 의미를 지닌 purge 행위를 사용합니다.

리소스 명명 규칙#

권한 이름의 리소스(그리고 선택적 하위 리소스)는 항상 다음을 지켜야 합니다.

  1. 단수형을 사용합니다(예: read_projects가 아니라 read_project).
  2. 대상이 되는 도메인 객체와 일치시킵니다(예: Issue를 대상으로 행위를 평가한다면 권한 이름은 {action}_issue 형식이어야 합니다).
  3. 구현 세부 사항을 드러내는 대신 사용자에게 노출되는 도메인 용어를 사용합니다(예: 고객이 해당 리소스의 존재를 알 방법이 없다면 그 이름은 권한 이름에 들어가지 않는 편이 좋습니다).

권한 이름에서 리소스 경계 방지#

권한 이름에는 project, group, user 같은 리소스 경계를 직접 담지 않아야 합니다.

예를 들어 read_project_insights_dashboard와 read_group_insights_dashboard처럼 권한을 따로 도입하지 않습니다. 대신 read_insights_dashboard처럼 기능 자체를 설명하는 단일 시맨틱 권한을 정의합니다.

can? 검사에 주체를 전달하는 시점에 이미 범위가 결정되므로, 권한 이름에 project 나 group 같은 경계를 포함하는 것은 불필요합니다. 예를 들면 다음과 같습니다.

can?(:read_insights_dashboard, project)
can?(:read_insights_dashboard, group)

예외#

이 규칙을 따르지 않는 새 권한이 필요하다고 판단되면 Govern:Authorization 팀에 문의합니다. 논의는 언제나 환영합니다. 이 지침은 엔지니어의 작업을 복잡하게 만들기 위한 것이 아니라 더 수월하게 하기 위한 것입니다.

비공개 권한#

비공개 권한은 조건부이거나 미묘한 기능을 나타내는 좁은 범위의 권한입니다. 이름 앞에 밑줄(_)을 붙여 정책 로직 내부에서만 쓰이며 집행 지점(컨트롤러, 서비스, 파인더, GraphQL)에서 직접 검사해서는 안 된다는 점을 나타냅니다.

비공개 권한이 존재하는 이유#

GitLab RBAC 모델에서는 같은 행위라도 역할에 따라 다르게 동작할 수 있습니다. 예를 들면 다음과 같습니다.

  • Guest는 자신이 작성했거나 자신에게 할당된 기밀 이슈를 읽을 수 있습니다.
  • **Planner+**는 모든 기밀 이슈를 읽을 수 있습니다.

비공개 권한이 없으면 두 경우 모두 하나의 read_issue 권한으로 매핑되고 그 차이는 절차적 정책 로직 안에 묻힙니다. 이때 다음 문제가 생깁니다.

  • 권한 상승 위험: 사용자가 다른 사용자를 초대할 때 시스템은 초대되는 역할의 권한이 초대하는 사용자 자신의 권한을 넘지 않는지 확인해야 합니다. read_issue 같은 평면적 권한에서는 자신이 작성한 이슈만 읽을 수 있는 Guest와 모든 이슈를 읽을 수 있는 Owner가 동일하게 보입니다.
  • 커스텀 역할의 모호성: 평면적 권한으로 구성한 커스텀 역할은 "자신이 작성한 이슈만 읽기"를 표현할 수 없습니다. 커스텀 역할은 read_issue 전체를 받거나(너무 넓음) 아무것도 받지 못합니다(너무 제한적).

비공개 권한은 그 차이를 명시적이고 기계가 읽을 수 있는 형태로 만들어 이러한 문제를 해결합니다.

명명 규칙#

비공개 권한은 _<action>_<qualifier>_<resource> 패턴을 따르며, qualifier는 해당 권한이 적용되는 조건을 설명합니다.

권한 설명 일반적인 역할
_read_authored_issue 기밀 여부와 무관하게 자신이 작성한 이슈를 읽습니다 Guest+, Internal
_read_assigned_issue 기밀 여부와 무관하게 자신에게 할당된 이슈를 읽습니다 Guest+, Internal
_read_confidential_issue 모든 기밀 이슈를 읽습니다 Planner, Reporter+

밑줄 접두사는 다음과 같은 목적을 갖습니다.

  1. 코드와 YAML 정의에서 비공개 권한을 공개 권한과 시각적으로 구분합니다.
  2. 이 권한이 정책 파일 밖의 can? 검사에 나타나서는 안 된다는 점을 개발자와 도구에 알립니다.
  3. 린터가 부적절한 사용을 쉽게 표시할 수 있게 합니다.
  4. 설정보다 관례를 따릅니다. 권한 정의에 메타데이터를 저장하는 것보다 밑줄 문법이 더 낫습니다.

Qualifier 명명 규칙#

qualifier는 일반적으로 리소스의 속성이어야 합니다. 리소스 자체에 존재하는 속성을 기준으로, 해당 권한이 적용되는 리소스의 부분 집합을 설명합니다.

qualifier 지침은 다음과 같습니다.

  1. qualifier는 리소스의 실제 속성이나 관계에 대응해야 합니다. 행위자를 설명하는 qualifier는 사용하지 않습니다(예: admin_read_issue는 피합니다).
  2. 리소스에 대한 설명으로 자연스럽게 읽히는 과거 분사형이나 형용사형을 사용하는 것이 좋습니다(예: confidential, authored, assigned).
  3. qualifier는 같은 리소스에 대한 같은 행위가 리소스 상태에 따라 서로 다른 접근 수준을 요구할 때만 도입합니다. 해당 리소스의 모든 인스턴스가 같은 접근 수준을 요구한다면 qualifier는 불필요합니다.

정책에서 비공개 권한을 사용하는 방법#

비공개 권한은 게이트 역할을 합니다. 역할은 역할 YAML 정의를 통해 비공개 권한을 활성화합니다. 정책은 이 비공개 권한과 주체 수준 조건을 결합해 더 넓은 공개 권한을 활성화합니다.

# In the role YAML (e.g., config/authz/roles/guest.yml):
#   raw_permissions:
#     - _read_authored_issue
#     - _read_assigned_issue
#     - read_issue
#
# In the role YAML (e.g., config/authz/roles/reporter.yml):
#   raw_permissions:
#     - _read_authored_issue
#     - _read_assigned_issue
#     - _read_confidential_issue
#     - read_issue

# In the issue policy (e.g., app/policies/issue_policy.rb):
condition(:is_author) { @subject.author == @user }
condition(:is_assignee) { @subject.assignees.include?(@user) }
condition(:is_confidential) { @subject.confidential? }

rule { ~is_author }.prevent :_read_authored_issue
rule { ~is_assignee }.prevent :_read_assigned_issue

rule { can?(:_read_authored_issue) | can?(:_read_assigned_issue) }.policy do
  enable :read_issue
  enable :_read_confidential_issue
end

rule { is_confidential & ~can?(:_read_confidential_issue) }.prevent :read_issue

집행 지점(컨트롤러, 서비스, GraphQL)은 공개 권한만 검사합니다.

# Correct - check the public permission
authorize :read_issue

# Wrong - never check a private permission at an enforcement point
authorize :_read_authored_issue

비공개 권한을 사용해야 하는 경우#

다음과 같은 경우에 비공개 권한을 사용합니다.

  1. 주체 수준 조건이 충족될 때만 역할이 해당 행위를 수행할 수 있는 경우 (사용자가 작성, 사용자에게 할당, 사용자가 생성).
  2. 같은 행위에 대해 역할마다 접근 수준이 다른 경우(일부는 무조건, 일부는 조건부).
  3. 그 차이가 권한 상승 검사나 커스텀 역할 구성에 영향을 주는 경우.

다음 경우에는 비공개 권한을 사용하지 않습니다.

  • 기능 플래그나 라이선스 검사. 대신 prevent 규칙을 사용합니다.
  • 설정 기반 제한. 대신 prevent 규칙을 사용합니다.
  • 모든 역할에 동일하게 적용되는 조건. 단일 prevent 규칙을 사용합니다.

권한 정의 파일#

비공개 권한도 다른 권한과 마찬가지로 정의 파일이 필요합니다. 파일 이름은 권한 이름과 맞추기 위해 밑줄 접두사를 사용합니다.

config/authz/permissions/<resource>/_<action>_<qualifier>.yml

예를 들면 다음과 같습니다.

# config/authz/permissions/issue/_read_authored.yml
---
name: _read_authored_issue
description: Allows users to read issues they authored when they would not otherwise have access

conditionally_enables로 더 넓은 권한 선언#

모든 비공개 권한은 conditionally_enables 필드를 선언해야 합니다. 이 필드에는 해당 비공개 권한이 좁혀 주는 기능을 이미 부여하는 더 넓은 공개 권한을 하나 또는 여러 개 나열합니다. 역할 확장은 이 필드를 읽습니다. 나열된 권한을 모두 보유한 역할은 비공개 권한도 암묵적으로 보유하므로, 더 넓은 권한을 이미 보유한 역할에는 비공개 권한을 따로 나열하지 않습니다.

하나의 공개 권한이 비공개 권한을 포괄한다면 더 넓은 권한 하나만 사용합니다.

# config/authz/permissions/work_item/_read_authored.yml
---
name: _read_authored_work_item
description: Grants the ability to read work items that were authored by the user.
conditionally_enables: read_work_item

read_work_item을 부여하는 역할은 확장을 통해 _read_authored_work_item도 함께 부여합니다. read_work_item을 부여하지 않는 역할은 영향을 받지 않으므로, Guest는 역할 정의를 통해 _read_authored_work_item을 직접 받을 수 있습니다.

역할이 비공개 권한을 함의하려면 여러 권한을 모두 보유해야 하는 경우에는 그 권한들을 나열합니다.

conditionally_enables:
  - push_code
  - create_merge_request_from
  - create_merge_request_in

비공개 권한을 포괄하는 공개 권한이 없으면 null을 사용합니다. 이는 일반적으로 비공개 권한이 조건부로 활성화하는 것이 아니라 조건부로 차단할 때에 해당합니다.

# config/authz/permissions/job/_update_protected.yml
---
name: _update_protected_job
description: Private permission to enable/prevent job update and write abilities
conditionally_enables: null

비공개 권한이 conditionally_enables를 생략하면 검증 태스크가 실패합니다.

권한 규칙

GitLab v19.4
원문 보기

요약

새 권한은 반드시 필요할 때만 도입합니다. 권한을 도입해야 하는 예는 admin_project처럼 권한 범위가 매우 넓은 경우입니다. 새 권한은 최소 권한 원칙을 뒷받침해야 합니다. 권한은 역할 정의 YAML 파일(기본 역할용), 커스텀 어빌리티 YAML 파일(커스텀 역할용), 할당 가능 권한 그룹(세분화된 PAT 스코프 지정용)에서 참조됩니다.

새로운 권한 도입#

새 권한은 반드시 필요할 때만 도입합니다. 항상 기존 권한을 먼저 사용해 봅니다. 예를 들어 이미 read_issue가 있고 두 권한이 요구하는 접근 수준이 같다면 read_issue_description 권한은 필요하지 않습니다. 일반적인 지침으로, 주체와 행위가 같다면 권한을 재사용할 수 있습니다. 앞의 예에서 주체는 issue 이고 행위는 read 입니다. 사용자가 읽을 수 있는 이슈의 속성마다 새 권한을 만들 필요는 없습니다.

권한을 도입해야 하는 예는 admin_project처럼 권한 범위가 매우 넓은 경우입니다. 이 경우 권한이 모호하며 프로젝트 메인테이너에게 부여됩니다. 이론상 이 권한은 CI/CD 변수 관리 기능이 메인테이너에게 부여되므로 프로젝트에서 CI/CD 변수를 관리하는 접근을 제어하는 데 쓸 수 있습니다. 다만 범위가 넓은 권한을 사용하면 권한 검사만 보아서는 무엇을 인가하는지 분명하지 않습니다. 또한 admin_cicd_variable 이나 manage_cicd_variable 같은 권한은 인가되는 행위가 여러 가지임을 함의하므로 피해야 합니다. 대신 행위는 create_cicd_variable 이나 read_cicd_variable처럼 구체적이어야 합니다. 세분화된 권한을 구현하면 커스텀 역할에서 최소 권한 원칙을 지킬 수 있고 표준 역할에도 훨씬 세밀한 선택지를 제공합니다.

새 권한은 최소 권한 원칙을 뒷받침해야 합니다. 단일 리소스에 대한 하나의 명확히 정의된 행위에 필요한 접근만 부여해야 하며 그 이상은 안 됩니다. 제안된 권한이 둘 이상의 행위에 대한 접근을 부여하거나 서로 무관한 기능을 묶는다면 별개의 권한으로 분리합니다. 권한을 이만큼 좁게 한정할 수 없다면 도입하기 전에 설계를 다시 검토합니다.

권한은 역할 정의 YAML 파일(기본 역할용), 커스텀 어빌리티 YAML 파일(커스텀 역할용), 할당 가능 권한 그룹(세분화된 PAT 스코프 지정용)에서 참조됩니다.

권한 명명#

모든 권한이 action_resource(_subresource)라는 일관된 패턴을 따르도록 하는 것이 목표입니다. 이 지침은 Assignable Permissions와 Raw Permissions 모두에 적용되지만, Assignable Permissions는 외부에 공개되므로 더 엄격하게 지켜야 합니다.

선호 행위(Actions)#

새 권한을 도입한다면 다음 행위 중 하나를 사용하는 것이 좋습니다.

행위 기능 예시
create 새 객체를 생성합니다 create_issue
read 객체를 조회하거나 검색합니다 read_project
update 기존 객체를 수정합니다 update_merge_request
delete 객체를 제거합니다 delete_issue

이 행위 집합이 제한적이며 모든 기능에 적용되지는 않는다는 점을 인지하고 있습니다. 상황에 따라 이 집합 밖의 행위도 허용되지만 Authorization 팀의 승인이 필요합니다.

허용되지 않는 행위(Actions)#

다음 행위 패턴은 권한 카탈로그에 도입해서는 안 되는 사례입니다.

행위 허용되지 않는 이유
admin 범위가 불명확한 광범위하고 정의되지 않은 권한을 함의합니다
change update와 중복됩니다
configure update와 중복됩니다
destroy 도메인 행위가 아니라 구현 시맨틱을 반영합니다. delete를 사용합니다
edit update와 중복됩니다
list read 시맨틱이 모호합니다. read를 사용합니다
manage 여러 CRUD 작업을 하나의 모호한 권한으로 묶습니다
modify update와 중복됩니다
set update와 중복됩니다
view read 시맨틱이 모호합니다. read를 사용합니다
write create, update, delete 작업을 모두 포함하므로, 사용자가 create 나 update 권한만 필요한 상황에서 실수로 delete 접근을 받는 보안 사고로 이어지는 의도치 않은 권한 상승을 일으킵니다. create, update, delete처럼 구체적인 행위를 사용합니다

이러한 행위를 쓰는 권한이 보이더라도, 이는 대체로 지금의 규칙이 정해지기 전에 도입된 것이며 현재 지침에 맞게 차차 리팩터링될 예정입니다.

새로운 행위를 도입해야 하는 경우#

선호 행위 집합 밖에도 사용자에게 안전하고 직관적인 권한 모델을 제공하는 데 필요한 행위가 있습니다.

새 행위는 다음과 같은 경우에 도입할 수 있습니다.

  1. 해당 행위가 GitLab 도메인 언어에 이미 존재하는 고유한 라이프사이클이나 상태 전환을 나타내는 경우. 예를 들어 archive_project 나 protect_branch는 GitLab 도메인 언어에 이미 자리 잡고 있어 사용자가 이해하고 기대하는 구체적인 행위를 나타냅니다.
  2. 해당 행위가 GitLab 도메인 언어에 속한 리소스 사이의 관계를 변경하는 경우. 예를 들어 transfer_project 나 move_issue는 리소스와 상위 네임스페이스 사이의 관계를 바꾸는 구체적인 행위를 나타냅니다.
  3. 해당 행위가 영향이 크거나 되돌릴 수 없으며 고유한 도메인 의미를 갖는 경우. 예를 들어 purge_maven_virtual_registry_cache는 되돌릴 수 없고 소프트웨어 업계 전반의 캐시 논의에서 확립된 의미를 지닌 purge 행위를 사용합니다.

리소스 명명 규칙#

권한 이름의 리소스(그리고 선택적 하위 리소스)는 항상 다음을 지켜야 합니다.

  1. 단수형을 사용합니다(예: read_projects가 아니라 read_project).
  2. 대상이 되는 도메인 객체와 일치시킵니다(예: Issue를 대상으로 행위를 평가한다면 권한 이름은 {action}_issue 형식이어야 합니다).
  3. 구현 세부 사항을 드러내는 대신 사용자에게 노출되는 도메인 용어를 사용합니다(예: 고객이 해당 리소스의 존재를 알 방법이 없다면 그 이름은 권한 이름에 들어가지 않는 편이 좋습니다).

권한 이름에서 리소스 경계 방지#

권한 이름에는 project, group, user 같은 리소스 경계를 직접 담지 않아야 합니다.

예를 들어 read_project_insights_dashboard와 read_group_insights_dashboard처럼 권한을 따로 도입하지 않습니다. 대신 read_insights_dashboard처럼 기능 자체를 설명하는 단일 시맨틱 권한을 정의합니다.

can? 검사에 주체를 전달하는 시점에 이미 범위가 결정되므로, 권한 이름에 project 나 group 같은 경계를 포함하는 것은 불필요합니다. 예를 들면 다음과 같습니다.

can?(:read_insights_dashboard, project)
can?(:read_insights_dashboard, group)

예외#

이 규칙을 따르지 않는 새 권한이 필요하다고 판단되면 Govern:Authorization 팀에 문의합니다. 논의는 언제나 환영합니다. 이 지침은 엔지니어의 작업을 복잡하게 만들기 위한 것이 아니라 더 수월하게 하기 위한 것입니다.

비공개 권한#

비공개 권한은 조건부이거나 미묘한 기능을 나타내는 좁은 범위의 권한입니다. 이름 앞에 밑줄(_)을 붙여 정책 로직 내부에서만 쓰이며 집행 지점(컨트롤러, 서비스, 파인더, GraphQL)에서 직접 검사해서는 안 된다는 점을 나타냅니다.

비공개 권한이 존재하는 이유#

GitLab RBAC 모델에서는 같은 행위라도 역할에 따라 다르게 동작할 수 있습니다. 예를 들면 다음과 같습니다.

  • Guest는 자신이 작성했거나 자신에게 할당된 기밀 이슈를 읽을 수 있습니다.
  • **Planner+**는 모든 기밀 이슈를 읽을 수 있습니다.

비공개 권한이 없으면 두 경우 모두 하나의 read_issue 권한으로 매핑되고 그 차이는 절차적 정책 로직 안에 묻힙니다. 이때 다음 문제가 생깁니다.

  • 권한 상승 위험: 사용자가 다른 사용자를 초대할 때 시스템은 초대되는 역할의 권한이 초대하는 사용자 자신의 권한을 넘지 않는지 확인해야 합니다. read_issue 같은 평면적 권한에서는 자신이 작성한 이슈만 읽을 수 있는 Guest와 모든 이슈를 읽을 수 있는 Owner가 동일하게 보입니다.
  • 커스텀 역할의 모호성: 평면적 권한으로 구성한 커스텀 역할은 "자신이 작성한 이슈만 읽기"를 표현할 수 없습니다. 커스텀 역할은 read_issue 전체를 받거나(너무 넓음) 아무것도 받지 못합니다(너무 제한적).

비공개 권한은 그 차이를 명시적이고 기계가 읽을 수 있는 형태로 만들어 이러한 문제를 해결합니다.

명명 규칙#

비공개 권한은 _<action>_<qualifier>_<resource> 패턴을 따르며, qualifier는 해당 권한이 적용되는 조건을 설명합니다.

권한 설명 일반적인 역할
_read_authored_issue 기밀 여부와 무관하게 자신이 작성한 이슈를 읽습니다 Guest+, Internal
_read_assigned_issue 기밀 여부와 무관하게 자신에게 할당된 이슈를 읽습니다 Guest+, Internal
_read_confidential_issue 모든 기밀 이슈를 읽습니다 Planner, Reporter+

밑줄 접두사는 다음과 같은 목적을 갖습니다.

  1. 코드와 YAML 정의에서 비공개 권한을 공개 권한과 시각적으로 구분합니다.
  2. 이 권한이 정책 파일 밖의 can? 검사에 나타나서는 안 된다는 점을 개발자와 도구에 알립니다.
  3. 린터가 부적절한 사용을 쉽게 표시할 수 있게 합니다.
  4. 설정보다 관례를 따릅니다. 권한 정의에 메타데이터를 저장하는 것보다 밑줄 문법이 더 낫습니다.

Qualifier 명명 규칙#

qualifier는 일반적으로 리소스의 속성이어야 합니다. 리소스 자체에 존재하는 속성을 기준으로, 해당 권한이 적용되는 리소스의 부분 집합을 설명합니다.

qualifier 지침은 다음과 같습니다.

  1. qualifier는 리소스의 실제 속성이나 관계에 대응해야 합니다. 행위자를 설명하는 qualifier는 사용하지 않습니다(예: admin_read_issue는 피합니다).
  2. 리소스에 대한 설명으로 자연스럽게 읽히는 과거 분사형이나 형용사형을 사용하는 것이 좋습니다(예: confidential, authored, assigned).
  3. qualifier는 같은 리소스에 대한 같은 행위가 리소스 상태에 따라 서로 다른 접근 수준을 요구할 때만 도입합니다. 해당 리소스의 모든 인스턴스가 같은 접근 수준을 요구한다면 qualifier는 불필요합니다.

정책에서 비공개 권한을 사용하는 방법#

비공개 권한은 게이트 역할을 합니다. 역할은 역할 YAML 정의를 통해 비공개 권한을 활성화합니다. 정책은 이 비공개 권한과 주체 수준 조건을 결합해 더 넓은 공개 권한을 활성화합니다.

# In the role YAML (e.g., config/authz/roles/guest.yml):
#   raw_permissions:
#     - _read_authored_issue
#     - _read_assigned_issue
#     - read_issue
#
# In the role YAML (e.g., config/authz/roles/reporter.yml):
#   raw_permissions:
#     - _read_authored_issue
#     - _read_assigned_issue
#     - _read_confidential_issue
#     - read_issue

# In the issue policy (e.g., app/policies/issue_policy.rb):
condition(:is_author) { @subject.author == @user }
condition(:is_assignee) { @subject.assignees.include?(@user) }
condition(:is_confidential) { @subject.confidential? }

rule { ~is_author }.prevent :_read_authored_issue
rule { ~is_assignee }.prevent :_read_assigned_issue

rule { can?(:_read_authored_issue) | can?(:_read_assigned_issue) }.policy do
  enable :read_issue
  enable :_read_confidential_issue
end

rule { is_confidential & ~can?(:_read_confidential_issue) }.prevent :read_issue

집행 지점(컨트롤러, 서비스, GraphQL)은 공개 권한만 검사합니다.

# Correct - check the public permission
authorize :read_issue

# Wrong - never check a private permission at an enforcement point
authorize :_read_authored_issue

비공개 권한을 사용해야 하는 경우#

다음과 같은 경우에 비공개 권한을 사용합니다.

  1. 주체 수준 조건이 충족될 때만 역할이 해당 행위를 수행할 수 있는 경우 (사용자가 작성, 사용자에게 할당, 사용자가 생성).
  2. 같은 행위에 대해 역할마다 접근 수준이 다른 경우(일부는 무조건, 일부는 조건부).
  3. 그 차이가 권한 상승 검사나 커스텀 역할 구성에 영향을 주는 경우.

다음 경우에는 비공개 권한을 사용하지 않습니다.

  • 기능 플래그나 라이선스 검사. 대신 prevent 규칙을 사용합니다.
  • 설정 기반 제한. 대신 prevent 규칙을 사용합니다.
  • 모든 역할에 동일하게 적용되는 조건. 단일 prevent 규칙을 사용합니다.

권한 정의 파일#

비공개 권한도 다른 권한과 마찬가지로 정의 파일이 필요합니다. 파일 이름은 권한 이름과 맞추기 위해 밑줄 접두사를 사용합니다.

config/authz/permissions/<resource>/_<action>_<qualifier>.yml

예를 들면 다음과 같습니다.

# config/authz/permissions/issue/_read_authored.yml
---
name: _read_authored_issue
description: Allows users to read issues they authored when they would not otherwise have access

conditionally_enables로 더 넓은 권한 선언#

모든 비공개 권한은 conditionally_enables 필드를 선언해야 합니다. 이 필드에는 해당 비공개 권한이 좁혀 주는 기능을 이미 부여하는 더 넓은 공개 권한을 하나 또는 여러 개 나열합니다. 역할 확장은 이 필드를 읽습니다. 나열된 권한을 모두 보유한 역할은 비공개 권한도 암묵적으로 보유하므로, 더 넓은 권한을 이미 보유한 역할에는 비공개 권한을 따로 나열하지 않습니다.

하나의 공개 권한이 비공개 권한을 포괄한다면 더 넓은 권한 하나만 사용합니다.

# config/authz/permissions/work_item/_read_authored.yml
---
name: _read_authored_work_item
description: Grants the ability to read work items that were authored by the user.
conditionally_enables: read_work_item

read_work_item을 부여하는 역할은 확장을 통해 _read_authored_work_item도 함께 부여합니다. read_work_item을 부여하지 않는 역할은 영향을 받지 않으므로, Guest는 역할 정의를 통해 _read_authored_work_item을 직접 받을 수 있습니다.

역할이 비공개 권한을 함의하려면 여러 권한을 모두 보유해야 하는 경우에는 그 권한들을 나열합니다.

conditionally_enables:
  - push_code
  - create_merge_request_from
  - create_merge_request_in

비공개 권한을 포괄하는 공개 권한이 없으면 null을 사용합니다. 이는 일반적으로 비공개 권한이 조건부로 활성화하는 것이 아니라 조건부로 차단할 때에 해당합니다.

# config/authz/permissions/job/_update_protected.yml
---
name: _update_protected_job
description: Private permission to enable/prevent job update and write abilities
conditionally_enables: null

비공개 권한이 conditionally_enables를 생략하면 검증 태스크가 실패합니다.