Skip to content

feat(users): add a settings page for managing users and their roles - #396

Open
dexion wants to merge 1 commit into
vxcontrol:mainfrom
dexion:feat/users-management-page
Open

feat(users): add a settings page for managing users and their roles#396
dexion wants to merge 1 commit into
vxcontrol:mainfrom
dexion:feat/users-management-page

Conversation

@dexion

@dexion dexion commented Aug 25, 2026

Copy link
Copy Markdown

Problem

PentAGI ships user management as REST endpoints only. Everything else about an installation is administrable from the web UI, but to see who has an account — let alone change what they may do — an administrator has to call /api/v1/users/?page=1&pageSize=100&type=init by hand. Accounts created by an OAuth provider on first login are especially invisible: they appear in the database with the User role and nothing in the interface ever mentions them.

Changing a role was not possible at all: PatchUser updated name, status and the password, so role_id was silently ignored no matter what the caller sent.

What this adds

Settings → Users lists every account with its sign-in method (local, or the OAuth provider it came from), role, status and creation time, and offers the administrative actions the API already supports: change a role, block or unblock, delete, and create a local account.

Role changes in PatchUser. role_id is now honoured under three conditions, mirroring what CreateUser already does:

  • the caller holds users.edit;
  • the target role grants no privilege the caller lacks, so the endpoint cannot be used to escalate;
  • the caller is not changing their own role, which would let the last administrator demote themselves and leave the installation without one.

Access control

The page follows the privileges the backend already enforces, so the UI never offers an action the API would refuse:

Capability Privilege Roles holding it by default
See the page and the sidebar entry users.view Admin
Role selector, block / unblock users.edit Admin
Delete users.delete Admin
Add user users.create Admin

Without users.view the page states that the account lacks administrator privileges instead of rendering a table. That is deliberate: the list endpoint narrows its scope to the caller when the privilege is missing, so a table would show the viewer their own single row and read as if the installation had one user.

Your own row shows the role as a plain badge and carries no row actions, matching the self-change rule the API enforces.

Verification

Backend, new tests in users_test.go:

  • an administrator changes another account's role;
  • a caller without users.edit patching itself cannot change its role;
  • an administrator cannot change their own role;
  • a role holding users.edit but fewer privileges than Admin cannot promote anyone to Admin;
  • an unchanged role_id leaves the role alone and still updates the other fields.

Frontend, new tests for the page and the sidebar: the page refuses to load anything without users.view, lists accounts with users.view, keeps roles read-only without users.edit, offers the selector for other users and issues the PUT, never offers a selector for your own row, hides row actions and the create action without the matching privilege; the sidebar hides and shows the Users entry by privilege.

Manually verified against a local stand (PentAGI built from this branch, PostgreSQL, Keycloak-provisioned OAuth accounts): the page lists a local administrator alongside two accounts created through an identity provider, promoting one to Admin persists in the database, and the sidebar entry disappears for an account carrying the User role.

go build ./...                                    ok
go vet ./pkg/server/services/                     ok
go test ./pkg/server/services/                    ok

pnpm exec tsc -b                                  No errors found
pnpm exec vitest run                              79 files, 1375 tests passed
pnpm exec eslint --max-warnings 0 "src/**/*.{ts,tsx}"   No issues found
pnpm exec prettier --check "src/**/*.{ts,tsx}"    All files formatted correctly

/settings/users is added to the e2e route manifest's exclusion list with a reason, the same way /settings/account is handled — it needs a users cassette and a visual baseline before it can join the screenshot sweep.

PentAGI ships user management as REST endpoints only. Everything else about an
installation is administrable from the web UI, but to see who has an account —
let alone change what they may do — an administrator has to call
/api/v1/users by hand. Accounts created by an OAuth provider on first login are
especially invisible: they simply appear in the database with the User role.

This adds Settings -> Users, listing every account with its sign-in method
(local or the OAuth provider it came from), role, status and creation time, and
offering the administrative actions the API already supports: change a role,
block or unblock, delete, and create a local account.

The page is gated by the same privileges the backend enforces. It is only
reachable, and only shown in the settings sidebar, for accounts holding
users.view; the role selector appears with users.edit, the row actions with
users.edit or users.delete, and the create action with users.create. Without
users.view the page explains that the account lacks administrator privileges
rather than rendering an empty table — the backend narrows the list to the
caller in that case, so an empty-looking table would be misleading.

Changing roles needed a small backend addition: PatchUser updated name, status
and password, so a role could not be changed through the API at all. It now
accepts role_id under three conditions, mirroring what CreateUser already does:

- the caller holds users.edit,
- the target role grants no privilege the caller lacks, so the endpoint cannot
  be used to escalate,
- and the caller is not changing their own role, which would let the last
  administrator demote themselves and leave the installation without one.

The same reasoning is reflected in the page: your own row shows the role as a
plain badge and has no row actions.
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