Skip to content

fix: let tenant admins manage models of their own tenant - #4043

Merged
jeffwu-1999 merged 1 commit into
developfrom
fix/tenant-admin-model-manage-scope
Sep 29, 2026
Merged

jeffwu-1999 merged 1 commit into
developfrom
fix/tenant-admin-model-manage-scope

Conversation

@jeffwu-1999

@jeffwu-1999 jeffwu-1999 commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor

fix: let tenant admins manage models of their own tenant

#4008 restricted the cross-tenant /model/manage/* endpoints to the SU role.
The whitelist could not tell a foreign-tenant call from one naming the
caller's own tenant, so ADMIN users lost the whole Models tab on
/resource-manage: manage/list returned 403 and the page rendered an empty
table without surfacing an error.

  • Replace _require_manage_role with _require_manage_scope: SU may target any
    tenant, ADMIN only the tenant its bearer token belongs to, and every other
    role is rejected as before
  • Allow ADMIN in _MANAGE_ALLOWED_ROLES; the role still has to be checked
    separately because ADMIN and SU share the same model:* permission seeds
  • Apply the scope check to all 8 /manage/* endpoints (list, create, update,
    delete, batch_create, healthcheck, provider/list, provider/create)
  • Update the module docstring to describe the role + tenant scope contract
  • Reject the ADMIN cross-tenant test's blind spot: the old case used a foreign
    tenant_id, which is why losing own-tenant access went uncaught
  • Add own-tenant list/update coverage, a foreign-tenant update case, and a DEV
    case proving require(model:read) alone cannot reach the manage surface

#4008 closed a horizontal-privilege hole on /model/manage/* by restricting
the endpoints to the SU role. The whitelist did not distinguish a
cross-tenant call from one naming the caller's own tenant, so ADMIN users
lost the whole Models tab on /resource-manage: manage/list returned 403 and
the page rendered an empty table with no error, because the create, update,
delete, healthcheck and provider endpoints share the same guard.

The page always sends the caller's own tenant_id (UserManageComp falls back
to user.tenantId for non-SU), so rejecting ADMIN blocked no cross-tenant
access -- it only broke tenant admins managing their own models.

Replace the role whitelist with a role + tenant scope check: SU may target
any tenant, ADMIN only the tenant its token belongs to, and every other role
is rejected as before. ADMIN and SU share the same model:* permission seeds,
so the role itself still has to be checked -- permissions alone cannot
separate them.

The existing ADMIN cross-tenant test kept passing because it used a foreign
tenant_id, which is why the regression went uncaught. Add own-tenant
coverage for list and update, plus a DEV case proving require(model:read) is
not sufficient on its own to reach the manage surface.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@codecov

codecov Bot commented Sep 29, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@jeffwu-1999
jeffwu-1999 merged commit 89d9274 into develop Sep 29, 2026
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants