Repository navigation
@W-24145098 - Add Workload Identity Federation (WIF) admin REST API - #1894
Open
sushmasreedharan-tableau wants to merge 5 commits into
Open
sushmasreedharan-tableau wants to merge 5 commits into
sushmasreedharan-tableau wants to merge 5 commits into
Conversation
Expose the Tableau Server WIF admin surface (REST API 3.30+ / Tableau Server release 2026.3) on the TSC server object as `server.auth_federation`. Site admins can now manage the whole WIF lifecycle from the client, complementing the per-connection WIF attributes delivered in PR tableau#1887. New endpoint methods on `server.auth_federation`: Admin config (`/sites/{siteLuid}/auth-federation/admin`) - get() -> AuthFederationItem - enable(config) -> AuthFederationItem (idempotent PUT; requires enabled=True) - update(config) -> AuthFederationItem (merge-patch; only `token_lifetime_seconds` is mutable - `issuer_url`, `active_key_id`, and `connectors` are read-only and never sent on the wire) - delete() (deletes config + keys + connectors) Connectors (`.../admin/connectors`) - create_connector(connector) -> AuthFederationConnectorItem - list_connectors() -> list[AuthFederationConnectorItem] - get_connector(id) (accepts luid string or item) - delete_connector(id) (accepts luid string or item; idempotent) Keys (`.../admin/keys`) - create_key() -> AuthFederationKeyItem (inactive by default; private key is never returned) - activate_key(id) (atomic; deactivates whatever key was active) - deactivate_key(id) - delete_key(id) (400 if the key is still active) Three new public models exported from both `tableauserverclient.models` and the TSC top-level: `AuthFederationItem`, `AuthFederationConnectorItem`, `AuthFederationKeyItem`. All methods version-gated by @api(version="3.30"). Includes 22 unit tests under `test/test_auth_federation.py` covering the happy path for every method, request-body shape (via defusedxml parsing of `request_history[0].text`) for PUT/PATCH/POST verbs, the read-only-field hygiene of `update()`, the enabled=True guard on `enable()`, the required-field guards on `create_connector`, the list-connectors empty case, both the "pass a str" and "pass an item" forms of every id-taking method, and the EndpointUnavailableError raised when running against a server older than 3.30. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Server-side AuthFederationAdminAppService.updateConfig rejects any non-null `enabled` on PATCH with HTTP 400 and tells callers to use enable() / delete() to change state. The client's update_req already only sends `tokenLifetimeSeconds`, but the docstring listed only issuer_url, active_key_id, and connectors as "silently ignored", leaving `enabled` as a hidden footgun. Add it to the list and point callers at the correct methods. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three docstring claims didn't match what the server actually does per AuthFederationService.java and RestApiAuthFederationKeysAppService.java: * enable(): did not mention that AuthFederationService.enableWif auto-generates one active signing key on first enable (AuthFederationService.java:160-164). A caller doesn't need to immediately create_key + activate_key — the returned item's active_key_id is already populated. * list_connectors(): claimed empty-list on "WIF not enabled". The server's requireWifEnabled (AuthFederationService.java:226-233) throws WIF_NOT_ENABLED_FOR_SITE / FAILED_PRECONDITION, which the client surfaces as ServerResponseError. Only an empty configured site returns []. * create_key(): claimed 409 for the max-keys-exceeded case. The server throws BadRequestRestException (HTTP 400), not 409 (RestApiAuthFederationKeysAppService.java:44-48). Docstring-only; no code or test changes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
RestApiAuthFederationAdminAppService.listConnectors intercepts TableauStatusCode.FAILED_PRECONDITION from the service-layer requireWifEnabled check and re-throws configNotFound (HTTP 404) with AUTH_FEDERATION_NOT_FOUND -- see RestApiAuthFederationAdminAppService.java:157-162. The service-layer behavior cited in the previous commit does not reach the wire; callers see 404. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Reword PAT/OAuth-scope guidance: the four key methods are bound to OAuthScopes.NO_ACCESS server-side, so every OAuth-scoped session (including every PAT) is rejected unconditionally. Authenticate with a username/password session instead. Reflected in CHANGELOG and the class docstring. - Drop the misleading "Snowflake" case-sensitivity counter-example in create_connector; just state that class names are case-sensitive and must be the exact lowercase name. - Note in CHANGELOG that delete_key fails with 400 on an active key; deactivate first. - Replace vague "server rejects the request" in the class docstring with the specific HTTP 404 the server returns when WIF has never been enabled. - Make the _coerce_uuid wording in CHANGELOG explicit: None, empty strings, and non-UUID-shaped targets are rejected client-side. - Add a comment in test_auth_federation_update_empty_patch noting that server-side binding of an empty <authFederationAdmin/> must be covered by an integration test, not this unit test. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Expose the Tableau Server WIF admin REST surface (REST API 3.30+ / Tableau Server release 2026.3) on the TSC server object as
server.auth_federation. Lets a site admin manage the WIF site configuration, signing keys, and connectors from the client, and complements the per-connection WIF attributes delivered in #1887.Scope stops at the admin surface (
/sites/{siteLuid}/auth-federation/admin[/connectors|/keys]). The public.well-knownendpoints (/openid-configuration,/jwks.json) and the sign-in path are consumed by Snowflake / GCP / BigQuery directly, not by TSC callers, and are intentionally out of scope for this PR.New endpoint methods
Admin config (
/sites/{siteLuid}/auth-federation/admin)server.auth_federation.get() -> AuthFederationItemserver.auth_federation.enable(config) -> AuthFederationItem— idempotent PUT; requiresconfig.enabled=Trueserver.auth_federation.update(config) -> AuthFederationItem— merge-patch; onlytoken_lifetime_secondsis sent on the wire;issuer_url,active_key_id, andconnectorsare server-assigned and omittedserver.auth_federation.delete() -> None— removes the config, every signing key, and every connector on the siteConnectors (
.../admin/connectors)server.auth_federation.create_connector(connector) -> AuthFederationConnectorItemserver.auth_federation.list_connectors() -> list[AuthFederationConnectorItem]server.auth_federation.get_connector(id)— accepts LUID string orAuthFederationConnectorItemserver.auth_federation.delete_connector(id)— accepts LUID string orAuthFederationConnectorItem; idempotent server-sideKeys (
.../admin/keys)server.auth_federation.create_key() -> AuthFederationKeyItem— inactive by default; private key is never returnedserver.auth_federation.activate_key(id)— atomic: deactivates whichever key was previously activeserver.auth_federation.deactivate_key(id)server.auth_federation.delete_key(id)— HTTP 400 if the key is still activeAll methods are version-gated by
@api(version="3.30").New public types
Exported from both
tableauserverclient.modelsand the TSC top level (TSC.X):AuthFederationItem— admin config (enabled,issuer_url,active_key_id,token_lifetime_seconds,connectors)AuthFederationConnectorItem— single connector (luid,connector_class,audience)AuthFederationKeyItem— JWK metadata (kid,kty,alg,use,n,e)Design notes mirrored from the server
enable()enforcesenabled=Trueclient-side; the server also rejectsenabled=falsewith HTTP 400 and tells callers to useDELETEinstead.update()only emitstokenLifetimeSecondson the wire. The server silently ignores read-only fields on PATCH; the client does the same to avoid pretending they're mutable.delete_connector()is idempotent server-side (returns 204 even if the connector does not exist); orphan connections that referenced the deleted connector are not cascade-deleted.create_key()sends only the empty<tsRequest/>envelope (POST body is empty on the Java controller side).activate_key()/deactivate_key()/delete_key()take the JWKkidas the URL path parameter — same value the server returns asauthFederationKey@kid.Test plan
python -m pytest test/test_auth_federation.py— 22 new tests, all passingpython -m pytest test/— full suite: 977 passed, 1 pre-existing skip, 0 failurespython -m mypy tableauserverclient— no issues in 106 source filespython -m black tableauserverclient test— formatteddefusedxmlparsing ofrequests_mockhistory for every PUT / PATCH / POST method (not just string matching)EndpointUnavailableErrorverified when running against a server pinned to 3.29🤖 Generated with Claude Code