Add admin events for realm create/delete. (#10831)

Closes #10733
This commit is contained in:
Tero Saarni 2023-03-07 16:57:06 +02:00 committed by GitHub
parent 96c1cf3c49
commit 9052ec2b02
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 63 additions and 6 deletions

View file

@ -450,6 +450,12 @@ public class RealmAdminResource {
if (!new RealmManager(session).removeRealm(realm)) { if (!new RealmManager(session).removeRealm(realm)) {
throw new NotFoundException("Realm doesn't exist"); throw new NotFoundException("Realm doesn't exist");
} }
// The delete event is associated with the realm of the user executing the operation,
// instead of the realm being deleted.
AdminEventBuilder deleteAdminEvent = new AdminEventBuilder(auth.adminAuth().getRealm(), auth.adminAuth(), session, connection);
deleteAdminEvent.operation(OperationType.DELETE).resource(ResourceType.REALM)
.realm(auth.adminAuth().getRealm().getId()).resourcePath(realm.getName()).success();
} }
/** /**

View file

@ -19,12 +19,15 @@ package org.keycloak.services.resources.admin;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.jboss.resteasy.annotations.cache.NoCache; import org.jboss.resteasy.annotations.cache.NoCache;
import org.keycloak.common.ClientConnection; import org.keycloak.common.ClientConnection;
import org.keycloak.events.admin.OperationType;
import org.keycloak.events.admin.ResourceType;
import org.keycloak.models.AdminRoles; import org.keycloak.models.AdminRoles;
import org.keycloak.models.ClientModel; import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ModelDuplicateException; import org.keycloak.models.ModelDuplicateException;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
import org.keycloak.models.utils.ModelToRepresentation; import org.keycloak.models.utils.ModelToRepresentation;
import org.keycloak.models.utils.StripSecretsUtils;
import org.keycloak.policy.PasswordPolicyNotMetException; import org.keycloak.policy.PasswordPolicyNotMetException;
import org.keycloak.protocol.oidc.TokenManager; import org.keycloak.protocol.oidc.TokenManager;
import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.representations.idm.RealmRepresentation;
@ -134,6 +137,15 @@ public class RealmsAdminResource {
URI location = AdminRoot.realmsUrl(session.getContext().getUri()).path(realm.getName()).build(); URI location = AdminRoot.realmsUrl(session.getContext().getUri()).path(realm.getName()).build();
logger.debugv("imported realm success, sending back: {0}", location.toString()); logger.debugv("imported realm success, sending back: {0}", location.toString());
// The create event is associated with the realm of the user executing the operation,
// instead of the realm being created.
AdminEventBuilder adminEvent = new AdminEventBuilder(auth.getRealm(), auth, session, clientConnection);
adminEvent.resource(ResourceType.REALM).realm(auth.getRealm().getId()).operation(OperationType.CREATE)
.resourcePath(realm.getName())
.representation(
StripSecretsUtils.strip(ModelToRepresentation.toRepresentation(session, realm, false)))
.success();
return Response.created(location).build(); return Response.created(location).build();
} catch (ModelDuplicateException e) { } catch (ModelDuplicateException e) {
logger.error("Conflict detected", e); logger.error("Conflict detected", e);

View file

@ -23,6 +23,7 @@ import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.events.admin.OperationType; import org.keycloak.events.admin.OperationType;
import org.keycloak.representations.idm.AdminEventRepresentation; import org.keycloak.representations.idm.AdminEventRepresentation;
import org.keycloak.representations.idm.AuthDetailsRepresentation; import org.keycloak.representations.idm.AuthDetailsRepresentation;
import org.keycloak.representations.idm.RealmEventsConfigRepresentation;
import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.UserRepresentation; import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.testsuite.admin.ApiUtil; import org.keycloak.testsuite.admin.ApiUtil;
@ -39,7 +40,7 @@ import static org.hamcrest.Matchers.greaterThanOrEqualTo;
import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.nullValue; import static org.hamcrest.Matchers.nullValue;
import static org.junit.Assert.assertThat; import static org.hamcrest.MatcherAssert.assertThat;
import static org.keycloak.testsuite.auth.page.AuthRealm.MASTER; import static org.keycloak.testsuite.auth.page.AuthRealm.MASTER;
/** /**
@ -188,7 +189,7 @@ public class AdminEventTest extends AbstractEventTest {
private void checkUpdateRealmEventsConfigEvent(int size) { private void checkUpdateRealmEventsConfigEvent(int size) {
List<AdminEventRepresentation> events = events(); List<AdminEventRepresentation> events = events();
assertThat(events.size(), is(equalTo(size))); assertThat(events.size(), is(equalTo(size)));
AdminEventRepresentation event = events().get(0); AdminEventRepresentation event = events().get(0);
assertThat(event.getOperationType(), is(equalTo("UPDATE"))); assertThat(event.getOperationType(), is(equalTo("UPDATE")));
assertThat(event.getRealmId(), is(equalTo(realmName()))); assertThat(event.getRealmId(), is(equalTo(realmName())));
@ -196,7 +197,7 @@ public class AdminEventTest extends AbstractEventTest {
assertThat(event.getAuthDetails().getRealmId(), is(equalTo(masterRealmId))); assertThat(event.getAuthDetails().getRealmId(), is(equalTo(masterRealmId)));
assertThat(event.getRepresentation(), is(notNullValue())); assertThat(event.getRepresentation(), is(notNullValue()));
} }
@Test @Test
public void updateRealmEventsConfig() { public void updateRealmEventsConfig() {
// change from OFF to ON should be stored // change from OFF to ON should be stored
@ -204,20 +205,58 @@ public class AdminEventTest extends AbstractEventTest {
configRep.setAdminEventsEnabled(Boolean.TRUE); configRep.setAdminEventsEnabled(Boolean.TRUE);
saveConfig(); saveConfig();
checkUpdateRealmEventsConfigEvent(1); checkUpdateRealmEventsConfigEvent(1);
// any other change should be store too // any other change should be store too
configRep.setEventsEnabled(Boolean.TRUE); configRep.setEventsEnabled(Boolean.TRUE);
saveConfig(); saveConfig();
checkUpdateRealmEventsConfigEvent(2); checkUpdateRealmEventsConfigEvent(2);
// change from ON to OFF should be stored too // change from ON to OFF should be stored too
configRep.setAdminEventsEnabled(Boolean.FALSE); configRep.setAdminEventsEnabled(Boolean.FALSE);
saveConfig(); saveConfig();
checkUpdateRealmEventsConfigEvent(3); checkUpdateRealmEventsConfigEvent(3);
// another change should not be stored cos it was OFF already // another change should not be stored cos it was OFF already
configRep.setAdminEventsDetailsEnabled(Boolean.FALSE); configRep.setAdminEventsDetailsEnabled(Boolean.FALSE);
saveConfig(); saveConfig();
assertThat(events().size(), is(equalTo(3))); assertThat(events().size(), is(equalTo(3)));
} }
@Test
public void createAndDeleteRealm() {
// Enable admin events on "master" realm, since realm create/delete events will be stored in realm of
// the authenticated user who executes the operations (admin in master realm),
RealmResource master = adminClient.realm(MASTER);
RealmEventsConfigRepresentation masterConfig = master.getRealmEventsConfig();
masterConfig.setAdminEventsDetailsEnabled(true);
masterConfig.setAdminEventsEnabled(true);
master.updateRealmEventsConfig(masterConfig);
master.clearAdminEvents();
// Create realm.
RealmRepresentation realm = new RealmRepresentation();
realm.setId("test-realm");
realm.setRealm("test-realm");
importRealm(realm);
// Delete realm.
removeRealm("test-realm");
// Check that events were logged.
List<AdminEventRepresentation> events = master.getAdminEvents();
assertThat(events.size(), is(equalTo(2)));
AdminEventRepresentation createEvent = events.get(1);
assertThat(createEvent.getOperationType(), is(equalTo("CREATE")));
assertThat(createEvent.getRealmId(), is(equalTo(masterRealmId)));
assertThat(createEvent.getResourceType(), is(equalTo("REALM")));
assertThat(createEvent.getResourcePath(), is(equalTo("test-realm")));
assertThat(createEvent.getRepresentation(), is(notNullValue()));
AdminEventRepresentation deleteEvent = events.get(0);
assertThat(deleteEvent.getOperationType(), is(equalTo("DELETE")));
assertThat(deleteEvent.getRealmId(), is(equalTo(masterRealmId)));
assertThat(deleteEvent.getResourceType(), is(equalTo("REALM")));
assertThat(deleteEvent.getResourcePath(), is(equalTo("test-realm")));
}
} }