Add admin authorization to /api/admin/* and /api/users #27

Open
opened 2026-07-17 03:07:15 +00:00 by claude-bot · 1 comment
Contributor

backend/src/app.js:83-104: the admin status/diagnostics routes and the user list/create routes sit behind authMiddleware but have no role gate. The repository calls behind them (repository.pg.js:589-677) aggregate integrations, background jobs, and calculation diagnostics across all homes; GET /api/users lists all user emails; POST /api/users accepts arbitrary oidcSubject values, which is the conflict key for the OIDC login upsert.

Fix: add is_admin boolean not null default false to users (new migration, numbered 007_), a requireAdmin guard, and apply it to all four routes. Ignore client-supplied oidcSubject on user creation. When AUTH_MODE=disabled (single-user dev mode) keep current behavior, but deny by default whenever auth.required is true.

Acceptance criteria

  • Non-admin authenticated user receives 403 from /api/admin/* and /api/users
  • POST /api/users cannot set oidc_subject
  • Admin flag settable via SQL/migration seed; documented in README
  • Tests cover the 403 paths

Relations: relates to #17 (viewer-access decision is the same trust-boundary conversation)

(Audit finding F-06, 2026-07-16 — Severity: High, Confidence: High)

`backend/src/app.js:83-104`: the admin status/diagnostics routes and the user list/create routes sit behind `authMiddleware` but have no role gate. The repository calls behind them (`repository.pg.js:589-677`) aggregate integrations, background jobs, and calculation diagnostics across all homes; `GET /api/users` lists all user emails; `POST /api/users` accepts arbitrary `oidcSubject` values, which is the conflict key for the OIDC login upsert. Fix: add `is_admin boolean not null default false` to `users` (new migration, numbered 007_), a `requireAdmin` guard, and apply it to all four routes. Ignore client-supplied `oidcSubject` on user creation. When `AUTH_MODE=disabled` (single-user dev mode) keep current behavior, but deny by default whenever `auth.required` is true. **Acceptance criteria** - [ ] Non-admin authenticated user receives 403 from /api/admin/* and /api/users - [ ] POST /api/users cannot set oidc_subject - [ ] Admin flag settable via SQL/migration seed; documented in README - [ ] Tests cover the 403 paths **Relations:** relates to #17 (viewer-access decision is the same trust-boundary conversation) *(Audit finding F-06, 2026-07-16 — Severity: High, Confidence: High)*
Author
Contributor

Scope addition from the #17 decision (2026-07-17): while adding the requireAdmin guard, also narrow raw usage-row access per the decided viewer policy — routes serving raw usageReadings (row-level hourly data) move to editor/owner; viewers keep calculation results, analytics summaries, and reports. Acceptance addition: a viewer-role request for raw usage rows returns 403, and the shared dashboard still renders for viewers.

**Scope addition from the #17 decision (2026-07-17):** while adding the `requireAdmin` guard, also narrow raw usage-row access per the decided viewer policy — routes serving raw `usageReadings` (row-level hourly data) move to editor/owner; viewers keep calculation results, analytics summaries, and reports. Acceptance addition: a viewer-role request for raw usage rows returns 403, and the shared dashboard still renders for viewers.
Sign in to join this conversation.
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
rbrooks/BatteryStorageCalculator#27
No description provided.