diff --git a/pom.xml b/pom.xml index 0f4d66a..6f9450a 100644 --- a/pom.xml +++ b/pom.xml @@ -29,12 +29,11 @@ 17 UTF-8 io.phasetwo.keycloak.events - 4.13.2 - 23.0.4 + 5.8.2 + 24.0.0 1.18.30 1.1.1 https://s01.oss.sonatype.org - true @@ -114,6 +113,16 @@ ${buildNumber} + + org.apache.maven.plugins + maven-surefire-plugin + 3.2.2 + + + ${keycloak.version} + + + @@ -203,11 +212,59 @@ - junit - junit + org.junit.jupiter + junit-jupiter + ${junit.version} + test + + + org.junit.jupiter + junit-jupiter-params ${junit.version} test + + org.testcontainers + junit-jupiter + 1.19.3 + test + + + com.github.dasniko + testcontainers-keycloak + 3.3.0 + test + + + org.jboss.shrinkwrap.resolver + shrinkwrap-resolver-impl-maven-archive + 3.2.1 + test + + + ch.qos.logback + logback-classic + 1.4.5 + test + + + org.hamcrest + hamcrest + 2.2 + test + + + org.apache.httpcomponents + httpmime + 4.5.13 + test + + + io.phasetwo.keycloak + keycloak-orgs + [0.61,) + test + org.keycloak keycloak-admin-client @@ -216,38 +273,4 @@ - - - test - - false - - - - org.keycloak - keycloak-testsuite-utils - ${keycloak.version} - test - - - io.phasetwo.keycloak - keycloak-orgs - [0.45,) - test - - - - - - org.apache.maven.plugins - maven-surefire-plugin - 3.0.0-M7 - - 1 - - - - - - diff --git a/src/main/java/io/phasetwo/keycloak/config/ConfigurationAware.java b/src/main/java/io/phasetwo/keycloak/config/ConfigurationAware.java index 8c17e07..5c4610d 100644 --- a/src/main/java/io/phasetwo/keycloak/config/ConfigurationAware.java +++ b/src/main/java/io/phasetwo/keycloak/config/ConfigurationAware.java @@ -1,6 +1,7 @@ package io.phasetwo.keycloak.config; import com.google.common.base.Joiner; +import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import java.util.List; import java.util.Map; @@ -13,8 +14,9 @@ public interface ConfigurationAware { String getId(); default List> getConfigurations(KeycloakSession session) { - return RealmAttributesConfigLoader.loadConfigurations( - session, getRealm(session).getName(), getId()) + RealmModel realm = getRealm(session); + if (realm == null) return ImmutableList.of(); + return RealmAttributesConfigLoader.loadConfigurations(session, realm.getName(), getId()) .stream() .map(config -> RealmAttributesConfigLoader.safeConvertToMap(config)) .collect(Collectors.toList()); diff --git a/src/main/java/io/phasetwo/keycloak/resources/CorsResource.java b/src/main/java/io/phasetwo/keycloak/resources/CorsResource.java index 27568f5..4f61def 100644 --- a/src/main/java/io/phasetwo/keycloak/resources/CorsResource.java +++ b/src/main/java/io/phasetwo/keycloak/resources/CorsResource.java @@ -6,7 +6,7 @@ import org.keycloak.http.HttpRequest; import org.keycloak.http.HttpResponse; import org.keycloak.models.KeycloakSession; -import org.keycloak.services.resources.Cors; +import org.keycloak.services.cors.Cors; import org.keycloak.services.resources.admin.AdminAuth; @JBossLog diff --git a/src/test/java/io/phasetwo/keycloak/KeycloakSuite.java b/src/test/java/io/phasetwo/keycloak/KeycloakSuite.java deleted file mode 100644 index f895c6f..0000000 --- a/src/test/java/io/phasetwo/keycloak/KeycloakSuite.java +++ /dev/null @@ -1,119 +0,0 @@ -package io.phasetwo.keycloak; - -import com.google.common.collect.ObjectArrays; -import com.google.common.io.MoreFiles; -import com.google.common.io.RecursiveDeleteOption; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.concurrent.atomic.AtomicBoolean; -import org.junit.rules.TestRule; -import org.junit.runner.Description; -import org.junit.runners.model.Statement; -import org.keycloak.OAuth2Constants; -import org.keycloak.admin.client.Keycloak; -import org.keycloak.admin.client.KeycloakBuilder; -import org.keycloak.testsuite.KeycloakServer; - -public class KeycloakSuite implements TestRule { - - public static final KeycloakSuite SERVER = new KeycloakSuite(); - private final AtomicBoolean initialized = new AtomicBoolean(); - private KeycloakServer keycloak; - private int port; - - @Override - public synchronized Statement apply(Statement base, Description description) { - if (!initialized.get()) { - init(); - initialized.set(true); - } - return base; - } - - public KeycloakServer getKeycloak() { - return this.keycloak; - } - - public int getPort() { - return this.port; - } - - public String getAuthUrl() { - return String.format("%sauth", getRootUrl()); - } - - public String getRootUrl() { - return String.format("http://127.0.0.1:%d/", getPort()); - } - - private static final String[] ARGS = {}; - - private static final String[] PROPS = { - "keycloak.bind.address=127.0.0.1", - "java.net.preferIPv4Stack=true", - "keycloak.connectionsJpa.url=jdbc:h2:file:./target/data/keycloak_4_x_master", - "keycloak.connectionsJpa.driver=org.h2.Driver", - "keycloak.connectionsJpa.driverDialect=org.hibernate.dialect.H2Dialect", - "keycloak.connectionsJpa.user=sa", - "keycloak.connectionsJpa.password=", - "profile=COMMUNITY", - "product.default-profile=COMMUNITY", - "keycloak.password.blacklists.path=./target/data/blacklists/", - "com.sun.net.ssl.checkRevocation=false", - "keycloak.product.name=keycloak", - "product.name=keycloak", - "keycloak.profile=preview", - "keycloak.profile.feature.account_api=enabled", - "keycloak.profile.feature.account2=enabled", - "keycloak.profile.feature.scripts=enabled" - }; - // "product.version=${keycloak.version}", - - private void setSystemProps(String[] props) { - for (String prop : props) { - String[] t = prop.split("="); - String val = t.length < 2 ? "" : t[1]; - System.setProperty(t[0], val); - } - } - - private void deleteDataDirs() throws IOException { - Path data = Paths.get("./target/data"); - if (Files.exists(data)) { - MoreFiles.deleteRecursively(data, RecursiveDeleteOption.ALLOW_INSECURE); - } - } - - private void init() { - port = Helpers.nextFreePort(8082, 10000); - String portProp = String.format("keycloak.port=%d", port); - String[] props = ObjectArrays.concat(PROPS, portProp); - setSystemProps(props); - try { - deleteDataDirs(); - keycloak = KeycloakServer.bootstrapKeycloakServer(ARGS); - } catch (Throwable e) { - throw new IllegalStateException("Unable to start KeycloakServer", e); - } - } - - public Keycloak client() { - return getAdminClient(getAuthUrl(), "master", "admin-cli", "admin", "admin"); - } - - private static Keycloak getAdminClient( - String url, String realm, String clientId, String username, String password) { - Keycloak keycloak = - KeycloakBuilder.builder() - .serverUrl(url) - .realm(realm) - .grantType(OAuth2Constants.PASSWORD) - .clientId(clientId) - .username(username) - .password(password) - .build(); - return keycloak; - } -} diff --git a/src/test/java/io/phasetwo/keycloak/resources/AbstractResourceTest.java b/src/test/java/io/phasetwo/keycloak/resources/AbstractResourceTest.java new file mode 100644 index 0000000..56d6e72 --- /dev/null +++ b/src/test/java/io/phasetwo/keycloak/resources/AbstractResourceTest.java @@ -0,0 +1,73 @@ +package io.phasetwo.keycloak.resources; + +import dasniko.testcontainers.keycloak.KeycloakContainer; +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import org.jboss.shrinkwrap.resolver.api.maven.Maven; +import org.junit.jupiter.api.BeforeAll; +import org.keycloak.admin.client.Keycloak; +import org.testcontainers.Testcontainers; + +public abstract class AbstractResourceTest { + + public static final String KEYCLOAK_IMAGE = + String.format( + "quay.io/phasetwo/keycloak-crdb:%s", System.getProperty("keycloak-version", "23.0.0")); + public static final String REALM = "master"; + public static final String ADMIN_CLI = "admin-cli"; + + static final String[] deps = { + "org.keycloak:keycloak-admin-client", + "io.phasetwo.keycloak:keycloak-orgs", + "com.github.xgp:kitchen-sink", + "org.openjdk.nashorn:nashorn-core" + }; + + static List getDeps() { + List dependencies = new ArrayList(); + for (String dep : deps) { + dependencies.addAll(getDep(dep)); + } + return dependencies; + } + + static List getDep(String pkg) { + return Maven.resolver() + .loadPomFromFile("./pom.xml") + .resolve(pkg) + .withoutTransitivity() + .asList(File.class); + } + + public static Keycloak keycloak; + + public static final KeycloakContainer container = + new KeycloakContainer(KEYCLOAK_IMAGE) + .withContextPath("/auth") + .withReuse(true) + .withProviderClassesFrom("target/classes") + .withProviderLibsFrom(getDeps()) + .withAccessToHost(true); + + protected static final int WEBHOOK_SERVER_PORT = 8083; + + static { + container.start(); + } + + @BeforeAll + public static void beforeAll() { + Testcontainers.exposeHostPorts(WEBHOOK_SERVER_PORT); + keycloak = + getKeycloak(REALM, ADMIN_CLI, container.getAdminUsername(), container.getAdminPassword()); + } + + public static Keycloak getKeycloak(String realm, String clientId, String user, String pass) { + return Keycloak.getInstance(getAuthUrl(), realm, user, pass, clientId); + } + + public static String getAuthUrl() { + return container.getAuthServerUrl(); + } +} diff --git a/src/test/java/io/phasetwo/keycloak/resources/EventsResourceTest.java b/src/test/java/io/phasetwo/keycloak/resources/EventsResourceTest.java index c04f472..44793bc 100644 --- a/src/test/java/io/phasetwo/keycloak/resources/EventsResourceTest.java +++ b/src/test/java/io/phasetwo/keycloak/resources/EventsResourceTest.java @@ -3,13 +3,13 @@ import static io.phasetwo.keycloak.Helpers.*; import static org.hamcrest.CoreMatchers.*; import static org.hamcrest.MatcherAssert.*; +import static org.hamcrest.Matchers.isEmptyOrNullString; import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThat; import com.github.xgp.http.server.Server; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; -import io.phasetwo.keycloak.KeycloakSuite; import io.phasetwo.keycloak.representation.RealmAttributeRepresentation; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; @@ -17,44 +17,40 @@ import lombok.extern.jbosslog.JBossLog; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; -import org.junit.ClassRule; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.keycloak.admin.client.Keycloak; import org.keycloak.broker.provider.util.SimpleHttp; @JBossLog -public class EventsResourceTest { - - @ClassRule public static KeycloakSuite server = KeycloakSuite.SERVER; +public class EventsResourceTest extends AbstractResourceTest { CloseableHttpClient httpClient = HttpClients.createDefault(); String webhookUrl() { - return server.getAuthUrl() + "/realms/master/webhooks"; + return getAuthUrl() + "/realms/master/webhooks"; } String eventsUrl() { - return server.getAuthUrl() + "/realms/master/events"; + return getAuthUrl() + "/realms/master/events"; } String attributesUrl() { - return server.getAuthUrl() + "/realms/master/attributes"; + return getAuthUrl() + "/realms/master/attributes"; } @Test public void testWebhookReceivesEvent() throws Exception { - Keycloak keycloak = server.client(); // update a realm with the ext-event-webhook listener addEventListener(keycloak, "master", "ext-event-webhook"); AtomicReference body = new AtomicReference(); // create a server on a free port with a handler to listen for the event - int port = nextFreePort(8083, 10000); + int port = WEBHOOK_SERVER_PORT; createWebhook( keycloak, httpClient, webhookUrl(), - "http://127.0.0.1:" + port + "/webhook", + "http://host.testcontainers.internal:" + port + "/webhook", "qlfwemke", ImmutableSet.of("admin.*", "foo.*")); @@ -89,12 +85,10 @@ public void testWebhookReceivesEvent() throws Exception { @Test public void testHttpConfiguredEvent() throws Exception { - Keycloak keycloak = server.client(); - AtomicInteger cnt = new AtomicInteger(0); AtomicReference body = new AtomicReference(); // create a server on a free port with a handler to listen for the event - int port = nextFreePort(8087, 10000); + int port = WEBHOOK_SERVER_PORT; Server server = new Server(port); server .router() @@ -117,7 +111,7 @@ public void testHttpConfiguredEvent() throws Exception { server.start(); Thread.sleep(1000l); - String targetUri = "http://127.0.0.1:" + port + "/webhook"; + String targetUri = "http://host.testcontainers.internal:" + port + "/webhook"; // create the config for a http event listener String key = "_providerConfig.ext-event-http.0"; @@ -145,7 +139,7 @@ public void testHttpConfiguredEvent() throws Exception { // check the handler for the event, after a delay assertThat(cnt.get(), is(1)); - assertNull(body.get()); + assertThat(body.get(), isEmptyOrNullString()); /* // retry = true diff --git a/src/test/java/io/phasetwo/keycloak/resources/RealmAttributesResourceTest.java b/src/test/java/io/phasetwo/keycloak/resources/RealmAttributesResourceTest.java index e721f8e..90ab348 100644 --- a/src/test/java/io/phasetwo/keycloak/resources/RealmAttributesResourceTest.java +++ b/src/test/java/io/phasetwo/keycloak/resources/RealmAttributesResourceTest.java @@ -6,27 +6,22 @@ import static org.junit.Assert.assertTrue; import com.fasterxml.jackson.core.type.TypeReference; -import io.phasetwo.keycloak.KeycloakSuite; import io.phasetwo.keycloak.representation.RealmAttributeRepresentation; import java.net.URLEncoder; import java.util.Map; import lombok.extern.jbosslog.JBossLog; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; -import org.junit.ClassRule; -import org.junit.Test; -import org.keycloak.admin.client.Keycloak; +import org.junit.jupiter.api.Test; import org.keycloak.broker.provider.util.SimpleHttp; @JBossLog -public class RealmAttributesResourceTest { - - @ClassRule public static KeycloakSuite server = KeycloakSuite.SERVER; +public class RealmAttributesResourceTest extends AbstractResourceTest { CloseableHttpClient httpClient = HttpClients.createDefault(); String baseUrl() { - return server.getAuthUrl() + "/realms/master/attributes"; + return getAuthUrl() + "/realms/master/attributes"; } String urlencode(String u) { @@ -39,7 +34,6 @@ String urlencode(String u) { @Test public void testGetAttributes() throws Exception { - Keycloak keycloak = server.client(); SimpleHttp.Response response = SimpleHttp.doGet(baseUrl(), httpClient) .auth(keycloak.tokenManager().getAccessTokenString()) @@ -52,7 +46,6 @@ public void testGetAttributes() throws Exception { @Test public void testAddGetAttribute() throws Exception { - Keycloak keycloak = server.client(); String key = "_providerConfig.ext-event-script.0"; String value = @@ -84,8 +77,6 @@ public void testAddGetAttribute() throws Exception { @Test public void testUpdateGetAttribute() throws Exception { - Keycloak keycloak = server.client(); - String key = "_providerConfig.test.1"; String value0 = "foo"; String value1 = "bar"; @@ -135,8 +126,6 @@ public void testUpdateGetAttribute() throws Exception { @Test public void testRemoveAttribute() throws Exception { - Keycloak keycloak = server.client(); - String key = "_providerConfig.test.2"; String value0 = "foo"; diff --git a/src/test/java/io/phasetwo/keycloak/resources/WebhooksResourceTest.java b/src/test/java/io/phasetwo/keycloak/resources/WebhooksResourceTest.java index 66db038..c2e7a50 100644 --- a/src/test/java/io/phasetwo/keycloak/resources/WebhooksResourceTest.java +++ b/src/test/java/io/phasetwo/keycloak/resources/WebhooksResourceTest.java @@ -11,7 +11,6 @@ import com.fasterxml.jackson.core.type.TypeReference; import com.github.xgp.http.server.Server; import com.google.common.collect.ImmutableSet; -import io.phasetwo.keycloak.KeycloakSuite; import io.phasetwo.keycloak.events.HttpSenderEventListenerProvider; import io.phasetwo.keycloak.representation.WebhookRepresentation; import java.net.URLEncoder; @@ -23,21 +22,18 @@ import lombok.extern.jbosslog.JBossLog; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; -import org.junit.ClassRule; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.keycloak.admin.client.Keycloak; import org.keycloak.broker.provider.util.SimpleHttp; import org.keycloak.util.JsonSerialization; @JBossLog -public class WebhooksResourceTest { - - @ClassRule public static KeycloakSuite server = KeycloakSuite.SERVER; +public class WebhooksResourceTest extends AbstractResourceTest { CloseableHttpClient httpClient = HttpClients.createDefault(); String baseUrl() { - return server.getAuthUrl() + "/realms/master/webhooks"; + return getAuthUrl() + "/realms/master/webhooks"; } String urlencode(String u) { @@ -50,8 +46,6 @@ String urlencode(String u) { @Test public void testAddGetWebhook() throws Exception { - Keycloak keycloak = server.client(); - String url = "https://example.com/testAddGetWebhook"; String id = createWebhook(keycloak, httpClient, baseUrl(), url, "A3jt6D8lz", null); @@ -84,8 +78,6 @@ public void testAddGetWebhook() throws Exception { /** https://github.com/p2-inc/keycloak-events/issues/42 */ @Test public void testAddGetWebhookEventTypes() throws Exception { - Keycloak keycloak = server.client(); - String url = "https://pipedream.m.pipedream.net"; Set types = ImmutableSet.of( @@ -126,8 +118,6 @@ public void testAddGetWebhookEventTypes() throws Exception { @Test public void testUpdateGetWebhook() throws Exception { - Keycloak keycloak = server.client(); - String url = "https://example.com/testUpdateGetWebhook"; String secret = "A3jt6D8lz"; String id = createWebhook(keycloak, httpClient, baseUrl(), url, secret, null); @@ -169,8 +159,6 @@ public void testUpdateGetWebhook() throws Exception { @Test public void testRemoveWebhoook() throws Exception { - Keycloak keycloak = server.client(); - String id = createWebhook( keycloak, @@ -195,7 +183,6 @@ public void testRemoveWebhoook() throws Exception { @Test public void testWebhookReceivesEvent() throws Exception { - Keycloak keycloak = server.client(); // update a realm with the ext-event-webhook listener addEventListener(keycloak, "master", "ext-event-webhook"); @@ -203,13 +190,13 @@ public void testWebhookReceivesEvent() throws Exception { AtomicReference body = new AtomicReference(); AtomicReference shaHeader = new AtomicReference(); // create a server on a free port with a handler to listen for the event - int port = nextFreePort(8083, 10000); + int port = WEBHOOK_SERVER_PORT; String id = createWebhook( keycloak, httpClient, baseUrl(), - "http://127.0.0.1:" + port + "/webhook", + "http://host.testcontainers.internal:" + port + "/webhook", "qlfwemke", ImmutableSet.of("admin.*")); @@ -286,7 +273,7 @@ void createOrg(Keycloak keycloak, String name) throws Exception { m.put("realm", "master"); SimpleHttp.Response response = - SimpleHttp.doPost(server.getAuthUrl() + "/realms/master/orgs", httpClient) + SimpleHttp.doPost(getAuthUrl() + "/realms/master/orgs", httpClient) .auth(keycloak.tokenManager().getAccessTokenString()) .json(m) .asResponse(); diff --git a/src/test/resources/log4j.properties b/src/test/resources/log4j.properties deleted file mode 100644 index 1da8230..0000000 --- a/src/test/resources/log4j.properties +++ /dev/null @@ -1,8 +0,0 @@ -# Root logger option -log4j.rootLogger=INFO, stdout - -# Direct log messages to stdout -log4j.appender.stdout=org.apache.log4j.ConsoleAppender -log4j.appender.stdout.Target=System.out -log4j.appender.stdout.layout=org.apache.log4j.PatternLayout -log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n diff --git a/src/test/resources/logback-test.xml b/src/test/resources/logback-test.xml new file mode 100644 index 0000000..823a6fb --- /dev/null +++ b/src/test/resources/logback-test.xml @@ -0,0 +1,15 @@ + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n + + + + + + + + + + +