--- phase: 09-backend-admin-authentication-and-schema-pipeline plan: 12 subsystem: admin tags: [security, postgres, openapi, authorization, acceptance] requires: - phase: 09-backend-admin-authentication-and-schema-pipeline provides: Backend guard, controllers, schemas, CRUD, relations, permissions, and settings provides: - Route-derived Phase 9 security matrix across guard, cabana, and the assembled app - Fresh PostgreSQL admin migration rollback that preserves other histories - Fail-closed phase gate and committed admin OpenAPI contract affects: [phase-10-spa, admin-api] actuals: tokens: 18000 tasks: 3 commits: 4 tech-stack: added: [] patterns: - "The mounted admin route table is the permission matrix; a new handler without the backend guard fails the gate" - "OpenAPI admin paths are generated from cabana annotations and checked against that same route list" - "PostgreSQL stages fail when a TestPhase09 test is skipped or matches nothing" key-files: created: - bouncer/backend_guard_test.go - cabana/security_coverage_test.go - cabana/admin_openapi.go - cabana/phase09_contract_test.go - scripts/check-phase9.sh - ../fonoteka.go/plugins/golem15/fonoteka/admin_phase09_security_test.go - ../fonoteka.go/plugins/golem15/fonoteka/admin_phase09_e2e_test.go - .planning/phases/09-backend-admin-authentication-and-schema-pipeline/09-SECURITY-REVIEW.md modified: - lagoon/backend_admin_migrations_test.go - ../fonoteka.go/scripts/check-openapi.sh - ../fonoteka.go/docs/openapi.json - ../fonoteka.go/plugins/golem15/fonoteka/controllers/genre_controller.go - .planning/phases/09-backend-admin-authentication-and-schema-pipeline/09-VALIDATION.md key-decisions: - "Admin OpenAPI annotations live in cabana/admin_openapi.go so swag can see them; TestPhase09PermissionMatrix keeps that list equal to service.mount" - "Migration rollback is proven on a dedicated database. The shared assembled test database is not rolled back" patterns-established: - "scripts/check-phase9.sh is the only phase acceptance command" requirements-completed: [AUTH-08, ADMIN-01, ADMIN-02, ADMIN-03, ADMIN-04, ADMIN-05] coverage: - id: D1 description: "Frontend and backend tokens cannot cross guards, secrets, refresh, blacklist, or password-reset cutoff." requirement: AUTH-08 verification: - kind: unit ref: bouncer/backend_guard_test.go#TestPhase09GuardIsolation status: pass human_judgment: false - id: D2 description: "Every mounted admin route is inventoried, protected routes carry the backend guard, and denial happens before handler work." requirement: ADMIN-02 verification: - kind: unit ref: cabana/security_coverage_test.go#TestPhase09PermissionMatrix status: pass - kind: integration ref: plugins/golem15/fonoteka/admin_phase09_security_test.go#TestPhase09SecurityRoutes status: pass human_judgment: false - id: D3 description: "Mass assignment, identifier injection, pivot forgery, auth-log redaction, and hook rollback fail closed." requirement: ADMIN-04 verification: - kind: integration ref: cabana/security_coverage_test.go#TestPhase09SecurityCoverage status: pass human_judgment: false - id: D4 description: "Fresh PostgreSQL migration, rollback, and re-migrate keep framework and plugin histories independent and reseed roles." requirement: AUTH-08 verification: - kind: integration ref: lagoon/backend_admin_migrations_test.go#TestPhase09MigrationsFreshRollback status: pass human_judgment: false - id: D5 description: "Assembled acceptance covers login replay, five controllers, settings, relations, locale, empty/single/adjacent pages, and concurrent reads." requirement: ADMIN-01 verification: - kind: e2e ref: plugins/golem15/fonoteka/admin_phase09_e2e_test.go#TestPhase09AssembledAcceptance status: pass human_judgment: false - id: D6 description: "Committed OpenAPI lists every D-09 route with 401 responses and BackendBearer on protected operations." requirement: ADMIN-05 verification: - kind: unit ref: cabana/phase09_contract_test.go#TestPhase09ContractInventory status: pass human_judgment: false duration: 3h completed: 2026-09-27 status: complete --- # Phase 9 Plan 12: Security, PostgreSQL, OpenAPI, and Acceptance Summary **The admin surface now has one fail-closed gate for guard isolation, route permissions, real PostgreSQL, and the committed OpenAPI contract.** ## Performance - **Duration:** 3h - **Started:** 2026-09-27T00:30:00+02:00 - **Completed:** 2026-09-27T03:05:00+02:00 - **Tasks:** 3 - **Files modified:** 14 ## Accomplishments - Added `TestPhase09GuardIsolation`, `TestPhase09PermissionMatrix`, and `TestPhase09SecurityCoverage`. - Added assembled route and denial tests plus a PostgreSQL journey across the five admin controllers, settings, and the editors relation. - Added `scripts/check-phase9.sh` with self-test, security, postgres, openapi, and evidence stages. - Regenerated `fonoteka.go/docs/openapi.json` from the public genre route and the cabana admin annotations. - Recorded threat evidence in `09-SECURITY-REVIEW.md` and replaced the pending validation rows. ## Task Commits 1. **Task 1: Build a non-bypassable Phase 9 security matrix** - `30bfd2d` (summercms.go), `336a90b` (fonoteka.go) 2. **Task 2: Gate all routes, PostgreSQL behavior, and committed OpenAPI** - `4392550` (summercms.go), `feaca6b` (fonoteka.go) 3. **Task 3: Record independent threat evidence and finalize Nyquist mappings** - docs commit on summercms.go ## Files Created/Modified - `bouncer/backend_guard_test.go` - cross-guard token matrix - `cabana/security_coverage_test.go` - mounted route inventory and adversarial fixtures - `cabana/admin_openapi.go` - swag annotations for every D-09 route - `cabana/phase09_contract_test.go` - committed OpenAPI inventory - `scripts/check-phase9.sh` - fail-closed phase gate - `lagoon/backend_admin_migrations_test.go` - fresh migrate/rollback/reseed - `fonoteka.go` `admin_phase09_security_test.go`, `admin_phase09_e2e_test.go`, `docs/openapi.json`, `scripts/check-openapi.sh` ## Decisions Made - Swag documents exported functions in `cabana/admin_openapi.go`. The mount test fails if that list and `service.mount` disagree. - Rollback uses a dedicated database so the shared assembled test database stays intact for the rest of the package. ## Deviations from Plan ### [Rule 3 - Blocking] TDD tests passed on the first run **Found during:** Task 1 **Issue:** The guard, permission order, projection, and migration behavior already existed from plans 09-01 through 09-11. A new assertion written against that behavior passed immediately, so there was no honest RED commit. **Fix:** Committed the new tests as `test(09-12)` once they passed. The gate still fails if a later change removes the control. **Files modified:** `bouncer/backend_guard_test.go`, `cabana/security_coverage_test.go`, `lagoon/backend_admin_migrations_test.go` **Verification:** `scripts/check-phase9.sh --evidence` **Commit:** `30bfd2d` **Total deviations:** 1 **Impact:** No production control was weakened. The new tests are the regression net the plan asked for. ## Issues Encountered None. ## User Setup Required None. ## Next Phase Readiness Plan 09-12 is executed. Phase 9 still needs its verification report before the roadmap phase checkbox can close. ## Self-Check: PASSED - `bouncer/backend_guard_test.go` exists - `scripts/check-phase9.sh` exists - `09-SECURITY-REVIEW.md` exists - Commits `30bfd2d`, `336a90b`, `4392550`, and `feaca6b` are present