You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@iceberg.apache.org by dw...@apache.org on 2024/02/17 01:35:58 UTC
(iceberg) branch main updated: Support usage of Separate OIDC Authorization Server URI (#8976)
This is an automated email from the ASF dual-hosted git repository.
dweeks pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/iceberg.git
The following commit(s) were added to refs/heads/main by this push:
new 9dcf8dbc42 Support usage of Separate OIDC Authorization Server URI (#8976)
9dcf8dbc42 is described below
commit 9dcf8dbc4285ad4ab4c1975562bf93fb04747cdd
Author: Sung Yun <10...@users.noreply.github.com>
AuthorDate: Fri Feb 16 18:35:53 2024 -0700
Support usage of Separate OIDC Authorization Server URI (#8976)
* configurable token-uri
* more tests
* lint
* minor changes
* suppress style
* address feedback
* adopt review feedback
* update config name
* lint
* adopt review comments
---
.../aws/s3/signer/S3V4RestSignerClient.java | 16 +-
.../java/org/apache/iceberg/rest/HTTPClient.java | 29 +-
.../apache/iceberg/rest/RESTSessionCatalog.java | 7 +-
.../apache/iceberg/rest/auth/OAuth2Properties.java | 3 +
.../org/apache/iceberg/rest/auth/OAuth2Util.java | 86 +++++-
.../apache/iceberg/rest/RESTCatalogAdapter.java | 66 ++--
.../org/apache/iceberg/rest/TestRESTCatalog.java | 342 +++++++++++++++------
7 files changed, 391 insertions(+), 158 deletions(-)
diff --git a/aws/src/main/java/org/apache/iceberg/aws/s3/signer/S3V4RestSignerClient.java b/aws/src/main/java/org/apache/iceberg/aws/s3/signer/S3V4RestSignerClient.java
index 99f1588f0c..f284822161 100644
--- a/aws/src/main/java/org/apache/iceberg/aws/s3/signer/S3V4RestSignerClient.java
+++ b/aws/src/main/java/org/apache/iceberg/aws/s3/signer/S3V4RestSignerClient.java
@@ -41,6 +41,7 @@ import org.apache.iceberg.relocated.com.google.common.collect.Maps;
import org.apache.iceberg.rest.ErrorHandlers;
import org.apache.iceberg.rest.HTTPClient;
import org.apache.iceberg.rest.RESTClient;
+import org.apache.iceberg.rest.ResourcePaths;
import org.apache.iceberg.rest.auth.OAuth2Properties;
import org.apache.iceberg.rest.auth.OAuth2Util;
import org.apache.iceberg.rest.auth.OAuth2Util.AuthSession;
@@ -111,6 +112,12 @@ public abstract class S3V4RestSignerClient
return properties().get(OAuth2Properties.CREDENTIAL);
}
+ /** Token endpoint URI to fetch token from if the Rest Catalog is not the authorization server. */
+ @Value.Lazy
+ public String oauth2ServerUri() {
+ return properties().getOrDefault(OAuth2Properties.OAUTH2_SERVER_URI, ResourcePaths.tokens());
+ }
+
/** A Bearer token supplier which will be used for interaction with the server. */
@Value.Default
public Supplier<String> token() {
@@ -199,7 +206,8 @@ public abstract class S3V4RestSignerClient
tokenRefreshExecutor(),
token,
expiresAtMillis(properties()),
- new AuthSession(ImmutableMap.of(), token, null, credential(), SCOPE)));
+ new AuthSession(
+ ImmutableMap.of(), token, null, credential(), SCOPE, oauth2ServerUri())));
}
if (credentialProvided()) {
@@ -208,10 +216,12 @@ public abstract class S3V4RestSignerClient
credential(),
id -> {
AuthSession session =
- new AuthSession(ImmutableMap.of(), null, null, credential(), SCOPE);
+ new AuthSession(
+ ImmutableMap.of(), null, null, credential(), SCOPE, oauth2ServerUri());
long startTimeMillis = System.currentTimeMillis();
OAuthTokenResponse authResponse =
- OAuth2Util.fetchToken(httpClient(), session.headers(), credential(), SCOPE);
+ OAuth2Util.fetchToken(
+ httpClient(), session.headers(), credential(), SCOPE, oauth2ServerUri());
return AuthSession.fromTokenResponse(
httpClient(), tokenRefreshExecutor(), authResponse, startTimeMillis, session);
});
diff --git a/core/src/main/java/org/apache/iceberg/rest/HTTPClient.java b/core/src/main/java/org/apache/iceberg/rest/HTTPClient.java
index 55ed1422b3..9366630954 100644
--- a/core/src/main/java/org/apache/iceberg/rest/HTTPClient.java
+++ b/core/src/main/java/org/apache/iceberg/rest/HTTPClient.java
@@ -205,16 +205,24 @@ public class HTTPClient implements RESTClient {
}
private URI buildUri(String path, Map<String, String> params) {
- String baseUri = String.format("%s/%s", uri, path);
+ // if full path is provided, use the input path as path
+ if (path.startsWith("/")) {
+ throw new RESTException(
+ "Received a malformed path for a REST request: %s. Paths should not start with /", path);
+ }
+ String fullPath =
+ (path.startsWith("https://") || path.startsWith("http://"))
+ ? path
+ : String.format("%s/%s", uri, path);
try {
- URIBuilder builder = new URIBuilder(baseUri);
+ URIBuilder builder = new URIBuilder(fullPath);
if (params != null) {
params.forEach(builder::addParameter);
}
return builder.build();
} catch (URISyntaxException e) {
throw new RESTException(
- "Failed to create request URI from base %s, params %s", baseUri, params);
+ "Failed to create request URI from base %s, params %s", fullPath, params);
}
}
@@ -223,7 +231,7 @@ public class HTTPClient implements RESTClient {
*
* @param method - HTTP method, such as GET, POST, HEAD, etc.
* @param queryParams - A map of query parameters
- * @param path - URL path to send the request to
+ * @param path - URI to send the request to
* @param requestBody - Content to place in the request body
* @param responseType - Class of the Response type. Needs to have serializer registered with
* ObjectMapper
@@ -250,7 +258,7 @@ public class HTTPClient implements RESTClient {
*
* @param method - HTTP method, such as GET, POST, HEAD, etc.
* @param queryParams - A map of query parameters
- * @param path - URL path to send the request to
+ * @param path - URL to send the request to
* @param requestBody - Content to place in the request body
* @param responseType - Class of the Response type. Needs to have serializer registered with
* ObjectMapper
@@ -270,11 +278,6 @@ public class HTTPClient implements RESTClient {
Map<String, String> headers,
Consumer<ErrorResponse> errorHandler,
Consumer<Map<String, String>> responseHeaders) {
- if (path.startsWith("/")) {
- throw new RESTException(
- "Received a malformed path for a REST request: %s. Paths should not start with /", path);
- }
-
HttpUriRequestBase request = new HttpUriRequestBase(method.name(), buildUri(path, queryParams));
if (requestBody instanceof Map) {
@@ -459,9 +462,9 @@ public class HTTPClient implements RESTClient {
this.properties = properties;
}
- public Builder uri(String baseUri) {
- Preconditions.checkNotNull(baseUri, "Invalid uri for http client: null");
- this.uri = RESTUtil.stripTrailingSlash(baseUri);
+ public Builder uri(String path) {
+ Preconditions.checkNotNull(path, "Invalid uri for http client: null");
+ this.uri = RESTUtil.stripTrailingSlash(path);
return this;
}
diff --git a/core/src/main/java/org/apache/iceberg/rest/RESTSessionCatalog.java b/core/src/main/java/org/apache/iceberg/rest/RESTSessionCatalog.java
index 5f660f0f4f..87519cfd9d 100644
--- a/core/src/main/java/org/apache/iceberg/rest/RESTSessionCatalog.java
+++ b/core/src/main/java/org/apache/iceberg/rest/RESTSessionCatalog.java
@@ -178,11 +178,14 @@ public class RESTSessionCatalog extends BaseViewSessionCatalog
OAuthTokenResponse authResponse;
String credential = props.get(OAuth2Properties.CREDENTIAL);
String scope = props.getOrDefault(OAuth2Properties.SCOPE, OAuth2Properties.CATALOG_SCOPE);
+ String oauth2ServerUri =
+ props.getOrDefault(OAuth2Properties.OAUTH2_SERVER_URI, ResourcePaths.tokens());
try (RESTClient initClient = clientBuilder.apply(props)) {
Map<String, String> initHeaders =
RESTUtil.merge(configHeaders(props), OAuth2Util.authHeaders(initToken));
if (credential != null && !credential.isEmpty()) {
- authResponse = OAuth2Util.fetchToken(initClient, initHeaders, credential, scope);
+ authResponse =
+ OAuth2Util.fetchToken(initClient, initHeaders, credential, scope, oauth2ServerUri);
Map<String, String> authHeaders =
RESTUtil.merge(initHeaders, OAuth2Util.authHeaders(authResponse.token()));
config = fetchConfig(initClient, authHeaders, props);
@@ -209,7 +212,7 @@ public class RESTSessionCatalog extends BaseViewSessionCatalog
this.paths = ResourcePaths.forCatalogProperties(mergedProps);
String token = mergedProps.get(OAuth2Properties.TOKEN);
- this.catalogAuth = new AuthSession(baseHeaders, null, null, credential, scope);
+ this.catalogAuth = new AuthSession(baseHeaders, null, null, credential, scope, oauth2ServerUri);
if (authResponse != null) {
this.catalogAuth =
AuthSession.fromTokenResponse(
diff --git a/core/src/main/java/org/apache/iceberg/rest/auth/OAuth2Properties.java b/core/src/main/java/org/apache/iceberg/rest/auth/OAuth2Properties.java
index 8bd75c5ebb..e1a9181d16 100644
--- a/core/src/main/java/org/apache/iceberg/rest/auth/OAuth2Properties.java
+++ b/core/src/main/java/org/apache/iceberg/rest/auth/OAuth2Properties.java
@@ -27,6 +27,9 @@ public class OAuth2Properties {
/** A credential to exchange for a token in the OAuth2 client credentials flow. */
public static final String CREDENTIAL = "credential";
+ /** Token endpoint URI to fetch token from if the Rest Catalog is not the authorization server. */
+ public static final String OAUTH2_SERVER_URI = "oauth2-server-uri";
+
/**
* Interval in milliseconds to wait before attempting to exchange the configured catalog Bearer
* token. By default, token exchange will be attempted after 1 hour.
diff --git a/core/src/main/java/org/apache/iceberg/rest/auth/OAuth2Util.java b/core/src/main/java/org/apache/iceberg/rest/auth/OAuth2Util.java
index 678642830c..ad1821a3f2 100644
--- a/core/src/main/java/org/apache/iceberg/rest/auth/OAuth2Util.java
+++ b/core/src/main/java/org/apache/iceberg/rest/auth/OAuth2Util.java
@@ -134,7 +134,8 @@ public class OAuth2Util {
Map<String, String> headers,
String subjectToken,
String subjectTokenType,
- String scope) {
+ String scope,
+ String oauth2ServerUri) {
Map<String, String> request =
tokenExchangeRequest(
subjectToken,
@@ -143,7 +144,7 @@ public class OAuth2Util {
OAuthTokenResponse response =
client.postForm(
- ResourcePaths.tokens(),
+ oauth2ServerUri,
request,
OAuthTokenResponse.class,
headers,
@@ -160,7 +161,8 @@ public class OAuth2Util {
String subjectTokenType,
String actorToken,
String actorTokenType,
- String scope) {
+ String scope,
+ String oauth2ServerUri) {
Map<String, String> request =
tokenExchangeRequest(
subjectToken,
@@ -171,7 +173,7 @@ public class OAuth2Util {
OAuthTokenResponse response =
client.postForm(
- ResourcePaths.tokens(),
+ oauth2ServerUri,
request,
OAuthTokenResponse.class,
headers,
@@ -181,15 +183,38 @@ public class OAuth2Util {
return response;
}
+ public static OAuthTokenResponse exchangeToken(
+ RESTClient client,
+ Map<String, String> headers,
+ String subjectToken,
+ String subjectTokenType,
+ String actorToken,
+ String actorTokenType,
+ String scope) {
+ return exchangeToken(
+ client,
+ headers,
+ subjectToken,
+ subjectTokenType,
+ actorToken,
+ actorTokenType,
+ scope,
+ ResourcePaths.tokens());
+ }
+
public static OAuthTokenResponse fetchToken(
- RESTClient client, Map<String, String> headers, String credential, String scope) {
+ RESTClient client,
+ Map<String, String> headers,
+ String credential,
+ String scope,
+ String oauth2ServerUri) {
Map<String, String> request =
clientCredentialsRequest(
credential, scope != null ? ImmutableList.of(scope) : ImmutableList.of());
OAuthTokenResponse response =
client.postForm(
- ResourcePaths.tokens(),
+ oauth2ServerUri,
request,
OAuthTokenResponse.class,
headers,
@@ -199,6 +224,12 @@ public class OAuth2Util {
return response;
}
+ public static OAuthTokenResponse fetchToken(
+ RESTClient client, Map<String, String> headers, String credential, String scope) {
+
+ return fetchToken(client, headers, credential, scope, ResourcePaths.tokens());
+ }
+
private static Map<String, String> tokenExchangeRequest(
String subjectToken, String subjectTokenType, List<String> scopes) {
return tokenExchangeRequest(subjectToken, subjectTokenType, null, null, scopes);
@@ -361,7 +392,26 @@ public class OAuth2Util {
private final String credential;
private final String scope;
private volatile boolean keepRefreshed = true;
+ private final String oauth2ServerUri;
+ public AuthSession(
+ Map<String, String> baseHeaders,
+ String token,
+ String tokenType,
+ String credential,
+ String scope,
+ String oauth2ServerUri) {
+ this.headers = RESTUtil.merge(baseHeaders, authHeaders(token));
+ this.token = token;
+ this.tokenType = tokenType;
+ this.expiresAtMillis = OAuth2Util.expiresAtMillis(token);
+ this.credential = credential;
+ this.scope = scope;
+ this.oauth2ServerUri = oauth2ServerUri;
+ }
+
+ /** @deprecated since 1.5.0, will be removed in 1.6.0 */
+ @Deprecated
public AuthSession(
Map<String, String> baseHeaders,
String token,
@@ -374,6 +424,7 @@ public class OAuth2Util {
this.expiresAtMillis = OAuth2Util.expiresAtMillis(token);
this.credential = credential;
this.scope = scope;
+ this.oauth2ServerUri = ResourcePaths.tokens();
}
public Map<String, String> headers() {
@@ -404,6 +455,10 @@ public class OAuth2Util {
return credential;
}
+ public String oauth2ServerUri() {
+ return oauth2ServerUri;
+ }
+
@VisibleForTesting
static void setTokenRefreshNumRetries(int retries) {
tokenRefreshNumRetries = retries;
@@ -415,7 +470,8 @@ public class OAuth2Util {
* @return A new {@link AuthSession} with empty headers.
*/
public static AuthSession empty() {
- return new AuthSession(ImmutableMap.of(), null, null, null, OAuth2Properties.CATALOG_SCOPE);
+ return new AuthSession(
+ ImmutableMap.of(), null, null, null, OAuth2Properties.CATALOG_SCOPE, null);
}
/**
@@ -470,14 +526,14 @@ public class OAuth2Util {
return refreshExpiredToken(client);
} else {
// attempt a normal refresh
- return refreshToken(client, headers(), token, tokenType, scope);
+ return refreshToken(client, headers(), token, tokenType, scope, oauth2ServerUri);
}
}
private OAuthTokenResponse refreshExpiredToken(RESTClient client) {
if (credential != null) {
Map<String, String> basicHeaders = RESTUtil.merge(headers(), basicAuthHeaders(credential));
- return refreshToken(client, basicHeaders, token, tokenType, scope);
+ return refreshToken(client, basicHeaders, token, tokenType, scope, oauth2ServerUri);
}
return null;
@@ -533,7 +589,8 @@ public class OAuth2Util {
token,
OAuth2Properties.ACCESS_TOKEN_TYPE,
parent.credential(),
- parent.scope());
+ parent.scope(),
+ parent.oauth2ServerUri());
long startTimeMillis = System.currentTimeMillis();
Long expiresAtMillis = session.expiresAtMillis();
@@ -571,7 +628,8 @@ public class OAuth2Util {
AuthSession parent) {
long startTimeMillis = System.currentTimeMillis();
OAuthTokenResponse response =
- fetchToken(client, parent.headers(), credential, parent.scope());
+ fetchToken(
+ client, parent.headers(), credential, parent.scope(), parent.oauth2ServerUri());
return fromTokenResponse(client, executor, response, startTimeMillis, parent, credential);
}
@@ -598,7 +656,8 @@ public class OAuth2Util {
response.token(),
response.issuedTokenType(),
credential,
- parent.scope());
+ parent.scope(),
+ parent.oauth2ServerUri());
Long expiresAtMillis = session.expiresAtMillis();
if (null == expiresAtMillis && response.expiresInSeconds() != null) {
@@ -627,7 +686,8 @@ public class OAuth2Util {
tokenType,
parent.token(),
parent.tokenType(),
- parent.scope());
+ parent.scope(),
+ parent.oauth2ServerUri());
return fromTokenResponse(client, executor, response, startTimeMillis, parent);
}
}
diff --git a/core/src/test/java/org/apache/iceberg/rest/RESTCatalogAdapter.java b/core/src/test/java/org/apache/iceberg/rest/RESTCatalogAdapter.java
index 1974838ede..7fccc4e974 100644
--- a/core/src/test/java/org/apache/iceberg/rest/RESTCatalogAdapter.java
+++ b/core/src/test/java/org/apache/iceberg/rest/RESTCatalogAdapter.java
@@ -112,6 +112,8 @@ public class RESTCatalogAdapter implements RESTClient {
enum Route {
TOKENS(HTTPMethod.POST, "v1/oauth/tokens", null, OAuthTokenResponse.class),
+ SEPARATE_AUTH_TOKENS_URI(
+ HTTPMethod.POST, "https://auth-server.com/token", null, OAuthTokenResponse.class),
CONFIG(HTTPMethod.GET, "v1/config", null, ConfigResponse.class),
LIST_NAMESPACES(HTTPMethod.GET, "v1/namespaces", null, ListNamespacesResponse.class),
CREATE_NAMESPACE(
@@ -245,43 +247,40 @@ public class RESTCatalogAdapter implements RESTClient {
}
}
+ private static OAuthTokenResponse handleOAuthRequest(Object body) {
+ Map<String, String> request = (Map<String, String>) castRequest(Map.class, body);
+ String grantType = request.get("grant_type");
+ switch (grantType) {
+ case "client_credentials":
+ return OAuthTokenResponse.builder()
+ .withToken("client-credentials-token:sub=" + request.get("client_id"))
+ .withIssuedTokenType("urn:ietf:params:oauth:token-type:access_token")
+ .withTokenType("Bearer")
+ .build();
+
+ case "urn:ietf:params:oauth:grant-type:token-exchange":
+ String actor = request.get("actor_token");
+ String token =
+ String.format(
+ "token-exchange-token:sub=%s%s",
+ request.get("subject_token"), actor != null ? ",act=" + actor : "");
+ return OAuthTokenResponse.builder()
+ .withToken(token)
+ .withIssuedTokenType("urn:ietf:params:oauth:token-type:access_token")
+ .withTokenType("Bearer")
+ .build();
+
+ default:
+ throw new UnsupportedOperationException("Unsupported grant_type: " + grantType);
+ }
+ }
+
@SuppressWarnings({"MethodLength", "checkstyle:CyclomaticComplexity"})
public <T extends RESTResponse> T handleRequest(
Route route, Map<String, String> vars, Object body, Class<T> responseType) {
switch (route) {
case TOKENS:
- {
- @SuppressWarnings("unchecked")
- Map<String, String> request = (Map<String, String>) castRequest(Map.class, body);
- String grantType = request.get("grant_type");
- switch (grantType) {
- case "client_credentials":
- return castResponse(
- responseType,
- OAuthTokenResponse.builder()
- .withToken("client-credentials-token:sub=" + request.get("client_id"))
- .withIssuedTokenType("urn:ietf:params:oauth:token-type:access_token")
- .withTokenType("Bearer")
- .build());
-
- case "urn:ietf:params:oauth:grant-type:token-exchange":
- String actor = request.get("actor_token");
- String token =
- String.format(
- "token-exchange-token:sub=%s%s",
- request.get("subject_token"), actor != null ? ",act=" + actor : "");
- return castResponse(
- responseType,
- OAuthTokenResponse.builder()
- .withToken(token)
- .withIssuedTokenType("urn:ietf:params:oauth:token-type:access_token")
- .withTokenType("Bearer")
- .build());
-
- default:
- throw new UnsupportedOperationException("Unsupported grant_type: " + grantType);
- }
- }
+ return castResponse(responseType, handleOAuthRequest(body));
case CONFIG:
return castResponse(responseType, ConfigResponse.builder().build());
@@ -469,6 +468,9 @@ public class RESTCatalogAdapter implements RESTClient {
}
default:
+ if (responseType == OAuthTokenResponse.class) {
+ return castResponse(responseType, handleOAuthRequest(body));
+ }
}
return null;
diff --git a/core/src/test/java/org/apache/iceberg/rest/TestRESTCatalog.java b/core/src/test/java/org/apache/iceberg/rest/TestRESTCatalog.java
index 0e260a4e53..cd7710d460 100644
--- a/core/src/test/java/org/apache/iceberg/rest/TestRESTCatalog.java
+++ b/core/src/test/java/org/apache/iceberg/rest/TestRESTCatalog.java
@@ -353,8 +353,7 @@ public class TestRESTCatalog extends CatalogTests<RESTCatalog> {
any());
}
- @Test
- public void testCatalogCredential() {
+ public void testCatalogCredentialNoOauth2ServerUri() {
Map<String, String> emptyHeaders = ImmutableMap.of();
Map<String, String> catalogHeaders =
ImmutableMap.of("Authorization", "Bearer client-credentials-token:sub=catalog");
@@ -400,8 +399,64 @@ public class TestRESTCatalog extends CatalogTests<RESTCatalog> {
any());
}
- @Test
- public void testCatalogBearerTokenWithClientCredential() {
+ @ParameterizedTest
+ @ValueSource(strings = {"v1/oauth/tokens", "https://auth-server.com/token"})
+ public void testCatalogCredential(String oauth2ServerUri) {
+ Map<String, String> emptyHeaders = ImmutableMap.of();
+ Map<String, String> catalogHeaders =
+ ImmutableMap.of("Authorization", "Bearer client-credentials-token:sub=catalog");
+
+ RESTCatalogAdapter adapter = Mockito.spy(new RESTCatalogAdapter(backendCatalog));
+
+ RESTCatalog catalog =
+ new RESTCatalog(SessionCatalog.SessionContext.createEmpty(), (config) -> adapter);
+ catalog.initialize(
+ "prod",
+ ImmutableMap.of(
+ CatalogProperties.URI,
+ "ignored",
+ "credential",
+ "catalog:secret",
+ OAuth2Properties.OAUTH2_SERVER_URI,
+ oauth2ServerUri));
+
+ Assertions.assertThat(catalog.tableExists(TableIdentifier.of("ns", "table"))).isFalse();
+
+ // no token or credential for catalog token exchange
+ Mockito.verify(adapter)
+ .execute(
+ eq(HTTPMethod.POST),
+ eq(oauth2ServerUri),
+ any(),
+ any(),
+ eq(OAuthTokenResponse.class),
+ eq(emptyHeaders),
+ any());
+ // no token or credential for config
+ Mockito.verify(adapter)
+ .execute(
+ eq(HTTPMethod.GET),
+ eq("v1/config"),
+ any(),
+ any(),
+ eq(ConfigResponse.class),
+ eq(catalogHeaders),
+ any());
+ // use the catalog token for all interactions
+ Mockito.verify(adapter)
+ .execute(
+ eq(HTTPMethod.GET),
+ eq("v1/namespaces/ns/tables/table"),
+ any(),
+ any(),
+ eq(LoadTableResponse.class),
+ eq(catalogHeaders),
+ any());
+ }
+
+ @ParameterizedTest
+ @ValueSource(strings = {"v1/oauth/tokens", "https://auth-server.com/token"})
+ public void testCatalogBearerTokenWithClientCredential(String oauth2ServerUri) {
Map<String, String> contextHeaders =
ImmutableMap.of("Authorization", "Bearer client-credentials-token:sub=user");
Map<String, String> catalogHeaders = ImmutableMap.of("Authorization", "Bearer bearer-token");
@@ -417,7 +472,14 @@ public class TestRESTCatalog extends CatalogTests<RESTCatalog> {
RESTCatalog catalog = new RESTCatalog(context, (config) -> adapter);
catalog.initialize(
- "prod", ImmutableMap.of(CatalogProperties.URI, "ignored", "token", "bearer-token"));
+ "prod",
+ ImmutableMap.of(
+ CatalogProperties.URI,
+ "ignored",
+ "token",
+ "bearer-token",
+ OAuth2Properties.OAUTH2_SERVER_URI,
+ oauth2ServerUri));
Assertions.assertThat(catalog.tableExists(TableIdentifier.of("ns", "table"))).isFalse();
@@ -435,7 +497,7 @@ public class TestRESTCatalog extends CatalogTests<RESTCatalog> {
Mockito.verify(adapter)
.execute(
eq(HTTPMethod.POST),
- eq("v1/oauth/tokens"),
+ eq(oauth2ServerUri),
any(),
any(),
eq(OAuthTokenResponse.class),
@@ -453,8 +515,9 @@ public class TestRESTCatalog extends CatalogTests<RESTCatalog> {
any());
}
- @Test
- public void testCatalogCredentialWithClientCredential() {
+ @ParameterizedTest
+ @ValueSource(strings = {"v1/oauth/tokens", "https://auth-server.com/token"})
+ public void testCatalogCredentialWithClientCredential(String oauth2ServerUri) {
Map<String, String> emptyHeaders = ImmutableMap.of();
Map<String, String> contextHeaders =
ImmutableMap.of("Authorization", "Bearer client-credentials-token:sub=user");
@@ -472,7 +535,14 @@ public class TestRESTCatalog extends CatalogTests<RESTCatalog> {
RESTCatalog catalog = new RESTCatalog(context, (config) -> adapter);
catalog.initialize(
- "prod", ImmutableMap.of(CatalogProperties.URI, "ignored", "credential", "catalog:secret"));
+ "prod",
+ ImmutableMap.of(
+ CatalogProperties.URI,
+ "ignored",
+ "credential",
+ "catalog:secret",
+ OAuth2Properties.OAUTH2_SERVER_URI,
+ oauth2ServerUri));
Assertions.assertThat(catalog.tableExists(TableIdentifier.of("ns", "table"))).isFalse();
@@ -480,7 +550,7 @@ public class TestRESTCatalog extends CatalogTests<RESTCatalog> {
Mockito.verify(adapter)
.execute(
eq(HTTPMethod.POST),
- eq("v1/oauth/tokens"),
+ eq(oauth2ServerUri),
any(),
any(),
eq(OAuthTokenResponse.class),
@@ -500,7 +570,7 @@ public class TestRESTCatalog extends CatalogTests<RESTCatalog> {
Mockito.verify(adapter)
.execute(
eq(HTTPMethod.POST),
- eq("v1/oauth/tokens"),
+ eq(oauth2ServerUri),
any(),
any(),
eq(OAuthTokenResponse.class),
@@ -518,8 +588,9 @@ public class TestRESTCatalog extends CatalogTests<RESTCatalog> {
any());
}
- @Test
- public void testCatalogBearerTokenAndCredentialWithClientCredential() {
+ @ParameterizedTest
+ @ValueSource(strings = {"v1/oauth/tokens", "https://auth-server.com/token"})
+ public void testCatalogBearerTokenAndCredentialWithClientCredential(String oauth2ServerUri) {
Map<String, String> contextHeaders =
ImmutableMap.of("Authorization", "Bearer client-credentials-token:sub=user");
Map<String, String> initHeaders = ImmutableMap.of("Authorization", "Bearer bearer-token");
@@ -544,7 +615,9 @@ public class TestRESTCatalog extends CatalogTests<RESTCatalog> {
"credential",
"catalog:secret",
"token",
- "bearer-token"));
+ "bearer-token",
+ OAuth2Properties.OAUTH2_SERVER_URI,
+ oauth2ServerUri));
Assertions.assertThat(catalog.tableExists(TableIdentifier.of("ns", "table"))).isFalse();
@@ -552,7 +625,7 @@ public class TestRESTCatalog extends CatalogTests<RESTCatalog> {
Mockito.verify(adapter)
.execute(
eq(HTTPMethod.POST),
- eq("v1/oauth/tokens"),
+ eq(oauth2ServerUri),
any(),
any(),
eq(OAuthTokenResponse.class),
@@ -572,7 +645,7 @@ public class TestRESTCatalog extends CatalogTests<RESTCatalog> {
Mockito.verify(adapter)
.execute(
eq(HTTPMethod.POST),
- eq("v1/oauth/tokens"),
+ eq(oauth2ServerUri),
any(),
any(),
eq(OAuthTokenResponse.class),
@@ -590,8 +663,9 @@ public class TestRESTCatalog extends CatalogTests<RESTCatalog> {
any());
}
- @Test
- public void testClientBearerToken() {
+ @ParameterizedTest
+ @ValueSource(strings = {"v1/oauth/tokens", "https://auth-server.com/token"})
+ public void testClientBearerToken(String oauth2ServerUri) {
testClientAuth(
"bearer-token",
ImmutableMap.of(
@@ -602,11 +676,13 @@ public class TestRESTCatalog extends CatalogTests<RESTCatalog> {
"urn:ietf:params:oauth:token-type:jwt", "jwt-token",
"urn:ietf:params:oauth:token-type:saml2", "saml2-token",
"urn:ietf:params:oauth:token-type:saml1", "saml1-token"),
- ImmutableMap.of("Authorization", "Bearer client-bearer-token"));
+ ImmutableMap.of("Authorization", "Bearer client-bearer-token"),
+ oauth2ServerUri);
}
- @Test
- public void testClientCredential() {
+ @ParameterizedTest
+ @ValueSource(strings = {"v1/oauth/tokens", "https://auth-server.com/token"})
+ public void testClientCredential(String oauth2ServerUri) {
testClientAuth(
"bearer-token",
ImmutableMap.of(
@@ -616,11 +692,13 @@ public class TestRESTCatalog extends CatalogTests<RESTCatalog> {
"urn:ietf:params:oauth:token-type:jwt", "jwt-token",
"urn:ietf:params:oauth:token-type:saml2", "saml2-token",
"urn:ietf:params:oauth:token-type:saml1", "saml1-token"),
- ImmutableMap.of("Authorization", "Bearer client-credentials-token:sub=user"));
+ ImmutableMap.of("Authorization", "Bearer client-credentials-token:sub=user"),
+ oauth2ServerUri);
}
- @Test
- public void testClientIDToken() {
+ @ParameterizedTest
+ @ValueSource(strings = {"v1/oauth/tokens", "https://auth-server.com/token"})
+ public void testClientIDToken(String oauth2ServerUri) {
testClientAuth(
"bearer-token",
ImmutableMap.of(
@@ -630,11 +708,13 @@ public class TestRESTCatalog extends CatalogTests<RESTCatalog> {
"urn:ietf:params:oauth:token-type:saml2", "saml2-token",
"urn:ietf:params:oauth:token-type:saml1", "saml1-token"),
ImmutableMap.of(
- "Authorization", "Bearer token-exchange-token:sub=id-token,act=bearer-token"));
+ "Authorization", "Bearer token-exchange-token:sub=id-token,act=bearer-token"),
+ oauth2ServerUri);
}
- @Test
- public void testClientAccessToken() {
+ @ParameterizedTest
+ @ValueSource(strings = {"v1/oauth/tokens", "https://auth-server.com/token"})
+ public void testClientAccessToken(String oauth2ServerUri) {
testClientAuth(
"bearer-token",
ImmutableMap.of(
@@ -643,11 +723,13 @@ public class TestRESTCatalog extends CatalogTests<RESTCatalog> {
"urn:ietf:params:oauth:token-type:saml2", "saml2-token",
"urn:ietf:params:oauth:token-type:saml1", "saml1-token"),
ImmutableMap.of(
- "Authorization", "Bearer token-exchange-token:sub=access-token,act=bearer-token"));
+ "Authorization", "Bearer token-exchange-token:sub=access-token,act=bearer-token"),
+ oauth2ServerUri);
}
- @Test
- public void testClientJWTToken() {
+ @ParameterizedTest
+ @ValueSource(strings = {"v1/oauth/tokens", "https://auth-server.com/token"})
+ public void testClientJWTToken(String oauth2ServerUri) {
testClientAuth(
"bearer-token",
ImmutableMap.of(
@@ -655,31 +737,39 @@ public class TestRESTCatalog extends CatalogTests<RESTCatalog> {
"urn:ietf:params:oauth:token-type:saml2", "saml2-token",
"urn:ietf:params:oauth:token-type:saml1", "saml1-token"),
ImmutableMap.of(
- "Authorization", "Bearer token-exchange-token:sub=jwt-token,act=bearer-token"));
+ "Authorization", "Bearer token-exchange-token:sub=jwt-token,act=bearer-token"),
+ oauth2ServerUri);
}
- @Test
- public void testClientSAML2Token() {
+ @ParameterizedTest
+ @ValueSource(strings = {"v1/oauth/tokens", "https://auth-server.com/token"})
+ public void testClientSAML2Token(String oauth2ServerUri) {
testClientAuth(
"bearer-token",
ImmutableMap.of(
"urn:ietf:params:oauth:token-type:saml2", "saml2-token",
"urn:ietf:params:oauth:token-type:saml1", "saml1-token"),
ImmutableMap.of(
- "Authorization", "Bearer token-exchange-token:sub=saml2-token,act=bearer-token"));
+ "Authorization", "Bearer token-exchange-token:sub=saml2-token,act=bearer-token"),
+ oauth2ServerUri);
}
- @Test
- public void testClientSAML1Token() {
+ @ParameterizedTest
+ @ValueSource(strings = {"v1/oauth/tokens", "https://auth-server.com/token"})
+ public void testClientSAML1Token(String oauth2ServerUri) {
testClientAuth(
"bearer-token",
ImmutableMap.of("urn:ietf:params:oauth:token-type:saml1", "saml1-token"),
ImmutableMap.of(
- "Authorization", "Bearer token-exchange-token:sub=saml1-token,act=bearer-token"));
+ "Authorization", "Bearer token-exchange-token:sub=saml1-token,act=bearer-token"),
+ oauth2ServerUri);
}
private void testClientAuth(
- String catalogToken, Map<String, String> credentials, Map<String, String> expectedHeaders) {
+ String catalogToken,
+ Map<String, String> credentials,
+ Map<String, String> expectedHeaders,
+ String oauth2ServerUri) {
Map<String, String> catalogHeaders = ImmutableMap.of("Authorization", "Bearer " + catalogToken);
RESTCatalogAdapter adapter = Mockito.spy(new RESTCatalogAdapter(backendCatalog));
@@ -690,7 +780,14 @@ public class TestRESTCatalog extends CatalogTests<RESTCatalog> {
RESTCatalog catalog = new RESTCatalog(context, (config) -> adapter);
catalog.initialize(
- "prod", ImmutableMap.of(CatalogProperties.URI, "ignored", "token", catalogToken));
+ "prod",
+ ImmutableMap.of(
+ CatalogProperties.URI,
+ "ignored",
+ "token",
+ catalogToken,
+ OAuth2Properties.OAUTH2_SERVER_URI,
+ oauth2ServerUri));
Assertions.assertThat(catalog.tableExists(TableIdentifier.of("ns", "table"))).isFalse();
@@ -710,7 +807,7 @@ public class TestRESTCatalog extends CatalogTests<RESTCatalog> {
Mockito.verify(adapter)
.execute(
eq(HTTPMethod.POST),
- eq("v1/oauth/tokens"),
+ eq(oauth2ServerUri),
any(),
any(),
eq(OAuthTokenResponse.class),
@@ -729,18 +826,21 @@ public class TestRESTCatalog extends CatalogTests<RESTCatalog> {
any());
}
- @Test
- public void testTableBearerToken() {
+ @ParameterizedTest
+ @ValueSource(strings = {"v1/oauth/tokens", "https://auth-server.com/token"})
+ public void testTableBearerToken(String oauth2ServerUri) {
testTableAuth(
"catalog",
ImmutableMap.of("urn:ietf:params:oauth:token-type:id_token", "id-token"),
ImmutableMap.of("token", "table-bearer-token"),
ImmutableMap.of("Authorization", "Bearer token-exchange-token:sub=id-token,act=catalog"),
- ImmutableMap.of("Authorization", "Bearer table-bearer-token"));
+ ImmutableMap.of("Authorization", "Bearer table-bearer-token"),
+ oauth2ServerUri);
}
- @Test
- public void testTableIDToken() {
+ @ParameterizedTest
+ @ValueSource(strings = {"v1/oauth/tokens", "https://auth-server.com/token"})
+ public void testTableIDToken(String oauth2ServerUri) {
testTableAuth(
"catalog",
ImmutableMap.of("urn:ietf:params:oauth:token-type:id_token", "id-token"),
@@ -748,17 +848,20 @@ public class TestRESTCatalog extends CatalogTests<RESTCatalog> {
ImmutableMap.of("Authorization", "Bearer token-exchange-token:sub=id-token,act=catalog"),
ImmutableMap.of(
"Authorization",
- "Bearer token-exchange-token:sub=table-id-token,act=token-exchange-token:sub=id-token,act=catalog"));
+ "Bearer token-exchange-token:sub=table-id-token,act=token-exchange-token:sub=id-token,act=catalog"),
+ oauth2ServerUri);
}
- @Test
- public void testTableCredential() {
+ @ParameterizedTest
+ @ValueSource(strings = {"v1/oauth/tokens", "https://auth-server.com/token"})
+ public void testTableCredential(String oauth2ServerUri) {
testTableAuth(
"catalog",
ImmutableMap.of("urn:ietf:params:oauth:token-type:id_token", "id-token"),
ImmutableMap.of("credential", "table-user:secret"),
ImmutableMap.of("Authorization", "Bearer token-exchange-token:sub=id-token,act=catalog"),
- ImmutableMap.of("Authorization", "Bearer client-credentials-token:sub=table-user"));
+ ImmutableMap.of("Authorization", "Bearer client-credentials-token:sub=table-user"),
+ oauth2ServerUri);
}
@Test
@@ -914,7 +1017,6 @@ public class TestRESTCatalog extends CatalogTests<RESTCatalog> {
String branch = "divergedBranch";
table.manageSnapshots().createBranch(branch, table.currentSnapshot().snapshotId()).commit();
- // branch and main are diverged now
table
.newFastAppend()
.appendFile(
@@ -1095,12 +1197,14 @@ public class TestRESTCatalog extends CatalogTests<RESTCatalog> {
assertThat(refsTables.history()).hasSize(numSnapshots);
}
+ @SuppressWarnings("MethodLength")
public void testTableAuth(
String catalogToken,
Map<String, String> credentials,
Map<String, String> tableConfig,
Map<String, String> expectedContextHeaders,
- Map<String, String> expectedTableHeaders) {
+ Map<String, String> expectedTableHeaders,
+ String oauth2ServerUri) {
TableIdentifier ident = TableIdentifier.of("ns", "table");
Map<String, String> catalogHeaders = ImmutableMap.of("Authorization", "Bearer " + catalogToken);
@@ -1145,7 +1249,14 @@ public class TestRESTCatalog extends CatalogTests<RESTCatalog> {
RESTCatalog catalog = new RESTCatalog(context, (config) -> adapter);
catalog.initialize(
- "prod", ImmutableMap.of(CatalogProperties.URI, "ignored", "token", catalogToken));
+ "prod",
+ ImmutableMap.of(
+ CatalogProperties.URI,
+ "ignored",
+ "token",
+ catalogToken,
+ OAuth2Properties.OAUTH2_SERVER_URI,
+ oauth2ServerUri));
Schema expectedSchema =
new Schema(
@@ -1181,7 +1292,7 @@ public class TestRESTCatalog extends CatalogTests<RESTCatalog> {
Mockito.verify(adapter)
.execute(
eq(HTTPMethod.POST),
- eq("v1/oauth/tokens"),
+ eq(oauth2ServerUri),
any(),
any(),
eq(OAuthTokenResponse.class),
@@ -1205,7 +1316,7 @@ public class TestRESTCatalog extends CatalogTests<RESTCatalog> {
Mockito.verify(adapter, times(1))
.execute(
eq(HTTPMethod.POST),
- eq("v1/oauth/tokens"),
+ eq(oauth2ServerUri),
any(),
any(),
eq(OAuthTokenResponse.class),
@@ -1247,8 +1358,9 @@ public class TestRESTCatalog extends CatalogTests<RESTCatalog> {
any());
}
- @Test
- public void testCatalogTokenRefresh() {
+ @ParameterizedTest
+ @ValueSource(strings = {"v1/oauth/tokens", "https://auth-server.com/token"})
+ public void testCatalogTokenRefresh(String oauth2ServerUri) {
Map<String, String> emptyHeaders = ImmutableMap.of();
Map<String, String> catalogHeaders =
ImmutableMap.of("Authorization", "Bearer client-credentials-token:sub=catalog");
@@ -1271,7 +1383,7 @@ public class TestRESTCatalog extends CatalogTests<RESTCatalog> {
.when(adapter)
.execute(
eq(HTTPMethod.POST),
- eq("v1/oauth/tokens"),
+ eq(oauth2ServerUri),
any(),
any(),
eq(OAuthTokenResponse.class),
@@ -1285,7 +1397,14 @@ public class TestRESTCatalog extends CatalogTests<RESTCatalog> {
RESTCatalog catalog = new RESTCatalog(context, (config) -> adapter);
catalog.initialize(
- "prod", ImmutableMap.of(CatalogProperties.URI, "ignored", "credential", "catalog:secret"));
+ "prod",
+ ImmutableMap.of(
+ CatalogProperties.URI,
+ "ignored",
+ "credential",
+ "catalog:secret",
+ OAuth2Properties.OAUTH2_SERVER_URI,
+ oauth2ServerUri));
Awaitility.await()
.atMost(5, TimeUnit.SECONDS)
@@ -1295,7 +1414,7 @@ public class TestRESTCatalog extends CatalogTests<RESTCatalog> {
Mockito.verify(adapter)
.execute(
eq(HTTPMethod.POST),
- eq("v1/oauth/tokens"),
+ eq(oauth2ServerUri),
any(),
any(),
eq(OAuthTokenResponse.class),
@@ -1323,7 +1442,7 @@ public class TestRESTCatalog extends CatalogTests<RESTCatalog> {
Mockito.verify(adapter)
.execute(
eq(HTTPMethod.POST),
- eq("v1/oauth/tokens"),
+ eq(oauth2ServerUri),
any(),
Mockito.argThat(firstRefreshRequest::equals),
eq(OAuthTokenResponse.class),
@@ -1345,7 +1464,7 @@ public class TestRESTCatalog extends CatalogTests<RESTCatalog> {
Mockito.verify(adapter)
.execute(
eq(HTTPMethod.POST),
- eq("v1/oauth/tokens"),
+ eq(oauth2ServerUri),
any(),
Mockito.argThat(secondRefreshRequest::equals),
eq(OAuthTokenResponse.class),
@@ -1354,8 +1473,9 @@ public class TestRESTCatalog extends CatalogTests<RESTCatalog> {
});
}
- @Test
- public void testCatalogRefreshedTokenIsUsed() {
+ @ParameterizedTest
+ @ValueSource(strings = {"v1/oauth/tokens", "https://auth-server.com/token"})
+ public void testCatalogRefreshedTokenIsUsed(String oauth2ServerUri) {
Map<String, String> emptyHeaders = ImmutableMap.of();
Map<String, String> catalogHeaders =
ImmutableMap.of("Authorization", "Bearer client-credentials-token:sub=catalog");
@@ -1378,7 +1498,7 @@ public class TestRESTCatalog extends CatalogTests<RESTCatalog> {
.when(adapter)
.execute(
eq(HTTPMethod.POST),
- eq("v1/oauth/tokens"),
+ eq(oauth2ServerUri),
any(),
any(),
eq(OAuthTokenResponse.class),
@@ -1392,7 +1512,14 @@ public class TestRESTCatalog extends CatalogTests<RESTCatalog> {
RESTCatalog catalog = new RESTCatalog(context, (config) -> adapter);
catalog.initialize(
- "prod", ImmutableMap.of(CatalogProperties.URI, "ignored", "credential", "catalog:secret"));
+ "prod",
+ ImmutableMap.of(
+ CatalogProperties.URI,
+ "ignored",
+ "credential",
+ "catalog:secret",
+ OAuth2Properties.OAUTH2_SERVER_URI,
+ oauth2ServerUri));
Awaitility.await()
.atMost(5, TimeUnit.SECONDS)
@@ -1406,7 +1533,7 @@ public class TestRESTCatalog extends CatalogTests<RESTCatalog> {
Mockito.verify(adapter)
.execute(
eq(HTTPMethod.POST),
- eq("v1/oauth/tokens"),
+ eq(oauth2ServerUri),
any(),
any(),
eq(OAuthTokenResponse.class),
@@ -1434,7 +1561,7 @@ public class TestRESTCatalog extends CatalogTests<RESTCatalog> {
Mockito.verify(adapter)
.execute(
eq(HTTPMethod.POST),
- eq("v1/oauth/tokens"),
+ eq(oauth2ServerUri),
any(),
Mockito.argThat(firstRefreshRequest::equals),
eq(OAuthTokenResponse.class),
@@ -1531,8 +1658,9 @@ public class TestRESTCatalog extends CatalogTests<RESTCatalog> {
catalog.initialize("prod", ImmutableMap.of(CatalogProperties.URI, "ignored", "token", token));
}
- @Test
- public void testCatalogExpiredBearerTokenIsRefreshedWithCredential() {
+ @ParameterizedTest
+ @ValueSource(strings = {"v1/oauth/tokens", "https://auth-server.com/token"})
+ public void testCatalogExpiredBearerTokenIsRefreshedWithCredential(String oauth2ServerUri) {
// expires at epoch second = 1
String token =
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjF9.gQADTbdEv-rpDWKSkGLbmafyB5UUjTdm9B_1izpuZ6E";
@@ -1551,7 +1679,14 @@ public class TestRESTCatalog extends CatalogTests<RESTCatalog> {
RESTCatalog catalog = new RESTCatalog(context, (config) -> adapter);
// the init token at the catalog level is a valid token
catalog.initialize(
- "prod", ImmutableMap.of(CatalogProperties.URI, "ignored", "credential", credential));
+ "prod",
+ ImmutableMap.of(
+ CatalogProperties.URI,
+ "ignored",
+ "credential",
+ credential,
+ OAuth2Properties.OAUTH2_SERVER_URI,
+ oauth2ServerUri));
Assertions.assertThat(catalog.tableExists(TableIdentifier.of("ns", "table"))).isFalse();
@@ -1559,7 +1694,7 @@ public class TestRESTCatalog extends CatalogTests<RESTCatalog> {
Mockito.verify(adapter)
.execute(
eq(HTTPMethod.POST),
- eq("v1/oauth/tokens"),
+ eq(oauth2ServerUri),
any(),
any(),
eq(OAuthTokenResponse.class),
@@ -1585,7 +1720,7 @@ public class TestRESTCatalog extends CatalogTests<RESTCatalog> {
Mockito.verify(adapter)
.execute(
eq(HTTPMethod.POST),
- eq("v1/oauth/tokens"),
+ eq(oauth2ServerUri),
any(),
Mockito.argThat(firstRefreshRequest::equals),
eq(OAuthTokenResponse.class),
@@ -1602,7 +1737,7 @@ public class TestRESTCatalog extends CatalogTests<RESTCatalog> {
Mockito.verify(adapter)
.execute(
eq(HTTPMethod.POST),
- eq("v1/oauth/tokens"),
+ eq(oauth2ServerUri),
any(),
Mockito.argThat(secondRefreshRequest::equals),
eq(OAuthTokenResponse.class),
@@ -1662,8 +1797,9 @@ public class TestRESTCatalog extends CatalogTests<RESTCatalog> {
any());
}
- @Test
- public void testCatalogTokenRefreshFailsAndUsesCredentialForRefresh() {
+ @ParameterizedTest
+ @ValueSource(strings = {"v1/oauth/tokens", "https://auth-server.com/token"})
+ public void testCatalogTokenRefreshFailsAndUsesCredentialForRefresh(String oauth2ServerUri) {
Map<String, String> emptyHeaders = ImmutableMap.of();
Map<String, String> catalogHeaders =
ImmutableMap.of("Authorization", "Bearer client-credentials-token:sub=catalog");
@@ -1689,7 +1825,7 @@ public class TestRESTCatalog extends CatalogTests<RESTCatalog> {
.when(adapter)
.execute(
eq(HTTPMethod.POST),
- eq("v1/oauth/tokens"),
+ eq(oauth2ServerUri),
any(),
any(),
eq(OAuthTokenResponse.class),
@@ -1708,7 +1844,7 @@ public class TestRESTCatalog extends CatalogTests<RESTCatalog> {
.when(adapter)
.execute(
eq(HTTPMethod.POST),
- eq("v1/oauth/tokens"),
+ eq(oauth2ServerUri),
any(),
Mockito.argThat(firstRefreshRequest::equals),
eq(OAuthTokenResponse.class),
@@ -1725,7 +1861,14 @@ public class TestRESTCatalog extends CatalogTests<RESTCatalog> {
RESTCatalog catalog = new RESTCatalog(context, (config) -> adapter);
catalog.initialize(
- "prod", ImmutableMap.of(CatalogProperties.URI, "ignored", "credential", credential));
+ "prod",
+ ImmutableMap.of(
+ CatalogProperties.URI,
+ "ignored",
+ "credential",
+ credential,
+ OAuth2Properties.OAUTH2_SERVER_URI,
+ oauth2ServerUri));
Awaitility.await()
.atMost(5, TimeUnit.SECONDS)
@@ -1739,7 +1882,7 @@ public class TestRESTCatalog extends CatalogTests<RESTCatalog> {
Mockito.verify(adapter)
.execute(
eq(HTTPMethod.POST),
- eq("v1/oauth/tokens"),
+ eq(oauth2ServerUri),
any(),
any(),
eq(OAuthTokenResponse.class),
@@ -1762,7 +1905,7 @@ public class TestRESTCatalog extends CatalogTests<RESTCatalog> {
Mockito.verify(adapter, times(2))
.execute(
eq(HTTPMethod.POST),
- eq("v1/oauth/tokens"),
+ eq(oauth2ServerUri),
any(),
Mockito.argThat(firstRefreshRequest::equals),
eq(OAuthTokenResponse.class),
@@ -1774,7 +1917,7 @@ public class TestRESTCatalog extends CatalogTests<RESTCatalog> {
Mockito.verify(adapter)
.execute(
eq(HTTPMethod.POST),
- eq("v1/oauth/tokens"),
+ eq(oauth2ServerUri),
any(),
Mockito.argThat(firstRefreshRequest::equals),
eq(OAuthTokenResponse.class),
@@ -1798,8 +1941,9 @@ public class TestRESTCatalog extends CatalogTests<RESTCatalog> {
});
}
- @Test
- public void testCatalogWithCustomTokenScope() {
+ @ParameterizedTest
+ @ValueSource(strings = {"v1/oauth/tokens", "https://auth-server.com/token"})
+ public void testCatalogWithCustomTokenScope(String oauth2ServerUri) {
Map<String, String> emptyHeaders = ImmutableMap.of();
Map<String, String> catalogHeaders =
ImmutableMap.of("Authorization", "Bearer client-credentials-token:sub=catalog");
@@ -1822,7 +1966,7 @@ public class TestRESTCatalog extends CatalogTests<RESTCatalog> {
.when(adapter)
.execute(
eq(HTTPMethod.POST),
- eq("v1/oauth/tokens"),
+ eq(oauth2ServerUri),
any(),
any(),
eq(OAuthTokenResponse.class),
@@ -1844,7 +1988,9 @@ public class TestRESTCatalog extends CatalogTests<RESTCatalog> {
"credential",
"catalog:secret",
OAuth2Properties.SCOPE,
- scope));
+ scope,
+ OAuth2Properties.OAUTH2_SERVER_URI,
+ oauth2ServerUri));
Awaitility.await()
.atMost(5, TimeUnit.SECONDS)
@@ -1854,7 +2000,7 @@ public class TestRESTCatalog extends CatalogTests<RESTCatalog> {
Mockito.verify(adapter)
.execute(
eq(HTTPMethod.POST),
- eq("v1/oauth/tokens"),
+ eq(oauth2ServerUri),
any(),
any(),
eq(OAuthTokenResponse.class),
@@ -1882,7 +2028,7 @@ public class TestRESTCatalog extends CatalogTests<RESTCatalog> {
Mockito.verify(adapter)
.execute(
eq(HTTPMethod.POST),
- eq("v1/oauth/tokens"),
+ eq(oauth2ServerUri),
any(),
Mockito.argThat(firstRefreshRequest::equals),
eq(OAuthTokenResponse.class),
@@ -1891,8 +2037,9 @@ public class TestRESTCatalog extends CatalogTests<RESTCatalog> {
});
}
- @Test
- public void testCatalogTokenRefreshDisabledWithToken() {
+ @ParameterizedTest
+ @ValueSource(strings = {"v1/oauth/tokens", "https://auth-server.com/token"})
+ public void testCatalogTokenRefreshDisabledWithToken(String oauth2ServerUri) {
String token = "some-token";
Map<String, String> catalogHeaders = ImmutableMap.of("Authorization", "Bearer " + token);
@@ -1914,7 +2061,7 @@ public class TestRESTCatalog extends CatalogTests<RESTCatalog> {
.when(adapter)
.execute(
eq(HTTPMethod.POST),
- eq("v1/oauth/tokens"),
+ eq(oauth2ServerUri),
any(),
any(),
eq(OAuthTokenResponse.class),
@@ -1935,7 +2082,9 @@ public class TestRESTCatalog extends CatalogTests<RESTCatalog> {
OAuth2Properties.TOKEN,
token,
OAuth2Properties.TOKEN_REFRESH_ENABLED,
- "false"));
+ "false",
+ OAuth2Properties.OAUTH2_SERVER_URI,
+ oauth2ServerUri));
Mockito.verify(adapter)
.execute(
@@ -1948,8 +2097,9 @@ public class TestRESTCatalog extends CatalogTests<RESTCatalog> {
any());
}
- @Test
- public void testCatalogTokenRefreshDisabledWithCredential() {
+ @ParameterizedTest
+ @ValueSource(strings = {"v1/oauth/tokens", "https://auth-server.com/token"})
+ public void testCatalogTokenRefreshDisabledWithCredential(String oauth2ServerUri) {
Map<String, String> catalogHeaders =
ImmutableMap.of("Authorization", "Bearer client-credentials-token:sub=catalog");
@@ -1968,7 +2118,9 @@ public class TestRESTCatalog extends CatalogTests<RESTCatalog> {
OAuth2Properties.CREDENTIAL,
"catalog:12345",
OAuth2Properties.TOKEN_REFRESH_ENABLED,
- "false"));
+ "false",
+ OAuth2Properties.OAUTH2_SERVER_URI,
+ oauth2ServerUri));
// fetch token from client credential
Map<String, String> fetchTokenFromCredential =
@@ -1984,7 +2136,7 @@ public class TestRESTCatalog extends CatalogTests<RESTCatalog> {
Mockito.verify(adapter)
.execute(
eq(HTTPMethod.POST),
- eq("v1/oauth/tokens"),
+ eq(oauth2ServerUri),
any(),
Mockito.argThat(fetchTokenFromCredential::equals),
eq(OAuthTokenResponse.class),