feat: dcy-003/004 토큰 surface 개편 및 daemon lifecycle 정책 구현 (BAT-006/007)

BAT-006 (REQ-016/017/018):
- client token 발급 경로 제거 (issue_client_token, POST /auth/tokens, 관련 스키마/테스트)
- service-token rotate unique index revoked_at IS NULL 조건 추가 (Alembic CONCURRENTLY 마이그레이션)
- POST /auth/service-tokens body daemon_id forward 누락 보정 (1-line fix)

BAT-007 (REQ-022/023):
- daemon deactivate 시 바인딩 service token 자동 revoke (CLI --force 옵션 + revoke_tokens_by_daemon_id)
- NULL daemon_id legacy token 진단 CLI (migrate-null-daemon --dry-run)

거버넌스 문서: dcy-003/004 Discovery, REQ-016~023/025~032 신규, BAT-006/007 전체 산출물

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-21 01:03:39 +00:00
co-authored by Claude Sonnet 4.6
parent d4b841bc37
commit c815ce39b6
48 changed files with 3573 additions and 117 deletions
@@ -0,0 +1,46 @@
# BAT-007 Implementation
- Status: completed
- Batch: BAT-007
## Plan Summary
- REQ-022: revoke_tokens_by_daemon_id repository + revoke_tokens_for_daemon service + daemon CLI 연결
- REQ-023: list_service_tokens_with_null_daemon repository + 서비스 헬퍼 + migrate_null_daemon CLI
## Changed Files
### Production Code
1. `app/repositories/portfolio_repository.py` — 추상 메서드 2개 추가: `revoke_tokens_by_daemon_id`, `list_service_tokens_with_null_daemon`
2. `app/repositories/postgres.py` — 위 2개 메서드 Postgres 구현 추가
3. `app/services/auth_service.py` — 서비스 메서드 3개 추가: `revoke_tokens_for_daemon`, `list_null_daemon_service_tokens`, `find_candidate_daemons_for_user`
4. `app/cli/commands/daemon.py` — `update_daemon` 커맨드에 `--force` 옵션 + deactivate 시 service token 자동 revoke 연결
5. `app/cli/commands/service_tokens.py` — `migrate-null-daemon` 커맨드 추가 (dry-run only)
### Tests
6. `tests/test_daemon_registry.py` — `_DaemonRepo` fake에 `revoke_tokens_by_daemon_id` / `list_service_tokens_with_null_daemon` 추가; REQ-022 테스트 5건 추가
7. `tests/test_null_daemon_migration.py` — 신규 파일; REQ-023 AC-1~5 + candidate daemon 출력 테스트 6건
## Execution Log
1. `portfolio_repository.py`: 추상 메서드 2개 추가
2. `postgres.py`: `revoke_tokens_by_daemon_id` — `UPDATE auth_tokens SET revoked_at=NOW() WHERE daemon_id=%s AND revoked_at IS NULL`; `list_service_tokens_with_null_daemon` — `WHERE token_type='service' AND daemon_id IS NULL AND revoked_at IS NULL ORDER BY created_at ASC`
3. `auth_service.py`: 3개 메서드 위임 구현
4. `daemon.py`: `--force` / `--deactivate` / `--activate` 옵션 추가; deactivate 경로에서 `repo.revoke_tokens_by_daemon_id(daemon_id)` 호출 후 revoke 건수 출력
5. `service_tokens.py`: `migrate-null-daemon` 커맨드 — `--dry-run` 없으면 즉시 exit(1) + 수동 안내; `--dry-run` 시 NULL daemon token 목록 + user별 candidate daemon ID 출력
6. `test_daemon_registry.py`: `_DaemonRepo` fake 확장; 5개 테스트 추가
7. `test_null_daemon_migration.py`: `_FakeRepo` + `create_app(repository_factory=...)` 패턴으로 CLI runner 구성; 6개 테스트 추가
8. 수정 사항: `_make_runner`에서 `obj=ctx_obj` 전달 방식은 Typer `main_callback`이 `ctx.obj`를 덮어쓰므로 `create_app(repository_factory=...)` 방식으로 교체
## Validation
```
.venv/bin/python -m pytest -q
266 passed in 7.86s
```
- BAT-007 대상 테스트: 25개 (test_daemon_registry.py 19, test_null_daemon_migration.py 6) — 전부 PASSED
- 전체 회귀: 266 passed, 0 failed
## Remaining Risks
- Postgres 구현은 단위 테스트(fake repo) 기반 검증; PostgreSQL DB 통합 테스트는 별도 환경 필요
- `--deactivate` 시 revoke는 CLI 단계에서 수행 — API 경로에서 daemon deactivate가 발생할 경우 revoke 호출 별도 필요 (현재 API 경로 없음, 향후 추가 시 고려)