사용자 정의 역할
Ultimate 고객은 사용자 정의 역할을 생성하고 특정 능력을 할당하여 그 역할을 정의할 수 있습니다.
예를 들어 사용자는 “엔지니어” 역할을 생성하여 코드 읽기
및 Merge Request 관리자
능력을 부여할 수 있지만 이슈 관리자
와 같은 능력은 부여하지 않을 수 있습니다.
이 문맥에서 “권한”과 “능력”이라는 용어는 종종 서로 교환하여 사용됩니다.
- “능력”은 사용자가 할 수 있는 작업입니다. 이것은 선언적 정책 능력에 매핑되며
ee/app/policies/*
의 정책 클래스에서 사용됩니다. - “권한”은 사용자 대면 문서에서 능력을 참조하는 방식입니다. 권한 문서는 매뉴얼으로 생성되므로 문서에 기재된 권한과 정책 클래스에서 정의된 능력 간에 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_level
이10
이면, 사용자 정의 역할은 기본 Guest 역할이 받을 수 있는 모든 능력과 어떠한 추가적인 능력도 포함하게 됩니다. - 사용자 정의 역할은
base_access_level
에 대해 추가적인 능력을 활성화할 수 있지만 권한을 비활성화할 수는 없습니다. 결과적으로 사용자 정의 역할은 “추가적인 능력만”을 갖습니다. 이 선택의 근거는 이 댓글에 있습니다. - 사용자 정의 역할 능력은 프로젝트 수준과 그룹 수준에서 지원됩니다.
사용자 정의 역할을 위한 새로운 능력을 구현하는 방법
일반적으로 새로운 능력을 위해 2-3개의 Merge Request이 작성되어야 합니다. 대략적인 지침은 다음과 같습니다:
- 능력을 추가하고자 하는 기능을 선택합니다.
- 기능에 대한 능력을 재구성하고 통합합니다(기능 복잡도에 따라 1~2개의 Merge Request이 필요함).
- 새로운 능력을 구현합니다(1개의 Merge Request).
능력 재구성
기존 능력 확인
한 엔드포인트나 웹 요청에 대해 능력이 종종 여러 위치에서 확인됩니다. 따라서 주어진 엔드포인트에 대해 실행되는 권한 확인 디렉터리을 찾기 어려울 수 있습니다.
이를 돕기 위해 로컬에서 GITLAB_DEBUG_POLICIES=true
를 설정할 수 있습니다.
이렇게 하면 실행하는 모든 스펙에서 권한 확인에 대한 정보가 출력됩니다. 출력에는 권한 확인이 수행된 코드의 줄도 포함됩니다. Caller 정보는 특히 메타프로그래밍을 사용하는 경우 뉴포인트 이름 문자열을 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!'"]
이 설정을 사용하여 재구성하는 동안 권한 확인에 대해 더 알아보세요. 기본 브랜치의 스펙에 대해서는 이 설정을 사용하지 말아야 합니다.
개별 능력의 논리 이해
특정 능력에 대한 참조는 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_
, 또는 필요한 경우 추가 능력으로 통합해야 합니다.
GroupPolicy
및 ProjectPolicy
클래스에 있는 많은 능력은 많은 중복 정책을 갖고 있습니다. 이러한 정책 클래스를 통합하기 위한 epic이 있는 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
| 예 | 사용자 정의 능력을 설명하는 고유하고 소문자 및 밑줄로 구성된 이름. 파일 이름과 일치해야 합니다. |
description
| 예 | 사용자 정의 능력에 대한 설명 |
feature_category
| 예 | 기능 카테고리의 이름. 예를 들어, vulnerability_management .
|
introduced_by_issue
| 예 | 이 사용자 정의 능력 추가를 제안한 이슈 URL |
introduced_by_mr
| 예 | 해당 사용자 정의 능력이 추가된 MR URL |
milestone
| 예 | 이 사용자 정의 능력이 추가된 마일스톤 |
group_ability
| 예 | 이 능력이 그룹 수준에서 확인되는지에 대한 부울 값 |
project_ability
| 예 | 이 능력이 프로젝트 수준에서 확인되는지에 대한 부울 값 |
requirements
| 아니요 | 이 능력이 종속된 사용자 정의 권한 디렉터리. 예를 들어 admin_vulnerability 는 read_vulnerability 에 종속됩니다. 없는 경우 [] 를 입력하세요.
|
available_from_access_level
| 아니요 | 이 능력이 사용 가능한 기본 액세스 수준이 있는 경우에 해당합니다. 능력의 개별 로직에 대한 도움말은 개별 능력의 로직 이해 섹션을 참조하세요. |
단계 2: 마이그레이션 파일 생성
-
bundle exec rails generate gitlab:custom_roles:code --ability <ABILITY_NAME>
을 실행하여member_roles
테이블에 능력을 부울 열로 추가하는 마이그레이션 파일을 생성합니다.
단계 3: 정책 업데이트
- 능력이 그룹 수준에서 확인되는 경우, 능력을 활성화하기 위해 GroupPolicy에 규칙을 추가하세요.
- 예를 들어: 추가하려는 능력이
read_dependency
인 경우,ee/app/policies/ee/group_policy.rb
를 업데이트하는 것은 다음과 같습니다.
desc "사용자 정의 역할이 `read_dependency`를 가능하게 하는 그룹 룰"
condition(:role_enables_read_dependency) do
::Auth::MemberRoleAbilityLoader.new(
user: @user,
resource: @subject,
ability: :read_dependency
).has_ability?
end
rule { custom_roles_allowed & role_enables_read_dependency }.policy do
enable :read_dependency
end
- 마찬가지로, 능력이 프로젝트 수준에서 확인되는 경우, ProjectPolicy에 규칙을 추가하세요.
- 예를 들어: 추가하려는 능력이
read_dependency
인 경우,ee/app/policies/ee/project_policy.rb
를 업데이트하는 것은 다음과 같습니다.
desc "사용자 정의 역할이 `read_dependency`를 가능하게 하는 프로젝트 룰"
condition(:role_enables_read_dependency) do
::Auth::MemberRoleAbilityLoader.new(
user: @user,
resource: @subject,
ability: :read_dependency
).has_ability?
end
rule { custom_roles_allowed & role_enables_read_dependency }.policy do
enable :read_dependency
end
- 모든 능력이 두 수준에서 모두 활성화될 필요는 없습니다. 예를 들어
admin_terraform_state
는 사용자가 프로젝트의 지형 상태를 관리할 수 있도록 합니다. 이것은 그룹 수준에서가 아닌 프로젝트 수준에서만 활성화되어야 하므로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: 문서 업데이트
-
bundle exec rake gitlab:custom_roles:compile_docs
를 실행하여 사용자 정의 권한 디렉터리을 업데이트합니다. -
bundle exec rake gitlab:graphql:compile_docs
를 실행하여 GraphQL 문서를 업데이트합니다.
권한 상승 고려 사항
기본 역할은 일반적으로 해당 아티팩트와 상호 작용할 때 해당 아티팩트에 해당하는 아티팩트를 생성하거나 관리할 수 있는 권한을 가지고 있습니다. 예를 들어, ‘개발자’가 프로젝트용 액세스 토큰을 생성하는 경우 해당 자격 증명에 ‘개발자’ 액세스가 부여됩니다. 새로운 사용자 지정 권한이 생성될 때 GitLab 아티팩트와 상호 작용할 때 특히 권한이 상승할 위험이 있으므로 적절한 안전장치나 기본 역할 확인을 추가해야 합니다.
좌석 사용
Guest
역할을 가진 새로운 사용자가 CUSTOMIZABLE_PERMISSIONS_EXEMPT_FROM_CONSUMING_SEAT
배열에 없는 능력을 활성화하는 회원 역할에 추가되면 좌석이 소비됩니다. “고급” 능력을 가진 게스트 사용자에게 결제를 적용하기 위해 우리는 궁극적으로 고객에 대한 요금을 청구하려고 합니다. 이 기능은 SaaS의 요금 과금 사용자에만 적용됩니다(네임스페이스 구독에 포함된 과금 사용자). 이 주제에 대한 자세한 내용은 이 이슈에서 찾을 수 있습니다.