diff --git a/.planning/REQUIREMENTS.md b/.planning/REQUIREMENTS.md index dc7e625e..f2b7db10 100644 --- a/.planning/REQUIREMENTS.md +++ b/.planning/REQUIREMENTS.md @@ -43,8 +43,8 @@ Requirements for initial release. Each maps to roadmap phases. Tracked as Gitea ### Security -- [ ] **SECU-01**: All API endpoints (except health and register) require valid JWT Bearer token (#23) -- [ ] **SECU-02**: JWT refresh flow via `POST /api/v1/agents/{id}/refresh` (#24) +- [x] **SECU-01**: All API endpoints (except health and register) require valid JWT Bearer token (#23) +- [x] **SECU-02**: JWT refresh flow via `POST /api/v1/agents/{id}/refresh` (#24) - [x] **SECU-03**: Server generates Ed25519 keypair; public key delivered at registration (#25) - [x] **SECU-04**: All config-update and replay SSE payloads are signed with server's Ed25519 private key (#26) - [x] **SECU-05**: Bootstrap token from `CAMELEER_AUTH_TOKEN` env var validates initial agent registration (#27) diff --git a/.planning/STATE.md b/.planning/STATE.md index 58ea78d0..ab4719e5 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -2,10 +2,10 @@ gsd_state_version: 1.0 milestone: v1.0 milestone_name: milestone -status: in-progress -stopped_at: Completed 04-03-PLAN.md -last_updated: "2026-03-11T19:29:30Z" -last_activity: 2026-03-11 -- Completed 04-03 (SSE payload signing) +status: executing +stopped_at: Completed 04-02-PLAN.md +last_updated: "2026-03-11T19:40:20.252Z" +last_activity: 2026-03-11 -- Completed 04-02 (Security filter chain wiring) progress: total_phases: 4 completed_phases: 4 @@ -26,9 +26,9 @@ See: .planning/PROJECT.md (updated 2026-03-11) ## Current Position Phase: 4 of 4 (Security) -Plan: 3 of 3 in current phase (SSE payload signing) -Status: Phase 04 complete, all plans done -Last activity: 2026-03-11 -- Completed 04-03 (SSE payload signing) +Plan: 2 of 3 in current phase (Security filter chain wiring) +Status: Phase 04 in progress, Plan 02 complete +Last activity: 2026-03-11 -- Completed 04-02 (Security filter chain wiring) Progress: [██████████] 100% @@ -61,6 +61,7 @@ Progress: [██████████] 100% | Phase 03 P02 | 32min | 2 tasks | 7 files | | Phase 04 P01 | 12min | 1 tasks | 15 files | | Phase 04 P03 | 17min | 1 tasks | 4 files | +| Phase 04 P02 | 26min | 2 tasks | 25 files | ## Accumulated Context @@ -109,6 +110,9 @@ Recent decisions affecting current work: - [Phase 04]: InitializingBean pattern for fail-fast bootstrap token validation on startup - [Phase 04]: Signed payload parsed to JsonNode for correct SseEmitter serialization (avoids double-quoting) - [Phase 04]: SseSigningIT adapted to Plan 02 security layer (bootstrap token + JWT auth) +- [Phase 04]: Added /error to SecurityConfig permitAll for proper Spring Boot error forwarding through security +- [Phase 04]: Excluded register and refresh paths from ProtocolVersionInterceptor (auth endpoints not data endpoints) +- [Phase 04]: Refresh endpoint in permitAll with self-authentication via refresh token (not JWT access token) ### Pending Todos @@ -123,6 +127,6 @@ None yet. ## Session Continuity -Last session: 2026-03-11T19:29:30Z -Stopped at: Completed 04-03-PLAN.md -Resume file: All plans complete +Last session: 2026-03-11T19:40:20.248Z +Stopped at: Completed 04-02-PLAN.md +Resume file: None diff --git a/.planning/phases/04-security/04-02-SUMMARY.md b/.planning/phases/04-security/04-02-SUMMARY.md new file mode 100644 index 00000000..e0748b33 --- /dev/null +++ b/.planning/phases/04-security/04-02-SUMMARY.md @@ -0,0 +1,165 @@ +--- +phase: 04-security +plan: 02 +subsystem: auth +tags: [spring-security, jwt-filter, security-filter-chain, bootstrap-token, refresh-token, stateless-auth] + +# Dependency graph +requires: + - phase: 04-security + provides: "JwtService, Ed25519SigningService, BootstrapTokenValidator, SecurityProperties beans" + - phase: 03-agent-registry + provides: "AgentRegistryService, AgentRegistrationController, SseConnectionManager, SSE endpoints" +provides: + - "SecurityFilterChain with stateless JWT authentication and public/protected endpoint split" + - "JwtAuthenticationFilter extracting JWT from Authorization header or query param" + - "Registration endpoint with bootstrap token validation, JWT + refresh token + public key issuance" + - "Refresh endpoint issuing new access JWT from valid refresh token" + - "TestSecurityHelper for JWT-authenticated integration tests" +affects: [04-03] + +# Tech tracking +tech-stack: + added: [] + patterns: [OncePerRequestFilter for JWT extraction, SecurityFilterChain with permitAll/authenticated split, error path permit for proper Spring Boot error forwarding] + +key-files: + created: + - cameleer3-server-app/src/main/java/com/cameleer3/server/app/security/JwtAuthenticationFilter.java + - cameleer3-server-app/src/main/java/com/cameleer3/server/app/security/SecurityConfig.java + - cameleer3-server-app/src/test/java/com/cameleer3/server/app/TestSecurityHelper.java + - cameleer3-server-app/src/test/java/com/cameleer3/server/app/security/SecurityFilterIT.java + - cameleer3-server-app/src/test/java/com/cameleer3/server/app/security/BootstrapTokenIT.java + - cameleer3-server-app/src/test/java/com/cameleer3/server/app/security/RegistrationSecurityIT.java + - cameleer3-server-app/src/test/java/com/cameleer3/server/app/security/JwtRefreshIT.java + modified: + - cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/AgentRegistrationController.java + - cameleer3-server-app/src/main/java/com/cameleer3/server/app/config/WebConfig.java + - cameleer3-server-app/src/test/java/com/cameleer3/server/app/security/TestSecurityConfig.java + - cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/AgentRegistrationControllerIT.java + - cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/ExecutionControllerIT.java + - cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/DiagramControllerIT.java + - cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/MetricsControllerIT.java + - cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/BackpressureIT.java + - cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/DiagramRenderControllerIT.java + - cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/DetailControllerIT.java + - cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/SearchControllerIT.java + - cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/AgentCommandControllerIT.java + - cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/AgentSseControllerIT.java + - cameleer3-server-app/src/test/java/com/cameleer3/server/app/storage/DiagramLinkingIT.java + - cameleer3-server-app/src/test/java/com/cameleer3/server/app/storage/IngestionSchemaIT.java + - cameleer3-server-app/src/test/java/com/cameleer3/server/app/interceptor/ProtocolVersionIT.java + - cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/ForwardCompatIT.java + +key-decisions: + - "Added /error to SecurityConfig permitAll to allow Spring Boot error page forwarding through security" + - "Excluded register and refresh paths from ProtocolVersionInterceptor (auth endpoints, not data endpoints)" + - "SSE authentication via ?token= query parameter handled transparently by JwtAuthenticationFilter" + - "Refresh endpoint in permitAll (uses refresh token for self-authentication, not JWT access token)" + +patterns-established: + - "TestSecurityHelper @Component for registering test agents and creating auth headers in ITs" + - "Bootstrap token in Authorization: Bearer header for registration (same header format as JWT)" + - "SecurityFilterChain permits /error for proper error page rendering in authenticated context" + +requirements-completed: [SECU-01, SECU-02, SECU-05] + +# Metrics +duration: 26min +completed: 2026-03-11 +--- + +# Phase 4 Plan 02: Security Filter Chain and Endpoint Protection Summary + +**Spring Security filter chain with JWT authentication on all protected endpoints, bootstrap token validation on registration, refresh token flow, and 91 passing tests including 18 new security ITs** + +## Performance + +- **Duration:** 26 min +- **Started:** 2026-03-11T19:11:48Z +- **Completed:** 2026-03-11T19:38:07Z +- **Tasks:** 2 +- **Files modified:** 25 + +## Accomplishments +- SecurityFilterChain enforces JWT authentication on all endpoints except health, register, refresh, and docs +- JwtAuthenticationFilter extracts JWT from Authorization header or ?token= query param (SSE support) +- Registration endpoint requires bootstrap token, returns accessToken + refreshToken + serverPublicKey (Ed25519) +- Refresh endpoint issues new access JWT from valid refresh token with agent ID verification +- All 15 existing ITs adapted to use JWT authentication via TestSecurityHelper +- 4 new security ITs (SecurityFilterIT, BootstrapTokenIT, RegistrationSecurityIT, JwtRefreshIT) with 18 tests + +## Task Commits + +Each task was committed atomically: + +1. **Task 1: SecurityFilterChain + JwtAuthenticationFilter + registration/refresh integration** - `387e2e6` (feat) +2. **Task 2: Security integration tests + existing test adaptation** - `539b85f` (test) + +## Files Created/Modified + +- `...security/JwtAuthenticationFilter.java` - OncePerRequestFilter extracting JWT from header or query param +- `...security/SecurityConfig.java` - SecurityFilterChain with public/protected endpoint split +- `...controller/AgentRegistrationController.java` - Updated with bootstrap token validation, JWT issuance, refresh endpoint +- `...config/WebConfig.java` - Excluded register/refresh from ProtocolVersionInterceptor +- `...TestSecurityHelper.java` - Test utility for JWT-authenticated requests +- `...security/SecurityFilterIT.java` - 6 tests for protected/public endpoint access control +- `...security/BootstrapTokenIT.java` - 4 tests for bootstrap token validation on registration +- `...security/RegistrationSecurityIT.java` - 3 tests for registration security response +- `...security/JwtRefreshIT.java` - 5 tests for refresh token flow +- 15 existing IT files updated with JWT authentication headers + +## Decisions Made + +- **Added /error to permitAll:** Spring Boot forwards exceptions to /error endpoint; without permitting it, controllers returning 404 via ResponseStatusException would result in 403 to the client. +- **Excluded register/refresh from ProtocolVersionInterceptor:** These are auth/token-renewal endpoints that agents call without full protocol handshake context. Protocol version enforcement is for data/management endpoints. +- **Refresh endpoint uses permitAll + self-authentication:** The refresh endpoint validates the refresh token directly rather than requiring a separate JWT access token, simplifying the agent token renewal flow. +- **SSE query param authentication transparent:** JwtAuthenticationFilter checks both Authorization header and ?token= query param, so no SSE controller changes needed. + +## Deviations from Plan + +### Auto-fixed Issues + +**1. [Rule 3 - Blocking] Added /error to SecurityConfig permitAll** +- **Found during:** Task 2 (test execution) +- **Issue:** Controllers using ResponseStatusException(NOT_FOUND) forward to /error endpoint, which was blocked by Spring Security, resulting in 403 instead of 404 +- **Fix:** Added "/error" to the permitAll requestMatchers list +- **Files modified:** SecurityConfig.java +- **Verification:** All 91 tests pass, 404 responses correctly returned + +**2. [Rule 3 - Blocking] Excluded register/refresh from ProtocolVersionInterceptor** +- **Found during:** Task 2 (JwtRefreshIT tests returning 400) +- **Issue:** Refresh endpoint matched /api/v1/agents/** interceptor pattern, rejecting requests without X-Cameleer-Protocol-Version header with 400 +- **Fix:** Added /api/v1/agents/register and /api/v1/agents/*/refresh to interceptor excludePathPatterns +- **Files modified:** WebConfig.java +- **Verification:** All JwtRefreshIT and BootstrapTokenIT tests pass + +--- + +**Total deviations:** 2 auto-fixed (2 blocking) +**Impact on plan:** Both fixes necessary for correct Spring Security + Spring MVC interceptor integration. No scope creep. + +## Issues Encountered + +None beyond the auto-fixed blocking issues above. + +## User Setup Required + +None - no external service configuration required. + +## Next Phase Readiness + +- Full Spring Security filter chain active with JWT auth on all protected endpoints +- TestSecurityHelper available for all future integration tests +- Ready for Plan 03: Ed25519 signing of SSE command payloads +- Registration flow complete: bootstrap token -> register -> receive JWT + public key -> use JWT for all API calls -> refresh when expired + +## Self-Check: PASSED + +- All 7 created files verified present on disk +- Both commits (387e2e6, 539b85f) verified in git log +- Full `mvn clean verify` passed: 91 tests, 0 failures + +--- +*Phase: 04-security* +*Completed: 2026-03-11*