Platform admin cannot see or change which org a user belongs to — user management is org-blind #55

Open
opened 2026-08-23 23:57:23 -04:00 by logan · 0 comments
Owner

Owner asked for role/user management to be UI-configurable by an admin. Platform-role management already is — verified in source, so this issue is only the gap that remains.

What already works (do not rebuild)

/admin Users tab, backed by drb-c2-core/app/routers/users.py, is complete for platform roles:

Action Backend UI
List users GET /admin/users (users.py:85) Users tab
Create + invite POST /admin/users (users.py:97) app/admin/page.tsx:583+
Change role PATCH /admin/users/{uid} (users.py:175) role dropdown, page.tsx:462-474
Set owned nodes same PATCH shown when role is operator, page.tsx:474
Disable / enable POST .../disable, .../enable (users.py:229,253) Users tab
Delete DELETE /admin/users/{uid} (users.py:273) Users tab

Roles are admin / operator / viewer, all editable from the dropdown.

The actual gap: orgs are invisible to it

_format_user() (users.py:55-70) returns uid, email, display_name, role, owned_node_ids, disabled, timestamps and Discord link — no org_id, no org_role, no org name. Confirmed by grep: zero references to org_id / orgId / org_role anywhere under drb-frontend/app/admin/.

Consequences for a platform operator today:

  • Cannot tell which org any user belongs to, from any screen.
  • Cannot move a user between orgs.
  • Cannot set org role (owner vs member) from /admin — that lives on the separate /settings/members surface (org.py PATCH "", org.py:48), which is scoped to the caller's own org. So a platform admin managing someone else's org has no route at all.
  • Cannot answer "who is in this org" without reading org_members in the Firestore console.

This was flagged as an observation in ADMIN_BILLING_AUDIT.md section 1 on 2026-08-18 and has not changed since.

Why it matters now

Board minutes #54 ruled the binding constraint is support hours, not sales — at 5h/week any customer needing the owner personally is one the company cannot afford. Org administration done by hand in the Firestore console is exactly that kind of unaffordable per-customer time, and it scales linearly with customers.

Suggested scope

  1. Add org_id, org_name and org_role to _format_user() and render them as a column plus a detail field.
  2. Allow a platform admin to set a user's org and org role via the existing PATCH /admin/users/{uid}.
  3. Add an org filter to the Users tab.
  4. An organizations list is the natural companion but is genuinely separate — there is still no GET /admin/organizations and no route reads the organizations collection except org.py:38 (single org, caller-scoped).

Related, and deliberately not duplicated

  • #4 — /admin reads are not org-scoped (a data-leak problem; this issue is the inverse, org data being absent where it is wanted)
  • #9 — member/node writes are platform-admin-only, blocking org owners (the same seam from the org owner side)
  • #3 — billing UI is mock data
Owner asked for role/user management to be UI-configurable by an admin. **Platform-role management already is** — verified in source, so this issue is only the gap that remains. ## What already works (do not rebuild) `/admin` Users tab, backed by `drb-c2-core/app/routers/users.py`, is complete for platform roles: | Action | Backend | UI | |---|---|---| | List users | `GET /admin/users` (`users.py:85`) | Users tab | | Create + invite | `POST /admin/users` (`users.py:97`) | `app/admin/page.tsx:583+` | | Change role | `PATCH /admin/users/{uid}` (`users.py:175`) | role dropdown, `page.tsx:462-474` | | Set owned nodes | same PATCH | shown when role is `operator`, `page.tsx:474` | | Disable / enable | `POST .../disable`, `.../enable` (`users.py:229,253`) | Users tab | | Delete | `DELETE /admin/users/{uid}` (`users.py:273`) | Users tab | Roles are `admin` / `operator` / `viewer`, all editable from the dropdown. ## The actual gap: orgs are invisible to it `_format_user()` (`users.py:55-70`) returns `uid`, `email`, `display_name`, `role`, `owned_node_ids`, `disabled`, timestamps and Discord link — **no `org_id`, no `org_role`, no org name**. Confirmed by grep: **zero** references to `org_id` / `orgId` / `org_role` anywhere under `drb-frontend/app/admin/`. Consequences for a platform operator today: - Cannot tell which org any user belongs to, from any screen. - Cannot move a user between orgs. - Cannot set org role (`owner` vs `member`) from `/admin` — that lives on the separate `/settings/members` surface (`org.py PATCH ""`, `org.py:48`), which is scoped to the caller's own org. So a platform admin managing someone else's org has no route at all. - Cannot answer "who is in this org" without reading `org_members` in the Firestore console. This was flagged as an observation in `ADMIN_BILLING_AUDIT.md` section 1 on 2026-08-18 and has not changed since. ## Why it matters now Board minutes #54 ruled the binding constraint is **support hours, not sales** — at 5h/week any customer needing the owner personally is one the company cannot afford. Org administration done by hand in the Firestore console is exactly that kind of unaffordable per-customer time, and it scales linearly with customers. ## Suggested scope 1. Add `org_id`, `org_name` and `org_role` to `_format_user()` and render them as a column plus a detail field. 2. Allow a platform admin to set a user's org and org role via the existing `PATCH /admin/users/{uid}`. 3. Add an org filter to the Users tab. 4. An organizations list is the natural companion but is genuinely separate — there is still no `GET /admin/organizations` and no route reads the `organizations` collection except `org.py:38` (single org, caller-scoped). ## Related, and deliberately not duplicated - #4 — `/admin` reads are not org-scoped (a data-leak problem; this issue is the inverse, org data being absent where it is wanted) - #9 — member/node writes are platform-admin-only, blocking org owners (the same seam from the org owner side) - #3 — billing UI is mock data
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: logan/server-26#55