diff --git a/CHANGELOG.md b/CHANGELOG.md index f6ebb37..9b51809 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ CalVer image tags (`stable-YYYY.MM.N`, `dev-YYYY.MM.N`) are produced for every C - Corporate memory pages (`/corporate-memory`, `/corporate-memory/admin`) now render the shared app header at full viewport width, matching the dashboard. Previously the `_app_header.html` include sat inside `.container-memory` (max-width: 1000px) and was cropped on wide viewports. - `release.yml` now publishes a `:dev-` + `:dev--latest` image when a fresh branch is pushed off `main` with no extra commits. Pre-fix, `paths-ignore` on the `push` event diffed the new ref against the default branch — a same-SHA branch had zero diff, every file matched paths-ignore, and the workflow was skipped, so a developer creating a personal branch off main to deploy main's exact state to their dev VM (which pins to `:dev--latest`) had to either commit something or trigger the workflow manually. The `build-and-push` job's `if` was also tightened to `main || workflow_dispatch` only, which prevented branch-push images regardless. Both fixed: added `create:` trigger (filtered to branch refs at the job level so tag creates don't double-build with `keboola-deploy.yml`), and broadened `build-and-push.if` to also publish on non-main branch pushes / branch creates. +- Web header admin nav (All tokens, Marketplaces, Admin → Users / Groups / Resource access / Server config) is now visible to admin users again. Pre-fix, `_app_header.html` gated the admin block on `session.user.role == 'admin'`, but the v13 RBAC migration nulled `users.role` and moved admin authority onto `user_group_members` (Admin system group) — so the gate evaluated to false for everyone, including actual admins. `get_current_user` now injects `user["is_admin"]` (computed via `app.auth.access.is_user_admin`, the same call all server-side admin gates use), and the header reads `session.user.is_admin`. The role badge in the user-menu dropdown now reads "Admin" or hides — `users.role` is no longer surfaced in the UI. ## [0.15.0] — 2026-04-29 diff --git a/app/auth/dependencies.py b/app/auth/dependencies.py index 3b41e4a..84f6086 100644 --- a/app/auth/dependencies.py +++ b/app/auth/dependencies.py @@ -158,6 +158,7 @@ async def get_current_user( if is_local_dev_mode(): user = _get_local_dev_user(conn) if user: + _attach_admin_flag(user, conn) return user # Fall through to normal auth if seed missing — surfaces the bug # instead of hiding it. @@ -181,6 +182,7 @@ async def get_current_user( from app.auth.pat_resolver import resolve_token_to_user user, reason = resolve_token_to_user(conn, token, request) if user: + _attach_admin_flag(user, conn) return user raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, @@ -188,6 +190,29 @@ async def get_current_user( ) +def _attach_admin_flag(user: dict, conn: duckdb.DuckDBPyConnection) -> None: + """Inject ``user["is_admin"]`` so templates and route handlers can gate + admin-only UI without touching the legacy ``users.role`` column. + + v13 nulled out ``users.role`` and moved admin authority onto + ``user_group_members`` (Admin system group). The web header used to + gate its admin nav on ``session.user.role == 'admin'``, which silently + became false for every user — so no admin saw any admin menu items + after the v13 migration. Computing the flag once per request here + keeps every consumer in sync with ``app.auth.access.is_user_admin`` + (the same call all server-side admin gates use). + """ + from app.auth.access import is_user_admin + user_id = user.get("id") + if user_id: + try: + user["is_admin"] = is_user_admin(user_id, conn) + except Exception: + user["is_admin"] = False + else: + user["is_admin"] = False + + async def get_optional_user( request: Request = None, authorization: Optional[str] = Header(None), diff --git a/app/web/templates/_app_header.html b/app/web/templates/_app_header.html index 7013aba..f1195dd 100644 --- a/app/web/templates/_app_header.html +++ b/app/web/templates/_app_header.html @@ -12,7 +12,7 @@ {% set _path = request.url.path %} Dashboard Install CLI - {% if session.user.role == 'admin' %} + {% if session.user.is_admin %} All tokens Marketplaces {% set _admin_active = _path.startswith('/admin/users') or _path.startswith('/admin/groups') or _path.startswith('/admin/access') or _path.startswith('/admin/server-config') %} @@ -50,8 +50,8 @@