KEYCLOAK-8080 Audit the realm event configuration change
This commit is contained in:
parent
dc8268b30c
commit
1b88eaf817
4 changed files with 148 additions and 24 deletions
|
@ -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) {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue