Skip to content

feat: add Workload Identity Federation (WIF) support to connection update - #1887

Merged
jacalata merged 6 commits into
tableau:developmentfrom
sushmasreedharan-tableau:dev/sreedharan/w-24145082-wif-connection-update
Oct 15, 2026
Merged

jacalata merged 6 commits into
tableau:developmentfrom
sushmasreedharan-tableau:dev/sreedharan/w-24145082-wif-connection-update

Conversation

@sushmasreedharan-tableau

@sushmasreedharan-tableau sushmasreedharan-tableau commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

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: workgroupAuthMode and
authFederationServiceAccount. 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:

  • ConnectionItem gains two new string properties:
    workgroup_auth_mode and auth_federation_service_account.
  • ConnectionItem.WorkgroupAuthMode is 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_connection emit both attributes on the <connection>
    payload when the corresponding ConnectionItem fields 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_mode and
    auth_federation_service_account keyword arguments and apply them
    to every connection LUID in the request.
  • ConnectionItem.from_response parses 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 future
    server echo).

Usage rules the server enforces (TSC is permissive and relies on
server-side validation):

  • Resend workgroup_auth_mode on every subsequent update of a WIF
    connection; omitting it on a later update returns HTTP 400.
  • Leave auth_type unset (or "oauth") when sending a WIF mode; the
    server derives the stored auth type from the mode.
  • Set auth_federation_service_account only with the impersonation
    mode. The server rejects a service account alongside the direct WIF
    mode.

Compatibility:

  • Requires Tableau Server REST API 3.30+ (Tableau Server release
    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.
  • No endpoint @api version decorator changes — this matches TSC's
    established 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 — parametrized test_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 parametrized
    test_update_workbook_connections_wif[direct|impersonation] for the
    bulk endpoint.
  • test/test_datasource.py — identical shape against datasource
    endpoints.
  • test/test_flow.py — test_update_connection_wif exercising the
    shared Connection.update_req reach through the flow endpoint.

Each singular test populates a ConnectionItem with the new fields,
calls update_connection, and asserts against the serialized request
XML — verifying both that the attributes are emitted with the exact
spelling (workgroupAuthMode / authFederationServiceAccount) and
that authFederationServiceAccount is absent in direct (non-impersonation)
mode. Bulk tests assert the same on the single <connection> payload
emitted for the whole LUID list. The from_response tests assert that
WIF attrs are parsed when the server does echo them and are None
when 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_response degrade cleanly to None on response XML
without the new attributes.

black --check --line-length 120 and mypy 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

…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>

Copilot AI 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.

🟡 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.

Comment on lines +1229 to +1232
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

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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.WorkgroupAuthMode and dropped the two WORKGROUP_AUTH_MODE_WIF* module-level exports (prior review feedback on namespacing).
  • Added a docstring note on flows.update_connection clarifying that WIF fields reach the request via the shared Connection.update_req builder, 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, and test_update_connection_wif_from_response_absent on both workbook and datasource test suites, plus a new test_update_connection_wif on test_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>
@sushmasreedharan-tableau

Copy link
Copy Markdown
Contributor Author

Pushed three commits addressing the inline review feedback:

5507895 — fix: use googlebigquery and add WIF response attrs in test assets

  • test/assets/datasource_update_connection_wif.xml: switched type="bigquery" to the current connector class type="googlebigquery".
  • Both WIF test asset XMLs: dropped the broken xsi:schemaLocation="…/ts-api_3_30.xsd" (that URL 404s) and added authenticationType="oauth", workgroupAuthMode="…", and (for impersonation) authFederationServiceAccount="…" so the fixtures match a plausible WIF-configured server response.

3e70623 — feat: nest WIF value set under ConnectionItem and extend to bulk and flows

  • ConnectionItem.WorkgroupAuthMode.WIF / WIF_IMPERSONATION as a nested value set; dropped the two WORKGROUP_AUTH_MODE_WIF* module-level re-exports from tableauserverclient/__init__.py and tableauserverclient/models/__init__.py.
  • Bulk endpoints: both Datasource.update_connections_req and Workbook.update_connections_req builders in request_factory.py now accept workgroup_auth_mode and auth_federation_service_account kwargs and emit them on the single <connection> payload. The workbooks.update_connections and datasources.update_connections endpoint methods thread the kwargs through, with docstrings covering the new parameters.
  • Flows: added a docstring paragraph on flows.update_connection noting that WIF fields reach the request via the shared Connection.update_req builder, with the caveat that server-side flow handling may differ.
  • Expanded tests on test_workbook.py and test_datasource.py: parametrized test_update_connection_wif[direct|impersonation], plus 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 parametrized test_update_{workbook,datasource}_connections_wif[direct|impersonation] for the bulk path. Added test_update_connection_wif on test_flow.py.

f2bad9b — docs: expand WIF guidance in CHANGELOG

  • Rewrote the Unreleased WIF entry to name the nested value set, list all five WIF-emitting endpoints, and state the usage rules explicitly: resend workgroup_auth_mode on every subsequent update (server returns HTTP 400 otherwise), leave auth_type unset or "oauth", pair auth_federation_service_account only with WIF_IMPERSONATION, and note that the server does not echo these attributes on GET today so parsed values are typically None.
  • Clarified "3.30+" is the REST API version shipping with Tableau Server release 26.3, and that older servers silently ignore unknown connection attributes (so leaving these fields unset is safe on pre-3.30 targets).
  • PR description updated with the same accuracy improvements.

Local: black --check --line-length 120 and mypy tableauserverclient/ clean; pytest -n0 test/test_workbook.py test/test_datasource.py test/test_flow.py test/test_connection_.py — 164 passed.

@jacalata
jacalata merged commit ecff569 into tableau:development Oct 15, 2026
29 checks passed
sushmasreedharan-tableau added a commit that referenced this pull request Oct 15, 2026
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants