docs(dcy-011): draft-req + confirm-req — REQ-052/053/054 Approved

Add three approved requirements documents for dcy-011:
- REQ-052: User-owned credential management API (Interface)
- REQ-053: Admin daemon CRUD API (Interface)
- REQ-054: Credential management security with write-only constraint (Non-Functional)

Update discovery document to reference generated requirements and update index with new REQ IDs and Change Log.

Scope: Self-service credential management + admin daemon CRUD + security-isolated API for portfolio-hub.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VHtM5Ws1HKCKQ4JGi6GRb5
This commit is contained in:
2026-06-28 13:55:45 +09:00
co-authored by Claude Opus 4.8
parent a1bde2cf67
commit 80d0511d7f
5 changed files with 305 additions and 4 deletions
@@ -5,10 +5,13 @@
- 문서 ID: dcy-011_20260628_daemon-crud-and-self-service-credential-api
- 이슈명: 데몬 admin CRUD API + 사용자 self-service 브로커 인증정보 관리 API — 소유 스코프 write/rotate/메타데이터(복호화 secret 미반환), 데몬 생성·할당·(비)활성화는 admin. trex-web dcy-009 백엔드 (교차 발원)
- 작성 시각(KST): 2026-06-28
- 마지막 갱신 시각(KST): 2026-06-28
- 마지막 갱신 시각(KST): 2026-06-28 (REQ 초안 생성)
- 대체됨: 없음
- 후속 Discovery 참조: 없음
- 생성된 REQ 참조: 없음
- 생성된 REQ 참조:
- REQ-052: 사용자 소유 스코프 인증정보 관리 HTTP API ([docs/srs/Interface/req-052_user-owned-credential-management-api.md](../srs/Interface/req-052_user-owned-credential-management-api.md))
- REQ-053: admin 데몬 CRUD HTTP API ([docs/srs/Interface/req-053_admin-daemon-crud-api.md](../srs/Interface/req-053_admin-daemon-crud-api.md))
- REQ-054: 인증정보 관리 보안 격리 — write-only·secret 미반환·로그 미노출 ([docs/srs/Non-Functional/req-054_credential-management-security-write-only.md](../srs/Non-Functional/req-054_credential-management-security-write-only.md))
# 1. 계획 상태 요약
현재 Discovery가 어떤 배경에서 작성되고 있는지, 연계 문서가 있는지, 지금 시점의 해석이 무엇인지 짧게 요약한다.
@@ -0,0 +1,99 @@
# REQ-052: 사용자 소유 스코프 인증정보 관리 HTTP API
- Status: Approved
- Type: Interface
- Priority: High
- Owner: Justin Ko
## Intent
- 현재 BrokerCredential 관리는 CLI(`pfhub credential`)와 service-token read 경로(REQ-051/REQ-049)만 존재하며, HTTP user-token 기반 관리 표면이 없다.
- trex-web dcy-009 사용자 self-service UI가 소비하는 백엔드 경로를 신설한다: 사용자가 자기 소유 데몬의 KIS 인증정보를 HTTP API로 등록·회전·삭제하고, 메타데이터(secret 미포함)를 조회할 수 있도록 한다.
- dcy-010 자산(BrokerCredential 모델, Fernet 암호화, `CREDENTIAL_ENCRYPTION_KEY`, PK `(daemon_id, broker)`)을 재사용하여 CLI와 동일 repository·암호화 경로를 공유한다.
## Requirement
- 시스템은 user-token으로 인증된 사용자가 **자기 소유 데몬**의 broker 인증정보를 등록·회전·삭제할 수 있는 write 엔드포인트를 제공해야 한다.
- 등록/회전: `app_key` + `app_secret` + `account_ref` 입력을 Fernet 암호화하여 `BrokerCredential`에 upsert. 회전 시 `credential_version` 증가, `last_rotated_at` 갱신.
- 삭제: 해당 `(daemon_id, broker)` 행 제거.
- 시스템은 사용자가 자기 소유 데몬의 인증정보 **메타데이터**를 조회할 수 있는 read 엔드포인트를 제공해야 한다. 응답에는 `broker`, `account_ref`, `credential_version`, `last_rotated_at`, 보유 여부(`has_credential`)만 포함하며 **복호화된 `app_key`/`app_secret`은 절대 포함하지 않는다**.
- **소유 스코프 인가**를 모든 관리 경로에 강제해야 한다: 요청자 `user_id`가 대상 데몬의 `owner_user_id`와 일치하지 않으면 HTTP 403 Forbidden을 반환한다. 인증 토큰이 없는 요청은 HTTP 401을 반환한다.
- 인증정보 write 요청이 성공하면 응답에는 메타데이터(`broker`, `account_ref`, `credential_version`, `last_rotated_at`)만 반환하고 `app_key`/`app_secret`은 포함하지 않는다.
- 엔드포인트 경로·HTTP 메서드의 구체 형태는 draft-batch-design에서 v1 컨벤션(`/v1/daemons/{daemon_id}/credential` 방향 권고, dcy-011 OQ-1 Deferred)에 맞춰 확정한다.
- 기존 CLI(`pfhub credential`)와 동일한 `BrokerCredentialRepository` 메서드 및 `app/core/encryption.py` Fernet 자산을 재사용해야 한다.
## Acceptance Criteria
1. Given user-token(소유자 A)으로 자기 소유 daemon D에 `app_key`/`app_secret`/`account_ref` 를 등록, When write 엔드포인트를 호출, Then HTTP 200/201 + 메타데이터 응답(`broker`, `account_ref`, `credential_version`, `last_rotated_at`)이 반환되고 응답 JSON에 `app_key`/`app_secret` 필드가 존재하지 않는다.
2. Given 같은 소유자 A가 동일 daemon D 인증정보를 재등록(회전), When write 엔드포인트를 호출, Then 응답의 `credential_version`이 이전보다 1 증가하고 `last_rotated_at`이 갱신된다.
3. Given 소유자 A가 daemon D 인증정보를 등록한 뒤, When service-token `/v1/user_info`(REQ-049)로 해당 daemon D의 인증정보를 fetch하면, Then `broker.account_ref`가 방금 등록한 값과 일치한다(CLI·API·service-token 경로 정합).
4. Given user-token(소유자 A)으로 자기 소유 daemon D 인증정보 메타데이터를 GET, When 조회 엔드포인트를 호출, Then HTTP 200 + `broker`, `account_ref`, `credential_version`, `last_rotated_at`, `has_credential` 반환되고 `app_key`/`app_secret` 필드는 응답에 없다.
5. Given user-token(소유자 A)으로 자기 소유 daemon D 인증정보를 삭제, When delete 엔드포인트를 호출, Then HTTP 200/204 반환 후 해당 daemon D 메타데이터 조회 시 `has_credential: false`(또는 인증정보 없음 응답).
6. Given user-token(사용자 B)으로 **사용자 A 소유** daemon D의 인증정보에 대한 write/read/delete 요청, When 엔드포인트를 호출, Then HTTP 403 Forbidden 반환(소유 스코프 위반).
7. Given 인증 토큰 없는 요청으로 인증정보 관리 엔드포인트 호출, When 호출, Then HTTP 401 반환.
8. Given 존재하지 않는 daemon_id를 대상으로 인증정보 관리 요청, When 호출, Then HTTP 404 반환.
9. Given admin 토큰으로 자기 소유 데몬이 아닌 daemon D의 인증정보 write 엔드포인트 호출, When 호출, Then HTTP 403 반환(관리 API는 소유 스코프 전용 — admin role로 타인 인증정보 조작 불가).
10. Given service-token으로 인증정보 관리 엔드포인트 호출, When 호출, Then HTTP 403 반환(service-token은 read 전용 `/v1/user_info` 경로만 허용).
## Impacted Area
- Docs:
- `docs/interface-contract.md` — 인증정보 관리 엔드포인트 경로·권한·응답 스키마(secret 미포함 정책) 신규 섹션 추가.
- Modules:
- 신규 라우터 파일(경로·파일명은 design 확정) — 인증정보 관리 write/read/delete 엔드포인트.
- `app/schemas/` — `CredentialSetRequest`(app_key/app_secret/account_ref), `CredentialMetaResponse`(메타데이터 only, secret 미포함) 스키마 신규 정의.
- `app/services/` — 소유 스코프 인가 검사 + Fernet 암호화/upsert/삭제 서비스 메서드 신규 또는 확장.
- `app/repositories/broker_credential_repository.py` — 메타데이터 조회·write 메서드 확장(CLI와 공유).
- `app/core/encryption.py` — 재사용 (변경 없음).
- `app/main.py` 또는 라우터 등록 — 신규 라우터 include.
- Tests:
- AC-1~10 커버 pytest 케이스. 특히 AC-6(소유 스코프 음성), AC-9(admin 소유 스코프 음성), AC-10(service-token 음성), AC-3(CLI·API·service-token 정합) 강제 검증.
## Notes
- OQ-1(엔드포인트 경로): draft-batch-design 위임(dcy-011 Deferred). 팀장 권고 = daemon-scoped(`/v1/daemons/{daemon_id}/credential`). v1 컨벤션·dcy-009 UI 흐름 정합 설계 단계 확정.
- **복호화 secret 미반환 불변**: 이 REQ의 어떤 응답도 `app_secret`/`app_key` 평문을 포함하지 않는다. 유일 decrypt-return 경로는 service-token `GET /v1/user_info`(REQ-051/REQ-049) — 본 REQ로 변경하지 않는다.
- AC-9(admin 소유 스코프): admin은 데몬 CRUD(REQ-053)를 담당하며, admin role로 타인 인증정보 write/read를 허용하지 않는다. 인증정보는 **사용자 본인 소유 스코프만**. 이 경계가 REQ-052(user-token, 소유 스코프)와 REQ-053(admin, 데몬 CRUD)의 책임 분리이다.
- NFR-1(보안 — secret 미반환·로그 미노출)은 REQ-054에서 독립 Non-Functional REQ로 추적한다. 이 REQ AC는 응답 schema 단의 secret 부재 확인을 포함하며, REQ-054가 전체 보안 격리를 측정한다.
- NFR-3(일관성 — CLI·API 정합): AC-3이 흡수한다. 구현 시 동일 repository 메서드 재사용 강제.
- Source: dcy-011 FR-1, FR-2, FR-3, NFR-2, NFR-3.
## Change Log
### CHG-20260628-01
- Date: 2026-06-28
- Author: Justin Ko
#### Change Summary
- 신규 생성 (Proposed) — dcy-011 FR-1/FR-2/FR-3 산출물.
#### Intent
- dcy-011 confirmed Discovery의 FR-1(인증정보 등록·회전·삭제), FR-2(메타데이터 조회, secret 미반환), FR-3(소유 스코프 인가 강제)를 단일 Interface REQ로 정규화한다. 세 FR이 동일 관리 표면(user-token + 소유 스코프 인증정보 관리)에 속하므로 하나의 REQ로 묶는다.
#### Acceptance Criteria Delta
- Added:
- AC-1 ~ AC-10 전체 (신규 초안)
- Updated: 없음
- Removed: 없음
#### Impacted Area Delta
- Docs:
- `docs/srs/Interface/req-052_user-owned-credential-management-api.md` 신규 생성
- `docs/interface-contract.md` 갱신 예고
- Modules:
- 인증정보 관리 라우터(신규), 스키마(신규), 서비스·repository 확장
- Tests:
- 소유 스코프 음성(AC-6/9/10), 메타데이터 secret 부재(AC-1/4), CLI 정합(AC-3) 케이스 신규
#### Delivery Trace Delta
- Batches: 미정 (suggest-batch-reqs 대기)
- Releases: 미정
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: keep-current
- Follow-up Action: confirm-req 후 suggest-batch-reqs. REQ-053/REQ-054와 동일 batch 묶기 권고. OQ-1(경로)은 design 단계 해소.
#### Validation Plan
- pytest로 AC-1~10 각 케이스 확인. AC-6(타 사용자 소유 스코프 음성), AC-9(admin 소유 스코프 음성), AC-10(service-token 음성)은 음성 테스트로 강제 검증.
### CHG-20260628-02
- Date: 2026-06-28
- Author: 팀장(Claude)
- confirm-req: `Proposed` → `Approved`. dcy-011 FR-1/2/3 추적. 소유 스코프 음성(AC-6/9/10)·메타데이터 secret 부재(AC-1/4)·CLI·API·service-token 정합(AC-3) 적절. **AC-9(admin도 타인 인증정보 write→403)** = 사장님 "self-service 전용·admin 감독 없음" 결정과 정합 확인(admin은 데몬 CRUD만, 인증정보는 본인 소유 스코프). 경로(OQ-1) design 위임. blocker 없음.
@@ -0,0 +1,104 @@
# REQ-053: admin 데몬 CRUD HTTP API
- Status: Approved
- Type: Interface
- Priority: High
- Owner: Justin Ko
## Intent
- 현재 `Daemon` 엔티티는 `GET /v1/daemons`(read 전용, REQ-045)만 HTTP로 노출되며, 생성·변경·(비)활성화 HTTP API가 없다. 관리자(admin)가 HTTP API로 데몬을 provisioning하고 owner를 할당·변경·(비)활성화할 수 있어야 한다.
- trex-web dcy-009 admin UI가 소비하는 백엔드 경로를 신설한다.
- 데몬은 운영 런타임 식별자(service-token daemon_id 바인딩, KIS 계좌 연결)로 하드 삭제는 제공하지 않으며 **비활성화로 대체**한다(dcy-011 FR-4 결정). owner 재할당은 허용하되 **owner 변경 시 해당 데몬의 기존 인증정보를 자동 무효화**한다(이전 owner의 KIS 계좌 이관 금지, dcy-011 OQ-3 결정).
## Requirement
- 시스템은 admin role로 인증된 요청자가 데몬을 **생성**하고 `owner_user_id`를 할당할 수 있는 POST 엔드포인트를 제공해야 한다.
- 입력: `daemon_slug`(고유 식별자), `display_name`(선택), `owner_user_id`.
- 성공 시 생성된 Daemon의 `id`, `daemon_slug`, `display_name`, `owner_user_id`, `is_active` 반환.
- 시스템은 admin이 기존 데몬의 `display_name` 또는 `owner_user_id`를 **변경**할 수 있는 PATCH 엔드포인트를 제공해야 한다.
- **owner 변경 시 해당 데몬의 기존 `BrokerCredential` 전체를 자동 삭제(무효화)** 해야 한다 — 이전 owner의 KIS 계좌 정보가 새 owner에게 이관되는 것을 방지한다.
- 시스템은 admin이 데몬을 **비활성화**(`is_active=false`) 및 **재활성화**(`is_active=true`)할 수 있는 엔드포인트를 제공해야 한다.
- 비활성화 시 해당 데몬에 바인딩된 service token을 자동 revoke해야 한다(기존 REQ-022 정책 재사용).
- **하드 삭제(DELETE) 엔드포인트는 제공하지 않는다.** 비활성화가 유일한 종료 경로이다.
- admin role이 아닌 요청(user-token, service-token 포함)은 HTTP 403 Forbidden을 반환해야 한다.
- 엔드포인트 경로·HTTP 메서드의 구체 형태는 draft-batch-design에서 v1 admin 컨벤션(dcy-011 OQ-2 Deferred, dcy-006 admin 경로 참고)에 맞춰 확정한다.
## Acceptance Criteria
1. Given admin 토큰으로 daemon_slug, display_name, owner_user_id를 포함한 생성 요청, When POST 엔드포인트를 호출, Then HTTP 201 + 생성된 Daemon(`id`, `daemon_slug`, `display_name`, `owner_user_id`, `is_active=true`) 반환.
2. Given admin 토큰으로 중복 `daemon_slug`로 생성 요청, When POST 엔드포인트를 호출, Then HTTP 409(또는 422) Conflict 반환(중복 슬러그 거부).
3. Given admin 토큰으로 존재하는 daemon D의 `display_name` 변경 요청(owner 변경 없음), When PATCH 엔드포인트를 호출, Then HTTP 200 + 갱신된 Daemon 반환. 기존 BrokerCredential은 유지된다.
4. Given admin 토큰으로 daemon D의 `owner_user_id`를 사용자 B로 재할당 요청(원래 소유자 A), When PATCH 엔드포인트를 호출, Then HTTP 200 + 갱신된 Daemon 반환 + 해당 daemon D의 BrokerCredential이 모두 삭제(무효화)된다.
5. Given daemon D의 owner가 변경된 직후, When 변경 전 소유자 A의 service-token으로 `/v1/user_info`를 호출, Then `broker: null` 반환(이전 owner 인증정보 이관 차단).
6. Given admin 토큰으로 활성 daemon D 비활성화 요청, When 비활성화 엔드포인트를 호출, Then HTTP 200 + `is_active: false` 반환 + daemon D에 바인딩된 service token 자동 revoke(REQ-022 정책).
7. Given admin 토큰으로 비활성 daemon D 재활성화 요청, When 재활성화 엔드포인트를 호출, Then HTTP 200 + `is_active: true` 반환.
8. Given admin 토큰으로 daemon D에 DELETE 요청(하드 삭제), When 호출, Then HTTP 405 Method Not Allowed(또는 404) 반환 — 하드 삭제 엔드포인트 미제공.
9. Given user-token(admin role 없음)으로 데몬 생성·변경·비활성화 엔드포인트 호출, When 호출, Then HTTP 403 Forbidden 반환.
10. Given service-token으로 데몬 CRUD 엔드포인트 호출, When 호출, Then HTTP 403 Forbidden 반환.
11. Given 인증 토큰 없는 요청으로 데몬 CRUD 엔드포인트 호출, When 호출, Then HTTP 401 반환.
12. Given admin 토큰으로 존재하지 않는 daemon_id에 PATCH/비활성화 요청, When 호출, Then HTTP 404 반환.
## Impacted Area
- Docs:
- `docs/interface-contract.md` — admin 데몬 CRUD 엔드포인트 경로·권한·owner 재할당 시 인증정보 무효화 정책 신규 섹션 추가.
- Modules:
- 신규 또는 확장 admin 데몬 라우터(경로·파일명은 design 확정).
- `app/schemas/` — `DaemonCreateRequest`, `DaemonUpdateRequest`, `DaemonAdminResponse` 스키마 신규 정의.
- `app/services/` — 데몬 생성·변경·비활성화 서비스 메서드 신규. owner 재할당 시 `BrokerCredential` 무효화 로직 포함.
- `app/repositories/` — `DaemonRepository` create/update/deactivate/reactivate 메서드 신규.
- `app/repositories/broker_credential_repository.py` — owner 재할당 시 credential 삭제 메서드(신규 또는 확장).
- REQ-022(`daemon deactivation service token auto-revoke`) 서비스 로직 재사용 — 비활성화 경로에서 호출.
- Tests:
- AC-1~12 커버 pytest 케이스. AC-4/5(owner 재할당 + 인증정보 무효화), AC-8(DELETE 405 음성), AC-9/10/11(권한 음성) 강제 검증.
## Notes
- OQ-2(엔드포인트 경로): draft-batch-design 위임(dcy-011 Deferred). `/v1/admin/daemons` vs `/v1/daemons`(admin 분기) — dcy-006 admin 경로 컨벤션 참고.
- **owner 재할당 + 인증정보 자동 무효화(AC-4/5)**: dcy-011 OQ-3 Answered(2026-06-28 결정). 새 owner는 자기 KIS 인증정보를 REQ-052 API로 재등록해야 한다. 이 정책은 이전 owner의 KIS 계좌 정보가 데몬에 잔류하는 보안 위험을 제거한다.
- **하드 삭제 미제공(AC-8)**: 데몬은 운영 런타임 식별자(service-token daemon_id 바인딩)이므로 참조 무결성 보호를 위해 비활성화가 유일 종료 경로. dcy-011 FR-4 결정.
- **비활성화 → service token revoke(AC-6)**: 기존 REQ-022 정책(`daemon deactivation auto-revoke`)을 이 경로에서도 호출. REQ-022 구현 재사용.
- NFR-2(권한 — admin role 강제): AC-9/10/11로 흡수한다.
- daemon_slug 중복 처리(AC-2): HTTP 409 vs 422는 design 단계에서 v1 컨벤션에 맞춰 확정.
- Source: dcy-011 FR-4, NFR-2, OQ-3 Answered.
## Change Log
### CHG-20260628-01
- Date: 2026-06-28
- Author: Justin Ko
#### Change Summary
- 신규 생성 (Proposed) — dcy-011 FR-4 산출물.
#### Intent
- dcy-011 confirmed Discovery의 FR-4(admin 데몬 생성·owner 할당·변경·(비)활성화, 삭제 제외) 및 OQ-3 결정(owner 재할당 허용 + 인증정보 자동 무효화)을 Interface REQ로 정규화한다.
#### Acceptance Criteria Delta
- Added:
- AC-1 ~ AC-12 전체 (신규 초안)
- Updated: 없음
- Removed: 없음
#### Impacted Area Delta
- Docs:
- `docs/srs/Interface/req-053_admin-daemon-crud-api.md` 신규 생성
- `docs/interface-contract.md` 갱신 예고
- Modules:
- admin 데몬 라우터(신규), DaemonCreate/UpdateRequest/AdminResponse 스키마(신규), 서비스·repository 신규
- Tests:
- owner 재할당+인증정보 무효화(AC-4/5), DELETE 음성(AC-8), 권한 음성(AC-9~11) 케이스 신규
#### Delivery Trace Delta
- Batches: 미정 (suggest-batch-reqs 대기)
- Releases: 미정
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: keep-current
- Follow-up Action: confirm-req 후 suggest-batch-reqs. REQ-052/REQ-054와 동일 batch 묶기 권고. OQ-2(경로)은 design 단계 해소.
#### Validation Plan
- pytest로 AC-1~12 각 케이스 확인. AC-4/5(owner 재할당 + 인증정보 무효화) 통합 시나리오, AC-8(DELETE 음성), AC-9(admin role 강제 음성) 강제 검증.
### CHG-20260628-02
- Date: 2026-06-28
- Author: 팀장(Claude)
- confirm-req: `Proposed` → `Approved`. dcy-011 FR-4/OQ-3 추적. 데몬 생성·할당·(비)활성화, **owner 재할당 시 인증정보 자동 무효화(AC-4/5)** 보안정합, **하드 삭제 미제공(AC-8)**·비활성화→service token revoke(REQ-022 재사용, AC-6)·admin role 강제(AC-9/10/11) 적절. 경로(OQ-2)·409/422·405/404 design 위임. blocker 없음.
@@ -0,0 +1,91 @@
# REQ-054: 인증정보 관리 보안 격리 — write-only·secret 미반환·로그 미노출
- Status: Approved
- Type: Non-Functional
- Priority: High
- Owner: Justin Ko
## Intent
- dcy-011 NFR-1(보안 최우선): 복호화된 `app_secret`/`app_key`(KIS API 비밀키 — 실계좌·실자금 직결)는 user-token/admin 어떤 관리 API 응답·로그에도 포함되어서는 안 된다.
- REQ-052(사용자 소유 스코프 인증정보 관리 API)와 REQ-053(admin 데몬 CRUD API)이 신설됨으로써 응답/로그 노출 표면이 확대된다. 이 표면에서 복호화 secret이 유출되지 않음을 독립적으로 측정·검증한다.
- `GET /v1/user_info`(service-token 전용, REQ-051/REQ-049)가 유일 decrypt-return 경로라는 불변 정책을 신규 관리 API 추가 이후에도 유지한다.
## Requirement
- 인증정보 관리 API(REQ-052: 소유 스코프 write/read/delete + 메타데이터, REQ-053: admin 데몬 CRUD) 응답 스키마에 `app_key` 및 `app_secret` 필드가 존재해서는 안 된다.
- 인증정보 관리 API 처리 중 생성되는 모든 로그(INFO, WARNING, ERROR 수준 포함)에 `app_secret` 또는 `app_key` 평문이 포함되어서는 안 된다. 로그에는 `broker` 이름, `daemon_id`, 오류 유형만 기록한다.
- 복호화된 `credential_payload`(`app_key`, `app_secret`)를 반환하는 유일 경로는 service-token 전용 `GET /v1/user_info`(REQ-051/REQ-049)이다. 이 REQ-052·REQ-053 신설로 인해 이 경로가 변경되어서는 안 된다.
- 인증정보 쓰기(등록/회전) 후 DB에 저장되는 값은 반드시 Fernet 암호화된 형태여야 한다. 평문 `app_secret`/`app_key`가 `broker_credentials` 테이블에 저장되어서는 안 된다.
## Acceptance Criteria
1. Given REQ-052 인증정보 write(등록/회전) 엔드포인트 응답 JSON, When 응답 필드를 열거, Then `app_key`, `app_secret` 키가 최상위 및 중첩 객체에 포함되지 않는다.
2. Given REQ-052 인증정보 메타데이터 GET 엔드포인트 응답 JSON, When 응답 필드를 열거, Then `app_key`, `app_secret` 키가 포함되지 않는다.
3. Given REQ-053 admin 데몬 생성·변경·(비)활성화 엔드포인트 응답 JSON, When 응답 필드를 열거, Then `app_key`, `app_secret`, `credential_payload` 키가 포함되지 않는다.
4. Given 인증정보 write/rotate 처리 중 caplog 수집, When 로그 출력을 확인, Then 실제 `app_key`/`app_secret` 값이 로그 메시지에 포함되지 않는다.
5. Given 인증정보 write 요청의 `app_key`/`app_secret` 값으로 DB row를 직접 조회, When `broker_credentials.credential_payload` 컬럼을 확인, Then 평문이 아닌 Fernet 암호화 토큰(`gAAAA...`) 형태로 저장되어 있다.
6. Given REQ-052 인증정보 write 후 service-token `GET /v1/user_info`(REQ-049)로 조회, When 호출, Then `broker.credential_payload.app_secret`이 올바르게 복호화되어 반환된다 — 관리 API 추가가 유일 decrypt 경로(REQ-051)를 파괴하지 않음 확인.
7. Given REQ-052·REQ-053 신규 엔드포인트가 추가된 후 기존 `GET /v1/user_info` 보안 격리 테스트(REQ-051 AC-1~8), When 회귀 실행, Then 전체 PASS — 신규 API 추가가 기존 service-token 전용 경로에 영향 없음 확인.
## Impacted Area
- Docs:
- `docs/interface-contract.md` — 인증정보 관리 API 보안 정책 섹션: 복호화 secret 미반환·로그 미노출·Fernet at-rest 불변·service-token 유일 decrypt 경로 명시.
- Modules:
- `app/schemas/` — REQ-052 응답 스키마(`CredentialMetaResponse`)에 `app_key`/`app_secret` 필드 부재 강제(Pydantic 모델 설계 단계 확인).
- `app/services/` — 인증정보 write 서비스에서 로그 호출 시 secret 평문 비포함 확인.
- `app/core/encryption.py` — Fernet 암호화 후 저장 경로(REQ-052 write) 재사용.
- Tests:
- caplog 기반 로그 secret 비노출 테스트(AC-4).
- 응답 스키마 secret 필드 부재 테스트(AC-1/2/3).
- DB 저장 암호화 확인 테스트(AC-5).
- REQ-051 회귀 테스트(AC-7): 신규 API 추가 후 기존 service-token gate 테스트 전부 PASS.
## Notes
- dcy-011 NFR-1(보안 — 최우선): REQ-052/REQ-053의 각 AC에 secret 부재 조건이 포함되어 있으나, 이 REQ-054는 신규 관리 API 전체를 아우르는 **독립 측정·검증 목적** Non-Functional REQ로 분리한다. REQ-051(서비스 토큰 전용 격리)과 나란히 추적한다.
- AC-7(REQ-051 회귀): REQ-052·REQ-053 구현 후 기존 service-token 보안 격리(REQ-051 AC-1~8)가 회귀 없이 통과함을 이 REQ의 검증 항목으로 포함한다.
- at-rest Fernet 암호화(AC-5): `CREDENTIAL_ENCRYPTION_KEY` 관리 정책은 REQ-051 Notes에 따라 이 REQ 범위 밖(환경변수 기반 유지).
- NFR-2(권한)·NFR-3(일관성)의 나머지는 REQ-052/REQ-053 AC에 흡수된다. NFR-1 보안만 별도 Non-Functional REQ로 승격한다.
- Source: dcy-011 NFR-1, R-1(복호화 secret 유출 리스크), S-2(복호화 secret 미포함 성공 기준).
## Change Log
### CHG-20260628-01
- Date: 2026-06-28
- Author: Justin Ko
#### Change Summary
- 신규 생성 (Proposed) — dcy-011 NFR-1 산출물.
#### Intent
- dcy-011 NFR-1(복호화 secret이 user-token/admin 응답·로그에 미포함, REQ-051 패턴)을 신규 관리 API(REQ-052·REQ-053) 표면 전체에 대한 독립 측정 가능 Non-Functional REQ로 정규화한다.
#### Acceptance Criteria Delta
- Added:
- AC-1 ~ AC-7 전체 (신규 초안)
- Updated: 없음
- Removed: 없음
#### Impacted Area Delta
- Docs:
- `docs/srs/Non-Functional/req-054_credential-management-security-write-only.md` 신규 생성
- `docs/interface-contract.md` 갱신 예고
- Modules:
- 응답 스키마(secret 필드 부재), 서비스 로그 확인
- Tests:
- caplog 로그 비노출(AC-4), 응답 schema secret 부재(AC-1~3), DB 암호화 확인(AC-5), REQ-051 회귀(AC-7) 신규
#### Delivery Trace Delta
- Batches: 미정 (suggest-batch-reqs 대기)
- Releases: 미정
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: keep-current
- Follow-up Action: confirm-req 후 suggest-batch-reqs. REQ-052/REQ-053와 동일 batch 묶기 권고.
#### Validation Plan
- pytest caplog fixture로 AC-4(로그 비노출), 응답 JSON 필드 열거로 AC-1~3, DB row 직접 확인으로 AC-5, REQ-051 회귀 suite로 AC-7 검증.
### CHG-20260628-02
- Date: 2026-06-28
- Author: 팀장(Claude)
- confirm-req: `Proposed` → `Approved`. dcy-011 NFR-1 추적. 신규 관리 API(REQ-052/053) 응답·로그 secret 미포함(AC-1~4)·Fernet at-rest(AC-5)·service-token 유일 decrypt 불변(AC-6)·REQ-051 회귀(AC-7) testable. REQ-051과 나란히 독립 보안 REQ 승격 타당. blocker 없음.
+6 -2
View File
@@ -74,7 +74,7 @@
## Next Requirement ID
- Current: `REQ-052`
- Current: `REQ-055`
- Rule: `Requirement Register`에 있는 가장 큰 REQ 번호보다 정확히 1 커야 한다.
## Requirement Register
@@ -132,11 +132,15 @@
| REQ-049 | GET /v1/user_info daemon-scoped 단일 broker 전달로 확장 | Interface | Implemented | High | Justin Ko | [req-049](./Interface/req-049_user-info-daemon-scoped-broker-delivery.md) |
| REQ-050 | credential CLI --daemon-id 옵션 추가 | Interface | Implemented | High | Justin Ko | [req-050](./Interface/req-050_credential-cli-daemon-id-option.md) |
| REQ-051 | 브로커 인증정보 보안 격리 — service-token 전용·web 토큰 차단 | Non-Functional | Implemented | High | Justin Ko | [req-051](./Non-Functional/req-051_broker-credential-security-isolation.md) |
| REQ-052 | 사용자 소유 스코프 인증정보 관리 HTTP API | Interface | Approved | High | Justin Ko | [req-052](./Interface/req-052_user-owned-credential-management-api.md) |
| REQ-053 | admin 데몬 CRUD HTTP API | Interface | Approved | High | Justin Ko | [req-053](./Interface/req-053_admin-daemon-crud-api.md) |
| REQ-054 | 인증정보 관리 보안 격리 — write-only·secret 미반환·로그 미노출 | Non-Functional | Approved | High | Justin Ko | [req-054](./Non-Functional/req-054_credential-management-security-write-only.md) |
## Recent Change Log Summary
- 2026-06-28: REQ-052/053/054 `Proposed` → `Approved` (confirm-req 팀장 승인) — dcy-011 추적·AC testable·blocker 0. 소유 스코프 음성(REQ-052 AC-6/9/10)·owner 재할당+인증정보 무효화(REQ-053 AC-4/5)·secret 미반환·로그 미노출·REQ-051 회귀(REQ-054) 확인. admin도 인증정보는 소유 스코프 전용(self-service 결정 정합). 엔드포인트 경로(OQ-1/2) design 위임. 다음=suggest-batch-reqs.
- 2026-06-28: REQ-052/053/054 신규 생성 (Proposed) — dcy-011 confirmed Discovery 산출물. REQ-052(사용자 소유 스코프 인증정보 관리 HTTP API), REQ-053(admin 데몬 CRUD HTTP API), REQ-054(인증정보 관리 보안 격리 Non-Functional).
- 2026-06-28: REQ-047 `Approved` → `Implemented` (confirm-req-implemented) — BAT-021 PR #18 머지(7164f9e), 실 dev PG 통합검증 AC-1~7 PASS(Step1→backfill→Step2→downgrade), QC PASS-WITH-NOTES(blocker 0). **Step2(PK 전환) 실 운영 DB 적용은 별도 release 결재 게이트**(런북 `docs/runbooks/bat-021-req047-step2-migration-runbook.md` 엄수, 배포순서 Step1→backfill→Step2→코드배포, alembic stamp 사전확인). 운영 적용 시 AC-3/4/5 실운영 evidence 재확보.
- 2026-06-28: REQ-046/048/049/050/051 `Approved` → `Implemented` (confirm-req-implemented) — BAT-020 PR #17 머지(d4acfd4), QC PASS-WITH-NOTES(blocker 0), 테스트 통과. REQ-047은 실 운영 DB 적용·실 PostgreSQL 통합검증이 비가역 사장님 결재 게이트 미완 → Approved 유지. AC-3/4/5(Step2 적용 후 schema) real-DB evidence 대기. 결재·적용 시 TODO: ensure_schema()·upsert ON CONFLICT를 (daemon_id,broker)로 갱신.
- 2026-06-28: REQ-046~051 `Proposed` → `Approved` (confirm-req 팀장 승인) — dcy-010 daemon-scoped broker credential. 전건 dcy-010 FR/NFR 추적·AC testable·blocker 0 확인. 단계적 마이그레이션(REQ-047 Step1/2)·**REQ-047 Step2 PK 전환은 비가역 결재 게이트**·2데몬 격리(REQ-049 AC-4/5)·보안 격리(REQ-051) 반영. 비차단 design 위임 2건(REQ-048 구현방식, REQ-050 slug/UUID·과도기 정책).
- 2026-06-27: REQ-025 name 검증 완화(CHG-20260627-03) — `_PORTFOLIO_NAME_RE` 한글(가-힣)+공백 허용(`^[a-zA-Z0-9가-힣 ._:\-]{1,100}$`). minor-change. 기존 AC 유지, 테스트 3개 추가.
- 2026-06-27: REQ-044/045 `Approved` → `Implemented` — BAT-019 PR #16 main 머지(`7f15c6b`) QC PASS. 472 passed/0 failed. REQ-045 env토큰(user_id=None) 처리 Notes 정정: "HTTP 403" → "빈 목록 [] HTTP 200"(change-req CHG-20260627-02).