1
0
forked from baron/baron-sso
Files
baron-sso/docs/adminfront-tab-level-direct-permission-design.md
2026-06-15 20:05:47 +09:00

8.4 KiB

[RFC/Design] adminfront: 각 탭별 ReBAC 기반 세부 권한 직접 부여 기능 설계

1. 배경 및 목적

현재 adminfront 테넌트 상세 페이지는 대략적인 역할 기반 제어(Coarse-grained RBAC/ReBAC) 형태로만 동작합니다. 운영자는 사용자를 "소유자(Owner)" 또는 **"테넌트 관리자(Admin)"**로만 임명할 수 있으며, 이 역할에 의해 테넌트 하위의 4개 탭(프로필, 권한 관리, 조직 관리, 사용자 스키마)의 읽기/쓰기 권한이 통째로 결정됩니다.

하지만 더욱 세밀한 운영 권한 관리가 필요하다는 비즈니스 요구사항에 따라, "사용자 A에게는 조직 관리 및 스키마 읽기 권한만 부여", **"사용자 B에게는 스키마 수정 권한만 부여"**와 같이 탭 레벨에서 세분화된(Fine-grained) 권한을 직접 지정할 수 있는 기능을 신설합니다.

이 설계는 devfront에서 이슈 #1029를 통해 구현 완료한 "RP 세부 관계 직접 부여" 철학과 완벽히 동일하며, Ory Keto(ReBAC) 및 아웃박스 정합성 엔진을 관통하여 설계됩니다.


2. 세부 설계 사양

2.1 Ory Keto OPL 스키마 변경 (docker/ory/keto/namespaces.ts)

Tenant 네임스페이스 하위에 각 탭별 읽기(_viewers)와 쓰기(_managers)를 결정하는 **물리적인 직접 관계(Direct Relations)**를 추가합니다. 기존 members, admins, owners에 의한 상속 허가 식(Permits)을 유지하여 하위 호환성 및 기존 관리체계의 안정성을 완벽히 보장합니다.

class Tenant implements Namespace {
  related: {
    owners: (User | SubjectSet<System, "super_admins">)[]
    admins: (User | SubjectSet<System, "super_admins">)[]
    members: (User | SubjectSet<System, "super_admins"> | SubjectSet<Tenant, "admins"> | SubjectSet<Tenant, "owners">)[]
    parents: Tenant[]
    developer_console_viewer: (User | SubjectSet<System, "super_admins">)[]
    developer_console_grant_manager: (User | SubjectSet<System, "super_admins">)[]

    // 🌟 신규 직접 관계 (Direct Relations) 정의
    profile_viewers: (User | SubjectSet<System, "super_admins">)[]
    profile_managers: (User | SubjectSet<System, "super_admins">)[]

    permissions_viewers: (User | SubjectSet<System, "super_admins">)[]
    permissions_managers: (User | SubjectSet<System, "super_admins">)[]

    organization_viewers: (User | SubjectSet<System, "super_admins">)[]
    organization_managers: (User | SubjectSet<System, "super_admins">)[]

    schema_viewers: (User | SubjectSet<System, "super_admins">)[]
    schema_managers: (User | SubjectSet<System, "super_admins">)[]
  }

  permits = {
    // 1. 프로필 (Profile) 탭 허가 규칙
    view_profile: (ctx: Context): boolean =>
      this.related.profile_viewers.includes(ctx.subject) ||
      this.permits.manage_profile(ctx) ||
      this.permits.view(ctx), // 멤버/관리자/소유자는 기본 조회 가능

    manage_profile: (ctx: Context): boolean =>
      this.related.profile_managers.includes(ctx.subject) ||
      this.permits.manage(ctx), // 관리자/소유자는 기본 수정 가능

    // 2. 권한 관리 (Permissions) 탭 허가 규칙
    view_permissions: (ctx: Context): boolean =>
      this.related.permissions_viewers.includes(ctx.subject) ||
      this.permits.manage_permissions(ctx) ||
      this.permits.view(ctx),

    manage_permissions: (ctx: Context): boolean =>
      this.related.permissions_managers.includes(ctx.subject) ||
      this.permits.manage_admins(ctx), // 소유자는 기본 관리 가능

    // 3. 조직 관리 (Organization) 탭 허가 규칙
    view_organization: (ctx: Context): boolean =>
      this.related.organization_viewers.includes(ctx.subject) ||
      this.permits.manage_organization(ctx) ||
      this.permits.view(ctx),

    manage_organization: (ctx: Context): boolean =>
      this.related.organization_managers.includes(ctx.subject) ||
      this.permits.manage(ctx),

    // 4. 사용자 스키마 (Schema) 탭 허가 규칙
    view_schema: (ctx: Context): boolean =>
      this.related.schema_viewers.includes(ctx.subject) ||
      this.permits.manage_schema(ctx) ||
      this.permits.view(ctx),

    manage_schema: (ctx: Context): boolean =>
      this.related.schema_managers.includes(ctx.subject) ||
      this.permits.manage(ctx),

    // --- 기존 마스터 및 상속 규칙 보존 ---
    view: (ctx: Context): boolean =>
      this.related.members.includes(ctx.subject) ||
      this.related.admins.includes(ctx.subject) ||
      this.related.owners.includes(ctx.subject) ||
      this.related.parents.traverse((p) => p.permits.view(ctx)),

    manage: (ctx: Context): boolean =>
      this.related.admins.includes(ctx.subject) ||
      this.related.owners.includes(ctx.subject) ||
      this.related.parents.traverse((p) => p.permits.manage(ctx)),

    manage_admins: (ctx: Context): boolean =>
      this.related.owners.includes(ctx.subject) ||
      this.related.parents.traverse((p) => p.permits.manage_admins(ctx))
  }
}

2.2 백엔드 API 설계 (backend/internal/handler/tenant_handler.go)

세부 권한 부여/회수 API는 해당 테넌트의 최상위 권한 관리자만 수행할 수 있도록 Tenant#manage_admins 허가 규칙으로 강력하게 인가 보호합니다.

A. 세부 권한 관계 전체 조회 API

  • Endpoint: GET /api/v1/admin/tenants/:id/relations
  • 인가 필터: RequireKetoPermission(config, "Tenant", "manage_admins")
  • 반환 DTO:
    {
      "items": [
        {
          "userId": "00000000-0000-0000-0000-000000000010",
          "name": "홍길동",
          "email": "kildong@hmac.kr",
          "relations": ["profile_managers", "schema_viewers"]
        }
      ]
    }
    

B. 세부 권한 관계 부여 API

  • Endpoint: POST /api/v1/admin/tenants/:id/relations
  • 인가 필터: RequireKetoPermission(config, "Tenant", "manage_admins")
  • Payload:
    {
      "userId": "00000000-0000-0000-0000-000000000010",
      "relation": "profile_managers"
    }
    
  • 동작: 트랜잭셔널 아웃박스에 적재하여 Keto에 Tenant:<ID>#profile_managers@User:<UserID> 튜플 반영.

C. 세부 권한 관계 회수 API

  • Endpoint: DELETE /api/v1/admin/tenants/:id/relations
  • 인가 필터: RequireKetoPermission(config, "Tenant", "manage_admins")
  • Payload:
    {
      "userId": "00000000-0000-0000-0000-000000000010",
      "relation": "profile_managers"
    }
    
  • 동작: 트랜잭셔널 아웃박스에 적재하여 Keto 내 튜플 삭제 반영.

2.3 프론트엔드 UI 설계

사용자에게 역할(Role) 외에 세부적인 설정을 직관적으로 관리할 수 있도록, 기존 "권한 관리" 탭 하단에 "세부 권한 설정 (Fine-grained Permissions)" 섹션을 신설합니다.

A. 구성 요소

  1. 유저 검색/추가 패널: 테넌트 소속 사용자를 검색하여 격리 설정 테이블(Matrix)에 추가합니다.
  2. 세부 권한 격리 매트릭스 (Matrix Table):
    • 컬럼: 이름 | 이메일 | 테넌트 프로필 | 권한 관리 | 조직 관리 | 사용자 스키마 | 작업
    • 각 탭 컬럼은 드롭다운 셀렉트 박스로 채워집니다:
      • 권한 없음 (None) / 조회 가능 (Read) / 수정 가능 (Write)
  3. 상태 동기화 연동:
    • 셀렉트 박스에서 조회 가능(Read) 선택 시: _viewers 관계 추가(POST) & _managers 관계 회수(DELETE).
    • 셀렉트 박스에서 수정 가능(Write) 선택 시: _managers 관계 추가(POST) & _viewers 관계 회수(DELETE).
    • 셀렉트 박스에서 권한 없음(None) 선택 시: 둘 다 회수(DELETE).

3. 작업 계획 및 테스트 전략

  1. OPL 컴파일 및 빌드 검증:
    • namespaces.ts 수정 후 Keto OPL 테스트를 구동하여 컴파일 문법에 문제가 없는지 사전 검증합니다.
  2. 백엔드 구현 및 DB 연동:
    • tenant_handler.go에 신규 핸들러 추가 후 gg/gorm 아웃박스 통합을 완료합니다.
  3. 프론트엔드 연동 및 Matrix UI 개발:
    • TenantAdminsAndOwnersTab.tsx 하단부 카드에 매트릭스 테이블 영역을 추가합니다.
  4. 유형 및 단위 테스트:
    • 신설된 REST API 명세를 테스트하는 고성능 백엔드 단위 테스트를 작성합니다.
    • 프론트엔드에서 체크박스 변경 시 올바른 릴레이션이 트리거되는지 검증하는 Vitest 렌더 테스트를 작성합니다.