From 02451fdf8eab5ccaafa09c29267beb6b4ddee851 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Vav=C5=99=C3=ADk?= Date: Fri, 27 Sep 2024 22:17:08 +0200 Subject: [PATCH] Migrate 013-quarkus-oidc-restlcient to Keycloak Dev Svc and enable native tests --- 013-quarkus-oidc-restclient/pom.xml | 38 ++----- .../LookupAuthorizationPongClient.java | 25 ++--- .../src/main/resources/application.properties | 16 ++- .../qe/AbstractPingPongResourceTest.java | 27 +---- ...upAuthorizationRestPingPongResourceIT.java | 5 +- .../qe/containers/KeycloakTestResource.java | 100 ------------------ .../qe/secured/SecuredResourceTest.java | 5 +- .../src/test/resources/test-realm.json | 43 -------- 8 files changed, 35 insertions(+), 224 deletions(-) delete mode 100644 013-quarkus-oidc-restclient/src/test/java/io/quarkus/qe/containers/KeycloakTestResource.java delete mode 100644 013-quarkus-oidc-restclient/src/test/resources/test-realm.json diff --git a/013-quarkus-oidc-restclient/pom.xml b/013-quarkus-oidc-restclient/pom.xml index 66a0b07e..eedd28c6 100644 --- a/013-quarkus-oidc-restclient/pom.xml +++ b/013-quarkus-oidc-restclient/pom.xml @@ -10,11 +10,7 @@ io.quarkus - quarkus-resteasy-mutiny - - - io.quarkus - quarkus-resteasy-jackson + quarkus-rest-jackson io.quarkus @@ -26,24 +22,23 @@ io.quarkus - quarkus-resteasy-client-mutiny + quarkus-rest-client io.quarkus - quarkus-resteasy-client-oidc-filter + quarkus-rest-client-oidc-filter io.quarkus - quarkus-resteasy-client-oidc-token-propagation + quarkus-rest-client-oidc-token-propagation - org.keycloak - keycloak-authz-client + io.quarkus + quarkus-keycloak-admin-rest-client - org.testcontainers - testcontainers - test + io.quarkus + quarkus-test-keycloak-server org.awaitility @@ -69,23 +64,6 @@ - - - native - - - native - - - - false - true - - skip-tests-on-windows diff --git a/013-quarkus-oidc-restclient/src/main/java/io/quarkus/qe/ping/clients/LookupAuthorizationPongClient.java b/013-quarkus-oidc-restclient/src/main/java/io/quarkus/qe/ping/clients/LookupAuthorizationPongClient.java index af3f16ac..a8a18fa1 100644 --- a/013-quarkus-oidc-restclient/src/main/java/io/quarkus/qe/ping/clients/LookupAuthorizationPongClient.java +++ b/013-quarkus-oidc-restclient/src/main/java/io/quarkus/qe/ping/clients/LookupAuthorizationPongClient.java @@ -1,14 +1,11 @@ package io.quarkus.qe.ping.clients; -import java.util.Collections; - -import org.apache.http.impl.client.HttpClients; import org.eclipse.microprofile.config.Config; import org.eclipse.microprofile.config.ConfigProvider; import org.eclipse.microprofile.rest.client.annotation.ClientHeaderParam; import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; -import org.keycloak.authorization.client.AuthzClient; -import org.keycloak.authorization.client.Configuration; +import org.keycloak.admin.client.Keycloak; +import org.keycloak.admin.client.KeycloakBuilder; import io.quarkus.qe.model.Score; @@ -61,13 +58,17 @@ default String lookupAuth() { String clientId = config.getValue("quarkus.oidc.client-id", String.class); String clientSecret = config.getValue("quarkus.oidc.credentials.secret", String.class); - AuthzClient authzClient = AuthzClient.create(new Configuration( - authUrl, - realm, - clientId, - Collections.singletonMap("secret", clientSecret), - HttpClients.createDefault())); + Keycloak keycloak = KeycloakBuilder.builder() + .serverUrl(authUrl) + .realm(realm) + .clientId(clientId) + .clientSecret(clientSecret) + .grantType("password") + .username("alice") + .password("alice") + .build(); - return "Bearer " + authzClient.obtainAccessToken("test-user", "test-user").getToken(); + String keycloakToken = keycloak.tokenManager().getAccessToken().getToken(); + return "Bearer " + keycloakToken; } } diff --git a/013-quarkus-oidc-restclient/src/main/resources/application.properties b/013-quarkus-oidc-restclient/src/main/resources/application.properties index 65dc15a7..51cc2f2d 100644 --- a/013-quarkus-oidc-restclient/src/main/resources/application.properties +++ b/013-quarkus-oidc-restclient/src/main/resources/application.properties @@ -2,10 +2,6 @@ quarkus.http.port=8081 # Security -quarkus.oidc.auth-server-url=http://localhost:8180/auth/realms/test-realm -quarkus.oidc.client-id=test-application-client -quarkus.oidc.credentials.secret=test-application-client-secret - quarkus.http.auth.permission.unsecured.paths=/generate-token/* quarkus.http.auth.permission.unsecured.policy=permit @@ -15,16 +11,16 @@ org.eclipse.microprofile.rest.client.propagateHeaders=Authorization # OIDC Client Configuration quarkus.oidc-client.auth-server-url=${quarkus.oidc.auth-server-url} -quarkus.oidc-client.client-id=test-application-client -quarkus.oidc-client.credentials.secret=test-application-client-secret +quarkus.oidc-client.client-id=${quarkus.oidc.client-id} +quarkus.oidc-client.credentials.secret=${quarkus.oidc.credentials.secret} ## Normal User Password quarkus.oidc-client.test-user.auth-server-url=${quarkus.oidc.auth-server-url} -quarkus.oidc-client.test-user.client-id=test-application-client -quarkus.oidc-client.test-user.credentials.secret=test-application-client-secret +quarkus.oidc-client.test-user.client-id=${quarkus.oidc.client-id} +quarkus.oidc-client.test-user.credentials.secret=${quarkus.oidc.credentials.secret} quarkus.oidc-client.test-user.grant.type=password -quarkus.oidc-client.test-user.grant-options.password.username=test-user -quarkus.oidc-client.test-user.grant-options.password.password=test-user +quarkus.oidc-client.test-user.grant-options.password.username=alice +quarkus.oidc-client.test-user.grant-options.password.password=alice # RestClient io.quarkus.qe.ping.clients.PongClient/mp-rest/url=http://localhost:8081 diff --git a/013-quarkus-oidc-restclient/src/test/java/io/quarkus/qe/AbstractPingPongResourceTest.java b/013-quarkus-oidc-restclient/src/test/java/io/quarkus/qe/AbstractPingPongResourceTest.java index 7788f0bd..ba4258c1 100644 --- a/013-quarkus-oidc-restclient/src/test/java/io/quarkus/qe/AbstractPingPongResourceTest.java +++ b/013-quarkus-oidc-restclient/src/test/java/io/quarkus/qe/AbstractPingPongResourceTest.java @@ -1,46 +1,25 @@ package io.quarkus.qe; import static io.restassured.RestAssured.given; -import static io.restassured.config.HttpClientConfig.httpClientConfig; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.is; import java.util.UUID; import org.apache.http.HttpStatus; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.keycloak.authorization.client.AuthzClient; -import io.quarkus.qe.containers.KeycloakTestResource; import io.quarkus.qe.model.Score; -import io.quarkus.test.common.TestResourceScope; -import io.quarkus.test.common.WithTestResource; -import io.restassured.RestAssured; -import io.restassured.config.RestAssuredConfig; +import io.quarkus.test.keycloak.client.KeycloakTestClient; import io.restassured.http.ContentType; -@WithTestResource(value = KeycloakTestResource.class, scope = TestResourceScope.MATCHING_RESOURCES) public abstract class AbstractPingPongResourceTest { private static final String PING_ENDPOINT = "/%s-ping"; private static final String PONG_ENDPOINT = "/%s-pong"; - private static final String USER = "test-user"; private static final String WRONG_TOKEN = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - private static final String HTTP_SOCKET_TIMEOUT_PROPERTY = "http.socket.timeout"; - private static final String HTTP_CONNECTION_TIMEOUT_PROPERTY = "http.connection.timeout"; - private static final int TIMEOUT_IN_SECONDS = 1000; - - AuthzClient authzClient; - - @BeforeEach - public void setup() { - RestAssured.config = RestAssuredConfig.config() - .httpClient(httpClientConfig() - .setParam(HTTP_SOCKET_TIMEOUT_PROPERTY, TIMEOUT_IN_SECONDS) - .setParam(HTTP_CONNECTION_TIMEOUT_PROPERTY, TIMEOUT_IN_SECONDS)); - } + private KeycloakTestClient keycloakTestClient = new KeycloakTestClient(); @Test public void testPingUnauthorized() { @@ -152,6 +131,6 @@ protected String pongEndpoint() { } private String createToken() { - return authzClient.obtainAccessToken(USER, USER).getToken(); + return keycloakTestClient.getAccessToken("alice"); } } diff --git a/013-quarkus-oidc-restclient/src/test/java/io/quarkus/qe/NativeLookupAuthorizationRestPingPongResourceIT.java b/013-quarkus-oidc-restclient/src/test/java/io/quarkus/qe/NativeLookupAuthorizationRestPingPongResourceIT.java index 9cad4c5d..a47f7761 100644 --- a/013-quarkus-oidc-restclient/src/test/java/io/quarkus/qe/NativeLookupAuthorizationRestPingPongResourceIT.java +++ b/013-quarkus-oidc-restclient/src/test/java/io/quarkus/qe/NativeLookupAuthorizationRestPingPongResourceIT.java @@ -1,10 +1,7 @@ package io.quarkus.qe; -import org.junit.jupiter.api.Disabled; - import io.quarkus.test.junit.QuarkusIntegrationTest; -@Disabled("Annotation @ClientHeaderParam not working in Native. Reported by https://github.com/quarkusio/quarkus/issues/13660") @QuarkusIntegrationTest public class NativeLookupAuthorizationRestPingPongResourceIT extends LookupAuthorizationRestPingPongResourceTest { -} \ No newline at end of file +} diff --git a/013-quarkus-oidc-restclient/src/test/java/io/quarkus/qe/containers/KeycloakTestResource.java b/013-quarkus-oidc-restclient/src/test/java/io/quarkus/qe/containers/KeycloakTestResource.java deleted file mode 100644 index 627b5f87..00000000 --- a/013-quarkus-oidc-restclient/src/test/java/io/quarkus/qe/containers/KeycloakTestResource.java +++ /dev/null @@ -1,100 +0,0 @@ -package io.quarkus.qe.containers; - -import java.lang.reflect.Field; -import java.time.Duration; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; - -import org.apache.http.impl.client.HttpClients; -import org.keycloak.authorization.client.AuthzClient; -import org.keycloak.authorization.client.Configuration; -import org.testcontainers.containers.BindMode; -import org.testcontainers.containers.GenericContainer; -import org.testcontainers.containers.wait.strategy.Wait; - -import io.quarkus.test.common.QuarkusTestResourceLifecycleManager; - -public class KeycloakTestResource implements QuarkusTestResourceLifecycleManager { - - private static final String OIDC_AUTH_URL_PROPERTY = "quarkus.oidc.auth-server-url"; - private static final String OIDC_CLIENT_AUTH_URL_PROPERTY = "quarkus.oidc-client.auth-server-url"; - - private static final String USER = "admin"; - private static final String PASSWORD = "admin"; - private static final String REALM = "test-realm"; - private static final int PORT = 8080; - - private static final String REALM_FILE = "/tmp/realm.json"; - // TODO: update to newer Keycloak version, require code modifications - private static final String KEYCLOAK_IMAGE = "quay.io/keycloak/keycloak:11.0.3"; - - private GenericContainer container; - - @SuppressWarnings("resource") - @Override - public Map start() { - - container = new GenericContainer<>(KEYCLOAK_IMAGE) - .withEnv("KEYCLOAK_USER", USER) - .withEnv("KEYCLOAK_PASSWORD", PASSWORD) - .withEnv("KEYCLOAK_IMPORT", REALM_FILE) - .withClasspathResourceMapping("test-realm.json", REALM_FILE, BindMode.READ_ONLY) - .waitingFor(Wait.forHttp("/auth").withStartupTimeout(Duration.ofMinutes(5))); - container.addExposedPort(PORT); - container.start(); - - Map properties = new HashMap<>(); - properties.put(OIDC_AUTH_URL_PROPERTY, oidcAuthUrl()); - properties.put(OIDC_CLIENT_AUTH_URL_PROPERTY, oidcAuthUrl()); - - return properties; - } - - @Override - public void stop() { - Optional.ofNullable(container).ifPresent(GenericContainer::stop); - } - - @Override - public void inject(Object testInstance) { - Class c = testInstance.getClass(); - while (c != Object.class) { - for (Field f : c.getDeclaredFields()) { - if (f.getType().isAssignableFrom(AuthzClient.class)) { - setFieldValue(f, testInstance, initAuthzClient()); - } - } - c = c.getSuperclass(); - } - } - - private AuthzClient initAuthzClient() { - return AuthzClient.create(new Configuration( - keycloakUrl(), - REALM, - "test-application-client", - Collections.singletonMap("secret", "test-application-client-secret"), - HttpClients.createDefault())); - } - - private String keycloakUrl() { - return String.format("http://localhost:%s/auth", container.getMappedPort(PORT)); - } - - private String oidcAuthUrl() { - return String.format("%s/realms/%s", keycloakUrl(), REALM); - } - - private void setFieldValue(Field f, Object testInstance, Object value) { - try { - f.setAccessible(true); - f.set(testInstance, value); - return; - } catch (Exception e) { - throw new RuntimeException(e); - } - } - -} diff --git a/013-quarkus-oidc-restclient/src/test/java/io/quarkus/qe/secured/SecuredResourceTest.java b/013-quarkus-oidc-restclient/src/test/java/io/quarkus/qe/secured/SecuredResourceTest.java index 91a198ea..3180b987 100644 --- a/013-quarkus-oidc-restclient/src/test/java/io/quarkus/qe/secured/SecuredResourceTest.java +++ b/013-quarkus-oidc-restclient/src/test/java/io/quarkus/qe/secured/SecuredResourceTest.java @@ -7,6 +7,7 @@ import org.junit.jupiter.api.Test; import io.quarkus.test.junit.QuarkusTest; +import io.quarkus.test.keycloak.client.KeycloakTestClient; @QuarkusTest public class SecuredResourceTest { @@ -15,6 +16,8 @@ public class SecuredResourceTest { private static final String CLAIMS_FROM_BEANS_PATH = "/getClaimsFromBeans"; private static final String CLAIMS_FROM_TOKEN_PATH = "/getClaimsFromToken"; + private KeycloakTestClient keycloakClient = new KeycloakTestClient(); + @Test public void verifySecuredEndpointIsProtected() { given().get(SECURED_PATH + CLAIMS_FROM_BEANS_PATH) @@ -38,7 +41,7 @@ private String getClaimsFromToken() { } private String getClaimsInstancesFromPath(String path) { - String token = given().when().get("/generate-token/test-user").then().statusCode(200).extract().asString(); + String token = keycloakClient.getAccessToken("alice"); return given() .auth().preemptive().oauth2(token) diff --git a/013-quarkus-oidc-restclient/src/test/resources/test-realm.json b/013-quarkus-oidc-restclient/src/test/resources/test-realm.json deleted file mode 100644 index 9827607d..00000000 --- a/013-quarkus-oidc-restclient/src/test/resources/test-realm.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "realm": "test-realm", - "enabled": true, - "sslRequired": "none", - "roles": { - "realm": [ - { - "name": "test-user-role" - } - ] - }, - "users": [ - { - "username": "test-user", - "enabled": true, - "credentials": [ - { - "type": "password", - "value": "test-user" - } - ], - "realmRoles": [ - "test-user-role" - ] - } - ], - "clients": [ - { - "clientId": "test-application-client", - "enabled": true, - "protocol": "openid-connect", - "standardFlowEnabled": true, - "implicitFlowEnabled": false, - "directAccessGrantsEnabled": true, - "serviceAccountsEnabled": true, - "clientAuthenticatorType": "client-secret", - "secret": "test-application-client-secret", - "redirectUris": [ - "*" - ] - } - ] -}