<#1144 Add global RBAC roles: user management, env...
# osctrl
g
#1144 Add global RBAC roles: user management, env management, read-only, alert admin Pull request opened by javuto Granular RBAC, part 1: global roles Part of #704 and #683. Problem "Global admin" was one flag. It bundled harmless powers (reading the audit log, alerts, health) with the most dangerous one (managing users, which lets you grant yourself anything). There was no way to hand out alerts or read-only oversight without handing out everything. Change Global admin is split into a superuser plus four separately grantable roles: | Role | Can do | | --------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Superuser (existing admin flag) | Everything. Sole owner of service config, settings, log sinks, auth providers, the extensions catalog, global osquery versions and vuln sync. | | User management | Create, edit and delete users; set their permissions and non-superuser roles; manage other users' tokens. Cannot grant superuser, or edit, delete or re-token a superuser. | | Environment management | Create environments, becoming env admin of the ones it creates. Rename, update and delete only environments where it is env admin. | | Admin read-only | Global read endpoints: users, audit log (all users), events, health, environment map, platforms, all tags, node lookup, vuln feeds, alerts. | | Alert admin | Manage alert rules and channels. | Details: • Model: four additive boolean columns on `AdminUser`:
user_admin
,
env_manager
,
read_only_admin
,
alert_admin
. Role checks live in
pkg/users/roles.go
(
HasRole
,
HasRoleContext
,
SetRoles
,
CheckManageUser
) and reuse the per-request permission cache. • API: role fields are on
AdminUserView
,
GET /users/me
and
PATCH /users/me
.
ApiUserRequest
takes them as optional booleans; leaving a field out leaves that role unchanged. • Log-content alert rules (
result_log
,
query_log
,
status_log
) stay superuser-only for now. That covers creating, editing and deleting them, the channels they use (update, delete and test-send), and their alert history. History for non-superusers fails closed: only rows from currently existing non-log-content rules, filtered in the query before the limit. This gets relaxed in part 2. • Alert channel secrets (
reveal=1
) are revealed only to superusers. • Environment names must be unique on create and rename. • CLI:
osctrl-cli user add
and
user edit
take
--user-admin
,
--env-manager
,
--read-only-admin
and
--alert-admin
, plus
--no-…
variants on edit. Both the DB and API paths work. • OpenAPI is regenerated. Security / operational impact • Upgrade is a no-op for access. Existing global admins become superusers with no data migration. New columns default to false. Existing environment permissions are unchanged. • New escalation paths were reviewed and closed. User admin → superuser is blocked and fails closed on DB errors. An environment manager can no longer take over another environment by renaming it and recreating its name. Alert admins can't exfiltrate log content or channel secrets. • Known gap, not fixed here: nodes are linked to environments by name, and rename/delete don't migrate or remove
osquery_nodes
. After a superuser renames or deletes an environment, someone who recreates that name inherits its orphaned nodes. Marked with a
shortcut:
comment; follow-up PR. • By design, per the spec: user admins can grant themselves any per-environment access and any non-superuser role. • No frontend changes in this PR. The UI keeps keying off
admin
until part 5, so the new roles are grantable through the API and CLI only. Validation • `go test ./...`: pass • `gofmt`: clean • `make openapi-check`: clean • `cd frontend && npm run check`: pass • New tests:
pkg/users/roles_test.go
and
cmd/api/handlers/{users,environments,readonly}_roles_test.go
, plus additions to
alerts_test.go
and
cmd/cli/user_roles_test.go
. They cover every role × endpoint gate, check the database after each refusal, and test the escalation paths above. • `golangci-lint`: 13 errcheck warnings, all in files this PR doesn't touch. • Not run: Playwright (no frontend change). jmpsec/osctrl