fix(gsheets): don't require domain-wide delegation to validate a connection - #43309
fix(gsheets): don't require domain-wide delegation to validate a connection#43309aminghadersohi wants to merge 4 commits into
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #43309 +/- ##
==========================================
- Coverage 66.73% 66.67% -0.07%
==========================================
Files 2876 2876
Lines 164228 164149 -79
Branches 37890 37850 -40
==========================================
- Hits 109598 109443 -155
- Misses 52471 52546 +75
- Partials 2159 2160 +1
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:
|
Code Review Agent Run #88aeffActionable Suggestions - 0Additional Suggestions - 2
Review 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 |
|
The flagged issue is correct. The current implementation of To resolve this, you should modify Would you like me to fetch all other comments on this PR to validate them and implement a comprehensive fix? superset/db_engine_specs/gsheets.py |
|
@aminghadersohi I might be wrong here, but I think query execution later would actually use the logged in user's email? At least the Preset docs says "In order to create the connection, it is required that the email address associated with your Preset account has access to the Google Sheets file." Unsure if the doc here was explicitly calling out only the connection creation, or if it meant to include later execution. If that's really true (might be worth manually testing) then this would either:
Another important detail is that Superset has an "Impersonate logged in user" checkbox that typically controls OAuth2 enablement. If we want to support Service Account auth to bypass OAuth, we probably want to gate this behind this checkbox. I think in the past editing the GSheets connection wouldn't even allow you to disable the checkbox, so we might need to allow that. Now, a bigger question would be: do we even need to continue supporting OAuth2-type connection via Service Account? This feature was introduced before native support for DB OAuth2 was added to Superset (GSheets included, and it doesn't even require a Service Account). With that in mind, one possible outcome would be to create a SIP to deprecate this "OAuth2 validation" for Service Account auth, and keep only:
This would be a breaking change as existing connections with Service Account + Domain-wide delegation would have to migrate to OAuth2, but might make more sense long-term. Curious if @betodealmeida has any thoughts here as well. |
|
@Vitor-Avila you're right, and this invalidates a claim I made in the PR description. Thanks for catching it — I traced it properly and the docs you linked describe real behavior, not just connection creation. Query execution does use the logged-in user's email. The chain:
So my " On your second bullet — this PR can't affect query execution at all, since On gating it behind the checkbox — I think that's the correct narrow fix, and the plumbing already exists: On deprecating SA-based impersonation via a SIP — that's a maintainer call and I'd defer to you and @betodealmeida. I'd only note it's orthogonal to this bug: whichever way that lands, today a service account without domain-wide delegation fails with Keeping this in draft in the meantime. |
One limitation here is that the checkbox only shows up to be changed after the connection is created. So you might need to make the URL check loose, which is not great.
Oh, I meant to say that SA + DWD is deprecated + the checkbox is unchecked (and no longer works for SA auth) + queries always use SA only. |
✅ Deploy Preview for superset-docs-preview ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
|
Confirmed, and that kills the gating idea — thanks, I'd have shipped a fix with a hole in it. The Advanced tab that hosts the checkbox is gated on Your clarification also lands differently than I'd read it the first time. "SA + DWD deprecated, checkbox unchecked and no longer wired to SA auth, queries always SA-only" is a coherent model, and it's better than what's there now — the current design authenticates validation as one identity and queries as another, and no amount of patching inside So I don't think this PR should carry the behavior change. Two ways to land it, and I'd rather you pick than guess:
Happy to do either, or to help with the SIP write-up if that's where this is heading. Not asking for a merge on the current diff either way — the two commits on it now are the incomplete fix plus a test, and I'll strip or rewrite them to match whichever option you want. |
Code Review Agent Run #a0357fActionable 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 |
|
Closing this in favour of a SIP. @Vitor-Avila you convinced me. The core problem isn't in I'll put up the SIP. When I do, I'll carry over the findings from this PR so they don't get lost:
One alternative I'll raise in the SIP for completeness rather than relitigate here: your original checkbox-gating idea may survive if the hardcode is removed and the control is exposed during create, since existing connections keep their stored Thanks for the careful review here; you caught a wrong premise in my PR description before it turned into a fix that would have broken query execution for the people it claimed to help. |
|
thanks @aminghadersohi |
|
I think in general OAuth2 is always better than a service account. With a service account, the |
Why
Adding a Google Sheets connection in "Public and privately shared sheets" mode with a service-account JSON is rejected in the UI with
The URL could not be identified. Please check for typos and make sure that 'Type of Google Sheets allowed' selection matches the input.— even when the URL is correct and the service account can read the sheet.GSheetsEngineSpec.validate_parametersunconditionally passedsubject = g.user.emailto shillelagh'sgsheetsapiadapter. Passing asubjectmakes Google authenticate through domain-wide delegation, impersonating that user. A service account without domain-wide delegation configured gets backinvalid_grant: Invalid email or User ID, the validationSELECTfails, and the failure surfaces as the misleadingTABLE_DOES_NOT_EXIST_ERRORabove.Domain-wide delegation is a Google Workspace admin-only setting. The common setup — create a service account, share the sheet with its email — could therefore never pass validation, making the connection impossible to create through the modal.
POST /api/v1/database/does not go throughvalidate_parameters, so the API path worked while the UI path did not.Notably,
subjectis not passed at query time:update_params_from_encrypted_extraonly setsservice_account_infoandcatalog. Impersonation has its own correctly-scoped home inGSheetsEngineSpec.impersonate_user. Validation was the only place forcing delegation, so it was validating under different credentials than the connection actually uses.Alternatives considered: dropping
subjectentirely is simpler, but it regresses domain-wide delegation setups where the sheet is shared with the admin rather than the service account, which the original comment explicitly intended to support. Hence the fallback below.What
Validate each catalog URL as the service account itself first — matching how the connection behaves at query time — and only fall back to impersonating the current user if that read fails. Domain-wide delegation setups keep working; setups without it now succeed instead of failing with a misleading error. The URL read is extracted into
_can_read_url, and connection setup into_get_validation_connections.Blast radius
Google Sheets connections only, and only the
POST /api/v1/database/validate_parameters/path (the UI Connect button). No change to query execution, impersonation, OAuth2 connections (still skipped), auth, or any other engine spec. No schema or config change. Strictly widens what validates successfully — no connection that validated before will stop validating.How to test
Two regression tests in
tests/unit_tests/db_engine_specs/test_gsheets.py, both of which fail onmaster:test_validate_parameters_without_domain_wide_delegation— a sheet readable by the service account validates, and the first connection is built withsubject: None.test_validate_parameters_falls_back_to_domain_wide_delegation— when the service account itself can't read the sheet, validation falls back to impersonating the current user and still succeeds.The two existing
validate_parameterscatalog tests were updated to mock a distinct engine per subject so both paths are asserted.Risk & rollback
Low. The failure mode would be validation accepting a sheet that the connection can't later read — but the primary attempt now uses exactly the credentials the connection uses at query time, so this is strictly closer to reality than before. Back-out is a plain revert; there is no flag or migration.
Review guidance
Start with
superset/db_engine_specs/gsheets.py. The riskiest hunk is_get_validation_connections— specifically the ordering decision (service account first, impersonation second) and the fact that both connections are now built eagerly. Building a shillelagh connection is local and does no network I/O, andany()short-circuits so the second is only used when the first read fails. I'd welcome a sanity check from someone running domain-wide delegation that the fallback preserves their flow.