Skip to content

adopt v-permission where the UI doesn't gate an admin-only action #16494

Description

@mrveiss

Follow-up from #16243

#16243 wired up a real v-permission directive and usePermissions()
composable, but adopting it anywhere is a separate decision -- this issue
names the actual gaps rather than guessing.

Existing gates (for reference -- the right pattern already in use)

File:line What it gates Mechanism
router/index.ts (/admin/users, /admin/sandbox, /admin/advanced-control, /admin/budget-policies, /admin/system-health, /admin/provider-fallback, /secrets/llm-keys) Route-level admin views meta.admin: true, enforced by the nav guard
views/AgentRegistryView.vue:166,354 Agent runtime config panel v-if="isAdmin"
App.vue:159,307 Sidebar "Admin" nav section v-if="userStore.isAdmin"
views/UsageView.vue:21,51 Org-wide usage export/tab v-if="isAdmin"
views/BudgetPolicies.vue:115, views/SecretsView.vue:28, components/knowledge/KnowledgeScopeSelector.vue:48 Belt-and-suspenders / nav-visibility only v-if="isAdmin"

None of these currently use v-permission or usePermissions() -- they
predate it and use userStore.isAdmin directly, which is fine for a pure
admin/non-admin binary. v-permission matters once a gate needs to be
finer than "admin or not" (a specific Permission), which is a separate
call for whoever picks up one of the items below.

Candidate gaps -- frontend doesn't gate, but the backend action does

Component What it does Backend gate
components/security/SecretsManager.vue + composables/security/useSecretsInfraApi.ts Renders an "Infrastructure Hosts" category; lists/deletes hosts via GET/DELETE /api/infrastructure/hosts autobot-backend/api/infrastructure.py -- already being fixed by open PR #16442/#16426 (admin-gates both routes AND hides the category from non-admins in this same component). No action needed here once that merges.
components/settings/FeatureFlagsSettingsPanel.vue (tab + panel in views/SettingsView.vue) Feature-flag enforcement mode, endpoint enforcement, flag history, access metrics autobot-backend/api/feature_flags.py -- every route depends on require_admin
components/settings/TelemetrySettingsPanel.vue (in views/SettingsView.vue) Global telemetry-enabled toggle autobot-backend/api/settings.py -- POST /api/settings/telemetry requires check_admin_permission (GET is intentionally public)
views/slm/ThemeManagerView.vue (route has no meta.admin) Theme upload/install/uninstall UI autobot-backend/api/themes.py -- install/uninstall depend on check_admin_permission; list/asset GETs are open

Each of these lets a non-admin see and attempt an action that already 403s
server-side -- a confusing dead-end rather than a hidden control, not a
privilege-escalation bug (the backend still refuses it), but still the
wrong UX and worth fixing with the same v-if="isAdmin" pattern already
used elsewhere (or v-permission if a finer-grained gate turns out to be
needed for one of them).

Acceptance criteria

Refs #16243.

Activity

  1. added this to the v0.11.0 milestone on Sep 14, 2026
  2. mrveiss commented on Sep 23, 2026

    @mrveiss
    OwnerAuthor

    Closure evidence — each criterion checked against merged code in origin/main (f72de7e)

    Severity first, because it decides how to read the rest: the backend already refuses all three actions, verified before any UI was touched — api/feature_flags.py carries Depends(require_admin) on 8 of 8 routes, POST /api/settings/telemetry takes _: None = Depends(check_admin_permission) (the GET is public by design), and api/themes.py:28,34 gates install and uninstall the same way. So these were confusing dead-ends, not privilege escalation: nothing was reachable, only 403s were.

    • FeatureFlagsSettingsPanel's tab/panel gated on isAdmin. views/SettingsView.vue:74 — v-if="userStore.isAdmin" on the tab button, and :280 — <section v-if="activeTab === 'featureflags' && userStore.isAdmin">. Both halves, so the section cannot be reached even if activeTab is set another way.
    • TelemetrySettingsPanel's admin-only toggle gated, the public GET half still visible. components/settings/TelemetrySettingsPanel.vue:34 — <label v-if="userStore.isAdmin" class="toggle-switch">. Only the control is gated; the state it displays comes from the public GET and still renders for everyone.
    • ThemeManagerView's install/uninstall gated, listing open. views/slm/ThemeManagerView.vue:69 — <template v-if="userStore.isAdmin"> around the upload prompt and file input, and :81 — v-if="userStore.isAdmin" on the Uninstall button. The theme list itself stays visible, matching the open listing GETs.
    • SecretsManager's infrastructure-hosts category: confirmed, no change needed. security(secrets): admin-gate infrastructure hosts, fix vault-backed template names (#16426, #16427) #16442 is MERGED and security(secrets): any logged-in user can list and delete infrastructure hosts through /api/infrastructure/hosts #16426 CLOSED, and components/security/SecretsManager.vue already gates both the category and its quick-add button on userStore.isAdmin.

    v-if with the existing userStore.isAdmin pattern rather than v-permission, since each of these is a plain admin/non-admin binary — the finer-grained directive is for a gate that needs a specific Permission, which none of these do. No new UI strings, so no locale changes.

    Covered by tests, which the gate did not have before: views/slm/__tests__/ThemeManagerView.test.ts now asserts a non-admin sees the list but neither the file input nor the Uninstall button, and an admin sees both.

    Merged in #17321 (f72de7e), green on the merged head.

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

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions