fix(mcp): user/role tools demanded a permission FAB never registers#41858
Conversation
list_users, get_user_info, list_roles, and get_role_info defaulted to the "read" method permission, requiring can_read on User/Role. FAB's security API views only register can_get/can_info/can_post/can_put/ can_delete, so the required permission-view pair can never exist and the tools failed closed for every principal, including Admin. Declare method_permission_name="get" (the read-class permission FAB actually registers for those views) and map "get" to the superset:read scope in _METHOD_TO_REQUIRED_SCOPE, whose fail-closed handling of unmapped methods would otherwise still deny scoped-JWT deployments. find_users is unaffected (no class_permission_name; allow-by-default path). 5 regression tests. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014z1fQxTadTEvpCJLx9WnB8
Code Review Agent Run #bcd21aActionable Suggestions - 0Review Details
Bito Usage GuideCommands Type the following command in the pull request comment and save the comment.
Refer to the documentation for additional commands. Configuration This repository uses Documentation & Help |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #41858 +/- ##
==========================================
- Coverage 64.77% 64.74% -0.04%
==========================================
Files 2689 2691 +2
Lines 148945 149230 +285
Branches 34369 34416 +47
==========================================
+ Hits 96486 96615 +129
- Misses 50693 50837 +144
- Partials 1766 1778 +12
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
aminghadersohi
left a comment
There was a problem hiding this comment.
TIGHTEN vs LOOSEN verdict: NEITHER — fixes a fail-closed-for-everyone bug without expanding the effective access boundary.
OLD: can_read on User/can_read on Role — never registered by FAB (UserApi/RoleApi only register can_get/can_info/can_post/can_put/can_delete via @permission_name("get") on ModelRestApi.get/get_list), so these tools were dead for every principal including Admin (fail-closed, not a leak).
NEW: can_get on User/can_get on Role — the exact permission FAB registers for the GET endpoints these tools proxy. Critically, SupersetSecurityManager.ADMIN_ONLY_VIEW_MENUS lists "User"/"Role" literally, and _is_admin_only() gates on view_menu name, not permission string — so any permission on these views (can_get included) is Admin-only when Gamma/Alpha/Public role PVM sets sync. The can_read→can_get swap cannot cross that boundary.
Guest/embedded tokens: still denied. Guest roles resolve to GUEST_ROLE_NAME (default "Public"), and Public/Gamma exclude admin-only PVMs — a guest token can't carry can_get on User/Role out of the box.
Consistency: all 4 tools get the identical method_permission_name="get"; auth.py's scope map adds one key (get→superset:read, same tier as the removed "read" mapping) without touching read/write/delete/execute_sql_query for other tools.
Data exposure (serialize_user_object / sensitive-field gating) is pre-existing code, untouched by this diff.
Scans 1–20: no violations — diff is comments + one dict entry + method_permission_name kwargs + tests, no new runtime branches.
One inline suggestion below (non-blocking).
| ("superset.mcp_service.role.tool.get_role_info", "get_role_info"), | ||
| ], | ||
| ) | ||
| def test_user_role_tools_usable_by_stock_admin( |
There was a problem hiding this comment.
MEDIUM (non-blocking): this parametrized test proves the allow-path for a stock Admin holding exactly _FAB_SECURITY_VIEW_PERMISSIONS, but there's no equivalent denied-path test for these 4 tools specifically (e.g. a stock Gamma/Viewer holding none of that set → check_tool_permission is False). The generic deny mechanism is already covered by the pre-existing test_check_tool_permission_denied, so this isn't blocking — but a parametrized "stock non-admin is denied" companion test would make the negative case explicit for can_get on User/Role specifically, matching the rigor of the allow-path test above.
|
The reviewer's suggestion to add a negative test case for non-admin users is appropriate and would improve the test suite's rigor. Adding a parametrized test that verifies tests/unit_tests/mcp_service/test_auth_rbac.py |
rusackas
left a comment
There was a problem hiding this comment.
Thanks @gkneighb, this is a clean fix and the test actually proves the stock-admin failure mode instead of just asserting it. @aminghadersohi's suggestion for a negative-path companion test is a nice add but not a blocker, as noted. LGTM, approving now that CI's clean.
SUMMARY
Fixes a bug that makes four shipped MCP tools —
list_users,get_user_info,list_roles,get_role_info— unusable for every principal, including Admin, on a stock instance.Root cause. The tools declare
class_permission_name="User"/"Role"and rely on the@tooldecorator's default method permission,read, socheck_tool_permissiondemandscan_read on User/can_read on Role. But those permission-view pairs can never exist: FAB's security API views (UserApi,RoleApi) register onlycan_get / can_info / can_post / can_put / can_delete. Superset's ownModelRestApisubclasses remap their methods ontoread/write— which is whycan_read on Chartexists — but the FAB security views get no such remapping. Since Admin only holds permissions that are registered, even Admin is denied (probe evidence from a stock instance:"Permission denied: can_read on Role for user admin"; the metadata DB holds 14 Admin grants on those views, none of themcan_read).The failure is closed (deny), so this is not a security issue — just four dead tools.
Fix.
method_permission_name="get"on the four tools, matching the permission FAB actually registers for their views (can_getcovers both the list and single-item GET endpoints)."get": "superset:read"to_METHOD_TO_REQUIRED_SCOPEinsuperset/mcp_service/auth.py— that map fails closed for unmapped method permissions (by design), so without this entry scoped-JWT deployments would still see the tools denied. Its own doc comment mandates the addition: "When introducing a new method permission, add it here."find_userswas also suspected but is not affected — it declares noclass_permission_name, so it takes the documented allow-by-default path.Found via an automated REST↔MCP capability probe run against master
1c3b070d34.BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF
N/A (MCP service; no UI changes)
TESTING INSTRUCTIONS
New regression tests: a simulated stock Admin holding exactly the FAB-registered permission set (
can_get/can_info/can_post/can_put/can_delete) must passcheck_tool_permissionfor each of the four tools (fails before this change withcan_read), plus an assertion that thegetmethod permission is scope-mapped.Manual: on any instance with
MCP_RBAC_ENABLED = True(the default), calllist_usersas Admin — before this change it returns "Permission denied: can_read on User"; after, it succeeds.ADDITIONAL INFORMATION
🤖 Generated with Claude Code
https://claude.ai/code/session_014z1fQxTadTEvpCJLx9WnB8