Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
379 changes: 190 additions & 189 deletions RESOURCES/STANDARD_ROLES.md

Large diffs are not rendered by default.

16 changes: 14 additions & 2 deletions docs/docs/configuration/networking-settings.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,20 @@ Restart Superset for this configuration change to take effect.

#### Making a Dashboard Public

1. Add the `'DASHBOARD_RBAC': True` [Feature Flag](https://github.com/apache/superset/blob/master/RESOURCES/FEATURE_FLAGS.md) to `superset_config.py`
2. Add the `Public` role to your dashboard as described [here](https://superset.apache.org/docs/using-superset/creating-your-first-dashboard/#manage-access-to-dashboards)
There are two approaches to making dashboards publicly accessible:

**Option 1: Dataset-based access (simpler)**
1. Set `PUBLIC_ROLE_LIKE = "Public"` in `superset_config.py`
2. Grant the Public role access to the relevant datasets (Menu → Security → List Roles → Public)
3. All published dashboards using those datasets become visible to anonymous users

**Option 2: Dashboard-level access (selective control)**
1. Set `PUBLIC_ROLE_LIKE = "Public"` in `superset_config.py`
2. Add the `'DASHBOARD_RBAC': True` [Feature Flag](https://github.com/apache/superset/blob/master/RESOURCES/FEATURE_FLAGS.md)
3. Edit each dashboard's properties and add the "Public" role
4. Only dashboards with the Public role explicitly assigned are visible to anonymous users

See the [Public role documentation](/docs/security/security#public) for more details.

#### Embedding a Public Dashboard

Expand Down
100 changes: 95 additions & 5 deletions docs/docs/security/security.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,62 @@ to all databases by default, both **Alpha** and **Gamma** users need to be given

### Public

To allow logged-out users to access some Superset features, you can use the `PUBLIC_ROLE_LIKE` config setting and assign it to another role whose permissions you want passed to this role.
The **Public** role is the most restrictive built-in role, designed specifically for anonymous/unauthenticated
users who need to view dashboards. It provides minimal read-only access for:

- Viewing dashboards and charts
- Using interactive dashboard filters
- Accessing dashboard and chart permalinks
- Reading embedded dashboards
- Viewing annotations on charts

The Public role explicitly excludes:
- Any write permissions on dashboards, charts, or datasets
- SQL Lab access
- Share functionality
- User profile or admin features
- Menu access to most Superset features

Anonymous users are automatically assigned the Public role when `AUTH_ROLE_PUBLIC` is configured
(a Flask-AppBuilder setting). The `PUBLIC_ROLE_LIKE` setting is **optional** and controls what
permissions are synced to the Public role when you run `superset init`:

For example, by setting `PUBLIC_ROLE_LIKE = "Gamma"` in your `superset_config.py` file, you grant
public role the same set of permissions as for the **Gamma** role. This is useful if one
wants to enable anonymous users to view dashboards. Explicit grant on specific datasets is
still required, meaning that you need to edit the **Public** role and add the public data sources to the role manually.
```python
# Optional: Sync sensible default permissions to the Public role
PUBLIC_ROLE_LIKE = "Public"

# Alternative: Copy permissions from Gamma for broader access
# PUBLIC_ROLE_LIKE = "Gamma"
```

If you prefer to manually configure the Public role's permissions (or use `DASHBOARD_RBAC` to
grant access at the dashboard level), you do not need to set `PUBLIC_ROLE_LIKE`.

**Important notes:**

- **Data access is still required:** The Public role only grants UI/API permissions. You must
also grant access to specific datasets necessary to view a dashboard. As with other roles,
this can be done in two ways:

- **Without `DASHBOARD_RBAC`:** Dashboards only appear in the list and are accessible if
the user has permission to at least one of their datasets. Grant dataset access by editing
the Public role in the Superset UI (Menu → Security → List Roles → Public) and adding the
relevant data sources. All published dashboards using those datasets become visible.

- **With `DASHBOARD_RBAC` enabled:** Anonymous users will only see dashboards where the
"Public" role has been explicitly added in the dashboard's properties. Dataset permissions
are not required—DASHBOARD_RBAC handles the cascading permissions check. This provides
fine-grained control over which dashboards are publicly visible.

- **Role synchronization:** Built-in role permissions (Admin, Alpha, Gamma, sql_lab, and Public
when `PUBLIC_ROLE_LIKE = "Public"`) are synchronized when you run `superset init`. Any manual
permission edits to these roles may be overwritten during upgrades. To customize the Public
role permissions, you can either:
- Edit the Public role directly and avoid setting `PUBLIC_ROLE_LIKE` (permissions won't be
overwritten by `superset init`)
- Copy the Public role via "Copy Role" in the Superset web UI, save it under a different name
(e.g., "Public_Custom"), customize the permissions, then update **both** configs:
`PUBLIC_ROLE_LIKE = "Public_Custom"` and `AUTH_ROLE_PUBLIC = "Public_Custom"`

### Managing Data Source Access for Gamma Roles

Expand All @@ -64,6 +114,46 @@ tables in the **Permissions** dropdown. To select the data sources you want to a
You can then confirm with users assigned to the **Gamma** role that they see the
objects (dashboards and slices) associated with the tables you just extended them.

### Dashboard Access Control

Access to dashboards is managed via owners (users that have edit permissions to the dashboard).
Non-owner user access can be managed in two ways. Note that dashboards must be published to be
visible to other users.

#### Dataset-Based Access (Default)

By default, users can view published dashboards if they have access to at least one dataset
used in that dashboard. Grant dataset access by adding the relevant data source permissions
to a role (Menu → Security → List Roles).

This is the simplest approach but provides all-or-nothing access based on dataset permissions—
if a user has access to a dataset, they can see all published dashboards using that dataset.

#### Dashboard-Level Access (DASHBOARD_RBAC)

For fine-grained control over which dashboards specific roles can access, enable the
`DASHBOARD_RBAC` feature flag:

```python
FEATURE_FLAGS = {
"DASHBOARD_RBAC": True,
}
```

With this enabled, you can assign specific roles to each dashboard in its properties. Users
will only see dashboards where their role is explicitly added.

**Important considerations:**
- Dashboard access **bypasses** dataset-level checks—granting a role access to a dashboard
implicitly grants read access to all charts and datasets in that dashboard
- Dashboards without any assigned roles fall back to dataset-based access
- The dashboard must still be published to be visible

This feature is particularly useful for:
- Making specific dashboards public while keeping others private
- Granting access to dashboards without exposing the underlying datasets for other uses
- Creating dashboard-specific access patterns that don't align with dataset ownership

### SQL Execution Security Considerations

Apache Superset includes features designed to provide safeguards when interacting with connected databases, such as the `DISALLOWED_SQL_FUNCTIONS` configuration setting. This aims to prevent the execution of potentially harmful database functions or system variables directly from Superset interfaces like SQL Lab.
Expand Down
12 changes: 5 additions & 7 deletions docs/docs/using-superset/creating-your-first-dashboard.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -183,14 +183,12 @@ slices and dashboards of your own.

### Manage access to Dashboards

Access to dashboards is managed via owners (users that have edit permissions to the dashboard).
Access to dashboards is managed via owners and permissions. Non-owner access can be controlled
through dataset permissions or dashboard-level roles (using the `DASHBOARD_RBAC` feature flag).

Non-owner users access can be managed in two different ways. The dashboard needs to be published to be visible to other users.

1. Dataset permissions - if you add to the relevant role permissions to datasets it automatically grants implicit access to all dashboards that uses those permitted datasets.
2. Dashboard roles - if you enable [**DASHBOARD_RBAC** feature flag](/docs/configuration/configuring-superset#feature-flags) then you will be able to manage which roles can access the dashboard
- Granting a role access to a dashboard will bypass dataset level checks. Having dashboard access implicitly grants read access to all the featured charts in the dashboard, and thereby also all the associated datasets.
- If no roles are specified for a dashboard, regular **Dataset permissions** will apply.
For detailed information on configuring dashboard access, see the
[Dashboard Access Control](/docs/security/security#dashboard-access-control) section in the
Security documentation.

<img src={useBaseUrl("/img/tutorial/tutorial_dashboard_access.png" )} />

Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,7 @@ select = [

ignore = [
"S101",
"PT004", # Fixtures that don't return values - underscore prefix conflicts with pytest usage
"PT006",
"T201",
"N999",
Expand Down
97 changes: 94 additions & 3 deletions superset/security/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,7 @@ class SupersetSecurityManager( # pylint: disable=too-many-public-methods
}

GAMMA_READ_ONLY_MODEL_VIEWS = {
"CssTemplate",
"Dataset",
"Datasource",
} | READ_ONLY_MODEL_VIEWS
Expand Down Expand Up @@ -302,7 +303,6 @@ class SupersetSecurityManager( # pylint: disable=too-many-public-methods
"Annotation",
"CSS Templates",
"ColumnarToDatabaseView",
"CssTemplate",
"ExcelToDatabaseView",
"Import dashboards",
"ImportExportRestApi",
Expand Down Expand Up @@ -389,6 +389,60 @@ class SupersetSecurityManager( # pylint: disable=too-many-public-methods
("can_read", "Database"),
}

# Permissions for the Public role - minimal read-only access for viewing
# dashboards without authentication. This is more restrictive than Gamma.
# Users can set PUBLIC_ROLE_LIKE = "Public" to use these sensible defaults.
PUBLIC_ROLE_PERMISSIONS = {
# Core dashboard viewing
("can_read", "Dashboard"),
("can_read", "Chart"),
("can_dashboard", "Superset"),
("can_slice", "Superset"),
("can_explore_json", "Superset"),
("can_dashboard_permalink", "Superset"),
("can_read", "DashboardPermalinkRestApi"),
# Dashboard filter interactions
("can_read", "DashboardFilterStateRestApi"),
("can_write", "DashboardFilterStateRestApi"),
# API access for chart rendering
("can_time_range", "Api"),
("can_query_form_data", "Api"),
("can_query", "Api"),
# CSS for dashboard styling
("can_read", "CssTemplate"),
# Embedded dashboard support
("can_read", "EmbeddedDashboard"),
# Datasource metadata for chart rendering
("can_get", "Datasource"),
("can_external_metadata", "Datasource"),
# Annotations on charts
("can_read", "Annotation"),
("can_read", "AnnotationLayerRestApi"),
# Chart permalinks (for shared chart links)
("can_read", "ExplorePermalinkRestApi"),
}

Comment on lines +395 to +424
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Public is given several permissions that Gamma does not have:

can_read → CssTemplate

can_read → SecurityRestApi

can_get → MenuApi

can_get → OpenApi

can_external_metadata_by_name → Datasource

Because of these, public_perm_set - gamma_perm_set is not empty, so test_public_role_more_restrictive_than_gamma will fail. Either remove these permissions from Public or update the test, but both cannot be true at once.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for catching that... I'll take 'em out.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, I added CssTemplate can_read to Gamma :D

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a Public role in production. I set it up around 2022 so many versions ago. Here I compare this PR's list to the list I'm using, both ways.

An original artifact of the list I used was this gist: https://gist.github.com/byk0t/bd6e9c3839967b4ac28a8da30f468b2a its comments have a couple of useful clues.

Present here but not in my Public role

  1. ("can_read", "EmbeddedDashboard") - makes sense to add

It surprises me that my Public role works without having these:
# Datasource metadata for chart rendering
2. ("can_get", "Datasource"),
3. ("can_external_metadata", "Datasource")

Present in my Public role but not this PR

I think we might want to add:

  • can annotation json Superset - I would think this is needed for annotations to render on a Public-facing chart?
  • can read Annotation - same
  • can filter Superset - I don't know how this is different from the filter permissions you have above
  • can read ExplorePermalinkRestApi - I think this was for permalinks to charts? I was trying to embed charts in webpages when I started using Superset for public facing.

I'm not sure about these - I have them but it's not obvious to me what they do and I don't remember why I added each:

  • can favstar Superset - at least in Superset 1.5.0 this was needed to avoid the error message: "There was an issue fetching the favorite status of this dashboard"
  • can get OpenApi
  • can list FilterSets
  • can queries Superset
  • can read AdvancedDataType
  • can read ExploreFormDataRestApi,
  • can share dashboard Superset
  • can slice json Superset
  • can sql json Superset
  • can validate sql json Superset
  • can write DashboardPermalinkRestApi - maybe I had this because of Without "can write on DashboardPermalinkRestApi", a click on a anchor (tabs or header) redirect to login page #30004 which has been fixed?
  • can write ExploreFormDataRestApi
  • can write ExplorePermalinkRestApi

Probably only a fit for my use case:

  • can csv Superset - maybe we note in the docs that if you want the public user to be able to download the data behind a chart, add this?

# View menus that Public role should NOT have access to
PUBLIC_EXCLUDED_VIEW_MENUS = {
"SQL Lab",
"SQL Editor",
"Saved Queries",
"Query Search",
"Queries",
"Security",
"List Users",
"List Roles",
"Row Level Security",
"Row Level Security Filters",
"Access Requests",
"Action Log",
"Manage",
"Import dashboards",
"Annotation Layers",
"CSS Templates",
"Alerts & Report",
}

data_access_permissions = (
"database_access",
"schema_access",
Expand Down Expand Up @@ -1205,9 +1259,18 @@ def sync_role_definitions(self) -> None:
self.set_role("sql_lab", self._is_sql_lab_pvm, pvms)

# Configure public role
if get_conf()["PUBLIC_ROLE_LIKE"]:
# If PUBLIC_ROLE_LIKE is "Public", use the built-in Public role with
# sensible defaults for anonymous dashboard viewing.
# If set to another role name (e.g., "Gamma"), copy permissions from that role.
# If not set (None), the Public role remains empty (default/legacy behavior).
public_role_like = get_conf()["PUBLIC_ROLE_LIKE"]
if public_role_like == "Public":
# Use the built-in Public role with minimal read-only permissions
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Role Name Inconsistency

The set_role call uses a hardcoded "Public" string, but to match the AUTH_ROLE_PUBLIC config (which defaults to "Public" but can be customized), it should use self.auth_role_public. This ensures consistency, as the copy_role in the elif branch correctly uses self.auth_role_public.

Citations

Code Review Run #8f0e3a


Should Bito avoid suggestions like this for future reviews? (Manage Rules)

  • Yes, avoid them

self.set_role("Public", self._is_public_pvm, pvms)
elif public_role_like:
# Copy permissions from another role (e.g., "Gamma") to Public
self.copy_role(
get_conf()["PUBLIC_ROLE_LIKE"],
public_role_like,
self.auth_role_public,
merge=True,
)
Expand Down Expand Up @@ -1427,6 +1490,34 @@ def _is_sql_lab_pvm(self, pvm: PermissionView) -> bool:
in self.SQLLAB_EXTRA_PERMISSION_VIEWS
)

def _is_public_pvm(self, pvm: PermissionView) -> bool:
"""
Return True if the FAB permission/view is appropriate for the Public role,
False otherwise.

The Public role is designed for anonymous/unauthenticated users who need
to view dashboards. It provides minimal read-only access - more restrictive
than Gamma - suitable for public-facing dashboard deployments.

:param pvm: The FAB permission/view
:returns: Whether the FAB object is appropriate for Public role
"""
# Explicitly allow permissions in the PUBLIC_ROLE_PERMISSIONS set
if (pvm.permission.name, pvm.view_menu.name) in self.PUBLIC_ROLE_PERMISSIONS:
return True

# Exclude any view menus in the excluded list
if pvm.view_menu.name in self.PUBLIC_EXCLUDED_VIEW_MENUS:
return False

# Exclude user-defined permissions (datasource_access, schema_access, etc.)
# These must be explicitly granted to the Public role
if self._is_user_defined_permission(pvm):
return False

# Exclude all other permissions not explicitly allowed
return False

def database_after_insert(
self,
mapper: Mapper,
Expand Down
2 changes: 1 addition & 1 deletion superset/views/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ def sanitize_datasource_data(
def bootstrap_user_data(user: User, include_perms: bool = False) -> dict[str, Any]:
if user.is_anonymous:
payload = {}
user.roles = (security_manager.find_role("Public"),)
user.roles = (security_manager.get_public_role(),)
elif security_manager.is_guest_user(user):
payload = {
"username": user.username,
Expand Down
1 change: 1 addition & 0 deletions tests/integration_tests/fixtures/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
load_energy_table_with_slice,
)
from .public_role import ( # noqa: F401
public_role_builtin,
public_role_like_gamma,
public_role_like_test_role,
)
Expand Down
18 changes: 18 additions & 0 deletions tests/integration_tests/fixtures/public_role.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,21 @@ def public_role_like_test_role(app_context: AppContext):

security_manager.get_public_role().permissions = []
db.session.commit()


@pytest.fixture
def public_role_builtin(app_context: AppContext):
"""
Fixture that uses the built-in Public role with minimal read-only permissions.
This sets PUBLIC_ROLE_LIKE to "Public" to use the new sensible defaults.
"""
original_value = app.config.get("PUBLIC_ROLE_LIKE")
app.config["PUBLIC_ROLE_LIKE"] = "Public"
security_manager.sync_role_definitions()

yield

# Restore original config and clean up
app.config["PUBLIC_ROLE_LIKE"] = original_value
security_manager.get_public_role().permissions = []
db.session.commit()
Loading
Loading