gabotorresruiz commented on code in PR #45984:
URL: https://github.com/apache/superset/pull/45984#discussion_r4460653704


##########
superset/mcp_service/auth.py:
##########
@@ -185,11 +185,16 @@ def _get_token_scopes() -> set[str] | None:
     if access_token is None:
         return None
 
-    scopes = getattr(access_token, "scopes", None)
-    if not scopes:
+    scopes_from_token = getattr(access_token, "scopes", None)
+    if not scopes_from_token:
         # Token present but no scopes advertised: do NOT enforce scope checks.
         return None
-    return {str(s) for s in scopes}
+    ignored_scopes = current_app.config.get("MCP_IGNORED_SCOPES", {})
+    scopes = {str(s) for s in scopes_from_token if str(s) not in 
ignored_scopes}

Review Comment:
   Not a blocker, but this is the one line that decides whether scope 
enforcement runs at all, so I would really like to see it pinned in 
`tests/unit_tests/mcp_service/test_auth_rbac.py` before merge. Three flat tests 
using the existing `_patch_token_scopes` helper cover it; the first fails on 
master and passes here, the other two pin that dropping `openid` never loosens 
a token that also carries a real scope, and that the default stays unchanged:
   
   ```python
   def test_ignored_scopes_restore_rbac_only_when_nothing_remains(app_context, 
app) -> None:
       """A token whose only scopes are all ignored is treated as scope-less."""
       g.user = MagicMock(username="editor")
       func = _make_tool_func(class_perm="Chart", method_perm="write")
   
       mock_sm = MagicMock()
       mock_sm.can_access = MagicMock(return_value=True)
       app.config["MCP_IGNORED_SCOPES"] = {"openid", "profile", "email", 
"offline_access"}
       try:
           with (
               patch("superset.mcp_service.auth.security_manager", mock_sm),
               _patch_token_scopes(["openid", "profile"]),
           ):
               assert check_tool_permission(func) is True
           with _patch_token_scopes(["openid"]):
               assert check_tool_permission(_make_tool_func()) is True
       finally:
           app.config.pop("MCP_IGNORED_SCOPES", None)
   
   
   def test_ignored_scopes_keep_remaining_scope_enforced(app_context, app) -> 
None:
       """Dropping ``openid`` must not loosen a token that also carries a real 
scope."""
       g.user = MagicMock(username="editor")
       func = _make_tool_func(class_perm="Chart", method_perm="write")
   
       mock_sm = MagicMock()
       mock_sm.can_access = MagicMock(return_value=True)
       app.config["MCP_IGNORED_SCOPES"] = {"openid"}
       try:
           with (
               patch("superset.mcp_service.auth.security_manager", mock_sm),
               _patch_token_scopes(["openid", "superset:read"]),
           ):
               assert check_tool_permission(func) is False
       finally:
           app.config.pop("MCP_IGNORED_SCOPES", None)
   
   
   def test_ignored_scopes_default_leaves_scoped_tokens_enforced(app_context) 
-> None:
       """With the default (empty) ignored set, an identity-only token still 
denies."""
       g.user = MagicMock(username="editor")
       with _patch_token_scopes(["openid"]):
           assert check_tool_permission(_make_tool_func()) is False
   ```



##########
superset/mcp_service/auth.py:
##########
@@ -185,11 +185,16 @@ def _get_token_scopes() -> set[str] | None:
     if access_token is None:
         return None
 
-    scopes = getattr(access_token, "scopes", None)
-    if not scopes:
+    scopes_from_token = getattr(access_token, "scopes", None)
+    if not scopes_from_token:
         # Token present but no scopes advertised: do NOT enforce scope checks.
         return None
-    return {str(s) for s in scopes}
+    ignored_scopes = current_app.config.get("MCP_IGNORED_SCOPES", {})
+    scopes = {str(s) for s in scopes_from_token if str(s) not in 
ignored_scopes}
+    if not scopes:
+        # Token where all scopes advertised are ignored: do NOT enforce scope 
checks.
+        return None

Review Comment:
   Just to set expectations for the testing instructions: this branch only 
makes the `Scope denied for permission-less tool ...` line go away. The `is 
permission-protected but declares no class_permission_name` warning is emitted 
unconditionally once per tool whenever RBAC is on (in `check_tool_permission`, 
right before the scope check), so it still shows up once per tool here. I 
verified it with a `scope=openid` token against `superset mcp run`. Might be 
worth trimming the PR description so nobody reads that remaining line as the 
option not working.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

  • [PR] feat(c... yves-renier (via GitHub) via notifications
    • Re: [P... codeant-ai-for-open-source[bot] (via GitHub) via notifications
    • Re: [P... codeant-ai-for-open-source[bot] (via GitHub) via notifications
    • Re: [P... yves-renier (via GitHub) via notifications
    • Re: [P... codecov[bot] (via GitHub) via notifications
    • Re: [P... gabotorresruiz (via GitHub) via notifications
    • Re: [P... netlify[bot] (via GitHub) via notifications
    • Re: [P... yves-renier (via GitHub) via notifications
    • Re: [P... yves-renier (via GitHub) via notifications

Reply via email to