diff --git a/dependencies/server-all/pom.xml b/dependencies/server-all/pom.xml index 43907f6efa..339afad74c 100755 --- a/dependencies/server-all/pom.xml +++ b/dependencies/server-all/pom.xml @@ -217,12 +217,6 @@ - - - org.keycloak - keycloak-wildfly-extensions - ${project.version} - \ No newline at end of file diff --git a/distribution/modules/pom.xml b/distribution/modules/pom.xml index bf5c7c0305..9b69d37f60 100755 --- a/distribution/modules/pom.xml +++ b/distribution/modules/pom.xml @@ -23,6 +23,11 @@ ${project.version} pom + + org.keycloak + keycloak-wildfly-extensions + ${project.version} + org.keycloak keycloak-core diff --git a/events/api/src/main/java/org/keycloak/events/EventQuery.java b/events/api/src/main/java/org/keycloak/events/EventQuery.java index 00d9a4fb93..9a555caad5 100644 --- a/events/api/src/main/java/org/keycloak/events/EventQuery.java +++ b/events/api/src/main/java/org/keycloak/events/EventQuery.java @@ -15,6 +15,10 @@ public interface EventQuery { public EventQuery user(String userId); + public EventQuery fromDate(String fromDate); + + public EventQuery toDate(String toDate); + public EventQuery ipAddress(String ipAddress); public EventQuery firstResult(int result); diff --git a/events/jpa/src/main/java/org/keycloak/events/jpa/JpaEventQuery.java b/events/jpa/src/main/java/org/keycloak/events/jpa/JpaEventQuery.java index 4c7fbe5e90..5e39d179eb 100644 --- a/events/jpa/src/main/java/org/keycloak/events/jpa/JpaEventQuery.java +++ b/events/jpa/src/main/java/org/keycloak/events/jpa/JpaEventQuery.java @@ -10,6 +10,9 @@ import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; + +import java.text.ParseException; +import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; @@ -64,6 +67,32 @@ public class JpaEventQuery implements EventQuery { return this; } + @Override + public EventQuery fromDate(String fromDate) { + SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd"); + Long from = null; + try { + from = df.parse(fromDate).getTime(); + } catch (ParseException e) { + e.printStackTrace(); + } + predicates.add(cb.greaterThanOrEqualTo(root.get("time"), from)); + return this; + } + + @Override + public EventQuery toDate(String toDate) { + SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd"); + Long to = null; + try { + to = df.parse(toDate).getTime(); + } catch (ParseException e) { + e.printStackTrace(); + } + predicates.add(cb.lessThanOrEqualTo(root.get("time"), to)); + return this; + } + @Override public EventQuery ipAddress(String ipAddress) { predicates.add(cb.equal(root.get("ipAddress"), ipAddress)); diff --git a/events/mongo/src/main/java/org/keycloak/events/mongo/MongoEventQuery.java b/events/mongo/src/main/java/org/keycloak/events/mongo/MongoEventQuery.java index fe08949d88..51d329b9d1 100755 --- a/events/mongo/src/main/java/org/keycloak/events/mongo/MongoEventQuery.java +++ b/events/mongo/src/main/java/org/keycloak/events/mongo/MongoEventQuery.java @@ -1,12 +1,16 @@ package org.keycloak.events.mongo; import com.mongodb.BasicDBObject; +import com.mongodb.BasicDBObjectBuilder; import com.mongodb.DBCollection; import com.mongodb.DBCursor; + import org.keycloak.events.Event; import org.keycloak.events.EventQuery; import org.keycloak.events.EventType; +import java.text.ParseException; +import java.text.SimpleDateFormat; import java.util.LinkedList; import java.util.List; @@ -53,6 +57,32 @@ public class MongoEventQuery implements EventQuery { return this; } + @Override + public EventQuery fromDate(String fromDate) { + SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd"); + Long from = null; + try { + from = df.parse(fromDate).getTime(); + } catch (ParseException e) { + e.printStackTrace(); + } + query.put("time", BasicDBObjectBuilder.start("$gte", from).get()); + return this; + } + + @Override + public EventQuery toDate(String toDate) { + SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd"); + Long to = null; + try { + to = df.parse(toDate).getTime(); + } catch (ParseException e) { + e.printStackTrace(); + } + query.put("time", BasicDBObjectBuilder.start("$lte", to).get()); + return this; + } + @Override public EventQuery ipAddress(String ipAddress) { query.put("ipAddress", ipAddress); diff --git a/examples/providers/event-store-mem/src/main/java/org/keycloak/examples/providers/events/MemEventQuery.java b/examples/providers/event-store-mem/src/main/java/org/keycloak/examples/providers/events/MemEventQuery.java index 06038c75ff..e60b0555e8 100644 --- a/examples/providers/event-store-mem/src/main/java/org/keycloak/examples/providers/events/MemEventQuery.java +++ b/examples/providers/event-store-mem/src/main/java/org/keycloak/examples/providers/events/MemEventQuery.java @@ -4,6 +4,8 @@ import org.keycloak.events.Event; import org.keycloak.events.EventQuery; import org.keycloak.events.EventType; +import java.text.ParseException; +import java.text.SimpleDateFormat; import java.util.Collections; import java.util.Iterator; import java.util.List; @@ -73,7 +75,45 @@ public class MemEventQuery implements EventQuery { } return this; } - + + @Override + public EventQuery fromDate(String fromDate) { + SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd"); + Long from = null; + try { + from = df.parse(fromDate).getTime(); + } catch (ParseException e) { + e.printStackTrace(); + } + + Iterator itr = this.events.iterator(); + while (itr.hasNext()) { + if (!(itr.next().getTime() >= from)) { + itr.remove(); + } + } + return this; + } + + @Override + public EventQuery toDate(String toDate) { + SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd"); + Long to = null; + try { + to = df.parse(toDate).getTime(); + } catch (ParseException e) { + e.printStackTrace(); + } + + Iterator itr = this.events.iterator(); + while (itr.hasNext()) { + if (!(itr.next().getTime() <= to)) { + itr.remove(); + } + } + return this; + } + @Override public EventQuery ipAddress(String ipAddress) { Iterator itr = this.events.iterator(); diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/realm.js b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/realm.js index 7c5090db2b..e132cb4a9b 100755 --- a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/realm.js +++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/realm.js @@ -1154,7 +1154,12 @@ module.controller('RealmEventsConfigCtrl', function($scope, eventsConfig, RealmE module.controller('RealmEventsCtrl', function($scope, RealmEvents, realm) { $scope.realm = realm; $scope.page = 0; - + + $scope.eventTypes = [{tag:'LOGIN'}, {tag:'REGISTER'}, {tag:'LOGOUT'}, {tag:'CODE_TO_TOKEN'}, {tag:'REFRESH_TOKEN'}, + {tag:'LOGIN_ERROR'}, {tag:'REGISTER_ERROR'}, {tag:'LOGOUT_ERROR'}, {tag:'CODE_TO_TOKEN_ERROR'}, {tag:'REFRESH_TOKEN_ERROR'}, + {tag:'VALIDATE_ACCESS_TOKEN'}, {tag:'VALIDATE_ACCESS_TOKEN_ERROR'}, {tag:'SOCIAL_LINK'}, {tag:'SOCIAL_LINK_ERROR'}, {tag:'REMOVE_FEDERATED_IDENTITY'}, + {tag:'REMOVE_SOCIAL_LINK_ERROR'}, {tag:'UPDATE_EMAIL'}, {tag:'UPDATE_PROFILE'}, {tag:'UPDATE_PASSWORD'}, {tag:'UPDATE_TOTP'}]; + $scope.query = { id : realm.realm, max : 5, @@ -1162,6 +1167,7 @@ module.controller('RealmEventsCtrl', function($scope, RealmEvents, realm) { } $scope.update = function() { + $scope.query.first = 0; for (var i in $scope.query) { if ($scope.query[i] === '') { delete $scope.query[i]; @@ -1169,13 +1175,31 @@ module.controller('RealmEventsCtrl', function($scope, RealmEvents, realm) { } $scope.events = RealmEvents.query($scope.query); } - + + $scope.reset = function() { + $scope.query.first = 0; + $scope.query.max = 5; + $scope.query.type = ''; + $scope.query.client = ''; + $scope.query.user = ''; + $scope.query.dateFrom = ''; + $scope.query.dateTo = ''; + + $scope.update(); + } + + $scope.queryUpdate = function() { + for (var i in $scope.query) { + if ($scope.query[i] === '') { + delete $scope.query[i]; + } + } + $scope.events = RealmEvents.query($scope.query); + } + $scope.firstPage = function() { $scope.query.first = 0; - if ($scope.query.first < 0) { - $scope.query.first = 0; - } - $scope.update(); + $scope.queryUpdate(); } $scope.previousPage = function() { @@ -1183,12 +1207,12 @@ module.controller('RealmEventsCtrl', function($scope, RealmEvents, realm) { if ($scope.query.first < 0) { $scope.query.first = 0; } - $scope.update(); + $scope.queryUpdate(); } $scope.nextPage = function() { $scope.query.first += parseInt($scope.query.max); - $scope.update(); + $scope.queryUpdate(); } $scope.update(); diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-events.html b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-events.html index 7a66013bac..e8107178d5 100755 --- a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-events.html +++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-events.html @@ -27,14 +27,20 @@ Filter +
- -
- -
-
+ +
+ +
+
@@ -47,6 +53,20 @@
+ +
+ +
+ +
+
+
+ +
+ +
+
+
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 5b807ca321..bb6bedd914 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java @@ -1,39 +1,9 @@ package org.keycloak.services.resources.admin; -import org.jboss.logging.Logger; -import org.jboss.resteasy.annotations.cache.NoCache; -import org.jboss.resteasy.spi.NotFoundException; -import org.jboss.resteasy.spi.ResteasyProviderFactory; -import org.keycloak.events.Event; -import org.keycloak.events.EventQuery; -import org.keycloak.events.EventStoreProvider; -import org.keycloak.events.EventType; -import org.keycloak.exportimport.ApplicationImporter; -import org.keycloak.models.ApplicationModel; -import org.keycloak.models.KeycloakSession; -import org.keycloak.models.ModelDuplicateException; -import org.keycloak.models.ProtocolMapperModel; -import org.keycloak.models.RealmModel; -import org.keycloak.models.UserFederationProviderModel; -import org.keycloak.models.UserSessionModel; -import org.keycloak.models.cache.CacheRealmProvider; -import org.keycloak.models.cache.CacheUserProvider; -import org.keycloak.models.utils.ModelToRepresentation; -import org.keycloak.models.utils.RepresentationToModel; -import org.keycloak.protocol.LoginProtocol; -import org.keycloak.protocol.LoginProtocolFactory; -import org.keycloak.protocol.oidc.TokenManager; -import org.keycloak.provider.ProviderFactory; -import org.keycloak.representations.adapters.action.GlobalRequestResult; -import org.keycloak.representations.idm.ProtocolMapperRepresentation; -import org.keycloak.representations.idm.RealmEventsConfigRepresentation; -import org.keycloak.representations.idm.RealmRepresentation; -import org.keycloak.services.managers.LDAPConnectionTestManager; -import org.keycloak.services.managers.RealmManager; -import org.keycloak.services.managers.ResourceAdminManager; -import org.keycloak.services.managers.UsersSyncManager; -import org.keycloak.services.resources.flows.Flows; -import org.keycloak.timer.TimerProvider; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; @@ -48,10 +18,36 @@ import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.core.UriInfo; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; + +import org.jboss.logging.Logger; +import org.jboss.resteasy.annotations.cache.NoCache; +import org.jboss.resteasy.spi.NotFoundException; +import org.jboss.resteasy.spi.ResteasyProviderFactory; +import org.keycloak.events.Event; +import org.keycloak.events.EventQuery; +import org.keycloak.events.EventStoreProvider; +import org.keycloak.events.EventType; +import org.keycloak.exportimport.ApplicationImporter; +import org.keycloak.models.ApplicationModel; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.ModelDuplicateException; +import org.keycloak.models.RealmModel; +import org.keycloak.models.UserFederationProviderModel; +import org.keycloak.models.UserSessionModel; +import org.keycloak.models.cache.CacheRealmProvider; +import org.keycloak.models.cache.CacheUserProvider; +import org.keycloak.models.utils.ModelToRepresentation; +import org.keycloak.models.utils.RepresentationToModel; +import org.keycloak.protocol.oidc.TokenManager; +import org.keycloak.representations.adapters.action.GlobalRequestResult; +import org.keycloak.representations.idm.RealmEventsConfigRepresentation; +import org.keycloak.representations.idm.RealmRepresentation; +import org.keycloak.services.managers.LDAPConnectionTestManager; +import org.keycloak.services.managers.RealmManager; +import org.keycloak.services.managers.ResourceAdminManager; +import org.keycloak.services.managers.UsersSyncManager; +import org.keycloak.services.resources.flows.Flows; +import org.keycloak.timer.TimerProvider; /** * Base resource class for the admin REST api of one realm @@ -397,8 +393,10 @@ public class RealmAdminResource { @GET @NoCache @Produces(MediaType.APPLICATION_JSON) - public List getEvents(@QueryParam("client") String client, @QueryParam("type") String type, @QueryParam("user") String user, - @QueryParam("ipAddress") String ipAddress, @QueryParam("first") Integer firstResult, @QueryParam("max") Integer maxResults) { + public List getEvents(@QueryParam("client") String client, @QueryParam("type") String type, + @QueryParam("user") String user, @QueryParam("dateFrom") String dateFrom, @QueryParam("dateTo") String dateTo, + @QueryParam("ipAddress") String ipAddress, @QueryParam("first") Integer firstResult, + @QueryParam("max") Integer maxResults) { auth.init(RealmAuth.Resource.EVENTS).requireView(); EventStoreProvider eventStore = session.getProvider(EventStoreProvider.class); @@ -413,6 +411,14 @@ public class RealmAdminResource { if (user != null) { query.user(user); } + + if(dateFrom != null) { + query.fromDate(dateFrom); + } + if(dateTo != null) { + query.toDate(dateTo); + } + if (ipAddress != null) { query.ipAddress(ipAddress); } diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/events/EventStoreProviderTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/events/EventStoreProviderTest.java index e985421d09..f5f81ffc36 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/events/EventStoreProviderTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/events/EventStoreProviderTest.java @@ -11,6 +11,9 @@ import org.keycloak.events.EventType; import org.keycloak.models.KeycloakSession; import org.keycloak.testsuite.rule.KeycloakRule; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; import java.util.HashMap; import java.util.Map; @@ -70,8 +73,76 @@ public class EventStoreProviderTest { Assert.assertEquals(newest, eventStore.createQuery().maxResults(1).getResultList().get(0).getTime()); Assert.assertEquals(oldest, eventStore.createQuery().firstResult(5).maxResults(1).getResultList().get(0).getTime()); + + eventStore.clear("realmId"); + eventStore.clear("realmId2"); + + Assert.assertEquals(0, eventStore.createQuery().getResultList().size()); + + String d1 = new String("2015-03-04"); + String d2 = new String("2015-03-05"); + String d3 = new String("2015-03-06"); + String d4 = new String("2015-03-07"); + + SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd"); + Date date1 = null, date2 = null, date3 = null, date4 = null; + + try { + date1 = formatter.parse(d1); + date2 = formatter.parse(d2); + date3 = formatter.parse(d3); + date4 = formatter.parse(d4); + } catch (ParseException e) { + e.printStackTrace(); + } + + eventStore.onEvent(create(date1, EventType.LOGIN, "realmId", "clientId", "userId", "127.0.0.1", "error")); + eventStore.onEvent(create(date1, EventType.LOGIN, "realmId", "clientId", "userId", "127.0.0.1", "error")); + eventStore.onEvent(create(date2, EventType.REGISTER, "realmId", "clientId", "userId", "127.0.0.1", "error")); + eventStore.onEvent(create(date2, EventType.REGISTER, "realmId", "clientId", "userId", "127.0.0.1", "error")); + eventStore.onEvent(create(date3, EventType.CODE_TO_TOKEN, "realmId", "clientId", "userId2", "127.0.0.1", "error")); + eventStore.onEvent(create(date3, EventType.LOGOUT, "realmId", "clientId", "userId2", "127.0.0.1", "error")); + eventStore.onEvent(create(date4, EventType.UPDATE_PROFILE, "realmId2", "clientId2", "userId2", "127.0.0.1", "error")); + eventStore.onEvent(create(date4, EventType.UPDATE_EMAIL, "realmId2", "clientId2", "userId2", "127.0.0.1", "error")); + + resetSession(); + + Assert.assertEquals(6, eventStore.createQuery().client("clientId").getResultList().size()); + Assert.assertEquals(2, eventStore.createQuery().client("clientId2").getResultList().size()); + + Assert.assertEquals(6, eventStore.createQuery().realm("realmId").getResultList().size()); + Assert.assertEquals(2, eventStore.createQuery().realm("realmId2").getResultList().size()); + + Assert.assertEquals(4, eventStore.createQuery().user("userId").getResultList().size()); + Assert.assertEquals(4, eventStore.createQuery().user("userId2").getResultList().size()); + + Assert.assertEquals(2, eventStore.createQuery().type(EventType.LOGIN).getResultList().size()); + Assert.assertEquals(2, eventStore.createQuery().type(EventType.REGISTER).getResultList().size()); + Assert.assertEquals(4, eventStore.createQuery().type(EventType.LOGIN, EventType.REGISTER).getResultList().size()); + Assert.assertEquals(1, eventStore.createQuery().type(EventType.CODE_TO_TOKEN).getResultList().size()); + Assert.assertEquals(1, eventStore.createQuery().type(EventType.LOGOUT).getResultList().size()); + Assert.assertEquals(1, eventStore.createQuery().type(EventType.UPDATE_PROFILE).getResultList().size()); + Assert.assertEquals(1, eventStore.createQuery().type(EventType.UPDATE_EMAIL).getResultList().size()); + + Assert.assertEquals(8, eventStore.createQuery().fromDate("2015-03-04").getResultList().size()); + Assert.assertEquals(8, eventStore.createQuery().toDate("2015-03-07").getResultList().size()); + + Assert.assertEquals(4, eventStore.createQuery().fromDate("2015-03-06").getResultList().size()); + Assert.assertEquals(4, eventStore.createQuery().toDate("2015-03-05").getResultList().size()); + + Assert.assertEquals(0, eventStore.createQuery().fromDate("2015-03-08").getResultList().size()); + Assert.assertEquals(0, eventStore.createQuery().toDate("2015-03-03").getResultList().size()); + + Assert.assertEquals(8, eventStore.createQuery().fromDate("2015-03-04").toDate("2015-03-07").getResultList().size()); + Assert.assertEquals(6, eventStore.createQuery().fromDate("2015-03-05").toDate("2015-03-07").getResultList().size()); + Assert.assertEquals(4, eventStore.createQuery().fromDate("2015-03-04").toDate("2015-03-05").getResultList().size()); + Assert.assertEquals(4, eventStore.createQuery().fromDate("2015-03-06").toDate("2015-03-07").getResultList().size()); + + Assert.assertEquals(0, eventStore.createQuery().fromDate("2015-03-01").toDate("2015-03-03").getResultList().size()); + Assert.assertEquals(0, eventStore.createQuery().fromDate("2015-03-08").toDate("2015-03-10").getResultList().size()); + } - + @Test public void clear() { eventStore.onEvent(create(System.currentTimeMillis() - 30000, EventType.LOGIN, "realmId", "clientId", "userId", "127.0.0.1", "error")); @@ -105,6 +176,10 @@ public class EventStoreProviderTest { private Event create(EventType event, String realmId, String clientId, String userId, String ipAddress, String error) { return create(System.currentTimeMillis(), event, realmId, clientId, userId, ipAddress, error); } + + private Event create(Date date, EventType event, String realmId, String clientId, String userId, String ipAddress, String error) { + return create(date.getTime(), event, realmId, clientId, userId, ipAddress, error); + } private Event create(long time, EventType event, String realmId, String clientId, String userId, String ipAddress, String error) { Event e = new Event();