keycloak-aplcache

Merge pull request #2187 from ssilvert/admin-endpoint-tests KEYCLOAK-1976:

2/8/2016 5:51:55 AM

Details

diff --git a/core/src/main/java/org/keycloak/representations/idm/AdminEventRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/AdminEventRepresentation.java
new file mode 100644
index 0000000..f615cbd
--- /dev/null
+++ b/core/src/main/java/org/keycloak/representations/idm/AdminEventRepresentation.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2016 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * 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.representations.idm;
+
+/**
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc.
+ */
+public class AdminEventRepresentation {
+
+    private long time;
+    private String realmId;
+    private AuthDetailsRepresentation authDetails;
+    private String operationType;
+    private String resourcePath;
+    private String representation;
+    private String error;
+
+    public long getTime() {
+        return time;
+    }
+
+    public void setTime(long time) {
+        this.time = time;
+    }
+
+    public String getRealmId() {
+        return realmId;
+    }
+
+    public void setRealmId(String realmId) {
+        this.realmId = realmId;
+    }
+
+    public AuthDetailsRepresentation getAuthDetails() {
+        return authDetails;
+    }
+
+    public void setAuthDetails(AuthDetailsRepresentation authDetails) {
+        this.authDetails = authDetails;
+    }
+
+    public String getOperationType() {
+        return operationType;
+    }
+
+    public void setOperationType(String operationType) {
+        this.operationType = operationType;
+    }
+
+    public String getResourcePath() {
+        return resourcePath;
+    }
+
+    public void setResourcePath(String resourcePath) {
+        this.resourcePath = resourcePath;
+    }
+
+    public String getRepresentation() {
+        return representation;
+    }
+
+    public void setRepresentation(String representation) {
+        this.representation = representation;
+    }
+
+    public String getError() {
+        return error;
+    }
+
+    public void setError(String error) {
+        this.error = error;
+    }
+
+}
diff --git a/core/src/main/java/org/keycloak/representations/idm/AuthDetailsRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/AuthDetailsRepresentation.java
new file mode 100644
index 0000000..746aa71
--- /dev/null
+++ b/core/src/main/java/org/keycloak/representations/idm/AuthDetailsRepresentation.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2016 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * 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.representations.idm;
+
+/**
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc.
+ */
+public class AuthDetailsRepresentation {
+
+    private String realmId;
+    private String clientId;
+    private String userId;
+    private String ipAddress;
+
+    public String getRealmId() {
+        return realmId;
+    }
+
+    public void setRealmId(String realmId) {
+        this.realmId = realmId;
+    }
+
+    public String getClientId() {
+        return clientId;
+    }
+
+    public void setClientId(String clientId) {
+        this.clientId = clientId;
+    }
+
+    public String getUserId() {
+        return userId;
+    }
+
+    public void setUserId(String userId) {
+        this.userId = userId;
+    }
+
+    public String getIpAddress() {
+        return ipAddress;
+    }
+
+    public void setIpAddress(String ipAddress) {
+        this.ipAddress = ipAddress;
+    }
+
+}
diff --git a/core/src/main/java/org/keycloak/representations/idm/EventRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/EventRepresentation.java
new file mode 100644
index 0000000..93be310
--- /dev/null
+++ b/core/src/main/java/org/keycloak/representations/idm/EventRepresentation.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2016 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * 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.representations.idm;
+
+import java.util.Map;
+
+/**
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc.
+ */
+public class EventRepresentation {
+
+    private long time;
+    private String type;
+    private String realmId;
+    private String clientId;
+    private String userId;
+    private String sessionId;
+    private String ipAddress;
+    private String error;
+    private Map<String, String> details;
+
+    public long getTime() {
+        return time;
+    }
+
+    public void setTime(long time) {
+        this.time = time;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public String getRealmId() {
+        return realmId;
+    }
+
+    public void setRealmId(String realmId) {
+        this.realmId = realmId;
+    }
+
+    public String getClientId() {
+        return clientId;
+    }
+
+    public void setClientId(String clientId) {
+        this.clientId = clientId;
+    }
+
+    public String getUserId() {
+        return userId;
+    }
+
+    public void setUserId(String userId) {
+        this.userId = userId;
+    }
+
+    public String getSessionId() {
+        return sessionId;
+    }
+
+    public void setSessionId(String sessionId) {
+        this.sessionId = sessionId;
+    }
+
+    public String getIpAddress() {
+        return ipAddress;
+    }
+
+    public void setIpAddress(String ipAddress) {
+        this.ipAddress = ipAddress;
+    }
+
+    public String getError() {
+        return error;
+    }
+
+    public void setError(String error) {
+        this.error = error;
+    }
+
+    public Map<String, String> getDetails() {
+        return details;
+    }
+
+    public void setDetails(Map<String, String> details) {
+        this.details = details;
+    }
+}
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RealmResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RealmResource.java
index 0242d24..87f6763 100755
--- a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RealmResource.java
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RealmResource.java
@@ -19,6 +19,7 @@ package org.keycloak.admin.client.resource;
 
 import org.jboss.resteasy.annotations.cache.NoCache;
 import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.representations.idm.EventRepresentation;
 import org.keycloak.representations.idm.GroupRepresentation;
 import org.keycloak.representations.idm.RealmRepresentation;
 
@@ -26,6 +27,8 @@ import javax.ws.rs.*;
 import javax.ws.rs.core.MediaType;
 import java.util.List;
 import java.util.Map;
+import org.keycloak.representations.idm.AdminEventRepresentation;
+import org.keycloak.representations.idm.RealmEventsConfigRepresentation;
 
 /**
  * @author rodrigo.sasaki@icarros.com.br
@@ -61,6 +64,52 @@ public interface RealmResource {
     @Path("groups")
     GroupsResource groups();
 
+    @DELETE
+    @Path("events")
+    void clearEvents();
+
+    @GET
+    @Path("events")
+    @Produces(MediaType.APPLICATION_JSON)
+    List<EventRepresentation> getEvents();
+
+    @Path("events")
+    @GET
+    @NoCache
+    @Produces(MediaType.APPLICATION_JSON)
+    public List<EventRepresentation> getEvents(@QueryParam("type") List<String> types, @QueryParam("client") String client,
+            @QueryParam("user") String user, @QueryParam("dateFrom") String dateFrom, @QueryParam("dateTo") String dateTo,
+            @QueryParam("ipAddress") String ipAddress, @QueryParam("first") Integer firstResult,
+            @QueryParam("max") Integer maxResults);
+
+    @DELETE
+    @Path("admin-events")
+    void clearAdminEvents();
+
+    @GET
+    @Path("admin-events")
+    @Produces(MediaType.APPLICATION_JSON)
+    List<AdminEventRepresentation> getAdminEvents();
+
+    @GET
+    @Path("admin-events")
+    @Produces(MediaType.APPLICATION_JSON)
+    List<AdminEventRepresentation> getAdminEvents(@QueryParam("operationTypes") List<String> operationTypes, @QueryParam("authRealm") String authRealm, @QueryParam("authClient") String authClient,
+            @QueryParam("authUser") String authUser, @QueryParam("authIpAddress") String authIpAddress,
+            @QueryParam("resourcePath") String resourcePath, @QueryParam("dateFrom") String dateFrom,
+            @QueryParam("dateTo") String dateTo, @QueryParam("first") Integer firstResult,
+            @QueryParam("max") Integer maxResults);
+
+    @GET
+    @Path("events/config")
+    @Produces(MediaType.APPLICATION_JSON)
+    public RealmEventsConfigRepresentation getRealmEventsConfig();
+
+    @PUT
+    @Path("events/config")
+    @Consumes(MediaType.APPLICATION_JSON)
+    public void updateRealmEventsConfig(RealmEventsConfigRepresentation rep);
+
     @GET
     @Path("group-by-path/{path: .*}")
     @NoCache
diff --git a/server-spi/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java b/server-spi/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
index 868aa10..30f7d5f 100755
--- a/server-spi/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
+++ b/server-spi/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
@@ -17,6 +17,9 @@
 
 package org.keycloak.models.utils;
 
+import org.keycloak.events.Event;
+import org.keycloak.events.admin.AdminEvent;
+import org.keycloak.events.admin.AuthDetails;
 import org.keycloak.models.AuthenticationExecutionModel;
 import org.keycloak.models.AuthenticationFlowModel;
 import org.keycloak.models.AuthenticatorConfigModel;
@@ -41,12 +44,15 @@ import org.keycloak.models.UserFederationProviderModel;
 import org.keycloak.models.UserModel;
 import org.keycloak.models.UserSessionModel;
 
+import org.keycloak.representations.idm.AdminEventRepresentation;
+import org.keycloak.representations.idm.AuthDetailsRepresentation;
 import org.keycloak.representations.idm.AuthenticationExecutionRepresentation;
 import org.keycloak.representations.idm.AuthenticationFlowRepresentation;
 import org.keycloak.representations.idm.AuthenticatorConfigRepresentation;
 import org.keycloak.representations.idm.ClientRepresentation;
 import org.keycloak.representations.idm.ClientTemplateRepresentation;
 import org.keycloak.representations.idm.CredentialRepresentation;
+import org.keycloak.representations.idm.EventRepresentation;
 import org.keycloak.representations.idm.FederatedIdentityRepresentation;
 import org.keycloak.representations.idm.GroupRepresentation;
 import org.keycloak.representations.idm.IdentityProviderMapperRepresentation;
@@ -167,6 +173,43 @@ public class ModelToRepresentation {
         return rep;
     }
 
+    public static EventRepresentation toRepresentation(Event event) {
+        EventRepresentation rep = new EventRepresentation();
+        rep.setTime(event.getTime());
+        rep.setType(event.getType().toString());
+        rep.setRealmId(event.getRealmId());
+        rep.setClientId(event.getClientId());
+        rep.setUserId(event.getUserId());
+        rep.setSessionId(event.getSessionId());
+        rep.setError(event.getError());
+        rep.setDetails(event.getDetails());
+        return rep;
+    }
+
+    public static AdminEventRepresentation toRepresentation(AdminEvent adminEvent) {
+        AdminEventRepresentation rep = new AdminEventRepresentation();
+        rep.setTime(adminEvent.getTime());
+        rep.setRealmId(adminEvent.getRealmId());
+        if (adminEvent.getAuthDetails() != null) {
+            rep.setAuthDetails(toRepresentation(adminEvent.getAuthDetails()));
+        }
+        rep.setOperationType(adminEvent.getOperationType().toString());
+        rep.setResourcePath(adminEvent.getResourcePath());
+        rep.setRepresentation(adminEvent.getRepresentation());
+        rep.setError(adminEvent.getError());
+
+        return rep;
+    }
+
+    public static AuthDetailsRepresentation toRepresentation(AuthDetails authDetails) {
+        AuthDetailsRepresentation rep = new AuthDetailsRepresentation();
+        rep.setRealmId(authDetails.getRealmId());
+        rep.setClientId(authDetails.getClientId());
+        rep.setUserId(authDetails.getUserId());
+        rep.setIpAddress(authDetails.getIpAddress());
+        return rep;
+    }
+
     public static RoleRepresentation toRepresentation(RoleModel role) {
         RoleRepresentation rep = new RoleRepresentation();
         rep.setId(role.getId());
@@ -383,15 +426,15 @@ public class ModelToRepresentation {
         if (realm.getEventsListeners() != null) {
             rep.setEventsListeners(new LinkedList<>(realm.getEventsListeners()));
         }
-        
+
         if(realm.getEnabledEventTypes() != null) {
             rep.setEnabledEventTypes(new LinkedList<>(realm.getEnabledEventTypes()));
         }
-        
+
         rep.setAdminEventsEnabled(realm.isAdminEventsEnabled());
-        
+
         rep.setAdminEventsDetailsEnabled(realm.isAdminEventsDetailsEnabled());
-        
+
         return rep;
     }
 
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 22eb5db..15b0e81 100644
--- a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
@@ -45,8 +45,11 @@ import org.keycloak.models.utils.RepresentationToModel;
 import org.keycloak.protocol.oidc.TokenManager;
 import org.keycloak.provider.ProviderFactory;
 import org.keycloak.representations.adapters.action.GlobalRequestResult;
+import org.keycloak.representations.idm.AdminEventRepresentation;
 import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.representations.idm.EventRepresentation;
 import org.keycloak.representations.idm.GroupRepresentation;
+import org.keycloak.representations.idm.PartialImportRepresentation;
 import org.keycloak.representations.idm.RealmEventsConfigRepresentation;
 import org.keycloak.representations.idm.RealmRepresentation;
 import org.keycloak.services.managers.AuthenticationManager;
@@ -75,6 +78,7 @@ import javax.ws.rs.core.UriInfo;
 
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
+import java.util.ArrayList;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.LinkedList;
@@ -82,7 +86,6 @@ import java.util.List;
 import java.util.Map;
 import java.util.regex.PatternSyntaxException;
 import org.keycloak.partialimport.PartialImportManager;
-import org.keycloak.representations.idm.PartialImportRepresentation;
 
 /**
  * Base resource class for the admin REST api of one realm
@@ -428,6 +431,7 @@ public class RealmAdminResource {
      *
      * Returns all events, or filters them based on URL query parameters listed here
      *
+     * @param types The types of events to return
      * @param client App or oauth client name
      * @param user User id
      * @param ipAddress IP address
@@ -441,7 +445,7 @@ public class RealmAdminResource {
     @GET
     @NoCache
     @Produces(MediaType.APPLICATION_JSON)
-    public List<Event> getEvents(@QueryParam("client") String client,
+    public List<EventRepresentation> getEvents(@QueryParam("type") List<String> types, @QueryParam("client") String client,
             @QueryParam("user") String user, @QueryParam("dateFrom") String dateFrom, @QueryParam("dateTo") String dateTo,
             @QueryParam("ipAddress") String ipAddress, @QueryParam("first") Integer firstResult,
             @QueryParam("max") Integer maxResults) {
@@ -454,8 +458,7 @@ public class RealmAdminResource {
             query.client(client);
         }
 
-        List<String> types = uriInfo.getQueryParameters().get("type");
-        if (types != null) {
+        if (types != null & !types.isEmpty()) {
             EventType[] t = new EventType[types.size()];
             for (int i = 0; i < t.length; i++) {
                 t[i] = EventType.valueOf(types.get(i));
@@ -499,7 +502,15 @@ public class RealmAdminResource {
             query.maxResults(maxResults);
         }
 
-        return query.getResultList();
+        return toEventListRep(query.getResultList());
+    }
+
+    private List<EventRepresentation> toEventListRep(List<Event> events) {
+        List<EventRepresentation> reps = new ArrayList<>();
+        for (Event event : events) {
+            reps.add(ModelToRepresentation.toRepresentation(event));
+        }
+        return reps;
     }
 
     /**
@@ -507,6 +518,7 @@ public class RealmAdminResource {
      *
      * Returns all admin events, or filters events based on URL query parameters listed here
      *
+     * @param operationTypes
      * @param authRealm
      * @param authClient
      * @param authUser user id
@@ -514,7 +526,6 @@ public class RealmAdminResource {
      * @param resourcePath
      * @param dateTo
      * @param dateFrom
-     * @param resourcePath
      * @param firstResult
      * @param maxResults
      * @return
@@ -523,7 +534,7 @@ public class RealmAdminResource {
     @GET
     @NoCache
     @Produces(MediaType.APPLICATION_JSON)
-    public List<AdminEvent> getEvents(@QueryParam("authRealm") String authRealm, @QueryParam("authClient") String authClient,
+    public List<AdminEventRepresentation> getEvents(@QueryParam("operationTypes") List<String> operationTypes, @QueryParam("authRealm") String authRealm, @QueryParam("authClient") String authClient,
             @QueryParam("authUser") String authUser, @QueryParam("authIpAddress") String authIpAddress,
             @QueryParam("resourcePath") String resourcePath, @QueryParam("dateFrom") String dateFrom,
             @QueryParam("dateTo") String dateTo, @QueryParam("first") Integer firstResult,
@@ -553,8 +564,7 @@ public class RealmAdminResource {
             query.resourcePath(resourcePath);
         }
 
-        List<String> operationTypes = uriInfo.getQueryParameters().get("operationTypes");
-        if (operationTypes != null) {
+        if (operationTypes != null && !operationTypes.isEmpty()) {
             OperationType[] t = new OperationType[operationTypes.size()];
             for (int i = 0; i < t.length; i++) {
                 t[i] = OperationType.valueOf(operationTypes.get(i));
@@ -591,7 +601,16 @@ public class RealmAdminResource {
             query.maxResults(maxResults);
         }
 
-        return query.getResultList();
+        return toAdminEventRep(query.getResultList());
+    }
+
+    private List<AdminEventRepresentation> toAdminEventRep(List<AdminEvent> events) {
+        List<AdminEventRepresentation> reps = new ArrayList<>();
+        for (AdminEvent event : events) {
+            reps.add(ModelToRepresentation.toRepresentation(event));
+        }
+
+        return reps;
     }
 
     /**
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/event/AbstractEventTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/event/AbstractEventTest.java
new file mode 100644
index 0000000..77c61b3
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/event/AbstractEventTest.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2016 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * 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.event;
+
+import java.util.Collections;
+import org.junit.Before;
+import org.keycloak.admin.client.resource.RealmResource;
+import org.keycloak.representations.idm.RealmEventsConfigRepresentation;
+import org.keycloak.testsuite.AbstractAuthTest;
+
+/**
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc.
+ */
+public abstract class AbstractEventTest extends AbstractAuthTest {
+
+    protected RealmEventsConfigRepresentation configRep;
+
+    @Before
+    public void setConfigRep() {
+        RealmResource testRsc = testRealmResource();
+        configRep = testRsc.getRealmEventsConfig();
+        configRep.setAdminEventsDetailsEnabled(false);
+        configRep.setAdminEventsEnabled(false);
+        configRep.setEventsEnabled(false);
+        configRep.setEnabledEventTypes(Collections.EMPTY_LIST); // resets to all types
+        saveConfig();
+    }
+
+    protected void saveConfig() {
+        RealmResource testRsc = testRealmResource();
+        testRsc.updateRealmEventsConfig(configRep);
+        configRep = testRsc.getRealmEventsConfig();
+    }
+
+    protected void enableEvents() {
+        configRep.setEventsEnabled(true);
+        configRep.setAdminEventsEnabled(Boolean.TRUE);
+
+        saveConfig();
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/event/AdminEventTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/event/AdminEventTest.java
new file mode 100644
index 0000000..bdf699a
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/event/AdminEventTest.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2016 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * 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.event;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import org.junit.Before;
+import org.junit.Test;
+import org.keycloak.representations.idm.AdminEventRepresentation;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.representations.idm.UserRepresentation;
+import org.keycloak.testsuite.admin.ApiUtil;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Test getting and filtering admin events.
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc.
+ */
+public class AdminEventTest extends AbstractEventTest {
+
+    @Before
+    public void initConfig() {
+        enableEvents();
+        testRealmResource().clearAdminEvents();
+    }
+
+    private List<AdminEventRepresentation> events() {
+        return testRealmResource().getAdminEvents();
+    }
+
+    private void createUser(String username) {
+        UserRepresentation user = createUserRepresentation(username, username + "@foo.com", "foo", "bar", true);
+        ApiUtil.createUserWithAdminClient(testRealmResource(), user);
+    }
+
+    private void updateRealm() {
+        RealmRepresentation realm = testRealmResource().toRepresentation();
+        realm.setDisplayName("Fury Road");
+        testRealmResource().update(realm);
+    }
+
+    private String realmName() {
+        return testRealmResource().toRepresentation().getId();
+    }
+
+    @Test
+    public void clearAdminEventsTest() {
+        createUser("user0");
+        assertEquals(1, events().size());
+        testRealmResource().clearAdminEvents();
+        assertEquals(Collections.EMPTY_LIST, events());
+    }
+
+    @Test
+    public void retrieveAdminEventTest() {
+        createUser("user1");
+        List<AdminEventRepresentation> events = events();
+
+        assertEquals(1, events.size());
+        AdminEventRepresentation event = events().get(0);
+        assertEquals("CREATE", event.getOperationType());
+
+        assertEquals(realmName(), event.getRealmId());
+        assertEquals(realmName(), event.getAuthDetails().getRealmId());
+        assertNull(event.getRepresentation());
+    }
+
+    @Test
+    public void testGetRepresentation() {
+        configRep.setAdminEventsDetailsEnabled(Boolean.TRUE);
+        saveConfig();
+
+        createUser("user2");
+        AdminEventRepresentation event = events().get(0);
+        assertNotNull(event.getRepresentation());
+        assertTrue(event.getRepresentation().contains("foo"));
+    }
+
+    @Test
+    public void testFilterAdminEvents() {
+        // two CREATE and one UPDATE
+        createUser("user3");
+        createUser("user4");
+        updateRealm();
+        assertEquals(3, events().size());
+
+        List<AdminEventRepresentation> events = testRealmResource().getAdminEvents(Arrays.asList("CREATE"), realmName(), null, null, null, null, null, null, null, null);
+        assertEquals(2, events.size());
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/event/EventConfigTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/event/EventConfigTest.java
new file mode 100644
index 0000000..a2bf746
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/event/EventConfigTest.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2016 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * 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.event;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Test updates to the events configuration.
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc.
+ */
+public class EventConfigTest extends AbstractEventTest {
+
+    @Test
+    public void defaultEventConfigTest() {
+        assertFalse(configRep.isAdminEventsDetailsEnabled());
+        assertFalse(configRep.isAdminEventsEnabled());
+        assertFalse(configRep.isEventsEnabled());
+
+        List<String> eventListeners = configRep.getEventsListeners();
+        assertEquals(1, eventListeners.size());
+        assertEquals("jboss-logging", eventListeners.get(0));
+    }
+
+    @Test
+    public void enableEventsTest() {
+        enableEvents();
+
+        assertTrue(configRep.isEventsEnabled());
+        assertTrue(configRep.isAdminEventsEnabled());
+    }
+
+    @Test
+    public void addRemoveListenerTest() {
+        configRep.setEventsListeners(Collections.EMPTY_LIST);
+        saveConfig();
+        assertEquals(0, configRep.getEventsListeners().size());
+
+        configRep.setEventsListeners(Arrays.asList("email"));
+        saveConfig();
+        List<String> eventListeners = configRep.getEventsListeners();
+        assertEquals(1, eventListeners.size());
+        assertEquals("email", eventListeners.get(0));
+    }
+
+    @Test
+    public void loginEventSettingsTest() {
+        enableEvents();
+
+        assertTrue(hasEventType("LOGIN"));
+        assertTrue(hasEventType("LOGOUT"));
+        assertTrue(hasEventType("CLIENT_DELETE_ERROR"));
+
+        int defaultEventCount = configRep.getEnabledEventTypes().size();
+
+        configRep.setEnabledEventTypes(Arrays.asList("CLIENT_DELETE", "CLEINT_DELETE_ERROR"));
+        saveConfig();
+
+        List<String> enabledEventTypes = configRep.getEnabledEventTypes();
+        assertEquals(2, enabledEventTypes.size());
+
+        // remove all event types
+        configRep.setEnabledEventTypes(Collections.EMPTY_LIST);
+        saveConfig();
+
+        // removing all event types restores default events
+        assertEquals(defaultEventCount, configRep.getEnabledEventTypes().size());
+    }
+
+    private boolean hasEventType(String eventType) {
+        for (String event : configRep.getEnabledEventTypes()) {
+            if (eventType.equals(event)) return true;
+        }
+
+        return false;
+    }
+
+    @Test
+    public void includeRepresentationTest() {
+        enableEvents();
+
+        assertTrue(configRep.isAdminEventsEnabled());
+        assertFalse(configRep.isAdminEventsDetailsEnabled());
+
+        configRep.setAdminEventsDetailsEnabled(Boolean.TRUE);
+        saveConfig();
+
+        assertTrue(configRep.isAdminEventsDetailsEnabled());
+    }
+
+    @Test
+    public void setLoginEventExpirationTest() {
+        enableEvents();
+
+        assertNull(configRep.getEventsExpiration());
+
+        Long oneHour = 3600L;
+        configRep.setEventsExpiration(oneHour);
+        saveConfig();
+
+        assertEquals(oneHour, configRep.getEventsExpiration());
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/event/LoginEventsTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/event/LoginEventsTest.java
new file mode 100644
index 0000000..142bfff
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/event/LoginEventsTest.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2016 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * 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.event;
+
+import java.util.Arrays;
+import java.util.List;
+import org.jboss.arquillian.graphene.page.Page;
+import org.junit.Before;
+import org.junit.Test;
+import org.keycloak.representations.idm.EventRepresentation;
+import org.keycloak.testsuite.console.page.events.LoginEvents;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+/**
+ * Test getting and filtering login-related events.
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc.
+ */
+public class LoginEventsTest extends AbstractEventTest {
+
+    @Page
+    private LoginEvents loginEventsPage;
+
+    @Before
+    public void init() {
+        configRep.setEventsEnabled(true);
+        saveConfig();
+        testRealmResource().clearEvents();
+    }
+
+    private List<EventRepresentation> events() {
+        return testRealmResource().getEvents();
+    }
+
+    private void badLogin() {
+        loginEventsPage.navigateTo();
+        loginPage.form().login("bad", "user");
+    }
+
+    private void pause(int seconds) {
+        try {
+            Thread.sleep(seconds * 1000L);
+        } catch (InterruptedException e) {
+            fail(e.getMessage());
+        }
+    }
+
+    @Test
+    public void clearEventsTest() {
+        assertEquals(0, events().size());
+        badLogin();
+        badLogin();
+        assertEquals(2, events().size());
+        testRealmResource().clearEvents();
+        assertEquals(0, events().size());
+    }
+
+    @Test
+    public void loggingOfCertainTypeTest() {
+        assertEquals(0, events().size());
+        configRep.setEnabledEventTypes(Arrays.asList("REVOKE_GRANT"));
+        saveConfig();
+
+        badLogin();
+        assertEquals(0, events().size());
+
+        configRep.setEnabledEventTypes(Arrays.asList("LOGIN_ERROR"));
+        saveConfig();
+
+        badLogin();
+        assertEquals(1, events().size());
+    }
+
+    @Test
+    public void filterTest() {
+        badLogin();
+        badLogin();
+        assertEquals(2, events().size());
+
+        List<EventRepresentation> filteredEvents = testRealmResource().getEvents(Arrays.asList("REVOKE_GRANT"), null, null, null, null, null, null, null);
+        assertEquals(0, filteredEvents.size());
+
+        filteredEvents = testRealmResource().getEvents(Arrays.asList("LOGIN_ERROR"), null, null, null, null, null, null, null);
+        assertEquals(2, filteredEvents.size());
+    }
+
+    /*
+    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
+    a way to set the cleanup thread to a lower interval for testing.
+    @Test
+    public void eventExpirationTest() {
+        configRep.setEventsExpiration(1L); //  second
+        saveConfig();
+        badLogin();
+        assertEquals(1, events().size());
+        pause(900); // pause 900 seconds
+        assertEquals(0, events().size());
+    }**/
+
+}