Compare commits
6 Commits
master
...
fix/from-p
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
24a791f304 | ||
|
|
4ebe508d9f | ||
|
|
aa31bfde37 | ||
|
|
f34049e925 | ||
|
|
d0c8a71ccb | ||
|
|
34f5025398 |
@@ -462,6 +462,78 @@ async def metrics_guide(ctx: Context) -> str:
|
||||
"""
|
||||
```
|
||||
|
||||
## Built-in Dashboard Governance Tools
|
||||
|
||||
Superset ships three built-in MCP tools for dashboard governance:
|
||||
`manage_dashboard_owners`, `manage_dashboard_roles`, and
|
||||
`manage_dashboard_certification`. They exist alongside the generic
|
||||
`update_dashboard` tool because a few governance fields are unsafe to expose
|
||||
as full-replacement lists to an LLM caller — each tool instead takes
|
||||
explicit, narrow operations with its own safety semantics.
|
||||
|
||||
### `manage_dashboard_owners`
|
||||
|
||||
Adds or removes dashboard owners (the USER-type entries in the dashboard's
|
||||
Subject-based `editors` list) via `add_owner_ids`/`remove_owner_ids`, never
|
||||
a full-replacement list. This guards against the "empty owners" footgun:
|
||||
`update_dashboard` intentionally dropped its `owners` field because a
|
||||
full-replacement list has no way to prevent an admin from emptying it
|
||||
outright.
|
||||
|
||||
- **Owner-removal guard**: a request that would leave the dashboard with
|
||||
zero owners is rejected. To transfer ownership, add the new owner in the
|
||||
same call as removing the last existing one.
|
||||
- **Self-removal protection**: a non-admin caller who removes themselves is
|
||||
automatically re-added, mirroring the lockout protection
|
||||
`update_dashboard` already relies on (unless an `EXTRA_EDITORS_RESOLVER`
|
||||
is configured). The response's `warnings` reports when this happens.
|
||||
- ROLE- or GROUP-type editors already on the dashboard are left untouched.
|
||||
- A no-op request (e.g. "adding" an ID that's already an owner) returns an
|
||||
empty `owners` list rather than the full current set. Requesting removal
|
||||
of an ID that isn't an owner is rejected with an error that lists the
|
||||
current owner IDs, so an editor can still read that list.
|
||||
|
||||
### `manage_dashboard_roles`
|
||||
|
||||
Adds or removes dashboard access roles (the ROLE-type entries in the
|
||||
dashboard's Subject-based `viewers` list) via
|
||||
`add_role_ids`/`remove_role_ids`, never a full-replacement list. This
|
||||
guards against silently widening or narrowing who can see a dashboard,
|
||||
which is why `update_dashboard` dropped its `roles` field.
|
||||
|
||||
- **`ENABLE_VIEWERS` reporting**: the response reports the flag's current
|
||||
state via `viewers_enabled` and `warnings` notes when it's disabled — but
|
||||
that warning is informational only. `raise_for_access` enforces a
|
||||
nonempty `viewers` list on a published dashboard regardless of the flag,
|
||||
so a role added here restricts access immediately even with
|
||||
`ENABLE_VIEWERS` off. Removing every ROLE-type entry only restores normal
|
||||
permissions if no USER- or GROUP-type viewers remain — since
|
||||
`raise_for_access` checks the whole `viewers` list, not just roles, any
|
||||
such subjects left on the dashboard keep it restricted.
|
||||
- USER- or GROUP-type viewers already on the dashboard are left untouched.
|
||||
- Like `manage_dashboard_owners`, a no-op request returns an empty `roles`
|
||||
list instead of the full current set. Requesting removal of an unassigned
|
||||
role ID is rejected with an error that lists the current role IDs, so an
|
||||
editor can still read that list.
|
||||
|
||||
### `manage_dashboard_certification`
|
||||
|
||||
Sets or clears the `certified_by`/`certification_details` badge fields,
|
||||
split out from `update_dashboard` because certification is a distinct
|
||||
governance concern from layout/theme/metadata edits.
|
||||
|
||||
- **Clear-vs-unchanged convention**: `certified_by` and
|
||||
`certification_details` are independent optional fields — omit a field
|
||||
(`None`) to leave it unchanged, pass an empty string to clear it, or pass
|
||||
a value to set it. This mirrors the `slug`/`css` clear-with-empty-string
|
||||
convention already used by `update_dashboard`.
|
||||
- Both fields are sanitized before being stored, since they render as a UI
|
||||
badge/tooltip next to the dashboard title. An input that sanitizes down
|
||||
to nothing (HTML-only or whitespace-only) is rejected rather than
|
||||
silently clearing the field — callers must pass an explicit empty string
|
||||
to clear.
|
||||
- The response's `changed_fields` lists which fields were actually applied.
|
||||
|
||||
## Next Steps
|
||||
|
||||
- **[Development](./development.md)** - Project structure, APIs, and dev workflow
|
||||
|
||||
@@ -139,7 +139,7 @@ Dashboard Management:
|
||||
- remove_chart_from_dashboard: Remove a chart from an existing dashboard (requires write access)
|
||||
- restore_dashboard: Restore a soft-deleted dashboard from trash by ID/UUID (requires editor rights — owner or Admin; only applies to dashboards trashed under the SOFT_DELETE feature flag)
|
||||
- manage_dashboard_owners: Add/remove dashboard owners by explicit operation (requires write access; rejects changes that would leave zero owners)
|
||||
- manage_dashboard_roles: Add/remove dashboard access roles by explicit operation (requires write access; only affects access when ENABLE_VIEWERS is enabled)
|
||||
- manage_dashboard_roles: Add/remove dashboard access roles by explicit operation (requires write access; applies regardless of the ENABLE_VIEWERS flag)
|
||||
- manage_dashboard_certification: Set or clear a dashboard's certified_by/certification_details badge (requires write access)
|
||||
|
||||
Annotation Layers:
|
||||
|
||||
@@ -1224,9 +1224,9 @@ class ManageDashboardRolesResponse(DashboardMutationErrorFields):
|
||||
default=False,
|
||||
description=(
|
||||
"Whether the ENABLE_VIEWERS feature flag is enabled on this "
|
||||
"instance. When False, dashboard viewers are stored but have no "
|
||||
"effect on access control — access still follows normal "
|
||||
"Superset permissions/editorship."
|
||||
"instance. Access checks restrict access to any nonempty viewers "
|
||||
"list regardless of this flag, so role changes take effect "
|
||||
"either way."
|
||||
),
|
||||
)
|
||||
warnings: list[str] = Field(
|
||||
|
||||
@@ -279,8 +279,8 @@ def manage_dashboard_owners(
|
||||
up current owners — those remain off-limits per the server instructions.
|
||||
A request that has no effective change (e.g. "adding" an ID that is
|
||||
already an owner) returns an empty ``owners`` list rather than the full
|
||||
current set, so this tool cannot be used as a disguised directory
|
||||
lookup.
|
||||
current set. Requesting removal of an unassigned owner ID is an error
|
||||
that includes the current owner IDs.
|
||||
|
||||
Example::
|
||||
|
||||
|
||||
@@ -171,12 +171,13 @@ def manage_dashboard_roles(
|
||||
Add or remove dashboard access roles with explicit operations.
|
||||
|
||||
Dashboard access roles restrict who can view a dashboard to members of
|
||||
the listed roles, on top of normal Superset permissions. An empty roles
|
||||
list means "no role restriction" — the dashboard is visible per standard
|
||||
permissions instead. This only takes effect when the ``ENABLE_VIEWERS``
|
||||
feature flag is enabled; the response's ``viewers_enabled`` field
|
||||
reports whether it is, and ``warnings`` notes when a change was applied
|
||||
but has no live effect.
|
||||
the listed roles, on top of normal Superset permissions. Removing every
|
||||
role only restores standard permissions if no USER- or GROUP-type
|
||||
viewers remain on the dashboard — ``raise_for_access`` restricts access
|
||||
whenever the ``viewers`` list is nonempty, regardless of subject type.
|
||||
This applies regardless of the ``ENABLE_VIEWERS`` feature flag; the
|
||||
response's ``viewers_enabled`` field reports the flag's state, and
|
||||
``warnings`` notes when it is disabled.
|
||||
|
||||
Roles are the ROLE-type entries in the dashboard's Subject-based
|
||||
``viewers`` list. Any USER- or GROUP-type viewers already on the
|
||||
@@ -193,8 +194,8 @@ def manage_dashboard_roles(
|
||||
look up current roles — those remain off-limits per the server
|
||||
instructions. A request that has no effective change (e.g. "adding" a
|
||||
role that is already assigned) returns an empty ``roles`` list rather
|
||||
than the full current set, so this tool cannot be used as a disguised
|
||||
directory lookup.
|
||||
than the full current set. Requesting removal of an unassigned role ID
|
||||
is an error that includes the current role IDs.
|
||||
|
||||
Example::
|
||||
|
||||
@@ -222,8 +223,8 @@ def manage_dashboard_roles(
|
||||
if not viewers_enabled:
|
||||
warnings.append(
|
||||
"The ENABLE_VIEWERS feature flag is disabled on this instance; "
|
||||
"dashboard viewers will be stored but have no effect on access "
|
||||
"control until it is enabled."
|
||||
"dashboard access checks still restrict access to any nonempty "
|
||||
"viewers list, so role changes take effect regardless."
|
||||
)
|
||||
|
||||
current_role_ids, new_role_ids, compute_error = _compute_new_role_ids(
|
||||
|
||||
Reference in New Issue
Block a user