fix(api): enforce requireSuperUser on settings PATCH and fix dev-mode auth bypass (#206)

* fix(api): enforce requireSuperUser on settings PATCH and fix dev-mode auth bypass

- Add requireSuperUser() middleware to PATCH /api/admin/settings route
  to ensure only super users can modify business settings

- Fix dev-mode (AUTH_DISABLED=true) force-set of isSuperUser:true
  for all staff records in resolveStaffMiddleware. Now preserves
  actual database value with isSuperUser ?? false fallback.
  This prevents non-super-users (e.g., receptionists) from
  bypassing RBAC checks in dev mode.

- Fix test data: RECEPTIONIST and GROOMER now correctly have
  isSuperUser: false (was incorrectly inheriting true from MANAGER)

- Add 7 new tests for requireSuperUser middleware covering:
  - Super user access allowed
  - Non-super-user receptionist blocked with 403
  - Non-super-user groomer blocked with 403
  - Unresolved staff record returns 403
  - Receptionist cannot grant super user via PATCH
  - JSON error response format

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

* fix(api): remove dead code in rbac test

Remove unused `app` variable from 'returns 403 when staff record is
not resolved' test - the test uses `testApp` instead.

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>
This commit was merged in pull request #206.
This commit is contained in:
groombook-engineer[bot]
2026-04-02 12:57:56 +00:00
committed by GitHub
parent d8d91ab409
commit 004e23f8bc
3 changed files with 79 additions and 4 deletions
+3 -3
View File
@@ -42,7 +42,7 @@ export const resolveStaffMiddleware: MiddlewareHandler<AppEnv> = async (
if (!manager) {
return c.json({ error: "Forbidden: no staff records found" }, 403);
}
c.set("staff", { ...manager, isSuperUser: true });
c.set("staff", { ...manager, isSuperUser: manager.isSuperUser ?? false });
await next();
return;
}
@@ -52,7 +52,7 @@ export const resolveStaffMiddleware: MiddlewareHandler<AppEnv> = async (
.from(staff)
.where(eq(staff.userId, devUserId));
if (row) {
c.set("staff", { ...row, isSuperUser: true });
c.set("staff", { ...row, isSuperUser: row.isSuperUser ?? false });
await next();
return;
}
@@ -68,7 +68,7 @@ export const resolveStaffMiddleware: MiddlewareHandler<AppEnv> = async (
403
);
}
c.set("staff", { ...fallbackRow, isSuperUser: true });
c.set("staff", { ...fallbackRow, isSuperUser: fallbackRow.isSuperUser ?? false });
await next();
return;
}