Skip to content

Admin page: tabs, Authentik/CMS status, role security fix, sessions, activity log and system status - #229

Merged
GabrielMSilva04 merged 3 commits into
mainfrom
refactor/admin-page-sections
Sep 28, 2026
Merged

GabrielMSilva04 merged 3 commits into
mainfrom
refactor/admin-page-sections

Conversation

@GabrielMSilva04

@GabrielMSilva04 GabrielMSilva04 commented Sep 27, 2026 •

Copy link
Copy Markdown
Contributor

Admin page

/admin, with tabs kept in the URL (?tab=…). The old /admin/roles redirects here.

Tab What it does
Users & roles Authentik banner with an Open Authentik link. Role columns, Last sign-in, a "never signed in with Authentik" filter, and a per-user Sign out everywhere. If AUTHENTIK_TOKEN is missing it explains that instead of showing an empty table.
Content Content is edited in the Directus CMS: Open CMS link, what's edited there (from Infrastructure's managed-tables.txt) and who can edit (cms-manager).
Arraial The existing settings, grouped into Page, Extras and Reset. No change in behaviour.
Activity Role changes, sign-outs and Arraial changes and resets, newest first.
System Environment, deployed commit, DB migration vs code, enabled extensions, integrations on/off. Warns if the DB is behind or if email/reCAPTCHA is off in production.
Your account Your name and scopes, plus links to the Family manager and the Arraial scoreboard.

Backend

  • New admin-only endpoints: GET /admin/authentik/status, GET /admin/cms, POST /admin/users/{id}/sign-out, GET /admin/activity, GET /admin/system.
  • Migration d6f8b0c2e4a7: adds user.last_login_at (set when a session is created, not on refresh) and an admin_activity table. Activity entries copy names in and have no foreign keys, so the log outlives the accounts it mentions.
  • Group-to-role mapping is shared between login and the admin endpoints (group_role_name).
  • GIT_COMMIT is passed by the deploy workflow through compose.prod.yml, so the System tab can show what's live.

Known limits

  • Sign out everywhere ends every session so they can't be refreshed. An already-issued access token still works for up to an hour; the confirmation says so.
  • Last sign-in reads "never" for everyone until their first sign-in after this deploy.
  • Not changed here: at sign-in, stored scopes only update when Authentik sends a non-empty list (if scopes and … in oidc.py). Someone removed from every role group keeps their old roles. This may be deliberate, as protection against a missing claim wiping everyone's roles, so it deserves its own decision.

Deploy notes

  • The migration runs in the existing api_nei_migrate step.
  • After deploy, Authentik groups that don't grant a role (e.g. authentik Admins, authentik Read-only, default) no longer appear in the table. That's intended; they're managed in Authentik.

Tests

  • Backend: 354 passed (new tests for the allowlist and 403s, scope removal with and without another granting group, sign-out, activity log, system status, last_login_at). mypy is clean.
  • Frontend: 188 passed (38 on the admin page). npm run build passes.

…e Authentik groups

Security: the Authentik group endpoints listed every group and accepted any
group id, so a platform admin could add anyone (themselves included) to
Authentik's own "authentik Admins" group, i.e. SSO superuser. Groups are now
limited to those that grant a platform role (except "default") or
cms-manager; add/remove look the group up and return 403 for anything else.
Group-to-role normalisation is shared with the OIDC login path.

Admin page:
- /admin with tabs kept in the URL (?tab=...); /admin/roles redirects there
- Users & roles: Authentik banner with a link to its admin UI, backed by a
  new GET /admin/authentik/status; explains a missing AUTHENTIK_TOKEN
  instead of rendering an empty table; columns show the role granted
- Content: points to the Directus CMS (GET /admin/cms), lists what is edited
  there and who may edit it
- Arraial and Your account moved to their own tabs, unchanged in behaviour
- load failures now surface an error instead of being swallowed
…em status

Removing a role now takes effect: role removal also drops the role from the
user's stored scopes (unless another group still grants it), since a session
refresh reissues whatever is stored and those were only updated at sign-in.

- POST /admin/users/{id}/sign-out ends every session of a user
- user.last_login_at, set when a new session is created (not on refresh)
- admin_activity table + GET /admin/activity: role changes, sign-outs,
  Arraial settings and resets. Names are copied into each entry and there
  are no foreign keys, so the log outlives the accounts it mentions
- GET /admin/system: commit (GIT_COMMIT, now passed by the deploy workflow),
  DB migration vs code head, enabled extensions, integrations on/off
- Admin page: Last sign-in column, "never signed in" filter, per-user sign
  out, Activity and System tabs, links to other admin pages
- explicit None defaults on optional response fields; drop response_model
  where it duplicates the return annotation; Annotated Query params;
  USER_NOT_FOUND constant
- one throwing call per pytest.raises block
- tab arrow-key handling on the focusable tab buttons, not the tablist
- guard against empty CMS / system status responses
- success toast is an <output>; static banners drop the live-region role
- pending state starts as "" so its string comparisons type-check
@sonarqubecloud

Copy link
Copy Markdown

@GabrielMSilva04
GabrielMSilva04 merged commit a044e42 into main Sep 28, 2026
10 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.

1 participant