From 718ec1c1a86b1f2c813fb837907a79aaa911415f Mon Sep 17 00:00:00 2001 From: Stephan Schnabel Date: Mon, 24 Apr 2023 13:31:59 +0200 Subject: [PATCH] Check realm from context for null, fix #25 --- .../metrics/MicrometerEventListener.java | 23 ++-- .../metrics/MicrometerEventListenerTest.java | 107 ++++++++++++++++++ 2 files changed, 123 insertions(+), 7 deletions(-) diff --git a/src/main/java/io/kokuwa/keycloak/metrics/MicrometerEventListener.java b/src/main/java/io/kokuwa/keycloak/metrics/MicrometerEventListener.java index 643d636..7a83b3f 100644 --- a/src/main/java/io/kokuwa/keycloak/metrics/MicrometerEventListener.java +++ b/src/main/java/io/kokuwa/keycloak/metrics/MicrometerEventListener.java @@ -1,10 +1,14 @@ package io.kokuwa.keycloak.metrics; +import java.util.Optional; + import org.jboss.logging.Logger; import org.keycloak.events.Event; import org.keycloak.events.EventListenerProvider; import org.keycloak.events.admin.AdminEvent; +import org.keycloak.models.KeycloakContext; import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RealmModel; import io.micrometer.core.instrument.MeterRegistry; @@ -20,7 +24,7 @@ public class MicrometerEventListener implements EventListenerProvider, AutoClose private final KeycloakSession session; private final boolean replace; - public MicrometerEventListener(MeterRegistry registry, KeycloakSession session, boolean replaceId) { + MicrometerEventListener(MeterRegistry registry, KeycloakSession session, boolean replaceId) { this.registry = registry; this.session = session; this.replace = replaceId; @@ -50,12 +54,17 @@ public class MicrometerEventListener implements EventListenerProvider, AutoClose public void close() {} private String getRealmName(String id) { - var model = session.getContext().getRealm(); - if (id == null || id.equals(model.getId())) { - return model.getName(); - } - log.warnv("Failed to resolve realmName for id {0}", id); - return id; + return Optional.ofNullable(session.getContext()).map(KeycloakContext::getRealm) + .filter(realm -> id == null || id.equals(realm.getId())) + .or(() -> { + log.tracev("Context realm was empty with id {0}", id); + return Optional.ofNullable(id).map(session.realms()::getRealm); + }) + .map(RealmModel::getName) + .orElseGet(() -> { + log.warnv("Failed to find realm with id {0}", id); + return id; + }); } private String toBlank(Object value) { diff --git a/src/test/java/io/kokuwa/keycloak/metrics/MicrometerEventListenerTest.java b/src/test/java/io/kokuwa/keycloak/metrics/MicrometerEventListenerTest.java index 0fc3b82..5d358c5 100644 --- a/src/test/java/io/kokuwa/keycloak/metrics/MicrometerEventListenerTest.java +++ b/src/test/java/io/kokuwa/keycloak/metrics/MicrometerEventListenerTest.java @@ -25,6 +25,7 @@ import org.keycloak.events.admin.ResourceType; import org.keycloak.models.KeycloakContext; import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; +import org.keycloak.models.RealmProvider; import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.Mock; @@ -46,6 +47,8 @@ public class MicrometerEventListenerTest { @Mock RealmModel realmModel; @Mock + RealmProvider realmProvider; + @Mock KeycloakContext context; @Mock MeterRegistry registry; @@ -116,6 +119,58 @@ public class MicrometerEventListenerTest { assertEvent(realmName, "", "", ""); } + @DisplayName("replace(true) - context is null") + @Test + void replaceFieldsContextNull() { + + var realmId = UUID.randomUUID().toString(); + var realmName = UUID.randomUUID().toString(); + var clientId = UUID.randomUUID().toString(); + var type = EventType.LOGIN_ERROR; + + when(session.realms()).thenReturn(realmProvider); + when(realmProvider.getRealm(realmId)).thenReturn(realmModel); + when(realmModel.getName()).thenReturn(realmName); + + listener(true).onEvent(toEvent(realmId, clientId, type, null)); + assertEvent(realmName, clientId, type.toString(), ""); + } + + @DisplayName("replace(true) - context is empty") + @Test + void replaceFieldsContextEmpty() { + + var realmId = UUID.randomUUID().toString(); + var realmName = UUID.randomUUID().toString(); + var clientId = UUID.randomUUID().toString(); + var type = EventType.LOGIN_ERROR; + + when(session.getContext()).thenReturn(context); + when(session.realms()).thenReturn(realmProvider); + when(realmProvider.getRealm(realmId)).thenReturn(realmModel); + when(realmModel.getName()).thenReturn(realmName); + + listener(true).onEvent(toEvent(realmId, clientId, type, null)); + assertEvent(realmName, clientId, type.toString(), ""); + } + + @DisplayName("replace(true) - realmId is unknown") + @Test + void replaceFieldsRealmIdUnknown() { + + var realmId = UUID.randomUUID().toString(); + var clientId = UUID.randomUUID().toString(); + var type = EventType.LOGIN_ERROR; + + when(session.getContext()).thenReturn(context); + when(session.realms()).thenReturn(realmProvider); + when(context.getRealm()).thenReturn(realmModel); + when(realmModel.getId()).thenReturn(UUID.randomUUID().toString()); + + listener(true).onEvent(toEvent(realmId, clientId, type, null)); + assertEvent(realmId, clientId, type.toString(), ""); + } + @DisplayName("replace(false) - without error") @Test void notReplaceWithoutError() { @@ -221,6 +276,58 @@ public class MicrometerEventListenerTest { assertAdminEvent(realmName, "", "", ""); } + @DisplayName("replace(true) - context is null") + @Test + void replaceFieldsContextNull() { + + var realmId = UUID.randomUUID().toString(); + var realmName = UUID.randomUUID().toString(); + var resource = ResourceType.USER; + var operation = OperationType.CREATE; + + when(session.realms()).thenReturn(realmProvider); + when(realmProvider.getRealm(realmId)).thenReturn(realmModel); + when(realmModel.getName()).thenReturn(realmName); + + listener(true).onEvent(toAdminEvent(realmId, resource, operation, null), false); + assertAdminEvent(realmName, resource.toString(), operation.toString(), ""); + } + + @DisplayName("replace(true) - context is empty") + @Test + void replaceFieldsContextEmpty() { + + var realmId = UUID.randomUUID().toString(); + var realmName = UUID.randomUUID().toString(); + var resource = ResourceType.USER; + var operation = OperationType.CREATE; + + when(session.getContext()).thenReturn(context); + when(session.realms()).thenReturn(realmProvider); + when(realmProvider.getRealm(realmId)).thenReturn(realmModel); + when(realmModel.getName()).thenReturn(realmName); + + listener(true).onEvent(toAdminEvent(realmId, resource, operation, null), false); + assertAdminEvent(realmName, resource.toString(), operation.toString(), ""); + } + + @DisplayName("replace(true) - realmId is unknown") + @Test + void replaceFieldsRealmIdUnknown() { + + var realmId = UUID.randomUUID().toString(); + var resource = ResourceType.USER; + var operation = OperationType.CREATE; + + when(session.getContext()).thenReturn(context); + when(session.realms()).thenReturn(realmProvider); + when(context.getRealm()).thenReturn(realmModel); + when(realmModel.getId()).thenReturn(UUID.randomUUID().toString()); + + listener(true).onEvent(toAdminEvent(realmId, resource, operation, null), false); + assertAdminEvent(realmId, resource.toString(), operation.toString(), ""); + } + @DisplayName("replace(false) - without error") @Test void noReplaceWithoutError() {