Issue #8749: Add an option to control the order of the event query and admin event query

This commit is contained in:
Christoph Leistert 2021-11-15 08:25:26 +01:00 committed by Hynek Mlnařík
parent 1d2d3e5ca5
commit 7e5b45f999
13 changed files with 486 additions and 127 deletions

View file

@ -50,7 +50,8 @@ public class JpaAdminEventQuery implements AdminEventQuery {
private final ArrayList<Predicate> predicates;
private Integer firstResult;
private Integer maxResults;
private boolean orderByDescTime = true;
public JpaAdminEventQuery(EntityManager em) {
this.em = em;
@ -143,13 +144,29 @@ public class JpaAdminEventQuery implements AdminEventQuery {
return this;
}
@Override
public AdminEventQuery orderByDescTime() {
orderByDescTime = true;
return this;
}
@Override
public AdminEventQuery orderByAscTime() {
orderByDescTime = false;
return this;
}
@Override
public Stream<AdminEvent> getResultStream() {
if (!predicates.isEmpty()) {
cq.where(cb.and(predicates.toArray(new Predicate[predicates.size()])));
}
cq.orderBy(cb.desc(root.get("time")));
if (orderByDescTime) {
cq.orderBy(cb.desc(root.get("time")));
} else {
cq.orderBy(cb.asc(root.get("time")));
}
TypedQuery<AdminEventEntity> query = em.createQuery(cq);

View file

@ -48,6 +48,7 @@ public class JpaEventQuery implements EventQuery {
private final ArrayList<Predicate> predicates;
private Integer firstResult;
private Integer maxResults;
private boolean orderByDescTime = true;
public JpaEventQuery(EntityManager em) {
this.em = em;
@ -116,13 +117,29 @@ public class JpaEventQuery implements EventQuery {
return this;
}
@Override
public EventQuery orderByDescTime() {
orderByDescTime = true;
return this;
}
@Override
public EventQuery orderByAscTime() {
orderByDescTime = false;
return this;
}
@Override
public Stream<Event> getResultStream() {
if (!predicates.isEmpty()) {
cq.where(cb.and(predicates.toArray(new Predicate[predicates.size()])));
}
cq.orderBy(cb.desc(root.get("time")));
if(orderByDescTime) {
cq.orderBy(cb.desc(root.get("time")));
} else {
cq.orderBy(cb.asc(root.get("time")));
}
TypedQuery<EventEntity> query = em.createQuery(cq);

View file

@ -35,6 +35,7 @@ import static org.keycloak.models.map.storage.ModelCriteriaBuilder.Operator.GE;
import static org.keycloak.models.map.storage.ModelCriteriaBuilder.Operator.IN;
import static org.keycloak.models.map.storage.ModelCriteriaBuilder.Operator.LE;
import static org.keycloak.models.map.storage.ModelCriteriaBuilder.Operator.LIKE;
import static org.keycloak.models.map.storage.QueryParameters.Order.ASCENDING;
import static org.keycloak.models.map.storage.QueryParameters.Order.DESCENDING;
import static org.keycloak.models.map.storage.criteria.DefaultModelCriteria.criteria;
@ -42,6 +43,7 @@ public class MapAdminEventQuery implements AdminEventQuery {
private Integer firstResult;
private Integer maxResults;
private QueryParameters.Order order = DESCENDING;
private DefaultModelCriteria<AdminEvent> mcb = criteria();
private final Function<QueryParameters<AdminEvent>, Stream<AdminEvent>> resultProducer;
@ -121,12 +123,24 @@ public class MapAdminEventQuery implements AdminEventQuery {
return this;
}
@Override
public AdminEventQuery orderByDescTime() {
order = DESCENDING;
return this;
}
@Override
public AdminEventQuery orderByAscTime() {
order = ASCENDING;
return this;
}
@Override
public Stream<AdminEvent> getResultStream() {
return resultProducer.apply(QueryParameters.withCriteria(mcb)
.offset(firstResult)
.limit(maxResults)
.orderBy(SearchableFields.TIMESTAMP, DESCENDING)
.orderBy(SearchableFields.TIMESTAMP, order)
);
}
}

View file

@ -33,6 +33,7 @@ import static org.keycloak.models.map.storage.ModelCriteriaBuilder.Operator.EQ;
import static org.keycloak.models.map.storage.ModelCriteriaBuilder.Operator.GE;
import static org.keycloak.models.map.storage.ModelCriteriaBuilder.Operator.IN;
import static org.keycloak.models.map.storage.ModelCriteriaBuilder.Operator.LE;
import static org.keycloak.models.map.storage.QueryParameters.Order.ASCENDING;
import static org.keycloak.models.map.storage.QueryParameters.Order.DESCENDING;
import static org.keycloak.models.map.storage.criteria.DefaultModelCriteria.criteria;
@ -40,6 +41,7 @@ public class MapAuthEventQuery implements EventQuery {
private Integer firstResult;
private Integer maxResults;
private QueryParameters.Order order = DESCENDING;
private DefaultModelCriteria<Event> mcb = criteria();
private final Function<QueryParameters<Event>, Stream<Event>> resultProducer;
@ -101,11 +103,23 @@ public class MapAuthEventQuery implements EventQuery {
return this;
}
@Override
public EventQuery orderByDescTime() {
order = DESCENDING;
return this;
}
@Override
public EventQuery orderByAscTime() {
order = ASCENDING;
return this;
}
@Override
public Stream<Event> getResultStream() {
return resultProducer.apply(QueryParameters.withCriteria(mcb)
.offset(firstResult)
.limit(maxResults)
.orderBy(SearchableFields.TIMESTAMP, DESCENDING));
.orderBy(SearchableFields.TIMESTAMP, order));
}
}

View file

@ -90,6 +90,20 @@ public interface EventQuery {
*/
EventQuery maxResults(int max);
/**
* Order the result by descending time
*
* @return <code>this</code> for method chaining
*/
EventQuery orderByDescTime();
/**
* Order the result by ascending time
*
* @return <code>this</code> for method chaining
*/
EventQuery orderByAscTime();
/**
* @deprecated Use {@link #getResultStream() getResultStream} instead.
*/

View file

@ -127,6 +127,20 @@ public interface AdminEventQuery {
*/
AdminEventQuery maxResults(int max);
/**
* Order the result by descending time
*
* @return <code>this</code> for method chaining
*/
AdminEventQuery orderByDescTime();
/**
* Order the result by ascending time
*
* @return <code>this</code> for method chaining
*/
AdminEventQuery orderByAscTime();
/**
* Executes the query and returns the results
* @deprecated Use {@link #getResultStream() getResultStream} instead.

View file

@ -77,7 +77,7 @@ public class ProtectionService {
KeycloakSession keycloakSession = authorization.getKeycloakSession();
UserModel serviceAccount = keycloakSession.users().getServiceAccount(client);
AdminEventBuilder adminEvent = new AdminEventBuilder(realm, new AdminAuth(realm, identity.getAccessToken(), serviceAccount, client), keycloakSession, clientConnection);
return adminEvent.realm(realm).authClient(client).authUser(serviceAccount);
return adminEvent;
}
@Path("/permission")

View file

@ -55,7 +55,7 @@ public class AdminEventBuilder {
this.listeners = new HashMap<>();
updateStore(session);
addListeners(session);
realm(realm);
authRealm(auth.getRealm());
authClient(auth.getClient());
authUser(auth.getUser());

View file

@ -143,7 +143,7 @@ public class RealmAdminResource {
this.auth = auth;
this.realm = realm;
this.tokenManager = tokenManager;
this.adminEvent = adminEvent.realm(realm).resource(ResourceType.REALM);
this.adminEvent = adminEvent.resource(ResourceType.REALM);
}
/**
@ -701,7 +701,7 @@ 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)
adminEvent.operation(OperationType.UPDATE).resource(ResourceType.REALM)
.resourcePath(session.getContext().getUri()).representation(rep)
// refresh the builder to consider old and new config
.refreshRealmEventsConfig(session)

View file

@ -160,6 +160,31 @@ public class AdminEventTest extends AbstractEventTest {
assertThat(realm.getAdminEvents(null, null, null, null, null, null, null, null, 0, 1000).size(), is(greaterThanOrEqualTo(110)));
}
@Test
public void orderResultsTest() {
RealmResource realm = adminClient.realms().realm("test");
AdminEventRepresentation firstEvent = new AdminEventRepresentation();
firstEvent.setOperationType(OperationType.CREATE.toString());
firstEvent.setAuthDetails(new AuthDetailsRepresentation());
firstEvent.setRealmId(realm.toRepresentation().getId());
firstEvent.setTime(System.currentTimeMillis() - 1000);
AdminEventRepresentation secondEvent = new AdminEventRepresentation();
secondEvent.setOperationType(OperationType.DELETE.toString());
secondEvent.setAuthDetails(new AuthDetailsRepresentation());
secondEvent.setRealmId(realm.toRepresentation().getId());
secondEvent.setTime(System.currentTimeMillis());
testingClient.testing("test").onAdminEvent(firstEvent, false);
testingClient.testing("test").onAdminEvent(secondEvent, false);
List<AdminEventRepresentation> adminEvents = realm.getAdminEvents(null, null, null, null, null, null, null, null, null, null);
assertThat(adminEvents.size(), is(equalTo(2)));
assertThat(adminEvents.get(0).getOperationType(), is(equalTo(OperationType.DELETE.toString())));
assertThat(adminEvents.get(1).getOperationType(), is(equalTo(OperationType.CREATE.toString())));
}
private void checkUpdateRealmEventsConfigEvent(int size) {
List<AdminEventRepresentation> events = events();
assertThat(events.size(), is(equalTo(size)));

View file

@ -152,6 +152,30 @@ public class LoginEventsTest extends AbstractEventTest {
assertTrue(realm.getEvents(null, null, null, null, null, null, 0, 1000).size() >= 110);
}
@Test
public void orderResultsTest() {
RealmResource realm = adminClient.realms().realm("test");
EventRepresentation firstEvent = new EventRepresentation();
firstEvent.setRealmId(realm.toRepresentation().getId());
firstEvent.setType(EventType.LOGIN.toString());
firstEvent.setTime(System.currentTimeMillis() - 1000);
EventRepresentation secondEvent = new EventRepresentation();
secondEvent.setRealmId(realm.toRepresentation().getId());
secondEvent.setType(EventType.LOGOUT.toString());
secondEvent.setTime(System.currentTimeMillis());
testingClient.testing("test").onEvent(firstEvent);
testingClient.testing("test").onEvent(secondEvent);
List<EventRepresentation> events = realm.getEvents(null, null, null, null, null, null, null, null);
assertEquals(2, events.size());
assertEquals(EventType.LOGOUT.toString(), events.get(0).getType());
assertEquals(EventType.LOGIN.toString(), events.get(1).getType());
}
/*
Removed this test because it takes too long. The default interval for
event cleanup is 15 minutes (900 seconds). I don't have time to figure out

View file

@ -16,32 +16,30 @@
*/
package org.keycloak.testsuite.model.events;
import org.keycloak.common.ClientConnection;
import org.keycloak.common.util.Time;
import org.keycloak.events.Event;
import org.keycloak.events.EventBuilder;
import org.keycloak.events.EventStoreProvider;
import org.keycloak.events.EventType;
import org.keycloak.events.admin.AdminEvent;
import org.keycloak.models.Constants;
import org.keycloak.models.KeycloakSession;
import org.keycloak.testsuite.model.KeycloakModelTest;
import org.keycloak.testsuite.model.RequireProvider;
import org.keycloak.models.RealmModel;
import java.util.Set;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import org.junit.Test;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is;
/**
*
* @author hmlnarik
*/
import org.junit.Test;
import org.keycloak.common.ClientConnection;
import org.keycloak.events.EventStoreProvider;
import org.keycloak.events.admin.AdminEvent;
import org.keycloak.events.admin.OperationType;
import org.keycloak.events.admin.ResourceType;
import org.keycloak.models.ClientModel;
import org.keycloak.models.Constants;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.delegate.ClientModelLazyDelegate;
import org.keycloak.models.utils.UserModelDelegate;
import org.keycloak.services.resources.admin.AdminAuth;
import org.keycloak.services.resources.admin.AdminEventBuilder;
import org.keycloak.testsuite.model.KeycloakModelTest;
import org.keycloak.testsuite.model.RequireProvider;
import java.util.List;
import java.util.stream.Collectors;
@RequireProvider(EventStoreProvider.class)
public class AdminEventQueryTest extends KeycloakModelTest {
@ -56,128 +54,71 @@ public class AdminEventQueryTest extends KeycloakModelTest {
@Override
public void cleanEnvironment(KeycloakSession s) {
EventStoreProvider eventStore = s.getProvider(EventStoreProvider.class);
eventStore.clearAdmin(s.realms().getRealm(realmId));
s.realms().removeRealm(realmId);
}
@Test
public void testClear() {
inRolledBackTransaction(null, (session, t) -> {
EventStoreProvider eventStore = session.getProvider(EventStoreProvider.class);
eventStore.clear();
});
}
private Event createAuthEventForUser(RealmModel realm, String user) {
return new EventBuilder(realm, null, DummyClientConnection.DUMMY_CONNECTION)
.event(EventType.LOGIN)
.user(user)
.getEvent();
}
@Test
public void testQuery() {
withRealm(realmId, (session, realm) -> {
EventStoreProvider eventStore = session.getProvider(EventStoreProvider.class);
eventStore.onEvent(createAuthEventForUser(realm,"u1"));
eventStore.onEvent(createAuthEventForUser(realm,"u2"));
eventStore.onEvent(createAuthEventForUser(realm,"u3"));
eventStore.onEvent(createAuthEventForUser(realm,"u4"));
return realm.getId();
eventStore.onEvent(createClientEvent(realm, OperationType.CREATE), false);
eventStore.onEvent(createClientEvent(realm, OperationType.UPDATE), false);
eventStore.onEvent(createClientEvent(realm, OperationType.DELETE), false);
eventStore.onEvent(createClientEvent(realm, OperationType.CREATE), false);
return null;
});
withRealm(realmId, (session, realm) -> {
EventStoreProvider eventStore = session.getProvider(EventStoreProvider.class);
assertThat(eventStore.createQuery()
.firstResult(2)
.getResultStream()
.collect(Collectors.counting()),
is(2L)
);
assertThat(eventStore.createAdminQuery()
.firstResult(2)
.getResultStream()
.collect(Collectors.counting()),
is(2L));
return null;
});
}
@Test
@RequireProvider(value = EventStoreProvider.class, only = "map")
public void testEventExpiration() {
public void testQueryOrder() {
withRealm(realmId, (session, realm) -> {
EventStoreProvider eventStore = session.getProvider(EventStoreProvider.class);
AdminEvent firstEvent = createClientEvent(realm, OperationType.CREATE);
firstEvent.setTime(1L);
AdminEvent secondEvent = createClientEvent(realm, OperationType.DELETE);
secondEvent.setTime(2L);
eventStore.onEvent(firstEvent, false);
eventStore.onEvent(secondEvent, false);
List<AdminEvent> adminEventsAsc = eventStore.createAdminQuery()
.orderByAscTime()
.getResultStream()
.collect(Collectors.toList());
assertThat(adminEventsAsc.size(), is(2));
assertThat(adminEventsAsc.get(0).getOperationType(), is(OperationType.CREATE));
assertThat(adminEventsAsc.get(1).getOperationType(), is(OperationType.DELETE));
// Set expiration so no event is valid
realm.setEventsExpiration(5);
Event e = createAuthEventForUser(realm, "u1");
eventStore.onEvent(e);
// Set expiration to 1000 seconds
realm.setEventsExpiration(1000);
e = createAuthEventForUser(realm, "u2");
eventStore.onEvent(e);
List<AdminEvent> adminEventsDesc = eventStore.createAdminQuery()
.orderByDescTime()
.getResultStream()
.collect(Collectors.toList());
assertThat(adminEventsDesc.size(), is(2));
assertThat(adminEventsDesc.get(0).getOperationType(), is(OperationType.DELETE));
assertThat(adminEventsDesc.get(1).getOperationType(), is(OperationType.CREATE));
return null;
});
Time.setOffset(10);
try {
withRealm(realmId, (session, realm) -> {
EventStoreProvider eventStore = session.getProvider(EventStoreProvider.class);
Set<Event> events = eventStore.createQuery()
.getResultStream().collect(Collectors.toSet());
assertThat(events, hasSize(1));
assertThat(events.iterator().next().getUserId(), equalTo("u2"));
return null;
});
} finally {
Time.setOffset(0);
}
}
@Test
@RequireProvider(value = EventStoreProvider.class, only = "map")
public void testEventsClearedOnRealmRemoval() {
// Create another realm
String newRealmId = inComittedTransaction(null, (session, t) -> {
RealmModel realm = session.realms().createRealm("events-realm");
realm.setDefaultRole(session.roles().addRealmRole(realm, Constants.DEFAULT_ROLES_ROLE_PREFIX + "-" + realm.getName()));
EventStoreProvider eventStore = session.getProvider(EventStoreProvider.class);
Event e = createAuthEventForUser(realm, "u1");
eventStore.onEvent(e);
AdminEvent ae = new AdminEvent();
ae.setRealmId(realm.getId());
eventStore.onEvent(ae, false);
return realm.getId();
});
// Check if events were created
inComittedTransaction(session -> {
EventStoreProvider eventStore = session.getProvider(EventStoreProvider.class);
assertThat(eventStore.createQuery().getResultStream().count(), is(1L));
assertThat(eventStore.createAdminQuery().getResultStream().count(), is(1L));
});
// Remove realm
inComittedTransaction((Consumer<KeycloakSession>) session -> session.realms().removeRealm(newRealmId));
// Check events were removed
inComittedTransaction(session -> {
EventStoreProvider eventStore = session.getProvider(EventStoreProvider.class);
assertThat(eventStore.createQuery().getResultStream().count(), is(0L));
assertThat(eventStore.createAdminQuery().getResultStream().count(), is(0L));
});
private AdminEvent createClientEvent(RealmModel realm, OperationType operation) {
return new AdminEventBuilder(realm, new DummyAuth(realm), null, DummyClientConnection.DUMMY_CONNECTION)
.resource(ResourceType.CLIENT).operation(operation).getEvent();
}
private static class DummyClientConnection implements ClientConnection {
private static DummyClientConnection DUMMY_CONNECTION = new DummyClientConnection();
private static final AdminEventQueryTest.DummyClientConnection DUMMY_CONNECTION =
new AdminEventQueryTest.DummyClientConnection();
@Override
public String getRemoteAddr() {
@ -205,4 +146,34 @@ public class AdminEventQueryTest extends KeycloakModelTest {
}
}
private static class DummyAuth extends AdminAuth {
private final RealmModel realm;
public DummyAuth(RealmModel realm) {
super(realm, null, null, null);
this.realm = realm;
}
@Override
public RealmModel getRealm() {
return realm;
}
@Override
public ClientModel getClient() {
return new ClientModelLazyDelegate.WithId("dummy-client", null);
}
@Override
public UserModel getUser() {
return new UserModelDelegate(null) {
@Override
public String getId() {
return "dummy-user";
}
};
}
}
}

View file

@ -0,0 +1,249 @@
/*
* Copyright 2020 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.testsuite.model.events;
import org.keycloak.common.ClientConnection;
import org.keycloak.common.util.Time;
import org.keycloak.events.Event;
import org.keycloak.events.EventBuilder;
import org.keycloak.events.EventStoreProvider;
import org.keycloak.events.EventType;
import org.keycloak.events.admin.AdminEvent;
import org.keycloak.models.Constants;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.testsuite.model.KeycloakModelTest;
import org.keycloak.testsuite.model.RequireProvider;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import org.junit.Test;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is;
/**
*
* @author hmlnarik
*/
@RequireProvider(EventStoreProvider.class)
public class EventQueryTest extends KeycloakModelTest {
private String realmId;
@Override
public void createEnvironment(KeycloakSession s) {
RealmModel realm = s.realms().createRealm("realm");
realm.setDefaultRole(s.roles().addRealmRole(realm, Constants.DEFAULT_ROLES_ROLE_PREFIX + "-" + realm.getName()));
this.realmId = realm.getId();
}
@Override
public void cleanEnvironment(KeycloakSession s) {
s.realms().removeRealm(realmId);
}
@Test
public void testClear() {
inRolledBackTransaction(null, (session, t) -> {
EventStoreProvider eventStore = session.getProvider(EventStoreProvider.class);
eventStore.clear();
});
}
private Event createAuthEventForUser(RealmModel realm, String user) {
return new EventBuilder(realm, null, DummyClientConnection.DUMMY_CONNECTION)
.event(EventType.LOGIN)
.user(user)
.getEvent();
}
@Test
public void testQuery() {
withRealm(realmId, (session, realm) -> {
EventStoreProvider eventStore = session.getProvider(EventStoreProvider.class);
eventStore.onEvent(createAuthEventForUser(realm,"u1"));
eventStore.onEvent(createAuthEventForUser(realm,"u2"));
eventStore.onEvent(createAuthEventForUser(realm,"u3"));
eventStore.onEvent(createAuthEventForUser(realm,"u4"));
return realm.getId();
});
withRealm(realmId, (session, realm) -> {
EventStoreProvider eventStore = session.getProvider(EventStoreProvider.class);
assertThat(eventStore.createQuery()
.firstResult(2)
.getResultStream()
.collect(Collectors.counting()),
is(2L)
);
return null;
});
}
@Test
public void testQueryOrder() {
withRealm(realmId, (session, realm) -> {
EventStoreProvider eventStore = session.getProvider(EventStoreProvider.class);
Event firstEvent = createAuthEventForUser(realm, "u1");
firstEvent.setTime(1L);
Event secondEvent = createAuthEventForUser(realm, "u2");
secondEvent.setTime(2L);
eventStore.onEvent(firstEvent);
eventStore.onEvent(secondEvent);
return realm.getId();
});
withRealm(realmId, (session, realm) -> {
EventStoreProvider eventStore = session.getProvider(EventStoreProvider.class);
List<Event> eventsAsc = eventStore.createQuery()
.realm(realmId)
.orderByAscTime()
.getResultStream()
.collect(Collectors.toList());
assertThat(eventsAsc.size(), is(2));
assertThat(eventsAsc.get(0).getUserId(), is("u1"));
assertThat(eventsAsc.get(1).getUserId(), is("u2"));
List<Event> eventsDesc = eventStore.createQuery()
.realm(realmId)
.orderByDescTime()
.getResultStream()
.collect(Collectors.toList());
assertThat(eventsDesc.size(), is(2));
assertThat(eventsDesc.get(0).getUserId(), is("u2"));
assertThat(eventsDesc.get(1).getUserId(), is("u1"));
return null;
});
}
@Test
@RequireProvider(value = EventStoreProvider.class, only = "map")
public void testEventExpiration() {
withRealm(realmId, (session, realm) -> {
EventStoreProvider eventStore = session.getProvider(EventStoreProvider.class);
// Set expiration so no event is valid
realm.setEventsExpiration(5);
Event e = createAuthEventForUser(realm, "u1");
eventStore.onEvent(e);
// Set expiration to 1000 seconds
realm.setEventsExpiration(1000);
e = createAuthEventForUser(realm, "u2");
eventStore.onEvent(e);
return null;
});
Time.setOffset(10);
try {
withRealm(realmId, (session, realm) -> {
EventStoreProvider eventStore = session.getProvider(EventStoreProvider.class);
Set<Event> events = eventStore.createQuery()
.getResultStream().collect(Collectors.toSet());
assertThat(events, hasSize(1));
assertThat(events.iterator().next().getUserId(), equalTo("u2"));
return null;
});
} finally {
Time.setOffset(0);
}
}
@Test
@RequireProvider(value = EventStoreProvider.class, only = "map")
public void testEventsClearedOnRealmRemoval() {
// Create another realm
String newRealmId = inComittedTransaction(null, (session, t) -> {
RealmModel realm = session.realms().createRealm("events-realm");
realm.setDefaultRole(session.roles().addRealmRole(realm, Constants.DEFAULT_ROLES_ROLE_PREFIX + "-" + realm.getName()));
EventStoreProvider eventStore = session.getProvider(EventStoreProvider.class);
Event e = createAuthEventForUser(realm, "u1");
eventStore.onEvent(e);
AdminEvent ae = new AdminEvent();
ae.setRealmId(realm.getId());
eventStore.onEvent(ae, false);
return realm.getId();
});
// Check if events were created
inComittedTransaction(session -> {
EventStoreProvider eventStore = session.getProvider(EventStoreProvider.class);
assertThat(eventStore.createQuery().getResultStream().count(), is(1L));
assertThat(eventStore.createAdminQuery().getResultStream().count(), is(1L));
});
// Remove realm
inComittedTransaction((Consumer<KeycloakSession>) session -> session.realms().removeRealm(newRealmId));
// Check events were removed
inComittedTransaction(session -> {
EventStoreProvider eventStore = session.getProvider(EventStoreProvider.class);
assertThat(eventStore.createQuery().getResultStream().count(), is(0L));
assertThat(eventStore.createAdminQuery().getResultStream().count(), is(0L));
});
}
private static class DummyClientConnection implements ClientConnection {
private static DummyClientConnection DUMMY_CONNECTION = new DummyClientConnection();
@Override
public String getRemoteAddr() {
return "remoteAddr";
}
@Override
public String getRemoteHost() {
return "remoteHost";
}
@Override
public int getRemotePort() {
return -1;
}
@Override
public String getLocalAddr() {
return "localAddr";
}
@Override
public int getLocalPort() {
return -2;
}
}
}