본문으로 건너뛰기

RBAC란 무엇인가? Casbin에서의 역할 기반 접근 제어

RBAC란 무엇인가?​

**역할 기반 접근 제어(RBAC)**는 개별 사용자 대신 **역할(roles)**에 권한을 부여하고, 각 사용자에게 하나 이상의 역할을 할당합니다. 새 직원이 업무를 수행하려면 editor나 billing과 같은 역할을 할당하고, 모든 편집자의 작업 범위를 변경하려면 해당 역할을 한 번만 수정하면 됩니다.

RBAC는 세 가지 부분으로 구성됩니다:

  • 사용자(Users): 요청을 하는 사람 또는 서비스.
  • 역할(Roles): admin, editor, viewer와 같은 이름이 지정된 직무 기능.
  • 권한(Permissions): /articles에 대한 edit과 같이 리소스에 대한 특정 작업.

사용자의 역할 중 하나가 권한을 보유하고 있을 때 요청이 허용됩니다. 역할은 다른 역할로부터 **상속(inherit)**될 수도 있습니다. 예를 들어 editor가 viewer를 상속받으면, 편집자는 뷰어가 할 수 있는 모든 작업을 수행할 수 있습니다. RBAC는 1990년대에 NIST에 의해 공식화되었으며, RBAC96 모델 계열은 평면 역할(flat roles), 역할 계층 구조(role hierarchies) 및 제약 조건(constraints)과 같은 변형들을 설명합니다. Casbin이 이를 어떻게 매핑하는지는 RBAC vs. RBAC96 문서를 참조하세요.

Casbin의 최소 RBAC 모델 및 정책(policy):

[request_definition]
r = sub, obj, act

[policy_definition]
p = sub, obj, act

[role_definition]
g = _, _

[policy_effect]
e = some(where (p.eft == allow))

[matchers]
m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act
p, editor, article, edit
p, viewer, article, read

g, editor, viewer
g, alice, editor
g, bob, viewer

alice는 기사를 편집하고 읽을 수 있으며(editor가 viewer를 상속함), bob은 기사 읽기만 가능합니다.

RBAC vs ABAC: RBAC은 감사(audit)하기 쉽지만, 레코드마다 역할을 생성하지 않고서는 "오직 자신의 기록만"이라는 조건을 표현할 수 없습니다. ABAC는 소유자나 시간과 같은 속성에 대한 조건을 추가하며, 대부분의 애플리케이션에서는 두 모델을 결합하여 사용합니다. ACL, RBAC, ABAC 및 ReBAC을 하나의 예시로 비교한 내용은 접근 제어 모델 선택 문서를 참조하세요.

튜토리얼: 웹 API에 RBAC을 추가하는 방법은 Go with Gin, Node.js with Express, NestJS, Python with FastAPI, Java with Spring Boot, Rust 또는 gRPC 튜토리얼을 참고하세요. 또한 온라인 에디터에서 RBAC 예제를 직접 시도해 볼 수 있습니다.

역할 정의(Role definition)​

[role_definition] 섹션은 RBAC 역할(상속) 관계를 정의합니다. Casbin은 여러 RBAC 시스템을 동시에 실행할 수 있습니다. 예를 들어 사용자용 시스템(역할 상속 포함)과 선택적으로 리소스용 시스템을 각각 독립적으로 운영할 수 있습니다.

이 섹션은 선택 사항이며, RBAC을 사용하지 않는 경우 생략할 수 있습니다.

[role_definition]
g = _, _
g2 = _, _

여기서 g와 g2는 두 개의 역할 시스템이며, _, _는 각 시스템이 두 개의 측면(예: 사용자와 역할)을 가짐을 의미합니다. 사용자 역할에는 g만 사용하고, 리소스에도 역할이 있는 경우 g2를 추가합니다. 예제: rbac_model.conf, rbac_with_resource_roles_model.conf.

사용자–역할(및 리소스–역할) 매핑은 정책(policy)에 저장됩니다. 예:

p, data2_admin, data2, read
g, alice, data2_admin

따라서 alice는 data2_admin 역할을 가집니다. Casbin은 모든 이름(사용자, 리소스 또는 역할)을 문자열로 처리합니다.

matcher에서 역할을 사용하는 방법:

[matchers]
m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act

요청 주체(subject)가 정책에 명시된 역할을 가지고 있을 때만 요청이 허용됩니다.

노트
  1. Casbin은 사용자–역할(및 리소스–역할) 매핑만 저장하고 평가하며, 사용자나 역할의 존재 여부를 검증하지 않습니다. 이는 인증(authentication)의 책임입니다.
  2. 사용자와 역할에 동일한 이름을 재사용하지 마세요(예: 사용자 alice와 역할 alice). Casbin은 둘을 구별할 수 없기 때문입니다. 필요하다면 접두사를 사용하세요(예: role_alice).
  3. 역할 상속은 전이적(transitive)이며 제한이 없습니다. A가 B 역할을 가지고 있고 B가 C 역할을 가지고 있다면, A는 사실상 C 역할을 가집니다.
토큰 이름 규칙(Token name convention)

정책의 주체는 일반적으로 sub로 명명되며 첫 번째에 위치합니다. Go Casbin에서는 다른 이름과 순서를 사용할 수 있지만, 이 경우 enforcer 생성 후 e.SetFieldIndex("p", constant.SubjectIndex, index)를 호출해야 DeleteUser와 같은 API가 올바른 열을 사용합니다.

# `subject` here is for sub
[policy_definition]
p = obj, act, subject
e.SetFieldIndex("p", constant.SubjectIndex, 2) // index starts from 0
ok, err := e.DeleteUser("alice") // without SetFieldIndex, it will raise an error

명명된 역할 시스템 및 권한 의존성(Named role systems and permission dependencies)​

g, g2, g3, ...은 독립적인 역할 시스템입니다. 각 시스템은 matcher가 동일한 이름으로 호출하는 그래프를 정의합니다. g2는 내장된 "권한이 권한에 의존"하는 관계가 아닙니다. g2, edit, read와 같은 행은 단순히 간선(edge)일 뿐이며, matcher가 g2(...)를 요구할 때만 의사결정에 영향을 미칩니다.

권한 카탈로그에 의존성이 있는 경우(예: "편집은 읽기를 포함한다") 중요해집니다. 즉, 편집할 수 있는 역할은 읽기 권한도 가져야 합니다. 이를 모델링하는 두 가지 깔끔한 방법이 있습니다.

의존성이 권한 부여의 일부인 경우 역할 상속을 사용하세요. 각 묶음(bundle)에 역할을 부여하고 역할이 서로 상속되도록 설정합니다:

p, editor, article, edit
p, viewer, article, read

g, editor, viewer
g, alice, editor

editor가 viewer를 상속받으므로 alice는 읽기 권한을 가집니다. g2는 관련이 없습니다.

의존성 자체를 정책에 포함해야 하는 경우 별도의 g2 그래프를 사용하세요. matcher가 의미를 정의합니다:

[role_definition]
g = _, _
g2 = _, _

[matchers]
m = g(r.sub, p.sub) && r.obj == p.obj && (r.act == p.act || g2(p.act, r.act))
p, admin, article, write
g2, write, read
g, alice, admin

g2(write, read)가 참이므로 요청 alice, article, read는 p, admin, article, write 행과 일치합니다. 여기서 write는 read의 하위 항목이며, 이는 g 간선과 동일한 방향입니다. g2 행은 그 자체로 권한을 부여하지 않으며 Casbin에 의해 검증되지 않습니다. 각 간선의 방향과 의미는 사용자가 정의하지만, Casbin은 g와 동일한 전이적 상속(transitive inheritance)을 적용합니다.

의존성이 단순한 카탈로그 메타데이터인 경우(예: 관리 UI에서 "편집"을 체크했을 때 "읽기"가 사전 선택되어야 함)는 권한 카탈로그에 유지하고, 정책을 생성할 때 p, g 또는 g2 행으로 확장하세요. Casbin은 평가되지 않는 데이터를 저장할 필요가 없습니다. 정책이 반드시 CSV 파일에 있어야 하는 것도 아닙니다. 어댑터(adapter)를 사용하여 데이터베이스나 YAML/JSON 카탈로그에서 생성된 행을 로드할 수 있으므로, 카탈로그가 진실된 소스(source of truth)로 유지됩니다. 어댑터 API에 대한 자세한 내용은 Adapters 문서를 참조하세요.

역할 계층 구조(Role hierarchy)​

Casbin은 RBAC1 스타일의 역할 계층 구조를 구현합니다. alice가 role1을 가지고 있고 role1이 role2를 가지고 있다면, alice는 사실상 role2와 그 권한을 가집니다.

계층 깊이(hierarchy depth)는 허용되는 상속 레벨의 수를 의미합니다. 기본 역할 관리자는 최대 10단계의 깊이를 사용하며(설정 가능), 따라서 사용자는 최대 10단계까지 역할을 상속받을 수 있습니다.

// NewRoleManager is the constructor for creating an instance of the
// default RoleManager implementation.
func NewRoleManager(maxHierarchyLevel int) rbac.RoleManager {
rm := RoleManager{}
rm.allRoles = &sync.Map{}
rm.maxHierarchyLevel = maxHierarchyLevel
rm.hasPattern = false

return &rm
}

사용자와 역할 구분​

Casbin에서는 사용자와 역할이 모두 문자열입니다. 평면 RBAC(역할 계층 없음)에서는 GetAllSubjects()와 GetAllRoles()가 g 규칙의 좌측 및 우측 값(일반적으로 사용자와 역할)을 반환합니다. 계층 구조가 있는 경우 동일한 이름이 사용자와 역할로 모두 나타날 수 있으며, 애플리케이션에서 이를 구분하지 않는다면 명명 규칙(예: 접두어 role::)을 사용하고 결과를 해석할 때 이를 확인하세요.

암시적 역할 및 권한​

계층을 통해 상속된 역할과 권한(직접 할당뿐 아니라)은 암시적(implicit)이라고 합니다. 이러한 항목을 포함하려면 GetImplicitRolesForUser()와 GetImplicitPermissionsForUser()를 사용하세요. GetRolesForUser()와 GetPermissionsForUser()는 직접 할당된 항목만 반환합니다. GitHub #137 참조.

RBAC에서의 패턴 매칭​

패턴이 포함된 RBAC를 참조하세요.

역할 관리자​

맞춤형 역할 관리는 역할 관리자를 참조하세요.

Comments are powered by giscus and GitHub Discussions. They load from giscus.app and GitHub only after you click the button below.