Files
portfolio-hub/docs/technical/portfolio-ownership-and-auth-design.md
T

5.6 KiB

Portfolio Ownership and Auth Design

  • Related REQ: REQ-004

목표

  • 포트폴리오 리소스에 소유주(owner_user_id)를 도입해 사용자 단위 데이터 격리를 강제한다.
  • 기존 RBAC(read, trade-write, admin)와 소유권 검사 규칙을 결합해 접근 제어를 표준화한다.
  • frontend와 portfolio-hub가 같은 서버에서 개별 컨테이너로 운영되는 구조에서도 단일 인증 체계로 일관된 권한 모델을 유지한다.

범위

  • 포함:
    • DB 스키마 변경 및 레거시 데이터 백필
    • API/서비스/리포지토리 ownership 정책
    • 통합 테스트 및 회귀 테스트 시나리오
  • 제외:
    • 외부 IdP/OIDC 연동
    • 인증 서비스를 별도 프로세스로 분리하는 배포 작업

DB 마이그레이션 설계

Revision Plan

  1. owner_user_id nullable 추가
  2. 레거시 row 백필
  3. 인덱스 추가
  4. not null 제약 적용
  5. 필요 시 FK 제약(users.id) 적용

권장 DDL 단계

  1. 컬럼 추가
ALTER TABLE portfolios
ADD COLUMN owner_user_id TEXT;
  1. 백필 기준 사용자 결정
  • 환경변수 기반 bootstrap admin이 있으면 해당 사용자 id를 우선 사용한다.
  • 없으면 users.created_at ASC 첫 사용자 id를 사용한다.
  • 사용자 자체가 없으면 migration을 실패시키고 운영자가 사용자 생성 후 재시도한다.
  1. 레거시 데이터 백필
UPDATE portfolios
SET owner_user_id = :fallback_user_id
WHERE owner_user_id IS NULL;
  1. 제약 및 인덱스 적용
ALTER TABLE portfolios
ALTER COLUMN owner_user_id SET NOT NULL;

CREATE INDEX idx_portfolios_owner_updated
ON portfolios (owner_user_id, updated_at DESC);
  1. 선택적 FK 제약
ALTER TABLE portfolios
ADD CONSTRAINT fk_portfolios_owner_user
FOREIGN KEY (owner_user_id) REFERENCES users(id);

롤백 전략

  • downgrade에서는 FK, 인덱스, not null 제약 해제 후 컬럼 제거 순서로 진행한다.
  • 운영 데이터 보존이 필요하면 컬럼 삭제 대신 nullable 유지 downgrade를 별도 운영 방침으로 둔다.

API 권한 규칙 설계

공통 정책

  • 1단계: RBAC 검사 통과 필요
  • 2단계: admin이 아니면 ownership 검사 수행
  • ownership 검사 기준: portfolio.owner_user_id == auth_context.user_id
  • auth_context.user_id가 없는 요청은 ownership 필요한 엔드포인트에서 403

Endpoint Policy Matrix

Endpoint RBAC 최소 권한 Ownership 적용 비고
GET /portfolios read 적용 admin은 전체, 그 외 owner만 필터
GET /portfolios/{portfolio_id} read 적용 owner 불일치 시 403
POST /portfolios trade-write 생성 시 owner 고정 owner_user_id는 인증 주체로 강제
PATCH /portfolios/{portfolio_id} trade-write 적용 owner 불일치 시 403
POST /portfolios/{portfolio_id}/buy trade-write 적용 owner 불일치 시 403
POST /portfolios/{portfolio_id}/sell trade-write 적용 owner 불일치 시 403
POST /portfolios/{portfolio_id}/invest trade-write 적용 owner 불일치 시 403
POST /portfolios/{portfolio_id}/transfer trade-write 적용 owner 불일치 시 403
POST /portfolios/{portfolio_id}/reset trade-write 적용 owner 불일치 시 403
DELETE /portfolios/{portfolio_id} admin admin only 기존 정책 유지
POST/GET/PATCH/DELETE /portfolios/{id}/comments* read or trade-write 적용 상위 포트폴리오 ownership을 재사용

서비스/리포지토리 반영 포인트

  • Domain model:
    • Portfolio에 owner_user_id 추가
  • Schema:
    • PortfolioSummary/CreatePortfolioRequest 응답/입력 계약에 owner 노출 정책 정의
    • 외부 입력으로 owner_user_id 수동 지정은 금지
  • Repository:
    • create 시 owner_user_id 필수 인자
    • list/get 시 요청자 컨텍스트 기반 owner 필터 함수 제공
  • Service/API:
    • request principal을 service 계층으로 전달
    • 공통 ownership 가드 함수 도입

frontend 연계 방침

  • frontend는 credential 저장/검증 자체를 수행하지 않고 portfolio-hub 인증 API를 사용한다.
  • 브라우저 경로는 HttpOnly + Secure cookie 기반 세션을 우선 고려한다.
  • 동일 서버 내 별도 컨테이너 운영 시 reverse proxy에서 CORS/Cookie domain 정책을 일관되게 관리한다.

테스트 케이스 목록

Migration

  1. 기존 portfolios row가 존재할 때 owner_user_id 백필 후 null row가 0건인지 검증
  2. users가 없는 상태에서 migration 실행 시 실패 동작 검증
  3. downgrade 시 인덱스/제약 해제가 순서대로 수행되는지 검증

API Authorization

  1. 사용자 A 소유 portfolio를 사용자 B가 조회하면 403
  2. 사용자 A 소유 portfolio를 사용자 B가 수정/매매/리셋하면 403
  3. admin이 타 사용자 portfolio 조회/수정 가능
  4. 일반 사용자의 GET /portfolios는 본인 소유만 반환
  5. user_id 없는 bot principal 요청이 ownership 필요 API 호출 시 403

API Creation

  1. POST /portfolios에서 owner_user_id가 인증 사용자로 저장되는지 검증
  2. 요청 body에 owner_user_id를 넣어도 무시되거나 validation 실패하는지 검증

Regression

  1. 기존 REQ-001 댓글 API가 ownership 정책 적용 후에도 owner 경계 내에서 정상 동작
  2. 기존 RBAC 오류(401, 403) 계약이 ownership 도입 후에도 유지
  3. admin delete 동작이 ownership 변경과 충돌 없이 유지

추적 항목

  • 본 설계 문서 구현 시 docs/SRS/traceability-matrix.md의 REQ-004 행에 Issue/PR/Test evidence를 갱신한다.