17 KiB
schema, id, title, revision, status, depends_on, applies_to, language
| schema | id | title | revision | status | depends_on | applies_to | language | |
|---|---|---|---|---|---|---|---|---|
| gitea-agent.requirement/v1 | REQ-001 | Dashboard 관리자 인증 | 2.0 | approved |
|
ko |
REQ-001 — Dashboard 관리자 인증
1. 목적
Dashboard의 금융 기능 앞에 단일 fail-closed 관리자 인증 경계를 두고, 유효한 인증 cookie가 같은 browser profile의 새 연결에서 복원되도록 한다.
2. 배경
Issue #1의 관리자 인증을 Issue #6에서 persistent signed cookie와 명시적 logout으로 확장한다. 기존 요구사항의 정체성, 승인 범위, Issue #1과 구현 PR #3의 추적성을 유지하며 revision 2.0으로 갱신한다.
3. 범위
포함
- Dashboard 전 기능 앞의 인증 gate와 인증 전 DB 차단
JARCA_DASHBOARD_ADMIN_ID,JARCA_DASHBOARD_ADMIN_PASSWORD,JARCA_DASHBOARD_SIGNING_SECRET기반 server-only 검증- constant-time credential 비교, 일반 오류, 실패당 최소 1초 지연
- same-origin HTTPS login, cleanup, logout route
Secure/HttpOnly/SameSite=Lax/Path=/signed cookie와 24시간·30일 absolute TTL- 고정된 비민감 role subject와 설정 credential에 묶인 서명 검증
- invalid, expired, tampered, malformed, configuration-failure cookie의 cleanup 및 matching deletion
- password, signing-secret, admin-ID 변경에 따른 기존 token 거부
- HTTPS, proxy, Kubernetes/Compose 운영 제약과 민감정보 비노출
제외
- 다중 사용자, RBAC, OAuth/OIDC, SSO, MFA, CAPTCHA, 계정 잠금
- 외부 인증 proxy로 책임 이전
- sliding expiration, DB-backed session/token registry, per-token revocation
- MCP endpoint 인증 변경, SQLite schema/data migration
- 다른 browser profile·기기 간 로그인 공유
4. 용어와 기준
- role subject: token의
sub에 고정 기록하는 비민감 값dashboard-admin; 관리자 ID나 credential이 아니다. - server-only binding: signing secret과 현재 설정 credential을 서버에서만 읽어 key를 파생하며 browser에 값을 보내지 않는 방식.
- cleanup: 인증 실패 cookie를 같은 origin의 cleanup route에서 matching name/path/security 속성으로 삭제하는 절차.
- canonical 근거: Issue #1, Issue #6, 구현 PR #3 및 #7.
5. 요구사항
REQ-001.01 — 공통 인증 경계
모든 DB path, page dispatch, gateway, upload, import보다 먼저 설정과 cookie를 검증하고, 실패하면 로그인 화면만 표시한다.
영향 범위
- 인터페이스:
dashboard-auth-gate - 영속성: 없음
- 후속 사용 주체: Dashboard page와 gateway
- 마이그레이션: 없음
- 호환성: 인증 후 기존 page/query 동작 유지
- 배포 전제 조건: 유효한 세 환경변수
REQ-001.02 — 설정과 server-only binding
세 환경변수만 사용하고 누락·빈 값·read error·짧은 signing secret이면 fail-closed한다. signing key는 서버에서 configured credential과 secret으로만 파생한다.
영향 범위
- 인터페이스:
dashboard-auth-config,dashboard-auth-cookie - 영속성: 없음
- 후속 사용 주체: 인증 verifier
- 마이그레이션: 없음
- 호환성: Streamlit secrets/fallback 미사용
- 배포 전제 조건: secret manager 주입
REQ-001.03 — Credential과 민감정보 비노출
ID와 password는 constant-time 비교한다. credential, secret, signing material, token, raw cookie와 Set-Cookie 값은 UI, URL, log, 문서, session state에 기록하지 않는다.
영향 범위
- 인터페이스: login form, event log
- 영속성: 없음
- 후속 사용 주체: 운영자와 감사 도구
- 마이그레이션: 없음
- 호환성: 비민감 correlation ID만 유지
- 배포 전제 조건: redacted logging
REQ-001.04 — Login 실패 계약
ID 또는 password가 틀리면 동일한 일반 오류와 최소 1초 지연을 적용하고 상태를 변경하지 않는다. 성공에는 지연을 적용하지 않는다.
영향 범위
- 인터페이스:
POST /api/auth/login - 영속성: 없음
- 후속 사용 주체: browser
- 마이그레이션: 없음
- 호환성: 기존 login form 유지
- 배포 전제 조건: 외부 rate limiting
REQ-001.05 — Persistent token
성공 login은 기본 24시간, remember-me 선택 시 30일의 absolute TTL token을 발급한다. iat와 exp는 고정하며 활동·rerun·복원으로 연장하지 않는다. sub는 항상 dashboard-admin이다.
영향 범위
- 인터페이스:
dashboard-auth-cookie - 영속성: browser cookie만
- 후속 사용 주체: 새 Streamlit session
- 마이그레이션: 기존 session boolean에서 cookie로 전환
- 호환성: 같은 browser profile 복원
- 배포 전제 조건: HTTPS
REQ-001.06 — Cookie 보안 속성
Cookie 이름은 __Host-jarca_dashboard_auth이며 Secure, HttpOnly, SameSite=Lax, Path=/, no Domain을 사용한다. 일반 요청은 재발급하지 않는다.
영향 범위
- 인터페이스: HTTP response와 browser
- 영속성: browser cookie
- 후속 사용 주체: browser/Streamlit handshake
- 마이그레이션: 없음
- 호환성: host-only cookie
- 배포 전제 조건: 공개 origin HTTPS
REQ-001.07 — Invalid cookie cleanup
없음·malformed·oversized·tampered·expired·claim-invalid·rotation-invalid 또는 configuration-failure cookie는 보호 자원에 접근하기 전에 거부한다. 같은 origin cleanup route를 통해 동일 name/path/security 속성과 Max-Age=0 deletion을 반환한 뒤 unauthenticated page를 표시한다.
영향 범위
- 인터페이스:
GET /api/auth/cleanup,dashboard-auth-gate - 영속성: 서버 session/registry 없음
- 후속 사용 주체: browser
- 마이그레이션: 없음
- 호환성: stale cookie가 남지 않음
- 배포 전제 조건: same-origin HTTPS
REQ-001.08 — Login, cleanup, logout
Login과 logout은 trusted same-origin HTTPS POST만 허용하고 cleanup GET은 same-origin HTTPS Referer를 검증한다. logout은 명시적으로 matching deletion을 반환한다. cleanup과 logout은 금융 DB를 변경하지 않는다.
영향 범위
- 인터페이스:
POST /api/auth/login,GET /api/auth/cleanup,POST /api/auth/logout - 영속성: 없음
- 후속 사용 주체: browser
- 마이그레이션: 없음
- 호환성: root redirect 유지
- 배포 전제 조건: proxy가 Host/Origin/Proto 보존
REQ-001.09 — Rotation과 무상태성
Token claim은 v, 고정 sub, iat, exp, jti만 허용한다. password, signing secret 또는 admin ID 변경은 기존 token을 거부한다. per-token revocation이나 DB session은 만들지 않는다.
영향 범위
- 인터페이스: token verifier
- 영속성: revocation/session 저장소 없음
- 후속 사용 주체: 운영 rotation 절차
- 마이그레이션: 없음
- 호환성: 전체 무효화는 rotation으로 수행
- 배포 전제 조건: 모든 replica 동일 설정
REQ-001.10 — 보호 범위와 운영
인증 후 기존 page, query parameter, gateway와 MCP 동작을 유지한다. Compose/Kubernetes는 실제 secret을 저장하지 않으며 HTTPS, forwarded-header allowlist와 rate limit을 적용한다.
영향 범위
- 인터페이스: Dashboard 운영 진입점
- 영속성: 기존 SQLite만
- 후속 사용 주체: 운영 배포
- 마이그레이션: DB migration 없음
- 호환성: MCP endpoint는 별도 계약
- 배포 전제 조건: TLS와 제한된 proxy CIDR
REQ-001.11 — 관찰 가능성
설정 오류, 인증 필요, token 수락, login 성공/실패/reject, cleanup, logout event와 비민감 correlation ID만 기록한다. 민감 값과 구체적 검증 원인은 기록하지 않는다.
영향 범위
- 인터페이스: event log
- 영속성: 로그에 비민감 메타데이터만
- 후속 사용 주체: 운영 관찰 도구
- 마이그레이션: 없음
- 호환성: 일반 오류 메시지 유지
- 배포 전제 조건: redaction 정책
6. 제약과 불변조건
sub는 비민감 고정 role 값이며 credential에서 파생하거나 관리자 ID를 담지 않는다.- credential, secret, token, cookie 값은 저장·출력·문서화하지 않는다.
- 인증 성공 전 DB/gateway/page dispatch는 실행하지 않는다.
- 만료는 absolute이며 활동으로 연장하지 않는다.
- invalid cookie cleanup과 explicit logout의 deletion은 발급 cookie와 matching scope를 사용한다.
- 서버는 per-token revocation과 DB-backed session을 유지하지 않는다.
7. 인수 기준
REQ-001.01-AC01 — 인증 전 차단
판정 기준
모든 인증 거부 경로에서 DB, gateway, page dispatch가 실행되지 않는다.
검증
AppTest와 gate ordering 테스트에서 database access sentinel이 호출되지 않음을 확인한다.
검증 분류
- 수준: integration
- 백엔드: Streamlit AppTest
- 케이스: missing config, no cookie, invalid cookie
상태 불변조건
거부 시 금융 상태와 DB는 변경되지 않는다.
REQ-001.02-AC01 — 설정 fail-closed
판정 기준
세 설정 중 하나라도 유효하지 않으면 일반 설정 오류로 인증을 거부한다.
검증
설정별 누락·빈 값·짧은 secret 테스트를 실행한다.
검증 분류
- 수준: unit/integration
- 백엔드: pytest
- 케이스: 각 환경변수 누락, 빈 값, short secret
상태 불변조건
cookie 발급과 보호 자원 접근이 없다.
REQ-001.03-AC01 — 민감정보 비노출
판정 기준
credential, secret, token, cookie가 UI·URL·log·session state에 나타나지 않는다.
검증
로그·응답·session state와 문서를 redaction 검색한다.
검증 분류
- 수준: static/integration
- 백엔드: pytest, ruff, repository search
- 케이스: 성공·실패 login과 invalid cookie
상태 불변조건
비민감 correlation ID 외 인증 비밀 상태가 없다.
REQ-001.03-AC02 — 두 credential의 constant-time 비교
판정 기준
제출 ID와 password는 각각 hmac.compare_digest로 비교하며, 한 필드가 일치하지 않아도 다른 필드의 비교를 생략하지 않는다.
검증
tests/test_dashboard_authentication.py::test_verify_credentials_uses_constant_time_comparison_for_both_fields에서 성공 및 두 필드가 모두 잘못된 경우에 두 비교의 호출 순서와 인자를 계측한다.
검증 분류
- 수준: unit
- 백엔드: pytest monkeypatch
- 케이스: 정상 credential, 잘못된 ID와 password
상태 불변조건
일반 동등 비교나 한 필드 실패에 따른 short-circuit를 사용하지 않는다.
REQ-001.04-AC01 — 실패 지연과 일반 오류
판정 기준
잘못된 ID와 잘못된 password가 같은 일반 오류를 반환하고 각각 최소 1초 지연된다.
검증
두 실패 경로의 elapsed time과 response를 측정한다.
검증 분류
- 수준: integration
- 백엔드: pytest/HTTP client
- 케이스: wrong ID, wrong password
상태 불변조건
cookie·DB·gateway 상태가 변하지 않는다.
REQ-001.05-AC01 — Absolute TTL과 고정 subject
판정 기준
기본 24시간과 remember-me 30일 token의 iat, exp, sub=dashboard-admin이 고정된다.
검증
token claim, TTL, rerun/non-sliding 테스트를 실행한다.
검증 분류
- 수준: unit/integration
- 백엔드: pytest
- 케이스: normal, remember-me, activity, reconnect
상태 불변조건
복원은 token을 갱신하지 않는다.
REQ-001.06-AC01 — Hardened cookie
판정 기준
발급 cookie가 이름·Secure·HttpOnly·SameSite·Path·Domain 계약을 모두 만족한다.
검증
실제 HTTP Set-Cookie를 파싱하고 속성을 검사한다.
검증 분류
- 수준: integration
- 백엔드: Starlette TestClient와 loopback HTTPS
- 케이스: login normal/remember
상태 불변조건
Domain attribute가 없고 평문 전송에서 발급되지 않는다.
REQ-001.07-AC01 — Invalid cookie cleanup
판정 기준
invalid/expired/tampered/config-failure cookie는 보호 자원 전에 거부되고 same-origin cleanup route의 matching deletion 후 unauthenticated page가 표시된다.
검증
실제 browser/HTTP 흐름에서 각 invalid cookie와 cleanup response를 확인한다.
검증 분류
- 수준: integration/browser
- 백엔드: HTTPS probe와 Streamlit browser evidence
- 케이스: malformed, expired, tampered, wrong claims, config failure
상태 불변조건
cleanup은 DB를 변경하지 않으며 stale cookie가 남지 않는다.
REQ-001.08-AC01 — Explicit logout
판정 기준
logout POST가 matching Max-Age=0 deletion을 반환하고 다음 접근이 unauthenticated다.
검증
login 후 logout response와 fresh request를 확인한다.
검증 분류
- 수준: integration/browser
- 백엔드: HTTPS probe
- 케이스: same-origin logout, cross-origin rejection
상태 불변조건
금융 DB와 token registry는 변경되지 않는다.
REQ-001.09-AC01 — Rotation과 무상태성
판정 기준
configured credential 변경은 기존 token을 거부하며 per-token revocation/DB session 구현이 없다.
검증
rotation 테스트와 source/schema/diff 검색을 실행한다.
검증 분류
- 수준: unit/static
- 백엔드: pytest와 repository search
- 케이스: password, signing secret, admin ID rotation
상태 불변조건
검증은 현재 설정과 서명만 사용한다.
REQ-001.10-AC01 — 운영 제약
판정 기준
README와 Kubernetes 문서에 HTTPS, proxy allowlist, rate limit, secret 주입, rotation과 stateless logout이 기록된다.
검증
문서 검색과 deployment manifest 검토를 실행한다.
검증 분류
- 수준: documentation/static
- 백엔드: repository search
- 케이스: README, deploy/k8s/README.md
상태 불변조건
실제 credential·secret 값이 문서와 manifest에 없다.
REQ-001.10-AC02 — 인증 후 기존 페이지 호환성
판정 기준
유효한 Dashboard 인증 cookie로 인증하면 기존 page query parameter를 유지한 채 요약, 자산/마감, 분류 규칙, 확인필요 큐와 월정산 핵심 페이지가 예외 없이 예상 내용을 렌더링한다.
검증
tests/test_dashboard_app.py::test_streamlit_apptest_core_pages_use_synthetic_fixture_db와 tests/test_dashboard_authentication_app.py::test_valid_cookie_allows_dashboard_and_is_not_stored_in_session_state를 실행한다.
검증 분류
- 수준: regression integration
- 백엔드: Streamlit AppTest, synthetic SQLite
- 케이스: 인증된 핵심 page query와 기존 gateway
상태 불변조건
기존 SQLite schema, page query와 gateway contract를 변경하지 않는다.
REQ-001.10-AC03 — MCP 인증 범위 분리
판정 기준
Dashboard cookie 인증은 MCP endpoint의 인증을 의미하지 않으며 MCP server 진입점, transport와 port를 변경하지 않는다.
검증
Base-to-head diff에서 MCP 인증·진입점·transport·port를 담당하는 jarca_ledger_mcp/server.py, jarca_ledger_mcp/adapters/mcp/**, tests/test_mcp_http_runtime.py의 불변성을 확인하고 기존 stdio, streamable HTTP와 invalid transport 회귀 test를 실행한다.
검증 분류
- 수준: static/regression
- 백엔드: Git diff, pytest
- 케이스: MCP source, stdio, streamable HTTP, invalid transport
상태 불변조건
Dashboard와 MCP 인증 경계를 결합하지 않으며 MCP 인증 상태를 변경하지 않는다.
REQ-001.11-AC01 — 안전한 관찰성
판정 기준
허용 event와 correlation ID만 기록되고 민감 값·구체적 검증 원인은 기록되지 않는다.
검증
로그 테스트와 redaction 검색을 실행한다.
검증 분류
- 수준: unit/static
- 백엔드: pytest와 repository search
- 케이스: config, login, cleanup, logout success/failure
상태 불변조건
로그에서 credential·secret·token·cookie가 복원되지 않는다.
8. 오류 및 충돌 계약
동일 요청에서 설정 오류가 credential 오류보다 우선하며, 모든 인증 실패는 보호 자원 전에 종료된다. invalid cookie는 cleanup deletion을 시도하고, cleanup route가 거부되거나 실패해도 보호 자원은 열지 않는다. cross-origin/plain HTTP 요청은 403, malformed request는 400이다. 동시 login은 application lock 없이 독립 처리한다.
9. 데이터 및 인터페이스 계약
Token은 v, sub=dashboard-admin, iat, exp, jti와 서명만 가지며 browser cookie에만 저장된다. 세 환경변수는 server process에서만 읽는다. 상세 HTTP, cookie, cleanup, logout 계약은 docs/interface-contract.md에 정의한다.
10. 하위 호환성
Issue #1의 Dashboard-only 범위, 기존 page/query/gateway/MCP 동작과 구현 PR #3의 추적성을 유지한다. session-only 인증은 persistent cookie로 대체하며 SQLite schema와 MCP transport는 변경하지 않는다.
11. 추적성
- 근거 이슈:
#1,#6 - 구현 PR:
#3,#7 - 관련 설계 문서:
docs/interface-contract.md,docs/project-structure.md,docs/runtime-flow.md,README.md,deploy/k8s/README.md - 관련 요구사항: 없음