Skip to content

RBAC 기반 Organization 및 Team Permission 관리 API #15

Description

@yoonki1207

작업 설명

기존 이슈 본문은 구 정책(user_team_permissions, can_* boolean permission, organization hierarchy, tag 권한)을 전제로 작성되어 있었으므로, 이 이슈의 기준을 현재 docs/ 문서로 재정의한다.

문서 기준:

  • docs/api/organization-rbac.md
  • docs/api/errors.md
  • docs/api/auth.md
  • docs/data-model/rbac-permission-policy.md
  • docs/data-model/physical-data-model.md
  • docs/architecture/auth-rbac.md
  • docs/decisions/ADR-202606271559-active-organization.md
  • docs/decisions/ADR-202606271559-auth-state-standard.md
  • docs/decisions/ADR-202606271559-user-direct-permission.md

충돌 해결 원칙:

  • GitHub 이슈 내용과 docs/가 충돌하면 항상 docs/를 따른다.
  • roles, user_roles, polymorphic resource_permissions는 만들지 않는다.
  • user_team_permissions, can_read, can_write, can_execute, can_delete, can_manage_tag, can_assign_tag 기준 API/권한 판정은 만들지 않는다.
  • 권한 상태는 DB enum이 아니라 auth_state application-level matrix 기준으로 해석한다.
  • admin은 permission이 아니다.
  • Admin, Builder, Operator, Viewer, Auditor는 DB role이 아니라 team template 이름이다.
  • Active organization은 신규 organization-scoped API에서 X-Organization-Id header로 전달한다.
  • 서버는 active organization을 session/cookie/organization row에 저장하지 않으며 PATCH /api/v1/organizations/current는 만들지 않는다.

범위 분리

이 이슈는 MVP1 범위로 좁힌다.

상세 작업

Organization / Active Organization API

  • GET /api/v1/organizations API를 추가한다. 인증된 사용자가 속한 active organization 목록을 반환한다.
  • GET /api/v1/organizations는 현재 사용자의 active team membership 기준으로 조회하고 중복 organization을 제거한다.
  • GET /api/v1/organizations 정렬은 created_at ASC, id ASC다.
  • GET /api/v1/organizations/{organization_id} API를 추가한다. 사용자의 active team membership scope 안에 있는 특정 active organization을 반환한다.
  • GET /api/v1/organizations/{organization_id}는 organization이 없거나 inactive 또는 scope 밖이면 404 + resource.not_found를 반환한다.
  • GET /api/v1/organizations/current API를 추가한다. X-Organization-Id header가 현재 사용자의 active team membership scope 안에 있으면 OrganizationResponse를 반환한다.
  • PATCH /api/v1/organizations/{organization_id} API를 추가한다. organization 자체 정보(name, options)만 partial update한다.
  • PATCH /api/v1/organizations/{organization_id}는 active organization 변경 API가 아니다.
  • PATCH /api/v1/organizations/{organization_id}X-Organization-Id header를 요구하고, header 값과 path의 organization_id가 다르면 404 + resource.not_found를 반환한다.
  • organization 수정 권한은 organization.created_by 또는 organization.managed_by인 organization manager 기준으로 판정한다.
  • created_by, managed_by, flags, is_active, deactivated_at은 이 endpoint에서 수정하지 않는다.
  • OrganizationResponseid, name, options, is_active, created_at, updated_at을 반환한다.

Team 관리 API

  • GET /api/v1/teams API를 추가한다. X-Organization-Id header와 limit query를 받고 list[TeamResponse]를 반환한다.
  • GET /api/v1/teams는 관리 화면용 team 목록 API로만 사용한다. 일반 member의 “내 team 목록” 조회와 섞지 않는다.
  • GET /api/v1/teams는 organization manager만 허용한다. organization.created_by 또는 organization.managed_by가 현재 user면 active team membership 없이도 접근 가능하다.
  • GET /api/v1/teams는 active/inactive team을 모두 포함하고 name ASC, id ASC로 정렬한다.
  • GET /api/v1/teamslimit 기본값은 10, 허용 범위는 1..100이다.
  • GET /api/v1/teams 응답에는 member_count를 포함하지 않는다. teams table에 해당 column이 없다.
  • POST /api/v1/teams API는 organization manager 권한으로 추가한다. Request/Response 상세는 문서상 TBD이므로 임의 schema를 만들지 않는다.
  • PATCH /api/v1/teams/{team_id} API는 organization manager 권한으로 추가한다. Request/Response 상세는 문서상 TBD이므로 임의 schema를 만들지 않는다.
  • POST /api/v1/teams/{team_id}/members API는 organization manager 권한으로 추가한다. Request/Response 상세는 문서상 TBD이므로 임의 schema를 만들지 않는다.
  • DELETE /api/v1/teams/{team_id}/members/{user_id} API는 organization manager 권한으로 추가한다. Request는 없고 Response 상세는 문서상 TBD이므로 임의 schema를 만들지 않는다.

Resource Permission Grant/Revoke API

  • PUT /api/v1/permissions/workflows/{workflow_id}/teams/{team_id} API를 추가한다. workflow manage 또는 organization manager 권한을 요구한다.
  • DELETE /api/v1/permissions/workflows/{workflow_id}/teams/{team_id} API를 추가한다. workflow manage 또는 organization manager 권한을 요구한다.
  • PUT /api/v1/permissions/workflows/{workflow_id}/users/{user_id} API를 추가한다. workflow manage 또는 organization manager 권한을 요구한다.
  • DELETE /api/v1/permissions/workflows/{workflow_id}/users/{user_id} API를 추가한다. workflow manage 또는 organization manager 권한을 요구한다.
  • PUT /api/v1/permissions/llm-credentials/{credential_id}/teams/{team_id} API를 추가한다. credential manage 또는 organization manager 권한을 요구한다.
  • DELETE /api/v1/permissions/llm-credentials/{credential_id}/teams/{team_id} API를 추가한다. credential manage 또는 organization manager 권한을 요구한다.
  • PUT /api/v1/permissions/llm-credentials/{credential_id}/users/{user_id} API를 추가한다. credential manage 또는 organization manager 권한을 요구한다.
  • DELETE /api/v1/permissions/llm-credentials/{credential_id}/users/{user_id} API를 추가한다. credential manage 또는 organization manager 권한을 요구한다.
  • Permission grant request body는 { "auth_state": "viewer" } 형태를 따른다.
  • 허용 auth_state와 의미는 docs/data-model/rbac-permission-policy.md의 resource별 matrix를 따른다.

권한 계산 / Enforcement

  • RBAC enforcement는 Gateway endpoint와 runtime service가 함께 쓰는 재사용 가능한 service/helper logic으로 구현한다. Controller가 workflow/LLM permission 비즈니스 규칙을 소유하지 않는다.
  • 기본 권한 subject는 teamsteam_memberships다.
  • RBAC 기반 Organization 및 Team Permission 관리 API #15 범위의 resource 권한은 team_workflow_permissions, team_llm_permissions를 기준으로 판정한다.
  • RBAC 기반 Organization 및 Team Permission 관리 API #15 범위의 user direct permission은 user_workflow_permissions, user_llm_permissions로만 다룬다.
  • user direct permission은 additive allow 전용이다. team 권한을 낮추거나 deny하지 않는다.
  • 권한 row가 없거나 auth_state='none'이면 deny by default로 처리한다.
  • 허용되지 않은 auth_state 값은 none과 동일하게 fail-closed 처리한다.
  • organization.created_by 또는 organization.managed_by에 해당하는 user는 구현된 organization/team 관리 API에서 organization manager로 판정한다.
  • connection use는 connection permission table이 아니라 consuming workflow 권한으로 허용한다. knowledge base 기준 connection use는 nodease/mbased#80에서 처리한다.
  • Knowledge Base RBAC enforcement는 nodease/mbased#80에서 처리한다.
  • Audit RBAC visibility enforcement는 nodease/mbased#81에서 처리한다.

Error / Audit

  • 구현된 organization/team API는 docs/api/errors.md의 목표 Error Envelope을 따른다.
  • GET /api/v1/teams에서 인증 없음은 401 + auth.required를 반환한다.
  • GET /api/v1/teams에서 X-Organization-Id header 누락은 400 + organization.required를 반환한다.
  • GET /api/v1/teams에서 invalid X-Organization-Id 또는 invalid limit422 + validation.failed를 반환한다.
  • GET /api/v1/teams에서 organization이 없거나 inactive 또는 사용자 scope 밖이면 404 + resource.not_found를 반환한다.
  • GET /api/v1/teams에서 organization member지만 manager가 아니면 403 + permission.denied를 반환한다.
  • GET /api/v1/organizations/{organization_id}에서 organization이 없거나 inactive 또는 사용자 scope 밖이면 404 + resource.not_found를 반환한다.
  • PATCH /api/v1/organizations/{organization_id}에서 X-Organization-Id header 누락은 400 + organization.required를 반환한다.
  • PATCH /api/v1/organizations/{organization_id}에서 invalid X-Organization-Id422 + validation.failed를 반환한다.
  • PATCH /api/v1/organizations/{organization_id}에서 header 값과 path의 organization_id가 다르면 404 + resource.not_found를 반환한다.
  • PATCH /api/v1/organizations/{organization_id}에서 organization이 없거나 inactive 또는 현재 사용자의 scope 밖이면 403 + permission.denied를 반환한다.
  • PATCH /api/v1/organizations/{organization_id}에서 현재 사용자가 organization member이지만 owner/manager가 아니면 403 + permission.denied를 반환한다.
  • PATCH /api/v1/organizations/{organization_id}에서 변경 가능한 field가 없거나 name이 빈 문자열이면 400 + validation.failed를 반환한다.
  • PATCH /api/v1/organizations/{organization_id}에서 name이 database column 길이보다 길면 422 + validation.failed를 반환한다.
  • 조직 정보 수정은 audit_logsorganization.update로 기록한다.
  • workflow/LLM 권한 부여, 권한 회수, 권한 거부는 audit_logs에 기록한다.
  • Knowledge permission audit는 nodease/mbased#80에서 처리한다.
  • Audit visibility permission audit는 nodease/mbased#81에서 처리한다.
구 정책 기준에서 제외하는 항목

아래 항목은 기존 이슈 본문에 있었더라도 현재 문서와 충돌하므로 이 이슈 범위에서 제외한다.

  • organization 상위/하위 구조 API 또는 schema 신규 설계
  • PATCH /api/v1/organizations/current
  • user_team_permissions 조회/생성/삭제 API
  • can_manage_tag, can_assign_tag 기반 관리 권한
  • can_read, can_write, can_execute, can_delete boolean OR 기반 권한 계산
  • roles, user_roles, polymorphic resource_permissions 신규 생성
  • audit_events 신규 생성
  • user_connection_permissions, user_app_permissions, user_document_permissions, user_model_permissions 신규 생성
  • team 목록 응답의 effective permission 필드 추가
  • 문서에 TBD로 남아 있는 Team 생성/수정/member API request/response schema의 임의 확정

테스트 기준

  • GET /api/v1/organizations active membership 조회, 중복 제거, 정렬 검증
  • GET /api/v1/organizations/{organization_id} active membership scope와 404 envelope 검증
  • PATCH /api/v1/organizations/{organization_id} header 누락/invalid/header-path mismatch 검증
  • PATCH /api/v1/organizations/{organization_id} owner/manager 권한과 non-manager 거부 검증
  • PATCH /api/v1/organizations/{organization_id} inactive/scope 밖 organization 거부 검증
  • PATCH /api/v1/organizations/{organization_id} 빈 update, blank name, max length validation 검증
  • GET /api/v1/teamsX-Organization-Id header 누락/invalid 케이스
  • GET /api/v1/teamslimit 기본값과 범위 검증
  • GET /api/v1/teams에서 organization이 없거나 inactive이거나 사용자 scope 밖인 케이스
  • GET /api/v1/teams에서 organization member지만 manager가 아닌 케이스
  • GET /api/v1/teams에서 organization owner/manager가 active team membership 없이 접근하는 케이스
  • GET /api/v1/teams가 active/inactive team을 모두 반환하는 케이스
  • GET /api/v1/teams 정렬이 name ASC, id ASC인 케이스
  • workflow/LLM resource별 auth_state matrix 및 invalid state fail-closed 검증
  • workflow/LLM permission grant/revoke/denied audit 기록 검증
  • Knowledge Base RBAC 테스트는 nodease/mbased#80에서 처리한다.
  • Audit RBAC 테스트는 nodease/mbased#81에서 처리한다.

Metadata

Metadata

Assignees

Labels

Type

No type

Fields

Priority

None yet

Projects

Status
Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions