Skip to content

Commit b1c7adf

Browse files
committed
Add integration tests with Keycloak
1 parent 78d5723 commit b1c7adf

31 files changed

+747
-201
lines changed

gradle/libs.versions.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ jakarta-validation-api = { module = "jakarta.validation:jakarta.validation-api",
7272
jakarta-ws-rs-api = { module = "jakarta.ws.rs:jakarta.ws.rs-api", version = "4.0.0" }
7373
javax-servlet-api = { module = "javax.servlet:javax.servlet-api", version = "4.0.1" }
7474
junit-bom = { module = "org.junit:junit-bom", version = "5.13.4" }
75+
keycloak-admin-client = { module = "org.keycloak:keycloak-admin-client", version = "26.0.6" }
7576
logback-classic = { module = "ch.qos.logback:logback-classic", version = "1.5.18" }
7677
micrometer-bom = { module = "io.micrometer:micrometer-bom", version = "1.15.3" }
7778
microprofile-fault-tolerance-api = { module = "org.eclipse.microprofile.fault-tolerance:microprofile-fault-tolerance-api", version = "4.1.2" }
@@ -94,6 +95,7 @@ spark35-sql-scala212 = { module = "org.apache.spark:spark-sql_2.12", version.ref
9495
swagger-annotations = { module = "io.swagger:swagger-annotations", version.ref = "swagger" }
9596
swagger-jaxrs = { module = "io.swagger:swagger-jaxrs", version.ref = "swagger" }
9697
testcontainers-bom = { module = "org.testcontainers:testcontainers-bom", version = "1.21.3" }
98+
testcontainers-keycloak = { module = "com.github.dasniko:testcontainers-keycloak", version = "3.8.0" }
9799
threeten-extra = { module = "org.threeten:threeten-extra", version = "1.8.0" }
98100

99101
[plugins]

integration-tests/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ dependencies {
2424
implementation(project(":polaris-api-management-model"))
2525
implementation(project(":polaris-api-catalog-service"))
2626

27+
implementation(libs.jakarta.annotation.api)
2728
implementation(libs.jakarta.ws.rs.api)
2829
implementation(libs.guava)
2930

integration-tests/src/main/java/org/apache/polaris/service/it/env/CatalogApi.java

Lines changed: 2 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
import com.google.common.base.Joiner;
2626
import jakarta.ws.rs.client.Client;
2727
import jakarta.ws.rs.client.Entity;
28-
import jakarta.ws.rs.core.MultivaluedHashMap;
2928
import jakarta.ws.rs.core.Response;
3029
import java.net.URI;
3130
import java.util.ArrayList;
@@ -42,38 +41,17 @@
4241
import org.apache.iceberg.rest.responses.ListNamespacesResponse;
4342
import org.apache.iceberg.rest.responses.ListTablesResponse;
4443
import org.apache.iceberg.rest.responses.LoadTableResponse;
45-
import org.apache.iceberg.rest.responses.OAuthTokenResponse;
4644

4745
/**
4846
* A simple, non-exhaustive set of helper methods for accessing the Iceberg REST API.
4947
*
50-
* @see PolarisClient#catalogApi(ClientCredentials)
48+
* @see PolarisClient#catalogApi(String)
5149
*/
52-
public class CatalogApi extends RestApi {
50+
public class CatalogApi extends PolarisRestApi {
5351
public CatalogApi(Client client, PolarisApiEndpoints endpoints, String authToken, URI uri) {
5452
super(client, endpoints, authToken, uri);
5553
}
5654

57-
public String obtainToken(ClientCredentials credentials) {
58-
try (Response response =
59-
request("v1/oauth/tokens")
60-
.post(
61-
Entity.form(
62-
new MultivaluedHashMap<>(
63-
Map.of(
64-
"grant_type",
65-
"client_credentials",
66-
"scope",
67-
"PRINCIPAL_ROLE:ALL",
68-
"client_id",
69-
credentials.clientId(),
70-
"client_secret",
71-
credentials.clientSecret()))))) {
72-
assertThat(response).returns(Response.Status.OK.getStatusCode(), Response::getStatus);
73-
return response.readEntity(OAuthTokenResponse.class).token();
74-
}
75-
}
76-
7755
public void createNamespace(String catalogName, String namespaceName) {
7856
try (Response response =
7957
request("v1/{cat}/namespaces", Map.of("cat", catalogName))

integration-tests/src/main/java/org/apache/polaris/service/it/env/ClientCredentials.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,21 @@
1818
*/
1919
package org.apache.polaris.service.it.env;
2020

21+
import org.apache.polaris.core.admin.model.PrincipalWithCredentialsCredentials;
2122
import org.apache.polaris.service.it.ext.PolarisIntegrationTestExtension;
2223

2324
/**
2425
* This class holds credentials for accessing the test Polaris Server. An instance of this class
2526
* representing an admin user is injected into test parameters by {@link
2627
* PolarisIntegrationTestExtension}.
2728
*/
28-
public record ClientCredentials(String clientId, String clientSecret) {}
29+
public record ClientCredentials(String clientId, String clientSecret) {
30+
31+
/**
32+
* Creates a {@link ClientCredentials} from an instance of the Admin API model {@link
33+
* PrincipalWithCredentialsCredentials}.
34+
*/
35+
public ClientCredentials(PrincipalWithCredentialsCredentials credentials) {
36+
this(credentials.getClientId(), credentials.getClientSecret());
37+
}
38+
}

integration-tests/src/main/java/org/apache/polaris/service/it/env/ClientPrincipal.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
*/
1919
package org.apache.polaris.service.it.env;
2020

21+
import org.apache.polaris.core.admin.model.PrincipalWithCredentials;
2122
import org.apache.polaris.service.it.ext.PolarisIntegrationTestExtension;
2223

2324
/**
@@ -27,4 +28,13 @@
2728
*
2829
* @see Server#adminCredentials()
2930
*/
30-
public record ClientPrincipal(String principalName, ClientCredentials credentials) {}
31+
public record ClientPrincipal(String principalName, ClientCredentials credentials) {
32+
33+
/**
34+
* Creates a {@link ClientPrincipal} from an instance of the Admin API model {@link
35+
* PrincipalWithCredentials}.
36+
*/
37+
public ClientPrincipal(PrincipalWithCredentials principal) {
38+
this(principal.getPrincipal().getName(), new ClientCredentials(principal.getCredentials()));
39+
}
40+
}

integration-tests/src/main/java/org/apache/polaris/service/it/env/GenericTableApi.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,9 @@
3838
/**
3939
* A simple, non-exhaustive set of helper methods for accessing the generic tables REST API
4040
*
41-
* @see PolarisClient#genericTableApi(ClientCredentials)
41+
* @see PolarisClient#genericTableApi(String)
4242
*/
43-
public class GenericTableApi extends RestApi {
43+
public class GenericTableApi extends PolarisRestApi {
4444
GenericTableApi(Client client, PolarisApiEndpoints endpoints, String authToken, URI uri) {
4545
super(client, endpoints, authToken, uri);
4646
}

integration-tests/src/main/java/org/apache/polaris/service/it/env/IcebergHelper.java

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,18 +22,15 @@
2222
import java.util.Map;
2323
import org.apache.iceberg.rest.RESTCatalog;
2424
import org.apache.iceberg.rest.auth.OAuth2Properties;
25-
import org.apache.polaris.core.admin.model.PrincipalWithCredentials;
2625

2726
public final class IcebergHelper {
2827
private IcebergHelper() {}
2928

3029
public static RESTCatalog restCatalog(
31-
PolarisClient client,
3230
PolarisApiEndpoints endpoints,
33-
PrincipalWithCredentials credentials,
3431
String catalog,
35-
Map<String, String> extraProperties) {
36-
String authToken = client.obtainToken(credentials);
32+
Map<String, String> extraProperties,
33+
String authToken) {
3734
RESTCatalog restCatalog = new RESTCatalog();
3835

3936
ImmutableMap.Builder<String, String> propertiesBuilder =

integration-tests/src/main/java/org/apache/polaris/service/it/env/IcebergTokenAccessManager.java

Lines changed: 0 additions & 43 deletions
This file was deleted.

integration-tests/src/main/java/org/apache/polaris/service/it/env/ManagementApi.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,9 @@
5151
/**
5252
* A simple, non-exhaustive set of helper methods for accessing the Polaris Management API.
5353
*
54-
* @see PolarisClient#managementApi(ClientCredentials)
54+
* @see PolarisClient#managementApi(String)
5555
*/
56-
public class ManagementApi extends RestApi {
56+
public class ManagementApi extends PolarisRestApi {
5757
public ManagementApi(Client client, PolarisApiEndpoints endpoints, String authToken, URI uri) {
5858
super(client, endpoints, authToken, uri);
5959
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.polaris.service.it.env;
20+
21+
import static org.assertj.core.api.Assertions.assertThat;
22+
23+
import jakarta.ws.rs.client.Client;
24+
import jakarta.ws.rs.client.Entity;
25+
import jakarta.ws.rs.core.MultivaluedHashMap;
26+
import jakarta.ws.rs.core.Response;
27+
import java.net.URI;
28+
import java.util.Map;
29+
import org.apache.iceberg.rest.responses.OAuthTokenResponse;
30+
31+
/**
32+
* A simple facade to an OAuth2 token endpoint. It works with both Polaris internal token endpoint
33+
* and with external identity providers.
34+
*/
35+
public class OAuth2Api extends RestApi {
36+
37+
private final String endpointPath;
38+
39+
public OAuth2Api(Client client, URI issuerUrl, String endpointPath) {
40+
super(client, issuerUrl);
41+
this.endpointPath = endpointPath;
42+
}
43+
44+
public String obtainAccessToken(ClientCredentials credentials, String scope) {
45+
return obtainAccessToken(
46+
Map.of(
47+
"grant_type",
48+
"client_credentials",
49+
"client_id",
50+
credentials.clientId(),
51+
"client_secret",
52+
credentials.clientSecret(),
53+
"scope",
54+
scope));
55+
}
56+
57+
public String obtainAccessToken(Map<String, String> requestBody) {
58+
try (Response response =
59+
request(endpointPath).post(Entity.form(new MultivaluedHashMap<>(requestBody)))) {
60+
assertThat(response).returns(Response.Status.OK.getStatusCode(), Response::getStatus);
61+
String token = response.readEntity(OAuthTokenResponse.class).token();
62+
return token;
63+
}
64+
}
65+
}

0 commit comments

Comments
 (0)