Skip to content

Extensions: take admin from is_staff, cap workspace roles at auditor - #84

Open
avalyset wants to merge 2 commits into
SimulaMet:mainfrom
avalyset:fix/extensions-instance-roles
Open

avalyset wants to merge 2 commits into
SimulaMet:mainfrom
avalyset:fix/extensions-instance-roles

Conversation

@avalyset

@avalyset avalyset commented Nov 6, 2026

Copy link
Copy Markdown
Contributor

Follow-up to #83, from the comment on #77 (direction 1 there), limited to the extensions.

What changes

user_level() in infra/extensions.py gave a user the highest role they held in any workspace. Any signed-in user can create a workspace and become its admin (create_workspace in accounts/services.py), so any user could reach admin-only extensions: the menu entry, /x/<slug>/ and /ext/authz?role=admin.

Now superuser comes from is_superuser and admin from is_staff. Workspace roles still count, but only up to auditor. The Roles section of docs/extensions.md describes the new rule. chat/config.py is not touched.

Reproduction

infra/tests/test_extensions_self_admin.py is the test from the #77 comment, unchanged. A user creates a workspace through the service, through the API, or by self-registering and then using the API. After that, the user must not see the admin-only menu entry, open its embed page or pass /ext/authz?role=admin. All three tests fail on main 254d63d and pass on this branch.

Existing tests that changed (infra/tests/test_extensions.py)

  • setUp (lines 88–90): self.admin was a workspace admin. It is now a staff user with a viewer row, which only gives it an active workspace. The tests that use it check the same things as before.
  • test_authz (line 188): X-Studio-Role for that user is now user, not admin. The header still comes from chat.config.identity(), which maps only superusers and workspace admins to the Open WebUI admin role, and this user has no workspace admin row.
  • New, test_workspace_roles_count_up_to_auditor (lines 168–182): a workspace admin without is_staff sees an auditor entry but not the admin one, has role: "auditor" in window.Studio, and gets 200 from /ext/authz?role=auditor and 403 from ?role=admin.

Checked

  • uv run pytest -m "not embedded_hatchet" -n 4: 1681 passed, 2 skipped and 2 failed on this branch; 1677 passed, 2 skipped and the same 2 failed on main 254d63d. The 4 extra passes are the new tests. Both failures also fail on their own, on main as on the branch, and come from this machine: test_live_clef_flash_answers_and_renders runs because a local Ollama serves clef-flash, and test_time_wait_leftovers_do_not_block_but_a_live_listener_does gets ConnectionResetError on macOS.
  • With the main version of infra/extensions.py, all 8 of these fail: the three reproduction tests, the four tests that use self.admin and the new one.
  • uv run ruff check . is clean.

Design questions

  1. Should admin come from is_staff, or would you rather have a separate instance flag? Today only the bootstrap admin gets is_staff, and that user is also a superuser (accounts/services.py:68). Anyone else has to be made staff in Django admin.
  2. A user can still reach viewer and auditor through a workspace they created themselves. Is that acceptable for extensions, which apply to the whole instance?
  3. /ext/authz still copies X-Studio-Role from chat.config.identity(), which follows the rule Workspace admin is treated as a global privilege (Open WebUI admin role, ADMINS connection sharing) #77 is about. On this branch, a user who creates a workspace and calls /ext/authz?role=viewer gets 200 with X-Studio-Role: admin, and a staff user with no workspace admin row gets user. This PR leaves the header to whatever Workspace admin is treated as a global privilege (Open WebUI admin role, ADMINS connection sharing) #77 settles.

AI disclosure: implemented with Claude Code.

…workspace roles at auditor

user_level() gave an extension user the highest role they held in any
workspace. Any signed-in user can create a workspace and become its
admin, so every user could reach admin-only extensions: the menu entry,
/x/<slug>/ and /ext/authz?role=admin.

Extensions are instance-wide, so the admin level now comes from
is_staff and the superuser level from is_superuser. Workspace roles
still count, up to auditor.

The existing extension tests gave their admin a workspace admin row;
that user is now a staff user with a viewer row for the active
workspace. test_extensions_self_admin.py covers the self-created
workspace via the service, the API and self-registration.

This branch has not been deployed

No deployments
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.

1 participant