사용자 정의 역할
궁극 솔루션 고객은 사용자 정의 역할을 만들고 특정 기능을 할당함으로써 해당 역할을 정의할 수 있습니다.
예를 들어 사용자는 “엔지니어” 역할을 만들어 코드 읽기
및 병합 요청 관리자
권한을 부여할 수 있지만, 이슈 관리자
권한과 같은 권한은 부여하지 않을 수 있습니다.
이 맥락에서 “권한”과 “능력”이라는 용어가 종종 서로 교환되어 사용됩니다.
- “능력”은 사용자가 수행할 수 있는 작업입니다. 이는 선언적 정책 능력에 매핑되며
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
이면 사용자 정의 역할은 기본 게스트 역할로부터 받는 모든 능력과read_code
와 같은 추가 능력을 포함합니다. - 사용자 정의 역할은
base_access_level
에 대해 추가적인 능력을 활성화할 수는 있지만 권한을 비활성화할 수는 없습니다. 결과적으로 사용자 정의 역할은 “추가만 가능”합니다. 이 선택의 근거는 이 댓글에서 확인할 수 있습니다. - 사용자 정의 역할 능력은 프로젝트 수준 및 그룹 수준에서 지원됩니다.
사용자 정의 역할을 위한 새로운 능력 구현 방법
일반적으로 새로운 능력에 대해 2-3개의 병합 요청을 생성해야 합니다. 대략적인 지침은 다음과 같습니다:
- 능력을 추가하려는 기능을 선택합니다.
- 기능의 능력을 재구성하고 통합합니다 (기능의 복잡성에 따라 1-2개의 병합 요청이 필요함).
- 새로운 능력을 구현합니다 (1개의 병합 요청).
능력 재구성
기존 능력 확인하기
여러 위치에서 하나의 엔드포인트나 웹 요청에 대해 능력이 여러 번 확인됩니다. 따라서 특정 엔드포인트에 대해 실행되는 권한 확인 목록을 찾기 어려울 수 있습니다.
이를 위해 로컬에서 GITLAB_DEBUG_POLICIES=true
를 설정할 수 있습니다.
이 설정은 실행하는 특정 스펙에서 실행되는 요청에서 검사된 권한에 대한 정보를 출력합니다. 출력에는 권한 확인이 이루어진 코드 행도 포함되어 있습니다. 호출자 정보는 특히 메타 프로그래밍이 사용되는 경우 권한 이름 문자열을 그립하여 찾기 어려운 경우에 도움이 됩니다.
예:
# 예시 스펙 실행
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
옵션에 정의되어 있습니다.
추가적인 능력이 필요한 기능이 있을 수 있지만 최소화하려고 노력하십시오. 언제든지 인증 및 승인 그룹의 구성원들에게 의견 또는 도움을 요청할 수 있습니다.
이것은 여러 GroupPolicy
및 ProjectPolicy
클래스의 많은 중복 정책을 가지고 있습니다. Policy 클래스를 통합하는 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>
을 실행하여 새로운 능력에 대한 구성 파일을 생성합니다. - 이렇게 하면 새로운 능력을 위한 YAML 파일이
ee/config/custom_abilities
에 생성되며, 다음 스키마를 따릅니다:
필드 | 필수 여부 | 설명 |
---|---|---|
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 "Custom role on group that enables 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 "Custom role on project that enables 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
로 이동합니다. -
새 역할 추가
를 클릭하고 방금 만든 사용자 정의 역할을 만듭니다. - 그룹의
관리 -> 구성원
페이지로 이동하여 새로 만든 사용자 정의 역할에 구성원을 할당합니다. - 다음으로 해당 구성원으로 로그인하여 방금 만든 사용자 정의 능력이 의도한 페이지에 액세스할 수 있는지 확인합니다.
단계 5: 테스트 추가
-
ee/spec/factories/member_roles.rb
에서MemberRoles
팩토리에 능력을 특성으로 추가합니다. -
ee/spec/requests/custom_roles/<ABILITY_NAME>/request_spec.rb
에 테스트를 추가하여 사용자에게 사용자 정의 능력이 할당된 후 컨트롤러, REST API 엔드포인트 및 GraphQL API 엔드포인트에 성공적으로 액세스할 수 있는지 확인합니다. - 아래는 Rails Controller 엔드포인트를 테스트하기 위해 필요한 일반적인 설정의 예입니다.
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 문서를 업데이트합니다.
권한 상승 고려사항
기본 역할은 일반적으로 해당 artifact와 상호 작용할 때 해당 artifact에 해당하는 artifact를 생성하거나 관리할 수 있는 권한을 갖고 있습니다. 예를 들어, 개발자
가 프로젝트에 대한 액세스 토큰을 생성할 때, 해당 자격 증명에 개발자
액세스가 인코딩되어 생성됩니다. 새로운 사용자 정의 권한이 생성됨에 따라 GitLab artifact와 상호 작용할 때 권한이 상승할 위험이 있으며, 적절한 보호 조치나 기본 역할 확인을 추가해야 합니다.
라이센스 소모
게스트
역할을 갖는 새로운 사용자가 CUSTOMIZABLE_PERMISSIONS_EXEMPT_FROM_CONSUMING_SEAT
배열에 없는 능력을 활성화하는 회원 역할에 추가되면 라이센스가 소비됩니다. 우리는 단순히 “규정에 따라 요금이 부과되는 유저가 Ultimate 고객에 의해 고객님이 만든 능력을 가진 게스트 사용자에 대해 요금을 부과하고자” 하는 것입니다. 이는 SaaS의 요금이 청구되는 사용자에게만 적용됩니다(네임스페이스 구독에 포함된 사용자에 대해 청구되는 사용자). 이 주제에 대한 자세한 내용은 이 이슈에서 확인할 수 있습니다.