인증 코드 리뷰 가이드라인
GitLab v19.5요약
이 페이지는 정책 변경, 권한 정의, 인가 로직이 포함된 머지 리퀘스트를 리뷰용으로 준비하는 방법에 관한 Govern:Authorization 팀의 지침을 제공합니다. 권한 확인을 추가, 제거 또는 변경하는 모든 변경에는 확인이 어디에 있든 이 가이드라인을 적용합니다.
이 페이지는 정책 변경, 권한 정의, 인가 로직이 포함된 머지 리퀘스트를 리뷰용으로 준비하는 방법에 관한 Govern:Authorization 팀의 지침을 제공합니다.
권한 확인을 추가, 제거 또는 변경하는 모든 변경에는 확인이 어디에 있든 이 가이드라인을 적용합니다.
권한 확인에는 can?, Ability.allowed?, authorize!, GraphQL authorize, route_setting :authorization이 포함됩니다.
이러한 확인은 정책, 컨트롤러, 서비스, 파인더, 모델, 헬퍼, 워커, 뷰, REST 또는 GraphQL 코드에 있을 수 있습니다.
리뷰 체크리스트#
인가를 다루는 MR을 리뷰하거나 준비할 때 이 목록을 사용합니다. 각 항목은 자세한 내용이 있는 절로 연결됩니다.
- 권한 이름이 규칙을 따르고, 작업에 맞으며, 중복되지 않습니다.
- 역할 부여는 역할 YAML에 있고, 해당 권한을 가져야 하는 모든 역할이 여전히 권한을 받습니다.
- 정책 규칙은
prevent로만 제한하고(비공개 권한 패턴 제외), 액세스 수준을 조회하지 않으며, 다른 권한에서 권한을 도출하지 않고, 다른 리소스를 근거로 액세스를 부여하지 않습니다. - 조건 이름은 조건이 확인하는 내용을 설명하며,
can?(:read_project)를 멤버십 확인으로 사용하지 않습니다. - 사용자 지정 역할은 연쇄(한 권한이 다른 권한을 활성화하는 것), 이름 또는 권한이 제거되거나 변경될 때 확인합니다.
- 적용은 예외에 해당하지 않는 한 서비스와 파인더를 포함한 모든 진입점에서 정책을 사용합니다.
- 세분화된 토큰은 모든 새 엔드포인트에 대해 선언하거나 명시적으로 제외합니다.
- GraphQL 인가는 가능하면 개별 필드가 아니라 타입에 둡니다.
- 스펙은 스텁 대신 실제 멤버십과 설정으로 모든 정책 변경을 다룹니다.
Gitlab/Authzcop은 비활성화하지 않습니다. 불가피한 비활성화는 Authorization 팀의 승인을 받습니다.
역할 YAML 파일이 정본#
역할 정의 YAML 파일(config/authz/roles/*.yml)은 각 역할이 어떤 권한을
가지는지에 대한 단일 진실 공급원(Single Source Of Truth, SSOT)입니다. 정책 파일에는 역할 조건을
기준으로 권한을 부여하는 enable 규칙을 두지 않습니다. 대신 해당 역할 YAML 파일에 권한을 추가하고,
기능이나 설정을 사용할 수 없을 때 접근을 제한하려면 정책에서 prevent 규칙을
사용합니다.
# bad - enabling a permission for a role in a policy file
rule { developer & model_registry_enabled }.policy do
enable :write_model_registry
end
# good - permission is in the developer role YAML
# (config/authz/roles/developer.yml)
# raw_permissions:
# - write_model_registry
#
# Policy only restricts when the feature is unavailable:
rule { ~model_registry_enabled }.prevent :write_model_registry
이 패턴은 역할 권한을 다음과 같이 만듭니다.
- 기계 판독 가능: GATE와 같은 외부 시스템이 정책 로직을 평가하지 않고도 역할의 권한을 판별할 수 있습니다.
- 열거 가능: YAML 파일 하나만 읽으면 역할이 가진 모든 권한을 확인할 수 있습니다.
- 예측 가능: 역할은 항상 전체 권한 집합에서 시작합니다. 조건은 접근을 제거하기만 하고 확장하지 않습니다.
파일 구성#
같은 조건에 대한 prevent 규칙은 파일 곳곳에 흩어 두지 않고 하나의 .policy 블록에
모읍니다.
# bad - prevents for the same condition scattered across the file
rule { ~security_dashboard_enabled }.prevent :read_vulnerability
rule { ~security_dashboard_enabled }.prevent :admin_vulnerability
# good - all prevents for the same condition grouped together
rule { ~security_dashboard_enabled }.policy do
prevent :admin_vulnerability
prevent :read_vulnerability
end
권한 설계#
이름 짓기는 리뷰에서 가장 자주 다뤄지는 주제입니다. 권한 이름 규칙을 따르고, 먼저 다음 사항을 확인합니다.
권한을 작업에 맞추기#
읽기 작업에는 읽기 권한을, 변경 작업에는 변경 권한을 사용합니다.
데이터를 읽기만 하는 필드나 엔드포인트에서 start_나 update_처럼 상태 변경을 의미하는 작업의 권한을 확인하지 않습니다.
쓰기를 읽기 권한으로 보호하지 않습니다.
접근 방식이나 위치를 이름에 담지 않기#
확인에 전달되는 주체가 접근 경로와 경계를 담고 있으므로, 이름에 접근 경로나 경계를 넣지 않습니다.
예를 들어 read_widget_via_parent_group은 read_widget이어야 합니다.
권한 이름에서 리소스 경계 피하기를 참고합니다.
추가하기 전에 재사용#
권한을 추가하기 전에 기존 권한이 있는지 검색하고, 하나의 권한을 서로 다른 의미로 재사용하지 않습니다. 새 권한 도입을 참고합니다.
권한이 제어하는 대상 설명#
설명에는 권한이 무엇을 제어하는지 적습니다. 역할은 바뀔 수 있으므로 어떤 역할이 이 권한을 가지는지는 언급하지 않습니다.
정의 파일 완성#
모든 새 권한에 대해 완전한 권한 정의 파일을 생성합니다.
기존 할 일 목록인 config/authz/permissions/definitions_todo.txt와 config/authz/graphql/authorization_todo.txt에는 항목을 추가하지 않습니다.
역할 YAML 변경#
역할 YAML 파일에서 권한을 옮기거나 추가할 때, 해당 권한을 가져야 하는 모든 역할의 동작은 그대로 유지되어야 합니다.
- 권한이
group과project모두에 적용되면 두 섹션을 모두 업데이트합니다. - 권한이 이전에 넓은 범위의 읽기 권한을 통해 활성화되었다면
public_anonymous와guest를 확인합니다. - 새
read_*권한은 보통auditor역할에 속합니다. 프로젝트나 그룹 데이터를 변경하는 권한은 추가하지 않습니다. - 보관됨 및 삭제 보류 목록(
config/authz/permission_groups/internal/)이나 기능 사용 가능 여부 규칙처럼 관련 권한이 이미prevent로 제한되는 곳에 새 권한도 추가합니다. 그렇지 않으면 새 권한이 이러한 제한을 우회합니다. - 역할이
inherits_from을 통해 이미 가지는 권한을 다시 추가하지 않습니다. - 완전히 새로운 권한은 할당 가능한 권한 그룹을 통해 추가합니다.
raw_permissions는 기존 권한을 옮길 때만 사용합니다. 기존 역할 수정을 참고합니다.
안티패턴#
기본 정책에서 권한 활성화 금지#
BasePolicy는 다른 모든 정책이 상속하므로, 여기서 활성화한 권한은 시스템의 모든 객체에서 암묵적으로 사용할 수 있게 됩니다. 권한이 어떤 리소스에 대해 인가되는지 제약이 없으므로 모호함과 보안 위험이 생깁니다.
동적 권한 정의 지양#
동적으로 정의된 권한은 코드베이스에서 추적하기 어렵습니다. 권한을 명시적으로 선언하지 않고 런타임에 생성하면 권한 이름으로 검색해도 결과가 나오지 않으므로, 이름 변경이나 제거가 완료되었는지 확인할 수 없습니다.
# bad - permission name is constructed dynamically; cannot be searched,
# might enable/prevent permissions that are not actually used anywhere.
readonly_features.each do |feature|
prevent :"create_#{feature}"
prevent :"update_#{feature}"
prevent :"admin_#{feature}"
end
# good - each prevention declared explicitly
rule { read_only }.policy do
prevent :create_issue
prevent :update_issue
prevent :admin_issue
# ... one line per permission
end
예외: 모든 이름이 YAML에 선언되므로 역할 또는 권한 그룹 YAML에서 권한을 불러오는 방식은 의도된 패턴입니다.
예를 들어 Authz::Role.get(:developer).permissions(:project) 또는
Authz::PermissionGroups::Internal.get('project:archived').permissions가 있습니다.
조건에서 잘못된 :scope 사용 지양#
모든 condition은 캐시됩니다. :scope 옵션은 캐시 키가 무엇인지 DeclarativePolicy에
알려 줍니다. 이 값을 잘못 설정하면 캐시된 결과가 지나치게 넓게 공유되어 한 사용자의 결과가
다른 컨텍스트로 새어 들어가는 버그가 발생합니다.
규칙은 다음과 같습니다.
- 조건이 사용자 데이터만 읽고 주체 데이터를 읽지 않을 때만
scope: :user를 사용합니다. - 조건이 주체 데이터만 읽고 사용자 데이터를 읽지 않을 때만
scope: :subject를 사용합니다. - 조건에 사용자 데이터도 주체 데이터도 필요하지 않을 때만
scope: :global을 사용합니다. - 조건이 사용자 데이터와 주체 데이터를 모두 읽으면
:scope를 생략합니다(기본값).
참고: DeclarativePolicy 캐시 공유 스코프
# bad - scope: :user means the result is cached per-user and shared across all
# subjects, but the condition reads from @subject, so different projects will
# get the same cached result incorrectly
condition(:security_dashboard_enabled, scope: :user) do
@subject.security_dashboard_enabled?
end
# good - reads subject data only, so scope: :subject is correct
condition(:security_dashboard_enabled, scope: :subject) do
@subject.security_dashboard_enabled?
end
# good - reads user data only, so scope: :user is correct
condition(:admin_user, scope: :user) do
@user.admin?
end
# good - reads both user and subject, so no scope is declared
condition(:member_with_access) do
@subject.member?(@user)
end
# good - doesn't need either user or subject, so scope: :global is correct
condition(:default_project_deletion_protection, scope: :global) do
::Gitlab::CurrentSettings.current_application_settings
.default_project_deletion_protection
end
수정 예시: MR !224604
중간 ability를 통한 권한 연쇄 지양#
read_security_resource가 read_vulnerability를 활성화하는 식으로 중간 ability를 거쳐
권한을 연결하지 않습니다. 연쇄가 생기면 여러 단계의 간접 참조를 따라가지 않고서는 어떤 역할이
어떤 권한을 가지는지 파악하기
어렵습니다.
대신 각 권한을 해당 역할 YAML 파일에 직접 추가합니다.
# bad - an intermediate ability fans out to other permissions
rule { can?(:read_security_resource) }.enable :read_vulnerability
rule { can?(:read_security_resource) }.enable :read_security_dashboard
# good - each permission is in the role YAML directly
# (config/authz/roles/developer.yml)
# raw_permissions:
# - read_vulnerability
# - read_security_dashboard
예외: 비공개 권한#
비공개 권한(밑줄로 시작하는 권한)은 중간 ability를 거친
연쇄가 올바른 유일한 사례입니다.
_read_authored_issue와 같은 비공개 권한은 역할 YAML 정의에서 역할에 할당된 뒤,
정책에서 주체 수준 조건과 결합되어 더 넓은 공개 권한을 활성화합니다. 이 패턴이
의도된 이유는
다음과 같습니다.
- 비공개 권한은 역할의 조건부 역량을 명시적이고 기계 판독 가능하게 만들며, 이는 권한 상승 검사와 사용자 지정 역할 구성에 필요합니다.
- 연쇄는 항상 정확히 한 단계입니다. 비공개 권한과 조건이 결합해 공개 권한을 활성화합니다. 더 깊은 연쇄는 여전히 허용되지 않습니다.
# good - private permission gates the broader permission with a condition
rule { can?(:_read_authored_issue) & is_author }.enable :read_issue
rule { can?(:_read_assigned_issue) & is_assignee }.enable :read_issue
# bad - cascading through a non-private intermediate ability
rule { can?(:read_security_resource) }.enable :read_vulnerability
다른 권한에서 권한 도출 금지#
권한들을 조합해 세 번째 권한을 활성화하는 조건은 연쇄의 한 형태입니다. 둘 중 하나가 나중에 더 넓게 부여되면 도출된 권한도 조용히 넓어집니다. 권한은 역할 YAML에서 부여하고 그 권한 하나만 확인합니다. 주체 자체의 정책이 역할 YAML 부여를 받지 않는 경우에는 데이터를 소유한 리소스에서 같은 권한을 확인합니다. 소유 리소스에서 같은 권한을 확인하는 것은 앞 절에서 경고한 연쇄가 아닙니다.
# bad - two read permissions combined to grant a third
condition(:can_read_widget_report) do
can?(:read_widget, @subject.group) || can?(:read_widget_settings, @subject.group)
end
rule { can_read_widget_report }.enable :read_widget_report
# good - the same permission, granted in role YAML and checked on the
# resource that owns the data (the instance when there is no group)
condition(:can_read_widget_report) do
can?(:read_widget_report, @subject.group || :global)
end
rule { can_read_widget_report }.enable :read_widget_report
중첩 조건 지양#
역할 확인과 설정·플래그 확인을 &로 묶어 하나의 rule로
만들지 않습니다. 대신
역할 YAML 파일에 권한을 추가하고, 조건이 충족되지 않을 때
이를 제한하도록 prevent를 사용하는 별도의 rule을 둡니다.
참고
# bad - mixes role and settings check in a single rule
rule { developer & model_registry_enabled }.policy do
enable :write_model_registry
end
# good - permission is in the developer role YAML, policy only prevents
rule { ~model_registry_enabled }.prevent :write_model_registry
admin | owner 규칙 지양#
admin 사용자는 condition(:owner)에서 true를 반환하므로 admin | owner 규칙을
따로 정의할 필요가 없습니다. 조직 소유자도 마찬가지입니다. 권한은 정책에서
활성화하지 말고 역할 YAML 파일에
두어야 합니다.
# bad - redundant admin/org owner check, and enabling in policy
rule { admin | organization_owner | owner }.enable :delete_project
# good - permission is in the owner role YAML
# (config/authz/roles/owner.yml)
# raw_permissions:
# - delete_project
액세스 수준 조회로 권한 부여 금지#
max_member_access_for_user, team.member?(@user, Gitlab::Access::DEVELOPER), access_level >= 비교, developer와 같은 단독 역할 조건, can?(:developer_access)로 액세스를 부여하거나 확인하지 않습니다.
대신 역할 YAML을 통해 권한을 부여합니다.
Planner와 Security Manager(config/authz/roles/planner.yml 및 security_manager.yml 참고)처럼 계층적이지 않은 역할 때문에 >= 비교는 잘못된 결과를 냅니다.
액세스 수준 확인은 권한이 나중에 다른 역할로 옮겨질 때도 조용히 깨집니다.
Gitlab/Authz/RoleCheckInRule cop이 규칙 안의 역할 확인을 표시합니다.
# bad - access-level lookup decides who gets the permission
condition(:developer_or_above) { @subject.team.member?(@user, Gitlab::Access::DEVELOPER) }
rule { developer_or_above }.enable :create_widget
# good - net-new permission is added through an assignable permission
# in config/authz/roles/developer.yml
# permissions:
# - create_widget
다른 리소스의 멤버십으로 액세스 부여 금지#
형제 또는 하위 프로젝트의 역할이 그룹 리소스나 다른 프로젝트에 대한 액세스를 부여해서는 안 됩니다. 예를 들어 두 사용자가 모두 프로젝트 A의 Guest이고 그중 한 명은 프로젝트 B의 Developer이기도 합니다. 그룹 내 어디에서든 Developer 권한이 해당 권한을 부여한다면, 두 번째 사용자는 프로젝트 A에서 작업할 수 있고 첫 번째 사용자는 이유가 드러나지 않은 채 할 수 없습니다. 고객이 임시 프로젝트를 만들어 그곳에서 모든 사람을 Developer로 지정하면 역할 기반 경계가 사라질 수도 있습니다.
작업이 일어나는 리소스를 기준으로 인가합니다.
# bad - a role in any other project grants access here
rule { developer_in_any_descendant_project }.enable :run_widget
# good - check the permission on the project where the action happens
authorize! :run_widget, project
허용되는 유일한 예외는 descendant_project_member 역할(config/authz/roles/descendant_project_member.yml)입니다.
이 역할은 하위 프로젝트의 구성원이 해당 프로젝트에서 작업할 수 있도록 기본적인 그룹 액세스(read_group, read_boundary, upload_file, update_work_item_user_preference)만 부여합니다.
정책에서 user.highest_role을 사용하지 않습니다.
이는 주체로 한정되지 않으므로 인스턴스 어디에서든 사용자가 가진 가장 높은 역할을 반환합니다.
Gitlab/Authz/AvoidHighestRoleUsage cop이 이를 표시합니다.
정책 파일에서 Ability.allowed? 또는 user.can? 호출 금지#
둘 다 Ability를 거쳐 다시 돌아오므로 순환 조회가 생길 수 있고 정책을 이해하기 어렵게 만듭니다.
대신 정책 안에서 DeclarativePolicy의 단독 can? 헬퍼를 사용합니다.
Gitlab/Authz/DisallowAbilityAllowed cop은 Ability.allowed?와 @user.can?처럼 수신자에서 호출한 can?을 표시합니다.
권한 연쇄에 대한 규칙은 여전히 적용됩니다.
# bad
condition(:can_read_project) { Ability.allowed?(@user, :read_project, @subject.project) }
# good
condition(:can_read_project) { can?(:read_project, @subject.project) }
인스턴스 관리자를 암묵적 부여로 취급 금지#
user.admin? 단축 방식은 관리자 모드 요구와 CI 작업 토큰 거부 등 정책의 admin 조건(app/policies/base_policy.rb)에 있는 확인을 건너뜁니다.
대신 정책을 통해 권한을 확인하고, 관리자와 조직 Owner를 어떻게 다룰지는 역할 정의가 결정하도록 합니다.
# bad
condition(:admin_or_owner) { @user.admin? || @subject.owner?(@user) }
# good - no admin shortcut. Role definitions and the base policy's `admin`
# condition, which requires admin mode, decide how admins are treated.
condition(:owner_of_widget) { @subject.owner?(@user) }
조건 이름은 확인하는 내용을 나타내도록 짓기#
조건 이름은 그 조건이 확인하는 내용을 설명해야 합니다.
읽기 권한만 확인하는 can_manage_*라는 조건은 읽는 사람이 실제보다 더 엄격하다고 오해하게 만듭니다.
"owner"는 Owner 역할 외의 용도로 사용하지 않습니다.
정책에서 "이 데이터를 소유한 그룹"을 뜻하는 group_owner나 boundary: :owner는 "사용자가 그룹 Owner이다"로 읽힙니다.
root_namespace처럼 리소스를 직접 지칭하는 이름을 사용합니다.
can?(:read_project)를 멤버십 확인으로 사용 금지#
공개 프로젝트에서 read_project는 config/authz/roles/의 public_anonymous 및 public_authenticated 역할 정의를 통해 익명 사용자를 포함한 모든 사용자에게 부여됩니다.
멤버십 확인을 can?(:read_project)로 바꾸면, 봇이 속한 프로젝트에만 적용되어야 하는 봇 조건처럼 멤버십에 의존하던 모든 규칙이 조용히 넓어집니다.
가시성, 외부 사용자, 봇과 같은 다른 상태와 실제 멤버십을 함께 확인해야 하는 규칙에는 user_is_user? && project.member?(@user)를 사용합니다.
user_is_user?(BasePolicy에 있음)는 배포 토큰과 배포 키가 ID로 일치하지 않도록 합니다.
멤버십을 다른 상태와 결합할 때만 이 방식을 사용합니다.
역할의 권한 부여는 액세스 수준 조회로 권한 부여 금지에서 설명한 대로 여전히 역할 YAML에 속합니다.
# bad - true for every user on a public project
condition(:widget_bot_in_project) { can?(:read_project, @subject) }
# good - real membership, and not a deploy token or deploy key
condition(:widget_bot_in_project) { user_is_user? && @subject.member?(@user) }
조건은 가볍고 부작용 없게 유지#
가벼운 확인을 먼저 두고 쿼리는 마지막에 실행합니다.
정책은 익명 사용자에 대해서도 평가되므로 nil 사용자를 방어합니다.
조건은 술어 메서드가 되어 이름이 ??로 끝나게 되므로 조건 이름을 ?로 끝내지 않습니다.
점수와 순서에 대해서는 점수, 순서, 성능을 참고합니다.
# bad - queries before the cheap attribute check, and no nil guard
condition(:can_use_widgets) do
@subject.members.exists?(user_id: @user.id) && @subject.widgets_enabled?
end
# good - cheap checks first, nil user guarded
condition(:can_use_widgets) do
@user.present? && @subject.widgets_enabled? && @subject.members.exists?(user_id: @user.id)
end
예시#
역할 YAML과 prevent를 사용하도록 결합 조건 리팩터링#
# bad - permission only enabled when all conditions are true, meaning the role's
# access grows based on feature flags and other conditions. Authorization logic
# should only remove access, never expand it.
rule { can?(:developer_access) & user_confirmed }.policy do
enable :create_pipeline
end
rule { ai_flow_triggers_enabled & (amazon_q_enabled | duo_workflow_available) & can?(:developer_access) & can?(:create_pipeline) }.policy do
enable :trigger_ai_flow
end
# good - trigger_ai_flow is in the developer role YAML.
# (config/authz/roles/developer.yml)
# raw_permissions:
# - trigger_ai_flow
#
# Each condition independently prevents it when not satisfied,
# so the role's base permissions are always enumerable.
rule { ~user_confirmed }.prevent :trigger_ai_flow
rule { ~ai_flow_triggers_enabled }.prevent :trigger_ai_flow
rule { ~amazon_q_enabled & ~duo_workflow_available }.prevent :trigger_ai_flow
사용자 지정 역할#
권한이 다른 권한을 활성화해서는 안 됩니다. 중간 ability를 통한 권한 연쇄 지양을 참고합니다.
- 기존 연쇄를 변경하거나 제거할 때
ee/config/custom_abilities/*.yml을 확인합니다. 사용자 지정 ability는 최상위 권한만 나열하고 나머지는 연쇄에 의존하는 경우가 많습니다. 연쇄를 제거하면 사용자 지정 역할 사용자의 액세스가 조용히 사라집니다. - 머지 리퀘스트의 동작 변경 노트에 기본 역할과 함께 사용자 지정 역할도 나열합니다.
- 권한 이름을 바꾸거나 제거할 때 해당 권한을 참조하는 사용자 지정 ability와 기존 사용자 지정 역할을 확인합니다. 사용자 지정 역할 ability는 데이터베이스에 저장되므로 이름 변경은 호환성을 깨는 변경입니다.
- 저장된 토큰 스코프는 할당 가능한 권한 이름 변경을 따릅니다.
- 역할이 기본 역할을 넘어 동작할 수 있게 하는 새 권한은 권한 상승 고려 사항을 참고합니다.
적용 위치#
- 결정은 정책에 둡니다. 복사본이 어긋나 정책을 우회할 수 있으므로 컨트롤러, 헬퍼, 뷰에서 다시 구현하지 않습니다.
- 서비스와 파인더에서도 적용하고, REST, GraphQL, 컨트롤러 전반에서 같은 권한을 사용합니다. 어디에서 확인해야 하는지에 대한 지침은 권한은 어디에서 확인해야 하는가?를 참고합니다.
- 봇, 서비스 계정, 내부 토큰에는 작업을 수행할 수 있는 가장 좁은 권한을 부여합니다.
- 사용자나 봇이 하는 요청에는 사용자 지정 역할을 포함한 정책 확인을 우회하므로
skip_authorization: true를 전달하지 않습니다. 프로비저닝과 같은 시스템 주도 흐름에만 사용하고, 사용자 입력에서 도출하지 않습니다. - 리소스의 존재를 노출하지 않습니다. 구별 가능한 403 및 404 응답이나 구체적인 오류 메시지는 호출자가 리소스와 구성원을 열거할 수 있게 합니다.
- 다른 모든 액션에 자체 확인이 있지 않은 한 기존
before_action을only:로 좁히지 않습니다. - 편집 가능한 UI는
read_*가 아니라update_*권한으로 제어합니다. - 탐색 또는 사이드바 항목은 연결된 페이지와 같은 권한으로 제어합니다.
세분화된 토큰 인가#
- 모든 새 엔드포인트는 세분화된 토큰 인가를 선언하거나 정당한 사유가 있는 명시적 제외를 선언합니다.
REST는 세분화된 토큰 인가 건너뛰기를 참고합니다.
GraphQL은
skip_reason으로 인가 건너뛰기를 참고합니다. - 토큰 확인은 요청 경로의 리소스를 다룹니다. 호출이 접근하는 다른 리소스에는 여전히 사용자 권한 확인이 필요합니다. 토큰은 API 호출을 인가합니다를 참고합니다.
- 할당 가능한 권한의
boundaries는 해당 권한의 원시 권한을 활성화하는 모든 정책이 아니라, 권한이 보호하는 엔드포인트를 기준으로 선택합니다.instance는 드물게, 보통 관리자용 권한에만 사용합니다. 경계 결정을 참고합니다. - 토큰 인가는 타입, 리졸버 또는 뮤테이션에
authorize_granular_token으로 한 번만 선언합니다. 개별 필드에granular_scope_directive를 추가하는 것보다 이 방식을 우선합니다. 응답의 일부가 다른 토큰 권한이나 경계를 필요로 하면, 자체 선언을 가진 별도의 타입, 리졸버 또는 뮤테이션으로 옮깁니다. - 뮤테이션이나 리졸버 코드 안에 두 번째 토큰 권한 확인을 추가하지 않습니다. 권한 검증 작업과 생성된 토큰 문서는 선언된 지시문만 읽습니다. 코드에 숨겨진 확인은 문서에 빠지므로 문서를 보고 만든 토큰이 거부됩니다. 자체 선언을 가진 별도의 뮤테이션이나 리졸버로 만듭니다.
GraphQL 인가#
- 개별 필드보다 타입 전체를 인가하는 방식을 우선합니다. 필드 인가가 널리 쓰이고 있지만 가능하면 더 늘리지 않습니다. 필드 인가는 리졸브 전에 현재 객체를 기준으로 확인하므로, 리졸버에 인가를 추가하거나 가능하면 타입에 추가합니다. 필드 인가를 참고합니다.
- 일부 필드가 타입의 나머지와 다른 액세스 수준을 필요로 하면, 자체 인가를 가진 별도의 하위 타입으로 옮기는 방안을 검토합니다.
- 필드의
authorize:는 부모 객체의 정책을 기준으로 확인합니다. 역할 YAML 부여는 프로젝트, 그룹, 조직 정책에 반영되므로, 그 외의 객체는 정책이 해당 데이터를 소유한 리소스로 확인을 넘겨야 합니다. 예시는 다른 권한에서 권한 도출 금지를 참고합니다.
테스트#
모든 정책 변경에는 정책 스펙이 필요합니다. 구조는 테스트 가이드라인을 참고합니다.
expect_allowed와expect_disallowed를 사용합니다.Gitlab/Authz/UsePolicyHelperscop이 이를 강제합니다.can?나Ability.allowed?를 스텁하지 말고 실제 멤버십과 설정을 사용합니다.
자동화된 검사#
이 페이지의 여러 규칙은 Gitlab/Authz/ 아래의 RuboCop cop이 강제합니다: RoleCheckInRule, AvoidHighestRoleUsage, DisallowAbilityAllowed, EnableInBasePolicy, ConditionScope, PermissionCheck, UsePolicyHelpers.
# rubocop:disable로 인라인 비활성화하거나 .rubocop_todo/gitlab/authz/ 아래에 제외 항목을 추가하는 방식으로 이러한 cop을 비활성화하지 않습니다.
비활성화가 불가피하면 Authorization 팀 구성원이 승인해야 합니다.
MR이 비활성화를 추가하면 Danger가 작성자에게 ~authorization 리뷰를 요청하라는 댓글을 남깁니다.
초안 MR은 건너뜁니다.
"다른 곳에서도 같은 권한을 사용한다"는 cop을 비활성화할 타당한 이유가 아닙니다. 적용 지점은 세분화된 권한을 확인해야 하며, 포괄적인 권한을 새로 사용할 때마다 이를 교체하는 작업이 늘어납니다. 대신 다음을 수행합니다.
- 변경하는 코드에 세분화된 권한을 추가하고 그 권한을 확인합니다.
- 다른 호출 지점을 세분화된 권한으로 옮기는 후속 이슈를 만듭니다.