fix(dashboard): address persistent auth review

This commit is contained in:
2026-10-02 03:02:23 +00:00
parent 91e106d75c
commit 7bb959859a
11 changed files with 885 additions and 347 deletions
+81 -66
View File
@@ -1,86 +1,101 @@
# Project Structure — Dashboard Persistent Authentication
# Project Structure — Dashboard 관리자 인증
## 목적과 범위
## 목적과 경계
Issue #6의 signed persistent authentication을 기존 Dashboard page 구조와 분리하여 배치하는 규칙을 정의한다. DB schema와 MCP 구조는 변경하지 않는다.
Issue #6 인증을 기존 Dashboard 구조에 배치하고 fail-closed 경계와 server-only secret binding을 유지한다.
## Source 영역
- 포함 경계: `dashboard/` 인증 adapter/gate, tests, 운영 문서
- 제외 경계: MCP transport, 금융 schema, DB migration, external auth middleware
| 경로 | 책임 | 주요 공개 요소 | 금지 의존성 |
|---|---|---|---|
| `dashboard/server.py` | Streamlit ASGI app과 인증 route 조립 | `app` | DB/gateway 직접 접근 |
| `dashboard/auth_server.py` | same-origin login/logout HTTP response와 hardened cookie | `create_auth_routes()` | page view, SQLite |
| `dashboard/authentication.py` | 설정, credential 비교, token 발급·검증, Streamlit gate | `require_dashboard_authentication()` | 금융 DB/gateway |
| `dashboard/app.py` | gate 성공 후 기존 page dispatch | `main()` | token signing 세부 구현 |
| `dashboard/common.py` | 기존 DB/UI helper | 기존 API | 인증 결정 |
| `dashboard/page_views/` | 기존 금융 화면 | `page_*` | 인증 transport |
## 디렉터리 구조
`dashboard.server:app`이 운영 진입점이다. 인증 endpoint는 Streamlit 1.58의 public ASGI `App(routes=...)` 확장 지점에 같은 process로 결합한다.
### dashboard-source — Dashboard source
## Test 영역
```text
dashboard/
├── server.py
├── auth_server.py
├── authentication.py
├── app.py
└── page_views/
```
| 경로 | 검증 책임 |
|---|---|
| `tests/test_dashboard_authentication.py` | 기존 credential 비교·지연·safe log |
| `tests/test_dashboard_persistent_authentication.py` | token claims, TTL, tamper, rotation, malformed input |
| `tests/test_dashboard_auth_server.py` | login/logout response, cookie 속성, same-origin, fail-closed |
| `tests/test_dashboard_authentication_app.py` | Streamlit gate, DB 이전 차단, cookie 복원·rotation |
| `tests/test_dashboard_app.py` | 인증 이후 기존 핵심 page regression |
| `scripts/persistent_auth_http_evidence.py` | 실제 loopback HTTPS cookie/reconnect/logout evidence |
| 경로 | 종류 | 책임 | 공개 진입점 | 내부 구현 경계 | 편집 정책 | 생성 주체 |
|---|---|---|---|---|---|---|
| `dashboard/server.py` | source | ASGI app 조립 | `app` | route wiring only | source review required | developer |
| `dashboard/auth_server.py` | source | login/cleanup/logout response | `create_auth_routes()` | no DB/page imports | source review required | developer |
| `dashboard/authentication.py` | source | config, constant-time compare, token verify, gate | `require_dashboard_authentication()` | server-only secret use | source review required | developer |
| `dashboard/app.py` | source | gate then page dispatch | `main()` | no token signing details | source review required | developer |
| `dashboard/page_views/` | source | financial UI pages | `page_*` functions | no auth transport calls | source review required | developer |
Test는 synthetic DB와 test-only credential/secret만 사용하고 token·secret 원문을 출력하지 않는다.
### verification-tests — 인증 검증
## Config·배포·문서 영역
```text
tests/
├── test_dashboard_authentication.py
├── test_dashboard_persistent_authentication.py
├── test_dashboard_auth_server.py
└── test_dashboard_authentication_app.py
```
| 경로 | 책임 |
|---|---|
| `pyproject.toml`, `uv.lock` | `itsdangerous`, `starlette`, `uvicorn` 직접 dependency 고정 |
| `.env.example` | 세 environment 변수의 이름만 제공 |
| `docker-compose.yml` | signing secret 전달과 ASGI server command |
| `deploy/k8s/base/deployment.yaml` | Kubernetes Secret `signing-secret` 전달과 ASGI command |
| `README.md`, `deploy/k8s/README.md` | HTTPS, rotation, expiry, rate-limit, logout 제약 |
| `docs/requirements/req-001_dashboard-administrator-authentication.md` | Issue #1·#6 통합 canonical 승인 명세 |
| `docs/interface-contract.md` | HTTP/cookie/token 계약 |
| `docs/runtime-flow.md` | 발급·복원·실패·logout 순서 |
| 경로 | 종류 | 책임 | 공개 진입점 | 내부 구현 경계 | 편집 정책 | 생성 주체 |
|---|---|---|---|---|---|---|
| `tests/test_dashboard_authentication.py` | test | credential compare, delay, redaction | pytest tests | test-only settings | source change paired with test | developer |
| `tests/test_dashboard_persistent_authentication.py` | test | claims, TTL, tamper, rotation | pytest tests | no real secrets | source change paired with test | developer |
| `tests/test_dashboard_auth_server.py` | test | HTTP routes and cookie scope | pytest tests | synthetic test credentials | source change paired with test | developer |
| `tests/test_dashboard_authentication_app.py` | test | gate ordering and cleanup/UI | AppTest | fixture DB only | source change paired with test | developer |
### deployment-docs — 운영 문서
```text
docs/requirements/req-001_dashboard-administrator-authentication.md
docs/interface-contract.md
docs/runtime-flow.md
README.md
deploy/k8s/README.md
```
| 경로 | 종류 | 책임 | 공개 진입점 | 내부 구현 경계 | 편집 정책 | 생성 주체 |
|---|---|---|---|---|---|---|
| `docs/requirements/req-001_dashboard-administrator-authentication.md` | requirement | approved REQ-001 revision 2.0 | REQ-001 | canonical traceability | update in place | developer |
| `docs/interface-contract.md` | contract | HTTP/cookie/gate contracts | contract IDs | no implementation secrets | update with API changes | developer |
| `docs/runtime-flow.md` | design | runtime states/transitions | flow IDs | no secret values | update with flow changes | developer |
| `README.md` | operations | local/deployment constraints | operator commands | no secret values | operational review | developer |
| `deploy/k8s/README.md` | operations | k3s secret/TLS procedure | deployment procedure | no secret values | deployment review | developer |
## 의존 방향
```text
browser
├─ POST /api/auth/login|logout → dashboard.auth_server
└─ Streamlit handshake cookie → dashboard.authentication
dashboard.server → dashboard.auth_server + Streamlit App(dashboard/app.py)
dashboard.app → dashboard.authentication → itsdangerous
→ (인증 성공 후) dashboard.common/page_views
```
`auth_server`와 `authentication`은 금융 DB를 import하지 않는다. Page module은 cookie와 token module을 직접 호출하지 않는다.
## 상태·데이터 배치
- Credential/signing secret: process environment에서 request/rerun 동안만 참조
- Auth token: browser의 `Secure`/`HttpOnly` cookie에만 저장
- Correlation ID: 비민감 값으로 `st.session_state`에 저장 가능
- 금융 데이터: 기존 SQLite
- Token registry/revocation table/migration: 생성하지 않음
따라서 `docs/data-model.md`는 추가하지 않는다.
| 출발 영역 | 대상 영역 | 허용 여부 | 규칙 | 공유 계약 위치 |
|---|---|---|---|---|
| `dashboard.server` | `dashboard.auth_server` | 허용 | compose ASGI routes only | `docs/interface-contract.md` |
| `dashboard.server` | `dashboard.app` | 허용 | app remains protected by gate | `docs/runtime-flow.md` |
| `dashboard.app` | `dashboard.authentication` | 허용 | gate precedes DB/page dispatch | `docs/interface-contract.md` |
| `dashboard.authentication` | `dashboard.page_views` | 금지 | auth module never imports financial pages | `docs/project-structure.md` |
| `dashboard.auth_server` | `jarca_ledger_mcp` DB | 금지 | login/cleanup/logout are DB-free | `docs/requirements/req-001_dashboard-administrator-authentication.md` |
## 변경 규칙
- 인증 환경변수는 `JARCA_DASHBOARD_` prefix를 사용한다.
- Cookie name은 host-only scope를 강제하는 `__Host-` prefix를 사용한다.
- Token schema/TTL/cookie attributes 변경은 REQ-001과 interface contract를 함께 변경한다.
- Secret, token, cookie header, credential 값을 source·fixture·log·문서에 기록하지 않는다.
- Streamlit private Tornado/WebSocket internals나 client-side cookie component를 사용하지 않는다.
| 구분 | 규칙 |
|---|---|
| 새 모듈·파일 배치 | auth route/gate changes stay in `dashboard/`; no DB session module |
| 이름·패키지 | use existing `dashboard` package and snake_case modules |
| 소스·테스트 대응 | auth source behavior requires focused pytest/AppTest coverage |
| 생성 산출물 | evidence may be redacted; never commit credentials, secrets, tokens, or cookie values |
## 검증
```text
uv run --extra dashboard --extra dev pytest
uv run --extra dashboard --extra dev ruff check dashboard tests scripts
uv run python -m compileall -q dashboard tests scripts
```
- 구조 검사 명령:
Diff에서 DB migration과 `docs/data-model.md`가 추가되지 않았는지 확인한다.
```text
python -m compileall -q dashboard tests scripts
```
- 구조 검사 기대 결과: exit 0 and no generated source outside ignored bytecode locations
- 정적 분석 명령:
```text
uv run --extra dashboard --extra dev ruff check dashboard tests scripts
```
- 정적 분석 기대 결과: exit 0 with no diagnostics
- 문서와 실제 트리의 정합성: every listed source/test/document path exists; dependency table has no forbidden reverse edge