494 lines
17 KiB
Markdown
494 lines
17 KiB
Markdown
---
|
|
{
|
|
"schema": "gitea-agent.requirement/v1",
|
|
"id": "REQ-001",
|
|
"title": "Dashboard 관리자 인증",
|
|
"revision": "1.0",
|
|
"status": "approved",
|
|
"depends_on": [],
|
|
"applies_to": ["dashboard"],
|
|
"language": "ko"
|
|
}
|
|
---
|
|
|
|
# REQ-001 — Dashboard 관리자 인증
|
|
|
|
## 1. 목적
|
|
|
|
Streamlit Dashboard의 모든 금융 데이터 조회·수정·import 기능 앞에 단일 관리자 인증 경계를 두고, 인증되지 않았거나 인증 설정이 유효하지 않은 요청을 fail-closed한다.
|
|
|
|
## 2. 배경
|
|
|
|
Dashboard는 SQLite 가계부를 직접 읽고 쓰며 거래 수정, 계좌 변경, PDF import와 import rollback을 제공한다. 기존 진입점은 인증 없이 DB 경로를 얻고 page view를 dispatch했으므로, 외부 노출 시 금융 데이터와 운영 기능을 보호할 애플리케이션 수준의 경계가 필요하다.
|
|
|
|
## 3. 범위
|
|
|
|
### 포함
|
|
|
|
- 모든 Dashboard 페이지의 공통 관리자 로그인과 로그아웃
|
|
- 환경변수 기반 단일 관리자 credential 검증
|
|
- 인증 전 DB·gateway·page dispatch 차단
|
|
- browser session 범위의 인증 상태
|
|
- 실패 로그인 최소 지연, 일반 오류와 인증 event 관찰 가능성
|
|
- Compose·환경 예시·운영 문서의 secret 주입 계약
|
|
|
|
### 제외
|
|
|
|
- 다중 사용자, 역할 기반 권한과 사용자별 데이터 분리
|
|
- 회원가입, 비밀번호 찾기와 credential 관리 UI
|
|
- OIDC, SSO, 외부 IdP와 persistent cookie
|
|
- SQLite schema와 가계부 도메인 모델 변경
|
|
- MCP endpoint 인증, TLS, Ingress와 네트워크 경계 전체 구현
|
|
|
|
## 4. 용어와 기준
|
|
|
|
- Dashboard: `dashboard/app.py`를 진입점으로 실행되는 Streamlit 애플리케이션
|
|
- 인증 전: 현재 Streamlit browser session의 인증 상태가 참이 아닌 상태
|
|
- credential source: 배포 secret이 주입하는 `JARCA_DASHBOARD_ADMIN_ID`와 `JARCA_DASHBOARD_ADMIN_PASSWORD` 환경변수
|
|
- browser session: Streamlit이 `st.session_state`를 격리하는 단일 클라이언트 session
|
|
- 정본 근거: Gitea Issue `#1`과 source revision `069d4c16fd84e871e8cd9dc8d9ae39b4fce4a6c6`
|
|
|
|
## 5. 요구사항
|
|
|
|
### REQ-001.01 — 공통 인증 경계
|
|
|
|
Dashboard는 어떤 DB 경로 취득, page dispatch, DB-backed 렌더링, gateway 호출, upload 또는 rollback 처리보다 먼저 인증을 판정해야 한다. 인증 전에는 로그인 화면만 렌더링해야 한다.
|
|
|
|
**영향 범위**
|
|
|
|
- 인터페이스: Streamlit Dashboard 진입점과 모든 page view
|
|
- 영속성: 인증 전에는 SQLite 읽기·쓰기를 수행하지 않음
|
|
- 후속 사용 주체: Dashboard 사용자와 page view
|
|
- 마이그레이션: 없음
|
|
- 호환성: 인증 성공 후 기존 page query parameter와 페이지 기능 유지
|
|
- 배포 전제 조건: 관리자 환경변수 두 개가 유효하게 주입됨
|
|
|
|
### REQ-001.02 — Credential 설정과 fail-closed
|
|
|
|
애플리케이션은 `JARCA_DASHBOARD_ADMIN_ID`와 `JARCA_DASHBOARD_ADMIN_PASSWORD`만 credential source로 읽어야 한다. Streamlit secrets 또는 다른 fallback을 사용하지 않으며, 어느 값이든 누락·빈 값·비문자열·읽기 오류이면 일반 설정 오류를 표시하고 인증을 허용하지 않아야 한다.
|
|
|
|
**영향 범위**
|
|
|
|
- 인터페이스: process environment
|
|
- 영속성: credential을 저장하지 않음
|
|
- 후속 사용 주체: Dashboard 인증 경계
|
|
- 마이그레이션: 없음
|
|
- 호환성: 두 환경변수를 설정하지 않은 기존 Dashboard 배포는 의도적으로 fail-closed
|
|
- 배포 전제 조건: 배포 secret의 환경변수 주입
|
|
|
|
### REQ-001.03 — Credential 검증과 비노출
|
|
|
|
제출된 관리자 ID와 password는 모두 비교하되 password를 포함한 검증은 constant-time primitive를 사용해야 한다. Credential 원문·verifier는 화면, 일반 오류, traceback, URL/query parameter, application log 또는 로그인 완료 후 `st.session_state`에 남지 않아야 한다.
|
|
|
|
**영향 범위**
|
|
|
|
- 인터페이스: 관리자 ID·password 입력 form
|
|
- 영속성: 없음
|
|
- 후속 사용 주체: 현재 Dashboard session
|
|
- 마이그레이션: 없음
|
|
- 호환성: 없음
|
|
- 배포 전제 조건: 평문 password secret은 repository가 아닌 secret store에서 제공
|
|
|
|
### REQ-001.04 — 실패 로그인 응답
|
|
|
|
잘못된 ID 또는 password의 각 제출은 제출 처리 시작부터 일반 오류 응답 완료까지 최소 1초가 걸려야 한다. 두 실패는 동일한 오류를 표시하고 DB·gateway·상태 변경을 수행하지 않아야 한다. 성공 로그인에는 실패 지연을 적용하지 않으며 애플리케이션 수준 잠금은 두지 않는다.
|
|
|
|
**영향 범위**
|
|
|
|
- 인터페이스: 로그인 제출과 오류 응답
|
|
- 영속성: 없음
|
|
- 후속 사용 주체: 미인증 Dashboard 사용자
|
|
- 마이그레이션: 없음
|
|
- 호환성: 없음
|
|
- 배포 전제 조건: 새 session·병렬 요청 우회를 제한할 별도 배포 경계 rate limiting 권고
|
|
|
|
### REQ-001.05 — Session과 로그아웃
|
|
|
|
인증 성공 상태는 현재 Streamlit browser session에서만 유지해야 한다. 같은 session의 rerun은 재로그인 없이 Dashboard를 사용할 수 있고, 명시적 로그아웃은 즉시 인증 상태를 폐기해야 하며 새 session은 다시 인증해야 한다.
|
|
|
|
**영향 범위**
|
|
|
|
- 인터페이스: `st.session_state`, 로그아웃 button
|
|
- 영속성: server-side 영속 session 없음
|
|
- 후속 사용 주체: 현재 browser session
|
|
- 마이그레이션: 없음
|
|
- 호환성: browser 재시작·새 session의 자동 로그인 없음
|
|
- 배포 전제 조건: 없음
|
|
|
|
### REQ-001.06 — 보호 범위와 기존 기능 호환성
|
|
|
|
인증 성공 후 요약, 자산/마감, 거래내역, 확인필요 큐, 미분류, 분류 규칙, 카테고리, 계좌와 Import를 포함한 기존 Dashboard 페이지를 사용할 수 있어야 한다. 이 인증은 Dashboard만 보호하며 MCP endpoint가 인증됐다고 간주해서는 안 된다.
|
|
|
|
**영향 범위**
|
|
|
|
- 인터페이스: 모든 Dashboard page view
|
|
- 영속성: 기존 SQLite schema와 데이터 계약 유지
|
|
- 후속 사용 주체: 인증된 Dashboard 사용자
|
|
- 마이그레이션: 없음
|
|
- 호환성: 인증 후 기존 Dashboard 동작 유지
|
|
- 배포 전제 조건: MCP는 별도의 네트워크 또는 후속 인증 요구사항으로 보호
|
|
|
|
### REQ-001.07 — 배포 설정과 운영 제약
|
|
|
|
환경 예시, Compose와 운영 문서는 두 credential 환경변수의 이름·용도·fail-closed 동작을 정의하되 실제 값을 저장하지 않아야 한다. 운영 문서는 1초 지연에 애플리케이션 잠금이 없고 새 session·병렬 요청으로 우회 가능하므로 배포 경계 rate limiting이 필요함을 명시해야 한다.
|
|
|
|
**영향 범위**
|
|
|
|
- 인터페이스: `.env`, Compose environment와 운영 문서
|
|
- 영속성: repository에 credential 값 저장 금지
|
|
- 후속 사용 주체: 배포 운영자
|
|
- 마이그레이션: 기존 Dashboard 배포에 secret 주입 필요
|
|
- 호환성: MCP container의 설정과 포트는 변경하지 않음
|
|
- 배포 전제 조건: secret manager 또는 동등한 안전한 환경변수 주입
|
|
|
|
### REQ-001.08 — 인증 관찰 가능성
|
|
|
|
설정 오류, 로그인 성공·실패와 로그아웃은 session-local correlation ID와 event 이름만 application log에 기록해야 한다. 관리자 ID, password, verifier, 제출 값과 query parameter를 진단 속성으로 기록하지 않아야 한다.
|
|
|
|
**영향 범위**
|
|
|
|
- 인터페이스: application log
|
|
- 영속성: correlation ID는 현재 session에만 저장
|
|
- 후속 사용 주체: 운영자
|
|
- 마이그레이션: 없음
|
|
- 호환성: 기존 금융 데이터 log 계약에 영향 없음
|
|
- 배포 전제 조건: log 수집기가 민감정보 비수집 계약을 유지
|
|
|
|
## 6. 제약과 불변조건
|
|
|
|
- 인증 전과 설정 오류·잘못된 credential 상태에서는 금융 DB와 gateway 상태가 변하지 않는다.
|
|
- Credential은 환경변수에서만 읽고 repository 문서·설정·test fixture에는 test-only 값 외의 실제 값을 기록하지 않는다.
|
|
- 인증 상태는 boolean 결과만 저장하며 credential 원문 또는 verifier를 session에 보존하지 않는다.
|
|
- 잘못된 ID와 password는 동일한 사용자 오류와 최소 지연을 사용한다.
|
|
- Dashboard와 MCP의 인증 경계를 혼동하지 않는다.
|
|
- 실제 운영 DB 또는 실제 credential을 test에 사용하지 않는다.
|
|
|
|
## 7. 인수 기준
|
|
|
|
### REQ-001.01-AC01 — 미인증 진입 차단
|
|
|
|
**판정 기준**
|
|
|
|
미인증 session은 로그인 화면만 표시하며 `get_db_path()`, page dispatch, DB·gateway·upload·rollback을 호출하지 않는다.
|
|
|
|
**검증**
|
|
|
|
`tests/test_dashboard_authentication_app.py`에서 인증 gate가 거부될 때 DB 경로 함수가 호출되지 않고 Dashboard 본문이 렌더링되지 않는지 검증한다.
|
|
|
|
**검증 분류**
|
|
|
|
- 수준: 통합
|
|
- 백엔드: Streamlit AppTest와 monkeypatch
|
|
- 케이스: 설정 누락, 미인증, 각 Dashboard query page
|
|
|
|
**상태 불변조건**
|
|
|
|
SQLite와 gateway 상태 무변경
|
|
|
|
### REQ-001.01-AC02 — Query parameter 우회 금지
|
|
|
|
**판정 기준**
|
|
|
|
`page=Import` 등 유효한 page query parameter를 제공해도 미인증 session은 해당 page를 렌더링하지 않는다.
|
|
|
|
**검증**
|
|
|
|
Streamlit AppTest로 Import query parameter를 설정하고 로그인 화면만 존재하는지 확인한다.
|
|
|
|
**검증 분류**
|
|
|
|
- 수준: 통합
|
|
- 백엔드: Streamlit AppTest
|
|
- 케이스: `page=Import`
|
|
|
|
**상태 불변조건**
|
|
|
|
Upload와 rollback 미호출
|
|
|
|
### REQ-001.02-AC01 — 환경변수 전용 설정
|
|
|
|
**판정 기준**
|
|
|
|
두 exact 환경변수가 있을 때만 설정을 생성하며 다른 이름이나 Streamlit secrets 값은 사용하지 않는다.
|
|
|
|
**검증**
|
|
|
|
`tests/test_dashboard_authentication.py`에서 환경 mapping과 무관한 secrets 형태 값을 함께 제공해도 exact 환경변수 값만 선택하는지 검증한다.
|
|
|
|
**검증 분류**
|
|
|
|
- 수준: 단위
|
|
- 백엔드: pytest
|
|
- 케이스: 두 값 존재, 대체 source 존재
|
|
|
|
**상태 불변조건**
|
|
|
|
Credential source 고정
|
|
|
|
### REQ-001.02-AC02 — 설정 오류 fail-closed
|
|
|
|
**판정 기준**
|
|
|
|
누락·빈 값·읽기 오류는 동일한 일반 설정 오류로 종료되고 Dashboard 기능을 노출하지 않는다.
|
|
|
|
**검증**
|
|
|
|
단위 test와 AppTest로 각 오류 조합과 로그인 input 미노출을 검증한다.
|
|
|
|
**검증 분류**
|
|
|
|
- 수준: 단위·통합
|
|
- 백엔드: pytest, Streamlit AppTest
|
|
- 케이스: ID 누락, password 누락, 빈 값, environment read error
|
|
|
|
**상태 불변조건**
|
|
|
|
기존 인증 boolean을 제거하고 DB 미호출
|
|
|
|
### REQ-001.03-AC01 — Constant-time 비교
|
|
|
|
**판정 기준**
|
|
|
|
ID와 password 검증이 모두 `hmac.compare_digest`를 호출하며 한 필드 실패로 다른 비교를 생략하지 않는다.
|
|
|
|
**검증**
|
|
|
|
비교 함수를 계측한 단위 test에서 성공·실패 모두 두 비교가 수행되는지 확인한다.
|
|
|
|
**검증 분류**
|
|
|
|
- 수준: 단위
|
|
- 백엔드: pytest monkeypatch
|
|
- 케이스: 정상 credential, 잘못된 두 필드
|
|
|
|
**상태 불변조건**
|
|
|
|
Password 일반 동등 비교 금지
|
|
|
|
### REQ-001.03-AC02 — Credential 비노출
|
|
|
|
**판정 기준**
|
|
|
|
로그인 완료 후 제출 ID와 password가 화면·log payload·query parameter 또는 session state에 남지 않는다.
|
|
|
|
**검증**
|
|
|
|
실패·성공 AppTest의 body와 session state 표현 및 log helper 단위 test를 검사한다.
|
|
|
|
**검증 분류**
|
|
|
|
- 수준: 단위·통합
|
|
- 백엔드: pytest, Streamlit AppTest
|
|
- 케이스: 성공, 잘못된 ID, 잘못된 password, 설정 오류
|
|
|
|
**상태 불변조건**
|
|
|
|
인증 boolean과 비민감 correlation ID만 session에 유지 가능
|
|
|
|
### REQ-001.04-AC01 — 실패별 최소 지연과 일반 오류
|
|
|
|
**판정 기준**
|
|
|
|
잘못된 ID와 잘못된 password 각각의 AppTest 제출은 1초 전에 완료되지 않고 같은 오류 문자열을 표시한다.
|
|
|
|
**검증**
|
|
|
|
두 실패 입력을 별도 제출하고 monotonic elapsed time과 화면 오류를 측정한다.
|
|
|
|
**검증 분류**
|
|
|
|
- 수준: 통합
|
|
- 백엔드: Streamlit AppTest, monotonic clock
|
|
- 케이스: 잘못된 ID, 잘못된 password
|
|
|
|
**상태 불변조건**
|
|
|
|
인증 false, DB·gateway 무변경
|
|
|
|
### REQ-001.04-AC02 — 성공 지연·잠금 없음
|
|
|
|
**판정 기준**
|
|
|
|
정상 credential은 실패 지연 함수를 사용하지 않고 인증되며, 실패 횟수나 잠금 만료 상태를 생성하지 않는다.
|
|
|
|
**검증**
|
|
|
|
단위 test에서 실패 지연의 remaining-time 계산을 검증하고 source·session state 검사에서 failure counter·lock state가 없음을 확인한다.
|
|
|
|
**검증 분류**
|
|
|
|
- 수준: 단위·정적
|
|
- 백엔드: pytest, source inspection
|
|
- 케이스: 정상 credential, 반복 실패 후 재시도
|
|
|
|
**상태 불변조건**
|
|
|
|
애플리케이션 잠금 없음
|
|
|
|
### REQ-001.05-AC01 — Session 유지와 새 session 재인증
|
|
|
|
**판정 기준**
|
|
|
|
로그인한 AppTest session은 rerun 후 기존 page를 유지하지만 새 AppTest session은 로그인 화면부터 시작한다.
|
|
|
|
**검증**
|
|
|
|
한 session의 로그인·rerun과 별도 session 생성을 연속 검증한다.
|
|
|
|
**검증 분류**
|
|
|
|
- 수준: 통합
|
|
- 백엔드: Streamlit AppTest
|
|
- 케이스: 동일 session rerun, 새 session
|
|
|
|
**상태 불변조건**
|
|
|
|
Session 간 인증 상태 격리
|
|
|
|
### REQ-001.05-AC02 — 로그아웃
|
|
|
|
**판정 기준**
|
|
|
|
로그아웃 click 직후 인증 boolean이 폐기되고 로그인 화면으로 돌아가며 Dashboard page가 표시되지 않는다.
|
|
|
|
**검증**
|
|
|
|
인증된 AppTest에서 로그아웃 button을 click하고 화면 전이를 확인한다.
|
|
|
|
**검증 분류**
|
|
|
|
- 수준: 통합
|
|
- 백엔드: Streamlit AppTest
|
|
- 케이스: 인증 후 로그아웃
|
|
|
|
**상태 불변조건**
|
|
|
|
인증 상태 폐기, 금융 데이터 무변경
|
|
|
|
### REQ-001.06-AC01 — 인증 후 기존 페이지 호환성
|
|
|
|
**판정 기준**
|
|
|
|
Synthetic SQLite fixture와 test-only credential로 인증한 뒤 기존 핵심 페이지가 예외 없이 예상 내용을 렌더링한다.
|
|
|
|
**검증**
|
|
|
|
`tests/test_dashboard_app.py`에서 요약, 자산/마감, 분류 규칙, 확인필요 큐와 월정산을 인증 후 검증한다.
|
|
|
|
**검증 분류**
|
|
|
|
- 수준: 회귀 통합
|
|
- 백엔드: Streamlit AppTest, synthetic SQLite
|
|
- 케이스: 기존 핵심 page 목록
|
|
|
|
**상태 불변조건**
|
|
|
|
기존 SQLite schema와 gateway contract 유지
|
|
|
|
### REQ-001.06-AC02 — MCP 범위 분리
|
|
|
|
**판정 기준**
|
|
|
|
Dashboard 인증 구현이 MCP server 진입점·transport·port를 변경하지 않고 운영 문서가 MCP 미보호 경계를 명시한다.
|
|
|
|
**검증**
|
|
|
|
Base-to-worktree diff와 README를 검사한다.
|
|
|
|
**검증 분류**
|
|
|
|
- 수준: 정적
|
|
- 백엔드: Git diff, 문서 검사
|
|
- 케이스: MCP source·Compose service
|
|
|
|
**상태 불변조건**
|
|
|
|
MCP 인증 상태 변경 없음
|
|
|
|
### REQ-001.07-AC01 — Secret 주입 계약
|
|
|
|
**판정 기준**
|
|
|
|
`.env.example`과 Dashboard Compose service에 두 exact 변수 이름이 있고 repository에는 실제 값이 없다.
|
|
|
|
**검증**
|
|
|
|
설정 파일 parsing과 base-to-worktree secret scan을 수행한다.
|
|
|
|
**검증 분류**
|
|
|
|
- 수준: 정적
|
|
- 백엔드: Python·YAML text validation
|
|
- 케이스: 환경 예시, Compose Dashboard environment
|
|
|
|
**상태 불변조건**
|
|
|
|
실제 credential 비보존
|
|
|
|
### REQ-001.07-AC02 — Rate limiting 운영 문서
|
|
|
|
**판정 기준**
|
|
|
|
README가 최소 1초 실패 지연, 애플리케이션 잠금 부재, 새 session·병렬 요청 우회와 배포 경계 rate limiting 필요성을 함께 설명한다.
|
|
|
|
**검증**
|
|
|
|
README 필수 문구 정적 검사를 수행한다.
|
|
|
|
**검증 분류**
|
|
|
|
- 수준: 문서
|
|
- 백엔드: text validation
|
|
- 케이스: Dashboard 운영 안내
|
|
|
|
**상태 불변조건**
|
|
|
|
애플리케이션 내부 잠금 추가 금지
|
|
|
|
### REQ-001.08-AC01 — 비민감 인증 event
|
|
|
|
**판정 기준**
|
|
|
|
설정 오류, 성공, 실패와 로그아웃 event가 correlation ID와 함께 기록되고 credential은 log argument에 포함되지 않는다.
|
|
|
|
**검증**
|
|
|
|
Logger를 계측한 단위 test와 source 검사를 수행한다.
|
|
|
|
**검증 분류**
|
|
|
|
- 수준: 단위·정적
|
|
- 백엔드: pytest monkeypatch
|
|
- 케이스: 인증 event logging
|
|
|
|
**상태 불변조건**
|
|
|
|
Log에 관리자 ID·password·verifier 없음
|
|
|
|
## 8. 오류 및 충돌 계약
|
|
|
|
- 설정 오류: `관리자 인증 설정을 확인할 수 없습니다.`만 표시하고 로그인 form과 Dashboard를 노출하지 않는다.
|
|
- Credential 오류: ID와 password 오류를 구분하지 않는 일반 오류를 최소 1초 후 표시한다.
|
|
- Issue 또는 base drift: 구현 workflow에서 자동 merge·rebase하지 않고 중단한다.
|
|
- 중복 로그인 제출: 각 제출을 독립적으로 검증하며 성공 후 현재 session의 인증 boolean만 유지한다.
|
|
- 로그아웃 중복 요청: 인증 상태가 없으면 로그인 화면을 유지하고 금융 상태를 변경하지 않는다.
|
|
|
|
## 9. 데이터 및 인터페이스 계약
|
|
|
|
- 입력: 환경변수 두 개, 로그인 form의 ID·password, 로그아웃 button
|
|
- 출력: 로그인 화면, 일반 설정/credential 오류, 인증된 기존 Dashboard, session-local 인증 boolean
|
|
- 비민감 session 데이터: 인증 boolean, login-error boolean, correlation ID
|
|
- 영속 데이터: 인증 기능은 새 DB table, column, migration 또는 credential store를 만들지 않는다.
|
|
- 비교: ID와 password 모두 `hmac.compare_digest`를 사용하며 두 비교를 수행한다.
|
|
|
|
## 10. 하위 호환성
|
|
|
|
인증 후 기존 Dashboard page query parameter, SQLite schema, gateway와 화면 기능을 유지한다. Dashboard는 새 credential 설정 없이는 의도적으로 fail-closed한다. MCP server API, transport와 인증 상태는 변경하지 않는다.
|
|
|
|
## 11. 추적성
|
|
|
|
- 근거 이슈: #1
|
|
- 구현 PR: 미정
|
|
- 관련 설계 문서: docs/interface-contract.md, docs/project-structure.md, docs/runtime-flow.md
|
|
- 관련 요구사항: 없음
|