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
+21 -1
View File
@@ -188,7 +188,7 @@ class PostgresPortfolioRepository:
"""
CREATE UNIQUE INDEX IF NOT EXISTS idx_auth_tokens_service_name_unique
ON auth_tokens (name)
WHERE token_type = 'service'
WHERE token_type = 'service' AND revoked_at IS NULL
"""
)
cur.execute(
@@ -1952,6 +1952,26 @@ class PostgresPortfolioRepository:
row = cur.fetchone()
return self._row_to_daemon(row)
def revoke_tokens_by_daemon_id(self, daemon_id: str) -> int:
self.ensure_schema()
with self._connect().cursor() as cur:
cur.execute(
"UPDATE auth_tokens SET revoked_at = NOW() WHERE daemon_id = %s AND revoked_at IS NULL",
(daemon_id,),
)
return cur.rowcount
def list_service_tokens_with_null_daemon(self) -> list[AuthToken]:
self.ensure_schema()
with self._connect().cursor() as cur:
cur.execute(
_SELECT_AUTH_TOKEN
+ "WHERE token_type = 'service' AND daemon_id IS NULL AND revoked_at IS NULL "
+ "ORDER BY created_at ASC",
)
rows = cur.fetchall()
return [self._row_to_auth_token(row) for row in rows]
def database_exists(config: PortfolioDBConfig, *, maintenance_db: str = "postgres") -> bool:
with psycopg.connect(