Permission Propagation#
Superset's permission system flows through four distinct layers: backend role definitions → bootstrap data serialization → frontend Redux state → UI component visibility. Understanding the handoff between each layer is critical for debugging missing menu items, adding new capability checks, or modifying role behavior.
1. Backend: Role Definitions and Capability Checks#
Permissions are modeled as PermissionView (PVM) records — a composite of a Permission (action, e.g. can_read) and a ViewMenu (resource, e.g. Dashboard). All PVM lifecycle is managed by SupersetSecurityManager in superset/security/manager.py, which extends Flask-AppBuilder's SecurityManager.
Built-in roles (Admin, Alpha, Gamma, sql_lab) are rebuilt on every superset init via sync_role_definitions(). Custom object-scoped permissions (datasource_access, schema_access, database_access) are never assigned to built-in roles — they are managed per-object via SQLAlchemy event hooks.
Runtime resource access enforcement happens in raise_for_access(), which checks ownership, guest tokens, dashboard roles (if DASHBOARD_RBAC is on), and datasource access — in that order.
2. Backend → Frontend: Bootstrap Serialization#
On every page load, render_app_template() in BaseSupersetView calls bootstrap_user_data(g.user, include_perms=True) and embeds the result as JSON in the HTML template.
bootstrap_user_data calls get_permissions(user), which:
- Calls FAB's
get_user_roles_permissions(user)to get arole_name → [[perm, view], ...]mapping across all of the user's roles. - Separately extracts
datasource_accessanddatabase_accesstuples into a flatpermissionsdict.
The resulting bootstrap payload shape is typed on the frontend as UserWithPermissionsAndRoles:
roles: Record<string, [string, string][]> // role → [[action, view], ...]
permissions: { database_access?: string[], datasource_access?: string[] }
3. Frontend Redux State: Hydration#
The user slice of Redux state is populated from the bootstrap data at app startup. For the dashboard page specifically, hydrateDashboard reads state.user.roles and derives several computed permission booleans that are stored in dashboardInfo :
| Redux key | Permission checked |
|---|---|
dash_edit_perm | canUserEditDashboard(dashboard, user) |
dash_save_perm | canUserSaveAsDashboard(dashboard, user) |
dash_share_perm | findPermission('can_share_dashboard', 'Superset', roles) |
superset_can_explore | findPermission('can_explore', 'Superset', roles) |
superset_can_csv | findPermission('can_csv', 'Superset', roles) |
canUserEditDashboard requires isUserAdmin || isUserDashboardOwner and can_write on Dashboard. canUserSaveAsDashboard adds conditional logic for the DASHBOARD_RBAC feature flag.
4. Core Utility: findPermission#
findPermission(perm, view, roles) is the canonical frontend primitive for permission checking. It is memoized with memoizeOne and scans Object.values(roles) for any [perm_, view_] tuple matching the given (perm, view) pair.
5. UI Component Visibility#
Dashboard chart menu (SliceHeaderControls): The usePermissions hook calls findPermission against state.user.roles via useSelector to derive booleans for chart-level actions :
| Hook output | Permission |
|---|---|
canExplore | can_explore on Superset |
canDownload | can_csv on Superset |
canDrill | can_drill on Dashboard |
canViewQuery | can_view_query on Dashboard |
canViewTable | can_view_chart_as_table on Dashboard |
canDrillBy | (canExplore ∥ canDrill) && canWriteExploreFormData |
canDrillToDetail | (canExplore ∥ canDrill) && canDatasourceSamples |
SliceHeaderControls consumes these values to conditionally render menu items such as "Edit chart", "View query", "View as table", "Download", and "Share".
Dashboard header menu: canEditCrossFilters is read from dashboardInfo.dash_edit_perm in Redux directly via useSelector. The dashboard header kebab menu also gates items like "Embed dashboard" (userCanCurate) and "Share" (userCanShare) from dashboardInfo flags set during hydration.
Flow Diagram#
Key Source Files#
| File | Purpose |
|---|---|
superset/security/manager.py | PVM lifecycle, sync_role_definitions, raise_for_access |
superset/views/utils.py | bootstrap_user_data, get_permissions |
superset-frontend/src/types/bootstrapTypes.ts | UserRoles, UserWithPermissionsAndRoles types |
superset-frontend/src/utils/findPermission.ts | Core permission lookup primitive |
superset-frontend/src/hooks/usePermissions.ts | Chart-level permission hooks |
superset-frontend/src/dashboard/actions/hydrate.js | Permission booleans in dashboardInfo |
superset-frontend/src/dashboard/util/permissionUtils.ts | canUserEditDashboard, canUserSaveAsDashboard |
superset-frontend/src/dashboard/components/SliceHeaderControls/index.tsx | Dashboard chart context menu |