You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
PegaProx 0.9.9.3 dashboard's plugin frontend tab filter (added in #381) gates rendering on user.permissions.includes('plugins.view'), but for built-in admin users that personal permissions field is empty and the plugins.view permission lives only on the admin role definition. Result: admins never see plugin frontend tabs (or any other gated tab — site-recovery, plugins, compliance, plugin:*).
Reproduction
Tested on a clean PegaProx 0.9.9.3 install (LXC, archive update path).
The dashboard correctly synthesises the tab list — pluginFrontendTabs produces [{ id: 'plugin:docker_swarm', label: 'Docker Swarm Manager' }]. The plugin would render given any non-empty user permissions.
The blocking line lives in the tab .filter (paraphrased from the bundled JSX, comment block credits NS May 2026 / #381):
```js
.filter(tab => {
if (tab.id === 'site-recovery') return user?.permissions?.includes('site_recovery.view');
if (tab.id === 'plugins') return user?.permissions?.includes('plugins.view');
if (tab.id === 'compliance') return user?.permissions?.includes('admin.audit') || user?.permissions?.includes('node.maintenance');
if (typeof tab.id === 'string' && tab.id.startsWith('plugin:'))
return user?.permissions?.includes('plugins.view');
return true;
})
```
So user.permissions is empty for the two built-in admins, but the admin role they hold owns the gating permissions. The .filter checks the personal list directly and never consults the role, so every gated tab is hidden. This affects:
A reasonable fix is to compute an effective-permissions set client-side as user.permissions ∪ role.permissions[user.role], or have the server hydrate user.permissions from the role on /api/users / login. The latter is probably the more sustainable choice — every other place that reads user.permissions would benefit from the same fix.
Workarounds (for users hit by this today)
SQL: UPDATE users SET permissions = json('[\"plugins.view\"]') WHERE username = 'alfonso'; (UI doesn't surface per-user permission edits without going outside the admin role).
Navigate directly to https://<host>/api/plugins/<id>/api/ui — the plugin UI works, you just can't reach it from the tab strip.
Affected scope
Built-in admin role users with empty personal permissions. Fresh installs and SSO-provisioned admins are the typical case.
Custom roles do not show this — those users have non-empty permissions populated when their role was assigned.
Cross-link
This was found while validating pegaprox-docker-swarm v1.16.0, the plugin that #286 produced and #381 unblocked. v1.16.0 deletes ~1130 LOC of dashboard.js patcher infra in favour of the manifest-declared frontend hook. Backend integration verified, plugin UI verified end-to-end via direct route — only the tab strip discoverability is blocked by this gate.
Summary
PegaProx 0.9.9.3 dashboard's plugin frontend tab filter (added in #381) gates rendering on
user.permissions.includes('plugins.view'), but for built-in admin users that personalpermissionsfield is empty and theplugins.viewpermission lives only on theadminrole definition. Result: admins never see plugin frontend tabs (or any other gated tab —site-recovery,plugins,compliance,plugin:*).Reproduction
Tested on a clean PegaProx 0.9.9.3 install (LXC, archive update path).
has_frontend: true+frontend_route: \"ui\"in its manifest. I usedpegaprox-docker-swarmv1.16.0 which exists specifically to be [Feature] Plugin system: support embedded frontend UIs (iframe tab) + expose has_frontend in API #381-ready.INSERT INTO plugin_state VALUES('docker_swarm', 1, ...)).Diagnosis (verified end-to-end via Playwright + API + source)
/api/pluginsreturns the plugin with the correct shape:```json
{
"id": "docker_swarm",
"name": "Docker Swarm Manager",
"version": "1.16.0",
"enabled": true,
"loaded": true,
"error": "",
"has_frontend": true,
"frontend_route": "/api/plugins/docker_swarm/api/ui",
"routes": [...55 routes...]
}
```
The dashboard correctly synthesises the tab list —
pluginFrontendTabsproduces[{ id: 'plugin:docker_swarm', label: 'Docker Swarm Manager' }]. The plugin would render given any non-empty user permissions.The blocking line lives in the tab
.filter(paraphrased from the bundled JSX, comment block credits NS May 2026 / #381):```js
.filter(tab => {
if (tab.id === 'site-recovery') return user?.permissions?.includes('site_recovery.view');
if (tab.id === 'plugins') return user?.permissions?.includes('plugins.view');
if (tab.id === 'compliance') return user?.permissions?.includes('admin.audit') || user?.permissions?.includes('node.maintenance');
if (typeof tab.id === 'string' && tab.id.startsWith('plugin:'))
return user?.permissions?.includes('plugins.view');
return true;
})
```
API state on a fresh install:
GET /api/users[username=alfonso].permissions[]GET /api/users[username=alfonso].role\"admin\"GET /api/users[username=pegaprox].permissions[]GET /api/users[username=pegaprox].role\"admin\"GET /api/roles[id=admin].permissions.length123GET /api/roles[id=admin].permissions ⊇ {plugins.view, plugins.manage, site_recovery.view, admin.audit, …}trueSo
user.permissionsis empty for the two built-in admins, but theadminrole they hold owns the gating permissions. The.filterchecks the personal list directly and never consults the role, so every gated tab is hidden. This affects:plugins(the management tab)plugin:*(every plugin frontend tab — i.e. every plugin that adopts [Feature] Plugin system: support embedded frontend UIs (iframe tab) + expose has_frontend in API #381)site-recoverycomplianceA reasonable fix is to compute an effective-permissions set client-side as
user.permissions ∪ role.permissions[user.role], or have the server hydrateuser.permissionsfrom the role on/api/users/ login. The latter is probably the more sustainable choice — every other place that readsuser.permissionswould benefit from the same fix.Workarounds (for users hit by this today)
UPDATE users SET permissions = json('[\"plugins.view\"]') WHERE username = 'alfonso';(UI doesn't surface per-user permission edits without going outside the admin role).https://<host>/api/plugins/<id>/api/ui— the plugin UI works, you just can't reach it from the tab strip.Affected scope
adminrole users with empty personalpermissions. Fresh installs and SSO-provisioned admins are the typical case.permissionspopulated when their role was assigned.Cross-link
This was found while validating
pegaprox-docker-swarmv1.16.0, the plugin that #286 produced and #381 unblocked. v1.16.0 deletes ~1130 LOC of dashboard.js patcher infra in favour of the manifest-declared frontend hook. Backend integration verified, plugin UI verified end-to-end via direct route — only the tab strip discoverability is blocked by this gate.— Alfonso (cc @MrMasterbay @mkellermann97 from #381)