diff --git a/src/main/java/com/example/keycloak/WebhookEventListenerProvider.java b/src/main/java/com/example/keycloak/WebhookEventListenerProvider.java index 29b8dfd..1ea6d7c 100644 --- a/src/main/java/com/example/keycloak/WebhookEventListenerProvider.java +++ b/src/main/java/com/example/keycloak/WebhookEventListenerProvider.java @@ -6,6 +6,7 @@ import org.keycloak.models.KeycloakSession; import org.slf4j.Logger; import org.apache.http.auth.UsernamePasswordCredentials; +import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.StringEntity; import org.apache.http.impl.auth.BasicScheme; @@ -42,7 +43,9 @@ private void processEvent(Object event, String eventType) { logger.info("Sending webhook for {} type", eventType); String webhookUrl = getWebhookUrlForRealm(realmName); HttpPost httpPost = prepareHttpPost(webhookUrl, realmName, event); - httpClient.execute(httpPost); + CloseableHttpResponse response = httpClient.execute(httpPost); + logger.info("Webhook response: {}", response.getStatusLine()); + response.close(); } catch (Exception e) { logger.error("Error while sending webhook for {}: {}", eventType, e.getMessage(), e); } diff --git a/src/test/java/com/example/keycloak/WebhookEventListenerProviderTest.java b/src/test/java/com/example/keycloak/WebhookEventListenerProviderTest.java index 958b753..699e576 100644 --- a/src/test/java/com/example/keycloak/WebhookEventListenerProviderTest.java +++ b/src/test/java/com/example/keycloak/WebhookEventListenerProviderTest.java @@ -1,9 +1,12 @@ package com.example.keycloak; import org.apache.commons.io.IOExceptionWithCause; +import org.apache.http.ProtocolVersion; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPost; import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.message.BasicHttpResponse; +import org.apache.http.message.BasicStatusLine; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -29,10 +32,11 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; import static uk.org.lidalia.slf4jext.Level.ERROR; +import static uk.org.lidalia.slf4jext.Level.INFO; @ExtendWith(MockitoExtension.class) @ExtendWith(SystemStubsExtension.class) -public class WebhookEventListenerProviderTest { +class WebhookEventListenerProviderTest { @Mock private KeycloakSession session; @@ -58,7 +62,7 @@ public class WebhookEventListenerProviderTest { private TestLogger testLogger = TestLoggerFactory.getTestLogger(WebhookEventListenerProvider.class); @BeforeEach - public void setup() { + void setup() { TestLoggerFactory.clear(); testLogger = TestLoggerFactory.getTestLogger(WebhookEventListenerProvider.class); provider = new WebhookEventListenerProvider(session, httpClient, testLogger); @@ -68,7 +72,7 @@ public void setup() { } @Test - public void testOnEvent_WithBasicAuth() throws Exception { + void testOnEvent_WithBasicAuth() throws Exception { setupEnvironmentVariablesForBasicAuth(); Event event = createTestEvent(); CloseableHttpResponse mockResponse = mock(CloseableHttpResponse.class); @@ -80,7 +84,7 @@ public void testOnEvent_WithBasicAuth() throws Exception { } @Test - public void testOnEvent_NoBasicAuth() throws Exception { + void testOnEvent_NoBasicAuth() throws Exception { setupEnvironmentVariablesForNoAuth(); Event event = createTestEvent(); CloseableHttpResponse mockResponse = mock(CloseableHttpResponse.class); @@ -92,19 +96,12 @@ public void testOnEvent_NoBasicAuth() throws Exception { } @Test - public void testOnEvent_HttpExecutionFailure() throws Exception { + void testOnEvent_HttpExecutionFailure() throws Exception { setupEnvironmentVariablesForNoAuth(); Event event = createTestEvent(); doThrow(new IOExceptionWithCause("Simulated IO Exception", null)).when(httpClient).execute(any(HttpPost.class)); - // Logger rootLogger = (Logger) - // LoggerFactory.getLogger(WebhookEventListenerProvider.class); - // rootLogger.setLevel(Level.ERROR); - // ListAppender listAppender = new ListAppender<>(); - // listAppender.start(); - // rootLogger.addAppender(listAppender); - provider.onEvent(event); assertTrue(testLogger.getLoggingEvents().stream().anyMatch(log -> log.getLevel().equals(ERROR))); @@ -113,7 +110,7 @@ public void testOnEvent_HttpExecutionFailure() throws Exception { } @Test - public void testOnEvent_BasicAuthWithoutCredentials() throws Exception { + void testOnEvent_BasicAuthWithoutCredentials() throws Exception { environmentVariables.set("WEBHOOK_URL_TEST", "http://example.com/webhook"); environmentVariables.set("WEBHOOK_AUTH_METHOD_TEST", "basic"); // deliberately not setting USERNAME and PASSWORD @@ -128,6 +125,52 @@ public void testOnEvent_BasicAuthWithoutCredentials() throws Exception { verifyHttpPostExecution("http://example.com/webhook", "application/json", false); } + @Test + void testOnEvent_BasicAuthWithWrongUsername() throws Exception { + setupEnvironmentVariablesForBasicAuth(); + environmentVariables.set("WEBHOOK_AUTH_METHOD_TEST", "basic"); + environmentVariables.set("WEBHOOK_BASIC_AUTH_USERNAME_TEST", "wronguser"); + + Event event = createTestEvent(); + + CloseableHttpResponse unauthorizedResponse = mock(CloseableHttpResponse.class); + when(unauthorizedResponse.getStatusLine()) + .thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 401, "Unauthorized")); + + when(httpClient.execute(any(HttpPost.class))).thenReturn(unauthorizedResponse); + + provider.onEvent(event); + + verifyHttpPostExecution("http://example.com/webhook", "application/json", true); + + assertTrue(testLogger.getLoggingEvents().stream() + .anyMatch(log -> log.getLevel().equals(INFO) && log.getMessage().contains("Webhook response") + && log.getArguments().get(0).toString().contains("401 Unauthorized"))); + } + + @Test + void testOnEvent_BasicAuthWithWrongPassword() throws Exception { + setupEnvironmentVariablesForBasicAuth(); + environmentVariables.set("WEBHOOK_AUTH_METHOD_TEST", "basic"); + environmentVariables.set("WEBHOOK_BASIC_AUTH_PASSWORD_TEST", "wrongpassword"); + + Event event = createTestEvent(); + + CloseableHttpResponse unauthorizedResponse = mock(CloseableHttpResponse.class); + when(unauthorizedResponse.getStatusLine()) + .thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 401, "Unauthorized")); + + when(httpClient.execute(any(HttpPost.class))).thenReturn(unauthorizedResponse); + + provider.onEvent(event); + + verifyHttpPostExecution("http://example.com/webhook", "application/json", true); + + assertTrue(testLogger.getLoggingEvents().stream() + .anyMatch(log -> log.getLevel().equals(INFO) && log.getMessage().contains("Webhook response") + && log.getArguments().get(0).toString().contains("401 Unauthorized"))); + } + private void setupEnvironmentVariablesForBasicAuth() { environmentVariables.set("WEBHOOK_URL_TEST", "http://example.com/webhook"); environmentVariables.set("WEBHOOK_AUTH_METHOD_TEST", "basic"); @@ -155,6 +198,7 @@ private void verifyHttpPostExecution(String expectedUrl, String expectedContentT if (expectAuthHeader) { assertNotNull(actualPost.getFirstHeader("Authorization")); } else { + System.err.println("-------------------------------" + actualPost.getFirstHeader("Authorization")); assertNull(actualPost.getFirstHeader("Authorization")); } }