forked from baron/baron-sso
Merge commit 'ac778f836fb78550dce8088a567dc8bf5ffb8d2e' into feature/adminfront
This commit is contained in:
@@ -48,10 +48,19 @@
|
||||
- 환경 변수 추가/변경 시
|
||||
- `.env.sample` 반영
|
||||
- 문서/가이드 갱신
|
||||
- 클라이언트 로그 정책 영향 확인 (`CLIENT_LOG_DEBUG`, `USERFRONT_DEBUG_LOG`)
|
||||
|
||||
- 배포/운영 변경 시
|
||||
- `Makefile`/compose 실행 절차 영향 확인
|
||||
- 최소 Smoke 테스트 수행
|
||||
- 로그 수집 레벨이 운영 기본 정책(`WARN/ERROR`)을 유지하는지 확인
|
||||
|
||||
## 클라이언트 로그 정책
|
||||
- 상세 정책은 `docs/client-log-policy.md`를 기준으로 유지합니다.
|
||||
- 원칙:
|
||||
- 운영 기본값은 `WARN/ERROR`만 수집
|
||||
- 운영 디버그는 `CLIENT_LOG_DEBUG=true`로만 일시 허용
|
||||
- 민감정보 마스킹은 환경과 무관하게 항상 적용
|
||||
|
||||
## 테스트 참고
|
||||
- 테스트 계획 및 수동 실행 기준은 `docs/test-plan.md`를 따른다.
|
||||
|
||||
67
docs/client-log-policy.md
Normal file
67
docs/client-log-policy.md
Normal file
@@ -0,0 +1,67 @@
|
||||
# Client Log Policy
|
||||
|
||||
## 1. 목적
|
||||
- 운영 환경에서 클라이언트 로그를 최소권한으로 수집하고, 민감정보 유출을 방지합니다.
|
||||
- 장애 분석이 필요한 경우에만 명시적 디버그 옵션으로 로그 레벨을 확장합니다.
|
||||
|
||||
## 2. 환경별 수집 정책
|
||||
|
||||
### 2.1 기준 변수
|
||||
- `APP_ENV`
|
||||
- `CLIENT_LOG_DEBUG`
|
||||
- (UserFront fallback) `USERFRONT_DEBUG_LOG`
|
||||
|
||||
### 2.2 동작 매트릭스
|
||||
- `APP_ENV != production|prod`
|
||||
- 클라이언트 로그: `DEBUG/INFO/WARN/ERROR` 수집 허용
|
||||
- `APP_ENV == production|prod` AND `CLIENT_LOG_DEBUG` 미설정
|
||||
- 클라이언트 로그: `WARN/ERROR`만 수집
|
||||
- `INFO` 네비게이션 노이즈 로그는 필터
|
||||
- `APP_ENV == production|prod` AND `CLIENT_LOG_DEBUG=true|1|on|yes`
|
||||
- 클라이언트 로그: `DEBUG/INFO/WARN/ERROR` 수집 허용
|
||||
|
||||
## 3. 민감정보 마스킹 규칙
|
||||
|
||||
### 3.1 Key 기반 마스킹
|
||||
아래 키는 값 전체를 `*****`로 치환합니다.
|
||||
- `password`, `currentPassword`, `newPassword`, `oldPassword`
|
||||
- `token`, `accessToken`, `refreshToken`
|
||||
- `secret`, `clientSecret`
|
||||
- `authorization`, `cookie`, `setCookie`
|
||||
- `verificationCode`, `code`
|
||||
- `loginChallenge`, `loginVerifier`
|
||||
- `sessionJwt`, `accessJwt`, `refreshJwt`
|
||||
|
||||
### 3.2 문자열 패턴 마스킹
|
||||
메시지 본문에서도 아래 패턴을 마스킹합니다.
|
||||
- `token=...`
|
||||
- `authorization:...` 또는 `authorization=...`
|
||||
- JSON 문자열 내 민감 key/value
|
||||
|
||||
## 4. 구현 위치
|
||||
- Backend
|
||||
- 정책/마스킹 로직: `backend/internal/logger/client_log_policy.go`
|
||||
- 수집 엔드포인트 적용: `backend/cmd/server/main.go` (`POST /api/v1/client-log`)
|
||||
- UserFront
|
||||
- 정책/마스킹 로직: `userfront/lib/core/services/log_policy.dart`
|
||||
- 로그 출력/전송 정책: `userfront/lib/core/services/logger_service.dart`
|
||||
- 전송 직전 마스킹: `userfront/lib/core/services/auth_proxy_service.dart`
|
||||
|
||||
## 5. 검증
|
||||
|
||||
### 5.1 Backend
|
||||
```bash
|
||||
cd backend
|
||||
go test ./internal/logger -count=1
|
||||
go test ./cmd/server -count=1
|
||||
```
|
||||
|
||||
### 5.2 UserFront
|
||||
```bash
|
||||
cd userfront
|
||||
flutter test test/log_policy_test.dart
|
||||
```
|
||||
|
||||
## 6. 운영 가이드
|
||||
- 운영에서 디버그 로그가 필요하면 `CLIENT_LOG_DEBUG=true`를 명시적으로 설정하고, 이슈 해결 후 즉시 원복합니다.
|
||||
- 운영에서도 민감정보 마스킹은 항상 강제되며 비활성화할 수 없습니다.
|
||||
@@ -13,6 +13,7 @@
|
||||
- Backend(Go): **104개**
|
||||
- UserFront(Flutter): **47개**
|
||||
- AdminFront/DevFront(Playwright): **4개**
|
||||
- UserFront WASM Playwright E2E: **42개**
|
||||
|
||||
### Backend 패키지별 커버리지
|
||||
- `cmd/server`: 2.6%
|
||||
@@ -29,6 +30,7 @@
|
||||
- UserFront 테스트 전수 목록: `docs/test-plan/userfront-test-inventory.md`
|
||||
- AdminFront/DevFront E2E 전수 목록: `docs/test-plan/web-e2e-test-inventory.md`
|
||||
- UserFront WASM Playwright E2E 확장 계획: `docs/test-plan/userfront-wasm-e2e-expansion-plan.md`
|
||||
- UserFront WASM Playwright E2E 전수 목록: `docs/test-plan/userfront-wasm-e2e-route-inventory.md`
|
||||
|
||||
## 4) 실행 커맨드
|
||||
- Backend 전체 테스트: `cd backend && go test ./...`
|
||||
@@ -36,7 +38,8 @@
|
||||
- UserFront 테스트: `cd userfront && flutter test`
|
||||
- AdminFront E2E: `cd adminfront && npm test`
|
||||
- DevFront E2E: `cd devfront && npm test`
|
||||
- UserFront WASM E2E(계획): `docs/test-plan/userfront-wasm-e2e-expansion-plan.md` 기준으로 Playwright 워크스페이스를 추가한 뒤 실행
|
||||
- UserFront WASM E2E: `cd userfront-e2e && npm run test:wasm`
|
||||
- UserFront WASM E2E(테스트만): `cd userfront-e2e && npm test`
|
||||
|
||||
## 5) 유지 원칙
|
||||
- 신규 기능은 관련 테스트를 반드시 추가합니다.
|
||||
|
||||
@@ -58,12 +58,46 @@
|
||||
- 범위 6 구현
|
||||
- null-check 복구 라우팅 검증
|
||||
|
||||
## 4) 완료 기준
|
||||
## 4) 현재 구현 상태 (2026-02-24)
|
||||
- Phase 0: 완료
|
||||
- `userfront-e2e/` 워크스페이스 추가
|
||||
- 로컬 SPA fallback 서버(`scripts/serve-userfront-build.mjs`) 추가
|
||||
- 실행 커맨드: `cd userfront-e2e && npm run test:wasm`
|
||||
- CI 잡 연결: `.gitea/workflows/code_check.yml`의 `userfront-e2e-tests`
|
||||
- Phase 1: 완료
|
||||
- `tests/auth-routing.spec.ts` 추가
|
||||
- 구현 시나리오:
|
||||
- 비로그인 `/ko` → `/ko/signin` 리다이렉트
|
||||
- 로그인 상태 `/ko` 진입 + 새로고침 후 `/ko/dashboard` 유지
|
||||
- 비로그인 `/ko/approve?ref=...` 진입 시 `notice=qr_login_required`와 함께 signin 이동
|
||||
- 로그인 상태 `/ko/approve?ref=...`에서 approve API 호출 후 dashboard 이동
|
||||
- Phase 2: 완료
|
||||
- `tests/password-and-reset.spec.ts` 추가
|
||||
- 구현 시나리오:
|
||||
- 비밀번호 로그인 성공 시 dashboard 이동 + 토큰 저장 확인
|
||||
- 비밀번호 로그인 실패 시 코드 기반 에러(`password_or_email_mismatch`)가 client-log로 기록되는지 확인
|
||||
- reset-password 성공 시 signin 이동 확인
|
||||
- 참고:
|
||||
- WASM 렌더링에서는 접근성/DOM selector가 제한되어 로그인/리셋 폼은 `flt-glass-pane` 좌표 기반 입력으로 검증
|
||||
- 전수 인벤토리:
|
||||
- `docs/test-plan/userfront-wasm-e2e-route-inventory.md`
|
||||
- 라우트 22개 + 기능 회귀 12개(총 42 테스트) 코드화 완료
|
||||
- 프로필 소속 회귀 강화:
|
||||
- `tests/profile-department.spec.ts` 추가
|
||||
- 구현 시나리오:
|
||||
- 소속 수정 후 blur 저장 요청 전송
|
||||
- 입력 후 즉시 새로고침 시 저장 요청 미전송 재현
|
||||
- 동일값/빈값 입력 시 저장 요청 미전송
|
||||
- 수정 후 새로고침 뒤 재수정 저장 요청 누락 방지
|
||||
|
||||
## 5) 완료 기준
|
||||
- 핵심 인증 플로우(로그인/새로고침/리다이렉트/QR)가 Playwright 회귀군으로 자동화됩니다.
|
||||
- 프로덕션 이슈 재발 건은 재현 테스트가 먼저 추가됩니다.
|
||||
- PR에서 E2E 결과 링크(성공/실패 로그) 확인이 가능합니다.
|
||||
|
||||
## 5) 운영 원칙
|
||||
## 6) 운영 원칙
|
||||
- 버그는 반드시 재현 테스트를 먼저 추가합니다.
|
||||
- 재현 테스트가 실패하는 상태를 확인한 뒤 수정합니다.
|
||||
- 수정 후 동일 테스트를 반복 실행해 안정 통과까지 완료합니다.
|
||||
- 테스트 하네스는 단계별로 초기화/정리합니다.
|
||||
- 예: `beforeEach`에서 auth/mock state 재시드, `afterEach`에서 route mock 해제(`page.unroute`) 및 누수 상태 정리
|
||||
|
||||
59
docs/test-plan/userfront-wasm-e2e-route-inventory.md
Normal file
59
docs/test-plan/userfront-wasm-e2e-route-inventory.md
Normal file
@@ -0,0 +1,59 @@
|
||||
# UserFront WASM E2E 라우트/기능 전수 인벤토리
|
||||
|
||||
- 기준 소스: `userfront/lib/main.dart`
|
||||
- 목적: 라우트 전수 항목을 Playwright 테스트로 코드화하고 CI에서 상시 검증
|
||||
- 현재 구현 파일:
|
||||
- `userfront-e2e/tests/route-inventory.spec.ts`
|
||||
- `userfront-e2e/tests/auth-routing.spec.ts`
|
||||
- `userfront-e2e/tests/password-and-reset.spec.ts`
|
||||
- `userfront-e2e/tests/profile-department.spec.ts`
|
||||
|
||||
## 1) 라우트 전수 (main.dart 기준)
|
||||
|
||||
| ID | Route | 검증 상태 | 테스트 파일 |
|
||||
|---|---|---|---|
|
||||
| R01 | `/` | 비로그인 시 `/{locale}/signin` 리다이렉트 검증 완료 | `userfront-e2e/tests/route-inventory.spec.ts` |
|
||||
| R02 | `/:locale` (`/ko`) | 비로그인 `signin` / 로그인 `dashboard` 분기 검증 완료 | `userfront-e2e/tests/route-inventory.spec.ts`, `userfront-e2e/tests/auth-routing.spec.ts` |
|
||||
| R03 | `/:locale/dashboard` | 비로그인 `signin` / 로그인 유지 검증 완료 | `userfront-e2e/tests/route-inventory.spec.ts`, `userfront-e2e/tests/auth-routing.spec.ts` |
|
||||
| R04 | `/:locale/profile` | 비로그인 `signin` / 로그인 유지 검증 완료 | `userfront-e2e/tests/route-inventory.spec.ts` |
|
||||
| R05 | `/:locale/signin` | 진입 가능 검증 완료 | `userfront-e2e/tests/route-inventory.spec.ts` |
|
||||
| R06 | `/:locale/login` | 진입 가능 검증 완료 | `userfront-e2e/tests/route-inventory.spec.ts` |
|
||||
| R07 | `/:locale/consent` | challenge 유무 케이스 진입 검증 완료 | `userfront-e2e/tests/route-inventory.spec.ts` |
|
||||
| R08 | `/:locale/signup` | 진입 가능 검증 완료 | `userfront-e2e/tests/route-inventory.spec.ts` |
|
||||
| R09 | `/:locale/registration` | 진입 가능 검증 완료 | `userfront-e2e/tests/route-inventory.spec.ts` |
|
||||
| R10 | `/:locale/verify` | 진입 가능 검증 완료 | `userfront-e2e/tests/route-inventory.spec.ts` |
|
||||
| R11 | `/:locale/verify/:token` | verify 경로 진입/처리 검증 완료 | `userfront-e2e/tests/route-inventory.spec.ts` |
|
||||
| R12 | `/:locale/verification` | 진입 가능 검증 완료 | `userfront-e2e/tests/route-inventory.spec.ts` |
|
||||
| R13 | `/:locale/l/:shortCode` | short code 경로 진입/처리 검증 완료 | `userfront-e2e/tests/route-inventory.spec.ts` |
|
||||
| R14 | `/:locale/forgot-password` | 진입 가능 검증 완료 | `userfront-e2e/tests/route-inventory.spec.ts` |
|
||||
| R15 | `/:locale/recovery` | 진입 가능 검증 완료 | `userfront-e2e/tests/route-inventory.spec.ts` |
|
||||
| R16 | `/:locale/reset-password` | token 기반 진입 검증 완료 | `userfront-e2e/tests/route-inventory.spec.ts`, `userfront-e2e/tests/password-and-reset.spec.ts` |
|
||||
| R17 | `/:locale/error` | 진입 가능 검증 완료 | `userfront-e2e/tests/route-inventory.spec.ts` |
|
||||
| R18 | `/:locale/settings` | 진입 가능 검증 완료 | `userfront-e2e/tests/route-inventory.spec.ts` |
|
||||
| R19 | `/:locale/approve` | 비로그인 `signin?notice=...` / 로그인 `dashboard` 검증 완료 | `userfront-e2e/tests/route-inventory.spec.ts`, `userfront-e2e/tests/auth-routing.spec.ts` |
|
||||
| R20 | `/:locale/ql/:ref` | 비로그인 `signin?notice=...` / 로그인 `dashboard` 검증 완료 | `userfront-e2e/tests/route-inventory.spec.ts` |
|
||||
| R21 | `/:locale/scan` | 비로그인 `signin` / 로그인 진입 검증 완료 | `userfront-e2e/tests/route-inventory.spec.ts` |
|
||||
| R22 | `/:locale/admin/users` | 비로그인 `signin` / 로그인 진입 검증 완료 | `userfront-e2e/tests/route-inventory.spec.ts` |
|
||||
|
||||
## 2) 기능 회귀 (핵심)
|
||||
|
||||
| ID | 기능 | 검증 상태 | 테스트 파일 |
|
||||
|---|---|---|---|
|
||||
| F01 | `/ko` 비로그인 리다이렉트 | 완료 | `userfront-e2e/tests/auth-routing.spec.ts` |
|
||||
| F02 | 로그인 후 `/ko` + 새로고침 세션 유지 | 완료 | `userfront-e2e/tests/auth-routing.spec.ts` |
|
||||
| F03 | approve 경로 비로그인 보호 | 완료 | `userfront-e2e/tests/auth-routing.spec.ts` |
|
||||
| F04 | approve 경로 로그인 자동 승인 | 완료 | `userfront-e2e/tests/auth-routing.spec.ts` |
|
||||
| F05 | 비밀번호 로그인 성공 | 완료 | `userfront-e2e/tests/password-and-reset.spec.ts` |
|
||||
| F06 | 비밀번호 로그인 실패 코드 처리 | 완료 | `userfront-e2e/tests/password-and-reset.spec.ts` |
|
||||
| F07 | 비밀번호 재설정 완료 후 signin 이동 | 완료 | `userfront-e2e/tests/password-and-reset.spec.ts` |
|
||||
| F08 | 프로필 소속 수정 후 blur 저장 요청 전송 | 완료 | `userfront-e2e/tests/profile-department.spec.ts` |
|
||||
| F09 | 프로필 소속 입력 후 즉시 새로고침 시 저장 요청 미전송(재현) | 완료 | `userfront-e2e/tests/profile-department.spec.ts` |
|
||||
| F10 | 프로필 소속 동일값 입력 시 저장 요청 미전송 | 완료 | `userfront-e2e/tests/profile-department.spec.ts` |
|
||||
| F11 | 프로필 소속 빈값 입력 시 저장 요청 미전송 | 완료 | `userfront-e2e/tests/profile-department.spec.ts` |
|
||||
| F12 | 프로필 소속 수정 후 새로고침 뒤 재수정 저장 요청 누락 방지 | 완료 | `userfront-e2e/tests/profile-department.spec.ts` |
|
||||
|
||||
## 3) 실행/CI
|
||||
|
||||
- 로컬 실행: `cd userfront-e2e && npm run test:wasm`
|
||||
- CI 워크플로우: `.gitea/workflows/code_check.yml`의 `userfront-e2e-tests` 잡에서 매 실행 검증
|
||||
- 현재 스위트 수량: 총 42 테스트(라우트 30 + 인증/리다이렉트 4 + 비밀번호/리셋 3 + 프로필 소속 5)
|
||||
@@ -0,0 +1,47 @@
|
||||
# #303 로그인 링크/코드 진입 실패 (`/{locale}/l/{shortCode}`) 대응
|
||||
|
||||
## 요약
|
||||
- 로그인 링크 발송은 정상이나, 링크 클릭 후 로그인 검증 진입이 실패하는 사례가 있었습니다.
|
||||
- 특히 locale prefix가 포함된 short-code 경로(`/{locale}/l/{shortCode}`)에서 재현되었습니다.
|
||||
- 원인은 라우터의 public path 판별과 short-code 추출 로직이 locale prefix 경로를 고려하지 못한 점이었습니다.
|
||||
|
||||
## 증상
|
||||
- 링크 클릭 URL이 `https://.../ko/l/AB123456` 형태일 때 로그인 검증이 자동 시작되지 않음
|
||||
- 비로그인 상태에서 해당 경로 접근 시 signin으로 리다이렉트되어 short-code 검증이 끊김
|
||||
|
||||
## 원인
|
||||
1. Public path 판별에서 `/l/` 경로가 제외되어 있었음
|
||||
2. `LoginScreen`의 short-code 추출이 `uri.pathSegments.first == 'l'`에만 의존
|
||||
- `/{locale}/l/{shortCode}`에서는 첫 세그먼트가 locale이므로 추출 실패
|
||||
|
||||
## 조치 내용
|
||||
1. 경로 정책 분리
|
||||
- `userfront/lib/features/auth/domain/login_link_route_policy.dart` 신규 추가
|
||||
- public path 판별(`isPublicAuthPath`)과 short-code 추출(`extractLoginShortCode`)을 공용화
|
||||
|
||||
2. 라우터 반영
|
||||
- `userfront/lib/main.dart` redirect에서 `isPublicAuthPath` 사용
|
||||
- `/l/` 경로를 public path로 허용
|
||||
|
||||
3. 로그인 화면 반영
|
||||
- `userfront/lib/features/auth/presentation/login_screen.dart`에서
|
||||
`extractLoginShortCode(Uri.base)`로 short-code를 추출하도록 변경
|
||||
- locale prefix 유무와 관계없이 short-code 검증 진입 가능
|
||||
|
||||
## 테스트
|
||||
### 재현 테스트 (Failing first)
|
||||
- `flutter test test/login_link_route_policy_test.dart`
|
||||
- 초기 실패 확인:
|
||||
- localized short-code 추출 실패
|
||||
- localized short-code public path 판별 실패
|
||||
|
||||
### 수정 후 회귀 테스트
|
||||
- `flutter test test/login_link_route_policy_test.dart` 통과
|
||||
- `flutter test test/router_redirect_widget_test.dart` 통과
|
||||
|
||||
## 영향 범위
|
||||
- 링크/코드 로그인 진입 라우팅 (`/l/{shortCode}` 및 `/{locale}/l/{shortCode}`)
|
||||
- 기존 `/verify`, `/signin`, `/login` 경로에는 동작 변화 없음
|
||||
|
||||
## 관련 이슈
|
||||
- Gitea: #303 `[bug][auth] 링크 클릭/코드 입력 로그인 실패 재현 및 수정`
|
||||
@@ -66,7 +66,17 @@ cd userfront
|
||||
flutter test test/error_screen_test.dart
|
||||
```
|
||||
|
||||
## 6. 관련 이슈
|
||||
## 6. Backend `code` 주입/매핑 경로
|
||||
- 기본 매핑 함수: `backend/internal/response/error_response.go`
|
||||
- `404 -> not_found`
|
||||
- `429 -> rate_limited`
|
||||
- legacy 응답 보강 미들웨어: `backend/internal/middleware/error_code_enricher.go`
|
||||
- 핸들러가 `{"error": ...}`만 반환해도 status 기반 `code`를 주입
|
||||
- 신규 권장 패턴: `response.Error(...)` 또는 공통 helper(`errorJSON`, `errorJSONCode`)로 핸들러에서 명시 코드 반환
|
||||
|
||||
UserFront는 위 경로로 전달된 `code`를 기준으로 whitelist/ory/unknown 분기를 수행합니다.
|
||||
|
||||
## 7. 관련 이슈
|
||||
- `#164` `[UserFront] 에러 노출 whitelist 정의 및 적용`
|
||||
- `#259` `백엔드 i18n/에러 메시지 fallback 정책 재정리 및 반영 계획 수립`
|
||||
- `#260` `[Backend] 에러 응답 code 통일 구현 계획 (phase rollout)`
|
||||
|
||||
Reference in New Issue
Block a user