Repository navigation
feat: add Workload Identity Federation (WIF) support to connection update - #1887
Conversation
…unt to connection update The Tableau REST API's update-connection endpoints for workbooks and datasources (REST API 3.30+) accept two new XML attributes on <connection> payloads for Workload Identity Federation: - workgroupAuthMode: "workload-identity-federation" (direct, e.g. Snowflake) or "workload-identity-federation-impersonation" (impersonation, e.g. BigQuery) - authFederationServiceAccount: service account email, required for impersonation only Expose both as string properties on ConnectionItem and emit them from RequestFactory.Connection.update_req (the shared builder used by workbooks, datasources, and flows for both single and bulk variants). Also expose two constants on the public API: WORKGROUP_AUTH_MODE_WIF, WORKGROUP_AUTH_MODE_WIF_IMPERSONATION. Tests cover direct and impersonation variants against workbook and datasource update-connection endpoints. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Collapse auth_federation_service_account assignment onto a single line per the project's 120-char limit. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
🟡 Changes recommended
Bulk update_connections endpoints do not yet serialize the advertised WIF fields.
1 open finding
What changed in this PR
Adds Workload Identity Federation support for singular workbook and datasource connection updates.
Changes:
- Adds WIF fields and public constants.
- Serializes WIF attributes for singular updates.
- Adds response parsing, tests, fixtures, exports, and changelog documentation.
| File | Summary |
|---|---|
test/test_workbook.py |
Tests workbook WIF updates. |
test/test_datasource.py |
Tests datasource WIF updates. |
test/assets/workbook_update_connection_wif.xml |
Workbook WIF fixture. |
test/assets/datasource_update_connection_wif.xml |
Datasource WIF fixture. |
tableauserverclient/server/request_factory.py |
Serializes WIF attributes for singular updates. |
tableauserverclient/models/connection_item.py |
Adds WIF fields and response parsing. |
tableauserverclient/models/__init__.py |
Re-exports WIF constants. |
tableauserverclient/__init__.py |
Exports WIF constants. |
CHANGELOG.md |
Documents WIF support. |
🧠 Review effort: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| if connection_item.workgroup_auth_mode is not None: | ||
| connection_element.attrib["workgroupAuthMode"] = connection_item.workgroup_auth_mode | ||
| if connection_item.auth_federation_service_account is not None: | ||
| connection_element.attrib["authFederationServiceAccount"] = connection_item.auth_federation_service_account |
There was a problem hiding this comment.
Addressed in 3e70623.
Both Datasource.update_connections_req and Workbook.update_connections_req now accept workgroup_auth_mode and auth_federation_service_account kwargs and emit them on the single <connection> payload sent for the LUID list. The matching bulk endpoint methods (workbooks.update_connections / datasources.update_connections) thread those kwargs through, and the parametrized test_update_{workbook,datasource}_connections_wif[direct|impersonation] tests assert the emitted attributes.
Also, in the same commit:
- Nested the WIF value set under
ConnectionItem.WorkgroupAuthModeand dropped the twoWORKGROUP_AUTH_MODE_WIF*module-level exports (prior review feedback on namespacing). - Added a docstring note on
flows.update_connectionclarifying that WIF fields reach the request via the sharedConnection.update_reqbuilder, with the caveat that server-side flow handling may differ. - Added
test_update_connection_wif_switch_from_oauth,test_update_connection_wif_cleared_fields_omit_attrs,test_update_connection_wif_service_account_without_mode,test_update_connection_wif_from_response, andtest_update_connection_wif_from_response_absenton both workbook and datasource test suites, plus a newtest_update_connection_wifontest_flow.py.
Address review on tableau#1887: datasource WIF asset was carrying the legacy "bigquery" connector class, and both WIF assets referenced a 404 schemaLocation URL. Switch the datasource asset to the current "googlebigquery" class, drop the broken schemaLocation reference, and populate both WIF response assets with workgroupAuthMode (and authFederationServiceAccount where impersonation applies) so the new from_response coverage actually exercises WIF parsing. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…flows Address review on tableau#1887: * Replace the two module-level WORKGROUP_AUTH_MODE_WIF* constants with a nested value set at ConnectionItem.WorkgroupAuthMode (WIF / WIF_IMPERSONATION) and drop the top-level reexports from tableauserverclient and tableauserverclient.models. The nested class carries the usage rules (resend on every update, no service account with direct mode, auth_type unset or "oauth", server echo caveat). * Extend the two bulk endpoints to really support WIF: workbooks.update_connections and datasources.update_connections now accept workgroup_auth_mode and auth_federation_service_account keyword arguments, and both Workbook.update_connections_req and Datasource.update_connections_req builders emit them on the shared <connection> payload applied to every connection LUID. * Document on flows.update_connection that WIF fields on the ConnectionItem reach the endpoint via the shared Connection.update_req builder and that server-side handling on flow connections may differ from workbook / datasource endpoints. * Rewrite the WIF test suite to cover: parametrized direct and impersonation happy paths, OAuth to WIF switch, cleared-fields non emission, service-account-without-mode permissive emit, from_response WIF parsing, from_response absent (older servers), and bulk WIF forwarding. Add a flow WIF test exercising the shared builder reach. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Address review on tableau#1887: expand the Unreleased CHANGELOG entry to name the new nested value set (ConnectionItem.WorkgroupAuthMode.WIF / WIF_IMPERSONATION), note that the bulk workbooks.update_connections and datasources.update_connections endpoints now accept the matching keyword arguments, add flows.update_connection to the list of endpoints that forward WIF, state the usage rules (resend on every update, no service account with direct mode, auth_type unset or "oauth", server does not echo on GET today), and clarify that "3.30+" is a Tableau Server REST API version (26.3 server release) and that older servers silently ignore unknown connection attributes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Pushed three commits addressing the inline review feedback:
Local: |
Adds documentation on the gh-pages site for the WIF support added in PR #1887: - ConnectionItem attribute table: new rows for workgroup_auth_mode and auth_federation_service_account, naming the ConnectionItem.WorkgroupAuthMode nested value set and its two values. - datasources.update_connection: WIF how-to with direct and impersonation examples plus the server-side usage rules. - datasources.update_connections (bulk): updated signature, new parameter rows, version note, and a WIF example. - flows.update_connection: short note that WIF fields reach the request via the shared Connection.update_req builder, with the caveat that server-side flow handling may differ. - workbooks.update_connection: WIF how-to mirroring the datasource section. - workbooks.update_connections (bulk): updated signature, new parameter rows, version note, and a WIF example. Requires REST API 3.30+ (Tableau Server release 26.3) with WIF configured on the site. Older servers silently ignore unknown connection attributes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

Motivation
Tableau Server REST API 3.30 (Tableau Server release 26.3) adds
Workload Identity Federation (WIF) support to the Update Connection
endpoints via two new
<connection>attributes:workgroupAuthModeandauthFederationServiceAccount. TSC currently has no way to set these,so Python callers against a WIF-enabled 3.30+ site can't programmatically
switch a workbook, data source, or flow connection to WIF (e.g.
Snowflake or Google BigQuery) without hand-rolling raw REST calls.
Behavior change
Additive, opt-in, backward compatible. For users:
ConnectionItemgains two new string properties:workgroup_auth_modeandauth_federation_service_account.ConnectionItem.WorkgroupAuthModeis a new nested value set:ConnectionItem.WorkgroupAuthMode.WIF="workload-identity-federation"— direct WIF (e.g. Snowflake)ConnectionItem.WorkgroupAuthMode.WIF_IMPERSONATION="workload-identity-federation-impersonation"— impersonation WIF (e.g. Google BigQuery)workbooks.update_connection/datasources.update_connection/flows.update_connectionemit both attributes on the<connection>payload when the corresponding
ConnectionItemfields are set.Unset fields are not emitted, so existing callers see no payload
change.
workbooks.update_connections/datasources.update_connections(bulk) accept matching
workgroup_auth_modeandauth_federation_service_accountkeyword arguments and apply themto every connection LUID in the request.
ConnectionItem.from_responseparses the two attributes defensively(REST 3.30 does not echo them on GET today, so parsed values are
typically
None— the parse is forward-compatible with any futureserver echo).
Usage rules the server enforces (TSC is permissive and relies on
server-side validation):
workgroup_auth_modeon every subsequent update of a WIFconnection; omitting it on a later update returns HTTP 400.
auth_typeunset (or"oauth") when sending a WIF mode; theserver derives the stored auth type from the mode.
auth_federation_service_accountonly with the impersonationmode. The server rejects a service account alongside the direct
WIFmode.
Compatibility:
26.3) with WIF configured and enabled on the site for the attributes
to take effect. Older servers silently ignore unknown connection
attributes, so leaving these fields unset is safe on pre-3.30
targets.
@apiversion decorator changes — this matches TSC'sestablished pattern for additive attribute support on existing
endpoints.
CHANGELOG entry added under Unreleased.
Test plan
New unit tests, all mocked (no live server), cover both WIF modes
across all three endpoints:
test/test_workbook.py— parametrizedtest_update_connection_wif[direct|impersonation],test_update_connection_wif_switch_from_oauth,test_update_connection_wif_cleared_fields_omit_attrs,test_update_connection_wif_service_account_without_mode,test_update_connection_wif_from_response,test_update_connection_wif_from_response_absent, and parametrizedtest_update_workbook_connections_wif[direct|impersonation]for thebulk endpoint.
test/test_datasource.py— identical shape against datasourceendpoints.
test/test_flow.py—test_update_connection_wifexercising theshared
Connection.update_reqreach through the flow endpoint.Each singular test populates a
ConnectionItemwith the new fields,calls
update_connection, and asserts against the serialized requestXML — verifying both that the attributes are emitted with the exact
spelling (
workgroupAuthMode/authFederationServiceAccount) andthat
authFederationServiceAccountis absent in direct (non-impersonation)mode. Bulk tests assert the same on the single
<connection>payloademitted for the whole LUID list. The from_response tests assert that
WIF attrs are parsed when the server does echo them and are
Nonewhen it does not.
Baselines still pass:
test_odata_connection,test_update_connections,and
test_update_connections_without_auth_type— the additive.get()calls in
from_responsedegrade cleanly toNoneon response XMLwithout the new attributes.
black --check --line-length 120andmypy tableauserverclient/clean.
pytest -n0 test/test_workbook.py test/test_datasource.py test/test_flow.py test/test_connection_.py— 164 passed.Attribute spellings and value strings verified against the
authoritative REST API 3.30 schema and the server-side bindings for
the Update Connection endpoints. Not exercised end-to-end against a
live 3.30 server in this PR.
🤖 Generated with Claude Code