Spaces:
Paused
Paused
Download docs/admin_docs/security/security.mdx from Leon4gr45/powerbi: direct link, hf CLI and curl.
- Browser
- Download file 40.7 kB
-
https://huggingface.co/spaces/Leon4gr45/powerbi/resolve/main/docs/admin_docs/security/security.mdx
- Command line
-
hf download hf://spaces/Leon4gr45/powerbi/docs/admin_docs/security/security.mdx
-
curl -L -o security.mdx https://huggingface.co/spaces/Leon4gr45/powerbi/resolve/main/docs/admin_docs/security/security.mdx
40.7 kB
| --- | |
| title: Security Configurations | |
| sidebar_position: 1 | |
| --- | |
| Authentication and authorization in Superset is handled by Flask AppBuilder (FAB), an application development framework | |
| built on top of Flask. FAB provides authentication, user management, permissions and roles. | |
| Please read its [Security documentation](https://flask-appbuilder.readthedocs.io/en/latest/security.html). | |
| ### Provided Roles | |
| Superset ships with a set of roles that are handled by Superset itself. You can assume | |
| that these roles will stay up-to-date as Superset evolves (and as you update Superset versions). | |
| Even though **Admin** users have the ability, we don't recommend altering the | |
| permissions associated with each role (e.g. by removing or adding permissions to them). The permissions | |
| associated with each role will be re-synchronized to their original values when you run | |
| the **superset init** command (often done between Superset versions). | |
| A table with the permissions for these roles can be found at [/RESOURCES/STANDARD_ROLES.md](https://github.com/apache/superset/blob/master/RESOURCES/STANDARD_ROLES.md). | |
| ### Admin | |
| Admins have all possible rights, including granting or revoking rights from other | |
| users and altering other people’s slices and dashboards. | |
| > #### Threat Model and Privilege Boundaries: The Admin Role | |
| > | |
| > Apache Superset is built with a granular permission model where users assigned the Admin role are considered fully trusted. Admins possess complete control over the application's configuration, UI rendering, and access controls. | |
| > | |
| > Consequently, actions performed by an Admin that alter the application's behavior or presentation—such as injecting custom CSS, modifying Jinja templates, or altering security flags—are intended administrative capabilities by design. | |
| > | |
| > In accordance with MITRE CNA Rule 4.1, a vulnerability must represent a violation of an explicit security policy. Because the Admin role is defined as a trusted operational boundary, actions executed with Admin privileges do not cross a security perimeter. Therefore, exploit vectors that strictly require Admin access are not classified as security vulnerabilities and are ineligible for CVE assignment. | |
| ### Alpha | |
| Alpha users have access to all data sources, but they cannot grant or revoke access | |
| from other users. They are also limited to altering the objects that they own. Alpha users can add and alter data sources. | |
| ### Gamma | |
| Gamma users have limited access. They can only consume data coming from data sources | |
| they have been given access to through another complementary role. They only have access to | |
| view the slices and dashboards made from data sources that they have access to. Currently Gamma | |
| users are not able to alter or add data sources. We assume that they are mostly content consumers, though they can create slices and dashboards. | |
| Also note that when Gamma users look at the dashboards and slices list view, they will | |
| only see the objects that they have access to. | |
| ### sql_lab | |
| The **sql_lab** role grants access to SQL Lab. Note that while **Admin** users have access | |
| to all databases by default, both **Alpha** and **Gamma** users need to be given access on a per database basis. | |
| Beyond the base `sql_lab` role, two additional SQL Lab permissions must be explicitly granted for users who need these capabilities: | |
| | Permission | Feature | | |
| | ------------------------------------- | --------------------------------------- | | |
| | `can_estimate_query_cost` on `SQLLab` | Estimate query cost before running | | |
| | `can_format_sql` on `SQLLab` | Format SQL using the database's dialect | | |
| Grant these in **Security → List Roles** by adding the permissions to the relevant role. | |
| ### Public | |
| 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`: | |
| ```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 subject-based | |
| dashboard viewer assignments, you do not need to set `PUBLIC_ROLE_LIKE`. | |
| **Important notes:** | |
| - **Data access is still required by default:** 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: | |
| - **Dataset-based access:** 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. | |
| - **Subject-based dashboard access:** When `ENABLE_VIEWERS` is enabled, anonymous users can | |
| see published dashboards where the "Public" role subject has been explicitly added as a | |
| viewer. If `VIEWER_PROMISCUOUS_MODE` is enabled, viewer access can bypass dataset | |
| permission checks for dashboard rendering. | |
| - **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 | |
| Here’s how to provide users access to only specific datasets. First make sure the users with | |
| limited access have [only] the Gamma role assigned to them. Second, create a new role (Menu -> Security -> List Roles) and click the + sign. | |
| This new window allows you to give this new role a name, attribute it to users and select the | |
| tables in the **Permissions** dropdown. To select the data sources you want to associate with this role, simply click on the dropdown and use the typeahead to search for your table names. | |
| 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. | |
| ### Subjects | |
| A **subject** is a unified identity that can be granted access to Superset resources such as | |
| dashboards, charts, and datasets. Subjects come in three types: | |
| - **User** — an individual Superset user | |
| - **Group** — a FAB group | |
| - **Role** — a FAB role (e.g. Alpha, Gamma, or any custom role) | |
| Subjects are used throughout Superset to assign **editors** (who can modify a resource) and | |
| **viewers** (who can view it). For new resource-specific assignments, Superset defaults to | |
| **Users** and **Groups** in subject pickers. Groups are the recommended way to grant access to a | |
| set of people because they represent organizational membership, while roles remain focused on | |
| capability grants such as "can read dashboards" or "can write charts". | |
| Role subjects are still supported for backwards compatibility with features that previously used | |
| roles directly, including Row Level Security role assignments and `DASHBOARD_RBAC`. Existing role | |
| subject assignments continue to be enforced even when roles are not exposed in the default picker | |
| configuration. However, when an administrator edits a subject list, roles are not available as new | |
| dropdown values unless that picker has been configured to expose role subjects. Roles are not | |
| recommended for new subject assignments, because using roles for both permissions and resource | |
| membership couples two separate concerns. | |
| Administrators can control which subject types are available in pickers with | |
| `SUBJECTS_RELATED_TYPES`. By default, this setting exposes only users and groups: | |
| ```python | |
| from superset.subjects.types import SubjectType | |
| SUBJECTS_RELATED_TYPES = [ | |
| SubjectType.USER, | |
| SubjectType.GROUP, | |
| ] | |
| ``` | |
| Set `SUBJECTS_RELATED_TYPES = None` to expose all subject types, including roles. To expose roles | |
| only for a specific entity, set that entity's override: | |
| ```python | |
| from superset.subjects.types import SubjectType | |
| SUBJECTS_RELATED_TYPES = [ | |
| SubjectType.USER, | |
| SubjectType.GROUP, | |
| ] | |
| # Leave dashboard, chart, and alert/report overrides unset so they inherit | |
| # the users + groups default. Expose roles only in the RLS subject picker for | |
| # compatibility with existing role-based RLS workflows. | |
| SUBJECTS_RELATED_TYPES_RLS = [ | |
| SubjectType.USER, | |
| SubjectType.GROUP, | |
| SubjectType.ROLE, | |
| ] | |
| ``` | |
| Available per-entity overrides are: | |
| - `SUBJECTS_RELATED_TYPES_DASHBOARDS` for dashboard editor/viewer pickers | |
| - `SUBJECTS_RELATED_TYPES_CHARTS` for chart editor/viewer pickers | |
| - `SUBJECTS_RELATED_TYPES_RLS` for Row Level Security subject pickers | |
| - `SUBJECTS_RELATED_TYPES_ALERT_REPORTS` for alert/report editor pickers | |
| When an override is set, it replaces `SUBJECTS_RELATED_TYPES` for that picker. When it is `None`, | |
| the picker inherits the global default. | |
| #### Looking Up Subjects via API | |
| Superset exposes a read-only REST API for resolving subjects: | |
| ``` | |
| GET /api/v1/security/subject/ | |
| ``` | |
| The main use case is **id mapping** — given a user, role, or group id, callers (including | |
| extensions) can look up the corresponding subject entity, and vice versa. Access is gated by | |
| the `can_read` permission on the `Subject` resource, which is granted to **Admins only** by | |
| default, since subjects enumerate every user, role, and group on the instance. Callers without | |
| that permission receive a `403`. Only `GET` (list, get, info) is exposed — there is no create, | |
| update, or delete, because subjects are derived automatically from users, roles, and groups and | |
| kept in sync internally. | |
| **Resolve the subject for a given principal id** using [Rison](https://github.com/Nanonid/rison) | |
| query syntax: | |
| ``` | |
| GET /api/v1/security/subject/?q=(filters:!((col:user_id,opr:eq,value:5))) | |
| GET /api/v1/security/subject/?q=(filters:!((col:role_id,opr:eq,value:3))) | |
| GET /api/v1/security/subject/?q=(filters:!((col:group_id,opr:eq,value:2))) | |
| ``` | |
| **Filter by subject type or active status:** | |
| ``` | |
| GET /api/v1/security/subject/?q=(filters:!((col:type,opr:eq,value:1))) | |
| GET /api/v1/security/subject/?q=(filters:!((col:active,opr:eq,value:!t))) | |
| ``` | |
| The `type` column is an integer enum: `1` for User, `2` for Role, `3` for Group | |
| (`superset.subjects.types.SubjectType`). | |
| **Search by label:** | |
| ``` | |
| GET /api/v1/security/subject/?q=(filters:!((col:label,opr:subject_all_text,value:finance))) | |
| ``` | |
| Each subject in the response includes flat scalar ids (`user_id`, `role_id`, `group_id`) rather | |
| than a nested object, so callers can match directly on whichever id they already have — only the | |
| id field matching the subject's `type` is populated; the others are `null`. | |
| ### Dashboard Access Control | |
| Access to dashboards is managed via editors (subjects that have edit permissions to the dashboard). | |
| Non-editor 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 and Chart-Level Access | |
| For fine-grained control over which dashboards and charts specific users or groups can access, | |
| enable subject-based viewers: | |
| ```python | |
| FEATURE_FLAGS = { | |
| "ENABLE_VIEWERS": True, | |
| } | |
| ``` | |
| With this enabled, you can assign viewer subjects to each dashboard or chart in its properties. | |
| Users will see resources where they are assigned directly, or indirectly through a group subject. | |
| Role subjects can also be enabled for compatibility, but groups are recommended for new viewer | |
| assignments. | |
| `ENABLE_VIEWERS` can be adopted incrementally. Dashboards and charts with no assigned viewers keep | |
| the existing implicit dataset-access behavior: users who can access the underlying dataset can still | |
| see published dashboards that use that dataset and charts backed by that dataset. Assigning one or | |
| more viewers changes that specific resource to explicit viewer access for non-editors. Removing all | |
| viewers returns the resource to the dataset-based fallback. Explicit Viewers are the preferred model | |
| going forward; the implicit fallback may be deprecated and removed in a later major version. | |
| **Important considerations:** | |
| - Viewer access uses normal dataset checks unless `VIEWER_PROMISCUOUS_MODE` is enabled | |
| - With `VIEWER_PROMISCUOUS_MODE`, dashboard viewer access can bypass dataset-level checks for | |
| charts and datasets in that dashboard | |
| - Dashboards and charts without assigned viewers 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 permissions | |
| #### Assigning the creator's groups automatically | |
| ```python | |
| FEATURE_FLAGS = { | |
| "ENABLE_VIEWERS": True, | |
| "ASSIGN_CREATOR_GROUPS_AS_VIEWERS": True, | |
| } | |
| ``` | |
| This requires `ENABLE_VIEWERS` — it has no effect on its own, since it works by populating the | |
| `viewers` relationship that `ENABLE_VIEWERS` governs. | |
| With this enabled, a newly created dashboard or chart is shared read-only with every group its | |
| creator belongs to, unless the create payload names viewers explicitly. It applies to every path | |
| that creates an asset: the REST API, `/dashboard/new/`, save-as from Explore, dashboard copy, | |
| the import commands, and the MCP tools. Datasets are unaffected, as they have editors but no | |
| viewers. | |
| For dashboards, viewer access additionally requires the dashboard to be **published** — the same | |
| rule that governs every dashboard viewer. Until a new dashboard is published its creator's groups | |
| see nothing, and because it now has viewers the dataset-based fallback no longer applies to it | |
| either; group members gain access only once it is published. Charts have no such publish gate, so a | |
| new chart's viewer grant takes effect immediately. Note, though, that the viewer relationship | |
| governs access to the chart itself; rendering its **data** still runs the normal datasource | |
| permission check unless `VIEWER_PROMISCUOUS_MODE` is enabled — a group member without access to the | |
| underlying dataset can open the chart but won't load its data. | |
| Note that this **narrows** access rather than widening it. Because an asset with no viewers falls | |
| back to dataset-based access, giving new assets a viewer makes that fallback unreachable for them: | |
| a colleague who could previously open a new dashboard by virtue of dataset access can no longer do | |
| so unless they share one of the creator's groups. Existing assets are untouched — only assets | |
| created after the feature flag is enabled are affected. | |
| The same narrowing affects background execution as a fixed identity. Alerts, reports, and | |
| thumbnails that run as a `FixedExecutor` service account (via `ALERT_REPORTS_EXECUTE_AS` or | |
| `THUMBNAIL_EXECUTE_AS`) reach a chart or dashboard through the dataset fallback when that account | |
| is neither an editor nor a viewer. Once a new asset is scoped to the creator's groups that | |
| fallback is gone, so rendering fails `raise_for_access` unless the executor account is an admin, | |
| an editor, or shares one of those groups. If a fixed executor must render assets it does not own, | |
| grant it the needed access (make it an admin or add it to the relevant groups) — or leave the | |
| feature flag off. | |
| The setting assigns groups as *viewers*, not editors, so group members get read-only access and | |
| cannot modify or delete the asset. | |
| ### 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. | |
| However, it is crucial to understand the following: | |
| **Superset is Not a Database Firewall**: Superset's built-in checks, like `DISALLOWED_SQL_FUNCTIONS`, provide a layer of protection but cannot guarantee complete security against all database-level threats or advanced bypass techniques (like specific comment injection methods). They should be viewed as a supplement to, not a replacement for, robust database security. | |
| **Configuration is Key**: The effectiveness of Superset's safeguards heavily depends on proper configuration by the Superset administrator. This includes maintaining the `DISALLOWED_SQL_FUNCTIONS` list, carefully managing feature flags (like `ENABLE_TEMPLATE_PROCESSING`), and configuring other security settings appropriately. | |
| **Database Security is Paramount**: The ultimate responsibility for securing database access, controlling permissions, and preventing unauthorized function execution lies with the database administrators (DBAs) and security teams managing the underlying database instance. | |
| **Recommended Database Practices**: We strongly recommend implementing security best practices at the database level, including: | |
| - **Least Privilege**: Connecting Superset using dedicated database user accounts with the minimum permissions required for Superset's operation (typically read-only access to necessary schemas/tables). | |
| - **Database Roles & Permissions**: Utilizing database-native roles and permissions to restrict access to sensitive functions, system variables (like `@@hostname`), schemas, or tables. | |
| - **Network Security**: Employing network-level controls like database firewalls or proxies to restrict connections. | |
| - **Auditing**: Enabling database-level auditing to monitor executed queries and access patterns. | |
| By combining Superset's configurable safeguards with strong database-level security practices, you can achieve a more robust and layered security posture. | |
| **Dataset Sample Access**: The `get_samples()` endpoint now enforces datasource-level access control. Users can only fetch sample rows from datasets they have been explicitly granted access to — the same permission check applied when running chart queries. This closes a prior gap where unauthenticated or under-privileged access could retrieve sample data. | |
| ### REST API for user & role management | |
| Flask-AppBuilder supports a REST API for user CRUD, | |
| but this feature is in beta and is not enabled by default in Superset. | |
| To enable this feature, set the following in your Superset configuration: | |
| ```python | |
| FAB_ADD_SECURITY_API = True | |
| ``` | |
| Once configured, the documentation for additional "Security" endpoints will be visible in Swagger for you to explore. | |
| ### API Key Authentication | |
| Superset supports long-lived API keys for service accounts, CI/CD pipelines, and programmatic integrations (including MCP clients). | |
| #### Enabling API Key Authentication | |
| API key authentication is **disabled by default**. To turn it on, set the Flask-AppBuilder config value in `superset_config.py` and also enable the matching feature flag so the management UI is exposed: | |
| ```python | |
| FAB_API_KEY_ENABLED = True | |
| FEATURE_FLAGS = { | |
| "FAB_API_KEY_ENABLED": True, | |
| } | |
| ``` | |
| The config value registers the `ApiKeyApi` blueprint on the backend; the feature flag controls whether the UI for managing keys appears for the user. See the [Feature Flags](/admin-docs/configuration/feature-flags) documentation for more on feature flag configuration. | |
| #### Creating an API Key | |
| Once enabled, each user manages their own keys from their profile page: | |
| 1. Open the user menu (top-right) and click **Info** to navigate to the User Info page | |
| 2. Expand the **API Keys** section | |
| 3. Click **+ API Key** | |
| 4. Enter a name and optionally select resource scopes | |
| 5. Copy the generated token — it is shown only once | |
| Only users with the `can_read` and `can_write` permissions on `ApiKey` (granted by default to Admins) can manage API keys. | |
| #### Using an API Key | |
| Pass the key as a Bearer token in the `Authorization` header: | |
| ``` | |
| Authorization: Bearer <your-api-key> | |
| ``` | |
| This works for all REST API endpoints and the MCP server. The request is executed with the permissions of the user who created the key. | |
| #### API Key Scopes | |
| The creation dialog can restrict an API key to MCP resource actions such as | |
| `superset:dashboard:read` or `superset:chart:write`. A scope is an additional | |
| restriction: it never grants a permission that the creating user does not | |
| already have through Superset RBAC. Write scopes also cover update and delete | |
| operations for that resource; `superset:sqllab:write` covers SQL execution. | |
| Keys created without scopes retain legacy RBAC-only behavior. The scoped-key | |
| restrictions described here are enforced by the MCP server; regular REST API | |
| routes continue to apply their existing Superset RBAC checks. | |
| #### Use Cases | |
| - **CI/CD pipelines** — automated chart/dashboard exports and imports | |
| - **MCP integrations** — connect AI assistants without interactive login | |
| - **External services** — dashboards embedded in other applications | |
| - **Service accounts** — long-lived credentials that don't expire with session cookies | |
| :::caution | |
| Store API keys securely. Anyone with a valid key can make requests on behalf of the creating user. Revoke keys promptly if they are compromised by deleting them from the **API Keys** section of your User Info page. | |
| ::: | |
| ### Customizing Permissions | |
| The permissions exposed by FAB are very granular and allow for a great level of | |
| customization. FAB creates many permissions automatically for each model that is | |
| created (can_add, can_delete, can_show, can_edit, …) as well as for each view. | |
| On top of that, Superset can expose more granular permissions like **all_datasource_access**. | |
| **We do not recommend altering the 3 base roles as there are a set of assumptions that | |
| Superset is built upon**. It is possible though for you to create your own roles, and union them to existing ones. | |
| ### Permissions | |
| Roles are composed of a set of permissions, and Superset has many categories of | |
| permissions. Here are the different categories of permissions: | |
| - Model & Action: models are entities like Dashboard, Slice, or User. Each model has | |
| a fixed set of permissions, like **can_edit**, **can_show**, **can_delete**, **can_list**, **can_add**, | |
| and so on. For example, you can allow a user to delete dashboards by adding **can_delete** on | |
| Dashboard entity to a role and granting this user that role. | |
| - Views: views are individual web pages, like the Explore view or the SQL Lab view. | |
| When granted to a user, they will see that view in its menu items, and be able to load that page. | |
| - Data source: For each data source, a permission is created. If the user does not have the | |
| `all_datasource_access permission` granted, the user will only be able to see Slices or explore the data sources that are granted to them | |
| - Database: Granting access to a database allows for the user to access all | |
| data sources within that database, and will enable the user to query that | |
| database in SQL Lab, provided that the SQL Lab specific permission have been granted to the user | |
| ### Restricting Access to a Subset of Data Sources | |
| We recommend giving a user the **Gamma** role plus any other roles that would add | |
| access to specific data sources. We recommend that you create individual roles for | |
| each access profile. For example, the users on the Finance team might have access to a set of | |
| databases and data sources; these permissions can be consolidated in a single role. | |
| Users with this profile then need to be assigned the **Gamma** role as a foundation to | |
| the models and views they can access, and that Finance role that is a collection of permissions to data objects. | |
| A user can have multiple roles associated with them. For example, an executive on the Finance | |
| team could be granted **Gamma**, **Finance**, and the **Executive** roles. The **Executive** | |
| role could provide access to a set of data sources and dashboards made available only to executives. | |
| In the **Dashboards** view, a user can only see the ones they have access to | |
| based on the roles and permissions that were attributed. | |
| ### Row Level Security | |
| Using Row Level Security filters (under the **Security** menu) you can create filters | |
| that are assigned to a particular dataset, as well as a set of subjects. | |
| If you want members of the Finance team to only have access to | |
| rows where `department = "finance"`, you could: | |
| - Create a Row Level Security filter with that clause (`department = "finance"`) | |
| - Then assign the clause to the **Finance** role subject and the dataset it applies to | |
| The **clause** field, which can contain arbitrary text, is then added to the generated | |
| SQL statement's WHERE clause. So you could even do something like create a filter | |
| for the last 30 days and apply it to a specific subject, with a clause | |
| like `date_field > DATE_SUB(NOW(), INTERVAL 30 DAY)`. It can also support | |
| multiple conditions: `client_id = 6` AND `advertiser="foo"`, etc. | |
| RLS clauses also support **Jinja templating** when `ENABLE_TEMPLATE_PROCESSING` is enabled, so you can write dynamic filters such as | |
| `user_id = '{{ current_username() }}'` to restrict rows based on the logged-in user. | |
| #### Filter Types | |
| There are two types of RLS filters: | |
| - **Regular** — The filter clause is applied when the querying user matches one of the | |
| subjects assigned to the filter. Use this to restrict what specific users, roles, or groups | |
| can see. | |
| - **Base** — The filter clause is applied to **all** users _except_ those matching the assigned | |
| subjects. Use this to define a default restriction that privileged subjects (e.g. the Admin | |
| role subject) are exempt from. For example, a Base filter with clause `1 = 0` and the Admin | |
| role subject would hide all rows from everyone except Admin - useful as a deny-by-default | |
| baseline. | |
| #### Group Keys and Filter Combination | |
| All applicable RLS filters are combined before being added to the query. The combination | |
| rules are: | |
| - Filters that share the **same group key** are combined with **OR** (any match within | |
| the group is sufficient). | |
| - Different filter groups (different group keys, or no group key) are combined with | |
| **AND** (all groups must match). | |
| - Filters with **no group key** are each treated as their own group and are always AND'd. | |
| For example, if a dataset has three filters: | |
| | Filter | Clause | Group Key | | |
| | ------ | -------------------------- | ------------ | | |
| | F1 | `department = 'Finance'` | `department` | | |
| | F2 | `department = 'Marketing'` | `department` | | |
| | F3 | `region = 'Europe'` | `region` | | |
| The resulting WHERE clause would be: | |
| ```sql | |
| (department = 'Finance' OR department = 'Marketing') AND (region = 'Europe') | |
| ``` | |
| :::caution Conflicting filters | |
| It is possible to create filters that conflict and produce an empty result set. For | |
| example, the filters `client_id = 4` and `client_id = 5` **without a shared group key** | |
| will be AND'd together, producing `client_id = 4 AND client_id = 5`, which can never | |
| be true. | |
| If you intend for these to be alternatives, assign them the **same group key** so they | |
| are OR'd instead. | |
| ::: | |
| #### RLS and Virtual (SQL-Based) Datasets | |
| RLS filters are assigned to **datasets**, not to underlying database tables directly. This | |
| has important implications when working with virtual (SQL-based) datasets: | |
| - **Physical datasets** (backed directly by a table or view) — RLS filters assigned to | |
| the dataset are added as WHERE clauses to the query. | |
| - **Virtual datasets** (defined by a custom SQL query) — RLS filters assigned directly to | |
| the virtual dataset are applied to the _outer_ query that wraps the dataset's SQL. | |
| Additionally, RLS filters on the **underlying physical datasets** referenced by the | |
| virtual dataset's SQL are injected into the inner subquery for each referenced table. | |
| For example, if you have: | |
| 1. A physical dataset `orders` with RLS filter `region = 'US'` | |
| 2. A virtual dataset defined as `SELECT * FROM orders WHERE status = 'active'` | |
| A user affected by the RLS filter will effectively see: | |
| ```sql | |
| SELECT * FROM ( | |
| SELECT * FROM orders WHERE (region = 'US') AND status = 'active' | |
| ) ... | |
| ``` | |
| **Key considerations for virtual datasets:** | |
| - You generally do **not** need to duplicate RLS filters on both the physical and virtual | |
| dataset — filters on the physical dataset are applied automatically at query time. | |
| - If you assign an RLS filter directly to a virtual dataset, the clause must reference | |
| columns available in the virtual dataset's _output_, not necessarily the underlying | |
| table's columns. | |
| - In **SQL Lab**, RLS is enforced only when the `RLS_IN_SQLLAB` feature flag is enabled: | |
| queries run against tables that have associated datasets with RLS filters will then have | |
| the appropriate predicates injected automatically. | |
| #### Checking RLS Filters via the API | |
| You can use the RLS REST API to audit which filters are configured and which datasets | |
| they affect. This requires the `can_read` permission on the `Row Level Security` resource. | |
| **List all RLS rules:** | |
| ``` | |
| GET /api/v1/rowlevelsecurity/ | |
| ``` | |
| **Filter RLS rules for a specific dataset** (using [Rison](https://github.com/Nanonid/rison) query syntax): | |
| ``` | |
| GET /api/v1/rowlevelsecurity/?q=(filters:!((col:tables,opr:rel_m_m,value:<dataset_id>))) | |
| ``` | |
| **Filter RLS rules by subject:** | |
| ``` | |
| GET /api/v1/rowlevelsecurity/?q=(filters:!((col:subjects,opr:rel_m_m,value:<subject_id>))) | |
| ``` | |
| **View details of a specific rule** (including clause, assigned datasets, and subjects): | |
| ``` | |
| GET /api/v1/rowlevelsecurity/<id> | |
| ``` | |
| The response includes the filter's `name`, `filter_type` (Regular or Base), `clause`, | |
| `group_key`, assigned `tables` (with id, schema, and table_name), and assigned `subjects`. | |
| :::tip Auditing RLS for virtual datasets | |
| To find all RLS rules that could affect a particular virtual dataset, query the list | |
| endpoint filtered by that dataset's ID for any directly-assigned rules. Then also check | |
| the physical datasets referenced in the virtual dataset's SQL, since their RLS filters | |
| are applied at query time too. | |
| ::: | |
| ### User Sessions | |
| Superset uses [Flask](https://pypi.org/project/Flask/) | |
| and [Flask-Login](https://pypi.org/project/Flask-Login/) for user session management. | |
| Session cookies are used to maintain session info and user state between requests, | |
| although they do not contain personal user information they serve the purpose of identifying | |
| a user session on the server side. | |
| The session cookie is encrypted with the application `SECRET_KEY` and cannot be read by the client. | |
| So it's very important to keep the `SECRET_KEY` secret and set to a secure unique complex random value. | |
| Flask and Flask-Login offer a number of configuration options to control session behavior. | |
| - Relevant Flask settings: | |
| `SESSION_COOKIE_HTTPONLY`: (default: `False`): Controls if cookies should be set with the `HttpOnly` flag. | |
| `SESSION_COOKIE_SECURE`: (default: `False`) Browsers will only send cookies with requests over | |
| HTTPS if the cookie is marked “secure”. The application must be served over HTTPS for this to make sense. | |
| `SESSION_COOKIE_SAMESITE`: (default: "Lax") Prevents the browser from sending this cookie along with cross-site requests. | |
| `PERMANENT_SESSION_LIFETIME`: (default: "31 days") The lifetime of a permanent session as a `datetime.timedelta` object. | |
| #### Switching to server side sessions | |
| Server side sessions offer benefits over client side sessions on security and performance. | |
| By enabling server side sessions, the session data is stored server side and only a session ID | |
| is sent to the client. When a user logs in, a session is created server side and the session ID | |
| is sent to the client in a cookie. The client will send the session ID with each request and the | |
| server will use it to retrieve the session data. | |
| On logout, the session is destroyed server side and the session cookie is deleted on the client side. | |
| This reduces the risk for replay attacks and session hijacking. | |
| Superset uses [Flask-Session](https://flask-session.readthedocs.io/en/latest/) to manage server side sessions. | |
| To enable this extension you have to set: | |
| ```python | |
| SESSION_SERVER_SIDE = True | |
| ``` | |
| Flask-Session offers multiple backend session interfaces for Flask, here's an example for Redis: | |
| ```python | |
| from redis import Redis | |
| SESSION_TYPE = "redis" | |
| SESSION_REDIS = Redis(host="redis", port=6379, db=0) | |
| # sign the session cookie sid | |
| SESSION_USE_SIGNER = True | |
| ``` | |
| ### Content Security Policy (CSP) | |
| Superset uses the [Talisman](https://pypi.org/project/flask-talisman/) extension to enable implementation of a | |
| [Content Security Policy (CSP)](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP), an added | |
| layer of security that helps to detect and mitigate certain types of attacks, including | |
| Cross-Site Scripting (XSS) and data injection attacks. | |
| A CSP makes it possible for server administrators to reduce or eliminate the vectors by which XSS can | |
| occur by specifying the domains that the browser should consider to be valid sources of executable scripts. | |
| A CSP-compatible browser will then only execute scripts loaded in source files received from those allowed domains, | |
| ignoring all other scripts (including inline scripts and event-handling HTML attributes). | |
| A policy is described using a series of policy directives, each of which describes the policy for | |
| a certain resource type or policy area. You can check possible directives | |
| [here](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy). | |
| It's extremely important to correctly configure a Content Security Policy when deploying Superset to | |
| prevent many types of attacks. Superset provides two variables in `config.py` for deploying a CSP: | |
| - `TALISMAN_ENABLED` defaults to `True`; set this to `False` in order to disable CSP | |
| - `TALISMAN_CONFIG` holds the actual the policy definition (_see example below_) as well as any | |
| other arguments to be passed to Talisman. | |
| When running in production mode, Superset will check at startup for the presence | |
| of a CSP. If one is not found, it will issue a warning with the security risks. For environments | |
| where CSP policies are defined outside of Superset using other software, administrators can disable | |
| this warning using the `CONTENT_SECURITY_POLICY_WARNING` key in `config.py`. | |
| #### CSP Requirements | |
| - Superset needs the `style-src unsafe-inline` CSP directive in order to operate. | |
| ``` | |
| style-src 'self' 'unsafe-inline' | |
| ``` | |
| - Only scripts marked with a [nonce](https://content-security-policy.com/nonce/) can be loaded and executed. | |
| Nonce is a random string automatically generated by Talisman on each page load. | |
| You can get current nonce value by calling jinja macro `csp_nonce()`. | |
| ```html | |
| <script nonce="{{ csp_nonce() }}"> | |
| /* my script */ | |
| </script> | |
| ``` | |
| - Some dashboards load images using data URIs and require `data:` in their `img-src` | |
| ``` | |
| img-src 'self' data: | |
| ``` | |
| - MapBox charts use workers and need to connect to MapBox servers in addition to the Superset origin | |
| ``` | |
| worker-src 'self' blob: | |
| connect-src 'self' https://api.mapbox.com https://events.mapbox.com | |
| ``` | |
| - Cartodiagram charts request map data (image and json) from external resources that can be edited by users, | |
| and therefore either require a list of allowed domains to request from or a wildcard (`'*'`) for `img-src` and `connect-src`. | |
| - Other CSP directives default to `'self'` to limit content to the same origin as the Superset server. | |
| In order to adjust provided CSP configuration to your needs, follow the instructions and examples provided in | |
| [Content Security Policy Reference](https://content-security-policy.com/) | |
| #### Other Talisman security considerations | |
| Setting `TALISMAN_ENABLED = True` will invoke Talisman's protection with its default arguments, | |
| of which `content_security_policy` is only one. Those can be found in the | |
| [Talisman documentation](https://pypi.org/project/flask-talisman/) under _Options_. | |
| These generally improve security, but administrators should be aware of their existence. | |
| In particular, the option of `force_https = True` (`False` by default) may break Superset's Alerts & Reports | |
| if workers are configured to access charts via a `WEBDRIVER_BASEURL` beginning | |
| with `http://`. As long as a Superset deployment enforces https upstream, e.g., | |
| through a load balancer or application gateway, it should be acceptable to keep this | |
| option disabled. Otherwise, you may want to enable `force_https` like this: | |
| ```python | |
| TALISMAN_CONFIG = { | |
| "force_https": True, | |
| "content_security_policy": { ... | |
| ``` | |
| #### Configuring Talisman in Superset | |
| Talisman settings in Superset can be modified using superset_config.py. If you need to adjust security policies, you can override the default configuration. | |
| Example: Overriding Talisman Configuration in superset_config.py for loading images form s3 or other external sources. | |
| ```python | |
| TALISMAN_CONFIG = { | |
| "content_security_policy": { | |
| "base-uri": ["'self'"], | |
| "default-src": ["'self'"], | |
| "img-src": [ | |
| "'self'", | |
| "blob:", | |
| "data:", | |
| "https://apachesuperset.gateway.scarf.sh", | |
| "https://static.scarf.sh/", | |
| # "https://cdn.brandfolder.io", # Uncomment when SLACK_ENABLE_AVATARS is True # noqa: E501 | |
| "ows.terrestris.de", | |
| "aws.s3.com", # Add Your Bucket or external data source | |
| ], | |
| "worker-src": ["'self'", "blob:"], | |
| "connect-src": [ | |
| "'self'", | |
| "https://api.mapbox.com", | |
| "https://events.mapbox.com", | |
| ], | |
| "object-src": "'none'", | |
| "style-src": [ | |
| "'self'", | |
| "'unsafe-inline'", | |
| ], | |
| "script-src": ["'self'", "'strict-dynamic'"], | |
| }, | |
| "content_security_policy_nonce_in": ["script-src"], | |
| "force_https": False, | |
| "session_cookie_secure": False, | |
| } | |
| ``` | |
| For more information on setting up Talisman, please refer to | |
| https://superset.apache.org/admin-docs/configuration/networking-settings/#changing-flask-talisman-csp. | |
| ### Reporting Security Vulnerabilities | |
| Apache Software Foundation takes a rigorous standpoint in annihilating the security issues in its | |
| software projects. Apache Superset is highly sensitive and forthcoming to issues pertaining to its | |
| features and functionality. | |
| If you have apprehensions regarding Superset security or you discover vulnerability or potential | |
| threat, don’t hesitate to get in touch with the Apache Security Team by dropping a mail at | |
| security@apache.org. In the mail, specify the project name Superset with the description of the | |
| issue or potential threat. You are also urged to recommend the way to reproduce and replicate the | |
| issue. The security team and the Superset community will get back to you after assessing and | |
| analysing the findings. | |
| PLEASE PAY ATTENTION to report the security issue on the security email before disclosing it on | |
| public domain. The ASF Security Team maintains a page with the description of how vulnerabilities | |
| and potential threats are handled, check [their web page](https://apache.org/security/committers.html) | |
| for more details. | |