Details
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/AdminEventBuilder.java b/services/src/main/java/org/keycloak/services/resources/admin/AdminEventBuilder.java
index 39be678..2d5ca2a 100644
--- a/services/src/main/java/org/keycloak/services/resources/admin/AdminEventBuilder.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/AdminEventBuilder.java
@@ -34,15 +34,16 @@ import org.keycloak.util.JsonSerialization;
import javax.ws.rs.core.UriInfo;
import java.io.IOException;
-import java.util.LinkedList;
-import java.util.List;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
public class AdminEventBuilder {
protected static final Logger logger = Logger.getLogger(AdminEventBuilder.class);
private EventStoreProvider store;
- private List<EventListenerProvider> listeners;
+ private Map<String, EventListenerProvider> listeners;
private RealmModel realm;
private AdminEvent adminEvent;
@@ -50,26 +51,9 @@ public class AdminEventBuilder {
this.realm = realm;
adminEvent = new AdminEvent();
- if (realm.isAdminEventsEnabled()) {
- EventStoreProvider store = session.getProvider(EventStoreProvider.class);
- if (store != null) {
- this.store = store;
- } else {
- ServicesLogger.LOGGER.noEventStoreProvider();
- }
- }
-
- if (realm.getEventsListeners() != null && !realm.getEventsListeners().isEmpty()) {
- this.listeners = new LinkedList<>();
- for (String id : realm.getEventsListeners()) {
- EventListenerProvider listener = session.getProvider(EventListenerProvider.class, id);
- if (listener != null) {
- listeners.add(listener);
- } else {
- ServicesLogger.LOGGER.providerNotFound(id);
- }
- }
- }
+ this.listeners = new HashMap<>();
+ updateStore(session);
+ addListeners(session);
authRealm(auth.getRealm());
authClient(auth.getClient());
@@ -87,6 +71,45 @@ public class AdminEventBuilder {
return this;
}
+ /**
+ * Refreshes the builder assuming that the realm event information has
+ * changed. Thought to be used when the updateRealmEventsConfig has
+ * modified the events configuration. Now the store and the listeners are
+ * updated to have previous and new setup.
+ * @param session The session
+ * @return The same builder
+ */
+ public AdminEventBuilder refreshRealmEventsConfig(KeycloakSession session) {
+ return this.updateStore(session).addListeners(session);
+ }
+
+ private AdminEventBuilder updateStore(KeycloakSession session) {
+ if (realm.isAdminEventsEnabled() && store == null) {
+ this.store = session.getProvider(EventStoreProvider.class);
+ if (store == null) {
+ ServicesLogger.LOGGER.noEventStoreProvider();
+ }
+ }
+ return this;
+ }
+
+ private AdminEventBuilder addListeners(KeycloakSession session) {
+ Set<String> extraListeners = realm.getEventsListeners();
+ if (extraListeners != null && !extraListeners.isEmpty()) {
+ for (String id : extraListeners) {
+ if (!listeners.containsKey(id)) {
+ EventListenerProvider listener = session.getProvider(EventListenerProvider.class, id);
+ if (listener != null) {
+ listeners.put(id, listener);
+ } else {
+ ServicesLogger.LOGGER.providerNotFound(id);
+ }
+ }
+ }
+ }
+ return this;
+ }
+
public AdminEventBuilder operation(OperationType operationType) {
adminEvent.setOperationType(operationType);
return this;
@@ -220,7 +243,7 @@ public class AdminEventBuilder {
}
if (listeners != null) {
- for (EventListenerProvider l : listeners) {
+ for (EventListenerProvider l : listeners.values()) {
try {
l.onEvent(adminEvent, includeRepresentation);
} catch (Throwable t) {
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
index 585e287..683b44a 100644
--- a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
@@ -680,6 +680,11 @@ public class RealmAdminResource {
logger.debug("updating realm events config: " + realm.getName());
new RealmManager(session).updateRealmEventsConfig(rep, realm);
+ adminEvent.operation(OperationType.UPDATE).resource(ResourceType.REALM).realm(realm)
+ .resourcePath(session.getContext().getUri()).representation(rep)
+ // refresh the builder to consider old and new config
+ .refreshRealmEventsConfig(session)
+ .success();
}
/**
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/event/AdminEventTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/event/AdminEventTest.java
index ce23308..90e5125 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/event/AdminEventTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/event/AdminEventTest.java
@@ -39,6 +39,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import org.keycloak.representations.idm.RealmEventsConfigRepresentation;
/**
* Test getting and filtering admin events.
@@ -153,4 +154,37 @@ public class AdminEventTest extends AbstractEventTest {
assertTrue(realm.getAdminEvents(null, null, null, null, null, null, null, null, 0, 1000).size() >= 110);
}
+ private void checkupdateRealmEventsConfigEvent(int size) {
+ List<AdminEventRepresentation> events = events();
+ assertEquals(size, events.size());
+ AdminEventRepresentation event = events().get(0);assertEquals("UPDATE", event.getOperationType());
+ assertEquals(realmName(), event.getRealmId());
+ assertEquals("events/config", event.getResourcePath());
+ assertEquals("master", event.getAuthDetails().getRealmId());
+ assertNotNull(event.getRepresentation());
+ }
+
+ @Test
+ public void updateRealmEventsConfig() {
+ // change from OFF to ON should be stored
+ configRep.setAdminEventsDetailsEnabled(Boolean.TRUE);
+ configRep.setAdminEventsEnabled(Boolean.TRUE);
+ saveConfig();
+ checkupdateRealmEventsConfigEvent(1);
+
+ // any other change should be store too
+ configRep.setEventsEnabled(Boolean.TRUE);
+ saveConfig();
+ checkupdateRealmEventsConfigEvent(2);
+
+ // change from ON to OFF should be stored too
+ configRep.setAdminEventsEnabled(Boolean.FALSE);
+ saveConfig();
+ checkupdateRealmEventsConfigEvent(3);
+
+ // another change should not be stored cos it was OFF already
+ configRep.setAdminEventsDetailsEnabled(Boolean.FALSE);
+ saveConfig();
+ assertEquals(3, events().size());
+ }
}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/realm/RealmTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/realm/RealmTest.java
index 56d2bee..b9c43a6 100755
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/realm/RealmTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/realm/RealmTest.java
@@ -75,6 +75,10 @@ import java.util.Map;
import javax.ws.rs.BadRequestException;
import static org.junit.Assert.*;
+import org.keycloak.events.EventType;
+import org.keycloak.events.log.JBossLoggingEventListenerProviderFactory;
+import org.keycloak.representations.idm.RealmEventsConfigRepresentation;
+import org.keycloak.testsuite.events.EventsListenerProviderFactory;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
@@ -282,6 +286,64 @@ public class RealmTest extends AbstractAdminTest {
adminClient.realms().realm("test-immutable").remove();
}
+ private RealmEventsConfigRepresentation copyRealmEventsConfigRepresentation(RealmEventsConfigRepresentation rep) {
+ RealmEventsConfigRepresentation recr = new RealmEventsConfigRepresentation();
+ recr.setEnabledEventTypes(rep.getEnabledEventTypes());
+ recr.setEventsListeners(rep.getEventsListeners());
+ recr.setEventsExpiration(rep.getEventsExpiration());
+ recr.setEventsEnabled(rep.isEventsEnabled());
+ recr.setAdminEventsEnabled(rep.isAdminEventsEnabled());
+ recr.setAdminEventsDetailsEnabled(rep.isAdminEventsDetailsEnabled());
+ return recr;
+ }
+
+ private void checkRealmEventsConfigRepresentation(RealmEventsConfigRepresentation expected,
+ RealmEventsConfigRepresentation actual) {
+ assertEquals(expected.getEnabledEventTypes().size(), actual.getEnabledEventTypes().size());
+ assertTrue(actual.getEnabledEventTypes().containsAll(expected.getEnabledEventTypes()));
+ assertEquals(expected.getEventsListeners().size(), actual.getEventsListeners().size());
+ assertTrue(actual.getEventsListeners().containsAll(expected.getEventsListeners()));
+ assertEquals(expected.getEventsExpiration(), actual.getEventsExpiration());
+ assertEquals(expected.isEventsEnabled(), actual.isEventsEnabled());
+ assertEquals(expected.isAdminEventsEnabled(), actual.isAdminEventsEnabled());
+ assertEquals(expected.isAdminEventsDetailsEnabled(), actual.isAdminEventsDetailsEnabled());
+ }
+
+ @Test
+ public void updateRealmEventsConfig() {
+ RealmEventsConfigRepresentation rep = realm.getRealmEventsConfig();
+ RealmEventsConfigRepresentation repOrig = copyRealmEventsConfigRepresentation(rep);
+
+ // the "event-queue" listener should be enabled by default
+ assertTrue("event-queue should be enabled initially", rep.getEventsListeners().contains(EventsListenerProviderFactory.PROVIDER_ID));
+
+ // first modification => remove "event-queue", should be sent to the queue
+ rep.setEnabledEventTypes(Arrays.asList(EventType.LOGIN.name(), EventType.LOGIN_ERROR.name()));
+ rep.setEventsListeners(Arrays.asList(JBossLoggingEventListenerProviderFactory.ID));
+ rep.setEventsExpiration(36000L);
+ rep.setEventsEnabled(true);
+ rep.setAdminEventsEnabled(true);
+ rep.setAdminEventsDetailsEnabled(true);
+ adminClient.realms().realm(REALM_NAME).updateRealmEventsConfig(rep);
+ assertAdminEvents.assertEvent(realmId, OperationType.UPDATE, "events/config", rep, ResourceType.REALM);
+ RealmEventsConfigRepresentation actual = realm.getRealmEventsConfig();
+ checkRealmEventsConfigRepresentation(rep, actual);
+
+ // second modification => should not be sent cos event-queue was removed in the first mod
+ rep.setEnabledEventTypes(Arrays.asList(EventType.LOGIN.name(),
+ EventType.LOGIN_ERROR.name(), EventType.CLIENT_LOGIN.name()));
+ adminClient.realms().realm(REALM_NAME).updateRealmEventsConfig(rep);
+ assertAdminEvents.assertEmpty();
+ actual = realm.getRealmEventsConfig();
+ checkRealmEventsConfigRepresentation(rep, actual);
+
+ // third modification => restore queue => should be sent and recovered
+ adminClient.realms().realm(REALM_NAME).updateRealmEventsConfig(repOrig);
+ assertAdminEvents.assertEvent(realmId, OperationType.UPDATE, "events/config", repOrig, ResourceType.REALM);
+ actual = realm.getRealmEventsConfig();
+ checkRealmEventsConfigRepresentation(repOrig, actual);
+ }
+
@Test
public void updateRealm() {
// first change