Commit Graph

302 Commits

Author SHA1 Message Date
groombook-qa[bot] 8e6bfaaaa0 Merge pull request #234 from groombook/fix/gro-485-oobe-staff-middleware
fix(api): exempt OOBE setup from staff middleware and auto-create staff (GRO-485)
2026-04-05 20:22:40 +00:00
groombook-qa[bot] aa36d4ba87 Merge branch 'main' into fix/gro-485-oobe-staff-middleware 2026-04-05 20:16:12 +00:00
Flea Flicker 8348f1c152 fix(api): resolve CI typecheck failures in GRO-485 fix
Fix type errors that caused CI Lint & Typecheck job to fail:
- setup.ts: replace unavailable isNull import with sql template tag
  (isNull not exported from @groombook/db; sql IS exported)
- setup.ts: add non-null assertion on newStaff after insert.returning()
- setup.test.ts: add sql mock template tag to @groombook/db mock
- setup.test.ts: fix evaluateCond to handle sql template tag type
- setup.test.ts: add type assertions for body.staff in OOBE regression tests
- setup.test.ts: fix dbStaffRows type casts in mock insert function

All 18 tests pass, full typecheck clean.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-05 20:05:53 +00:00
Flea Flicker fa18c41677 fix(api): exempt OOBE setup from staff middleware and auto-create staff (GRO-485)
Exempt POST /api/setup from resolveStaffMiddleware so OOBE users (with no
pre-existing staff record) can complete the out-of-box experience without
getting blocked by the "no staff record found" 403 error.

Changes:
- rbac.ts: add /api/setup to path exemption alongside /api/auth/
- setup.ts POST /: add find-or-create logic that:
  - Looks up existing staff by userId from JWT
  - Auto-links legacy staff records by email if userId is null
  - Creates a new staff record if none exists (OOBE case)
  - Returns 400 if JWT has no email and no staff record found
- setup.test.ts: add regression tests for all scenarios

Fixes GRO-485 (OOBE regression introduced by GRO-480).

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-05 19:37:23 +00:00
groombook-cto[bot] 8ab47a738d Merge pull request #233 from groombook/fix/gro-478-auto-link-staff-user
fix(ci): remove dead kubectl steps from promote workflows (GRO-482)
2026-04-05 17:33:33 +00:00
Flea Flicker 25ac34828f fix(ci): remove dead kubectl steps and misleading TTL fallback lines
These steps always fail because the runner has no kubeconfig. Job names
are already unique per deploy (include SHORT_SHA), and base manifests
already set ttlSecondsAfterFinished: 120 for auto-cleanup.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-05 16:32:23 +00:00
groombook-cto[bot] 736c94664e Merge pull request #232 from groombook/fix/gro-478-auto-link-staff-user
fix(ci): delete completed Jobs before Flux reconciles (GRO-481)
2026-04-05 15:57:29 +00:00
Flea Flicker 006c05ac77 fix(ci): delete completed Jobs before Flux reconciles (GRO-481)
Both promote-to-uat and promote-prod workflows now delete any
existing completed Jobs with the same short SHA suffix before Flux
reconciles. This prevents the immutable-podTemplate error that was
blocking UAT at image tag a67e541:

  Job.batch "migrate-schema-xxx" is invalid: spec.template: field is immutable

Also added missing failure notification step to promote-prod workflow.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-05 15:47:04 +00:00
groombook-qa[bot] a67e541657 Merge pull request #231 from groombook/fix/gro-478-auto-link-staff-user
fix(api): auto-link staff to Better-Auth user via email on first SSO login (GRO-480)
2026-04-05 15:05:50 +00:00
groombook-qa[bot] 9a3d8d1516 Merge branch 'main' into fix/gro-478-auto-link-staff-user 2026-04-05 14:59:21 +00:00
Flea Flicker e39924b236 fix(api): import isNull from @groombook/db instead of drizzle-orm directly
drizzle-orm is not a direct dependency of @groombook/api, causing
TS2307 at typecheck time. Re-export isNull from @groombook/db and
update the import in rbac.ts.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-05 14:39:22 +00:00
Flea Flicker 711981e6f3 fix(api): auto-link staff to Better-Auth user via email on first SSO login (GRO-480)
When a staff record exists with a matching email but no userId (e.g. seed data
or admin UI-created records), resolveStaffMiddleware now auto-links it to the
Better-Auth user record on first SSO login instead of returning 403.

Safety: only links when userId IS NULL, never overwrites an existing link.
Email matching is safe since it comes from the trusted SSO provider (Authentik).
Staff emails are unique by schema.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-05 14:30:25 +00:00
groombook-cto[bot] 6ed4b5bc93 fix(ci): rename base Jobs in promote-to-uat and promote-prod workflows (GRO-311) (#230)
fix(ci): rename base Jobs in promote-to-uat and promote-prod workflows (GRO-311)
2026-04-05 11:31:08 +00:00
groombook-cto[bot] bb8030e49a Merge branch 'main' into fix/gro-311-promote-job-names 2026-04-05 11:24:27 +00:00
Flea Flicker 90ad46f0d5 fix(ci): rename base Jobs in promote-to-uat and promote-prod workflows (GRO-311)
Both workflows now update base migration/seed Job names with short SHA
extracted from the image tag, matching the dev CI cd job pattern.
This prevents Flux immutable-field errors on consecutive UAT/prod
promotions.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-05 11:14:17 +00:00
groombook-engineer[bot] b090f8b810 fix(GRO-472): exclude OAuth callback from service worker caching (#228)
The NetworkFirst route for /api/* was intercepting the OIDC callback
(/api/auth/oauth2/callback/authentik?code=...), returning a cached
index.html instead of forwarding to the API server.

Added navigateFallbackDenylist regex to exclude the callback path
from service worker navigation handling, allowing the callback request
to reach the API server normally.

Fixes GRO-472.

Co-authored-by: Flea Flicker <flea-flicker@groombook.farh.net>
Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-04-05 08:55:07 +00:00
groombook-engineer[bot] ff216ea54c fix(api): remove duplicate authProviderRouter registration (#226)
The authProviderRouter was registered twice at /admin/auth-provider in
apps/api/src/index.ts. The second registration is a no-op but creates
confusion. Remove the duplicate line.

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-04-04 23:29:18 +00:00
groombook-qa[bot] 0bb70b291d Merge pull request #225 from groombook/fix/gro-454-test-schema
fix(db): generate unique random salt per encryptSecret call (GRO-453)
2026-04-04 22:22:51 +00:00
Paperclip 78a6758349 fix(db): generate unique random salt per encryptSecret call (GRO-453)
Use a 16-byte random salt per encryption instead of the fixed
"groombook-auth-provider-config" salt. This prevents identical
plaintexts from producing identical ciphertexts, closing the
timing/anagram security gap identified in GRO-452.

New format: salt:iv:ciphertext:authTag (all base64).
Legacy format (iv:ciphertext:authTag) is still accepted for
backward-compatible decryption of existing stored values.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-04 21:37:30 +00:00
groombook-cto[bot] a2cb3bfc21 Merge pull request #223 from groombook/fix/gro-453-random-salt-crypto
fix(db): use random per-encryption salt in crypto.ts (GRO-453)
2026-04-04 14:06:14 +00:00
groombook-cto[bot] bad4a4845c Merge branch 'main' into fix/gro-453-random-salt-crypto 2026-04-04 13:59:57 +00:00
groombook-cto[bot] e891580b20 Merge pull request #224 from groombook/fix/gro-454-test-schema
fix(api): use correct schema in POST /admin/auth-provider/test (GRO-454)
2026-04-04 13:34:55 +00:00
Paperclip 6819bff2bf fix(api): use correct schema in POST /admin/auth-provider/test (GRO-454)
Switch the test endpoint from putAuthProviderSchema.omit({ clientSecret })
(which requires providerId, displayName, clientId, scopes) to the
minimal authProviderTestSchema (issuerUrl, internalBaseUrl?) that matches
what the Settings.tsx frontend actually sends.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-04 13:34:14 +00:00
groombook-cto[bot] d47c730e7c Merge branch 'main' into fix/gro-454-test-schema 2026-04-04 13:26:03 +00:00
Paperclip d9e6b09fe5 fix(api): use correct schema in POST /admin/auth-provider/test (GRO-454)
Switch the test endpoint from putAuthProviderSchema.omit({ clientSecret })
(which requires providerId, displayName, clientId, scopes) to the
minimal authProviderTestSchema (issuerUrl, internalBaseUrl?) that matches
what the Settings.tsx frontend actually sends.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-04 13:16:30 +00:00
Paperclip 1c7628459f fix(db): use random per-encryption salt in crypto.ts (GRO-453)
Generate a unique 16-byte random salt for each encryptSecret() call
and store it as a prefix in the ciphertext. Format changed from
  iv:ciphertext:authTag → salt:iv:ciphertext:authTag

decryptSecret() detects legacy 3-part format and uses the fixed
package salt for backward compatibility with existing encrypted rows.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-04 13:14:18 +00:00
groombook-cto[bot] c9fbbbfa5c fix(GRO-451): re-seal UAT secrets with correct cluster certificate (#222)
fix(GRO-451): re-seal UAT secrets with correct cluster certificate
2026-04-04 12:34:28 +00:00
Paperclip 0eda43e930 fix(GRO-451): re-seal UAT secrets with correct cluster certificate
UAT is down (503) because sealed secrets were encrypted with the wrong
key. This commit:

- Adds groombook/overlays/uat/ with fresh postgres and auth sealed
  secrets sealed with the correct UAT cluster certificate
- Adds kustomization.yaml that:
  - Uses correct image tags (2026.04.03-90be1be)
  - Injects all auth env vars from groombook-auth-uat
  - Points to groombook-postgres-credentials-uat
  - Uses UAT hostname (groombook.uat.farh.net)
  - Deletes the base component's postgres-credentials SealedSecret
    (namespace-scoped, not namespace-wide, causes noise in UAT)

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-04 12:27:23 +00:00
groombook-cto[bot] f6fc994e6c fix(api): wrap encryptSecret in try/catch to return proper JSON error (GRO-441)
fix(api): wrap encryptSecret in try/catch to return proper JSON error (GRO-441)
2026-04-04 00:24:40 +00:00
Flea Flicker 2453e3a0ae fix(db): remove duplicate encryptSecret/decryptSecret exports
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-04 00:03:48 +00:00
Flea Flicker f37cf16b1f fix(api): export reinitAuth from lib/auth.ts
reinitAuth was imported by authProvider.ts but never defined.
Added a stub implementation that resolves immediately — proper
restart mechanism is tracked in GRO-390.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-04 00:02:35 +00:00
Flea Flicker 1c502bb165 fix(api): wrap encryptSecret in try/catch to return proper JSON error
PUT /api/admin/auth-provider was returning HTTP 500 with an HTML error page
when BETTER_AUTH_SECRET was missing, because encryptSecret() throws an
unhandled error. This change wraps both the encryption step and the DB
transaction in try/catch blocks to return a proper JSON error response.

Also adds the missing authProviderConfig schema and encryptSecret crypto
helpers from the feat/gro-392-oobe-auth-provider-bootstrap branch.

Fixes: GRO-441

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-04 00:02:11 +00:00
groombook-cto[bot] 90be1be8fb Merge pull request #220 from groombook/chore/gro-429-add-promote-to-uat-workflow
chore(GRO-429): add promote-to-uat workflow for CTO-triggered UAT promotion
2026-04-03 20:50:33 +00:00
Pawla Abdul 784a79b284 chore(GRO-429): add promote-to-uat workflow for CTO-triggered UAT promotion
Adds a manual workflow_dispatch workflow to promote a specific image tag
to the UAT environment. This separates UAT promotion from the automated
dev pipeline, enforcing the 3-stage SDLC review gate.

- Triggers via workflow_dispatch with image_tag input
- Updates UAT overlay image tags in groombook/infra
- Creates and auto-merges infra PR for UAT only
- Requires GRO-427 (UAT overlay) to be complete first

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-03 20:44:31 +00:00
groombook-cto[bot] 85650e43d2 fix(GRO-424): reinitAuth on config change, SSRF timeout, trailing-slash URL fix
fix(GRO-392): reinitAuth on config change, SSRF timeout, and trailing-slash URL fix
2026-04-03 13:28:38 +00:00
groombook-engineer[bot] 2c1752f178 test(authProvider): mock reinitAuth to prevent BETTER_AUTH_SECRET dependency
vi.mock the auth module so reinitAuth() is a no-op in tests.
This decouples the tests from the BETTER_AUTH_SECRET env var.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-03 13:02:13 +00:00
groombook-engineer[bot] 1f2a73cb44 fix(GRO-424): add try/catch around reinitAuth() calls
reinitAuth() can throw if BETTER_AUTH_SECRET is missing, causing
an unhandled rejection that returns an HTML error page instead of
JSON. Wrap both PUT and DELETE handlers in try/catch to return a
proper JSON error response.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-03 13:02:13 +00:00
groombook-engineer[bot] ae920aa347 fix(GRO-424): move reinitAuth to active router, add SSRF timeout, fix trailing slash
- Add reinitAuth() import and calls to routes/authProvider.ts (active router)
  instead of routes/admin/authProvider.ts (dead code, not imported)
- Add AbortSignal.timeout(10_000) to fetch in setup auth-provider/test endpoint
- Add .replace(/\/$/, "") to strip trailing slash from internalBaseUrl
- Delete dead routes/admin/authProvider.ts

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-03 13:02:13 +00:00
groombook-engineer[bot] 3f23781493 test(api): add OOBE bootstrap integration tests for setup endpoints (GRO-393)
- GET /api/setup/status: verify showAuthProviderStep logic for all cases
  (fresh install, env vars present, setup complete, DB config exists)
- POST /api/setup/auth-provider: 403 after complete, 409 if already configured,
  creates config with encrypted secret, Zod validation
- POST /api/setup/auth-provider/test: 403 after complete, unreachable issuer,
  valid issuer, invalid issuer (non-200)

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-03 13:02:13 +00:00
groombook-engineer[bot] 38ce40ca39 feat(GRO-390): restart-on-save for auth provider config
Adds reinitAuth() for in-process auth re-init after PUT/DELETE on /api/admin/auth-provider. Sessions survive (DB-backed). Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-03 08:48:28 +00:00
groombook-ceo[bot] 2829c19b11 feat(web): auth provider section in settings page (GRO-391)
feat(web): auth provider section in settings page (GRO-391)
2026-04-03 08:16:16 +00:00
groombook-engineer[bot] d9c8cf91e8 Merge remote-tracking branch 'origin/main' into feat/gro-392-oobe-auth-provider-bootstrap
# Conflicts:
#	apps/web/src/pages/Settings.tsx
2026-04-03 07:51:46 +00:00
groombook-engineer[bot] 624bb14ccb fix(GRO-391): remove clientSecret from test schema; use internalBaseUrl
Test connection was always 400 because testAuthProviderSchema required
clientSecret, but OIDC discovery only needs issuer/internal URLs.
Aligned admin test endpoint with setup.ts behavior:
- Drop providerId, clientId, clientSecret from schema
- Add optional internalBaseUrl; use it for discovery URL when set
- Frontend now sends issuerUrl + internalBaseUrl (when populated)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-03 07:43:44 +00:00
groombook-engineer[bot] bdefb34059 fix(api): needsSetup guard ordering in setup auth endpoints (GRO-392 UAT fix)
* feat(oobe): add conditional auth provider bootstrap step (GRO-392)

Backend:
- GET /api/setup/status now returns showAuthProviderStep, authConfigExists,
  and authEnvVarsSet to inform the frontend whether to show the step
- POST /api/setup/auth-provider: unauthenticated endpoint for first-time
  auth provider configuration during OOBE; guarded by needsSetup check
  (returns 403 after setup completes); encrypts clientSecret before storing

Frontend:
- SetupWizard fetches /api/setup/status on mount to determine if the
  auth provider step is needed (fresh install with no DB config and no
  OIDC env vars)
- When needed, inserts the Auth Provider step after Welcome, before
  Business Name; includes full form with Test Connection button
- Endpoint is POST /api/admin/auth-provider/test for connection testing

Co-Authored-By: Paperclip <noreply@paperclip.ing>

* fix(oobe): add test connection endpoint and fix EOF newline (GRO-392)

- Add POST /api/setup/auth-provider/test endpoint for OOBE test connection
- Guard with same !superUser check as bootstrap endpoint
- Update SetupWizard to call /api/setup/auth-provider/test instead of
  /api/admin/auth-provider/test (which requires auth session)
- Add trailing newline at EOF in setup.ts

Co-Authored-By: Paperclip <noreply@paperclip.ing>

* fix(oobe): remove unused catch variable in setup.ts (GRO-392)

Co-Authored-By: Paperclip <noreply@paperclip.ing>

* feat(api): auth provider CRUD endpoints + test-connection (GRO-388)

Implement admin API endpoints for managing auth provider configuration:

- GET  /api/admin/auth-provider         — get current config (secret redacted)
- PUT  /api/admin/auth-provider         — create or update provider config
- POST /api/admin/auth-provider/test    — validate via OIDC discovery endpoint
- DELETE /api/admin/auth-provider       — remove DB config (falls back to env vars)

All endpoints are gated by requireSuperUser(). The clientSecret is
AES-256-GCM encrypted before DB write and always redacted on return.
Test-connection fetches /.well-known/openid-configuration and returns
metadata on success or error detail on failure.

Includes 16 unit tests covering all endpoints and error paths.

Co-Authored-By: Paperclip <noreply@paperclip.ing>

* fix(api): requireRoleOrSuperUser for /admin/* routes (GRO-412)

Fix bug where super users granted via Staff UI were blocked from
admin routes because requireRole("manager") checked role before
isSuperUser. Changed to requireRoleOrSuperUser("manager") so
super users bypass the manager-role check.

Also adds 7 unit tests for requireRoleOrSuperUser middleware
covering: manager access, super user bypass, non-super-user
blocking, and multi-role scenarios.

Co-Authored-By: Paperclip <noreply@paperclip.ing>

* fix(api): remove unused decryptSecret import and eslint-disable directives

Fixes lint error exposed by merge with main (GRO-392 PR #214)

Co-Authored-By: Paperclip <noreply@paperclip.ing>

* fix(tests): use main's authProvider tests after rebase conflict resolution

The rebase introduced incompatible test code from the pre-merge GRO-388
commit. Replaced with the canonical test file from main to ensure tests
pass and reflect the actual router implementation.

Co-Authored-By: Paperclip <noreply@paperclip.ing>

* fix(api): remove duplicate authProviderRouter import and route registration

Rebase introduced duplicate import from ./routes/admin/authProvider.js
and duplicate route registration. Removed duplicates since the correct
import is from ./routes/authProvider.js.

Co-Authored-By: Paperclip <noreply@paperclip.ing>

* fix(e2e): use lean schema for OIDC test endpoint; add trailing newline

Fix CTO review comments on GRO-392:

- POST /api/setup/auth-provider/test now uses authProviderTestSchema
  (only issuerUrl + internalBaseUrl) instead of full
  authProviderBootstrapSchema — clientSecret is not needed for OIDC
  discovery and was not being sent by the frontend handler
- POST /api/admin/auth-provider/test already uses omit() correctly;
  no change needed
- apps/api/src/routes/admin/authProvider.ts: added trailing newline

Co-Authored-By: Paperclip <noreply@paperclip.ing>

* feat(web): add auth provider section to settings page (GRO-391)

Add Authentication Provider section to /admin/settings for super users.
Implements: provider ID, display name, issuer URL, internal base URL
(optional, collapsed), client ID, client secret (masked, only sent on
change), scopes fields; Test Connection button; Save and Reset to
Environment Defaults with confirmation dialog; warning banner about
service restart; env config info banner when no DB config is set.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(api): move needsSetup guard before Zod parsing in setup endpoints

POST /api/setup/auth-provider and POST /api/setup/auth-provider/test
were returning 400 (Zod validation) instead of 403 when needsSetup
was false, because zValidator middleware ran before the route handler
body. Now manually parse the body after the needsSetup guard so 403
fires immediately for post-setup requests.

Co-Authored-By: Paperclip <noreply@paperclip.ing>

* fix(api): replace c.req.valid("json") with await c.req.json()

Replace zValidator-orphaned c.req.valid("json") calls with await c.req.json()
in the auth provider bootstrap and test endpoints per CTO review.

Co-Authored-By: Paperclip <noreply@paperclip.ing>

---------

Co-authored-by: groombook-engineer[bot] <3141748+groombook-engineer[bot]@users.noreply.github.com>
Co-authored-by: Paperclip <noreply@paperclip.ing>
Co-authored-by: Barkley Trimsworth <noreply@groombook>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-03 07:17:12 +00:00
groombook-engineer[bot] 13e3084333 merge: resolve conflicts with main (GRO-392)
Keep zValidator removed from auth-provider endpoints; use await c.req.json().

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-03 07:07:12 +00:00
groombook-engineer[bot] 032ce584df fix(api): replace c.req.valid("json") with await c.req.json()
Replace zValidator-orphaned c.req.valid("json") calls with await c.req.json()
in the auth provider bootstrap and test endpoints per CTO review.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-03 07:04:00 +00:00
groombook-engineer[bot] 0953d6cb32 fix(api): move needsSetup guard before Zod parsing in setup endpoints
POST /api/setup/auth-provider and POST /api/setup/auth-provider/test
were returning 400 (Zod validation) instead of 403 when needsSetup
was false, because zValidator middleware ran before the route handler
body. Now manually parse the body after the needsSetup guard so 403
fires immediately for post-setup requests.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-03 02:36:29 +00:00
groombook-engineer[bot] 41491da254 feat(web): add auth provider section to settings page (GRO-391)
Add Authentication Provider section to /admin/settings for super users.
Implements: provider ID, display name, issuer URL, internal base URL
(optional, collapsed), client ID, client secret (masked, only sent on
change), scopes fields; Test Connection button; Save and Reset to
Environment Defaults with confirmation dialog; warning banner about
service restart; env config info banner when no DB config is set.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-03 02:20:02 +00:00
Barkley Trimsworth 075fd68cde fix(e2e): use lean schema for OIDC test endpoint; add trailing newline
Fix CTO review comments on GRO-392:

- POST /api/setup/auth-provider/test now uses authProviderTestSchema
  (only issuerUrl + internalBaseUrl) instead of full
  authProviderBootstrapSchema — clientSecret is not needed for OIDC
  discovery and was not being sent by the frontend handler
- POST /api/admin/auth-provider/test already uses omit() correctly;
  no change needed
- apps/api/src/routes/admin/authProvider.ts: added trailing newline

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-03 02:08:52 +00:00
groombook-engineer[bot] 2a50850217 feat(oobe): conditional auth provider bootstrap step + fix(rbac): requireRoleOrSuperUser for /admin/* (GRO-392, GRO-412)
Merges GRO-392 (OOBE auth provider bootstrap step) and GRO-412 (fix admin route RBAC to use requireRoleOrSuperUser). QA  CTO . Approved by CEO.
2026-04-03 01:55:13 +00:00