From 7d4ba38ba7bf96bfe6426938d3d6977f272b75b2 Mon Sep 17 00:00:00 2001 From: Yong Date: Thu, 30 Oct 2025 22:34:29 -0500 Subject: [PATCH] Add support for token exchange in Iceberg REST catalog --- docs/src/main/sphinx/object-storage/metastores.md | 3 +++ .../iceberg/catalog/rest/OAuth2SecurityConfig.java | 14 ++++++++++++++ .../catalog/rest/OAuth2SecurityProperties.java | 1 + .../catalog/rest/TestOAuth2SecurityConfig.java | 7 +++++-- 4 files changed, 23 insertions(+), 2 deletions(-) diff --git a/docs/src/main/sphinx/object-storage/metastores.md b/docs/src/main/sphinx/object-storage/metastores.md index 5fec3303d28..8966b6fec56 100644 --- a/docs/src/main/sphinx/object-storage/metastores.md +++ b/docs/src/main/sphinx/object-storage/metastores.md @@ -509,6 +509,9 @@ following properties: * - `iceberg.rest-catalog.oauth2.token-refresh-enabled` - Controls whether a token should be refreshed if information about its expiration time is available. Defaults to `true` +* - `iceberg.rest-catalog.oauth2.token-exchange-enabled` + - Controls whether to use the token exchange flow to acquire new tokens. + Defaults to `true` * - `iceberg.rest-catalog.vended-credentials-enabled` - Use credentials provided by the REST backend for file system access. Defaults to `false`. diff --git a/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/catalog/rest/OAuth2SecurityConfig.java b/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/catalog/rest/OAuth2SecurityConfig.java index ccb5675d0e6..66da457f790 100644 --- a/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/catalog/rest/OAuth2SecurityConfig.java +++ b/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/catalog/rest/OAuth2SecurityConfig.java @@ -29,6 +29,7 @@ public class OAuth2SecurityConfig private String token; private URI serverUri; private boolean tokenRefreshEnabled = OAuth2Properties.TOKEN_REFRESH_ENABLED_DEFAULT; + private boolean tokenExchangeEnabled = OAuth2Properties.TOKEN_EXCHANGE_ENABLED_DEFAULT; public Optional getCredential() { @@ -97,6 +98,19 @@ public OAuth2SecurityConfig setTokenRefreshEnabled(boolean tokenRefreshEnabled) return this; } + public boolean isTokenExchangeEnabled() + { + return tokenExchangeEnabled; + } + + @Config("iceberg.rest-catalog.oauth2.token-exchange-enabled") + @ConfigDescription("Controls whether to use the token exchange flow to acquire new tokens") + public OAuth2SecurityConfig setTokenExchangeEnabled(boolean tokenExchangeEnabled) + { + this.tokenExchangeEnabled = tokenExchangeEnabled; + return this; + } + @AssertTrue(message = "OAuth2 requires a credential or token") public boolean credentialOrTokenPresent() { diff --git a/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/catalog/rest/OAuth2SecurityProperties.java b/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/catalog/rest/OAuth2SecurityProperties.java index 5a568306e8a..5e2c9de4926 100644 --- a/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/catalog/rest/OAuth2SecurityProperties.java +++ b/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/catalog/rest/OAuth2SecurityProperties.java @@ -45,6 +45,7 @@ public OAuth2SecurityProperties(OAuth2SecurityConfig securityConfig) securityConfig.getServerUri().ifPresent( value -> propertiesBuilder.put(OAuth2Properties.OAUTH2_SERVER_URI, value.toString())); propertiesBuilder.put(OAuth2Properties.TOKEN_REFRESH_ENABLED, String.valueOf(securityConfig.isTokenRefreshEnabled())); + propertiesBuilder.put(OAuth2Properties.TOKEN_EXCHANGE_ENABLED, String.valueOf(securityConfig.isTokenExchangeEnabled())); this.securityProperties = propertiesBuilder.buildOrThrow(); } diff --git a/plugin/trino-iceberg/src/test/java/io/trino/plugin/iceberg/catalog/rest/TestOAuth2SecurityConfig.java b/plugin/trino-iceberg/src/test/java/io/trino/plugin/iceberg/catalog/rest/TestOAuth2SecurityConfig.java index 6152afeb724..5ad16dc3999 100644 --- a/plugin/trino-iceberg/src/test/java/io/trino/plugin/iceberg/catalog/rest/TestOAuth2SecurityConfig.java +++ b/plugin/trino-iceberg/src/test/java/io/trino/plugin/iceberg/catalog/rest/TestOAuth2SecurityConfig.java @@ -35,7 +35,8 @@ public void testDefaults() .setToken(null) .setScope(null) .setServerUri(null) - .setTokenRefreshEnabled(OAuth2Properties.TOKEN_REFRESH_ENABLED_DEFAULT)); + .setTokenRefreshEnabled(OAuth2Properties.TOKEN_REFRESH_ENABLED_DEFAULT) + .setTokenExchangeEnabled(OAuth2Properties.TOKEN_EXCHANGE_ENABLED_DEFAULT)); } @Test @@ -47,6 +48,7 @@ public void testExplicitPropertyMappings() .put("iceberg.rest-catalog.oauth2.scope", "scope") .put("iceberg.rest-catalog.oauth2.server-uri", "http://localhost:8080/realms/iceberg/protocol/openid-connect/token") .put("iceberg.rest-catalog.oauth2.token-refresh-enabled", "false") + .put("iceberg.rest-catalog.oauth2.token-exchange-enabled", "false") .buildOrThrow(); OAuth2SecurityConfig expected = new OAuth2SecurityConfig() @@ -54,7 +56,8 @@ public void testExplicitPropertyMappings() .setToken("token") .setScope("scope") .setServerUri(URI.create("http://localhost:8080/realms/iceberg/protocol/openid-connect/token")) - .setTokenRefreshEnabled(false); + .setTokenRefreshEnabled(false) + .setTokenExchangeEnabled(false); assertThat(expected.credentialOrTokenPresent()).isTrue(); assertThat(expected.scopePresentOnlyWithCredential()).isFalse(); assertFullMapping(properties, expected);