--- { "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: #3 - 관련 설계 문서: docs/interface-contract.md, docs/project-structure.md, docs/runtime-flow.md - 관련 요구사항: 없음