From 5efe9da082d4aa6725b790d84a17f8b2e2ea89a1 Mon Sep 17 00:00:00 2001 From: Garth <244253+xgp@users.noreply.github.com> Date: Mon, 6 Jan 2025 16:27:27 +0100 Subject: [PATCH] added pagination to webhooks list. formatting. (#89) --- .../WebhookSenderEventListenerProvider.java | 3 +- .../representation/ExtendedAdminEvent.java | 3 +- .../keycloak/resources/WebhooksResource.java | 12 +- .../java/io/phasetwo/keycloak/Helpers.java | 19 +- ...ebhookSenderEventListenerProviderTest.java | 529 +++++++++--------- 5 files changed, 277 insertions(+), 289 deletions(-) diff --git a/src/main/java/io/phasetwo/keycloak/events/WebhookSenderEventListenerProvider.java b/src/main/java/io/phasetwo/keycloak/events/WebhookSenderEventListenerProvider.java index b704d1a..2825069 100644 --- a/src/main/java/io/phasetwo/keycloak/events/WebhookSenderEventListenerProvider.java +++ b/src/main/java/io/phasetwo/keycloak/events/WebhookSenderEventListenerProvider.java @@ -159,7 +159,8 @@ private ExtendedAdminEvent completeAdminEventAttributes(String uid, Event event) private ExtendedAdminEvent completeAdminEventAttributes(String uid, AdminEvent adminEvent) { RealmModel eventRealm = session.realms().getRealm(adminEvent.getRealmId()); RealmModel authRealm = session.realms().getRealm(adminEvent.getAuthDetails().getRealmId()); - ExtendedAdminEvent extendedAdminEvent = new ExtendedAdminEvent(uid, adminEvent, eventRealm, authRealm); + ExtendedAdminEvent extendedAdminEvent = + new ExtendedAdminEvent(uid, adminEvent, eventRealm, authRealm); // add always missing agent username ExtendedAuthDetails extendedAuthDetails = extendedAdminEvent.getAuthDetails(); if (!Strings.isNullOrEmpty(extendedAuthDetails.getUserId())) { diff --git a/src/main/java/io/phasetwo/keycloak/representation/ExtendedAdminEvent.java b/src/main/java/io/phasetwo/keycloak/representation/ExtendedAdminEvent.java index a2ffa8e..22a3e41 100644 --- a/src/main/java/io/phasetwo/keycloak/representation/ExtendedAdminEvent.java +++ b/src/main/java/io/phasetwo/keycloak/representation/ExtendedAdminEvent.java @@ -36,7 +36,8 @@ private static String createType(Event event) { public ExtendedAdminEvent() {} - public ExtendedAdminEvent(String uid, AdminEvent event, RealmModel eventRealm, RealmModel authRealm) { + public ExtendedAdminEvent( + String uid, AdminEvent event, RealmModel eventRealm, RealmModel authRealm) { this.uid = uid; this.type = createType(event); diff --git a/src/main/java/io/phasetwo/keycloak/resources/WebhooksResource.java b/src/main/java/io/phasetwo/keycloak/resources/WebhooksResource.java index 6dd661f..dad7fb9 100644 --- a/src/main/java/io/phasetwo/keycloak/resources/WebhooksResource.java +++ b/src/main/java/io/phasetwo/keycloak/resources/WebhooksResource.java @@ -32,11 +32,19 @@ public WebhooksResource(KeycloakSession session) { this.webhooks = session.getProvider(WebhookProvider.class); } + private static final Integer DEFAULT_MAX_RESULTS = 100; + @GET @Produces(MediaType.APPLICATION_JSON) - public Stream getWebhooks() { + public Stream getWebhooks( + @QueryParam("first") Integer firstResult, @QueryParam("max") Integer maxResults) { permissions.realm().requireViewEvents(); - return webhooks.getWebhooksStream(realm).map(w -> toRepresentation(w)); + firstResult = firstResult != null ? firstResult : 0; + maxResults = + (maxResults != null && maxResults <= DEFAULT_MAX_RESULTS) + ? maxResults + : DEFAULT_MAX_RESULTS; + return webhooks.getWebhooksStream(realm, firstResult, maxResults).map(w -> toRepresentation(w)); } private WebhookRepresentation toRepresentation(WebhookModel w) { diff --git a/src/test/java/io/phasetwo/keycloak/Helpers.java b/src/test/java/io/phasetwo/keycloak/Helpers.java index 56c5e11..68f8d41 100644 --- a/src/test/java/io/phasetwo/keycloak/Helpers.java +++ b/src/test/java/io/phasetwo/keycloak/Helpers.java @@ -88,18 +88,15 @@ public static String createWebhook( } public static void removeWebhook( - Keycloak keycloak, - CloseableHttpClient httpClient, - String baseUrl, - String webhookId) - throws Exception { + Keycloak keycloak, CloseableHttpClient httpClient, String baseUrl, String webhookId) + throws Exception { - LegacySimpleHttp.Response response = - LegacySimpleHttp.doDelete(baseUrl + "/" + webhookId, httpClient) - .auth(keycloak.tokenManager().getAccessTokenString()) - .asResponse(); - assertThat(response.getStatus(), is(204)); - } + LegacySimpleHttp.Response response = + LegacySimpleHttp.doDelete(baseUrl + "/" + webhookId, httpClient) + .auth(keycloak.tokenManager().getAccessTokenString()) + .asResponse(); + assertThat(response.getStatus(), is(204)); + } public static String urlencode(String u) { try { diff --git a/src/test/java/io/phasetwo/keycloak/events/WebhookSenderEventListenerProviderTest.java b/src/test/java/io/phasetwo/keycloak/events/WebhookSenderEventListenerProviderTest.java index a653056..0fcc458 100644 --- a/src/test/java/io/phasetwo/keycloak/events/WebhookSenderEventListenerProviderTest.java +++ b/src/test/java/io/phasetwo/keycloak/events/WebhookSenderEventListenerProviderTest.java @@ -5,11 +5,18 @@ import static org.hamcrest.CoreMatchers.*; import static org.hamcrest.MatcherAssert.*; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.xgp.http.server.Server; +import com.google.common.collect.ImmutableSet; +import io.phasetwo.keycloak.representation.ExtendedAdminEvent; +import io.phasetwo.keycloak.resources.AbstractResourceTest; +import jakarta.ws.rs.core.Response; import java.util.Arrays; import java.util.List; import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; - +import lombok.extern.jbosslog.JBossLog; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.junit.jupiter.api.Test; @@ -18,23 +25,13 @@ import org.keycloak.admin.client.KeycloakBuilder; import org.keycloak.admin.client.resource.RealmResource; import org.keycloak.representations.idm.ClientRepresentation; - import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.representations.idm.RoleRepresentation; import org.keycloak.representations.idm.UserRepresentation; -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.github.xgp.http.server.Server; -import com.google.common.collect.ImmutableSet; - -import io.phasetwo.keycloak.representation.ExtendedAdminEvent; -import io.phasetwo.keycloak.resources.AbstractResourceTest; -import jakarta.ws.rs.core.Response; -import lombok.extern.jbosslog.JBossLog; @JBossLog public class WebhookSenderEventListenerProviderTest extends AbstractResourceTest { - final static String TEST_REALM = "testRealm"; + static final String TEST_REALM = "testRealm"; CloseableHttpClient httpClient = HttpClients.createDefault(); String webhookUrl(String realm) { @@ -58,13 +55,14 @@ public void testAdminEventContainsCorrectRealmMasterMaster() throws Exception { AtomicReference body = new AtomicReference(); // create a server on a free port with a handler to listen for the event int port = WEBHOOK_SERVER_PORT; - String webhookId = createWebhook( - keycloak, - httpClient, - webhookUrl(REALM), - "http://host.testcontainers.internal:" + port + "/webhook", - "qlfwemke", - ImmutableSet.of("admin.*")); + String webhookId = + createWebhook( + keycloak, + httpClient, + webhookUrl(REALM), + "http://host.testcontainers.internal:" + port + "/webhook", + "qlfwemke", + ImmutableSet.of("admin.*")); Server server = new Server(port); server @@ -82,43 +80,39 @@ public void testAdminEventContainsCorrectRealmMasterMaster() throws Exception { Thread.sleep(1000l); try { - // cause an event to be sent - UserRepresentation userRepresentation = new UserRepresentation(); - userRepresentation.setUsername("username"); - Response userResponse = realm.users().create(userRepresentation); - assertThat(userResponse.getStatus(), is(201)); - - Thread.sleep(1000l); - - // check the handler for the event, after a delay - String receivedPayload = body.get(); - ExtendedAdminEvent event = parseEvent(receivedPayload); - assertThat(event.getRealmId(), equalTo(REALM)); - assertThat(event.getAuthDetails().getRealmId(), equalTo(REALM)); - assertThat(event.getType(), equalTo("admin.USER-CREATE")); - - // Now delete the user - List users = realm.users().search("username"); - assertThat(users.size(), is(1)); - String userId = users.getFirst().getId(); - userResponse = realm.users().delete(userId); - assertThat(userResponse.getStatus(), is(204)); - - Thread.sleep(1000l); - - // check the handler for the event, after a delay - receivedPayload = body.get(); - event = parseEvent(receivedPayload); - assertThat(event.getRealmId(), equalTo(REALM)); - assertThat(event.getAuthDetails().getRealmId(), equalTo(REALM)); - assertThat(event.getType(), equalTo("admin.USER-DELETE")); - } - finally { - server.stop(); - removeWebhook(keycloak, - httpClient, - webhookUrl(REALM), - webhookId); + // cause an event to be sent + UserRepresentation userRepresentation = new UserRepresentation(); + userRepresentation.setUsername("username"); + Response userResponse = realm.users().create(userRepresentation); + assertThat(userResponse.getStatus(), is(201)); + + Thread.sleep(1000l); + + // check the handler for the event, after a delay + String receivedPayload = body.get(); + ExtendedAdminEvent event = parseEvent(receivedPayload); + assertThat(event.getRealmId(), equalTo(REALM)); + assertThat(event.getAuthDetails().getRealmId(), equalTo(REALM)); + assertThat(event.getType(), equalTo("admin.USER-CREATE")); + + // Now delete the user + List users = realm.users().search("username"); + assertThat(users.size(), is(1)); + String userId = users.getFirst().getId(); + userResponse = realm.users().delete(userId); + assertThat(userResponse.getStatus(), is(204)); + + Thread.sleep(1000l); + + // check the handler for the event, after a delay + receivedPayload = body.get(); + event = parseEvent(receivedPayload); + assertThat(event.getRealmId(), equalTo(REALM)); + assertThat(event.getAuthDetails().getRealmId(), equalTo(REALM)); + assertThat(event.getType(), equalTo("admin.USER-DELETE")); + } finally { + server.stop(); + removeWebhook(keycloak, httpClient, webhookUrl(REALM), webhookId); } } @@ -162,42 +156,41 @@ public void testAdminEventContainsCorrectRealmMasterTest() throws Exception { Thread.sleep(1000l); try { - // cause an event to be sent - UserRepresentation userRepresentation = new UserRepresentation(); - userRepresentation.setUsername("username"); - Response userResponse = realm.users().create(userRepresentation); - assertThat(userResponse.getStatus(), is(201)); - - Thread.sleep(1000l); - - // check the handler for the event, after a delay - String receivedPayload = body.get(); - ExtendedAdminEvent event = parseEvent(receivedPayload); - assertThat(event.getRealmId(), equalTo(TEST_REALM)); - assertThat(event.getAuthDetails().getRealmId(), equalTo(REALM)); - assertThat(event.getType(), equalTo("admin.USER-CREATE")); - - // Now delete the user - List users = realm.users().search("username"); - assertThat(users.size(), is(1)); - String userId = users.getFirst().getId(); - userResponse = realm.users().delete(userId); - assertThat(userResponse.getStatus(), is(204)); - - Thread.sleep(1000l); - - // check the handler for the event, after a delay - receivedPayload = body.get(); - event = parseEvent(receivedPayload); - assertThat(event.getRealmId(), equalTo(TEST_REALM)); - assertThat(event.getAuthDetails().getRealmId(), equalTo(REALM)); - assertThat(event.getType(), equalTo("admin.USER-DELETE")); - - // cleanup - realm.remove(); - } - finally { - server.stop(); + // cause an event to be sent + UserRepresentation userRepresentation = new UserRepresentation(); + userRepresentation.setUsername("username"); + Response userResponse = realm.users().create(userRepresentation); + assertThat(userResponse.getStatus(), is(201)); + + Thread.sleep(1000l); + + // check the handler for the event, after a delay + String receivedPayload = body.get(); + ExtendedAdminEvent event = parseEvent(receivedPayload); + assertThat(event.getRealmId(), equalTo(TEST_REALM)); + assertThat(event.getAuthDetails().getRealmId(), equalTo(REALM)); + assertThat(event.getType(), equalTo("admin.USER-CREATE")); + + // Now delete the user + List users = realm.users().search("username"); + assertThat(users.size(), is(1)); + String userId = users.getFirst().getId(); + userResponse = realm.users().delete(userId); + assertThat(userResponse.getStatus(), is(204)); + + Thread.sleep(1000l); + + // check the handler for the event, after a delay + receivedPayload = body.get(); + event = parseEvent(receivedPayload); + assertThat(event.getRealmId(), equalTo(TEST_REALM)); + assertThat(event.getAuthDetails().getRealmId(), equalTo(REALM)); + assertThat(event.getType(), equalTo("admin.USER-DELETE")); + + // cleanup + realm.remove(); + } finally { + server.stop(); } } @@ -232,16 +225,11 @@ public void testAdminEventContainsCorrectRealmTestTest() throws Exception { String realmManagementId = realm.clients().findByClientId("realm-management").get(0).getId(); String clientId = realm.clients().findByClientId(realmAdminClientId).get(0).getId(); String serviceUserId = realm.clients().get(clientId).getServiceAccountUser().getId(); - List availableRoles = realm - .users() - .get(serviceUserId) - .roles() - .clientLevel(realmManagementId) - .listAvailable(); - List rolesToAssign = availableRoles - .stream() - .filter(r -> - "realm-admin".equalsIgnoreCase(r.getName())) + List availableRoles = + realm.users().get(serviceUserId).roles().clientLevel(realmManagementId).listAvailable(); + List rolesToAssign = + availableRoles.stream() + .filter(r -> "realm-admin".equalsIgnoreCase(r.getName())) .collect(Collectors.toList()); assertThat(rolesToAssign.size(), is(1)); realm.users().get(serviceUserId).roles().clientLevel(realmManagementId).add(rolesToAssign); @@ -273,183 +261,176 @@ public void testAdminEventContainsCorrectRealmTestTest() throws Exception { Thread.sleep(1000l); try { - // log in to the test realm - Keycloak keycloakTestRealm = KeycloakBuilder.builder() - .serverUrl(getAuthUrl()) - .clientId(realmAdminClientId) - .clientSecret(realmAdminClientSecret) - .realm(TEST_REALM) - .grantType(OAuth2Constants.CLIENT_CREDENTIALS) - .build(); - realm = keycloakTestRealm.realm(TEST_REALM); - - // cause an event to be sent - UserRepresentation userRepresentation = new UserRepresentation(); - userRepresentation.setUsername("username"); - Response userResponse = realm.users().create(userRepresentation); - assertThat(userResponse.getStatus(), is(201)); - - Thread.sleep(1000l); - - // check the handler for the event, after a delay - String receivedPayload = body.get(); - ExtendedAdminEvent event = parseEvent(receivedPayload); - assertThat(event.getRealmId(), equalTo(TEST_REALM)); - assertThat(event.getAuthDetails().getRealmId(), equalTo(TEST_REALM)); - assertThat(event.getType(), equalTo("admin.USER-CREATE")); - - // Now delete the user - List users = realm.users().search("username"); - assertThat(users.size(), is(1)); - String userId = users.getFirst().getId(); - userResponse = realm.users().delete(userId); - assertThat(userResponse.getStatus(), is(204)); - - Thread.sleep(1000l); - - // check the handler for the event, after a delay - receivedPayload = body.get(); - event = parseEvent(receivedPayload); - assertThat(event.getRealmId(), equalTo(TEST_REALM)); - assertThat(event.getAuthDetails().getRealmId(), equalTo(TEST_REALM)); - assertThat(event.getType(), equalTo("admin.USER-DELETE")); - - // cleanup - realm = keycloak.realm(TEST_REALM); - realm.remove(); - } - finally { - server.stop(); + // log in to the test realm + Keycloak keycloakTestRealm = + KeycloakBuilder.builder() + .serverUrl(getAuthUrl()) + .clientId(realmAdminClientId) + .clientSecret(realmAdminClientSecret) + .realm(TEST_REALM) + .grantType(OAuth2Constants.CLIENT_CREDENTIALS) + .build(); + realm = keycloakTestRealm.realm(TEST_REALM); + + // cause an event to be sent + UserRepresentation userRepresentation = new UserRepresentation(); + userRepresentation.setUsername("username"); + Response userResponse = realm.users().create(userRepresentation); + assertThat(userResponse.getStatus(), is(201)); + + Thread.sleep(1000l); + + // check the handler for the event, after a delay + String receivedPayload = body.get(); + ExtendedAdminEvent event = parseEvent(receivedPayload); + assertThat(event.getRealmId(), equalTo(TEST_REALM)); + assertThat(event.getAuthDetails().getRealmId(), equalTo(TEST_REALM)); + assertThat(event.getType(), equalTo("admin.USER-CREATE")); + + // Now delete the user + List users = realm.users().search("username"); + assertThat(users.size(), is(1)); + String userId = users.getFirst().getId(); + userResponse = realm.users().delete(userId); + assertThat(userResponse.getStatus(), is(204)); + + Thread.sleep(1000l); + + // check the handler for the event, after a delay + receivedPayload = body.get(); + event = parseEvent(receivedPayload); + assertThat(event.getRealmId(), equalTo(TEST_REALM)); + assertThat(event.getAuthDetails().getRealmId(), equalTo(TEST_REALM)); + assertThat(event.getType(), equalTo("admin.USER-DELETE")); + + // cleanup + realm = keycloak.realm(TEST_REALM); + realm.remove(); + } finally { + server.stop(); } } @Test - public void testAdminEventContainsCorrectRealmCreatedByMaterRemovedByTest() throws Exception - { - // Create a realm for tests - RealmRepresentation realmRepresentation = new RealmRepresentation(); - realmRepresentation.setId(TEST_REALM); - realmRepresentation.setDisplayName(TEST_REALM); - realmRepresentation.setAdminEventsEnabled(true); - realmRepresentation.setAdminEventsDetailsEnabled(true); - realmRepresentation.setRealm(TEST_REALM); - realmRepresentation.setEventsListeners(Arrays.asList("ext-event-webhook")); - realmRepresentation.setEnabled(true); - keycloak.realms().create(realmRepresentation); - RealmResource realm = keycloak.realm(TEST_REALM); - - // Creating admin client - final String realmAdminClientId = "realmAdmin"; - final String realmAdminClientSecret = "realmPassword"; - ClientRepresentation client = new ClientRepresentation(); - client.setSecret(realmAdminClientSecret); - client.setClientId(realmAdminClientId); - client.setEnabled(true); - client.setServiceAccountsEnabled(true); - client.setPublicClient(false); - client.setProtocol("openid-connect"); - Response clientCreationResponse = realm.clients().create(client); - assertThat(clientCreationResponse.getStatus(), is(201)); - - // Adding rights - String realmManagementId = realm.clients().findByClientId("realm-management").get(0).getId(); - String clientId = realm.clients().findByClientId(realmAdminClientId).get(0).getId(); - String serviceUserId = realm.clients().get(clientId).getServiceAccountUser().getId(); - List availableRoles = realm - .users() - .get(serviceUserId) - .roles() - .clientLevel(realmManagementId) - .listAvailable(); - List rolesToAssign = availableRoles - .stream() - .filter(r -> - "realm-admin".equalsIgnoreCase(r.getName())) - .collect(Collectors.toList()); - assertThat(rolesToAssign.size(), is(1)); - realm.users().get(serviceUserId).roles().clientLevel(realmManagementId).add(rolesToAssign); - - AtomicReference body = new AtomicReference(); - // create a server on a free port with a handler to listen for the event - int port = WEBHOOK_SERVER_PORT; - createWebhook( - keycloak, - httpClient, - webhookUrl(TEST_REALM), - "http://host.testcontainers.internal:" + port + "/webhook", - "qlfwemke", - ImmutableSet.of("admin.*")); - - Server server = new Server(port); - server - .router() - .POST( - "/webhook", - (request, response) -> { - String r = request.body(); - log.infof("%s", r); - body.set(r); - response.body("OK"); - response.status(202); - }); - server.start(); - Thread.sleep(1000l); - - try { - // cause an event to be sent - UserRepresentation userRepresentation = new UserRepresentation(); - userRepresentation.setUsername("username"); - Response userResponse = realm.users().create(userRepresentation); - assertThat(userResponse.getStatus(), is(201)); - - Thread.sleep(1000l); - - // check the handler for the event, after a delay - String receivedPayload = body.get(); - ExtendedAdminEvent event = parseEvent(receivedPayload); - assertThat(event.getRealmId(), equalTo(TEST_REALM)); - assertThat(event.getAuthDetails().getRealmId(), equalTo(REALM)); - assertThat(event.getType(), equalTo("admin.USER-CREATE")); - - // log in to the test realm - Keycloak keycloakTestRealm = KeycloakBuilder.builder() - .serverUrl(getAuthUrl()) - .clientId(realmAdminClientId) - .clientSecret(realmAdminClientSecret) - .realm(TEST_REALM) - .grantType(OAuth2Constants.CLIENT_CREDENTIALS) - .build(); - realm = keycloakTestRealm.realm(TEST_REALM); - - // Now delete the user - List users = realm.users().search("username"); - assertThat(users.size(), is(1)); - String userId = users.getFirst().getId(); - userResponse = realm.users().delete(userId); - assertThat(userResponse.getStatus(), is(204)); - - Thread.sleep(1000l); - - // check the handler for the event, after a delay - receivedPayload = body.get(); - event = parseEvent(receivedPayload); - assertThat(event.getRealmId(), equalTo(TEST_REALM)); - assertThat(event.getAuthDetails().getRealmId(), equalTo(TEST_REALM)); - assertThat(event.getType(), equalTo("admin.USER-DELETE")); - - // cleanup - realm = keycloak.realm(TEST_REALM); - realm.remove(); - } - finally { - server.stop(); - } + public void testAdminEventContainsCorrectRealmCreatedByMaterRemovedByTest() throws Exception { + // Create a realm for tests + RealmRepresentation realmRepresentation = new RealmRepresentation(); + realmRepresentation.setId(TEST_REALM); + realmRepresentation.setDisplayName(TEST_REALM); + realmRepresentation.setAdminEventsEnabled(true); + realmRepresentation.setAdminEventsDetailsEnabled(true); + realmRepresentation.setRealm(TEST_REALM); + realmRepresentation.setEventsListeners(Arrays.asList("ext-event-webhook")); + realmRepresentation.setEnabled(true); + keycloak.realms().create(realmRepresentation); + RealmResource realm = keycloak.realm(TEST_REALM); + + // Creating admin client + final String realmAdminClientId = "realmAdmin"; + final String realmAdminClientSecret = "realmPassword"; + ClientRepresentation client = new ClientRepresentation(); + client.setSecret(realmAdminClientSecret); + client.setClientId(realmAdminClientId); + client.setEnabled(true); + client.setServiceAccountsEnabled(true); + client.setPublicClient(false); + client.setProtocol("openid-connect"); + Response clientCreationResponse = realm.clients().create(client); + assertThat(clientCreationResponse.getStatus(), is(201)); + + // Adding rights + String realmManagementId = realm.clients().findByClientId("realm-management").get(0).getId(); + String clientId = realm.clients().findByClientId(realmAdminClientId).get(0).getId(); + String serviceUserId = realm.clients().get(clientId).getServiceAccountUser().getId(); + List availableRoles = + realm.users().get(serviceUserId).roles().clientLevel(realmManagementId).listAvailable(); + List rolesToAssign = + availableRoles.stream() + .filter(r -> "realm-admin".equalsIgnoreCase(r.getName())) + .collect(Collectors.toList()); + assertThat(rolesToAssign.size(), is(1)); + realm.users().get(serviceUserId).roles().clientLevel(realmManagementId).add(rolesToAssign); + + AtomicReference body = new AtomicReference(); + // create a server on a free port with a handler to listen for the event + int port = WEBHOOK_SERVER_PORT; + createWebhook( + keycloak, + httpClient, + webhookUrl(TEST_REALM), + "http://host.testcontainers.internal:" + port + "/webhook", + "qlfwemke", + ImmutableSet.of("admin.*")); + + Server server = new Server(port); + server + .router() + .POST( + "/webhook", + (request, response) -> { + String r = request.body(); + log.infof("%s", r); + body.set(r); + response.body("OK"); + response.status(202); + }); + server.start(); + Thread.sleep(1000l); + + try { + // cause an event to be sent + UserRepresentation userRepresentation = new UserRepresentation(); + userRepresentation.setUsername("username"); + Response userResponse = realm.users().create(userRepresentation); + assertThat(userResponse.getStatus(), is(201)); + + Thread.sleep(1000l); + + // check the handler for the event, after a delay + String receivedPayload = body.get(); + ExtendedAdminEvent event = parseEvent(receivedPayload); + assertThat(event.getRealmId(), equalTo(TEST_REALM)); + assertThat(event.getAuthDetails().getRealmId(), equalTo(REALM)); + assertThat(event.getType(), equalTo("admin.USER-CREATE")); + + // log in to the test realm + Keycloak keycloakTestRealm = + KeycloakBuilder.builder() + .serverUrl(getAuthUrl()) + .clientId(realmAdminClientId) + .clientSecret(realmAdminClientSecret) + .realm(TEST_REALM) + .grantType(OAuth2Constants.CLIENT_CREDENTIALS) + .build(); + realm = keycloakTestRealm.realm(TEST_REALM); + + // Now delete the user + List users = realm.users().search("username"); + assertThat(users.size(), is(1)); + String userId = users.getFirst().getId(); + userResponse = realm.users().delete(userId); + assertThat(userResponse.getStatus(), is(204)); + + Thread.sleep(1000l); + + // check the handler for the event, after a delay + receivedPayload = body.get(); + event = parseEvent(receivedPayload); + assertThat(event.getRealmId(), equalTo(TEST_REALM)); + assertThat(event.getAuthDetails().getRealmId(), equalTo(TEST_REALM)); + assertThat(event.getType(), equalTo("admin.USER-DELETE")); + + // cleanup + realm = keycloak.realm(TEST_REALM); + realm.remove(); + } finally { + server.stop(); + } } - private static ExtendedAdminEvent parseEvent(String input) throws Exception - { - ObjectMapper mapper = new ObjectMapper(); - mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); - return mapper.readValue(input, ExtendedAdminEvent.class); + private static ExtendedAdminEvent parseEvent(String input) throws Exception { + ObjectMapper mapper = new ObjectMapper(); + mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + return mapper.readValue(input, ExtendedAdminEvent.class); } }