Skip to content

feat(admin): read-only viewer role, administrator-only console SSO mapping - #59

Closed
jogunjobi wants to merge 3 commits into
mainfrom
feat/viewer-role
Closed

jogunjobi wants to merge 3 commits into
mainfrom
feat/viewer-role

Conversation

@jogunjobi

Copy link
Copy Markdown
Contributor

First of two PRs for console user management (adding console users with types: Administrator, Read-write, Read-only, Custom). This one adds the role and the safety fix the second PR builds on.

  • viewer role: reads every console page and read API an operator role can (it is added to every read rule that names a role), and changes nothing (no mutation rule or fallback admits it). A new test walks every route in the admin app and fails if any state-changing route admits viewer, or any /dashboard or /api route has no role rule. Dry-runs and Test Connection stay write-only: they use stored credentials or belong to editing flows.
  • Security: setting the SSO subject of a console account (/api/admin-auth/oidc/admins/*) now needs owner/admin. Previously security_admin could map its own SSO identity to an owner's account and sign in through SSO as that owner. Agent mappings still need security_admin.
  • Roles defined once in admin/roles.py (middleware, SSO allowlist, generated docs, and the user types the next PR uses).
  • Refused actions explained: htmx does not swap 4xx responses, so a refused click did nothing. A notice now says why (403, CSRF, 401, network). Read-only accounts get a sidebar label.

Tests: new viewer and SSO-mapping RBAC tests, role-helper and template tests; mutations viewer-added-to-mutation-fallback and oidc-admin-mapping-reopened-to-security-admin are both caught. Local: lint, type, 3394 unit/dashboard/integration tests, docs-check pass. Docs: admin-access page, regenerated admin-roles and admin-api references, CHANGELOG.

…pping

Adds a viewer role that opens every console page and read API an operator
role can, and changes nothing: it is added to every read rule that names a
role and to no mutation rule or fallback, which a test checks against every
route in the app. Roles now have one definition, admin/roles.py, used by the
middleware, the SSO allowlist and the generated admin-roles page.

Setting the SSO subject of a console account now needs owner or admin.
Before, security_admin could map its own SSO identity to an owner's account
and sign in as that owner; agent mappings still need security_admin.

A refused htmx request now shows a notice saying why, instead of the click
doing nothing, and read-only accounts are labelled in the sidebar.
@jogunjobi jogunjobi added the e2e Run the end-to-end suite on this pull request label Oct 15, 2026
@jogunjobi
jogunjobi requested a review from a team as a code owner October 15, 2026 05:13
@jogunjobi jogunjobi closed this Oct 15, 2026
@jogunjobi jogunjobi reopened this Oct 15, 2026
jogunjobi added a commit that referenced this pull request Oct 15, 2026
…pping (#59)

Adds a viewer role that opens every console page and read API an operator
role can, and changes nothing: it is added to every read rule that names a
role and to no mutation rule or fallback, which a test checks against every
route in the app. Roles now have one definition, admin/roles.py, used by the
middleware, the SSO allowlist and the generated admin-roles page.

Setting the SSO subject of a console account now needs owner or admin.
Before, security_admin could map its own SSO identity to an owner's account
and sign in as that owner; agent mappings still need security_admin.

A refused htmx request now shows a notice saying why, instead of the click
doing nothing, and read-only accounts are labelled in the sidebar.
@jogunjobi

Copy link
Copy Markdown
Contributor Author

Landed on main as a4d038a (squashed; change identical to the PR diff).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

e2e Run the end-to-end suite on this pull request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant