Skip to content

Commit

Permalink
[Test framework] AdminEventsSupplier support (keycloak#34704)
Browse files Browse the repository at this point in the history
* [Test framework] AdminEventsSupplier support

Signed-off-by: Lukas Hanusovsky <[email protected]>

* Update test-framework/core/src/main/java/org/keycloak/test/framework/events/AdminEventsParser.java

Co-authored-by: Stian Thorgersen <[email protected]>
Signed-off-by: Lukas Hanusovsky <[email protected]>

---------

Signed-off-by: Lukas Hanusovsky <[email protected]>
Signed-off-by: Lukas Hanusovsky <[email protected]>
Co-authored-by: Stian Thorgersen <[email protected]>
  • Loading branch information
lhanusov and stianst authored Nov 7, 2024
1 parent 83065b8 commit 1192dc3
Show file tree
Hide file tree
Showing 7 changed files with 250 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import org.keycloak.test.framework.database.DevFileDatabaseSupplier;
import org.keycloak.test.framework.database.DevMemDatabaseSupplier;
import org.keycloak.test.framework.database.TestDatabase;
import org.keycloak.test.framework.events.AdminEventsSupplier;
import org.keycloak.test.framework.events.EventsSupplier;
import org.keycloak.test.framework.events.SysLogServerSupplier;
import org.keycloak.test.framework.injection.Supplier;
Expand Down Expand Up @@ -35,7 +36,8 @@ public class CoreTestFrameworkExtension implements TestFrameworkExtension {
new DevFileDatabaseSupplier(),
new OAuthClientSupplier(),
new SysLogServerSupplier(),
new EventsSupplier()
new EventsSupplier(),
new AdminEventsSupplier()
);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package org.keycloak.test.framework.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface InjectAdminEvents {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package org.keycloak.test.framework.events;

import org.keycloak.events.admin.AdminEvent;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;

public class AdminEvents implements SysLogListener{

private final BlockingQueue<AdminEvent> adminEvents = new LinkedBlockingQueue<>();

public AdminEvent poll() {
try {
return adminEvents.poll(30, TimeUnit.SECONDS);
} catch (InterruptedException e) {
return null;
}
}

public void clear() {
adminEvents.clear();
}

@Override
public void onLog(SysLog sysLog) {
AdminEvent adminEvent = AdminEventsParser.parse(sysLog);
if (adminEvent != null) {
adminEvents.add(adminEvent);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package org.keycloak.test.framework.events;

import org.keycloak.events.admin.AdminEvent;
import org.keycloak.events.admin.AuthDetails;
import org.keycloak.events.admin.OperationType;
import org.keycloak.events.admin.ResourceType;

import java.util.HashMap;
import java.util.Map;

public class AdminEventsParser {

private AdminEventsParser() {
}

public static AdminEvent parse(SysLog sysLog) {
if (!sysLog.getCategory().equals("org.keycloak.events")) {
return null;
}

String message = sysLog.getMessage().substring(sysLog.getMessage().indexOf(')') + 1).trim();

if (!message.startsWith("operationType=")) {
return null;
}

String[] split = message.split(", ");

Map<String, String> eventMap = new HashMap<>();
for (String s : split) {
String[] split1 = s.split("=");
eventMap.put(split1[0], split1[1].substring(1, split1[1].length() - 1));
}

AdminEvent adminEvent = new AdminEvent();
adminEvent.setTime(sysLog.getTimestamp().getTime() / 1000);
adminEvent.setAuthDetails(new AuthDetails());

for (Map.Entry<String, String> e : eventMap.entrySet()) {
switch (e.getKey()) {
case "operationType":
adminEvent.setOperationType(OperationType.valueOf(e.getValue()));
break;
case "realmId":
adminEvent.setRealmId(e.getValue());
adminEvent.getAuthDetails().setRealmId(e.getValue());
break;
case "realmName":
adminEvent.getAuthDetails().setRealmName(e.getValue());
break;
case "clientId":
adminEvent.getAuthDetails().setClientId(e.getValue());
break;
case "userId":
adminEvent.getAuthDetails().setUserId(e.getValue());
break;
case "ipAddress":
adminEvent.getAuthDetails().setIpAddress(e.getValue());
break;
case "resourceType":
adminEvent.setResourceType(ResourceType.valueOf(e.getValue()));
break;
case "resourcePath":
adminEvent.setResourcePath(e.getValue());
break;
case "error":
adminEvent.setError(e.getValue());
break;
default:
break;
}
}

return adminEvent;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package org.keycloak.test.framework.events;

import org.keycloak.test.framework.annotations.InjectAdminEvents;
import org.keycloak.test.framework.injection.InstanceContext;
import org.keycloak.test.framework.injection.LifeCycle;
import org.keycloak.test.framework.injection.RequestedInstance;
import org.keycloak.test.framework.injection.Supplier;

public class AdminEventsSupplier implements Supplier<AdminEvents, InjectAdminEvents> {
@Override
public Class<InjectAdminEvents> getAnnotationClass() {
return InjectAdminEvents.class;
}

@Override
public Class<AdminEvents> getValueType() {
return AdminEvents.class;
}

@Override
public AdminEvents getValue(InstanceContext<AdminEvents, InjectAdminEvents> instanceContext) {
AdminEvents adminEvents = new AdminEvents();
SysLogServer sysLogServer = instanceContext.getDependency(SysLogServer.class);
instanceContext.addNote("server", sysLogServer);
return adminEvents;
}

@Override
public void onBeforeEach(InstanceContext<AdminEvents, InjectAdminEvents> instanceContext) {
instanceContext.getNote("server", SysLogServer.class).addListener(instanceContext.getValue());
}

@Override
public LifeCycle getDefaultLifecycle() {
return LifeCycle.METHOD;
}

@Override
public void close(InstanceContext<AdminEvents, InjectAdminEvents> instanceContext) {
instanceContext.getNote("server", SysLogServer.class).removeListener(instanceContext.getValue());
}

@Override
public boolean compatible(InstanceContext<AdminEvents, InjectAdminEvents> a, RequestedInstance<AdminEvents, InjectAdminEvents> b) {
return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ public Event poll() {
}
}

public void clear() {
events.clear();
}

@Override
public void onLog(SysLog sysLog) {
Event event = EventParser.parse(sysLog);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package org.keycloak.test.examples;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.keycloak.admin.client.Keycloak;
import org.keycloak.events.admin.OperationType;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.test.framework.annotations.InjectAdminClient;
import org.keycloak.test.framework.annotations.InjectAdminEvents;
import org.keycloak.test.framework.annotations.InjectRealm;
import org.keycloak.test.framework.annotations.KeycloakIntegrationTest;
import org.keycloak.test.framework.events.AdminEvents;
import org.keycloak.test.framework.realm.ManagedRealm;
import org.keycloak.test.framework.server.KeycloakTestServerConfig;

@KeycloakIntegrationTest(config = AdminEventsTest.ServerConfig.class)
public class AdminEventsTest {

@InjectAdminEvents
private AdminEvents adminEvents;

@InjectAdminClient
private Keycloak adminClient;

@InjectRealm
private ManagedRealm realm;

@Test
public void testAdminEventOnUserCreateAndDelete() {

String userName = "create_user";

UserRepresentation userRep = new UserRepresentation();
userRep.setUsername(userName);
userRep.setEnabled(true);

adminClient.realm(realm.getName()).users().create(userRep);

Assertions.assertEquals(OperationType.CREATE, adminEvents.poll().getOperationType());

String userId = adminClient.realm(realm.getName()).users().search(userName).get(0).getId();

adminClient.realm(realm.getName()).users().delete(userId);

Assertions.assertEquals(OperationType.DELETE, adminEvents.poll().getOperationType());
}

@Test
public void testAdminEventOnRealmCreateAndUpdate() {

String realmName = "create_realm";

RealmRepresentation realmRep = new RealmRepresentation();
realmRep.setRealm(realmName);
realmRep.setEnabled(true);

adminClient.realms().create(realmRep);

Assertions.assertEquals(OperationType.CREATE, adminEvents.poll().getOperationType());

RealmRepresentation realmRep2 = adminClient.realms().realm(realmName).toRepresentation();
realmRep2.setEnabled(false);

adminClient.realms().realm(realmName).update(realmRep2);

Assertions.assertEquals(OperationType.UPDATE, adminEvents.poll().getOperationType());
}

public static class ServerConfig implements KeycloakTestServerConfig {
@Override
public boolean enableSysLog() {
return true;
}
}

}

0 comments on commit 1192dc3

Please sign in to comment.