Visitar URL original
fix(mcp): preserve embedded guest identity during permalink lookup by rebenitez1802 · Pull Request #45346 · apache/superset · GitHub
Skip to content

fix(mcp): preserve embedded guest identity during permalink lookup - #45346

Merged
rebenitez1802 merged 1 commit into
masterfrom
rebenitez1802/mcp-permalink-guest-identity
Oct 15, 2026
Merged

rebenitez1802 merged 1 commit into
masterfrom
rebenitez1802/mcp-permalink-guest-identity

Conversation

@rebenitez1802

Copy link
Copy Markdown
Contributor

SUMMARY

refresh_request_user_for_permalink_access() reloads the request user from the metadata
database before dashboard permalink access checks, so a stored user's latest roles apply.
Embedded guest users have no stored account: their identity, dashboard resources, dataset
restrictions and row-level security rules come from the guest token. This change returns early
for guest users, as it already does for anonymous users, so permalink lookups keep the
token-scoped guest identity. Stored users are refreshed exactly as before.

BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF

N/A (backend only)

TESTING INSTRUCTIONS

pytest tests/unit_tests/mcp_service/dashboard/test_permalink.py

The new test resolves a permalink as a GuestUser and asserts the guest identity and its token
restrictions are unchanged and no stored-user reload happens. Existing tests cover stored-user
refresh.

ADDITIONAL INFORMATION

  • Has associated issue:
  • Required feature flags:
  • Changes UI
  • Includes DB Migration (follow approval process in SIP-59)
    • Migration is atomic, supports rollback & is backwards-compatible
    • Confirm DB migration upgrade and downgrade tested
    • Runtime estimates and downtime expectations provided
  • Introduces new feature or API
  • Removes existing feature or API

🤖 Generated with Claude Code

refresh_request_user_for_permalink_access() reloads the request user from
the metadata database so a stored user's latest roles apply to permalink
access checks. Embedded guest users have no stored account; their identity
and restrictions come from the guest token. Return early for guest users,
as is already done for anonymous users.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@netlify

netlify Bot commented Oct 15, 2026

Copy link
Copy Markdown

✅ Deploy Preview for superset-docs-preview ready!

Name Link
🔨 Latest commit 12ff232
🔍 Latest deploy log https://app.netlify.com/projects/superset-docs-preview/deploys/6ad0e27fe9ad9d25ec4bcd9c
😎 Deploy Preview https://deploy-preview-45346--superset-docs-preview.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@codeant-ai-for-open-source

Copy link
Copy Markdown
Contributor

CodeAnt PR Risk: Low Risk

  • The PR appears safe to merge; permalink access now preserves token-scoped guest identities instead of reloading them as stored users.
  • A regression test verifies the guest token and row-level security data remain intact and no user reload occurs.

Assessed commit: 12ff232cc24a

@codecov

codecov Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 83.44%. Comparing base (1aeb864) to head (12ff232).
⚠️ Report is 6 commits behind head on master.

Additional details and impacted files
@@             Coverage Diff             @@
##           master   #45346       +/-   ##
===========================================
+ Coverage   64.93%   83.44%   +18.51%     
===========================================
  Files        3040     3042        +2     
  Lines      198718   199070      +352     
  Branches    46381    46413       +32     
===========================================
+ Hits       129033   166119    +37086     
+ Misses      66779    29725    -37054     
- Partials     2906     3226      +320     
Flag Coverage Δ
hive 35.00% <0.00%> (+0.02%) ⬆️
mysql 52.97% <0.00%> (?)
postgres 52.97% <0.00%> (?)
presto 36.76% <0.00%> (+0.01%) ⬆️
python 86.97% <100.00%> (+34.09%) ⬆️
sqlite 52.73% <0.00%> (-0.03%) ⬇️
unit 80.99% <100.00%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@gabotorresruiz gabotorresruiz left a comment

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.

Hey @rebenitez1802, thanks for this one. The fix reads right to me and I could not break it: against a real metadata DB I ran a guest minted through create_guest_access_token / parse_jwt_guest_token with and without a username collision, plus an anonymous user and stored Gamma and Admin users, over two permalinks. The colliding guest is the only case that moves, and it moves the way you describe: on the parent commit g.user comes back as the stored User with that account's roles so the guest branch of raise_for_access never runs, while on this branch the GuestUser keeps its resources and rls and still resolves its own dashboard's permalink. Your new test fails on the parent and survived the mutations I tried.

Left one non-blocking note inline. Holding the formal approval only because unit-tests (current) is still pending, happy to flip it once that is green.

def refresh_request_user_for_permalink_access() -> None:
"""Reload the request user before permalink access checks."""
"""Reload stored users while preserving token-scoped guest identities."""
if not has_request_context() or not getattr(g, "user", None):

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.

Not a blocker, and the guard itself looks right to me. Flagging because it is easy to miss: when the MCP service runs on its own (superset mcp run, streamable-http or stdio) a tool call executes under an app context only. _mcp_tool_call_context() pushes app_context() and never a request context, so has_request_context() is False on this line and the helper returns before the anonymous branch or your new guest branch is reached. I confirmed that on a live get_dashboard_info call with a signed guest token.

That does not make the fix wrong, it makes it the right shape for a deployment that pushes a request context around the tool call, which is the g.user fallback get_user_from_request() documents for external middleware. Two small things:

  • worth a line in the docstring that the refresh only applies when a request context is present?
  • is has_request_context() the gate you actually want? Under the standalone server a stored user's roles are never refreshed either, which is what this helper exists for. If that ever becomes has_app_context() the guest guard goes from defensive to load bearing, so landing it first is right either way.

Or am I missing a transport that does push a request context?

mock_load.assert_called_once_with(username="admin")


def test_permalink_lookup_keeps_guest_identity(app: Any) -> None:

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.

Just a small NIT: the other three tests in this file take app: Flask, and Any is imported only for this one annotation, so the import on line 20 can go back to cast alone.

Suggested change
def test_permalink_lookup_keeps_guest_identity(app: Any) -> None:
def test_permalink_lookup_keeps_guest_identity(app: Flask) -> None:

Comment on lines +82 to +84
if getattr(current_user, "is_anonymous", False) or getattr(
current_user, "is_guest_user", False
):

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.

Worth noting for whoever reviews after me: worker.py:704 already carries this exact predicate, guest = user if getattr(user, "is_guest_user", False) else None, and line 726 puts the guest back after re-hydrating g.user by id on a worker thread. So both the spelling and the precedent were already upstream and this helper was the one place missing it. Good argument for the fix living right here.

@gabotorresruiz gabotorresruiz left a comment

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.

Approving, @rebenitez1802. The condition I held on has landed, so this clears from my side.

I could not get a colliding guest past the guard. A guest whose token username matches a stored account keeps its own identity, roles, resources and rls through both get_dashboard_permalink and get_dashboard_permalink_data_mask, and on the parent it resolved a permalink outside its token as that stored account. Stored users and anonymous are byte for byte unchanged. Your new test fails on the real merge base, and both mutations I tried on the guard are caught.

All twelve required checks are green at 12ff232c. The unit-tests (current) matrix job is re-running after an infrastructure cancellation that had nothing to do with this diff.

My two earlier comments stand as context and a nit, neither blocking.

@rebenitez1802
rebenitez1802 merged commit fed77ad into master Oct 15, 2026
78 of 80 checks passed
@rebenitez1802
rebenitez1802 deleted the rebenitez1802/mcp-permalink-guest-identity branch October 15, 2026 18:45
villebro pushed a commit that referenced this pull request Oct 16, 2026
…45346)

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
(cherry picked from commit fed77ad)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants