Skip to content

fix(lang): patch FAB's LocaleView to redirect to previous page#31692

Merged
mistercrunch merged 10 commits intoapache:masterfrom
open-craft:jill/patch_flask_locale_redirect
Apr 15, 2025
Merged

fix(lang): patch FAB's LocaleView to redirect to previous page#31692
mistercrunch merged 10 commits intoapache:masterfrom
open-craft:jill/patch_flask_locale_redirect

Conversation

@pomegranited
Copy link
Contributor

@pomegranited pomegranited commented Jan 3, 2025

SUMMARY

Overrides FAB's LocaleView to redirect to the request's referrer instead of relying on the session page history as suggested here.

This isn't a perfect solution, but it keeps the change very close to the actual issue.

Note that this view is marked out in the tests as an "unsecured" view, because we need to be able to change the current locale even when logged out (so that the login page can be presented in the user's preferred language).

Rejected alternatives

  1. Call update_redirect() whenever a GET view is rendered (like this).
    This is more in line with how Flask AppBuilder works (and was recommended here).
    However, this only works for pages rendered by Superset's backend, and most Superset pages are rendered by the frontend.
  2. Hook into the frontend's action logging to update the page history, e.g when a LOG_ACTIONS_SPA_NAVIGATION event is logged to the backend.
    This approach felt even hackier than the one posted here, and farther from the issue it addresses.
    Also, Flask AppBuilder only provides the update_redirect() method which uses the current request.url, so we'd need to add another method that could use a different url.

Fixes: #12768
Part of: #30334
Fixes: openedx/openedx-aspects#299

BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF

Before:

Untitled.video.-.Made.with.Clipchamp.12.mp4

After:

Language.redirect.-.Made.with.Clipchamp.mp4

TESTING INSTRUCTIONS

  1. Load Superset with multiple languages enabled.
  2. Visit any page.
  3. Change your current language using the language switcher menu on the top right.
  4. Ensure that your language is changed and you're redirect back to the page in step 2.

ADDITIONAL INFORMATION

  • Fixes Redirect happens whenever user changes language #12768
  • 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

to redirect to the request referrer instead of relying on the session
page_history.

This change is needed because most of Superset's page views are handled
by the frontend, and as such, not stored in the session page_history.
Copy link

@korbit-ai korbit-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review by Korbit AI

Korbit automatically attempts to detect when you fix issues in new commits.
Category Issue Fix Detected
Error Handling Non-descriptive locale error message ▹ view
Error Handling Unvalidated redirect from Referer header ▹ view
Functionality Insecure Open Redirect ▹ view

Need a new review? Comment /korbit-review on this PR and I'll review your latest changes.

Korbit Guide: Usage and Customization

Interacting with Korbit

  • You can manually ask Korbit to review your PR using the /korbit-review command in a comment at the root of your PR.
  • You can ask Korbit to generate a new PR description using the /korbit-generate-pr-description command in any comment on your PR.
  • Too many Korbit comments? I can resolve all my comment threads if you use the /korbit-resolve command in any comment on your PR.
  • Chat with Korbit on issues we post by tagging @korbit-ai in your reply.
  • Help train Korbit to improve your reviews by giving a 👍 or 👎 on the comments Korbit posts.

Customizing Korbit

  • Check out our docs on how you can make Korbit work best for you and your team.
  • Customize Korbit for your organization through the Korbit Console.

Feedback and Support

@pomegranited pomegranited changed the title fix(lang): patch FAB's LocaleView fix(lang): patch FAB's LocaleView to redirect to previous page Jan 3, 2025
@pull-request-size pull-request-size bot added size/M and removed size/S labels Jan 3, 2025
@rusackas rusackas requested a review from dpgaspar January 3, 2025 21:04
@rusackas
Copy link
Member

rusackas commented Jan 3, 2025

Installing the pre-commit hook should resolve at least some of the CI issues.

/
Superset uses Git pre-commit hooks courtesy of pre-commit. To install run the following:

pip3 install -r requirements/development.txt
pre-commit install
A series of checks will now run when you make a git commit.

Alternatively it is possible to run pre-commit by running pre-commit manually:

pre-commit run --all-files

@codecov
Copy link

codecov bot commented Jan 7, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 83.26%. Comparing base (5f62dea) to head (267e2aa).
⚠️ Report is 440 commits behind head on master.

Additional details and impacted files
@@             Coverage Diff             @@
##           master   #31692       +/-   ##
===========================================
+ Coverage        0   83.26%   +83.26%     
===========================================
  Files           0      552      +552     
  Lines           0    39892    +39892     
===========================================
+ Hits            0    33218    +33218     
- Misses          0     6674     +6674     
Flag Coverage Δ
hive 48.28% <46.66%> (?)
mysql 75.47% <100.00%> (?)
postgres 75.54% <100.00%> (?)
presto 52.75% <46.66%> (?)
python 83.26% <100.00%> (?)
sqlite 75.04% <100.00%> (?)
unit 61.32% <46.66%> (?)

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

☔ View full report in Codecov by Sentry.
📢 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.

Because we need to be able to change the locale even when logged out,
this view cannot be protected.
@pomegranited
Copy link
Contributor Author

Hi @rusackas :) Is there anything more I can do here to get this PR approved?

@rusackas
Copy link
Member

I was hoping someone more FABtastic than me would come along to review this. If not @dpgaspar, perhaps @mistercrunch can take a look.

@pomegranited
Copy link
Contributor Author

Hi @jpchev , would you like to review this PR for me? Might help get it merged if there's some community testing/support :)

@jpchev
Copy link
Contributor

jpchev commented Feb 12, 2025

Hi @jpchev , would you like to review this PR for me? Might help get it merged if there's some community testing/support :)

yes, I've tested the changes and can confirm I am not redirected to another page after switching language

Copy link
Member

@dpgaspar dpgaspar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, would be great if you could add a couple of tests

@pomegranited pomegranited requested a review from dpgaspar March 4, 2025 20:28
@pomegranited
Copy link
Contributor Author

hi @dpgaspar , is there anything else you need from me before this can be merged?

@pomegranited
Copy link
Contributor Author

Hi @rusackas , I've applied @dpgaspar 's suggestions, but this has been needing another review/approval for a while now.. What can I do to get this merged? Would love to make the next release candidate.

@pomegranited
Copy link
Contributor Author

@mistercrunch Can you help me get this PR merged?

return redirect("/superset/welcome/")

@staticmethod
def is_safe_url(target: str) -> bool:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

might belong in superset/utils/urls.py would be surprised if we don't already have a similar method there, grepping for urlparse or next= could point to a similar method.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

otherwise PR LGTM

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you @mistercrunch ! I didn't find a similar method anywhere, so 4bb4916 creates superset.utils.urls.is_safe_redirect_url(source_url, target_url).

Does this look ok now?

return parsed_url.scheme == "https"


def is_safe_redirect_url(source_url: str, target_url: str) -> bool:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sorry for the multi-phase review, but GPT recommends further checking here, and since this is security for XSS I'm thinking let's do it...

def is_safe_redirect_url(source_url: str, target_url: str) -> bool:
    if not target_url:
        return False
    joined = urljoin(source_url, target_url)  # resolves relative URLs
    parsed_source = urlparse(source_url)
    parsed_target = urlparse(joined)
    return (
        parsed_source.scheme == parsed_target.scheme and
        parsed_source.hostname == parsed_target.hostname
    )

GPT says: This handles edge cases like user-supplied target_url values starting with // (which browsers interpret as external redirects) or other relative path tricks. Using urljoin() ensures we're validating the fully resolved URL against the expected scheme and host. Safer for open internet exposure.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Easy done, thank you for this security fix @mistercrunch ! Applied with 08010c9

However, should we to use the joined target_url when doing the actual redirect?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I've found what we needed in FAB: get_safe_redirect. My only worry is it's not exposed as an explicit part of the API, and so might change out from under us. But it's used in several places in the FAB code base, so it's a calculated risk.

@rusackas
Copy link
Member

@pomegranited just approved CI and it looks like this is mergeable pending feedback/action on the last comment from @mistercrunch.

Copy link
Member

@rusackas rusackas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was getting itchy over the merge button, so I'll block this awaiting feedback/implementation of the security-based code suggestion provided by @mistercrunch. CC @dpgaspar / @Antonio-RiveroMartnez for any other additional feedback or suggestions on that approach.

@mistercrunch
Copy link
Member

We just need to be super careful around the ?next={some_url} pattern. Might be a good time to review what we already have here, and merge all codepaths that use this pattern into a state-of-the-art function that handles it all.

Couldn't find the pattern in the codebase after a quick search, maybe FAB exposes something we piggy-back on? @dpgaspar

Copy link
Member

@mistercrunch mistercrunch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@mistercrunch
Copy link
Member

Still would love for @dpgaspar to chime in, maybe FAB has builtin functions for that already

@pull-request-size pull-request-size bot added size/M and removed size/L labels Apr 2, 2025
@pomegranited
Copy link
Contributor Author

Hi @rusackas I found a FAB method to do what we need, so I think this resolves @mistercrunch 's concern?

@dpgaspar
Copy link
Member

dpgaspar commented Apr 2, 2025

Hi @rusackas I found a FAB method to do what we need, so I think this resolves @mistercrunch 's concern?

Nice! yes is_safe_redirect_url or get_safe_redirect on FAB's utils.base

Copy link
Member

@dpgaspar dpgaspar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, not a perfect solution but an improvement

@pomegranited
Copy link
Contributor Author

Hi @rusackas , I think we're good to merge now?

@pomegranited pomegranited requested a review from rusackas April 9, 2025 02:49
@pomegranited
Copy link
Contributor Author

Hi @rusackas , I think I've addressed everything here, and we got an approval from our FAB expert.. can this be merged?

@mistercrunch mistercrunch dismissed rusackas’s stale review April 15, 2025 16:32

Requested changes have been addressed

@mistercrunch
Copy link
Member

hitting a weird GH bug I've never seen before ->
Screenshot 2025-04-15 at 9 34 10 AM

Will try again a little later...

@mistercrunch mistercrunch merged commit 93fa39a into apache:master Apr 15, 2025
50 checks passed
@pomegranited pomegranited deleted the jill/patch_flask_locale_redirect branch April 16, 2025 05:21
@pomegranited
Copy link
Contributor Author

Yay!! Thank you @mistercrunch @rusackas @dpgaspar for your help here!

@michael-s-molina michael-s-molina added the v5.0 Label added by the release manager to track PRs to be included in the 5.0 branch label Apr 17, 2025
@mistercrunch mistercrunch added 🍒 5.0.0 Cherry-picked to 5.0.0 🏷️ bot A label used by `supersetbot` to keep track of which PR where auto-tagged with release labels labels Jul 29, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🏷️ bot A label used by `supersetbot` to keep track of which PR where auto-tagged with release labels size/M v5.0 Label added by the release manager to track PRs to be included in the 5.0 branch 🍒 5.0.0 Cherry-picked to 5.0.0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Superset i18n: redirect back to previous page after changing language setting Redirect happens whenever user changes language

6 participants