Commit Graph

20 Commits

Author SHA1 Message Date
Jakub Zych
a13a1214cb test(bouncer,cabana): cover admin refresh subject checks without a database
Quick 260927-q23 (CR-01), unit coverage that runs under -short.

- bouncer: TestRefreshAudienceForSubject covers active, pre/post cutoff,
  missing, nil provider, non-numeric sub, provider error, and proves
  token-only refusals never reach the provider
- bouncer: TestJWTGuardTokensValidAfter pins the unchanged "User not found"
  message and errors.Is(err, ErrSubjectRejected)
- cabana: TestPhase10Coverage subtest pins cookie expiry on subject
  refusals, no cookies over Bearer or on a provider error, and the
  post-cutoff success path
2026-09-27 19:00:12 +02:00
Jakub Zych
be4a923f36 fix(cabana): enforce tokens_valid_after and is_activated on admin refresh
Fixes review finding CR-01 (quick 260927-q23): POST {prefix}/api/v1/auth/refresh
minted a new token without loading the admin, so a session kept alive by the
SPA's refresh-on-401 survived admin:reset-password, deactivation and deletion.
This broke Phase 9 truth T-09-04.

- bouncer: extract the JWT guard's subject lookup into subjectPrincipal and
  issuedBeforeCutoff (same order and messages), add ErrSubjectRejected
- bouncer: add RefreshAudienceFor, which runs the guard's subject checks
  after the token-only checks and before minting; Refresh and
  RefreshAudience are unchanged (nil hook)
- cabana: share one lazyBackendUsers provider between the backend guard and
  refresh; a cookie refresh refused for its subject expires summer_admin
- test: TestAdminRefreshRevocation (Postgres, real admin:reset-password)
2026-09-27 18:58:15 +02:00
Jakub Zych
ef448da1cc test(10-05): cover every Phase 10 Go change with branch-level tests
- bouncer TestPhase10CookieGuard: cookie read without Bearer, Bearer wins,
  empty cookie, frontend audience and blacklisted jti rejected
- boardwalk TestPhase10BoardwalkServing: HEAD, query strings, encoded
  traversal, index by name, nested prefix, MIME fallback, constructor errors
- cabana TestPhase10Coverage: mounted unsafe routes vs the CSRF walk, option
  and filter edges, read-only labels, relation message defaults, bundle
  fallback locale, cookie refresh of an expired token in the refresh window
- phrasebook override precedence, new locale, Bundle merge order, Forms shapes
- surf prefix collision for deeper paths and the default /backend prefix
- swagger2openapi TestUnionRewrite and converter branch tests
- framework tests no longer name the application (acme fixtures instead)
2026-09-27 18:05:28 +02:00
Jakub Zych
5f9353841b feat(10-01): serve the embedded admin SPA at backend.uri with cookie login
- backend.uri prefix (default /backend) mounts the admin API at {prefix}/api/v1
  and the embedded SPA shell at {prefix} with an api/ JSON 404 fallback
- cookie transport: an X-Requested-With login sets the HttpOnly summer_admin
  cookie and returns no token; the backend guard reads the cookie after Bearer
- CSRF wrapper refuses cookie-only POST/PUT/DELETE without X-Requested-With
- boardwalk package embeds boardwalk/dist, rewrites index.html once per prefix
  and sets cache and security headers
- framework admin OpenAPI pipeline (swag, swagger2openapi, openapi-typescript)
  with prefix-relative paths and typed envelopes for the tracer routes
- admin/ Vite SPA: login, plugin rail, section panel and read-only list
  through the openapi-fetch client typed by the generated schema
2026-09-27 15:21:48 +02:00
Jakub Zych
30bfd2d8d5 test(09-12): add the phase 9 security matrix
- Guard isolation covers audience, secret, refresh, blacklist, and reset cutoff.
- The mounted admin route table must carry the backend guard.
- Fresh PostgreSQL migrate and rollback keep framework and plugin histories apart.
2026-09-27 03:01:54 +02:00
Jakub Zych
10ca7a02e3 feat(09-11): add permissioned admin metadata and settings 2026-09-26 23:06:22 +02:00
Jakub Zych
18b2e85106 feat(09-01): reject cross-audience tokens on both guards
- Frontend verification accepts a missing audience for PHP tokens
- An explicit audience must match the guard, even when the secret is shared
2026-09-24 17:20:57 +02:00
Jakub Zych
8c1de83f01 test(09-01): add failing audience crossover and authorization-order tests
- Same-secret backend token is still accepted by the frontend guard
- Permission denial must not invoke the schema or database callback
- Admin error bodies must not echo secrets or raw tokens
2026-09-24 17:19:57 +02:00
Jakub Zych
dfa00f7e3a feat(09-01): implement separate-admin genre list tracer
- Audience-aware mint, verify, refresh, and backend guard keep frontend tokens compatible
- Cabana mounts raw admin login, list schema, and record list behind admin.jwt.secret
- Framework migration seeds Winter backend users and developer/publisher roles
2026-09-24 17:17:19 +02:00
Jakub Zych
4754377442 feat(07-06): prove mint, refresh, and blacklist under concurrency
A minted token refreshes once, then the old jti is blacklisted. Memory and Postgres blacklists take concurrent Add and IsBlacklisted calls.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-22 19:20:14 +02:00
Jakub Zych
8fcaff77cf feat(07-01): add bcrypt, locale override, and validation rules
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-22 13:39:25 +02:00
Jakub Zych
bccd7f8f35 test(07-01): add failing tests for passwords, locale, and validation
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-22 13:36:08 +02:00
Jakub Zych
cad445a235 feat(07-01): add JWT mint, refresh, and blacklist primitives
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-22 13:34:58 +02:00
Jakub Zych
251f3cc4a0 test(07-01): add failing tests for JWT lifecycle primitives
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-22 13:33:37 +02:00
Jakub Zych
f1218f2c84 test(06-14): regression coverage for surf and bouncer gap closure 2026-09-21 19:47:16 +02:00
Jakub Zych
8a9449df63 fix(06-12): reject typed-nil guards and fractional JWT subjects 2026-09-21 19:43:35 +02:00
Jakub Zych
98dd09847a test(06-05): close framework coverage gaps in bouncer, surf, wire, fetchguard
- Registry neither-interface, nil-registry, and authenticate default
- MemoryStore sweep actually drops expired entries
- RegisterHouseMiddlewareFactory duplicate-name failure
- pathScopedCORS unmatched path plus empty-raw-group introspection
- Time UnmarshalJSON +00:00/Z and PublicOnlyMode host/IP cases
2026-09-19 21:08:04 +02:00
Jakub Zych
d376b1be2d feat(06-01): grow router verbs, factories, and guard registry
- Add Post/Put/Patch/Delete on pact.Router and surf Router/Group
- Resolve name:param middleware via RegisterMiddlewareFactory
- Add bouncer.Registry with Guard, CredentialGuard, UnauthorizedWriter
- Re-express jwt as NewJWTGuard without changing Middleware bodies
2026-09-19 18:59:12 +02:00
Jakub Zych
92255a46ed test(03-04): cover framework database, routing and JWT boundaries
- Shared pgx/GORM pool, ICU locale fail, and isolated plugin migrations
- Seven-stage middleware order, missing-guard boot failure, typed 404s
- Adversarial JWT matrix including alg:none, empty secret, and no leak

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-17 20:29:46 +02:00
Jakub Zych
4ee4c4a2fc feat(03-01): add ServeMux groups, JWT verifier, and serve command
Named middleware resolves at boot, HS256 tokens are pinned with required
exp/sub, and both binaries expose a signal-aware serve command.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-17 20:04:13 +02:00