Repository navigation
Make the Dashboard always reachable and gate its widgets by permission #1099
Description
Problem
The Dashboard is the landing page (/), but it is all-or-nothing: the page and its menu item require dashboard:view. A user without that permission can't use the landing page at all.
Since #1093 such a user is no longer shown access-denied after sign-in. They are sent to the first page they can open, or shown "No pages are available for your role" when there is none. That removes the dead end. It still leaves those users without a home page, and the Dashboard can't show a restricted user the parts that are relevant to them.
Idea
Every signed-in user can open the Dashboard. Permission is decided per widget: modules contribute widgets, and the Dashboard shows a user only the widgets they are allowed to see. Whether a widget the user can't see is hidden or shown disabled is still to be decided.
Current state
Pages/Index.razorandDashboardMenurequiredashboard:view.DashboardWidgetDescriptorhas no permission metadata. It has an id, a zone, an order, a component type, and an optional backend capability. A widget is visible whenever a snapshot is loaded.- All built-in widgets (metrics, trend, recent activity, needs attention, hotspots) draw from one snapshot, which comes from core's dashboard endpoints: overview, trends, recent activity, needs attention and hotspots.
- Every one of those core endpoints requires the single
dashboard:viewpermission. - So today one permission gates everything. Per-widget gating in Studio needs either per-widget permissions that core also enforces, or widgets mapped onto the domain permissions of the data they show, for example workflow-instance metrics onto
workflows/instances:view.
Questions to settle
- Hidden or disabled? Does a widget the user can't view disappear, or show as a locked placeholder?
- Which permission gates a widget? A new permission per dashboard widget, or the domain permission of the data the widget shows.
- What is
dashboard:viewfor afterwards? Dropped, kept as "see every widget", or kept to gate only the built-in operational widgets. - What does a user with no visible widgets see? An empty-state message, a welcome or shortcut panel, or the feat(shell): send users who can't view the landing page to their first accessible page #1093 redirect to their first accessible page.
- Core: the snapshot endpoints have to enforce the same split, or Studio's gating would be cosmetic.
- Landing redirect (feat(shell): send users who can't view the landing page to their first accessible page #1093): once the Dashboard is always reachable, the redirect and the "no pages" notice are only needed if an empty Dashboard is not an acceptable landing page.
Acceptance criteria (draft, to refine after the design is settled)
- Any signed-in user can open
/without an access-denied message. - Each widget declares the permission it needs, and third-party widgets can do the same.
- A user sees only the widgets they are permitted to see, or sees the rest clearly marked as unavailable.
- Core refuses the data behind a widget the user isn't permitted to see.
- A user with no permitted widgets gets a deliberate experience, not a blank page.
Activity
sfmskywalker commentedon Sep 30, 2026
Design decisions (2026-09-30)
Settled with the maintainer:
workflows/instances:view.DashboardWidgetDescriptorgains required permissions, so third-party widgets declare theirs the same way.MenuServiceExtensions, and replaces the landing redirect and the "no pages" notice from feat(shell): send users who can't view the landing page to their first accessible page #1093.Consequences:
dashboard:view. Every signed-in user can open/.dashboard:viewremains the permission for the built-in operational overview, so existing roles keep working. How it maps onto the data permissions is part of the core change.sfmskywalker commentedon Sep 30, 2026
Implementation plan
The core side is elsa-workflows/elsa-core#8561.
dashboard:viewstill covers everything. Each overview section, and each single-purpose endpoint, also accepts the permission of its data. The overview marks a section the caller can't read asCapability = Unauthorizedinstead of refusing the whole call.Studio:
dashboard:view, so every signed-in user can open/.DashboardWidgetDescriptorandAddDashboardWidgetgain the permissions a widget needs. A widget is visible when the user holds any of them, and a widget that declares none is visible to everyone.dashboard:viewplus the permission of their data:workflows/instances:viewfor instance metrics, trends, activity, findings and hotspots;workflows/runtime:viewfor runtime status; thediagnostics/*permissions for the log summaries.Unauthorizedsection as absent, so a restricted user never sees a 403.MenuServiceExtensions.PermissionPageGuardkeeps that behaviour as a generic fallback for hosts whose/is some other gated page.sfmskywalker commentedon Sep 30, 2026
Picking this up — autonomous delivery started.
feat(dashboard): open the dashboard to every user and gate widgets by…
sfmskywalker commentedon Oct 1, 2026
Delivered in #1103 (bd44366) on release/3.9.0, paired with elsa-workflows/elsa-core#8562.
dashboard:viewplus the permission of its data. Widgets the user can't see are hidden.Unauthorized, and keeps what loaded when a secondary endpoint refuses.dashboard:vieworworkflows/runtime:view.AddDashboardWidgetoverload. See the Dashboard README.