keycloak-developers

Merge branch 'master' of https://github.com/girirajsharma/keycloak

5/11/2015 8:36:30 AM

Changes

Details

diff --git a/connections/jpa/src/main/java/org/keycloak/connections/jpa/updater/JpaUpdaterProvider.java b/connections/jpa/src/main/java/org/keycloak/connections/jpa/updater/JpaUpdaterProvider.java
index 2b94243..ad41016 100755
--- a/connections/jpa/src/main/java/org/keycloak/connections/jpa/updater/JpaUpdaterProvider.java
+++ b/connections/jpa/src/main/java/org/keycloak/connections/jpa/updater/JpaUpdaterProvider.java
@@ -12,7 +12,7 @@ public interface JpaUpdaterProvider extends Provider {
 
     public String FIRST_VERSION = "1.0.0.Final";
 
-    public String LAST_VERSION = "1.2.0.RC1";
+    public String LAST_VERSION = "1.3.0.Beta1";
 
     public String getCurrentVersionSql();
 
diff --git a/connections/jpa/src/main/resources/META-INF/persistence.xml b/connections/jpa/src/main/resources/META-INF/persistence.xml
index 4dbad93..79b730a 100755
--- a/connections/jpa/src/main/resources/META-INF/persistence.xml
+++ b/connections/jpa/src/main/resources/META-INF/persistence.xml
@@ -34,9 +34,10 @@
         <class>org.keycloak.models.sessions.jpa.entities.UserSessionEntity</class>
         <class>org.keycloak.models.sessions.jpa.entities.UsernameLoginFailureEntity</class>
 
-        <!-- JpaAuditProvider -->
+        <!-- JpaAuditProviders -->
         <class>org.keycloak.events.jpa.EventEntity</class>
-
+        <class>org.keycloak.events.jpa.AdminEventEntity</class>
+        
         <exclude-unlisted-classes>true</exclude-unlisted-classes>
 
         <properties>
diff --git a/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.3.0.Beta1.xml b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.3.0.Beta1.xml
new file mode 100644
index 0000000..7086065
--- /dev/null
+++ b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.3.0.Beta1.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
+    <changeSet author="giriraj.sharma27@gmail.com" id="1.3.0.Beta1">
+        <createTable tableName="ADMIN_EVENT_ENTITY">
+            <column name="ID" type="VARCHAR(36)">
+                <constraints nullable="false"/>
+            </column>
+            <column name="ADMIN_EVENT_TIME" type="BIGINT"/>
+            <column name="OPERATION_TYPE" type="VARCHAR(255)"/>
+            <column name="REALM_ID" type="VARCHAR(255)"/>
+            <column name="CLIENT_ID" type="VARCHAR(255)"/>
+            <column name="USER_ID" type="VARCHAR(255)"/>
+            <column name="IP_ADDRESS" type="VARCHAR(255)"/>
+            <column name="RESOURCE_PATH" type="VARCHAR(255)"/>
+            <column name="REPRESENTATION" type="VARCHAR(25500)"/>
+            <column name="ERROR" type="VARCHAR(255)"/>
+        </createTable>
+        <createTable tableName="REALM_ENABLED_ADMIN_EVENT_OPERATIONS">
+            <column name="REALM_ID" type="VARCHAR(36)">
+                <constraints nullable="false"/>
+            </column>
+            <column name="VALUE" type="VARCHAR(255)"/>
+        </createTable>
+        <addColumn tableName="REALM">
+            <column name="ADMIN_EVENTS_ENABLED" type="BOOLEAN" defaultValueBoolean="false">
+                <constraints nullable="false"/>
+            </column>
+            <column name="ADMIN_EVENTS_DETAILS_ENABLED" type="BOOLEAN" defaultValueBoolean="false">
+                <constraints nullable="false"/>
+            </column>
+        </addColumn>
+        <addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="REALM_ENABLED_ADMIN_EVENT_OPERATIONS" constraintName="FKF8459C8FAE5C3B34" referencedColumnNames="ID" referencedTableName="REALM"/>        
+    </changeSet>
+</databaseChangeLog>
\ No newline at end of file
diff --git a/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-master.xml b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-master.xml
index 33eb5a1..8272af8 100755
--- a/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-master.xml
+++ b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-master.xml
@@ -5,4 +5,5 @@
     <include file="META-INF/jpa-changelog-1.1.0.Final.xml"/>
     <include file="META-INF/jpa-changelog-1.2.0.Beta1.xml"/>
     <include file="META-INF/jpa-changelog-1.2.0.CR1.xml"/>
+    <include file="META-INF/jpa-changelog-1.3.0.Beta1.xml"/>
 </databaseChangeLog>
diff --git a/core/src/main/java/org/keycloak/representations/idm/RealmEventsConfigRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/RealmEventsConfigRepresentation.java
index a88a75d..5b12c10 100755
--- a/core/src/main/java/org/keycloak/representations/idm/RealmEventsConfigRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/RealmEventsConfigRepresentation.java
@@ -11,6 +11,10 @@ public class RealmEventsConfigRepresentation {
     protected Long eventsExpiration;
     protected List<String> eventsListeners;
     protected List<String> enabledEventTypes;
+    
+    protected Boolean adminEventsEnabled;
+    protected List<String> adminEnabledEventOperations;
+    protected Boolean adminEventsDetailsEnabled;
 
     public boolean isEventsEnabled() {
         return eventsEnabled;
@@ -43,4 +47,29 @@ public class RealmEventsConfigRepresentation {
     public void setEnabledEventTypes(List<String> enabledEventTypes) {
         this.enabledEventTypes = enabledEventTypes;
     }
+
+    public Boolean isAdminEventsEnabled() {
+        return adminEventsEnabled;
+    }
+
+    public void setAdminEventsEnabled(Boolean adminEventsEnabled) {
+        this.adminEventsEnabled = adminEventsEnabled;
+    }
+
+    public List<String> getAdminEnabledEventOperations() {
+        return adminEnabledEventOperations;
+    }
+
+    public void setAdminEnabledEventOperations(List<String> adminEnabledEventOperations) {
+        this.adminEnabledEventOperations = adminEnabledEventOperations;
+    }
+
+    public Boolean isAdminEventsDetailsEnabled() {
+        return adminEventsDetailsEnabled;
+    }
+
+    public void setAdminEventsDetailsEnabled(Boolean adminEventsDetailsEnabled) {
+        this.adminEventsDetailsEnabled = adminEventsDetailsEnabled;
+    }
+    
 }
diff --git a/core/src/main/java/org/keycloak/representations/idm/RealmRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/RealmRepresentation.java
index d9926fe..2c9be55 100755
--- a/core/src/main/java/org/keycloak/representations/idm/RealmRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/RealmRepresentation.java
@@ -57,10 +57,16 @@ public class RealmRepresentation {
     protected String accountTheme;
     protected String adminTheme;
     protected String emailTheme;
+    
     protected Boolean eventsEnabled;
     protected Long eventsExpiration;
     protected List<String> eventsListeners;
     protected List<String> enabledEventTypes;
+    
+    protected Boolean adminEventsEnabled;
+    protected List<String> adminEnabledEventOperations;
+    protected Boolean adminEventsDetailsEnabled;
+    
     private List<IdentityProviderRepresentation> identityProviders;
     private List<IdentityProviderMapperRepresentation> identityProviderMappers;
     private List<ProtocolMapperRepresentation> protocolMappers;
@@ -507,6 +513,30 @@ public class RealmRepresentation {
         this.enabledEventTypes = enabledEventTypes;
     }
 
+    public Boolean isAdminEventsEnabled() {
+        return adminEventsEnabled;
+    }
+
+    public void setAdminEventsEnabled(Boolean adminEventsEnabled) {
+        this.adminEventsEnabled = adminEventsEnabled;
+    }
+
+    public List<String> getAdminEnabledEventOperations() {
+        return adminEnabledEventOperations;
+    }
+
+    public void setAdminEnabledEventOperations(List<String> adminEnabledEventOperations) {
+        this.adminEnabledEventOperations = adminEnabledEventOperations;
+    }
+
+    public Boolean isAdminEventsDetailsEnabled() {
+        return adminEventsDetailsEnabled;
+    }
+
+    public void setAdminEventsDetailsEnabled(Boolean adminEventsDetailsEnabled) {
+        this.adminEventsDetailsEnabled = adminEventsDetailsEnabled;
+    }
+
     public List<UserFederationProviderRepresentation> getUserFederationProviders() {
         return userFederationProviders;
     }
diff --git a/events/api/src/main/java/org/keycloak/events/admin/AdminEvent.java b/events/api/src/main/java/org/keycloak/events/admin/AdminEvent.java
new file mode 100644
index 0000000..fedd2bf
--- /dev/null
+++ b/events/api/src/main/java/org/keycloak/events/admin/AdminEvent.java
@@ -0,0 +1,106 @@
+package org.keycloak.events.admin;
+
+import java.util.Map;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class AdminEvent {
+
+    private long time;
+
+    private AuthDetails authDetails;
+
+    private OperationType operationType;
+
+    private String resourcePath;
+
+    private String representation;
+
+    private String error;
+    
+    /**
+     * Returns the time of the event
+     *
+     * @return time in millis
+     */
+    public long getTime() {
+        return time;
+    }
+
+    public void setTime(long time) {
+        this.time = time;
+    }
+
+    /**
+     * Returns authentication details
+     *
+     * @return
+     */
+    public AuthDetails getAuthDetails() {
+        return authDetails;
+    }
+
+    public void setAuthDetails(AuthDetails authDetails) {
+        this.authDetails = authDetails;
+    }
+
+    /**
+     * Returns the type of the operation
+     *
+     * @return
+     */
+    public OperationType getOperationType() {
+        return operationType;
+    }
+
+    public void setOperationType(OperationType operationType) {
+        this.operationType = operationType;
+    }
+
+    /**
+     * Returns the path of the resource. For example:
+     * <ul>
+     *     <li><b>realms</b> - realm list</li>
+     *     <li><b>realms/master</b> - master realm</li>
+     *     <li><b>realms/clients/00d4b16f-f1f9-4e73-8366-d76b18f3e0e1</b> - client within the master realm</li>
+     * </ul>
+     *
+     * @return
+     */
+    public String getResourcePath() {
+        return resourcePath;
+    }
+
+    public void setResourcePath(String resourcePath) {
+        this.resourcePath = resourcePath;
+    }
+
+    /**
+     * Returns the updated JSON representation if <code>operationType</code> is <code>CREATE</code> or <code>UPDATE</code>.
+     * Otherwise returns <code>null</code>.
+     *
+     * @return
+     */
+    public String getRepresentation() {
+        return representation;
+    }
+
+    public void setRepresentation(String representation) {
+        this.representation = representation;
+    }
+
+    /**
+     * If the event was unsuccessful returns the error message. Otherwise returns <code>null</code>.
+     *
+     * @return
+     */
+    public String getError() {
+        return error;
+    }
+
+    public void setError(String error) {
+        this.error = error;
+    }
+
+}
diff --git a/events/api/src/main/java/org/keycloak/events/admin/AdminEventQuery.java b/events/api/src/main/java/org/keycloak/events/admin/AdminEventQuery.java
new file mode 100644
index 0000000..45cd9e2
--- /dev/null
+++ b/events/api/src/main/java/org/keycloak/events/admin/AdminEventQuery.java
@@ -0,0 +1,102 @@
+package org.keycloak.events.admin;
+
+import java.util.List;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public interface AdminEventQuery {
+    
+    /**
+     * Search by authentication realm
+     *
+     * @param realm realm name
+     * @return Associated <code>AdminEventQuery</code> for method chaining
+     */
+    AdminEventQuery authRealm(String realm);
+    
+    /**
+     * Search by authenticated client
+     *
+     * @param client client uuid
+     * @return Associated <code>AdminEventQuery</code> for method chaining
+     */
+    AdminEventQuery authClient(String client);
+
+    /**
+     * Search by authenticated user
+     *
+     * @param user user uuid
+     * @return Associated <code>AdminEventQuery</code> for method chaining
+     */
+    AdminEventQuery authUser(String user);
+
+    /**
+     * Search by request ip address
+     *
+     * @param ipAddress
+     * @return Associated <code>AdminEventQuery</code> for method chaining
+     */
+    AdminEventQuery authIpAddress(String ipAddress);
+
+    /**
+     * Search by operation type
+     *
+     * @param operations
+     * @return <code>this</code> for method chaining
+     */
+    AdminEventQuery operation(OperationType... operations);
+
+    /**
+     * Search by resource path. Supports wildcards <code>*</code> and <code>**</code>. For example:
+     * <ul>
+     * <li><b>*&#47;master</b> - matches 'realms/master'</li>
+     * <li><b>**&#47;00d4b16f</b> - matches 'realms/master/clients/00d4b16f'</li>
+     * <li><b>realms&#47;master&#47;**</b> - matches anything under 'realms/master'</li>
+     * </ul>
+     *
+     * @param resourcePath
+     * @return <code>this</code> for method chaining
+     */
+    AdminEventQuery resourcePath(String resourcePath);
+
+    /**
+     * Search by events after the specified time
+     * 
+     * @param fromTime time in millis
+     * @return <code>this</code> for method chaining
+     */
+    AdminEventQuery fromTime(String fromTime);
+
+    /**
+     * Search by events before the specified time
+     * 
+     * @param toTime time in millis
+     * @return <code>this</code> for method chaining
+     */
+    AdminEventQuery toTime(String toTime);
+
+    /**
+     * Used for pagination
+     * 
+     * @param first first result to return
+     * @return <code>this</code> for method chaining
+     */
+    AdminEventQuery firstResult(int first);
+
+    /**
+     * Use for pagination
+     * 
+     * @param max the maximum results to return
+     * @return <code>this</code> for method chaining
+     */
+    AdminEventQuery maxResults(int max);
+
+    /**
+     * Executes the query and returns the results
+     * 
+     * @return
+     */
+    List<AdminEvent> getResultList();
+
+}
diff --git a/events/api/src/main/java/org/keycloak/events/admin/AuthDetails.java b/events/api/src/main/java/org/keycloak/events/admin/AuthDetails.java
new file mode 100644
index 0000000..d32eff1
--- /dev/null
+++ b/events/api/src/main/java/org/keycloak/events/admin/AuthDetails.java
@@ -0,0 +1,48 @@
+package org.keycloak.events.admin;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class AuthDetails {
+
+    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/events/api/src/main/java/org/keycloak/events/admin/AuthQuery.java b/events/api/src/main/java/org/keycloak/events/admin/AuthQuery.java
new file mode 100644
index 0000000..3be431f
--- /dev/null
+++ b/events/api/src/main/java/org/keycloak/events/admin/AuthQuery.java
@@ -0,0 +1,48 @@
+package org.keycloak.events.admin;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class AuthQuery {
+
+    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/events/api/src/main/java/org/keycloak/events/admin/OperationType.java b/events/api/src/main/java/org/keycloak/events/admin/OperationType.java
new file mode 100755
index 0000000..ddce0e2
--- /dev/null
+++ b/events/api/src/main/java/org/keycloak/events/admin/OperationType.java
@@ -0,0 +1,24 @@
+package org.keycloak.events.admin;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public enum OperationType {
+
+    VIEW(false),
+    CREATE(true),
+    UPDATE(true),
+    DELETE(true),
+    ACTION(false);
+
+    private boolean saveByDefault;
+
+    OperationType(boolean saveByDefault) {
+        this.saveByDefault = saveByDefault;
+    }
+
+    public boolean isSaveByDefault() {
+        return saveByDefault;
+    }
+
+}
diff --git a/events/api/src/main/java/org/keycloak/events/AdminEventBuilder.java b/events/api/src/main/java/org/keycloak/events/AdminEventBuilder.java
new file mode 100644
index 0000000..e822217
--- /dev/null
+++ b/events/api/src/main/java/org/keycloak/events/AdminEventBuilder.java
@@ -0,0 +1,205 @@
+package org.keycloak.events;
+
+import java.io.IOException;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.jboss.logging.Logger;
+import org.keycloak.ClientConnection;
+import org.keycloak.events.admin.AdminEvent;
+import org.keycloak.events.admin.AuthDetails;
+import org.keycloak.events.admin.OperationType;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.util.JsonSerialization;
+import org.keycloak.util.Time;
+
+public class AdminEventBuilder {
+    
+    private static final Logger log = Logger.getLogger(AdminEventBuilder.class);
+
+    private EventStoreProvider store;
+    private List<EventListenerProvider> listeners;
+    private RealmModel realm;
+    private AdminEvent adminEvent;
+
+    public AdminEventBuilder(RealmModel realm, KeycloakSession session, ClientConnection clientConnection) {
+        this.realm = realm;
+
+        adminEvent = new AdminEvent();
+
+        if (realm.isAdminEventsEnabled()) {
+            EventStoreProvider store = session.getProvider(EventStoreProvider.class);
+            if (store != null) {
+                this.store = store;
+            } else {
+                log.error("Admin Events enabled, but no event store provider configured");
+            }
+        }
+
+        if (realm.getEventsListeners() != null && !realm.getEventsListeners().isEmpty()) {
+            this.listeners = new LinkedList<>();
+            for (String id : realm.getEventsListeners()) {
+                EventListenerProvider listener = session.getProvider(EventListenerProvider.class, id);
+                if (listener != null) {
+                    listeners.add(listener);
+                } else {
+                    log.error("Event listener '" + id + "' registered, but provider not found");
+                }
+            }
+        }
+
+        realm(realm);
+        ipAddress(clientConnection.getRemoteAddr());
+    }
+    
+    public AdminEventBuilder operation(OperationType e) {
+        adminEvent.setOperationType(e);
+        return this;
+    }
+
+    public AdminEventBuilder realm(RealmModel realm) {
+        AuthDetails authDetails = adminEvent.getAuthDetails();
+        if(authDetails == null) {
+            authDetails =  new AuthDetails();
+            authDetails.setRealmId(realm.getId());
+        } else {
+            authDetails.setRealmId(realm.getId());
+        }
+        adminEvent.setAuthDetails(authDetails);
+        return this;
+    }
+
+    public AdminEventBuilder realm(String realmId) {
+        AuthDetails authDetails = adminEvent.getAuthDetails();
+        if(authDetails == null) {
+            authDetails =  new AuthDetails();
+            authDetails.setRealmId(realmId);
+        } else {
+            authDetails.setRealmId(realmId);
+        }
+        adminEvent.setAuthDetails(authDetails);
+        return this;
+    }
+
+    public AdminEventBuilder client(ClientModel client) {
+        AuthDetails authDetails = adminEvent.getAuthDetails();
+        if(authDetails == null) {
+            authDetails =  new AuthDetails();
+            authDetails.setClientId(client.getId());
+        } else {
+            authDetails.setClientId(client.getId());
+        }
+        adminEvent.setAuthDetails(authDetails);
+        return this;
+    }
+
+    public AdminEventBuilder client(String clientId) {
+        AuthDetails authDetails = adminEvent.getAuthDetails();
+        if(authDetails == null) {
+            authDetails =  new AuthDetails();
+            authDetails.setClientId(clientId);
+        } else {
+            authDetails.setClientId(clientId);
+        }
+        adminEvent.setAuthDetails(authDetails);
+        return this;
+    }
+
+    public AdminEventBuilder user(UserModel user) {
+        AuthDetails authDetails = adminEvent.getAuthDetails();
+        if(authDetails == null) {
+            authDetails =  new AuthDetails();
+            authDetails.setUserId(user.getId());
+        } else {
+            authDetails.setUserId(user.getId());
+        }
+        adminEvent.setAuthDetails(authDetails);
+        return this;
+    }
+
+    public AdminEventBuilder user(String userId) {
+        AuthDetails authDetails = adminEvent.getAuthDetails();
+        if(authDetails == null) {
+            authDetails =  new AuthDetails();
+            authDetails.setUserId(userId);
+        } else {
+            authDetails.setUserId(userId);
+        }
+        adminEvent.setAuthDetails(authDetails);
+        return this;
+    }
+
+    public AdminEventBuilder ipAddress(String ipAddress) {
+        AuthDetails authDetails = adminEvent.getAuthDetails();
+        if(authDetails == null) {
+            authDetails =  new AuthDetails();
+            authDetails.setIpAddress(ipAddress);
+        } else {
+            authDetails.setIpAddress(ipAddress);
+        }
+        adminEvent.setAuthDetails(authDetails);
+        return this;
+    }
+    
+    public AdminEventBuilder resourcePath(String resourcePath) {
+        adminEvent.setResourcePath(resourcePath);
+        return this;
+    }
+
+    public void error(String error) {
+        adminEvent.setOperationType(OperationType.valueOf(adminEvent.getOperationType().name() + "_ERROR"));
+        adminEvent.setError(error);
+        send();
+    }
+    
+    public AdminEventBuilder representation(Object value) {
+        if (value == null || value.equals("")) {
+            return this;
+        }
+        try {
+            adminEvent.setRepresentation(JsonSerialization.writeValueAsString(value));
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+        return this;
+    }
+    
+    public AdminEvent getEvent() {
+        return adminEvent;
+    }
+
+    public void success() {
+        send();
+    }
+
+    private void send() {
+        boolean includeRepresentation = false;
+        if(realm.isAdminEventsDetailsEnabled()) {
+            includeRepresentation = true;
+        }
+        adminEvent.setTime(Time.toMillis(Time.currentTime()));
+
+        if (store != null) {
+            if (realm.getAdminEnabledEventOperations() != null && !realm.getAdminEnabledEventOperations().isEmpty() ? realm.getAdminEnabledEventOperations().contains(adminEvent.getOperationType().name()) : adminEvent.getOperationType().isSaveByDefault()) {
+                try {
+                    store.onEvent(adminEvent, includeRepresentation);
+                } catch (Throwable t) {
+                    log.error("Failed to save event", t);
+                }
+            }
+        }
+        
+        if (listeners != null) {
+            for (EventListenerProvider l : listeners) {
+                try {
+                    l.onEvent(adminEvent, includeRepresentation);
+                } catch (Throwable t) {
+                    log.error("Failed to send type to " + l, t);
+                }
+            }
+        }
+    }
+}
diff --git a/events/api/src/main/java/org/keycloak/events/EventBuilder.java b/events/api/src/main/java/org/keycloak/events/EventBuilder.java
index 4945989..ee7a70a 100644
--- a/events/api/src/main/java/org/keycloak/events/EventBuilder.java
+++ b/events/api/src/main/java/org/keycloak/events/EventBuilder.java
@@ -7,6 +7,7 @@ import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.UserModel;
 import org.keycloak.models.UserSessionModel;
+import org.keycloak.util.Time;
 
 import java.util.HashMap;
 import java.util.LinkedList;
@@ -149,7 +150,7 @@ public class EventBuilder {
     }
 
     private void send() {
-        event.setTime(System.currentTimeMillis());
+        event.setTime(Time.toMillis(Time.currentTime()));
 
         if (store != null) {
             if (realm.getEnabledEventTypes() != null && !realm.getEnabledEventTypes().isEmpty() ? realm.getEnabledEventTypes().contains(event.getType().name()) : event.getType().isSaveByDefault()) {
diff --git a/events/api/src/main/java/org/keycloak/events/EventListenerProvider.java b/events/api/src/main/java/org/keycloak/events/EventListenerProvider.java
index 01ab302..957d639 100644
--- a/events/api/src/main/java/org/keycloak/events/EventListenerProvider.java
+++ b/events/api/src/main/java/org/keycloak/events/EventListenerProvider.java
@@ -1,5 +1,6 @@
 package org.keycloak.events;
 
+import org.keycloak.events.admin.AdminEvent;
 import org.keycloak.provider.Provider;
 
 /**
@@ -9,4 +10,6 @@ public interface EventListenerProvider extends Provider {
 
     public void onEvent(Event event);
 
+    public void onEvent(AdminEvent event, boolean includeRepresentation);
+
 }
diff --git a/events/api/src/main/java/org/keycloak/events/EventStoreProvider.java b/events/api/src/main/java/org/keycloak/events/EventStoreProvider.java
index c742556..afdaf92 100644
--- a/events/api/src/main/java/org/keycloak/events/EventStoreProvider.java
+++ b/events/api/src/main/java/org/keycloak/events/EventStoreProvider.java
@@ -1,5 +1,7 @@
 package org.keycloak.events;
 
+import org.keycloak.events.admin.AdminEventQuery;
+
 /**
  * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
  */
@@ -7,10 +9,18 @@ public interface EventStoreProvider extends EventListenerProvider {
 
     public EventQuery createQuery();
 
+    public AdminEventQuery createAdminQuery();
+
     public void clear();
 
     public void clear(String realmId);
 
     public void clear(String realmId, long olderThan);
 
+    public void clearAdmin();
+
+    public void clearAdmin(String realmId);
+
+    public void clearAdmin(String realmId, long olderThan);
+
 }
diff --git a/events/email/src/main/java/org/keycloak/events/email/EmailEventListenerProvider.java b/events/email/src/main/java/org/keycloak/events/email/EmailEventListenerProvider.java
index 71a432a..400ef04 100755
--- a/events/email/src/main/java/org/keycloak/events/email/EmailEventListenerProvider.java
+++ b/events/email/src/main/java/org/keycloak/events/email/EmailEventListenerProvider.java
@@ -3,6 +3,7 @@ package org.keycloak.events.email;
 import org.jboss.logging.Logger;
 import org.keycloak.email.EmailException;
 import org.keycloak.email.EmailProvider;
+import org.keycloak.events.admin.AdminEvent;
 import org.keycloak.events.Event;
 import org.keycloak.events.EventListenerProvider;
 import org.keycloak.events.EventType;
@@ -50,6 +51,11 @@ public class EmailEventListenerProvider implements EventListenerProvider {
     }
 
     @Override
+    public void onEvent(AdminEvent event, boolean includeRepresentation) {
+
+    }
+
+    @Override
     public void close() {
     }
 
diff --git a/events/jboss-logging/src/main/java/org/keycloak/events/log/JBossLoggingEventListenerProvider.java b/events/jboss-logging/src/main/java/org/keycloak/events/log/JBossLoggingEventListenerProvider.java
index dba4304..38283f7 100755
--- a/events/jboss-logging/src/main/java/org/keycloak/events/log/JBossLoggingEventListenerProvider.java
+++ b/events/jboss-logging/src/main/java/org/keycloak/events/log/JBossLoggingEventListenerProvider.java
@@ -1,6 +1,7 @@
 package org.keycloak.events.log;
 
 import org.jboss.logging.Logger;
+import org.keycloak.events.admin.AdminEvent;
 import org.keycloak.events.Event;
 import org.keycloak.events.EventListenerProvider;
 import org.keycloak.models.KeycloakContext;
@@ -9,8 +10,8 @@ import org.keycloak.models.KeycloakSession;
 import javax.ws.rs.core.Cookie;
 import javax.ws.rs.core.HttpHeaders;
 import javax.ws.rs.core.UriInfo;
+
 import java.util.Map;
-import java.util.logging.Level;
 
 /**
  * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
@@ -66,29 +67,42 @@ public class JBossLoggingEventListenerProvider implements EventListenerProvider 
                     }
                 }
             }
+            
+            if(logger.isTraceEnabled()) {
+                setKeycloakContext(sb);
+            }
 
-            if (logger.isTraceEnabled()) {
-                KeycloakContext context = session.getContext();
-                UriInfo uriInfo = context.getUri();
-                HttpHeaders headers = context.getRequestHeaders();
-                if (uriInfo != null) {
-                    sb.append(", requestUri=");
-                    sb.append(uriInfo.getRequestUri().toString());
-                }
+            logger.log(logger.isTraceEnabled() ? Logger.Level.TRACE : level, sb.toString());
+        }
+    }
 
-                if (headers != null) {
-                    sb.append(", cookies=[");
-                    boolean f = true;
-                    for (Map.Entry<String, Cookie> e : headers.getCookies().entrySet()) {
-                        if (f) {
-                            f = false;
-                        } else {
-                            sb.append(", ");
-                        }
-                        sb.append(e.getValue().toString());
-                    }
-                    sb.append("]");
-                }
+    @Override
+    public void onEvent(AdminEvent adminEvent, boolean includeRepresentation) {
+        Logger.Level level = adminEvent.getError() != null ? errorLevel : successLevel;
+
+        if (logger.isEnabled(level)) {
+            StringBuilder sb = new StringBuilder();
+
+            sb.append("operationType=");
+            sb.append(adminEvent.getOperationType());
+            sb.append(", realmId=");
+            sb.append(adminEvent.getAuthDetails().getRealmId());
+            sb.append(", clientId=");
+            sb.append(adminEvent.getAuthDetails().getClientId());
+            sb.append(", userId=");
+            sb.append(adminEvent.getAuthDetails().getUserId());
+            sb.append(", ipAddress=");
+            sb.append(adminEvent.getAuthDetails().getIpAddress());
+            sb.append(", resourcePath=");
+            sb.append(adminEvent.getResourcePath());
+
+            if (adminEvent.getError() != null) {
+                sb.append(", error=");
+                sb.append(adminEvent.getError());
+            }
+            
+            if(logger.isTraceEnabled()) {
+                setKeycloakContext(sb);
             }
 
             logger.log(logger.isTraceEnabled() ? Logger.Level.TRACE : level, sb.toString());
@@ -98,5 +112,30 @@ public class JBossLoggingEventListenerProvider implements EventListenerProvider 
     @Override
     public void close() {
     }
+    
+    private void setKeycloakContext(StringBuilder sb) {
+        KeycloakContext context = session.getContext();
+        UriInfo uriInfo = context.getUri();
+        HttpHeaders headers = context.getRequestHeaders();
+        if (uriInfo != null) {
+            sb.append(", requestUri=");
+            sb.append(uriInfo.getRequestUri().toString());
+        }
+
+        if (headers != null) {
+            sb.append(", cookies=[");
+            boolean f = true;
+            for (Map.Entry<String, Cookie> e : headers.getCookies().entrySet()) {
+                if (f) {
+                    f = false;
+                } else {
+                    sb.append(", ");
+                }
+                sb.append(e.getValue().toString());
+            }
+            sb.append("]");
+        }
+        
+    }
 
 }
diff --git a/events/jpa/src/main/java/org/keycloak/events/jpa/AdminEventEntity.java b/events/jpa/src/main/java/org/keycloak/events/jpa/AdminEventEntity.java
new file mode 100644
index 0000000..f45cd32
--- /dev/null
+++ b/events/jpa/src/main/java/org/keycloak/events/jpa/AdminEventEntity.java
@@ -0,0 +1,126 @@
+package org.keycloak.events.jpa;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.Table;
+
+/**
+ * @author <a href="mailto:giriraj.sharma27@gmail.com">Giriraj Sharma</a>
+ */
+@Entity
+@Table(name="ADMIN_EVENT_ENTITY")
+public class AdminEventEntity {
+    
+    @Id
+    @Column(name="ID", length = 36)
+    private String id;
+    
+    @Column(name="ADMIN_EVENT_TIME")
+    private long time;
+    
+    @Column(name="OPERATION_TYPE")
+    private String operationType;
+    
+    @Column(name="REALM_ID")
+    private String authRealmId;
+    
+    @Column(name="CLIENT_ID")
+    private String authClientId;
+
+    @Column(name="USER_ID")
+    private String authUserId;
+    
+    @Column(name="IP_ADDRESS")
+    private String authIpAddress;
+    
+    @Column(name="RESOURCE_PATH")
+    private String resourcePath;
+
+    @Column(name="REPRESENTATION", length = 25500)
+    private String representation;
+
+    @Column(name="ERROR")
+    private String error;
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public long getTime() {
+        return time;
+    }
+
+    public void setTime(long time) {
+        this.time = time;
+    }
+
+    public String getOperationType() {
+        return operationType;
+    }
+
+    public void setOperationType(String operationType) {
+        this.operationType = operationType;
+    }
+
+    public String getAuthRealmId() {
+        return authRealmId;
+    }
+
+    public void setAuthRealmId(String authRealmId) {
+        this.authRealmId = authRealmId;
+    }
+
+    public String getAuthClientId() {
+        return authClientId;
+    }
+
+    public void setAuthClientId(String authClientId) {
+        this.authClientId = authClientId;
+    }
+
+    public String getAuthUserId() {
+        return authUserId;
+    }
+
+    public void setAuthUserId(String authUserId) {
+        this.authUserId = authUserId;
+    }
+
+    public String getAuthIpAddress() {
+        return authIpAddress;
+    }
+
+    public void setAuthIpAddress(String authIpAddress) {
+        this.authIpAddress = authIpAddress;
+    }
+
+    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/events/jpa/src/main/java/org/keycloak/events/jpa/JpaAdminEventQuery.java b/events/jpa/src/main/java/org/keycloak/events/jpa/JpaAdminEventQuery.java
new file mode 100644
index 0000000..edb19c0
--- /dev/null
+++ b/events/jpa/src/main/java/org/keycloak/events/jpa/JpaAdminEventQuery.java
@@ -0,0 +1,148 @@
+package org.keycloak.events.jpa;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.persistence.EntityManager;
+import javax.persistence.TypedQuery;
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.criteria.Expression;
+import javax.persistence.criteria.Predicate;
+import javax.persistence.criteria.Root;
+
+import org.keycloak.events.admin.AdminEvent;
+import org.keycloak.events.admin.AdminEventQuery;
+import org.keycloak.events.admin.OperationType;
+
+/**
+ * @author <a href="mailto:giriraj.sharma27@gmail.com">Giriraj Sharma</a>
+ */
+public class JpaAdminEventQuery implements AdminEventQuery {
+    
+    private final EntityManager em;
+    private final CriteriaBuilder cb;
+    private final CriteriaQuery<AdminEventEntity> cq;
+    private final Root<AdminEventEntity> root;
+    private final ArrayList<Predicate> predicates;
+    private Integer firstResult;
+    private Integer maxResults;
+    
+    public JpaAdminEventQuery(EntityManager em) {
+        this.em = em;
+
+        cb = em.getCriteriaBuilder();
+        cq = cb.createQuery(AdminEventEntity.class);
+        root = cq.from(AdminEventEntity.class);
+        predicates = new ArrayList<Predicate>();
+    }
+
+    @Override
+    public AdminEventQuery operation(OperationType... operations) {
+        List<String> operationStrings = new LinkedList<String>();
+        for (OperationType e : operations) {
+            operationStrings.add(e.toString());
+        }
+        predicates.add(root.get("operationType").in(operationStrings));
+        return this;
+    }
+    
+    @Override
+    public AdminEventQuery authRealm(String realmId) {
+        predicates.add(cb.equal(root.get("authRealmId"), realmId));
+        return this;
+    }
+
+    @Override
+    public AdminEventQuery authClient(String clientId) {
+        predicates.add(cb.equal(root.get("authClientId"), clientId));
+        return this;
+    }
+
+    @Override
+    public AdminEventQuery authUser(String userId) {
+        predicates.add(cb.equal(root.get("authUserId"), userId));
+        return this;
+    }
+
+    @Override
+    public AdminEventQuery authIpAddress(String ipAddress) {
+        predicates.add(cb.equal(root.get("authIpAddress"), ipAddress));
+        return this;
+    }
+
+    @Override
+    public AdminEventQuery resourcePath(String resourcePath) {
+        Expression<String> rPath = root.get("resourcePath");
+        predicates.add(cb.like(rPath, "%"+resourcePath+"%"));
+        return this;
+    }
+
+    @Override
+    public AdminEventQuery fromTime(String fromTime) {
+        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
+        Long from = null;
+        try {
+            from = df.parse(fromTime).getTime();
+        } catch (ParseException e) {
+            e.printStackTrace();
+        }
+        predicates.add(cb.greaterThanOrEqualTo(root.<Long>get("time"), from));
+        return this;
+    }
+
+    @Override
+    public AdminEventQuery toTime(String toTime) {
+        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
+        Long to = null;
+        try {
+            to = df.parse(toTime).getTime();
+        } catch (ParseException e) {
+            e.printStackTrace();
+        }
+        predicates.add(cb.lessThanOrEqualTo(root.<Long>get("time"), to));
+        return this;
+    }
+
+    @Override
+    public AdminEventQuery firstResult(int firstResult) {
+        this.firstResult = firstResult;
+        return this;
+    }
+
+    @Override
+    public AdminEventQuery maxResults(int maxResults) {
+        this.maxResults = maxResults;
+        return this;
+    }
+
+    @Override
+    public List<AdminEvent> getResultList() {
+        if (!predicates.isEmpty()) {
+            cq.where(cb.and(predicates.toArray(new Predicate[predicates.size()])));
+        }
+
+        cq.orderBy(cb.desc(root.get("time")));
+
+        TypedQuery<AdminEventEntity> query = em.createQuery(cq);
+
+        if (firstResult != null) {
+            query.setFirstResult(firstResult);
+        }
+
+        if (maxResults != null) {
+            query.setMaxResults(maxResults);
+        }
+
+        List<AdminEvent> events = new LinkedList<AdminEvent>();
+        for (AdminEventEntity e : query.getResultList()) {
+            events.add(JpaEventStoreProvider.convertAdminEvent(e));
+        }
+
+        return events;
+    }
+        
+}
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 5e39d17..ffbf619 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
@@ -131,7 +131,7 @@ public class JpaEventQuery implements EventQuery {
 
         List<Event> events = new LinkedList<Event>();
         for (EventEntity e : query.getResultList()) {
-            events.add(JpaEventStoreProvider.convert(e));
+            events.add(JpaEventStoreProvider.convertEvent(e));
         }
 
         return events;
diff --git a/events/jpa/src/main/java/org/keycloak/events/jpa/JpaEventStoreProvider.java b/events/jpa/src/main/java/org/keycloak/events/jpa/JpaEventStoreProvider.java
index 8fca6ec..3088f1e 100755
--- a/events/jpa/src/main/java/org/keycloak/events/jpa/JpaEventStoreProvider.java
+++ b/events/jpa/src/main/java/org/keycloak/events/jpa/JpaEventStoreProvider.java
@@ -3,12 +3,17 @@ package org.keycloak.events.jpa;
 import org.codehaus.jackson.map.ObjectMapper;
 import org.codehaus.jackson.type.TypeReference;
 import org.jboss.logging.Logger;
+import org.keycloak.events.admin.AdminEvent;
+import org.keycloak.events.admin.AdminEventQuery;
+import org.keycloak.events.admin.AuthDetails;
+import org.keycloak.events.admin.OperationType;
 import org.keycloak.events.Event;
 import org.keycloak.events.EventQuery;
 import org.keycloak.events.EventStoreProvider;
 import org.keycloak.events.EventType;
 
 import javax.persistence.EntityManager;
+
 import java.io.IOException;
 import java.util.Map;
 import java.util.UUID;
@@ -51,49 +56,119 @@ public class JpaEventStoreProvider implements EventStoreProvider {
 
     @Override
     public void onEvent(Event event) {
-        em.persist(convert(event));
+        em.persist(convertEvent(event));
+    }
+
+    @Override
+    public AdminEventQuery createAdminQuery() {
+        return new JpaAdminEventQuery(em);
+    }
+
+    @Override
+    public void clearAdmin() {
+        em.createQuery("delete from AdminEventEntity").executeUpdate();
+    }
+
+    @Override
+    public void clearAdmin(String authRealmId) {
+        em.createQuery("delete from AdminEventEntity where authRealmId = :authRealmId").setParameter("authRealmId", authRealmId).executeUpdate();
+    }
+
+    @Override
+    public void clearAdmin(String authRealmId, long olderThan) {
+        em.createQuery("delete from AdminEventEntity where authRealmId = :authRealmId and time < :time").setParameter("authRealmId", authRealmId).setParameter("time", olderThan).executeUpdate();
+    }
+
+    @Override
+    public void onEvent(AdminEvent event, boolean includeRepresentation) {
+        em.persist(convertAdminEvent(event, includeRepresentation));
     }
 
     @Override
     public void close() {
     }
 
-    static EventEntity convert(Event o) {
-        EventEntity e = new EventEntity();
-        e.setId(UUID.randomUUID().toString());
-        e.setTime(o.getTime());
-        e.setType(o.getType().toString());
-        e.setRealmId(o.getRealmId());
-        e.setClientId(o.getClientId());
-        e.setUserId(o.getUserId());
-        e.setSessionId(o.getSessionId());
-        e.setIpAddress(o.getIpAddress());
-        e.setError(o.getError());
+    static EventEntity convertEvent(Event event) {
+        EventEntity eventEntity = new EventEntity();
+        eventEntity.setId(UUID.randomUUID().toString());
+        eventEntity.setTime(event.getTime());
+        eventEntity.setType(event.getType().toString());
+        eventEntity.setRealmId(event.getRealmId());
+        eventEntity.setClientId(event.getClientId());
+        eventEntity.setUserId(event.getUserId());
+        eventEntity.setSessionId(event.getSessionId());
+        eventEntity.setIpAddress(event.getIpAddress());
+        eventEntity.setError(event.getError());
         try {
-            e.setDetailsJson(mapper.writeValueAsString(o.getDetails()));
+            eventEntity.setDetailsJson(mapper.writeValueAsString(event.getDetails()));
         } catch (IOException ex) {
             logger.error("Failed to write log details", ex);
         }
-        return e;
-    }
-
-    static Event convert(EventEntity o) {
-        Event e = new Event();
-        e.setTime(o.getTime());
-        e.setType(EventType.valueOf(o.getType()));
-        e.setRealmId(o.getRealmId());
-        e.setClientId(o.getClientId());
-        e.setUserId(o.getUserId());
-        e.setSessionId(o.getSessionId());
-        e.setIpAddress(o.getIpAddress());
-        e.setError(o.getError());
+        return eventEntity;
+    }
+
+    static Event convertEvent(EventEntity eventEntity) {
+        Event event = new Event();
+        event.setTime(eventEntity.getTime());
+        event.setType(EventType.valueOf(eventEntity.getType()));
+        event.setRealmId(eventEntity.getRealmId());
+        event.setClientId(eventEntity.getClientId());
+        event.setUserId(eventEntity.getUserId());
+        event.setSessionId(eventEntity.getSessionId());
+        event.setIpAddress(eventEntity.getIpAddress());
+        event.setError(eventEntity.getError());
         try {
-            Map<String, String> details = mapper.readValue(o.getDetailsJson(), mapType);
-            e.setDetails(details);
+            Map<String, String> details = mapper.readValue(eventEntity.getDetailsJson(), mapType);
+            event.setDetails(details);
         } catch (IOException ex) {
             logger.error("Failed to read log details", ex);
         }
-        return e;
+        return event;
+    }
+    
+    static AdminEventEntity convertAdminEvent(AdminEvent adminEvent, boolean includeRepresentation) {
+        AdminEventEntity adminEventEntity = new AdminEventEntity();
+        adminEventEntity.setId(UUID.randomUUID().toString());
+        adminEventEntity.setTime(adminEvent.getTime());
+        setAuthDetails(adminEventEntity, adminEvent.getAuthDetails());
+        adminEventEntity.setOperationType(adminEvent.getOperationType().toString());
+        adminEventEntity.setResourcePath(adminEvent.getResourcePath());
+        adminEventEntity.setError(adminEvent.getError());
+        
+        if(includeRepresentation) {
+            adminEventEntity.setRepresentation(adminEvent.getRepresentation());
+        }
+        return adminEventEntity;
+    }
+
+    static AdminEvent convertAdminEvent(AdminEventEntity adminEventEntity) {
+        AdminEvent adminEvent = new AdminEvent();
+        adminEvent.setTime(adminEventEntity.getTime());
+        setAuthDetails(adminEvent, adminEventEntity);
+        adminEvent.setOperationType(OperationType.valueOf(adminEventEntity.getOperationType()));
+        adminEvent.setResourcePath(adminEventEntity.getResourcePath());
+        adminEvent.setError(adminEventEntity.getError());
+        
+        if(adminEventEntity.getRepresentation() != null) {
+            adminEvent.setRepresentation(adminEventEntity.getRepresentation());
+        }
+        return adminEvent;
+    }
+    
+    private static void setAuthDetails(AdminEventEntity adminEventEntity, AuthDetails authDetails) {
+        adminEventEntity.setAuthRealmId(authDetails.getRealmId());
+        adminEventEntity.setAuthClientId(authDetails.getClientId());
+        adminEventEntity.setAuthUserId(authDetails.getUserId());
+        adminEventEntity.setAuthIpAddress(authDetails.getIpAddress());
+    }
+    
+    private static void setAuthDetails(AdminEvent adminEvent, AdminEventEntity adminEventEntity) {
+        AuthDetails authDetails = new AuthDetails();
+        authDetails.setRealmId(adminEventEntity.getAuthRealmId());
+        authDetails.setClientId(adminEventEntity.getAuthClientId());
+        authDetails.setUserId(adminEventEntity.getAuthUserId());
+        authDetails.setIpAddress(adminEventEntity.getAuthIpAddress());
+        adminEvent.setAuthDetails(authDetails);
     }
 
 }
diff --git a/events/mongo/src/main/java/org/keycloak/events/mongo/MongoAdminEventQuery.java b/events/mongo/src/main/java/org/keycloak/events/mongo/MongoAdminEventQuery.java
new file mode 100644
index 0000000..4cc366c
--- /dev/null
+++ b/events/mongo/src/main/java/org/keycloak/events/mongo/MongoAdminEventQuery.java
@@ -0,0 +1,126 @@
+package org.keycloak.events.mongo;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.regex.Pattern;
+
+import org.keycloak.events.admin.AdminEvent;
+import org.keycloak.events.admin.AdminEventQuery;
+import org.keycloak.events.admin.OperationType;
+
+import com.mongodb.BasicDBObject;
+import com.mongodb.BasicDBObjectBuilder;
+import com.mongodb.DBCollection;
+import com.mongodb.DBCursor;
+
+public class MongoAdminEventQuery implements AdminEventQuery{
+    
+    private Integer firstResult;
+    private Integer maxResults;
+    private DBCollection audit;
+    private final BasicDBObject query;
+
+    public MongoAdminEventQuery(DBCollection audit) {
+        this.audit = audit;
+        query = new BasicDBObject();
+    }
+
+    @Override
+    public AdminEventQuery operation(OperationType... operations) {
+        List<String> operationStrings = new LinkedList<String>();
+        for (OperationType e : operations) {
+            operationStrings.add(e.toString());
+        }
+        query.put("operationType", new BasicDBObject("$in", operationStrings));
+        return this;
+    }
+    
+    @Override
+    public AdminEventQuery authRealm(String realmId) {
+        query.put("realmId", realmId);
+        return this;
+    }
+
+    @Override
+    public AdminEventQuery authClient(String clientId) {
+        query.put("clientId", clientId);
+        return this;
+    }
+
+    @Override
+    public AdminEventQuery authUser(String userId) {
+        query.put("userId", userId);
+        return this;
+    }
+
+    @Override
+    public AdminEventQuery authIpAddress(String ipAddress) {
+        query.put("ipAddress", ipAddress);
+        return this;
+    }
+    
+    @Override
+    public AdminEventQuery resourcePath(String resourcePath) {
+        query.put("resourcePath", Pattern.compile(resourcePath));
+        return this;
+    }
+
+    @Override
+    public AdminEventQuery fromTime(String fromTime) {
+        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
+        Long from = null;
+        try {
+            from = df.parse(fromTime).getTime();
+        } catch (ParseException e) {
+            e.printStackTrace();
+        }
+        query.put("time", BasicDBObjectBuilder.start("$gte", from).get());
+        return this;
+    }
+
+    @Override
+    public AdminEventQuery toTime(String toTime) {
+        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
+        Long to = null;
+        try {
+            to = df.parse(toTime).getTime();
+        } catch (ParseException e) {
+            e.printStackTrace();
+        }
+        query.put("time", BasicDBObjectBuilder.start("$lte", to).get());
+        return this;
+    }
+
+    @Override
+    public AdminEventQuery firstResult(int firstResult) {
+        this.firstResult = firstResult;
+        return this;
+    }
+
+    @Override
+    public AdminEventQuery maxResults(int maxResults) {
+        this.maxResults = maxResults;
+        return this;
+    }
+
+    @Override
+    public List<AdminEvent> getResultList() {
+        DBCursor cur = audit.find(query).sort(new BasicDBObject("time", -1));
+        if (firstResult != null) {
+            cur.skip(firstResult);
+        }
+        if (maxResults != null) {
+            cur.limit(maxResults);
+        }
+
+        List<AdminEvent> events = new LinkedList<AdminEvent>();
+        while (cur.hasNext()) {
+            events.add(MongoEventStoreProvider.convertAdminEvent((BasicDBObject) cur.next()));
+        }
+
+        return events;
+    }
+        
+}
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 c2569cc..75165c7 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
@@ -118,7 +118,7 @@ public class MongoEventQuery implements EventQuery {
 
         List<Event> events = new LinkedList<Event>();
         while (cur.hasNext()) {
-            events.add(MongoEventStoreProvider.convert((BasicDBObject) cur.next()));
+            events.add(MongoEventStoreProvider.convertEvent((BasicDBObject) cur.next()));
         }
 
         return events;
diff --git a/events/mongo/src/main/java/org/keycloak/events/mongo/MongoEventStoreProvider.java b/events/mongo/src/main/java/org/keycloak/events/mongo/MongoEventStoreProvider.java
index 65b6573..4b0dd2c 100755
--- a/events/mongo/src/main/java/org/keycloak/events/mongo/MongoEventStoreProvider.java
+++ b/events/mongo/src/main/java/org/keycloak/events/mongo/MongoEventStoreProvider.java
@@ -3,6 +3,11 @@ package org.keycloak.events.mongo;
 import com.mongodb.BasicDBObject;
 import com.mongodb.DBCollection;
 import com.mongodb.DBObject;
+
+import org.keycloak.events.admin.AdminEvent;
+import org.keycloak.events.admin.AdminEventQuery;
+import org.keycloak.events.admin.AuthDetails;
+import org.keycloak.events.admin.OperationType;
 import org.keycloak.events.Event;
 import org.keycloak.events.EventQuery;
 import org.keycloak.events.EventStoreProvider;
@@ -15,11 +20,13 @@ import java.util.Map;
  * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
  */
 public class MongoEventStoreProvider implements EventStoreProvider {
-
+    
     private DBCollection events;
+    private DBCollection adminEvents;
 
-    public MongoEventStoreProvider(DBCollection events) {
+    public MongoEventStoreProvider(DBCollection events, DBCollection adminEvents) {
         this.events = events;
+        this.adminEvents = adminEvents;
     }
 
     @Override
@@ -47,27 +54,55 @@ public class MongoEventStoreProvider implements EventStoreProvider {
 
     @Override
     public void onEvent(Event event) {
-        events.insert(convert(event));
+        events.insert(convertEvent(event));
+    }
+
+    @Override
+    public AdminEventQuery createAdminQuery() {
+        return new MongoAdminEventQuery(adminEvents);
+    }
+
+    @Override
+    public void clearAdmin() {
+        adminEvents.remove(new BasicDBObject());
+    }
+
+    @Override
+    public void clearAdmin(String realmId) {
+        adminEvents.remove(new BasicDBObject("realmId", realmId));
+    }
+
+    @Override
+    public void clearAdmin(String realmId, long olderThan) {
+        BasicDBObject q = new BasicDBObject();
+        q.put("realmId", realmId);
+        q.put("time", new BasicDBObject("$lt", olderThan));
+        adminEvents.remove(q);
+    }
+
+    @Override
+    public void onEvent(AdminEvent adminEvent, boolean includeRepresentation) {
+        adminEvents.insert(convertAdminEvent(adminEvent, includeRepresentation));
     }
 
     @Override
     public void close() {
     }
 
-    static DBObject convert(Event o) {
+    static DBObject convertEvent(Event event) {
         BasicDBObject e = new BasicDBObject();
-        e.put("time", o.getTime());
-        e.put("type", o.getType().toString());
-        e.put("realmId", o.getRealmId());
-        e.put("clientId", o.getClientId());
-        e.put("userId", o.getUserId());
-        e.put("sessionId", o.getSessionId());
-        e.put("ipAddress", o.getIpAddress());
-        e.put("error", o.getError());
+        e.put("time", event.getTime());
+        e.put("type", event.getType().toString());
+        e.put("realmId", event.getRealmId());
+        e.put("clientId", event.getClientId());
+        e.put("userId", event.getUserId());
+        e.put("sessionId", event.getSessionId());
+        e.put("ipAddress", event.getIpAddress());
+        e.put("error", event.getError());
 
         BasicDBObject details = new BasicDBObject();
-        if (o.getDetails() != null) {
-            for (Map.Entry<String, String> entry : o.getDetails().entrySet()) {
+        if (event.getDetails() != null) {
+            for (Map.Entry<String, String> entry : event.getDetails().entrySet()) {
                 details.put(entry.getKey(), entry.getValue());
             }
         }
@@ -76,16 +111,16 @@ public class MongoEventStoreProvider implements EventStoreProvider {
         return e;
     }
 
-    static Event convert(BasicDBObject o) {
-        Event e = new Event();
-        e.setTime(o.getLong("time"));
-        e.setType(EventType.valueOf(o.getString("type")));
-        e.setRealmId(o.getString("realmId"));
-        e.setClientId(o.getString("clientId"));
-        e.setUserId(o.getString("userId"));
-        e.setSessionId(o.getString("sessionId"));
-        e.setIpAddress(o.getString("ipAddress"));
-        e.setError(o.getString("error"));
+    static Event convertEvent(BasicDBObject o) {
+        Event event = new Event();
+        event.setTime(o.getLong("time"));
+        event.setType(EventType.valueOf(o.getString("type")));
+        event.setRealmId(o.getString("realmId"));
+        event.setClientId(o.getString("clientId"));
+        event.setUserId(o.getString("userId"));
+        event.setSessionId(o.getString("sessionId"));
+        event.setIpAddress(o.getString("ipAddress"));
+        event.setError(o.getString("error"));
 
         BasicDBObject d = (BasicDBObject) o.get("details");
         if (d != null) {
@@ -93,10 +128,55 @@ public class MongoEventStoreProvider implements EventStoreProvider {
             for (Object k : d.keySet()) {
                 details.put((String) k, d.getString((String) k));
             }
-            e.setDetails(details);
+            event.setDetails(details);
+        }
+
+        return event;
+    }
+    
+    private static DBObject convertAdminEvent(AdminEvent adminEvent, boolean includeRepresentation) {
+        BasicDBObject e = new BasicDBObject();
+        e.put("time", adminEvent.getTime());
+        e.put("operationType", adminEvent.getOperationType().toString());
+        setAuthDetails(e, adminEvent.getAuthDetails());
+        e.put("resourcePath", adminEvent.getResourcePath());
+        e.put("error", adminEvent.getError());
+        
+        if(includeRepresentation) {
+            e.put("representation", adminEvent.getRepresentation());
         }
 
         return e;
     }
+    
+    static AdminEvent convertAdminEvent(BasicDBObject o) {
+        AdminEvent adminEvent = new AdminEvent();
+        adminEvent.setTime(o.getLong("time"));
+        adminEvent.setOperationType(OperationType.valueOf(o.getString("operationType")));
+        setAuthDetails(adminEvent, o);
+        adminEvent.setResourcePath(o.getString("resourcePath"));
+        adminEvent.setError(o.getString("error"));
+        
+        if(o.getString("representation") != null) {
+            adminEvent.setRepresentation(o.getString("representation"));
+        }
+        return adminEvent;
+    }
+
+    private static void setAuthDetails(BasicDBObject e, AuthDetails authDetails) {
+        e.put("realmId", authDetails.getRealmId());
+        e.put("clientId", authDetails.getClientId());
+        e.put("userId", authDetails.getUserId());
+        e.put("ipAddress", authDetails.getIpAddress());
+    }
+    
+    private static void setAuthDetails(AdminEvent adminEvent, BasicDBObject o) {
+        AuthDetails authDetails = new AuthDetails();
+        authDetails.setRealmId(o.getString("realmId"));
+        authDetails.setClientId(o.getString("clientId"));
+        authDetails.setUserId(o.getString("userId"));
+        authDetails.setIpAddress(o.getString("ipAddress"));
+        adminEvent.setAuthDetails(authDetails);
+    }
 
 }
diff --git a/events/mongo/src/main/java/org/keycloak/events/mongo/MongoEventStoreProviderFactory.java b/events/mongo/src/main/java/org/keycloak/events/mongo/MongoEventStoreProviderFactory.java
index db4adeb..cbf41ac 100755
--- a/events/mongo/src/main/java/org/keycloak/events/mongo/MongoEventStoreProviderFactory.java
+++ b/events/mongo/src/main/java/org/keycloak/events/mongo/MongoEventStoreProviderFactory.java
@@ -24,9 +24,12 @@ public class MongoEventStoreProviderFactory implements EventStoreProviderFactory
         MongoConnectionProvider connection = session.getProvider(MongoConnectionProvider.class);
 
         DBCollection collection = connection.getDB().getCollection("events");
+        DBCollection adminCollection = connection.getDB().getCollection("adminEvents");
+        
         collection.setWriteConcern(WriteConcern.UNACKNOWLEDGED);
+        adminCollection.setWriteConcern(WriteConcern.UNACKNOWLEDGED);
 
-        return new MongoEventStoreProvider(collection);
+        return new MongoEventStoreProvider(collection, adminCollection);
     }
 
     @Override
diff --git a/events/syslog/src/main/java/org/keycloak/events/log/SysLoggingEventListenerProvider.java b/events/syslog/src/main/java/org/keycloak/events/log/SysLoggingEventListenerProvider.java
index 0536286..14188a8 100755
--- a/events/syslog/src/main/java/org/keycloak/events/log/SysLoggingEventListenerProvider.java
+++ b/events/syslog/src/main/java/org/keycloak/events/log/SysLoggingEventListenerProvider.java
@@ -1,5 +1,6 @@
 package org.keycloak.events.log;
 
+import org.keycloak.events.admin.AdminEvent;
 import org.keycloak.events.Event;
 import org.keycloak.events.EventListenerProvider;
 import org.productivity.java.syslog4j.SyslogConstants;
@@ -59,6 +60,33 @@ public class SysLoggingEventListenerProvider implements EventListenerProvider {
     }
 
     @Override
+    public void onEvent(AdminEvent adminEvent, boolean includeRepresentation) {
+        int level = adminEvent.getError() != null ? SyslogConstants.LEVEL_ERROR : SyslogConstants.LEVEL_INFO;
+
+        StringBuilder sb = new StringBuilder();
+
+        sb.append("operationType=");
+        sb.append(adminEvent.getOperationType());
+        sb.append(", realmId=");
+        sb.append(adminEvent.getAuthDetails().getRealmId());
+        sb.append(", clientId=");
+        sb.append(adminEvent.getAuthDetails().getClientId());
+        sb.append(", userId=");
+        sb.append(adminEvent.getAuthDetails().getUserId());
+        sb.append(", ipAddress=");
+        sb.append(adminEvent.getAuthDetails().getIpAddress());
+        sb.append(", resourcePath=");
+        sb.append(adminEvent.getResourcePath());
+
+        if (adminEvent.getError() != null) {
+            sb.append(", error=");
+            sb.append(adminEvent.getError());
+        }
+
+        syslogger.log(level, sb.toString());
+    }
+
+    @Override
     public void close() {
     }
 
diff --git a/examples/providers/event-listener-sysout/src/main/java/org/keycloak/examples/providers/events/SysoutEventListenerProvider.java b/examples/providers/event-listener-sysout/src/main/java/org/keycloak/examples/providers/events/SysoutEventListenerProvider.java
index 8bd001f..d81288e 100755
--- a/examples/providers/event-listener-sysout/src/main/java/org/keycloak/examples/providers/events/SysoutEventListenerProvider.java
+++ b/examples/providers/event-listener-sysout/src/main/java/org/keycloak/examples/providers/events/SysoutEventListenerProvider.java
@@ -1,5 +1,7 @@
 package org.keycloak.examples.providers.events;
 
+import org.keycloak.events.admin.AdminEvent;
+import org.keycloak.events.admin.OperationType;
 import org.keycloak.events.Event;
 import org.keycloak.events.EventListenerProvider;
 import org.keycloak.events.EventType;
@@ -13,9 +15,11 @@ import java.util.Set;
 public class SysoutEventListenerProvider implements EventListenerProvider {
 
     private Set<EventType> excludedEvents;
+    private Set<OperationType> excludedAdminOperations;
 
-    public SysoutEventListenerProvider(Set<EventType> excludedEvents) {
+    public SysoutEventListenerProvider(Set<EventType> excludedEvents, Set<OperationType> excludedAdminOpearations) {
         this.excludedEvents = excludedEvents;
+        this.excludedAdminOperations = excludedAdminOpearations;
     }
 
     @Override
@@ -28,6 +32,16 @@ public class SysoutEventListenerProvider implements EventListenerProvider {
         }
     }
 
+    @Override
+    public void onEvent(AdminEvent event, boolean includeRepresentation) {
+        // Ignore excluded operations
+        if (excludedAdminOperations != null && excludedAdminOperations.contains(event.getOperationType())) {
+            return;
+        } else {
+            System.out.println("EVENT: " + toString(event));
+        }
+    }
+
     private String toString(Event event) {
         StringBuilder sb = new StringBuilder();
 
@@ -64,7 +78,31 @@ public class SysoutEventListenerProvider implements EventListenerProvider {
 
         return sb.toString();
     }
+    
+    private String toString(AdminEvent adminEvent) {
+        StringBuilder sb = new StringBuilder();
 
+        sb.append("operationType=");
+        sb.append(adminEvent.getOperationType());
+        sb.append(", realmId=");
+        sb.append(adminEvent.getAuthDetails().getRealmId());
+        sb.append(", clientId=");
+        sb.append(adminEvent.getAuthDetails().getClientId());
+        sb.append(", userId=");
+        sb.append(adminEvent.getAuthDetails().getUserId());
+        sb.append(", ipAddress=");
+        sb.append(adminEvent.getAuthDetails().getIpAddress());
+        sb.append(", resourcePath=");
+        sb.append(adminEvent.getResourcePath());
+
+        if (adminEvent.getError() != null) {
+            sb.append(", error=");
+            sb.append(adminEvent.getError());
+        }
+        
+        return sb.toString();
+    }
+    
     @Override
     public void close() {
     }
diff --git a/examples/providers/event-listener-sysout/src/main/java/org/keycloak/examples/providers/events/SysoutEventListenerProviderFactory.java b/examples/providers/event-listener-sysout/src/main/java/org/keycloak/examples/providers/events/SysoutEventListenerProviderFactory.java
index 3f87d81..e7eb8d5 100755
--- a/examples/providers/event-listener-sysout/src/main/java/org/keycloak/examples/providers/events/SysoutEventListenerProviderFactory.java
+++ b/examples/providers/event-listener-sysout/src/main/java/org/keycloak/examples/providers/events/SysoutEventListenerProviderFactory.java
@@ -4,6 +4,7 @@ import org.keycloak.Config;
 import org.keycloak.events.EventListenerProvider;
 import org.keycloak.events.EventListenerProviderFactory;
 import org.keycloak.events.EventType;
+import org.keycloak.events.admin.OperationType;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.KeycloakSessionFactory;
 
@@ -16,10 +17,11 @@ import java.util.Set;
 public class SysoutEventListenerProviderFactory implements EventListenerProviderFactory {
 
     private Set<EventType> excludedEvents;
+    private Set<OperationType> excludedAdminOperations;
 
     @Override
     public EventListenerProvider create(KeycloakSession session) {
-        return new SysoutEventListenerProvider(excludedEvents);
+        return new SysoutEventListenerProvider(excludedEvents, excludedAdminOperations);
     }
 
     @Override
@@ -31,6 +33,14 @@ public class SysoutEventListenerProviderFactory implements EventListenerProvider
                 excludedEvents.add(EventType.valueOf(e));
             }
         }
+        
+        String[] excludesOperations = config.getArray("excludesOperations");
+        if (excludesOperations != null) {
+            excludedAdminOperations = new HashSet<>();
+            for (String e : excludesOperations) {
+                excludedAdminOperations.add(OperationType.valueOf(e));
+            }
+        }
     }
 
     @Override
diff --git a/examples/providers/event-store-mem/src/main/java/org/keycloak/examples/providers/events/MemAdminEventQuery.java b/examples/providers/event-store-mem/src/main/java/org/keycloak/examples/providers/events/MemAdminEventQuery.java
new file mode 100644
index 0000000..830b7b5
--- /dev/null
+++ b/examples/providers/event-store-mem/src/main/java/org/keycloak/examples/providers/events/MemAdminEventQuery.java
@@ -0,0 +1,162 @@
+package org.keycloak.examples.providers.events;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.regex.Pattern;
+
+import org.keycloak.events.admin.AdminEvent;
+import org.keycloak.events.admin.AdminEventQuery;
+import org.keycloak.events.admin.OperationType;
+
+/**
+ * @author <a href="mailto:giriraj.sharma27@gmail.com">Giriraj Sharma</a>
+ */
+public class MemAdminEventQuery implements AdminEventQuery {
+    
+    private List<AdminEvent> adminEvents;
+
+    private int first;
+    private int max;
+
+    public MemAdminEventQuery(List<AdminEvent> events) {
+        this.adminEvents = events;
+    }
+
+    @Override
+    public AdminEventQuery operation(OperationType... operations) {
+        Iterator<AdminEvent> itr = this.adminEvents.iterator();
+        while (itr.hasNext()) {
+            AdminEvent next = itr.next();
+            boolean include = false;
+            for (OperationType e : operations) {
+                if (next.getOperationType().equals(e)) {
+                    include = true;
+                    break;
+                }
+            }
+            if (!include) {
+                itr.remove();
+            }
+        }
+        return this;
+    }
+    
+    @Override
+    public AdminEventQuery authRealm(String realmId) {
+        Iterator<AdminEvent> itr = adminEvents.iterator();
+        while (itr.hasNext()) {
+            if (!itr.next().getAuthDetails().getRealmId().equals(realmId)) {
+                itr.remove();
+            }
+        }
+        return this;
+    }
+
+    @Override
+    public AdminEventQuery authClient(String clientId) {
+        Iterator<AdminEvent> itr = adminEvents.iterator();
+        while (itr.hasNext()) {
+            if (!itr.next().getAuthDetails().getClientId().equals(clientId)) {
+                itr.remove();
+            }
+        }
+        return this;
+    }
+
+    @Override
+    public AdminEventQuery authUser(String userId) {
+        Iterator<AdminEvent> itr = adminEvents.iterator();
+        while (itr.hasNext()) {
+            if (!itr.next().getAuthDetails().getUserId().equals(userId)) {
+                itr.remove();
+            }
+        }
+        return this;
+    }
+
+    @Override
+    public AdminEventQuery authIpAddress(String ipAddress) {
+        Iterator<AdminEvent> itr = adminEvents.iterator();
+        while (itr.hasNext()) {
+            if (!itr.next().getAuthDetails().getIpAddress().equals(ipAddress)) {
+                itr.remove();
+            }
+        }
+        return this;
+    }
+
+    @Override
+    public AdminEventQuery resourcePath(String resourcePath) {
+        Iterator<AdminEvent> itr = this.adminEvents.iterator();
+        while (itr.hasNext()) {
+            if(!Pattern.compile(resourcePath).matcher(itr.next().getResourcePath()).find()) {
+                itr.remove();
+            }
+        }
+        return (AdminEventQuery) this;
+    }
+
+    @Override
+    public AdminEventQuery fromTime(String fromTime) {
+        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
+        Long from = null;
+        try {
+            from = df.parse(fromTime).getTime();
+        } catch (ParseException e) {
+            e.printStackTrace();
+        }
+        
+        Iterator<AdminEvent> itr = this.adminEvents.iterator();
+        while (itr.hasNext()) {
+            if (!(itr.next().getTime() >= from)) {
+                itr.remove();
+            }
+        }
+        return this;
+    }
+
+    @Override
+    public AdminEventQuery toTime(String toTime) {
+        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
+        Long to = null;
+        try {
+            to = df.parse(toTime).getTime();
+        } catch (ParseException e) {
+            e.printStackTrace();
+        }
+        
+        Iterator<AdminEvent> itr = this.adminEvents.iterator();
+        while (itr.hasNext()) {
+            if (!(itr.next().getTime() <= to)) {
+                itr.remove();
+            }
+        }
+        return this;
+    }
+
+    @Override
+    public AdminEventQuery firstResult(int result) {
+        this.first = result;
+        return this;
+    }
+
+    @Override
+    public AdminEventQuery maxResults(int results) {
+        this.max = results;
+        return this;
+    }
+
+    @Override
+    public List<AdminEvent> getResultList() {
+        if (adminEvents.size() < first) {
+            return Collections.emptyList();
+        }
+        int end = first + max <= adminEvents.size() ? first + max : adminEvents.size();
+
+        return adminEvents.subList(first, end);
+    }
+        
+}
diff --git a/examples/providers/event-store-mem/src/main/java/org/keycloak/examples/providers/events/MemEventStoreProvider.java b/examples/providers/event-store-mem/src/main/java/org/keycloak/examples/providers/events/MemEventStoreProvider.java
index 592455c..7dd4606 100755
--- a/examples/providers/event-store-mem/src/main/java/org/keycloak/examples/providers/events/MemEventStoreProvider.java
+++ b/examples/providers/event-store-mem/src/main/java/org/keycloak/examples/providers/events/MemEventStoreProvider.java
@@ -1,5 +1,8 @@
 package org.keycloak.examples.providers.events;
 
+import org.keycloak.events.admin.AdminEvent;
+import org.keycloak.events.admin.AdminEventQuery;
+import org.keycloak.events.admin.OperationType;
 import org.keycloak.events.Event;
 import org.keycloak.events.EventQuery;
 import org.keycloak.events.EventStoreProvider;
@@ -16,10 +19,16 @@ import java.util.Set;
 public class MemEventStoreProvider implements EventStoreProvider {
     private final List<Event> events;
     private final Set<EventType> excludedEvents;
+    private final List<AdminEvent> adminEvents;
+    private final Set<OperationType> excludedOperations;
 
-    public MemEventStoreProvider(List<Event> events, Set<EventType> excludedEvents) {
+    public MemEventStoreProvider(List<Event> events, Set<EventType> excludedEvents, 
+            List<AdminEvent> adminEvents, Set<OperationType> excludedOperations) {
         this.events = events;
         this.excludedEvents = excludedEvents;
+        
+        this.adminEvents = adminEvents;
+        this.excludedOperations = excludedOperations;
     }
 
     @Override
@@ -65,6 +74,48 @@ public class MemEventStoreProvider implements EventStoreProvider {
     }
 
     @Override
+    public AdminEventQuery createAdminQuery() {
+        return new MemAdminEventQuery(new LinkedList<>(adminEvents));
+    }
+
+    @Override
+    public void clearAdmin() {
+
+    }
+
+    @Override
+    public void clearAdmin(String realmId) {
+        synchronized(adminEvents) {
+            Iterator<AdminEvent> itr = adminEvents.iterator();
+            while (itr.hasNext()) {
+                if (itr.next().getAuthDetails().getRealmId().equals(realmId)) {
+                    itr.remove();
+                }
+            }
+        }
+    }
+
+    @Override
+    public void clearAdmin(String realmId, long olderThan) {
+        synchronized(adminEvents) {
+            Iterator<AdminEvent> itr = adminEvents.iterator();
+            while (itr.hasNext()) {
+                AdminEvent e = itr.next();
+                if (e.getAuthDetails().getRealmId().equals(realmId) && e.getTime() < olderThan) {
+                    itr.remove();
+                }
+            }
+        }
+    }
+
+    @Override
+    public void onEvent(AdminEvent adminEvent, boolean includeRepresentation) {
+        if (excludedOperations == null || !excludedOperations.contains(adminEvent.getOperationType())) {
+            adminEvents.add(0, adminEvent);
+        }
+    }
+
+    @Override
     public void close() {
     }
 
diff --git a/examples/providers/event-store-mem/src/main/java/org/keycloak/examples/providers/events/MemEventStoreProviderFactory.java b/examples/providers/event-store-mem/src/main/java/org/keycloak/examples/providers/events/MemEventStoreProviderFactory.java
index 83fd80e..d09b1a3 100755
--- a/examples/providers/event-store-mem/src/main/java/org/keycloak/examples/providers/events/MemEventStoreProviderFactory.java
+++ b/examples/providers/event-store-mem/src/main/java/org/keycloak/examples/providers/events/MemEventStoreProviderFactory.java
@@ -5,6 +5,8 @@ import org.keycloak.events.Event;
 import org.keycloak.events.EventStoreProvider;
 import org.keycloak.events.EventStoreProviderFactory;
 import org.keycloak.events.EventType;
+import org.keycloak.events.admin.AdminEvent;
+import org.keycloak.events.admin.OperationType;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.KeycloakSessionFactory;
 
@@ -20,12 +22,13 @@ import java.util.Set;
 public class MemEventStoreProviderFactory implements EventStoreProviderFactory {
 
     private List<Event> events;
-
     private Set<EventType> excludedEvents;
+    private List<AdminEvent> adminEvents;
+    private Set<OperationType> excludedOperations;
 
     @Override
     public EventStoreProvider create(KeycloakSession session) {
-        return new MemEventStoreProvider(events, excludedEvents);
+        return new MemEventStoreProvider(events, excludedEvents, adminEvents, excludedOperations);
     }
 
     @Override
@@ -39,6 +42,14 @@ public class MemEventStoreProviderFactory implements EventStoreProviderFactory {
                 excludedEvents.add(EventType.valueOf(e));
             }
         }
+        
+        String excludesOperations = config.get("excludesOperations");
+        if (excludesOperations != null) {
+            excludedOperations = new HashSet<>();
+            for (String e : excludesOperations.split(",")) {
+                excludedOperations.add(OperationType.valueOf(e));
+            }
+        }
     }
 
     @Override
@@ -49,6 +60,8 @@ public class MemEventStoreProviderFactory implements EventStoreProviderFactory {
     public void close() {
         events = null;
         excludedEvents = null;
+        adminEvents = null;
+        excludedOperations = null;
     }
 
     @Override
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/app.js b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/app.js
index 3ee1fbe..2a470e2 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/app.js
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/app.js
@@ -303,6 +303,18 @@ module.config([ '$routeProvider', function($routeProvider) {
             },
             controller : 'RealmEventsCtrl'
         })
+        .when('/realms/:realm/admin-events', {
+            templateUrl : resourceUrl + '/partials/realm-events-admin.html',
+            resolve : {
+                realm : function(RealmLoader) {
+                    return RealmLoader();
+                },
+                serverInfo : function(ServerInfoLoader) {
+                    return ServerInfoLoader();
+                }
+            },
+            controller : 'RealmAdminEventsCtrl'
+        })
         .when('/realms/:realm/events-settings', {
             templateUrl : resourceUrl + '/partials/realm-events-config.html',
             resolve : {
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js
index f8ad7c3..3231640 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js
@@ -1180,7 +1180,7 @@ module.controller('RealmSMTPSettingsCtrl', function($scope, Current, Realm, real
     }
 });
 
-module.controller('RealmEventsConfigCtrl', function($scope, eventsConfig, RealmEventsConfig, RealmEvents, realm, serverInfo, $location, Notifications, TimeUnit, Dialog) {
+module.controller('RealmEventsConfigCtrl', function($scope, eventsConfig, RealmEventsConfig, RealmEvents, RealmAdminEvents, realm, serverInfo, $location, Notifications, TimeUnit, Dialog) {
     $scope.realm = realm;
 
     $scope.eventsConfig = eventsConfig;
@@ -1198,7 +1198,13 @@ module.controller('RealmEventsConfigCtrl', function($scope, eventsConfig, RealmE
     $scope.eventSelectOptions = {
         'multiple': true,
         'simple_tags': true,
-        'tags': serverInfo.eventTypes
+        'tags': serverInfo.enums['eventType']
+    };
+
+    $scope.adminEnabledEventOperationsOptions = {
+        'multiple': true,
+        'simple_tags': true,
+        'tags': serverInfo.enums['operationType']
     };
 
     var oldCopy = angular.copy($scope.eventsConfig);
@@ -1238,6 +1244,14 @@ module.controller('RealmEventsConfigCtrl', function($scope, eventsConfig, RealmE
             });
         });
     };
+    
+    $scope.clearAdminEvents = function() {
+        Dialog.confirmDelete($scope.realm.realm, 'admin-events', function() {
+            RealmAdminEvents.remove({ id : $scope.realm.realm }, function() {
+                Notifications.success("The admin events has been cleared.");
+            });
+        });
+    };
 });
 
 module.controller('RealmEventsCtrl', function($scope, RealmEvents, realm, serverInfo) {
@@ -1247,7 +1261,7 @@ module.controller('RealmEventsCtrl', function($scope, RealmEvents, realm, server
     $scope.eventSelectOptions = {
         'multiple': true,
         'simple_tags': true,
-        'tags': serverInfo.eventTypes
+        'tags': serverInfo.enums['eventType']
     };
 
     $scope.query = {
@@ -1308,6 +1322,106 @@ module.controller('RealmEventsCtrl', function($scope, RealmEvents, realm, server
     $scope.update();
 });
 
+module.controller('RealmAdminEventsCtrl', function($scope, RealmAdminEvents, realm, serverInfo, $modal, $filter) {
+    $scope.realm = realm;
+    $scope.page = 0;
+
+    $scope.query = {
+    	id : realm.realm,
+        max : 5,
+        first : 0
+    }
+    $scope.query.authRealm = 'master';
+
+    $scope.adminEnabledEventOperationsOptions = {
+        'multiple': true,
+        'simple_tags': true,
+        'tags': serverInfo.enums['operationType']
+    };
+    
+    $scope.update = function() {
+    	$scope.query.first = 0;
+        for (var i in $scope.query) {
+            if ($scope.query[i] === '') {
+                delete $scope.query[i];
+           }
+        }
+        $scope.events = RealmAdminEvents.query($scope.query);
+    }
+    
+    $scope.reset = function() {
+    	$scope.query.first = 0;
+    	$scope.query.max = 5;
+    	$scope.query.operationTypes = '';
+    	$scope.query.resourcePath = '';
+    	$scope.query.authRealm = 'master';
+    	$scope.query.authClient = '';
+    	$scope.query.authUser = '';
+    	$scope.query.authIpAddress = '';
+    	$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 = RealmAdminEvents.query($scope.query);
+    }
+    
+    $scope.firstPage = function() {
+        $scope.query.first = 0;
+        $scope.queryUpdate();
+    }
+
+    $scope.previousPage = function() {
+        $scope.query.first -= parseInt($scope.query.max);
+        if ($scope.query.first < 0) {
+            $scope.query.first = 0;
+        }
+        $scope.queryUpdate();
+    }
+
+    $scope.nextPage = function() {
+        $scope.query.first += parseInt($scope.query.max);
+        $scope.queryUpdate();
+    }
+
+    $scope.update();
+    
+    $scope.viewRepresentation = function(event) {
+        $modal.open({
+            templateUrl: resourceUrl + '/partials/modal/realm-events-admin-representation.html',
+            controller: 'RealmAdminEventsModalCtrl',
+            resolve: {
+                event: function () {
+                    return event;
+                }
+            }
+        })
+    }
+
+    $scope.viewAuth = function(event) {
+        $modal.open({
+            templateUrl: resourceUrl + '/partials/modal/realm-events-admin-auth.html',
+            controller: 'RealmAdminEventsModalCtrl',
+            resolve: {
+                event: function () {
+                    return event;
+                }
+            }
+        })
+    }
+});
+
+module.controller('RealmAdminEventsModalCtrl', function($scope, $filter, event) {
+    $scope.event = event;
+});
+
 module.controller('RealmBruteForceCtrl', function($scope, Realm, realm, $http, $location, Dialog, Notifications, TimeUnit) {
     console.log('RealmBruteForceCtrl');
 
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/services.js b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/services.js
index 7708821..1b43027 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/services.js
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/services.js
@@ -180,6 +180,12 @@ module.factory('RealmEvents', function($resource) {
     });
 });
 
+module.factory('RealmAdminEvents', function($resource) {
+    return $resource(authUrl + '/admin/realms/:id/admin-events', {
+        id : '@realm'
+    });
+});
+
 module.factory('RealmLDAPConnectionTester', function($resource) {
     return $resource(authUrl + '/admin/realms/:realm/testLDAPConnection');
 });
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/modal/realm-events-admin-auth.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/modal/realm-events-admin-auth.html
new file mode 100644
index 0000000..8f765f0
--- /dev/null
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/modal/realm-events-admin-auth.html
@@ -0,0 +1,8 @@
+<div style="padding: 20px 20px 0 20px">
+<table class="table table-striped table-bordered">
+    <tr><td width="100px">Realm</td><td>{{event.authDetails.realmId}}</td></tr>
+    <tr><td width="100px">Client</td><td>{{event.authDetails.clientId}}</td></tr>
+    <tr><td width="100px">User</td><td>{{event.authDetails.userId}}</td></tr>
+    <tr><td width="100px">IP Address</td><td>{{event.authDetails.ipAddress}}</td></tr>
+</table>
+</div>
\ No newline at end of file
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/modal/realm-events-admin-representation.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/modal/realm-events-admin-representation.html
new file mode 100644
index 0000000..837a164
--- /dev/null
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/modal/realm-events-admin-representation.html
@@ -0,0 +1,3 @@
+<div style="padding: 20px 20px 10px 20px">
+	<pre ng-bind = "{{event.representation}} | json"></pre>
+</div>
\ No newline at end of file
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-events.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-events.html
index 7c45014..8df1629 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-events.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-events.html
@@ -5,7 +5,8 @@
     </h1>
 
     <ul class="nav nav-tabs">
-        <li data-ng-class="(path[2] == 'events') && 'active'"><a href="#/realms/{{realm.realm}}/events">View</a></li>
+        <li data-ng-class="(path[2] == 'events') && 'active'"><a href="#/realms/{{realm.realm}}/events">Login Events</a></li>
+        <li data-ng-class="(path[2] == 'admin-events') && 'active'"><a href="#/realms/{{realm.realm}}/admin-events">Admin Events</a></li>
         <li data-ng-class="(path[2] == 'events-settings') && 'active'"><a href="#/realms/{{realm.realm}}/events-settings">Config</a></li>
     </ul>
 
@@ -121,4 +122,4 @@
     </table>
 </div>
 
-<kc-menu></kc-menu>
+<kc-menu></kc-menu>
\ No newline at end of file
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-events-admin.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-events-admin.html
new file mode 100755
index 0000000..7168aab
--- /dev/null
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-events-admin.html
@@ -0,0 +1,128 @@
+<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
+    <h1>
+        <span><strong>Admin Events</strong> {{realm.realm|capitalize}}</span>
+        <kc-tooltip>Displays saved admin events for the realm. Events are related to admin account, for example a realm creation. To enable persisted events go to config.</kc-tooltip>
+    </h1>
+    
+    <ul class="nav nav-tabs">
+        <li data-ng-class="(path[2] == 'events') && 'active'"><a href="#/realms/{{realm.realm}}/events">Login Events</a></li>
+        <li data-ng-class="(path[2] == 'admin-events') && 'active'"><a href="#/realms/{{realm.realm}}/admin-events">Admin Events</a></li>
+        <li data-ng-class="(path[2] == 'events-settings') && 'active'"><a href="#/realms/{{realm.realm}}/events-settings">Config</a></li>
+    </ul>
+    <h2></h2>
+
+    <div id="content">
+        <table class="table table-striped table-bordered">
+            <thead>
+            <tr>
+                <th class="kc-table-actions" colspan="5">
+                    <div class="pull-right">
+                        <select data-ng-model="query.max" data-ng-click="update()" class="btn btn-default">
+                            <option>5</option>
+                            <option>10</option>
+                            <option>50</option>
+                            <option>100</option>
+                        </select>
+                        <button class="btn btn-default" data-ng-click="filter = !filter">
+                            <span class="glyphicon glyphicon-plus" data-ng-show="!filter"></span>
+                            <span class="glyphicon glyphicon-minus" data-ng-show="filter"></span>
+                            Filter
+                        </button>
+                        <button class="btn btn-default btn-primary" data-ng-click="update()">Update</button>
+                        <button class="btn btn-default btn-primary" data-ng-click="reset()">Reset</button>
+                    </div>
+                    <form class="form-horizontal" data-ng-show="filter">
+                        <div class="form-group">
+      			            <label class="col-sm-2 control-label" for="adminEnabledEventOperations">Operation Types</label>
+                    	    <div class="col-sm-5">
+                                <input ui-select2="adminEnabledEventOperationsOptions" id="adminEnabledEventOperations" ng-model="query.operationTypes" data-placeholder="Select operations..."/>
+			                </div>
+			            </div>
+                        <div class="form-group">
+                            <label class="col-sm-2 control-label" for="resource">Resource Path</label>
+                            <div class="col-sm-4">
+                                <input class="form-control" type="text" id="resource" name="resource" data-ng-model="query.resourcePath">
+                            </div>
+                            <span tooltip-placement="right" tooltip="Filter by resource path. Supports wildcards '*' to match a single part of the path and '**' matches multiple parts. For example 'realms/*/clients/asbc' matches client with id asbc in any realm, while or 'realms/master/**' matches anything in the master realm." class="fa fa-info-circle"></span>
+                        </div>
+                        <div class="form-group">
+                            <label class="col-sm-2 control-label" for="dateFrom">Date (From)</label>
+                            <div class="col-sm-4">
+                                <input class="form-control" type="date" id="dateFrom" name="dateFrom" data-ng-model="query.dateFrom">
+                            </div>
+                        </div>
+                        <div class="form-group">
+                            <label class="col-sm-2 control-label" for="dateTo">Date (To)</label>
+                            <div class="col-sm-4">
+                                <input class="form-control" type="date" id="dateTo" name="dateTo" data-ng-model="query.dateTo">
+                            </div>
+                        </div>
+
+                        <fieldset>
+                            <legend><span class="text">Authentication Details</span></legend>
+
+                            <div class="form-group" data-ng-show="'master' === realm.realm">
+                                <label class="col-sm-2 control-label" for="realm">Realm</label>
+                                <div class="col-sm-4">
+                                    <input class="form-control" type="text" id="realm" name="realm" data-ng-model="query.authRealm">
+                                </div>
+                                <span tooltip-placement="right" tooltip="Filter by realm Id. This filter is supported only for master realm." class="fa fa-info-circle"></span>
+                            </div>
+                            <div class="form-group">
+                                <label class="col-sm-2 control-label" for="client">Client</label>
+                                <div class="col-sm-4">
+                                    <input class="form-control" type="text" id="client" name="client" data-ng-model="query.authClient">
+                                </div>
+                            </div>
+                            <div class="form-group">
+                                <label class="col-sm-2 control-label" for="user">User</label>
+                                <div class="col-sm-4">
+                                    <input class="form-control" type="text" id="user" name="user" data-ng-model="query.authUser">
+                                </div>
+                            </div>
+                            <div class="form-group">
+                                <label class="col-sm-2 control-label" for="ipAddress">IP Address</label>
+                                <div class="col-sm-4">
+                                    <input class="form-control" type="text" id="ipAddress" name="ipAddress" data-ng-model="query.authIpAddress">
+                                </div>
+                            </div>
+                        </fieldset>
+
+                    </form>
+                </th>
+            </tr>
+            <tr>
+                <th width="100px">Time</th>
+                <th width="180px">Operation Type</th>
+                <th width="180px">Resource Path</th>
+                <th>Details</th>
+            </tr>
+            </thead>
+            <tfoot>
+            <tr>
+            	<td colspan="7">
+                	<button data-ng-click="firstPage()" class="first" ng-disabled="query.first == 0"><i data-ng-class="query.first == 0 && 'text-muted'" class="fa fa-angle-double-left"></i></button>
+                	<button data-ng-click="previousPage()" class="prev" ng-disabled="query.first == 0"><i data-ng-class="query.first == 0 && 'text-muted'" class="fa fa-angle-left"></i></button>
+                	<button data-ng-click="nextPage()" class="next" ng-disabled="events.length < query.max"><i data-ng-class="events.length < query.max && 'text-muted'" class="fa fa-angle-right"></i></button>
+            	</td>
+            </tr>
+            </tfoot>
+            <tbody>
+                <tr data-ng-repeat="event in events">
+                    <td>{{event.time|date:'shortDate'}}<br>{{event.time|date:'mediumTime'}}</td>
+                    <td data-ng-class="events-error">{{event.operationType}}</td>
+                    <td>{{event.resourcePath}}</td>
+                    <td>
+                        <button type="button" class="btn btn-default btn-xs" data-ng-click="viewAuth(event)">
+                            Auth
+                        </button>
+                        <button type="button" class="btn btn-default btn-xs" data-ng-click="viewRepresentation(event)" data-ng-show="event.representation">
+                            Representation
+                        </button>
+                    </td>
+                </tr>
+            </tbody>
+        </table>
+    </div>
+</div>
+<kc-menu></kc-menu>
\ No newline at end of file
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-events-config.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-events-config.html
index 1b34385..7d05980 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-events-config.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-events-config.html
@@ -2,69 +2,120 @@
     <h1><strong>Events</strong> {{realm.realm|capitalize}}</span> Events</h1>
 
     <ul class="nav nav-tabs">
-        <li data-ng-class="(path[2] == 'events') && 'active'"><a href="#/realms/{{realm.realm}}/events">View</a></li>
+        <li data-ng-class="(path[2] == 'events') && 'active'"><a href="#/realms/{{realm.realm}}/events">Login Events</a></li>
+        <li data-ng-class="(path[2] == 'admin-events') && 'active'"><a href="#/realms/{{realm.realm}}/admin-events">Admin Events</a></li>
         <li data-ng-class="(path[2] == 'events-settings') && 'active'"><a href="#/realms/{{realm.realm}}/events-settings">Config</a></li>
     </ul>
+    <div id="content">
+        <h2><span>{{realm.realm}}</span> Events Config</h2>
 
-    <form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageEvents">
-        <fieldset class="border-top">
-            <div class="form-group">
-                <label class="col-md-2 control-label" for="enabled">Save Events</label>
-                <div class="col-md-6">
-                    <input ng-model="eventsConfig.eventsEnabled" name="enabled" id="enabled" onoffswitch />
+        <form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageEvents">
+
+            <fieldset class="border-top">
+                <div class="form-group">
+                    <label class="col-md-2 control-label" for="eventsListeners" class="control-label">Event Listeners</label>
+
+                    <div class="col-md-6">
+                        <select ui-select2 ng-model="eventsConfig.eventsListeners" data-placeholder="Select an action..." multiple>
+                            <option ng-repeat="listener in eventListeners" value="{{listener}}">{{listener}}</option>
+                        </select>
+                    </div>
+
+                    <span tooltip-placement="right" tooltip="Configure what listeners receive events for the realm." class="fa fa-info-circle"></span>
                 </div>
-                <kc-tooltip>If enabled events are saved to the database which makes events available to the admin and account management consoles.</kc-tooltip>
-            </div>
+            </fieldset>
 
-            <div class="form-group" data-ng-show="eventsConfig.eventsEnabled">
-                <label class="col-md-2 control-label" for="enabledEventTypes" class="control-label">Saved Types</label>
+            <fieldset>
+                <legend><span class="text">Login Events Settings</span></legend>
 
-                <div class="col-md-6">
-                    <input ui-select2="eventSelectOptions" id="enabledEventTypes" ng-model="eventsConfig.enabledEventTypes" data-placeholder="Select event types..."/>
+                <div class="form-group">
+                    <label class="col-md-2 control-label" for="enabled">Save Events</label>
+                    <div class="col-md-6">
+                        <input ng-model="eventsConfig.eventsEnabled" name="enabled" id="enabled" onoffswitch />
+                    </div>
+                    <span tooltip-placement="right" tooltip="If enabled login events are saved to the database which makes events available to the admin and account management consoles." class="fa fa-info-circle"></span>
                 </div>
 
-                <kc-tooltip>Configure what event types are saved. By default events related to login and users modifying their accounts are persisted.</kc-tooltip>
-            </div>
+                <div class="form-group" data-ng-show="eventsConfig.eventsEnabled">
+                    <label class="col-md-2 control-label" for="enabledEventTypes" class="control-label">Saved Types</label>
+
+                    <div class="col-md-6">
+                        <input ui-select2="eventSelectOptions" id="enabledEventTypes" ng-model="eventsConfig.enabledEventTypes" data-placeholder="Select event types..."/>
+                    </div>
 
-            <div class="form-group" data-ng-show="access.manageEvents && eventsConfig.eventsEnabled">
-                <label class="col-md-2 control-label" for="password">Clear Events</label>
-                <div class="col-md-6">
-                    <button class="btn btn-danger" type="submit" data-ng-click="clearEvents()" >Clear Events</button>
+                    <span tooltip-placement="right" tooltip="Configure what event types are saved. By default events related to login and users modifying their accounts are persisted." class="fa fa-info-circle"></span>
                 </div>
-                <kc-tooltip>Deletes all events in the database.</kc-tooltip>
-            </div>
-            <div class="form-group input-select" data-ng-show="eventsConfig.eventsEnabled">
-                <label class="col-md-2 control-label" for="expiration">Expiration</label>
-                <div class="col-md-6 form-inline">
-                    <input class="form-control" type="number" data-ng-model="eventsConfig.eventsExpiration" id="expiration" name="expiration" min="0"/>
-                    <select class="form-control" name="expirationUnit" data-ng-model="eventsConfig.expirationUnit" >
-                        <option>Minutes</option>
-                        <option>Hours</option>
-                        <option>Days</option>
-                    </select>
+
+                <div class="form-group" data-ng-show="access.manageEvents && eventsConfig.eventsEnabled">
+                    <label class="col-md-2 control-label" for="password">Clear Events</label>
+                    <div class="col-md-6">
+                        <button class="btn btn-danger" type="submit" data-ng-click="clearEvents()" >Clear Events</button>
+                    </div>
+                    <span tooltip-placement="right" tooltip="Deletes all events in the database." class="fa fa-info-circle"></span>
                 </div>
-                <div class="col-sm-1"></div>
-                <kc-tooltip>Sets the expiration for events. Expired events are periodically deleted from the database.</kc-tooltip>
-            </div>
+                <div class="form-group input-select" data-ng-show="eventsConfig.eventsEnabled">
+                    <label class="col-md-2 control-label" for="expiration">Expiration</label>
+                    <div class="col-md-6">
+                        <input class="form-control" type="number" data-ng-model="eventsConfig.eventsExpiration" id="expiration" name="expiration" min="0"/>
+                    </div>
+                    <div class="col-md-2 select-kc">
+                        <select name="expirationUnit" data-ng-model="eventsConfig.expirationUnit" >
+                            <option>Minutes</option>
+                            <option>Hours</option>
+                            <option>Days</option>
+                        </select>
+                    <span tooltip-placement="right" tooltip="Sets the expiration for events. Expired events are periodically deleted from the database." class="fa fa-info-circle"></span>
+                    </div>
+                </div>
+            </fieldset>
+
 
-            <div class="form-group">
-                <label class="col-md-2 control-label" for="eventsListeners" class="control-label">Listeners</label>
+            <fieldset>
+                <legend><span class="text">Admin Events Settings</span></legend>
 
-                <div class="col-md-6">
-                    <select ui-select2 ng-model="eventsConfig.eventsListeners" data-placeholder="Select an action..." multiple>
-                        <option ng-repeat="listener in eventListeners" value="{{listener}}">{{listener}}</option>
-                    </select>
+                <div class="form-group">
+                    <label class="col-md-2 control-label" for="adminEventsEnabled">Save Events</label>
+                    <div class="col-md-6">
+                        <input ng-model="eventsConfig.adminEventsEnabled" name="adminEventsEnabled" id="adminEventsEnabled" onoffswitch />
+                    </div>
+
+                    <span tooltip-placement="right" tooltip="If enabled admin events are saved to the database which makes events available to the admin console." class="fa fa-info-circle"></span>
                 </div>
 
-                <kc-tooltip>Configure what listeners receive events for the realm.</kc-tooltip>
-            </div>
-        </fieldset>
+                <div class="form-group" data-ng-show="eventsConfig.adminEventsEnabled">
+                    <label class="col-md-2 control-label" for="adminEnabledEventOperations" class="control-label">Saved Operations</label>
 
-        <div class="pull-right form-actions" data-ng-show="access.manageEvents">
-            <button data-kc-reset data-ng-show="changed">Clear changes</button>
-            <button data-kc-save data-ng-show="changed">Save</button>
-        </div>
-    </form>
-</div>
+                    <div class="col-md-6">
+                        <input ui-select2="adminEnabledEventOperationsOptions" id="adminEnabledEventOperations" ng-model="eventsConfig.adminEnabledEventOperations" data-placeholder="Select operations..."/>
+                    </div>
+
+                    <span tooltip-placement="right" tooltip="Configure what operations are saved." class="fa fa-info-circle"></span>
+                </div>
+
+                <div class="form-group" data-ng-show="eventsConfig.adminEventsEnabled">
+                    <label class="col-md-2 control-label" for="adminEventsDetailsEnabled">Include Representation</label>
+                    <div class="col-md-6">
+                        <input ng-model="eventsConfig.adminEventsDetailsEnabled" name="adminEventsDetailsEnabled" id="adminEventsDetailsEnabled" onoffswitch />
+                    </div>
 
-<kc-menu></kc-menu>
+                    <span tooltip-placement="right" tooltip="Include JSON representation for create and update requests." class="fa fa-info-circle"></span>
+                </div>
+                
+                <div class="form-group" data-ng-show="access.manageEvents && eventsConfig.adminEventsEnabled">
+                    <label class="col-md-2 control-label" for="password">Clear Admin Events</label>
+                    <div class="col-md-6">
+                        <button class="btn btn-danger" type="submit" data-ng-click="clearAdminEvents()" >Clear Admin Events</button>
+                    </div>
+                    <span tooltip-placement="right" tooltip="Deletes all admin events in the database." class="fa fa-info-circle"></span>
+                </div>
+                
+            </fieldset>
+
+                <div class="pull-right form-actions" data-ng-show="access.manageEvents">
+                <button data-kc-reset data-ng-show="changed">Clear changes</button>
+                <button data-kc-save data-ng-show="changed">Save</button>
+            </div>
+        </form>
+    </div>
+</div>
+<kc-menu></kc-menu>
\ No newline at end of file
diff --git a/forms/email-freemarker/src/main/java/org/keycloak/email/freemarker/beans/AdminEventBean.java b/forms/email-freemarker/src/main/java/org/keycloak/email/freemarker/beans/AdminEventBean.java
new file mode 100644
index 0000000..ca225fc
--- /dev/null
+++ b/forms/email-freemarker/src/main/java/org/keycloak/email/freemarker/beans/AdminEventBean.java
@@ -0,0 +1,37 @@
+package org.keycloak.email.freemarker.beans;
+
+import java.util.Date;
+
+import org.keycloak.events.admin.AdminEvent;
+
+/**
+ * @author <a href="mailto:giriraj.sharma27@gmail.com">Giriraj Sharma</a>
+ */
+public class AdminEventBean {
+    
+    private AdminEvent adminEvent;
+
+    public AdminEventBean(AdminEvent adminEvent) {
+        this.adminEvent = adminEvent;
+    }
+
+    public Date getDate() {
+        return new Date(adminEvent.getTime());
+    }
+
+    public String getOperationType() {
+        return adminEvent.getOperationType().toString().toLowerCase();
+    }
+
+    public String getClient() {
+        return adminEvent.getAuthDetails().getClientId();
+    }
+
+    public String getIpAddress() {
+        return adminEvent.getAuthDetails().getIpAddress();
+    }
+    
+    public String getResourcePath() {
+        return adminEvent.getResourcePath();
+    }
+}
diff --git a/model/api/src/main/java/org/keycloak/models/entities/RealmEntity.java b/model/api/src/main/java/org/keycloak/models/entities/RealmEntity.java
index 3c978f9..cbb4c6d 100755
--- a/model/api/src/main/java/org/keycloak/models/entities/RealmEntity.java
+++ b/model/api/src/main/java/org/keycloak/models/entities/RealmEntity.java
@@ -63,7 +63,11 @@ public class RealmEntity extends AbstractIdentifiableEntity {
     private long eventsExpiration;
     private List<String> eventsListeners = new ArrayList<String>();
     private List<String> enabledEventTypes = new ArrayList<String>();
-
+    
+    protected boolean adminEventsEnabled;
+    protected List<String> adminEnabledEventOperations = new ArrayList<String>();;
+    protected boolean adminEventsDetailsEnabled;
+    
     private String masterAdminClient;
 
     private boolean internationalizationEnabled;
@@ -391,6 +395,30 @@ public class RealmEntity extends AbstractIdentifiableEntity {
         this.enabledEventTypes = enabledEventTypes;
     }
     
+    public boolean isAdminEventsEnabled() {
+        return adminEventsEnabled;
+    }
+
+    public void setAdminEventsEnabled(boolean adminEventsEnabled) {
+        this.adminEventsEnabled = adminEventsEnabled;
+    }
+
+    public List<String> getAdminEnabledEventOperations() {
+        return adminEnabledEventOperations;
+    }
+
+    public void setAdminEnabledEventOperations(List<String> adminEnabledEventOperations) {
+        this.adminEnabledEventOperations = adminEnabledEventOperations;
+    }
+
+    public boolean isAdminEventsDetailsEnabled() {
+        return adminEventsDetailsEnabled;
+    }
+
+    public void setAdminEventsDetailsEnabled(boolean adminEventsDetailsEnabled) {
+        this.adminEventsDetailsEnabled = adminEventsDetailsEnabled;
+    }
+
     public String getMasterAdminClient() {
         return masterAdminClient;
     }
diff --git a/model/api/src/main/java/org/keycloak/models/RealmModel.java b/model/api/src/main/java/org/keycloak/models/RealmModel.java
index 8b901a2..3476374 100755
--- a/model/api/src/main/java/org/keycloak/models/RealmModel.java
+++ b/model/api/src/main/java/org/keycloak/models/RealmModel.java
@@ -232,7 +232,19 @@ public interface RealmModel extends RoleContainerModel {
     Set<String> getEnabledEventTypes();
 
     void setEnabledEventTypes(Set<String> enabledEventTypes);
+    
+    boolean isAdminEventsEnabled();
+
+    void setAdminEventsEnabled(boolean enabled);
+    
+    Set<String> getAdminEnabledEventOperations();
 
+    void setAdminEnabledEventOperations(Set<String> adminEnabledEventOperations);
+    
+    boolean isAdminEventsDetailsEnabled();
+
+    void setAdminEventsDetailsEnabled(boolean enabled);
+    
     ClientModel getMasterAdminClient();
 
     void setMasterAdminClient(ClientModel client);
diff --git a/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java b/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
index 32f4e89..144c140 100755
--- a/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
+++ b/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
@@ -14,6 +14,7 @@ import org.keycloak.models.UserCredentialModel;
 import org.keycloak.models.UserFederationProviderModel;
 import org.keycloak.models.UserModel;
 import org.keycloak.models.UserSessionModel;
+
 import org.keycloak.representations.idm.ClientRepresentation;
 import org.keycloak.representations.idm.CredentialRepresentation;
 import org.keycloak.representations.idm.FederatedIdentityRepresentation;
@@ -192,6 +193,14 @@ public class ModelToRepresentation {
             rep.setEnabledEventTypes(new LinkedList<String>(realm.getEnabledEventTypes()));
         }
         
+        rep.setAdminEventsEnabled(realm.isAdminEventsEnabled());
+        
+        if(realm.getAdminEnabledEventOperations() != null) {
+            rep.setAdminEnabledEventOperations(new LinkedList<String>(realm.getAdminEnabledEventOperations()));
+        }
+        
+        rep.setAdminEventsDetailsEnabled(realm.isAdminEventsDetailsEnabled());
+        
         return rep;
     }
 
diff --git a/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java b/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
index 45221e5..70976a8 100755
--- a/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
+++ b/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
@@ -410,10 +410,16 @@ public class RepresentationToModel {
         if (rep.getAccountTheme() != null) realm.setAccountTheme(rep.getAccountTheme());
         if (rep.getAdminTheme() != null) realm.setAdminTheme(rep.getAdminTheme());
         if (rep.getEmailTheme() != null) realm.setEmailTheme(rep.getEmailTheme());
+        
         if (rep.isEventsEnabled() != null) realm.setEventsEnabled(rep.isEventsEnabled());
         if (rep.getEventsExpiration() != null) realm.setEventsExpiration(rep.getEventsExpiration());
         if (rep.getEventsListeners() != null) realm.setEventsListeners(new HashSet<>(rep.getEventsListeners()));
         if (rep.getEnabledEventTypes() != null) realm.setEnabledEventTypes(new HashSet<>(rep.getEnabledEventTypes()));
+        
+        if (rep.isAdminEventsEnabled() != null) realm.setAdminEventsEnabled(rep.isAdminEventsEnabled());
+        if (rep.getAdminEnabledEventOperations() != null) realm.setAdminEnabledEventOperations(new HashSet<>(rep.getAdminEnabledEventOperations()));
+        if (rep.isAdminEventsDetailsEnabled() != null) realm.setAdminEventsDetailsEnabled(rep.isAdminEventsDetailsEnabled());
+        
 
         if (rep.getPasswordPolicy() != null) realm.setPasswordPolicy(new PasswordPolicy(rep.getPasswordPolicy()));
 
diff --git a/model/file/src/main/java/org/keycloak/models/file/adapter/RealmAdapter.java b/model/file/src/main/java/org/keycloak/models/file/adapter/RealmAdapter.java
index cdd658a..6501981 100755
--- a/model/file/src/main/java/org/keycloak/models/file/adapter/RealmAdapter.java
+++ b/model/file/src/main/java/org/keycloak/models/file/adapter/RealmAdapter.java
@@ -959,6 +959,40 @@ public class RealmAdapter implements RealmModel {
     }
 
     @Override
+    public boolean isAdminEventsEnabled() {
+        return realm.isAdminEventsEnabled();
+    }
+
+    @Override
+    public void setAdminEventsEnabled(boolean enabled) {
+        realm.setAdminEventsEnabled(enabled);
+    }
+
+    @Override
+    public Set<String> getAdminEnabledEventOperations() {
+        return new HashSet<String>(realm.getAdminEnabledEventOperations());
+    }
+
+    @Override
+    public void setAdminEnabledEventOperations(Set<String> adminEnabledEventOperations) {
+        if (adminEnabledEventOperations != null) {
+            realm.setAdminEnabledEventOperations(new ArrayList<String>(adminEnabledEventOperations));
+        } else {
+            realm.setAdminEnabledEventOperations(Collections.EMPTY_LIST);
+        }
+    }
+
+    @Override
+    public boolean isAdminEventsDetailsEnabled() {
+        return realm.isAdminEventsDetailsEnabled();
+    }
+
+    @Override
+    public void setAdminEventsDetailsEnabled(boolean enabled) {
+        realm.setAdminEventsDetailsEnabled(enabled);
+    }
+    
+    @Override
     public ClientModel getMasterAdminClient() {
         return this.masterAdminApp;
     }
diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedRealm.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedRealm.java
index e490814..97f2667 100755
--- a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedRealm.java
+++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedRealm.java
@@ -79,6 +79,9 @@ public class CachedRealm {
     private long eventsExpiration;
     private Set<String> eventsListeners = new HashSet<String>();
     private Set<String> enabledEventTypes = new HashSet<String>();
+    protected boolean adminEventsEnabled;
+    protected Set<String> adminEnabledEventOperations = new HashSet<String>();
+    protected boolean adminEventsDetailsEnabled;
     private List<String> defaultRoles = new LinkedList<String>();
     private Map<String, String> realmRoles = new HashMap<String, String>();
     private Map<String, String> clients = new HashMap<String, String>();
@@ -153,6 +156,11 @@ public class CachedRealm {
         eventsExpiration = model.getEventsExpiration();
         eventsListeners.addAll(model.getEventsListeners());
         enabledEventTypes.addAll(model.getEnabledEventTypes());
+        
+        adminEventsEnabled = model.isAdminEventsEnabled();
+        adminEnabledEventOperations.addAll(model.getAdminEnabledEventOperations());
+        adminEventsDetailsEnabled = model.isAdminEventsDetailsEnabled();
+        
         defaultRoles.addAll(model.getDefaultRoles());
         masterAdminClient = model.getMasterAdminClient().getId();
 
@@ -350,6 +358,18 @@ public class CachedRealm {
         return enabledEventTypes;
     }
 
+    public boolean isAdminEventsEnabled() {
+        return adminEventsEnabled;
+    }
+
+    public Set<String> getAdminEnabledEventOperations() {
+        return adminEnabledEventOperations;
+    }
+
+    public boolean isAdminEventsDetailsEnabled() {
+        return adminEventsDetailsEnabled;
+    }
+
     public List<UserFederationProviderModel> getUserFederationProviders() {
         return userFederationProviders;
     }
diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/RealmAdapter.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/RealmAdapter.java
index 68a65ac..dfa657f 100755
--- a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/RealmAdapter.java
+++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/RealmAdapter.java
@@ -751,6 +751,42 @@ public class RealmAdapter implements RealmModel {
     }
     
     @Override
+    public boolean isAdminEventsEnabled() {
+        if (updated != null) return updated.isAdminEventsEnabled();
+        return cached.isAdminEventsEnabled();
+    }
+
+    @Override
+    public void setAdminEventsEnabled(boolean enabled) {
+        getDelegateForUpdate();
+        updated.setAdminEventsEnabled(enabled);
+    }
+
+    @Override
+    public Set<String> getAdminEnabledEventOperations() {
+        if (updated != null) return updated.getAdminEnabledEventOperations();
+        return cached.getAdminEnabledEventOperations();
+    }
+
+    @Override
+    public void setAdminEnabledEventOperations(Set<String> adminEnabledEventOperations) {
+        getDelegateForUpdate();
+        updated.setAdminEnabledEventOperations(adminEnabledEventOperations);
+    }
+
+    @Override
+    public boolean isAdminEventsDetailsEnabled() {
+        if (updated != null) return updated.isAdminEventsDetailsEnabled();
+        return cached.isAdminEventsDetailsEnabled();
+    }
+
+    @Override
+    public void setAdminEventsDetailsEnabled(boolean enabled) {
+        getDelegateForUpdate();
+        updated.setAdminEventsDetailsEnabled(enabled);
+    }
+    
+    @Override
     public ClientModel getMasterAdminClient() {
         return cacheSession.getRealm(Config.getAdminRealm()).getClientById(cached.getMasterAdminClient());
     }
@@ -923,4 +959,5 @@ public class RealmAdapter implements RealmModel {
         }
         return null;
     }
+
 }
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RealmEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RealmEntity.java
index efdb4a8..9c69d1d 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RealmEntity.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RealmEntity.java
@@ -134,7 +134,18 @@ public class RealmEntity {
     @Column(name="VALUE")
     @CollectionTable(name="REALM_ENABLED_EVENT_TYPES", joinColumns={ @JoinColumn(name="REALM_ID") })
     protected Set<String> enabledEventTypes = new HashSet<String>();
-
+    
+    @Column(name="ADMIN_EVENTS_ENABLED")
+    protected boolean adminEventsEnabled;
+    
+    @ElementCollection
+    @Column(name="VALUE")
+    @CollectionTable(name="REALM_ENABLED_ADMIN_EVENT_OPERATIONS", joinColumns={ @JoinColumn(name="REALM_ID") })
+    protected Set<String> adminEnabledEventOperations = new HashSet<String>();
+    
+    @Column(name="ADMIN_EVENTS_DETAILS_ENABLED")
+    protected boolean adminEventsDetailsEnabled;
+    
     @OneToOne
     @JoinColumn(name="MASTER_ADMIN_CLIENT")
     protected ClientEntity masterAdminClient;
@@ -437,6 +448,30 @@ public class RealmEntity {
         this.enabledEventTypes = enabledEventTypes;
     }
     
+    public boolean isAdminEventsEnabled() {
+        return adminEventsEnabled;
+    }
+
+    public void setAdminEventsEnabled(boolean adminEventsEnabled) {
+        this.adminEventsEnabled = adminEventsEnabled;
+    }
+
+    public Set<String> getAdminEnabledEventOperations() {
+        return adminEnabledEventOperations;
+    }
+
+    public void setAdminEnabledEventOperations(Set<String> adminEnabledEventOperations) {
+        this.adminEnabledEventOperations = adminEnabledEventOperations;
+    }
+
+    public boolean isAdminEventsDetailsEnabled() {
+        return adminEventsDetailsEnabled;
+    }
+
+    public void setAdminEventsDetailsEnabled(boolean adminEventsDetailsEnabled) {
+        this.adminEventsDetailsEnabled = adminEventsDetailsEnabled;
+    }
+
     public ClientEntity getMasterAdminClient() {
         return masterAdminClient;
     }
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java
index 8618ddb..bcad0bb 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java
@@ -22,6 +22,7 @@ import org.keycloak.models.utils.KeycloakModelUtils;
 
 import javax.persistence.EntityManager;
 import javax.persistence.TypedQuery;
+
 import java.security.Key;
 import java.security.PrivateKey;
 import java.security.PublicKey;
@@ -1065,8 +1066,42 @@ public class RealmAdapter implements RealmModel {
         realm.setEnabledEventTypes(enabledEventTypes);
         em.flush();
     }
+    
+    @Override
+    public boolean isAdminEventsEnabled() {
+        return realm.isAdminEventsEnabled();
+    }
+
+    @Override
+    public void setAdminEventsEnabled(boolean enabled) {
+        realm.setAdminEventsEnabled(enabled);
+        em.flush();
+    }
+
+    @Override
+    public Set<String> getAdminEnabledEventOperations() {
+        return realm.getAdminEnabledEventOperations();
+    }
 
     @Override
+    public void setAdminEnabledEventOperations(Set<String> adminEnabledEventOperations) {
+        realm.setAdminEnabledEventOperations(adminEnabledEventOperations);
+        em.flush();
+        
+    }
+
+    @Override
+    public boolean isAdminEventsDetailsEnabled() {
+        return realm.isAdminEventsDetailsEnabled();
+    }
+
+    @Override
+    public void setAdminEventsDetailsEnabled(boolean enabled) {
+        realm.setAdminEventsDetailsEnabled(enabled);
+        em.flush();
+    }
+    
+    @Override
     public ClientModel getMasterAdminClient() {
         return new ClientAdapter(this, em, session, realm.getMasterAdminClient());
     }
@@ -1327,4 +1362,5 @@ public class RealmAdapter implements RealmModel {
         mapping.setConfig(config);
         return mapping;
     }
+
 }
\ No newline at end of file
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java
index fb08290..c5b999e 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java
@@ -2,6 +2,7 @@ package org.keycloak.models.mongo.keycloak.adapters;
 
 import com.mongodb.DBObject;
 import com.mongodb.QueryBuilder;
+
 import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
 import org.keycloak.enums.SslRequired;
 import org.keycloak.models.ClientModel;
@@ -985,8 +986,46 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
         }
         updateRealm();
     }
+    
+    @Override
+    public boolean isAdminEventsEnabled() {
+        return realm.isAdminEventsEnabled();
+    }
+
+    @Override
+    public void setAdminEventsEnabled(boolean enabled) {
+        realm.setAdminEventsEnabled(enabled);
+        updateRealm();
+        
+    }
+
+    @Override
+    public Set<String> getAdminEnabledEventOperations() {
+        return new HashSet<String>(realm.getAdminEnabledEventOperations());
+    }
+
+    @Override
+    public void setAdminEnabledEventOperations(Set<String> adminEnabledEventOperations) {
+        if (adminEnabledEventOperations != null) {
+            realm.setAdminEnabledEventOperations(new ArrayList<String>(adminEnabledEventOperations));
+        } else {
+            realm.setAdminEnabledEventOperations(Collections.EMPTY_LIST);
+        }
+        updateRealm();
+    }
 
     @Override
+    public boolean isAdminEventsDetailsEnabled() {
+        return realm.isAdminEventsDetailsEnabled();
+    }
+
+    @Override
+    public void setAdminEventsDetailsEnabled(boolean enabled) {
+        realm.setAdminEventsDetailsEnabled(enabled);
+        updateRealm();
+    }
+    
+    @Override
     public ClientModel getMasterAdminClient() {
         MongoClientEntity appData = getMongoStore().loadEntity(MongoClientEntity.class, realm.getMasterAdminClient(), invocationContext);
         return appData != null ? new ClientAdapter(session, this, appData, invocationContext) : null;
diff --git a/services/src/main/java/org/keycloak/services/managers/RealmManager.java b/services/src/main/java/org/keycloak/services/managers/RealmManager.java
index 88d9eb5..e3a3651 100755
--- a/services/src/main/java/org/keycloak/services/managers/RealmManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/RealmManager.java
@@ -168,6 +168,12 @@ public class RealmManager {
         if(rep.getEnabledEventTypes() != null) {
             realm.setEnabledEventTypes(new HashSet<String>(rep.getEnabledEventTypes()));
         }
+        
+        realm.setAdminEventsEnabled(rep.isAdminEventsEnabled());
+        if(rep.getAdminEnabledEventOperations() != null) {
+            realm.setAdminEnabledEventOperations(new HashSet<String>(rep.getAdminEnabledEventOperations()));
+        }
+        realm.setAdminEventsDetailsEnabled(rep.isAdminEventsDetailsEnabled());
     }
 
     // Should be RealmManager moved to model/api instead of referencing methods this way?
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/AdminRoot.java b/services/src/main/java/org/keycloak/services/resources/admin/AdminRoot.java
index 34e8272..8b8253f 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/AdminRoot.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/AdminRoot.java
@@ -8,6 +8,7 @@ import org.jboss.resteasy.spi.NotFoundException;
 import org.jboss.resteasy.spi.ResteasyProviderFactory;
 import org.jboss.resteasy.spi.UnauthorizedException;
 import org.keycloak.ClientConnection;
+import org.keycloak.events.AdminEventBuilder;
 import org.keycloak.jose.jws.JWSInput;
 import org.keycloak.models.AdminRoles;
 import org.keycloak.models.ClientModel;
@@ -29,6 +30,7 @@ import javax.ws.rs.core.HttpHeaders;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriBuilder;
 import javax.ws.rs.core.UriInfo;
+
 import java.io.IOException;
 
 /**
@@ -185,8 +187,11 @@ public class AdminRoot {
         }
 
         Cors.add(request).allowedOrigins(auth.getToken()).allowedMethods("GET", "PUT", "POST", "DELETE").auth().build(response);
-
-        RealmsAdminResource adminResource = new RealmsAdminResource(auth, tokenManager);
+        
+        AdminEventBuilder adminEvent = new AdminEventBuilder(auth.getRealm(), session, clientConnection);
+        adminEvent.user(auth.getUser()).client(auth.getClient());
+        
+        RealmsAdminResource adminResource = new RealmsAdminResource(auth, tokenManager, adminEvent);
         ResteasyProviderFactory.getInstance().injectProperties(adminResource);
         return adminResource;
     }
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ClientAttributeCertificateResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ClientAttributeCertificateResource.java
index 16a0fbe..4e75efb 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/ClientAttributeCertificateResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/ClientAttributeCertificateResource.java
@@ -6,6 +6,8 @@ import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataInput;
 import org.jboss.resteasy.spi.BadRequestException;
 import org.jboss.resteasy.spi.NotAcceptableException;
 import org.jboss.resteasy.spi.NotFoundException;
+import org.keycloak.events.AdminEventBuilder;
+import org.keycloak.events.admin.OperationType;
 import org.keycloak.models.ClientModel;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
@@ -21,6 +23,7 @@ import javax.ws.rs.Produces;
 import javax.ws.rs.core.Context;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.UriInfo;
+
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
@@ -47,11 +50,12 @@ public class ClientAttributeCertificateResource {
     private RealmAuth auth;
     protected ClientModel client;
     protected KeycloakSession session;
+    protected AdminEventBuilder adminEvent;
     protected String attributePrefix;
     protected String privateAttribute;
     protected String certificateAttribute;
 
-    public ClientAttributeCertificateResource(RealmModel realm, RealmAuth auth, ClientModel client, KeycloakSession session, String attributePrefix) {
+    public ClientAttributeCertificateResource(RealmModel realm, RealmAuth auth, ClientModel client, KeycloakSession session, String attributePrefix, AdminEventBuilder adminEvent) {
         this.realm = realm;
         this.auth = auth;
         this.client = client;
@@ -59,6 +63,7 @@ public class ClientAttributeCertificateResource {
         this.attributePrefix = attributePrefix;
         this.privateAttribute = attributePrefix + "." + PRIVATE_KEY;
         this.certificateAttribute = attributePrefix + "." + X509CERTIFICATE;
+        this.adminEvent = adminEvent;
     }
 
     public static class ClientKeyPairInfo {
@@ -94,6 +99,7 @@ public class ClientAttributeCertificateResource {
         ClientKeyPairInfo info = new ClientKeyPairInfo();
         info.setCertificate(client.getAttribute(certificateAttribute));
         info.setPrivateKey(client.getAttribute(privateAttribute));
+        adminEvent.operation(OperationType.VIEW).resourcePath(session.getContext().getUri().getPath()).success();
         return info;
     }
 
@@ -134,6 +140,7 @@ public class ClientAttributeCertificateResource {
         ClientKeyPairInfo info = new ClientKeyPairInfo();
         info.setCertificate(client.getAttribute(certificateAttribute));
         info.setPrivateKey(client.getAttribute(privateAttribute));
+        adminEvent.operation(OperationType.CREATE).resourcePath(session.getContext().getUri().getPath()).representation(info).success();
         return info;
     }
 
@@ -190,7 +197,8 @@ public class ClientAttributeCertificateResource {
             client.setAttribute(certificateAttribute, certPem);
             info.setCertificate(certPem);
         }
-
+        
+        adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo.getPath()).representation(info).success();
         return info;
     }
 
@@ -316,6 +324,9 @@ public class ClientAttributeCertificateResource {
             stream.flush();
             stream.close();
             byte[] rtn = stream.toByteArray();
+            
+            adminEvent.operation(OperationType.ACTION).resourcePath(session.getContext().getUri().getPath()).success();
+            
             return rtn;
         } catch (Exception e) {
             throw new RuntimeException(e);
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ClientResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ClientResource.java
index f769101..3f0d0ea 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/ClientResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/ClientResource.java
@@ -5,6 +5,8 @@ import org.jboss.resteasy.annotations.cache.NoCache;
 import org.jboss.resteasy.spi.BadRequestException;
 import org.jboss.resteasy.spi.NotFoundException;
 import org.jboss.resteasy.spi.ResteasyProviderFactory;
+import org.keycloak.events.AdminEventBuilder;
+import org.keycloak.events.admin.OperationType;
 import org.keycloak.models.ClientModel;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.ModelDuplicateException;
@@ -40,6 +42,7 @@ 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.io.IOException;
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -57,6 +60,7 @@ public class ClientResource {
     protected static final Logger logger = Logger.getLogger(ClientResource.class);
     protected RealmModel realm;
     private RealmAuth auth;
+    private AdminEventBuilder adminEvent;
     protected ClientModel client;
     protected KeycloakSession session;
     
@@ -70,19 +74,21 @@ public class ClientResource {
         return keycloak;
     }
 
-    public ClientResource(RealmModel realm, RealmAuth auth, ClientModel clientModel, KeycloakSession session) {
+    public ClientResource(RealmModel realm, RealmAuth auth, ClientModel clientModel, KeycloakSession session, AdminEventBuilder adminEvent) {
         this.realm = realm;
         this.auth = auth;
         this.client = clientModel;
         this.session = session;
+        this.adminEvent = adminEvent;
 
         auth.init(RealmAuth.Resource.CLIENT);
     }
 
     @Path("protocol-mappers")
     public ProtocolMappersResource getProtocolMappers() {
-        ProtocolMappersResource mappers = new ProtocolMappersResource(client, auth);
+        ProtocolMappersResource mappers = new ProtocolMappersResource(client, auth, adminEvent);
         ResteasyProviderFactory.getInstance().injectProperties(mappers);
+        adminEvent.operation(OperationType.VIEW).resourcePath(uriInfo.getPath()).success();
         return mappers;
     }
 
@@ -98,6 +104,7 @@ public class ClientResource {
 
         try {
             RepresentationToModel.updateClient(rep, client);
+            adminEvent.operation(OperationType.UPDATE).resourcePath(uriInfo.getPath()).representation(rep).success();
             return Response.noContent().build();
         } catch (ModelDuplicateException e) {
             return ErrorResponse.exists("Client " + rep.getClientId() + " already exists");
@@ -115,7 +122,7 @@ public class ClientResource {
     @Produces(MediaType.APPLICATION_JSON)
     public ClientRepresentation getClient() {
         auth.requireView();
-
+        adminEvent.operation(OperationType.VIEW).resourcePath(uriInfo.getPath()).success();
         return ModelToRepresentation.toRepresentation(client);
     }
 
@@ -126,7 +133,7 @@ public class ClientResource {
      */
     @Path("certificates/{attr}")
     public ClientAttributeCertificateResource getCertficateResource(@PathParam("attr") String attributePrefix) {
-        return new ClientAttributeCertificateResource(realm, auth, client, session, attributePrefix);
+        return new ClientAttributeCertificateResource(realm, auth, client, session, attributePrefix, adminEvent);
     }
 
 
@@ -145,6 +152,8 @@ public class ClientResource {
 
         ClientManager clientManager = new ClientManager(new RealmManager(session));
         Object rep = clientManager.toInstallationRepresentation(realm, client, getKeycloakApplication().getBaseUri(uriInfo));
+        
+        adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo.getPath()).success();
 
         // TODO Temporary solution to pretty-print
         return JsonSerialization.mapper.writerWithDefaultPrettyPrinter().writeValueAsString(rep);
@@ -164,6 +173,9 @@ public class ClientResource {
         auth.requireView();
 
         ClientManager clientManager = new ClientManager(new RealmManager(session));
+
+        adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo.getPath()).success();
+
         return clientManager.toJBossSubsystemConfig(realm, client, getKeycloakApplication().getBaseUri(uriInfo));
     }
 
@@ -176,6 +188,7 @@ public class ClientResource {
     public void deleteClient() {
         auth.requireManage();
         new ClientManager(new RealmManager(session)).removeClient(realm, client);
+        adminEvent.operation(OperationType.DELETE).resourcePath(uriInfo.getPath()).success();
     }
 
 
@@ -194,6 +207,7 @@ public class ClientResource {
         logger.debug("regenerateSecret");
         UserCredentialModel cred = KeycloakModelUtils.generateSecret(client);
         CredentialRepresentation rep = ModelToRepresentation.toRepresentation(cred);
+        adminEvent.operation(OperationType.CREATE).resourcePath(uriInfo.getPath()).representation(rep).success();
         return rep;
     }
 
@@ -212,6 +226,7 @@ public class ClientResource {
         logger.debug("getClientSecret");
         UserCredentialModel model = UserCredentialModel.secret(client.getSecret());
         if (model == null) throw new NotFoundException("Client does not have a secret");
+        adminEvent.operation(OperationType.VIEW).resourcePath(uriInfo.getPath()).success();
         return ModelToRepresentation.toRepresentation(model);
     }
 
@@ -222,12 +237,12 @@ public class ClientResource {
      */
     @Path("scope-mappings")
     public ScopeMappedResource getScopeMappedResource() {
-        return new ScopeMappedResource(realm, auth, client, session);
+        return new ScopeMappedResource(realm, auth, client, session, adminEvent);
     }
 
     @Path("roles")
     public RoleContainerResource getRoleContainerResource() {
-        return new RoleContainerResource(realm, auth, client);
+        return new RoleContainerResource(realm, auth, client, adminEvent);
     }
 
     /**
@@ -243,7 +258,7 @@ public class ClientResource {
     public Set<String> getAllowedOrigins()
     {
         auth.requireView();
-
+        adminEvent.operation(OperationType.VIEW).resourcePath(uriInfo.getPath()).success();
         return client.getWebOrigins();
     }
 
@@ -261,6 +276,7 @@ public class ClientResource {
         auth.requireManage();
 
         client.setWebOrigins(allowedOrigins);
+        adminEvent.operation(OperationType.UPDATE).resourcePath(uriInfo.getPath()).representation(client).success();
     }
 
     /**
@@ -279,6 +295,7 @@ public class ClientResource {
         for (String origin : allowedOrigins) {
             client.removeWebOrigin(origin);
         }
+        adminEvent.operation(OperationType.DELETE).resourcePath(uriInfo.getPath()).success();
     }
 
     /**
@@ -289,9 +306,11 @@ public class ClientResource {
     @POST
     public GlobalRequestResult pushRevocation() {
         auth.requireManage();
-        return new ResourceAdminManager(session).pushClientRevocationPolicy(uriInfo.getRequestUri(), realm, client);
+        adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo.getPath()).success();
+        return new ResourceAdminManager(session).pushClientRevocationPolicy(uriInfo.getRequestUri(), realm, client);
+    
     }
-
+    
     /**
      * Number of user sessions associated with this client
      *
@@ -309,6 +328,7 @@ public class ClientResource {
         auth.requireView();
         Map<String, Integer> map = new HashMap<String, Integer>();
         map.put("count", session.sessions().getActiveUserSessions(client.getRealm(), client));
+        adminEvent.operation(OperationType.VIEW).resourcePath(uriInfo.getPath()).success();
         return map;
     }
 
@@ -330,6 +350,7 @@ public class ClientResource {
             UserSessionRepresentation rep = ModelToRepresentation.toRepresentation(userSession);
             sessions.add(rep);
         }
+        adminEvent.operation(OperationType.VIEW).resourcePath(uriInfo.getPath()).success();
         return sessions;
     }
 
@@ -341,7 +362,9 @@ public class ClientResource {
     @POST
     public GlobalRequestResult logoutAll() {
         auth.requireManage();
+        adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo.getPath()).success();
         return new ResourceAdminManager(session).logoutClient(uriInfo.getRequestUri(), realm, client);
+
     }
 
     /**
@@ -356,7 +379,9 @@ public class ClientResource {
         if (user == null) {
             throw new NotFoundException("User not found");
         }
-        new ResourceAdminManager(session).logoutUserFromClient(uriInfo.getRequestUri(), realm, client, user);
+        adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo.getPath()).success();
+        new ResourceAdminManager(session).logoutUserFromClient(uriInfo.getRequestUri(), realm, client, user);
+
     }
 
     /**
@@ -376,6 +401,7 @@ public class ClientResource {
         }
         if (logger.isDebugEnabled()) logger.debug("Register node: " + node);
         client.registerNode(node, Time.currentTime());
+        adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo.getPath()).success();
     }
 
     /**
@@ -394,8 +420,8 @@ public class ClientResource {
         if (time == null) {
             throw new NotFoundException("Client does not have a node " + node);
         }
-
         client.unregisterNode(node);
+        adminEvent.operation(OperationType.DELETE).resourcePath(uriInfo.getPath()).success();
     }
 
     /**
@@ -408,9 +434,10 @@ public class ClientResource {
     @NoCache
     public GlobalRequestResult testNodesAvailable() {
         auth.requireManage();
-        logger.debug("Test availability of cluster nodes");
-
+        logger.debug("Test availability of cluster nodes");
+        adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo.getPath()).success();
         return new ResourceAdminManager(session).testNodesAvailability(uriInfo.getRequestUri(), realm, client);
+
     }
 
 }
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ClientsByIdResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ClientsByIdResource.java
index 46f2f87..bb9ae8d 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/ClientsByIdResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/ClientsByIdResource.java
@@ -1,5 +1,6 @@
 package org.keycloak.services.resources.admin;
 
+import org.keycloak.events.AdminEventBuilder;
 import org.keycloak.models.ClientModel;
 import org.keycloak.models.RealmModel;
 
@@ -8,8 +9,8 @@ import org.keycloak.models.RealmModel;
  * @version $Revision: 1 $
  */
 public class ClientsByIdResource extends ClientsResource {
-    public ClientsByIdResource(RealmModel realm, RealmAuth auth) {
-        super(realm, auth);
+    public ClientsByIdResource(RealmModel realm, RealmAuth auth, AdminEventBuilder adminEvent) {
+        super(realm, auth, adminEvent);
     }
 
     @Override
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ClientsResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ClientsResource.java
index f6df036..2d19c51 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/ClientsResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/ClientsResource.java
@@ -4,6 +4,8 @@ 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.AdminEventBuilder;
+import org.keycloak.events.admin.OperationType;
 import org.keycloak.models.ClientModel;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.ModelDuplicateException;
@@ -23,6 +25,7 @@ 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.ArrayList;
 import java.util.List;
 
@@ -36,13 +39,15 @@ public class ClientsResource {
     protected static final Logger logger = Logger.getLogger(RealmAdminResource.class);
     protected RealmModel realm;
     private RealmAuth auth;
-
+    private AdminEventBuilder adminEvent;
+    
     @Context
     protected KeycloakSession session;
 
-    public ClientsResource(RealmModel realm, RealmAuth auth) {
+    public ClientsResource(RealmModel realm, RealmAuth auth, AdminEventBuilder adminEvent) {
         this.realm = realm;
         this.auth = auth;
+        this.adminEvent = adminEvent;
         
         auth.init(RealmAuth.Resource.CLIENT);
     }
@@ -72,7 +77,7 @@ public class ClientsResource {
                 rep.add(client);
             }
         }
-
+        adminEvent.operation(OperationType.VIEW).resourcePath(session.getContext().getUri().getPath()).success();
         return rep;
     }
 
@@ -90,6 +95,9 @@ public class ClientsResource {
 
         try {
             ClientModel clientModel = RepresentationToModel.createClient(session, realm, rep, true);
+            adminEvent.operation(OperationType.CREATE).resourcePath(uriInfo.getAbsolutePathBuilder()
+                    .path(getClientPath(clientModel)).build().toString().substring(uriInfo.getBaseUri().toString().length()))
+                    .representation(rep).success();
             return Response.created(uriInfo.getAbsolutePathBuilder().path(getClientPath(clientModel)).build()).build();
         } catch (ModelDuplicateException e) {
             return ErrorResponse.exists("Client " + rep.getClientId() + " already exists");
@@ -112,8 +120,9 @@ public class ClientsResource {
         if (clientModel == null) {
             throw new NotFoundException("Could not find client: " + name);
         }
-        ClientResource clientResource = new ClientResource(realm, auth, clientModel, session);
+        ClientResource clientResource = new ClientResource(realm, auth, clientModel, session, adminEvent);
         ResteasyProviderFactory.getInstance().injectProperties(clientResource);
+        adminEvent.operation(OperationType.VIEW).resourcePath(session.getContext().getUri().getPath()).success();
         return clientResource;
     }
 
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/IdentityProviderResource.java b/services/src/main/java/org/keycloak/services/resources/admin/IdentityProviderResource.java
index 539c410..d70cfd9 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/IdentityProviderResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/IdentityProviderResource.java
@@ -6,6 +6,8 @@ import org.jboss.resteasy.spi.NotFoundException;
 import org.keycloak.broker.provider.IdentityProvider;
 import org.keycloak.broker.provider.IdentityProviderFactory;
 import org.keycloak.broker.provider.IdentityProviderMapper;
+import org.keycloak.events.AdminEventBuilder;
+import org.keycloak.events.admin.OperationType;
 import org.keycloak.models.ClientModel;
 import org.keycloak.models.FederatedIdentityModel;
 import org.keycloak.models.IdentityProviderMapperModel;
@@ -13,7 +15,6 @@ import org.keycloak.models.IdentityProviderModel;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.KeycloakSessionFactory;
 import org.keycloak.models.ModelDuplicateException;
-import org.keycloak.models.ProtocolMapperModel;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.UserModel;
 import org.keycloak.models.utils.ModelToRepresentation;
@@ -25,7 +26,6 @@ import org.keycloak.representations.idm.IdentityProviderMapperRepresentation;
 import org.keycloak.representations.idm.IdentityProviderMapperTypeRepresentation;
 import org.keycloak.representations.idm.IdentityProviderRepresentation;
 import org.keycloak.services.ErrorResponse;
-import org.keycloak.representations.idm.ProtocolMapperRepresentation;
 import org.keycloak.social.SocialIdentityProvider;
 
 import javax.ws.rs.Consumes;
@@ -41,6 +41,7 @@ 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.ArrayList;
 import java.util.HashMap;
 import java.util.LinkedList;
@@ -58,14 +59,16 @@ public class IdentityProviderResource {
     private final RealmModel realm;
     private final KeycloakSession session;
     private final IdentityProviderModel identityProviderModel;
-
+    private final AdminEventBuilder adminEvent;
+    
     @Context private UriInfo uriInfo;
 
-    public IdentityProviderResource(RealmAuth auth, RealmModel realm, KeycloakSession session, IdentityProviderModel identityProviderModel) {
+    public IdentityProviderResource(RealmAuth auth, RealmModel realm, KeycloakSession session, IdentityProviderModel identityProviderModel, AdminEventBuilder adminEvent) {
         this.realm = realm;
         this.session = session;
         this.identityProviderModel = identityProviderModel;
         this.auth = auth;
+        this.adminEvent = adminEvent;
     }
 
     @GET
@@ -74,7 +77,9 @@ public class IdentityProviderResource {
     public IdentityProviderRepresentation getIdentityProvider() {
         this.auth.requireView();
         IdentityProviderRepresentation rep = ModelToRepresentation.toRepresentation(this.identityProviderModel);
-
+        
+        adminEvent.operation(OperationType.VIEW).resourcePath(uriInfo.getPath()).success();
+        
         return rep;
     }
 
@@ -84,7 +89,9 @@ public class IdentityProviderResource {
         this.auth.requireManage();
 
         this.realm.removeIdentityProviderByAlias(this.identityProviderModel.getAlias());
-
+        
+        adminEvent.operation(OperationType.DELETE).resourcePath(uriInfo.getPath()).success();
+        
         return Response.noContent().build();
     }
 
@@ -108,7 +115,9 @@ public class IdentityProviderResource {
 
                 updateUsersAfterProviderAliasChange(this.session.users().getUsers(this.realm), oldProviderId, newProviderId);
             }
-
+            
+            adminEvent.operation(OperationType.UPDATE).resourcePath(uriInfo.getPath()).representation(providerRep).success();
+            
             return Response.noContent().build();
         } catch (ModelDuplicateException e) {
             return ErrorResponse.exists("Identity Provider " + providerRep.getAlias() + " already exists");
@@ -164,6 +173,7 @@ public class IdentityProviderResource {
         try {
             this.auth.requireView();
             IdentityProviderFactory factory = getIdentityProviderFactory();
+            adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo.getPath()).success();
             return factory.create(identityProviderModel).export(uriInfo, realm, format);
         } catch (Exception e) {
             return ErrorResponse.error("Could not export public broker configuration for identity provider [" + identityProviderModel.getProviderId() + "].", Response.Status.NOT_FOUND);
@@ -202,6 +212,7 @@ public class IdentityProviderResource {
                 }
             }
         }
+        adminEvent.operation(OperationType.VIEW).resourcePath(uriInfo.getPath()).success();
         return types;
     }
 
@@ -215,6 +226,7 @@ public class IdentityProviderResource {
         for (IdentityProviderMapperModel model : realm.getIdentityProviderMappersByAlias(identityProviderModel.getAlias())) {
             mappers.add(ModelToRepresentation.toRepresentation(model));
         }
+        adminEvent.operation(OperationType.VIEW).resourcePath(uriInfo.getPath()).success();
         return mappers;
     }
 
@@ -225,6 +237,9 @@ public class IdentityProviderResource {
         auth.requireManage();
         IdentityProviderMapperModel model = RepresentationToModel.toModel(mapper);
         model = realm.addIdentityProviderMapper(model);
+        adminEvent.operation(OperationType.CREATE).resourcePath(uriInfo.getAbsolutePathBuilder()
+                .path(model.getId()).build().toString().substring(uriInfo.getBaseUri().toString().length()))
+                .representation(mapper).success();
         return Response.created(uriInfo.getAbsolutePathBuilder().path(model.getId()).build()).build();
 
     }
@@ -237,6 +252,7 @@ public class IdentityProviderResource {
         auth.requireView();
         IdentityProviderMapperModel model = realm.getIdentityProviderMapperById(id);
         if (model == null) throw new NotFoundException("Model not found");
+        adminEvent.operation(OperationType.VIEW).resourcePath(uriInfo.getPath()).success();
         return ModelToRepresentation.toRepresentation(model);
     }
 
@@ -250,6 +266,8 @@ public class IdentityProviderResource {
         if (model == null) throw new NotFoundException("Model not found");
         model = RepresentationToModel.toModel(rep);
         realm.updateIdentityProviderMapper(model);
+        adminEvent.operation(OperationType.UPDATE).resourcePath(uriInfo.getPath()).representation(rep).success();
+
     }
 
     @DELETE
@@ -260,6 +278,8 @@ public class IdentityProviderResource {
         IdentityProviderMapperModel model = realm.getIdentityProviderMapperById(id);
         if (model == null) throw new NotFoundException("Model not found");
         realm.removeIdentityProviderMapper(model);
+        adminEvent.operation(OperationType.DELETE).resourcePath(uriInfo.getPath()).success();
+
     }
 
 
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/IdentityProvidersResource.java b/services/src/main/java/org/keycloak/services/resources/admin/IdentityProvidersResource.java
index ecedae7..84ffe2f 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/IdentityProvidersResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/IdentityProvidersResource.java
@@ -1,183 +1,198 @@
-package org.keycloak.services.resources.admin;
-
-import org.jboss.resteasy.annotations.cache.NoCache;
-import org.jboss.resteasy.plugins.providers.multipart.InputPart;
-import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataInput;
-import org.jboss.resteasy.spi.NotFoundException;
-import org.jboss.resteasy.spi.ResteasyProviderFactory;
-import org.keycloak.broker.provider.IdentityProvider;
-import org.keycloak.broker.provider.IdentityProviderFactory;
-import org.keycloak.connections.httpclient.HttpClientProvider;
-import org.keycloak.models.IdentityProviderModel;
-import org.keycloak.models.KeycloakSession;
-import org.keycloak.models.ModelDuplicateException;
-import org.keycloak.models.RealmModel;
-import org.keycloak.models.utils.ModelToRepresentation;
-import org.keycloak.models.utils.RepresentationToModel;
-import org.keycloak.provider.ProviderFactory;
-import org.keycloak.representations.idm.IdentityProviderRepresentation;
-import org.keycloak.services.ErrorResponse;
-import org.keycloak.social.SocialIdentityProvider;
-
-import javax.ws.rs.Consumes;
-import javax.ws.rs.GET;
-import javax.ws.rs.POST;
-import javax.ws.rs.Path;
-import javax.ws.rs.PathParam;
-import javax.ws.rs.Produces;
-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.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-
-import static javax.ws.rs.core.Response.Status.BAD_REQUEST;
-
-/**
- * @author Pedro Igor
- */
-public class IdentityProvidersResource {
-
-    private final RealmModel realm;
-    private final KeycloakSession session;
-    private RealmAuth auth;
-
-    public IdentityProvidersResource(RealmModel realm, KeycloakSession session, RealmAuth auth) {
-        this.realm = realm;
-        this.session = session;
-        this.auth = auth;
-        this.auth.init(RealmAuth.Resource.IDENTITY_PROVIDER);
-    }
-
-    @Path("/providers/{provider_id}")
-    @GET
-    @NoCache
-    @Produces(MediaType.APPLICATION_JSON)
-    public Response getIdentityProviders(@PathParam("provider_id") String providerId) {
-        this.auth.requireView();
-        IdentityProviderFactory providerFactory = getProviderFactorytById(providerId);
-
-        if (providerFactory != null) {
-            return Response.ok(providerFactory).build();
-        }
-
-        return Response.status(BAD_REQUEST).build();
-    }
-
-    @POST
-    @Path("import-config")
-    @Consumes(MediaType.MULTIPART_FORM_DATA)
-    @Produces(MediaType.APPLICATION_JSON)
-    public Map<String, String> importFrom(@Context UriInfo uriInfo, MultipartFormDataInput input) throws IOException {
-        this.auth.requireManage();
-        Map<String, List<InputPart>> formDataMap = input.getFormDataMap();
-        String providerId = formDataMap.get("providerId").get(0).getBodyAsString();
-        InputPart file = formDataMap.get("file").get(0);
-        InputStream inputStream = file.getBody(InputStream.class, null);
-        IdentityProviderFactory providerFactory = getProviderFactorytById(providerId);
-        Map<String, String> config = providerFactory.parseConfig(inputStream);
-        return config;
-    }
-
-    @POST
-    @Path("import-config")
-    @Consumes(MediaType.APPLICATION_JSON)
-    @Produces(MediaType.APPLICATION_JSON)
-    public Map<String, String> importFrom(@Context UriInfo uriInfo, Map<String, Object> data) throws IOException {
-        this.auth.requireManage();
-
-        String providerId = data.get("providerId").toString();
-        String from = data.get("fromUrl").toString();
-        InputStream inputStream = session.getProvider(HttpClientProvider.class).get(from);
-        try {
-            IdentityProviderFactory providerFactory = getProviderFactorytById(providerId);
-            Map<String, String> config;
-            config = providerFactory.parseConfig(inputStream);
-            return config;
-        } finally {
-            try {
-                inputStream.close();
-            } catch (IOException e) {
-            }
-        }
-    }
-
-    @GET
-    @Path("instances")
-    @NoCache
-    @Produces(MediaType.APPLICATION_JSON)
-    public List<IdentityProviderRepresentation> getIdentityProviders() {
-        this.auth.requireView();
-
-        List<IdentityProviderRepresentation> representations = new ArrayList<IdentityProviderRepresentation>();
-
-        for (IdentityProviderModel identityProviderModel : realm.getIdentityProviders()) {
-            representations.add(ModelToRepresentation.toRepresentation(identityProviderModel));
-        }
-
-        return representations;
-    }
-
-    @POST
-    @Path("instances")
-    @Consumes(MediaType.APPLICATION_JSON)
-    public Response create(@Context UriInfo uriInfo, IdentityProviderRepresentation representation) {
-        this.auth.requireManage();
-
-        try {
-            IdentityProviderModel identityProvider = RepresentationToModel.toModel(representation);
-            this.realm.addIdentityProvider(identityProvider);
-
-            return Response.created(uriInfo.getAbsolutePathBuilder().path(representation.getProviderId()).build()).build();
-        } catch (ModelDuplicateException e) {
-            return ErrorResponse.exists("Identity Provider " + representation.getAlias() + " already exists");
-        }
-    }
-
-    @Path("instances/{alias}")
-    public IdentityProviderResource getIdentityProvider(@PathParam("alias") String alias) {
-        this.auth.requireView();
-        IdentityProviderModel identityProviderModel = null;
-
-        for (IdentityProviderModel storedIdentityProvider : this.realm.getIdentityProviders()) {
-            if (storedIdentityProvider.getAlias().equals(alias)
-                    || storedIdentityProvider.getInternalId().equals(alias)) {
-                identityProviderModel = storedIdentityProvider;
-            }
-        }
-
-        if (identityProviderModel == null) {
-            throw new NotFoundException("Could not find identity provider: " + alias);
-        }
-
-        IdentityProviderResource identityProviderResource = new IdentityProviderResource(this.auth, realm, session, identityProviderModel);
-        ResteasyProviderFactory.getInstance().injectProperties(identityProviderResource);
-
-        return identityProviderResource;
-    }
-
-    private IdentityProviderFactory getProviderFactorytById(String providerId) {
-        List<ProviderFactory> allProviders = getProviderFactories();
-
-        for (ProviderFactory providerFactory : allProviders) {
-            if (providerFactory.getId().equals(providerId)) {
-                return (IdentityProviderFactory) providerFactory;
-            }
-        }
-
-        return null;
-    }
-
-    private List<ProviderFactory> getProviderFactories() {
-        List<ProviderFactory> allProviders = new ArrayList<ProviderFactory>();
-
-        allProviders.addAll(this.session.getKeycloakSessionFactory().getProviderFactories(IdentityProvider.class));
-        allProviders.addAll(this.session.getKeycloakSessionFactory().getProviderFactories(SocialIdentityProvider.class));
-
-        return allProviders;
-    }
+package org.keycloak.services.resources.admin;
+
+import org.jboss.resteasy.annotations.cache.NoCache;
+import org.jboss.resteasy.plugins.providers.multipart.InputPart;
+import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataInput;
+import org.jboss.resteasy.spi.NotFoundException;
+import org.jboss.resteasy.spi.ResteasyProviderFactory;
+import org.keycloak.broker.provider.IdentityProvider;
+import org.keycloak.broker.provider.IdentityProviderFactory;
+import org.keycloak.connections.httpclient.HttpClientProvider;
+import org.keycloak.events.AdminEventBuilder;
+import org.keycloak.events.admin.OperationType;
+import org.keycloak.models.IdentityProviderModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.ModelDuplicateException;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.utils.ModelToRepresentation;
+import org.keycloak.models.utils.RepresentationToModel;
+import org.keycloak.provider.ProviderFactory;
+import org.keycloak.representations.idm.IdentityProviderRepresentation;
+import org.keycloak.services.ErrorResponse;
+import org.keycloak.social.SocialIdentityProvider;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+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.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import static javax.ws.rs.core.Response.Status.BAD_REQUEST;
+
+/**
+ * @author Pedro Igor
+ */
+public class IdentityProvidersResource {
+
+    private final RealmModel realm;
+    private final KeycloakSession session;
+    private RealmAuth auth;
+    private AdminEventBuilder adminEvent;
+
+    public IdentityProvidersResource(RealmModel realm, KeycloakSession session, RealmAuth auth, AdminEventBuilder adminEvent) {
+        this.realm = realm;
+        this.session = session;
+        this.auth = auth;
+        this.auth.init(RealmAuth.Resource.IDENTITY_PROVIDER);
+        this.adminEvent = adminEvent;
+    }
+
+    @Path("/providers/{provider_id}")
+    @GET
+    @NoCache
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response getIdentityProviders(@PathParam("provider_id") String providerId) {
+        this.auth.requireView();
+        IdentityProviderFactory providerFactory = getProviderFactorytById(providerId);
+
+        if (providerFactory != null) {
+            adminEvent.operation(OperationType.VIEW).resourcePath(session.getContext().getUri().getPath()).success();
+            return Response.ok(providerFactory).build();
+        }
+
+        return Response.status(BAD_REQUEST).build();
+    }
+
+    @POST
+    @Path("import-config")
+    @Consumes(MediaType.MULTIPART_FORM_DATA)
+    @Produces(MediaType.APPLICATION_JSON)
+    public Map<String, String> importFrom(@Context UriInfo uriInfo, MultipartFormDataInput input) throws IOException {
+        this.auth.requireManage();
+        Map<String, List<InputPart>> formDataMap = input.getFormDataMap();
+        String providerId = formDataMap.get("providerId").get(0).getBodyAsString();
+        InputPart file = formDataMap.get("file").get(0);
+        InputStream inputStream = file.getBody(InputStream.class, null);
+        IdentityProviderFactory providerFactory = getProviderFactorytById(providerId);
+        Map<String, String> config = providerFactory.parseConfig(inputStream);
+        
+        adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo.getPath()).representation(config).success();
+
+        return config;
+    }
+
+    @POST
+    @Path("import-config")
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    public Map<String, String> importFrom(@Context UriInfo uriInfo, Map<String, Object> data) throws IOException {
+        this.auth.requireManage();
+
+        String providerId = data.get("providerId").toString();
+        String from = data.get("fromUrl").toString();
+        InputStream inputStream = session.getProvider(HttpClientProvider.class).get(from);
+        try {
+            IdentityProviderFactory providerFactory = getProviderFactorytById(providerId);
+            Map<String, String> config;
+            config = providerFactory.parseConfig(inputStream);
+            adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo.getPath()).representation(config).success();
+            return config;
+        } finally {
+            try {
+                inputStream.close();
+            } catch (IOException e) {
+            }
+        }
+    }
+
+    @GET
+    @Path("instances")
+    @NoCache
+    @Produces(MediaType.APPLICATION_JSON)
+    public List<IdentityProviderRepresentation> getIdentityProviders() {
+        this.auth.requireView();
+
+        List<IdentityProviderRepresentation> representations = new ArrayList<IdentityProviderRepresentation>();
+
+        for (IdentityProviderModel identityProviderModel : realm.getIdentityProviders()) {
+            representations.add(ModelToRepresentation.toRepresentation(identityProviderModel));
+        }
+        adminEvent.operation(OperationType.VIEW).resourcePath(session.getContext().getUri().getPath()).success();
+        return representations;
+    }
+
+    @POST
+    @Path("instances")
+    @Consumes(MediaType.APPLICATION_JSON)
+    public Response create(@Context UriInfo uriInfo, IdentityProviderRepresentation representation) {
+        this.auth.requireManage();
+
+        try {
+            IdentityProviderModel identityProvider = RepresentationToModel.toModel(representation);
+            this.realm.addIdentityProvider(identityProvider);
+
+            adminEvent.operation(OperationType.CREATE).resourcePath(uriInfo.getAbsolutePathBuilder()
+                    .path(representation.getProviderId()).build().toString().substring(uriInfo.getBaseUri().toString().length()))
+                    .representation(representation).success();
+            
+            return Response.created(uriInfo.getAbsolutePathBuilder().path(representation.getProviderId()).build()).build();
+        } catch (ModelDuplicateException e) {
+            return ErrorResponse.exists("Identity Provider " + representation.getAlias() + " already exists");
+        }
+    }
+
+    @Path("instances/{alias}")
+    public IdentityProviderResource getIdentityProvider(@PathParam("alias") String alias) {
+        this.auth.requireView();
+        IdentityProviderModel identityProviderModel = null;
+
+        for (IdentityProviderModel storedIdentityProvider : this.realm.getIdentityProviders()) {
+            if (storedIdentityProvider.getAlias().equals(alias)
+                    || storedIdentityProvider.getInternalId().equals(alias)) {
+                identityProviderModel = storedIdentityProvider;
+            }
+        }
+
+        if (identityProviderModel == null) {
+            throw new NotFoundException("Could not find identity provider: " + alias);
+        }
+
+        IdentityProviderResource identityProviderResource = new IdentityProviderResource(this.auth, realm, session, identityProviderModel, adminEvent);
+        ResteasyProviderFactory.getInstance().injectProperties(identityProviderResource);
+        
+        adminEvent.operation(OperationType.VIEW).resourcePath(session.getContext().getUri().getPath()).success();
+        return identityProviderResource;
+    }
+
+    private IdentityProviderFactory getProviderFactorytById(String providerId) {
+        List<ProviderFactory> allProviders = getProviderFactories();
+
+        for (ProviderFactory providerFactory : allProviders) {
+            if (providerFactory.getId().equals(providerId)) {
+                return (IdentityProviderFactory) providerFactory;
+            }
+        }
+
+        return null;
+    }
+
+    private List<ProviderFactory> getProviderFactories() {
+        List<ProviderFactory> allProviders = new ArrayList<ProviderFactory>();
+
+        allProviders.addAll(this.session.getKeycloakSessionFactory().getProviderFactories(IdentityProvider.class));
+        allProviders.addAll(this.session.getKeycloakSessionFactory().getProviderFactories(SocialIdentityProvider.class));
+
+        return allProviders;
+    }
 }
\ No newline at end of file
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ProtocolMappersResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ProtocolMappersResource.java
index 36428b8..a211087 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/ProtocolMappersResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/ProtocolMappersResource.java
@@ -3,6 +3,8 @@ 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.keycloak.events.AdminEventBuilder;
+import org.keycloak.events.admin.OperationType;
 import org.keycloak.models.ClientModel;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.ProtocolMapperModel;
@@ -22,6 +24,7 @@ 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.LinkedList;
 import java.util.List;
 
@@ -37,6 +40,8 @@ public class ProtocolMappersResource {
     protected ClientModel client;
 
     protected  RealmAuth auth;
+    
+    protected AdminEventBuilder adminEvent;
 
     @Context
     protected UriInfo uriInfo;
@@ -44,9 +49,10 @@ public class ProtocolMappersResource {
     @Context
     protected KeycloakSession session;
 
-    public ProtocolMappersResource(ClientModel client, RealmAuth auth) {
+    public ProtocolMappersResource(ClientModel client, RealmAuth auth, AdminEventBuilder adminEvent) {
         this.auth = auth;
         this.client = client;
+        this.adminEvent = adminEvent;
 
         auth.init(RealmAuth.Resource.USER);
     }
@@ -67,6 +73,7 @@ public class ProtocolMappersResource {
         for (ProtocolMapperModel mapper : client.getProtocolMappers()) {
             if (mapper.getProtocol().equals(protocol)) mappers.add(ModelToRepresentation.toRepresentation(mapper));
         }
+        adminEvent.operation(OperationType.VIEW).resourcePath(uriInfo.getPath()).success();
         return mappers;
     }
 
@@ -83,6 +90,9 @@ public class ProtocolMappersResource {
         auth.requireManage();
         ProtocolMapperModel model = RepresentationToModel.toModel(rep);
         model = client.addProtocolMapper(model);
+        adminEvent.operation(OperationType.CREATE).resourcePath(uriInfo.getAbsolutePathBuilder()
+                .path(model.getId()).build().toString().substring(uriInfo.getBaseUri().toString().length()))
+                .representation(rep).success();
         return Response.created(uriInfo.getAbsolutePathBuilder().path(model.getId()).build()).build();
     }
     /**
@@ -95,10 +105,12 @@ public class ProtocolMappersResource {
     @Consumes(MediaType.APPLICATION_JSON)
     public void createMapper(List<ProtocolMapperRepresentation> reps) {
         auth.requireManage();
+        ProtocolMapperModel model = null;
         for (ProtocolMapperRepresentation rep : reps) {
-            ProtocolMapperModel model = RepresentationToModel.toModel(rep);
+            model = RepresentationToModel.toModel(rep);
             model = client.addProtocolMapper(model);
         }
+        adminEvent.operation(OperationType.CREATE).resourcePath(uriInfo.getPath()).representation(reps).success();
     }
 
     @GET
@@ -111,6 +123,7 @@ public class ProtocolMappersResource {
         for (ProtocolMapperModel mapper : client.getProtocolMappers()) {
             mappers.add(ModelToRepresentation.toRepresentation(mapper));
         }
+        adminEvent.operation(OperationType.VIEW).resourcePath(uriInfo.getPath()).success();
         return mappers;
     }
 
@@ -122,6 +135,7 @@ public class ProtocolMappersResource {
         auth.requireView();
         ProtocolMapperModel model = client.getProtocolMapperById(id);
         if (model == null) throw new NotFoundException("Model not found");
+        adminEvent.operation(OperationType.VIEW).resourcePath(uriInfo.getPath()).success();
         return ModelToRepresentation.toRepresentation(model);
     }
 
@@ -135,6 +149,7 @@ public class ProtocolMappersResource {
         if (model == null) throw new NotFoundException("Model not found");
         model = RepresentationToModel.toModel(rep);
         client.updateProtocolMapper(model);
+        adminEvent.operation(OperationType.UPDATE).resourcePath(uriInfo.getPath()).representation(rep).success();
     }
 
     @DELETE
@@ -145,6 +160,8 @@ public class ProtocolMappersResource {
         ProtocolMapperModel model = client.getProtocolMapperById(id);
         if (model == null) throw new NotFoundException("Model not found");
         client.removeProtocolMapper(model);
+        adminEvent.operation(OperationType.DELETE).resourcePath(uriInfo.getPath()).success();
+
     }
 
 }
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 b0d171b..11b83a0 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
@@ -5,10 +5,15 @@ import org.jboss.resteasy.annotations.cache.NoCache;
 import org.jboss.resteasy.spi.NotFoundException;
 import org.jboss.resteasy.spi.ResteasyProviderFactory;
 import org.keycloak.ClientConnection;
+import org.keycloak.Config;
+import org.keycloak.events.AdminEventBuilder;
 import org.keycloak.events.Event;
 import org.keycloak.events.EventQuery;
 import org.keycloak.events.EventStoreProvider;
 import org.keycloak.events.EventType;
+import org.keycloak.events.admin.AdminEvent;
+import org.keycloak.events.admin.AdminEventQuery;
+import org.keycloak.events.admin.OperationType;
 import org.keycloak.exportimport.ClientImporter;
 import org.keycloak.models.ClientModel;
 import org.keycloak.models.KeycloakSession;
@@ -46,6 +51,7 @@ import javax.ws.rs.core.HttpHeaders;
 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;
@@ -63,6 +69,7 @@ public class RealmAdminResource {
     protected RealmAuth auth;
     protected RealmModel realm;
     private TokenManager tokenManager;
+    private AdminEventBuilder adminEvent;
 
     @Context
     protected KeycloakSession session;
@@ -76,10 +83,11 @@ public class RealmAdminResource {
     @Context
     protected HttpHeaders headers;
 
-    public RealmAdminResource(RealmAuth auth, RealmModel realm, TokenManager tokenManager) {
+    public RealmAdminResource(RealmAuth auth, RealmModel realm, TokenManager tokenManager, AdminEventBuilder adminEvent) {
         this.auth = auth;
         this.realm = realm;
         this.tokenManager = tokenManager;
+        this.adminEvent = adminEvent;
 
         auth.init(RealmAuth.Resource.REALM);
     }
@@ -102,7 +110,7 @@ public class RealmAdminResource {
      */
     @Path("clients")
     public ClientsResource getClients() {
-        ClientsResource clientsResource = new ClientsResource(realm, auth);
+        ClientsResource clientsResource = new ClientsResource(realm, auth, adminEvent);
         ResteasyProviderFactory.getInstance().injectProperties(clientsResource);
         return clientsResource;
     }
@@ -114,7 +122,7 @@ public class RealmAdminResource {
      */
     @Path("clients-by-id")
     public ClientsByIdResource getClientsById() {
-        ClientsByIdResource clientsResource = new ClientsByIdResource(realm, auth);
+        ClientsByIdResource clientsResource = new ClientsByIdResource(realm, auth, adminEvent);
         ResteasyProviderFactory.getInstance().injectProperties(clientsResource);
         return clientsResource;
     }
@@ -126,7 +134,7 @@ public class RealmAdminResource {
      */
     @Path("roles")
     public RoleContainerResource getRoleContainerResource() {
-        return new RoleContainerResource(realm, auth, realm);
+        return new RoleContainerResource(realm, auth, realm, adminEvent);
     }
 
     /**
@@ -148,12 +156,14 @@ public class RealmAdminResource {
                 CacheUserProvider cache = (CacheUserProvider)session.userStorage();
                 rep.setUserCacheEnabled(cache.isEnabled());
             }
+            adminEvent.operation(OperationType.VIEW).resourcePath(uriInfo.getPath()).success();
             return rep;
         } else {
             auth.requireAny();
 
             RealmRepresentation rep = new RealmRepresentation();
             rep.setRealm(realm.getName());
+            adminEvent.operation(OperationType.VIEW).resourcePath(uriInfo.getPath()).success();
             return rep;
         }
     }
@@ -188,7 +198,8 @@ public class RealmAdminResource {
             for (final UserFederationProviderModel fedProvider : federationProviders) {
                 usersSyncManager.refreshPeriodicSyncForProvider(session.getKeycloakSessionFactory(), session.getProvider(TimerProvider.class), fedProvider, realm.getId());
             }
-
+            
+            adminEvent.operation(OperationType.UPDATE).resourcePath(uriInfo.getPath()).representation(rep).success();
             return Response.noContent().build();
         } catch (PatternSyntaxException e) {
             return ErrorResponse.exists("Specified regex pattern(s) is invalid.");
@@ -209,6 +220,8 @@ public class RealmAdminResource {
 
         if (!new RealmManager(session).removeRealm(realm)) {
             throw new NotFoundException("Realm doesn't exist");
+        } else {
+            adminEvent.operation(OperationType.DELETE).resourcePath(uriInfo.getPath()).success();
         }
     }
 
@@ -219,7 +232,7 @@ public class RealmAdminResource {
      */
     @Path("users")
     public UsersResource users() {
-        UsersResource users = new UsersResource(realm, auth, tokenManager);
+        UsersResource users = new UsersResource(realm, auth, tokenManager, adminEvent);
         ResteasyProviderFactory.getInstance().injectProperties(users);
         //resourceContext.initResource(users);
         return users;
@@ -227,7 +240,7 @@ public class RealmAdminResource {
 
     @Path("user-federation")
     public UserFederationResource userFederation() {
-        UserFederationResource fed = new UserFederationResource(realm, auth);
+        UserFederationResource fed = new UserFederationResource(realm, auth, adminEvent);
         ResteasyProviderFactory.getInstance().injectProperties(fed);
         //resourceContext.initResource(fed);
         return fed;
@@ -240,7 +253,7 @@ public class RealmAdminResource {
      */
     @Path("roles-by-id")
     public RoleByIdResource rolesById() {
-        RoleByIdResource resource = new RoleByIdResource(realm, auth);
+        RoleByIdResource resource = new RoleByIdResource(realm, auth, adminEvent);
         ResteasyProviderFactory.getInstance().injectProperties(resource);
         //resourceContext.initResource(resource);
         return resource;
@@ -254,7 +267,8 @@ public class RealmAdminResource {
     @POST
     public GlobalRequestResult pushRevocation() {
         auth.requireManage();
-        return new ResourceAdminManager(session).pushRealmRevocationPolicy(uriInfo.getRequestUri(), realm);
+        adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo.getPath()).success();
+        return new ResourceAdminManager(session).pushRealmRevocationPolicy(uriInfo.getRequestUri(), realm);
     }
 
     /**
@@ -266,7 +280,8 @@ public class RealmAdminResource {
     @POST
     public GlobalRequestResult logoutAll() {
         session.sessions().removeUserSessions(realm);
-        return new ResourceAdminManager(session).logoutAll(uriInfo.getRequestUri(), realm);
+        adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo.getPath()).success();
+        return new ResourceAdminManager(session).logoutAll(uriInfo.getRequestUri(), realm);
     }
 
     /**
@@ -279,8 +294,10 @@ public class RealmAdminResource {
     @DELETE
     public void deleteSession(@PathParam("session") String sessionId) {
         UserSessionModel userSession = session.sessions().getUserSession(realm, sessionId);
-        if (userSession == null) throw new NotFoundException("Sesssion not found");
-        AuthenticationManager.backchannelLogout(session, realm, userSession, uriInfo, connection, headers, true);
+        if (userSession == null) throw new NotFoundException("Sesssion not found");
+        AuthenticationManager.backchannelLogout(session, realm, userSession, uriInfo, connection, headers, true);
+        adminEvent.operation(OperationType.DELETE).resourcePath(uriInfo.getPath()).success();
+
     }
 
     /**
@@ -302,6 +319,7 @@ public class RealmAdminResource {
             if (size == 0) continue;
             stats.put(client.getClientId(), size);
         }
+        adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo.getPath()).representation(stats).success();
         return stats;
     }
 
@@ -327,6 +345,7 @@ public class RealmAdminResource {
             map.put("active", size + "");
             data.add(map);
         }
+        adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo.getPath()).representation(data).success();
         return data;
     }
 
@@ -366,6 +385,8 @@ public class RealmAdminResource {
      * @param client app or oauth client name
      * @param user user id
      * @param ipAddress
+     * @param dateTo
+     * @param dateFrom
      * @param firstResult
      * @param maxResults
      * @return
@@ -419,6 +440,86 @@ public class RealmAdminResource {
 
         return query.getResultList();
     }
+    
+    /**
+     * Query admin events.  Returns all admin events, or will query based on URL query parameters listed here
+     *
+     * @param client app or oauth client name
+     * @param operationTypes operation type
+     * @param authUser user id
+     * @param authIpAddress
+     * @param resourcePath
+     * @param dateTo
+     * @param dateFrom
+     * @param resourcePath
+     * @param firstResult
+     * @param maxResults
+     * @return
+     */
+    @Path("admin-events")
+    @GET
+    @NoCache
+    @Produces(MediaType.APPLICATION_JSON)
+    public List<AdminEvent> getEvents(@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) {
+        auth.init(RealmAuth.Resource.EVENTS).requireView();
+
+        EventStoreProvider eventStore = session.getProvider(EventStoreProvider.class);
+        AdminEventQuery query = null;
+        
+        if(realm.getName().equals(Config.getAdminRealm())) {
+            query = eventStore.createAdminQuery();
+            if(authRealm != null) {
+                query.authRealm(authRealm);
+            }
+        } else {
+            query = eventStore.createAdminQuery().authRealm(realm.getId());
+        }
+        
+        if (authClient != null) {
+            query.authClient(authClient);
+        }
+        
+        if (authUser != null) {
+            query.authUser(authUser);
+        }
+        
+        if (authIpAddress != null) {
+            query.authIpAddress(authIpAddress);
+        }
+        
+        if (resourcePath != null) {
+            query.resourcePath(resourcePath);
+        }
+
+        List<String> operationTypes = uriInfo.getQueryParameters().get("operationTypes");
+        if (operationTypes != null) {
+            OperationType[] t = new OperationType[operationTypes.size()];
+            for (int i = 0; i < t.length; i++) {
+                t[i] = OperationType.valueOf(operationTypes.get(i));
+            }
+            query.operation(t);
+        }
+        
+        if(dateFrom != null) {
+            query.fromTime(dateFrom);
+        }
+        if(dateTo != null) {
+            query.toTime(dateTo);
+        }
+
+        if (firstResult != null) {
+            query.firstResult(firstResult);
+        }
+        if (maxResults != null) {
+            query.maxResults(maxResults);
+        }
+
+        return query.getResultList();
+    }
 
     /**
      * Delete all events.
@@ -432,6 +533,19 @@ public class RealmAdminResource {
         EventStoreProvider eventStore = session.getProvider(EventStoreProvider.class);
         eventStore.clear(realm.getId());
     }
+    
+    /**
+     * Delete all admin events.
+     *
+     */
+    @Path("admin-events")
+    @DELETE
+    public void clearAdminEvents() {
+        auth.init(RealmAuth.Resource.EVENTS).requireManage();
+
+        EventStoreProvider eventStore = session.getProvider(EventStoreProvider.class);
+        eventStore.clearAdmin(realm.getId());
+    }
 
     @Path("testLDAPConnection")
     @GET
@@ -446,6 +560,6 @@ public class RealmAdminResource {
 
     @Path("identity-provider")
     public IdentityProvidersResource getIdentityProviderResource() {
-        return new IdentityProvidersResource(realm, session, this.auth);
+        return new IdentityProvidersResource(realm, session, this.auth, adminEvent);
     }
 }
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RealmsAdminResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RealmsAdminResource.java
index c9fea3d..e50c25c 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/RealmsAdminResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/RealmsAdminResource.java
@@ -6,6 +6,8 @@ import org.jboss.resteasy.plugins.providers.multipart.InputPart;
 import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataInput;
 import org.jboss.resteasy.spi.NotFoundException;
 import org.jboss.resteasy.spi.ResteasyProviderFactory;
+import org.keycloak.events.AdminEventBuilder;
+import org.keycloak.events.admin.OperationType;
 import org.keycloak.models.AdminRoles;
 import org.keycloak.models.ClientModel;
 import org.keycloak.models.KeycloakSession;
@@ -33,6 +35,7 @@ import javax.ws.rs.core.HttpHeaders;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriInfo;
+
 import java.io.IOException;
 import java.net.URI;
 import java.util.ArrayList;
@@ -49,16 +52,18 @@ public class RealmsAdminResource {
     protected static final Logger logger = Logger.getLogger(RealmsAdminResource.class);
     protected AdminAuth auth;
     protected TokenManager tokenManager;
+    protected AdminEventBuilder adminEvent;
 
     @Context
     protected KeycloakSession session;
-
+    
     @Context
     protected KeycloakApplication keycloak;
 
-    public RealmsAdminResource(AdminAuth auth, TokenManager tokenManager) {
+    public RealmsAdminResource(AdminAuth auth, TokenManager tokenManager, AdminEventBuilder adminEvent) {
         this.auth = auth;
         this.tokenManager = tokenManager;
+        this.adminEvent = adminEvent;
     }
 
     public static final CacheControl noCache = new CacheControl();
@@ -87,6 +92,7 @@ public class RealmsAdminResource {
             ClientModel adminApp = auth.getRealm().getClientByClientId(realmManager.getRealmAdminClientId(auth.getRealm()));
             addRealmRep(reps, auth.getRealm(), adminApp);
         }
+        adminEvent.operation(OperationType.VIEW).resourcePath(session.getContext().getUri().getPath()).success();
         logger.debug(("getRealms()"));
         return reps;
     }
@@ -128,6 +134,8 @@ public class RealmsAdminResource {
 
             URI location = AdminRoot.realmsUrl(uriInfo).path(realm.getName()).build();
             logger.debugv("imported realm success, sending back: {0}", location.toString());
+            
+            adminEvent.operation(OperationType.CREATE).resourcePath(location.toString()).representation(rep).success();
 
             return Response.created(location).build();
         } catch (ModelDuplicateException e) {
@@ -158,10 +166,11 @@ public class RealmsAdminResource {
 
         Map<String, List<InputPart>> uploadForm = input.getFormDataMap();
         List<InputPart> inputParts = uploadForm.get("file");
-
+        RealmRepresentation rep = null;
+        
         for (InputPart inputPart : inputParts) {
             // inputPart.getBody doesn't work as content-type is wrong, and inputPart.setMediaType is not supported on AS7 (RestEasy 2.3.2.Final)
-            RealmRepresentation rep = JsonSerialization.readValue(inputPart.getBodyAsString(), RealmRepresentation.class);
+            rep = JsonSerialization.readValue(inputPart.getBodyAsString(), RealmRepresentation.class);
             RealmModel realm;
             try {
                 realm = realmManager.importRealm(rep);
@@ -170,13 +179,15 @@ public class RealmsAdminResource {
             }
 
             grantPermissionsToRealmCreator(realm);
-
+            
+            URI location = null;
             if (inputParts.size() == 1) {
-                URI location = AdminRoot.realmsUrl(uriInfo).path(realm.getName()).build();
+                location = AdminRoot.realmsUrl(uriInfo).path(realm.getName()).build();
+                adminEvent.operation(OperationType.CREATE).resourcePath(location.toString()).representation(rep).success();
                 return Response.created(location).build();
             }
         }
-
+        
         return Response.noContent().build();
     }
 
@@ -219,7 +230,7 @@ public class RealmsAdminResource {
             realmAuth = new RealmAuth(auth, realm.getClientByClientId(realmManager.getRealmAdminClientId(auth.getRealm())));
         }
 
-        RealmAdminResource adminResource = new RealmAdminResource(realmAuth, realm, tokenManager);
+        RealmAdminResource adminResource = new RealmAdminResource(realmAuth, realm, tokenManager, adminEvent);
         ResteasyProviderFactory.getInstance().injectProperties(adminResource);
         //resourceContext.initResource(adminResource);
         return adminResource;
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RoleByIdResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RoleByIdResource.java
index 67d8c12..b54d44f 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/RoleByIdResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/RoleByIdResource.java
@@ -3,6 +3,8 @@ 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.keycloak.events.AdminEventBuilder;
+import org.keycloak.events.admin.OperationType;
 import org.keycloak.models.ClientModel;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
@@ -20,6 +22,8 @@ import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
 import javax.ws.rs.core.Context;
 import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.UriInfo;
+
 import java.util.List;
 import java.util.Set;
 
@@ -33,15 +37,17 @@ public class RoleByIdResource extends RoleResource {
     protected static final Logger logger = Logger.getLogger(RoleByIdResource.class);
     private final RealmModel realm;
     private final RealmAuth auth;
+    private AdminEventBuilder adminEvent;
 
     @Context
     protected KeycloakSession session;
 
-    public RoleByIdResource(RealmModel realm, RealmAuth auth) {
+    public RoleByIdResource(RealmModel realm, RealmAuth auth, AdminEventBuilder adminEvent) {
         super(realm);
 
         this.realm = realm;
         this.auth = auth;
+        this.adminEvent = adminEvent;
     }
 
     /**
@@ -57,6 +63,8 @@ public class RoleByIdResource extends RoleResource {
     public RoleRepresentation getRole(final @PathParam("role-id") String id) {
         RoleModel roleModel = getRoleModel(id);
         auth.requireView();
+        
+        adminEvent.operation(OperationType.VIEW).resourcePath(session.getContext().getUri().getPath()).success();
 
         return getRole(roleModel);
     }
@@ -76,6 +84,8 @@ public class RoleByIdResource extends RoleResource {
             r = RealmAuth.Resource.USER;
         }
         auth.init(r);
+        
+        adminEvent.operation(OperationType.VIEW).resourcePath(session.getContext().getUri().getPath()).success();
 
         return roleModel;
     }
@@ -92,6 +102,7 @@ public class RoleByIdResource extends RoleResource {
         RoleModel role = getRoleModel(id);
         auth.requireManage();
         deleteRole(role);
+        adminEvent.operation(OperationType.DELETE).resourcePath(session.getContext().getUri().getPath()).success();
     }
 
     /**
@@ -107,6 +118,7 @@ public class RoleByIdResource extends RoleResource {
         RoleModel role = getRoleModel(id);
         auth.requireManage();
         updateRole(rep, role);
+        adminEvent.operation(OperationType.UPDATE).resourcePath(session.getContext().getUri().getPath()).representation(rep).success();
     }
 
     /**
@@ -122,6 +134,8 @@ public class RoleByIdResource extends RoleResource {
         RoleModel role = getRoleModel(id);
         auth.requireManage();
         addComposites(roles, role);
+        adminEvent.operation(OperationType.ACTION).resourcePath(session.getContext().getUri().getPath()).representation(roles).success();
+        
     }
 
     /**
@@ -139,6 +153,7 @@ public class RoleByIdResource extends RoleResource {
         if (logger.isDebugEnabled()) logger.debug("*** getRoleComposites: '" + id + "'");
         RoleModel role = getRoleModel(id);
         auth.requireView();
+        adminEvent.operation(OperationType.VIEW).resourcePath(session.getContext().getUri().getPath()).success();
         return getRoleComposites(role);
     }
 
@@ -155,6 +170,7 @@ public class RoleByIdResource extends RoleResource {
     public Set<RoleRepresentation> getRealmRoleComposites(final @PathParam("role-id") String id) {
         RoleModel role = getRoleModel(id);
         auth.requireView();
+        adminEvent.operation(OperationType.VIEW).resourcePath(session.getContext().getUri().getPath()).success();
         return getRealmRoleComposites(role);
     }
 
@@ -178,6 +194,7 @@ public class RoleByIdResource extends RoleResource {
             throw new NotFoundException("Could not find client: " + appName);
 
         }
+        adminEvent.operation(OperationType.VIEW).resourcePath(session.getContext().getUri().getPath()).success();
         return getClientRoleComposites(app, role);
     }
 
@@ -201,6 +218,7 @@ public class RoleByIdResource extends RoleResource {
             throw new NotFoundException("Could not find client: " + appId);
 
         }
+        adminEvent.operation(OperationType.VIEW).resourcePath(session.getContext().getUri().getPath()).success();
         return getClientRoleComposites(app, role);
     }
 
@@ -217,6 +235,7 @@ public class RoleByIdResource extends RoleResource {
         RoleModel role = getRoleModel(id);
         auth.requireManage();
         deleteComposites(roles, role);
+        adminEvent.operation(OperationType.DELETE).resourcePath(session.getContext().getUri().getPath()).success();
     }
 
 }
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RoleContainerResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RoleContainerResource.java
index fa0064f..43a41f6 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/RoleContainerResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/RoleContainerResource.java
@@ -2,7 +2,10 @@ package org.keycloak.services.resources.admin;
 
 import org.jboss.resteasy.annotations.cache.NoCache;
 import org.jboss.resteasy.spi.NotFoundException;
+import org.keycloak.events.AdminEventBuilder;
+import org.keycloak.events.admin.OperationType;
 import org.keycloak.models.ClientModel;
+import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.ModelDuplicateException;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.RoleContainerModel;
@@ -23,6 +26,7 @@ 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.ArrayList;
 import java.util.List;
 import java.util.Set;
@@ -35,12 +39,14 @@ public class RoleContainerResource extends RoleResource {
     private final RealmModel realm;
     private final RealmAuth auth;
     protected RoleContainerModel roleContainer;
+    private AdminEventBuilder adminEvent;
 
-    public RoleContainerResource(RealmModel realm, RealmAuth auth, RoleContainerModel roleContainer) {
+    public RoleContainerResource(RealmModel realm, RealmAuth auth, RoleContainerModel roleContainer, AdminEventBuilder adminEvent) {
         super(realm);
         this.realm = realm;
         this.auth = auth;
         this.roleContainer = roleContainer;
+        this.adminEvent = adminEvent;
     }
 
     /**
@@ -51,7 +57,7 @@ public class RoleContainerResource extends RoleResource {
     @GET
     @NoCache
     @Produces(MediaType.APPLICATION_JSON)
-    public List<RoleRepresentation> getRoles() {
+    public List<RoleRepresentation> getRoles(@Context final UriInfo uriInfo) {
         auth.requireAny();
 
         Set<RoleModel> roleModels = roleContainer.getRoles();
@@ -59,6 +65,7 @@ public class RoleContainerResource extends RoleResource {
         for (RoleModel roleModel : roleModels) {
             roles.add(ModelToRepresentation.toRepresentation(roleModel));
         }
+        adminEvent.operation(OperationType.VIEW).resourcePath(uriInfo.getPath()).success();
         return roles;
     }
 
@@ -77,6 +84,11 @@ public class RoleContainerResource extends RoleResource {
         try {
             RoleModel role = roleContainer.addRole(rep.getName());
             role.setDescription(rep.getDescription());
+
+            adminEvent.operation(OperationType.CREATE).resourcePath(uriInfo.getAbsolutePathBuilder()
+                    .path(role.getName()).build().toString().substring(uriInfo.getBaseUri().toString().length()))
+                    .representation(rep).success();
+
             return Response.created(uriInfo.getAbsolutePathBuilder().path(role.getName()).build()).build();
         } catch (ModelDuplicateException e) {
             return ErrorResponse.exists("Role with name " + rep.getName() + " already exists");
@@ -93,7 +105,7 @@ public class RoleContainerResource extends RoleResource {
     @GET
     @NoCache
     @Produces(MediaType.APPLICATION_JSON)
-    public RoleRepresentation getRole(final @PathParam("role-name") String roleName) {
+    public RoleRepresentation getRole(@Context final UriInfo uriInfo, final @PathParam("role-name") String roleName) {
         auth.requireView();
 
         RoleModel roleModel = roleContainer.getRole(roleName);
@@ -101,6 +113,8 @@ public class RoleContainerResource extends RoleResource {
             throw new NotFoundException("Could not find role: " + roleName);
         }
 
+        adminEvent.operation(OperationType.VIEW).resourcePath(uriInfo.getPath()).success();
+
         return getRole(roleModel);
     }
 
@@ -112,15 +126,18 @@ public class RoleContainerResource extends RoleResource {
     @Path("{role-name}")
     @DELETE
     @NoCache
-    public void deleteRole(final @PathParam("role-name") String roleName) {
+    public void deleteRole(@Context final UriInfo uriInfo, final @PathParam("role-name") String roleName) {
         auth.requireManage();
 
-        RoleRepresentation rep = getRole(roleName);
+        RoleRepresentation rep = getRole(uriInfo, roleName);
         RoleModel role = roleContainer.getRole(roleName);
         if (role == null) {
             throw new NotFoundException("Could not find role: " + roleName);
         }
         deleteRole(role);
+
+        adminEvent.operation(OperationType.DELETE).resourcePath(uriInfo.getPath()).success();
+
     }
 
     /**
@@ -133,7 +150,7 @@ public class RoleContainerResource extends RoleResource {
     @Path("{role-name}")
     @PUT
     @Consumes(MediaType.APPLICATION_JSON)
-    public Response updateRole(final @PathParam("role-name") String roleName, final RoleRepresentation rep) {
+    public Response updateRole(@Context final UriInfo uriInfo, final @PathParam("role-name") String roleName, final RoleRepresentation rep) {
         auth.requireManage();
 
         RoleModel role = roleContainer.getRole(roleName);
@@ -142,6 +159,9 @@ public class RoleContainerResource extends RoleResource {
         }
         try {
             updateRole(rep, role);
+
+            adminEvent.operation(OperationType.UPDATE).resourcePath(uriInfo.getPath()).representation(rep).success();
+
             return Response.noContent().build();
         } catch (ModelDuplicateException e) {
             return ErrorResponse.exists("Role with name " + rep.getName() + " already exists");
@@ -157,7 +177,7 @@ public class RoleContainerResource extends RoleResource {
     @Path("{role-name}/composites")
     @POST
     @Consumes(MediaType.APPLICATION_JSON)
-    public void addComposites(final @PathParam("role-name") String roleName, List<RoleRepresentation> roles) {
+    public void addComposites(@Context final UriInfo uriInfo, final @PathParam("role-name") String roleName, List<RoleRepresentation> roles) {
         auth.requireManage();
 
         RoleModel role = roleContainer.getRole(roleName);
@@ -165,6 +185,8 @@ public class RoleContainerResource extends RoleResource {
             throw new NotFoundException("Could not find role: " + roleName);
         }
         addComposites(roles, role);
+        adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo.getPath()).representation(roles).success();
+
     }
 
     /**
@@ -177,13 +199,14 @@ public class RoleContainerResource extends RoleResource {
     @GET
     @NoCache
     @Produces(MediaType.APPLICATION_JSON)
-    public Set<RoleRepresentation> getRoleComposites(final @PathParam("role-name") String roleName) {
+    public Set<RoleRepresentation> getRoleComposites(@Context final UriInfo uriInfo, final @PathParam("role-name") String roleName) {
         auth.requireManage();
 
         RoleModel role = roleContainer.getRole(roleName);
         if (role == null) {
             throw new NotFoundException("Could not find role: " + roleName);
         }
+        adminEvent.operation(OperationType.VIEW).resourcePath(uriInfo.getPath()).success();
         return getRoleComposites(role);
     }
 
@@ -197,13 +220,14 @@ public class RoleContainerResource extends RoleResource {
     @GET
     @NoCache
     @Produces(MediaType.APPLICATION_JSON)
-    public Set<RoleRepresentation> getRealmRoleComposites(final @PathParam("role-name") String roleName) {
+    public Set<RoleRepresentation> getRealmRoleComposites(@Context final UriInfo uriInfo, final @PathParam("role-name") String roleName) {
         auth.requireManage();
 
         RoleModel role = roleContainer.getRole(roleName);
         if (role == null) {
             throw new NotFoundException("Could not find role: " + roleName);
         }
+        adminEvent.operation(OperationType.VIEW).resourcePath(uriInfo.getPath()).success();
         return getRealmRoleComposites(role);
     }
 
@@ -218,7 +242,8 @@ public class RoleContainerResource extends RoleResource {
     @GET
     @NoCache
     @Produces(MediaType.APPLICATION_JSON)
-    public Set<RoleRepresentation> getClientRoleComposites(final @PathParam("role-name") String roleName,
+    public Set<RoleRepresentation> getClientRoleComposites(@Context final UriInfo uriInfo, 
+                                                           final @PathParam("role-name") String roleName,
                                                            final @PathParam("clientId") String clientId) {
         auth.requireManage();
 
@@ -231,6 +256,7 @@ public class RoleContainerResource extends RoleResource {
             throw new NotFoundException("Could not find client: " + clientId);
 
         }
+        adminEvent.operation(OperationType.VIEW).resourcePath(uriInfo.getPath()).success();
         return getClientRoleComposites(app, role);
     }
 
@@ -246,7 +272,8 @@ public class RoleContainerResource extends RoleResource {
     @GET
     @NoCache
     @Produces(MediaType.APPLICATION_JSON)
-    public Set<RoleRepresentation> getClientByIdRoleComposites(final @PathParam("role-name") String roleName,
+    public Set<RoleRepresentation> getClientByIdRoleComposites(@Context final UriInfo uriInfo,
+                                                                final @PathParam("role-name") String roleName,
                                                                 final @PathParam("id") String id) {
         auth.requireManage();
 
@@ -259,6 +286,7 @@ public class RoleContainerResource extends RoleResource {
             throw new NotFoundException("Could not find client: " + id);
 
         }
+        adminEvent.operation(OperationType.VIEW).resourcePath(uriInfo.getPath()).success();
         return getClientRoleComposites(client, role);
     }
 
@@ -272,7 +300,9 @@ public class RoleContainerResource extends RoleResource {
     @Path("{role-name}/composites")
     @DELETE
     @Consumes(MediaType.APPLICATION_JSON)
-    public void deleteComposites(final @PathParam("role-name") String roleName, List<RoleRepresentation> roles) {
+    public void deleteComposites(@Context final UriInfo uriInfo, 
+                                   final @PathParam("role-name") String roleName,
+                                   List<RoleRepresentation> roles) {
         auth.requireManage();
 
         RoleModel role = roleContainer.getRole(roleName);
@@ -280,6 +310,7 @@ public class RoleContainerResource extends RoleResource {
             throw new NotFoundException("Could not find role: " + roleName);
         }
         deleteComposites(roles, role);
+        adminEvent.operation(OperationType.DELETE).resourcePath(uriInfo.getPath()).success();
     }
 
 
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ScopeMappedClientResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ScopeMappedClientResource.java
index 01fe1d6..2825dac 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/ScopeMappedClientResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/ScopeMappedClientResource.java
@@ -2,6 +2,8 @@ package org.keycloak.services.resources.admin;
 
 import org.jboss.resteasy.annotations.cache.NoCache;
 import org.jboss.resteasy.spi.NotFoundException;
+import org.keycloak.events.AdminEventBuilder;
+import org.keycloak.events.admin.OperationType;
 import org.keycloak.models.ClientModel;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
@@ -15,7 +17,10 @@ import javax.ws.rs.GET;
 import javax.ws.rs.POST;
 import javax.ws.rs.Path;
 import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
 import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.UriInfo;
+
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Set;
@@ -30,13 +35,15 @@ public class ScopeMappedClientResource {
     protected ClientModel client;
     protected KeycloakSession session;
     protected ClientModel scopedClient;
-
-    public ScopeMappedClientResource(RealmModel realm, RealmAuth auth, ClientModel client, KeycloakSession session, ClientModel scopedClient) {
+    protected AdminEventBuilder adminEvent;
+    
+    public ScopeMappedClientResource(RealmModel realm, RealmAuth auth, ClientModel client, KeycloakSession session, ClientModel scopedClient, AdminEventBuilder adminEvent) {
         this.realm = realm;
         this.auth = auth;
         this.client = client;
         this.session = session;
         this.scopedClient = scopedClient;
+        this.adminEvent = adminEvent;
     }
 
     /**
@@ -55,6 +62,7 @@ public class ScopeMappedClientResource {
         for (RoleModel roleModel : mappings) {
             mapRep.add(ModelToRepresentation.toRepresentation(roleModel));
         }
+        adminEvent.operation(OperationType.VIEW).resourcePath(session.getContext().getUri().getPath()).success();
         return mapRep;
     }
 
@@ -71,6 +79,7 @@ public class ScopeMappedClientResource {
         auth.requireView();
 
         Set<RoleModel> roles = scopedClient.getRoles();
+        adminEvent.operation(OperationType.VIEW).resourcePath(session.getContext().getUri().getPath()).success();
         return ScopeMappedResource.getAvailable(client, roles);
     }
 
@@ -87,6 +96,7 @@ public class ScopeMappedClientResource {
         auth.requireView();
 
         Set<RoleModel> roles = scopedClient.getRoles();
+        adminEvent.operation(OperationType.VIEW).resourcePath(session.getContext().getUri().getPath()).success();
         return ScopeMappedResource.getComposite(client, roles);
     }
 
@@ -107,6 +117,7 @@ public class ScopeMappedClientResource {
             }
             client.addScopeMapping(roleModel);
         }
+        adminEvent.operation(OperationType.CREATE).resourcePath(session.getContext().getUri().getPath()).representation(roles).success();
 
     }
 
@@ -135,5 +146,6 @@ public class ScopeMappedClientResource {
                 client.deleteScopeMapping(roleModel);
             }
         }
+        adminEvent.operation(OperationType.DELETE).resourcePath(session.getContext().getUri().getPath()).success();
     }
 }
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ScopeMappedResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ScopeMappedResource.java
index 8d4e005..16b8744 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/ScopeMappedResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/ScopeMappedResource.java
@@ -2,6 +2,8 @@ package org.keycloak.services.resources.admin;
 
 import org.jboss.resteasy.annotations.cache.NoCache;
 import org.jboss.resteasy.spi.NotFoundException;
+import org.keycloak.events.AdminEventBuilder;
+import org.keycloak.events.admin.OperationType;
 import org.keycloak.models.ClientModel;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
@@ -18,7 +20,10 @@ import javax.ws.rs.POST;
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
 import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.UriInfo;
+
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
@@ -36,12 +41,14 @@ public class ScopeMappedResource {
     private RealmAuth auth;
     protected ClientModel client;
     protected KeycloakSession session;
+    protected AdminEventBuilder adminEvent;
 
-    public ScopeMappedResource(RealmModel realm, RealmAuth auth, ClientModel client, KeycloakSession session) {
+    public ScopeMappedResource(RealmModel realm, RealmAuth auth, ClientModel client, KeycloakSession session, AdminEventBuilder adminEvent) {
         this.realm = realm;
         this.auth = auth;
         this.client = client;
         this.session = session;
+        this.adminEvent = adminEvent;
     }
 
     /**
@@ -84,6 +91,7 @@ public class ScopeMappedResource {
                 }
             }
         }
+        adminEvent.operation(OperationType.VIEW).resourcePath(session.getContext().getUri().getPath()).success();
         return all;
     }
 
@@ -104,6 +112,7 @@ public class ScopeMappedResource {
         for (RoleModel roleModel : realmMappings) {
             realmMappingsRep.add(ModelToRepresentation.toRepresentation(roleModel));
         }
+        adminEvent.operation(OperationType.VIEW).resourcePath(session.getContext().getUri().getPath()).success();
         return realmMappingsRep;
     }
 
@@ -120,6 +129,7 @@ public class ScopeMappedResource {
         auth.requireView();
 
         Set<RoleModel> roles = realm.getRoles();
+        adminEvent.operation(OperationType.VIEW).resourcePath(session.getContext().getUri().getPath()).success();
         return getAvailable(client, roles);
     }
 
@@ -147,6 +157,7 @@ public class ScopeMappedResource {
         auth.requireView();
 
         Set<RoleModel> roles = realm.getRoles();
+        adminEvent.operation(OperationType.VIEW).resourcePath(session.getContext().getUri().getPath()).success();
         return getComposite(client, roles);
     }
 
@@ -176,7 +187,7 @@ public class ScopeMappedResource {
             }
             client.addScopeMapping(roleModel);
         }
-
+        adminEvent.operation(OperationType.ACTION).resourcePath(session.getContext().getUri().getPath()).representation(roles).success();
 
     }
 
@@ -206,6 +217,8 @@ public class ScopeMappedResource {
                 client.deleteScopeMapping(roleModel);
             }
         }
+        adminEvent.operation(OperationType.DELETE).resourcePath(session.getContext().getUri().getPath()).success();
+
     }
 
     @Path("clients/{clientId}")
@@ -215,8 +228,8 @@ public class ScopeMappedResource {
         if (app == null) {
             throw new NotFoundException("Role not found");
         }
-
-        return new ScopeMappedClientResource(realm, auth, client, session, app);
+        adminEvent.operation(OperationType.VIEW).resourcePath(session.getContext().getUri().getPath()).success();
+        return new ScopeMappedClientResource(realm, auth, client, session, app, adminEvent);
     }
 
     @Path("clients-by-id/{id}")
@@ -226,7 +239,7 @@ public class ScopeMappedResource {
         if (app == null) {
             throw new NotFoundException("Client not found");
         }
-
-        return new ScopeMappedClientResource(realm, auth, client, session, app);
+        adminEvent.operation(OperationType.VIEW).resourcePath(session.getContext().getUri().getPath()).success();
+        return new ScopeMappedClientResource(realm, auth, client, session, app, adminEvent);
     }
 }
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ServerInfoAdminResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ServerInfoAdminResource.java
index f535b19..4e0d8f9 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/ServerInfoAdminResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/ServerInfoAdminResource.java
@@ -5,6 +5,7 @@ import org.keycloak.broker.provider.IdentityProvider;
 import org.keycloak.broker.provider.IdentityProviderFactory;
 import org.keycloak.events.EventListenerProvider;
 import org.keycloak.events.EventType;
+import org.keycloak.events.admin.OperationType;
 import org.keycloak.exportimport.ClientImporter;
 import org.keycloak.exportimport.ClientImporterFactory;
 import org.keycloak.freemarker.Theme;
@@ -39,6 +40,8 @@ import java.util.Set;
  */
 public class ServerInfoAdminResource {
 
+    private static final Map<String, List<String>> ENUMS = createEnumsMap(EventType.class, OperationType.class);
+
     @Context
     private KeycloakSession session;
 
@@ -61,7 +64,7 @@ public class ServerInfoAdminResource {
         setProviders(info);
         setProtocolMapperTypes(info);
         setBuiltinProtocolMappers(info);
-        setEventTypes(info);
+        info.setEnums(ENUMS);
         return info;
     }
 
@@ -181,15 +184,6 @@ public class ServerInfoAdminResource {
         }
     }
 
-    private void setEventTypes(ServerInfoRepresentation info) {
-        List<String> eventTypes = new LinkedList<>();
-        for (EventType t : EventType.values()) {
-            eventTypes.add(t.name());
-        }
-        Collections.sort(eventTypes);
-        info.setEventTypes(eventTypes);
-    }
-
     public static class ServerInfoRepresentation {
 
         private String version;
@@ -209,7 +203,7 @@ public class ServerInfoAdminResource {
         private Map<String, List<ProtocolMapperTypeRepresentation>> protocolMapperTypes;
         private Map<String, List<ProtocolMapperRepresentation>> builtinProtocolMappers;
 
-        private List<String> eventTypes;
+        private Map<String, List<String>> enums;
 
         public ServerInfoRepresentation() {
         }
@@ -262,13 +256,30 @@ public class ServerInfoAdminResource {
             this.builtinProtocolMappers = builtinProtocolMappers;
         }
 
-        public List<String> getEventTypes() {
-            return eventTypes;
+        public Map<String, List<String>> getEnums() {
+            return enums;
+        }
+
+        public void setEnums(Map<String, List<String>> enums) {
+            this.enums = enums;
         }
+    }
+
+    private static Map<String, List<String>> createEnumsMap(Class... enums) {
+        Map<String, List<String>> m = new HashMap<>();
+        for (Class e : enums) {
+            String n = e.getSimpleName();
+            n = Character.toLowerCase(n.charAt(0)) + n.substring(1);
+
+            List<String> l = new LinkedList<>();
+            for (Object c :  e.getEnumConstants()) {
+                l.add(c.toString());
+            }
+            Collections.sort(l);
 
-        public void setEventTypes(List<String> eventTypes) {
-            this.eventTypes = eventTypes;
+            m.put(n, l);
         }
+        return m;
     }
 
 }
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/UserClientRoleMappingsResource.java b/services/src/main/java/org/keycloak/services/resources/admin/UserClientRoleMappingsResource.java
index e838333..39e180a 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/UserClientRoleMappingsResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/UserClientRoleMappingsResource.java
@@ -3,7 +3,10 @@ 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.keycloak.events.AdminEventBuilder;
+import org.keycloak.events.admin.OperationType;
 import org.keycloak.models.ClientModel;
+import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.RoleModel;
 import org.keycloak.models.UserModel;
@@ -16,7 +19,10 @@ import javax.ws.rs.GET;
 import javax.ws.rs.POST;
 import javax.ws.rs.Path;
 import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
 import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.UriInfo;
+
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.List;
@@ -33,12 +39,18 @@ public class UserClientRoleMappingsResource {
     protected RealmAuth auth;
     protected UserModel user;
     protected ClientModel client;
+    protected AdminEventBuilder adminEvent;
+    
+    @Context
+    protected KeycloakSession session;
+    
 
-    public UserClientRoleMappingsResource(RealmModel realm, RealmAuth auth, UserModel user, ClientModel client) {
+    public UserClientRoleMappingsResource(RealmModel realm, RealmAuth auth, UserModel user, ClientModel client, AdminEventBuilder adminEvent) {
         this.realm = realm;
         this.auth = auth;
         this.user = user;
         this.client = client;
+        this.adminEvent = adminEvent;
     }
 
     /**
@@ -57,6 +69,7 @@ public class UserClientRoleMappingsResource {
         for (RoleModel roleModel : mappings) {
             mapRep.add(ModelToRepresentation.toRepresentation(roleModel));
         }
+        adminEvent.operation(OperationType.VIEW).resourcePath(session.getContext().getUri().getPath()).success();
         return mapRep;
     }
 
@@ -77,6 +90,7 @@ public class UserClientRoleMappingsResource {
         for (RoleModel roleModel : roles) {
             if (user.hasRole(roleModel)) mapRep.add(ModelToRepresentation.toRepresentation(roleModel));
         }
+        adminEvent.operation(OperationType.VIEW).resourcePath(session.getContext().getUri().getPath()).success();
         return mapRep;
     }
 
@@ -93,6 +107,7 @@ public class UserClientRoleMappingsResource {
         auth.requireView();
 
         Set<RoleModel> available = client.getRoles();
+        adminEvent.operation(OperationType.VIEW).resourcePath(session.getContext().getUri().getPath()).success();
         return getAvailableRoles(user, available);
     }
 
@@ -127,6 +142,7 @@ public class UserClientRoleMappingsResource {
             }
             user.grantRole(roleModel);
         }
+        adminEvent.operation(OperationType.ACTION).resourcePath(session.getContext().getUri().getPath()).representation(roles).success();
 
     }
 
@@ -159,5 +175,6 @@ public class UserClientRoleMappingsResource {
                 user.deleteRoleMapping(roleModel);
             }
         }
+        adminEvent.operation(OperationType.DELETE).resourcePath(session.getContext().getUri().getPath()).success();
     }
 }
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/UserFederationResource.java b/services/src/main/java/org/keycloak/services/resources/admin/UserFederationResource.java
index 188cb30..a1d6851 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/UserFederationResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/UserFederationResource.java
@@ -4,6 +4,8 @@ import org.jboss.logging.Logger;
 import org.jboss.resteasy.annotations.cache.NoCache;
 import org.jboss.resteasy.spi.NotFoundException;
 import org.keycloak.constants.KerberosConstants;
+import org.keycloak.events.AdminEventBuilder;
+import org.keycloak.events.admin.OperationType;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.RequiredCredentialModel;
@@ -31,6 +33,7 @@ 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.LinkedList;
 import java.util.List;
 
@@ -46,6 +49,8 @@ public class UserFederationResource {
     protected RealmModel realm;
 
     protected  RealmAuth auth;
+    
+    protected AdminEventBuilder adminEvent;
 
     @Context
     protected UriInfo uriInfo;
@@ -53,10 +58,11 @@ public class UserFederationResource {
     @Context
     protected KeycloakSession session;
 
-    public UserFederationResource(RealmModel realm, RealmAuth auth) {
+    public UserFederationResource(RealmModel realm, RealmAuth auth, AdminEventBuilder adminEvent) {
         this.auth = auth;
         this.realm = realm;
-
+        this.adminEvent = adminEvent;
+        
         auth.init(RealmAuth.Resource.USER);
     }
 
@@ -78,6 +84,7 @@ public class UserFederationResource {
             rep.setOptions(((UserFederationProviderFactory)factory).getConfigurationOptions());
             providers.add(rep);
         }
+        adminEvent.operation(OperationType.VIEW).resourcePath(uriInfo.getPath()).success();
         return providers;
     }
 
@@ -99,6 +106,9 @@ public class UserFederationResource {
             UserFederationProviderFactoryRepresentation rep = new UserFederationProviderFactoryRepresentation();
             rep.setId(factory.getId());
             rep.setOptions(((UserFederationProviderFactory)factory).getConfigurationOptions());
+
+            adminEvent.operation(OperationType.VIEW).resourcePath(uriInfo.getPath()).success();
+
             return rep;
         }
         throw new NotFoundException("Could not find provider");
@@ -123,6 +133,11 @@ public class UserFederationResource {
                 rep.getFullSyncPeriod(), rep.getChangedSyncPeriod(), rep.getLastSync());
         new UsersSyncManager().refreshPeriodicSyncForProvider(session.getKeycloakSessionFactory(), session.getProvider(TimerProvider.class), model, realm.getId());
         checkKerberosCredential(model);
+        
+        adminEvent.operation(OperationType.CREATE).resourcePath(uriInfo.getAbsolutePathBuilder()
+                .path(model.getId()).build().toString().substring(uriInfo.getBaseUri().toString().length()))
+                .representation(rep).success();
+
         return Response.created(uriInfo.getAbsolutePathBuilder().path(model.getId()).build()).build();
     }
 
@@ -146,6 +161,9 @@ public class UserFederationResource {
         realm.updateUserFederationProvider(model);
         new UsersSyncManager().refreshPeriodicSyncForProvider(session.getKeycloakSessionFactory(), session.getProvider(TimerProvider.class), model, realm.getId());
         checkKerberosCredential(model);
+        
+        adminEvent.operation(OperationType.UPDATE).resourcePath(uriInfo.getPath()).representation(rep).success();
+
     }
 
     /**
@@ -161,10 +179,10 @@ public class UserFederationResource {
         auth.requireView();
         for (UserFederationProviderModel model : realm.getUserFederationProviders()) {
             if (model.getId().equals(id)) {
+                adminEvent.operation(OperationType.VIEW).resourcePath(uriInfo.getPath()).success();
                 return ModelToRepresentation.toRepresentation(model);
             }
         }
-        
         throw new NotFoundException("could not find provider");
     }
 
@@ -182,6 +200,9 @@ public class UserFederationResource {
         UserFederationProviderModel model = new UserFederationProviderModel(id, null, null, -1, null, -1, -1, 0);
         realm.removeUserFederationProvider(model);
         new UsersSyncManager().removePeriodicSyncForProvider(session.getProvider(TimerProvider.class), model);
+        
+        adminEvent.operation(OperationType.DELETE).resourcePath(uriInfo.getPath()).success();
+
     }
 
 
@@ -201,6 +222,7 @@ public class UserFederationResource {
             UserFederationProviderRepresentation rep = ModelToRepresentation.toRepresentation(model);
             reps.add(rep);
         }
+        adminEvent.operation(OperationType.VIEW).resourcePath(uriInfo.getPath()).success();
         return reps;
     }
 
@@ -224,6 +246,7 @@ public class UserFederationResource {
                 } else if ("triggerChangedUsersSync".equals(action)) {
                     syncManager.syncChangedUsers(session.getKeycloakSessionFactory(), realm.getId(), model);
                 }
+                adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo.getPath()).success();
                 return Response.noContent().build();
             }
         }
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java b/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java
index ee9dc90..291387e 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java
@@ -7,6 +7,8 @@ import org.jboss.resteasy.spi.NotFoundException;
 import org.keycloak.ClientConnection;
 import org.keycloak.email.EmailException;
 import org.keycloak.email.EmailProvider;
+import org.keycloak.events.AdminEventBuilder;
+import org.keycloak.events.admin.OperationType;
 import org.keycloak.models.ClientModel;
 import org.keycloak.models.ClientSessionModel;
 import org.keycloak.models.Constants;
@@ -56,6 +58,7 @@ import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriBuilder;
 import javax.ws.rs.core.UriInfo;
+
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -78,6 +81,8 @@ public class UsersResource {
 
     private RealmAuth auth;
     
+    private AdminEventBuilder adminEvent;
+    
     @Context
     protected ClientConnection clientConnection;
 
@@ -90,9 +95,10 @@ public class UsersResource {
     @Context
     protected HttpHeaders headers;
 
-    public UsersResource(RealmModel realm, RealmAuth auth, TokenManager tokenManager) {
+    public UsersResource(RealmModel realm, RealmAuth auth, TokenManager tokenManager, AdminEventBuilder adminEvent) {
         this.auth = auth;
         this.realm = realm;
+        this.adminEvent = adminEvent;
 
         auth.init(RealmAuth.Resource.USER);
     }
@@ -116,11 +122,11 @@ public class UsersResource {
                 throw new NotFoundException("User not found");
             }
             updateUserFromRep(user, rep);
+            adminEvent.operation(OperationType.UPDATE).resourcePath(uriInfo.getPath()).representation(rep).success();
 
             if (session.getTransaction().isActive()) {
                 session.getTransaction().commit();
             }
-
             return Response.noContent().build();
         } catch (ModelDuplicateException e) {
             return ErrorResponse.exists("User exists with same username or email");
@@ -152,11 +158,15 @@ public class UsersResource {
         try {
             UserModel user = session.users().addUser(realm, rep.getUsername());
             updateUserFromRep(user, rep);
-
+            
+            adminEvent.operation(OperationType.CREATE).resourcePath(uriInfo.getAbsolutePathBuilder()
+                    .path(user.getUsername()).build().toString().substring(uriInfo.getBaseUri().toString().length()))
+                    .representation(rep).success();
+            
             if (session.getTransaction().isActive()) {
                 session.getTransaction().commit();
             }
-
+            
             return Response.created(uriInfo.getAbsolutePathBuilder().path(user.getUsername()).build()).build();
         } catch (ModelDuplicateException e) {
             if (session.getTransaction().isActive()) {
@@ -217,6 +227,8 @@ public class UsersResource {
         if (user == null) {
             throw new NotFoundException("User not found");
         }
+        
+        adminEvent.operation(OperationType.VIEW).resourcePath(uriInfo.getPath()).success();
 
         UserRepresentation rep = ModelToRepresentation.toRepresentation(user);
 
@@ -256,6 +268,7 @@ public class UsersResource {
             UserSessionRepresentation rep = ModelToRepresentation.toRepresentation(session);
             reps.add(rep);
         }
+        adminEvent.operation(OperationType.VIEW).resourcePath(uriInfo.getPath()).success();
         return reps;
     }
 
@@ -287,6 +300,7 @@ public class UsersResource {
                 }
             }
         }
+        adminEvent.operation(OperationType.VIEW).resourcePath(uriInfo.getPath()).success();
         return result;
     }
 
@@ -305,7 +319,7 @@ public class UsersResource {
 
         FederatedIdentityModel socialLink = new FederatedIdentityModel(provider, rep.getUserId(), rep.getUserName());
         session.users().addFederatedIdentity(realm, user, socialLink);
-
+        adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo.getPath()).representation(rep).success();
         return Response.noContent().build();
     }
 
@@ -321,6 +335,7 @@ public class UsersResource {
         if (!session.users().removeFederatedIdentity(realm, user, provider)) {
             throw new NotFoundException("Link not found");
         }
+        adminEvent.operation(OperationType.DELETE).resourcePath(uriInfo.getPath()).success();
     }
 
     /**
@@ -347,6 +362,7 @@ public class UsersResource {
             UserConsentRepresentation rep = ModelToRepresentation.toRepresentation(consent);
             result.add(rep);
         }
+        adminEvent.operation(OperationType.VIEW).resourcePath(uriInfo.getPath()).success();
         return result;
     }
 
@@ -374,6 +390,7 @@ public class UsersResource {
         } else {
             throw new NotFoundException("Consent not found for user " + username + " and client " + clientId);
         }
+        adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo.getPath()).success();
     }
 
     /**
@@ -395,6 +412,7 @@ public class UsersResource {
         for (UserSessionModel userSession : userSessions) {
             AuthenticationManager.backchannelLogout(session, realm, userSession, uriInfo, clientConnection, headers, true);
         }
+        adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo.getPath()).success();
     }
 
     /**
@@ -416,6 +434,7 @@ public class UsersResource {
 
         boolean removed = new UserManager(session).removeUser(realm, user);
         if (removed) {
+            adminEvent.operation(OperationType.DELETE).resourcePath(uriInfo.getPath()).success();
             return Response.noContent().build();
         } else {
             return ErrorResponse.error("User couldn't be deleted", Response.Status.BAD_REQUEST);
@@ -524,6 +543,7 @@ public class UsersResource {
                 }
             }
         }
+        adminEvent.operation(OperationType.VIEW).resourcePath(uriInfo.getPath()).success();
         return all;
     }
 
@@ -550,6 +570,7 @@ public class UsersResource {
         for (RoleModel roleModel : realmMappings) {
             realmMappingsRep.add(ModelToRepresentation.toRepresentation(roleModel));
         }
+        adminEvent.operation(OperationType.VIEW).resourcePath(uriInfo.getPath()).success();
         return realmMappingsRep;
     }
 
@@ -578,6 +599,7 @@ public class UsersResource {
                realmMappingsRep.add(ModelToRepresentation.toRepresentation(roleModel));
             }
         }
+        adminEvent.operation(OperationType.VIEW).resourcePath(uriInfo.getPath()).success();
         return realmMappingsRep;
     }
 
@@ -600,6 +622,7 @@ public class UsersResource {
         }
 
         Set<RoleModel> available = realm.getRoles();
+        adminEvent.operation(OperationType.VIEW).resourcePath(uriInfo.getPath()).success();
         return UserClientRoleMappingsResource.getAvailableRoles(user, available);
     }
 
@@ -628,7 +651,8 @@ public class UsersResource {
             }
             user.grantRole(roleModel);
         }
-
+        
+        adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo.getPath()).representation(roles).success();
 
     }
 
@@ -665,6 +689,8 @@ public class UsersResource {
                 user.deleteRoleMapping(roleModel);
             }
         }
+        
+        adminEvent.operation(OperationType.DELETE).resourcePath(uriInfo.getPath()).success();
     }
 
     @Path("{username}/role-mappings/clients/{clientId}")
@@ -679,8 +705,8 @@ public class UsersResource {
         if (client == null) {
             throw new NotFoundException("Client not found");
         }
-
-        return new UserClientRoleMappingsResource(realm, auth, user, client);
+        adminEvent.operation(OperationType.VIEW).resourcePath(uriInfo.getPath()).success();
+        return new UserClientRoleMappingsResource(realm, auth, user, client, adminEvent);
 
     }
     @Path("{username}/role-mappings/clients-by-id/{id}")
@@ -695,8 +721,9 @@ public class UsersResource {
         if (client == null) {
             throw new NotFoundException("Client not found");
         }
-
-        return new UserClientRoleMappingsResource(realm, auth, user, client);
+        
+        adminEvent.operation(OperationType.VIEW).resourcePath(uriInfo.getPath()).success();
+        return new UserClientRoleMappingsResource(realm, auth, user, client, adminEvent);
 
     }
     /**
@@ -729,6 +756,8 @@ public class UsersResource {
             throw new BadRequestException("Can't reset password as account is read only");
         }
         if (pass.isTemporary()) user.addRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD);
+        
+        adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo.getPath()).success();
     }
 
     /**
@@ -748,6 +777,7 @@ public class UsersResource {
         }
 
         user.setTotp(false);
+        adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo.getPath()).success();
     }
 
     /**
@@ -823,6 +853,9 @@ public class UsersResource {
             this.session.getProvider(EmailProvider.class).setRealm(realm).setUser(user).sendPasswordReset(link, expiration);
 
             //audit.user(user).detail(Details.EMAIL, user.getEmail()).detail(Details.CODE_ID, accessCode.getCodeId()).success();
+
+            adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo.getPath()).success();
+
             return Response.ok().build();
         } catch (EmailException e) {
             logger.error("Failed to send password reset email", e);
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/AssertEvents.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/AssertEvents.java
index 13c181b..ce80527 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/AssertEvents.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/AssertEvents.java
@@ -8,6 +8,7 @@ import org.junit.Assert;
 import org.junit.rules.TestRule;
 import org.junit.runners.model.Statement;
 import org.keycloak.Config;
+import org.keycloak.events.admin.AdminEvent;
 import org.keycloak.events.Details;
 import org.keycloak.events.Event;
 import org.keycloak.events.EventListenerProvider;
@@ -195,6 +196,12 @@ public class AssertEvents implements TestRule, EventListenerProviderFactory {
             @Override
             public void close() {
             }
+
+            @Override
+            public void onEvent(AdminEvent event, boolean includeRepresentation) {
+                // TODO Auto-generated method stub
+                
+            }
         };
     }
 
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/events/AdminEventStoreProviderTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/events/AdminEventStoreProviderTest.java
new file mode 100644
index 0000000..dacf7c5
--- /dev/null
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/events/AdminEventStoreProviderTest.java
@@ -0,0 +1,226 @@
+package org.keycloak.testsuite.events;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.keycloak.events.EventStoreProvider;
+import org.keycloak.events.admin.AdminEvent;
+import org.keycloak.events.admin.AuthDetails;
+import org.keycloak.events.admin.OperationType;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.testsuite.rule.KeycloakRule;
+
+/**
+ * @author <a href="mailto:giriraj.sharma27@gmail.com">Giriraj Sharma</a>
+ */
+public class AdminEventStoreProviderTest {
+
+    @ClassRule
+    public static KeycloakRule kc = new KeycloakRule();
+
+    private KeycloakSession session;
+
+    private EventStoreProvider eventStore;
+
+    @Before
+    public void before() {
+        session = kc.startSession();
+        eventStore = session.getProvider(EventStoreProvider.class);
+    }
+
+    @After
+    public void after() {
+        eventStore.clearAdmin();
+        kc.stopSession(session, true);
+    }
+
+    @Test
+    public void save() {
+        eventStore.onEvent(create(OperationType.VIEW, "realmId", "clientId", "userId", "127.0.0.1", "/admin/realms/master", "error"), false);
+    }
+
+    @Test
+    public void query() {
+        long oldest = System.currentTimeMillis() - 30000;
+        long newest = System.currentTimeMillis() + 30000;
+
+        eventStore.onEvent(create(OperationType.VIEW, "realmId", "clientId", "userId", "127.0.0.1", "/admin/realms/master", "error"), false);
+        eventStore.onEvent(create(newest, OperationType.ACTION, "realmId", "clientId", "userId", "127.0.0.1", "/admin/realms/master", "error"), false);
+        eventStore.onEvent(create(newest, OperationType.ACTION, "realmId", "clientId", "userId2", "127.0.0.1", "/admin/realms/master", "error"), false);
+        eventStore.onEvent(create(OperationType.VIEW, "realmId2", "clientId", "userId", "127.0.0.1", "/admin/realms/master", "error"), false);
+        eventStore.onEvent(create(oldest, OperationType.VIEW, "realmId", "clientId2", "userId", "127.0.0.1", "/admin/realms/master", "error"), false);
+        eventStore.onEvent(create(OperationType.VIEW, "realmId", "clientId", "userId2", "127.0.0.1", "/admin/realms/master", "error"), false);
+
+        resetSession();
+
+        Assert.assertEquals(5, eventStore.createAdminQuery().authClient("clientId").getResultList().size());
+        Assert.assertEquals(5, eventStore.createAdminQuery().authRealm("realmId").getResultList().size());
+        Assert.assertEquals(4, eventStore.createAdminQuery().operation(OperationType.VIEW).getResultList().size());
+        Assert.assertEquals(6, eventStore.createAdminQuery().operation(OperationType.VIEW, OperationType.ACTION).getResultList().size());
+        Assert.assertEquals(4, eventStore.createAdminQuery().authUser("userId").getResultList().size());
+
+        Assert.assertEquals(1, eventStore.createAdminQuery().authUser("userId").operation(OperationType.ACTION).getResultList().size());
+
+        Assert.assertEquals(2, eventStore.createAdminQuery().maxResults(2).getResultList().size());
+        Assert.assertEquals(1, eventStore.createAdminQuery().firstResult(5).getResultList().size());
+
+        Assert.assertEquals(newest, eventStore.createAdminQuery().maxResults(1).getResultList().get(0).getTime());
+        Assert.assertEquals(oldest, eventStore.createAdminQuery().firstResult(5).maxResults(1).getResultList().get(0).getTime());
+        
+        eventStore.clearAdmin("realmId");
+        eventStore.clearAdmin("realmId2");
+        
+        Assert.assertEquals(0, eventStore.createAdminQuery().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, OperationType.VIEW, "realmId", "clientId", "userId", "127.0.0.1", "/admin/realms/master", "error"), false);
+        eventStore.onEvent(create(date1, OperationType.VIEW, "realmId", "clientId", "userId", "127.0.0.1", "/admin/realms/master", "error"), false);
+        eventStore.onEvent(create(date2, OperationType.ACTION, "realmId", "clientId", "userId", "127.0.0.1", "/admin/realms/master", "error"), false);
+        eventStore.onEvent(create(date2, OperationType.ACTION, "realmId", "clientId", "userId", "127.0.0.1", "/admin/realms/master", "error"), false);
+        eventStore.onEvent(create(date3, OperationType.UPDATE, "realmId", "clientId", "userId2", "127.0.0.1", "/admin/realms/master", "error"), false);
+        eventStore.onEvent(create(date3, OperationType.DELETE, "realmId", "clientId", "userId2", "127.0.0.1", "/admin/realms/master", "error"), false);
+        eventStore.onEvent(create(date4, OperationType.CREATE, "realmId2", "clientId2", "userId2", "127.0.0.1", "/admin/realms/master", "error"), false);
+        eventStore.onEvent(create(date4, OperationType.CREATE, "realmId2", "clientId2", "userId2", "127.0.0.1", "/admin/realms/master", "error"), false);
+        
+        resetSession();
+        
+        Assert.assertEquals(6, eventStore.createAdminQuery().authClient("clientId").getResultList().size());
+        Assert.assertEquals(2, eventStore.createAdminQuery().authClient("clientId2").getResultList().size());
+        
+        Assert.assertEquals(6, eventStore.createAdminQuery().authRealm("realmId").getResultList().size());
+        Assert.assertEquals(2, eventStore.createAdminQuery().authRealm("realmId2").getResultList().size());
+        
+        Assert.assertEquals(4, eventStore.createAdminQuery().authUser("userId").getResultList().size());
+        Assert.assertEquals(4, eventStore.createAdminQuery().authUser("userId2").getResultList().size());
+        
+        Assert.assertEquals(2, eventStore.createAdminQuery().operation(OperationType.VIEW).getResultList().size());
+        Assert.assertEquals(2, eventStore.createAdminQuery().operation(OperationType.ACTION).getResultList().size());
+        Assert.assertEquals(4, eventStore.createAdminQuery().operation(OperationType.VIEW, OperationType.ACTION).getResultList().size());
+        Assert.assertEquals(1, eventStore.createAdminQuery().operation(OperationType.UPDATE).getResultList().size());
+        Assert.assertEquals(1, eventStore.createAdminQuery().operation(OperationType.DELETE).getResultList().size());
+        Assert.assertEquals(2, eventStore.createAdminQuery().operation(OperationType.CREATE).getResultList().size());
+        
+        Assert.assertEquals(8, eventStore.createAdminQuery().fromTime("2015-03-04").getResultList().size());
+        Assert.assertEquals(8, eventStore.createAdminQuery().toTime("2015-03-07").getResultList().size());
+        
+        Assert.assertEquals(4, eventStore.createAdminQuery().fromTime("2015-03-06").getResultList().size());
+        Assert.assertEquals(4, eventStore.createAdminQuery().toTime("2015-03-05").getResultList().size());
+        
+        Assert.assertEquals(0, eventStore.createAdminQuery().fromTime("2015-03-08").getResultList().size());
+        Assert.assertEquals(0, eventStore.createAdminQuery().toTime("2015-03-03").getResultList().size());
+        
+        Assert.assertEquals(8, eventStore.createAdminQuery().fromTime("2015-03-04").toTime("2015-03-07").getResultList().size());
+        Assert.assertEquals(6, eventStore.createAdminQuery().fromTime("2015-03-05").toTime("2015-03-07").getResultList().size());
+        Assert.assertEquals(4, eventStore.createAdminQuery().fromTime("2015-03-04").toTime("2015-03-05").getResultList().size());
+        Assert.assertEquals(4, eventStore.createAdminQuery().fromTime("2015-03-06").toTime("2015-03-07").getResultList().size());
+        
+        Assert.assertEquals(0, eventStore.createAdminQuery().fromTime("2015-03-01").toTime("2015-03-03").getResultList().size());
+        Assert.assertEquals(0, eventStore.createAdminQuery().fromTime("2015-03-08").toTime("2015-03-10").getResultList().size());
+        
+    }
+    
+    @Test
+    public void queryResourcePath() {
+        long oldest = System.currentTimeMillis() - 30000;
+        long newest = System.currentTimeMillis() + 30000;
+
+        eventStore.onEvent(create(OperationType.VIEW, "realmId", "clientId", "userId", "127.0.0.1", "/admin/realms/master", "error"), false);
+        eventStore.onEvent(create(newest, OperationType.ACTION, "realmId", "clientId", "userId", "127.0.0.1", "/admin/realms/master", "error"), false);
+        eventStore.onEvent(create(newest, OperationType.ACTION, "realmId", "clientId", "userId2", "127.0.0.1", "/admin/realms/master", "error"), false);
+        eventStore.onEvent(create(OperationType.VIEW, "realmId2", "clientId", "userId", "127.0.0.1", "/admin/realms/master", "error"), false);
+        eventStore.onEvent(create(oldest, OperationType.VIEW, "realmId", "clientId2", "userId", "127.0.0.1", "/admin/realms/master", "error"), false);
+        eventStore.onEvent(create(OperationType.VIEW, "realmId", "clientId", "userId2", "127.0.0.1", "/admin/realms/master", "error"), false);
+
+        resetSession();
+
+        Assert.assertEquals(6, eventStore.createAdminQuery().resourcePath("/admin").getResultList().size());
+        Assert.assertEquals(6, eventStore.createAdminQuery().resourcePath("/realms").getResultList().size());
+        Assert.assertEquals(6, eventStore.createAdminQuery().resourcePath("/master").getResultList().size());
+        Assert.assertEquals(6, eventStore.createAdminQuery().resourcePath("/admin/realms").getResultList().size());
+        Assert.assertEquals(6, eventStore.createAdminQuery().resourcePath("/realms/master").getResultList().size());
+        Assert.assertEquals(6, eventStore.createAdminQuery().resourcePath("/admin/realms/master").getResultList().size());
+    }
+    
+    @Test
+    public void clear() {
+        eventStore.onEvent(create(System.currentTimeMillis() - 30000, OperationType.VIEW, "realmId", "clientId", "userId", "127.0.0.1", "/admin/realms/master", "error"), false);
+        eventStore.onEvent(create(System.currentTimeMillis() - 20000, OperationType.VIEW, "realmId", "clientId", "userId", "127.0.0.1", "/admin/realms/master", "error"), false);
+        eventStore.onEvent(create(System.currentTimeMillis(), OperationType.VIEW, "realmId", "clientId", "userId", "127.0.0.1", "/admin/realms/master", "error"), false);
+        eventStore.onEvent(create(System.currentTimeMillis(), OperationType.VIEW, "realmId", "clientId", "userId", "127.0.0.1", "/admin/realms/master", "error"), false);
+        eventStore.onEvent(create(System.currentTimeMillis() - 30000, OperationType.VIEW, "realmId2", "clientId", "userId", "127.0.0.1", "/admin/realms/master", "error"), false);
+
+        resetSession();
+
+        eventStore.clearAdmin("realmId");
+
+        Assert.assertEquals(1, eventStore.createAdminQuery().getResultList().size());
+    }
+
+    @Test
+    public void clearOld() {
+        eventStore.onEvent(create(System.currentTimeMillis() - 30000, OperationType.VIEW, "realmId", "clientId", "userId", "127.0.0.1", "/admin/realms/master", "error"), false);
+        eventStore.onEvent(create(System.currentTimeMillis() - 20000, OperationType.VIEW, "realmId", "clientId", "userId", "127.0.0.1", "/admin/realms/master", "error"), false);
+        eventStore.onEvent(create(System.currentTimeMillis(), OperationType.VIEW, "realmId", "clientId", "userId", "127.0.0.1", "/admin/realms/master", "error"), false);
+        eventStore.onEvent(create(System.currentTimeMillis(), OperationType.VIEW, "realmId", "clientId", "userId", "127.0.0.1", "/admin/realms/master", "error"), false);
+        eventStore.onEvent(create(System.currentTimeMillis() - 30000, OperationType.VIEW, "realmId2", "clientId", "userId", "127.0.0.1", "/admin/realms/master", "error"), false);
+
+        resetSession();
+
+        eventStore.clearAdmin("realmId", System.currentTimeMillis() - 10000);
+
+        Assert.assertEquals(3, eventStore.createAdminQuery().getResultList().size());
+    }
+
+    private AdminEvent create(OperationType operation, String realmId, String clientId, String userId, String ipAddress, String resourcePath, String error) {
+        return create(System.currentTimeMillis(), operation, realmId, clientId, userId, ipAddress, resourcePath, error);
+    }
+    
+    private AdminEvent create(Date date, OperationType operation, String realmId, String clientId, String userId, String ipAddress, String resourcePath, String error) {
+        return create(date.getTime(), operation, realmId, clientId, userId, ipAddress, resourcePath, error);
+    }
+
+    private AdminEvent create(long time, OperationType operation, String realmId, String clientId, String userId, String ipAddress, String resourcePath, String error) {
+        AdminEvent e = new AdminEvent();
+        e.setTime(time);
+        e.setOperationType(operation);
+        AuthDetails authDetails = new AuthDetails();
+        authDetails.setRealmId(realmId);
+        authDetails.setClientId(clientId);
+        authDetails.setUserId(userId);
+        authDetails.setIpAddress(ipAddress);
+        e.setAuthDetails(authDetails);
+        e.setResourcePath(resourcePath);
+        e.setError(error);
+
+        return e;
+    }
+
+    private void resetSession() {
+        kc.stopSession(session, true);
+        session = kc.startSession();
+        eventStore = session.getProvider(EventStoreProvider.class);
+    }
+
+}