keycloak-memoizeit

Changes

Details

diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/js/app.js b/admin-ui/src/main/resources/META-INF/resources/admin/js/app.js
index f846459..4802f1f 100755
--- a/admin-ui/src/main/resources/META-INF/resources/admin/js/app.js
+++ b/admin-ui/src/main/resources/META-INF/resources/admin/js/app.js
@@ -141,6 +141,21 @@ module.config([ '$routeProvider', function($routeProvider) {
                     return RealmLoader();
                 }
             },
+            controller : 'RealmAuditEventsCtrl'
+        })
+        .when('/realms/:realm/audit-settings', {
+            templateUrl : 'partials/realm-audit-config.html',
+            resolve : {
+                realm : function(RealmLoader) {
+                    return RealmLoader();
+                },
+                serverInfo : function(ServerInfoLoader) {
+                    return ServerInfoLoader();
+                },
+                auditConfig : function(RealmAuditLoader) {
+                    return RealmAuditLoader();
+                }
+            },
             controller : 'RealmAuditCtrl'
         })
         .when('/realms/:realm/auth-settings', {
diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/js/controllers/realm.js b/admin-ui/src/main/resources/META-INF/resources/admin/js/controllers/realm.js
index b0642b7..dc54399 100755
--- a/admin-ui/src/main/resources/META-INF/resources/admin/js/controllers/realm.js
+++ b/admin-ui/src/main/resources/META-INF/resources/admin/js/controllers/realm.js
@@ -1022,7 +1022,65 @@ module.controller('RealmAuthSettingsDetailCtrl', function($scope, $routeParams, 
     };
 });
 
-module.controller('RealmAuditCtrl', function($scope, RealmAudit, realm) {
+module.controller('RealmAuditCtrl', function($scope, auditConfig, RealmAudit, RealmAuditEvents, realm, serverInfo, $location, Notifications, TimeUnit, Dialog) {
+    $scope.realm = realm;
+
+    $scope.auditConfig = auditConfig;
+
+    $scope.auditConfig.expirationUnit = TimeUnit.autoUnit(auditConfig.auditExpiration);
+    if ($scope.auditConfig.expirationUnit) {
+        $scope.auditConfig.expirationUnit = 'Hours';
+    }
+
+    $scope.auditConfig.auditExpiration = TimeUnit.toUnit(auditConfig.auditExpiration, $scope.auditConfig.expirationUnit);
+    $scope.$watch('auditConfig.expirationUnit', function(to, from) {
+        if ($scope.auditConfig.auditExpiration) {
+            $scope.auditConfig.auditExpiration = TimeUnit.convert($scope.auditConfig.auditExpiration, from, to);
+        }
+    });
+
+    $scope.auditListeners = serverInfo.auditListeners;
+
+    var oldCopy = angular.copy($scope.auditConfig);
+    $scope.changed = false;
+
+    $scope.$watch('auditConfig', function() {
+        if (!angular.equals($scope.auditConfig, oldCopy)) {
+            $scope.changed = true;
+        }
+    }, true);
+
+    $scope.save = function() {
+        $scope.changed = false;
+
+        var copy = angular.copy($scope.auditConfig)
+        delete copy['expirationUnit'];
+
+        copy.auditExpiration = TimeUnit.toSeconds($scope.auditConfig.auditExpiration, $scope.auditConfig.expirationUnit);
+
+        RealmAudit.update({
+            id : realm.realm
+        }, copy, function () {
+            $location.url("/realms/" + realm.realm + "/audit-settings");
+            Notifications.success("Your changes have been saved to the realm.");
+        });
+    };
+
+    $scope.reset = function() {
+        $scope.auditConfig = angular.copy(oldCopy);
+        $scope.changed = false;
+    };
+
+    $scope.clearAudit = function() {
+        Dialog.confirmDelete($scope.realm.realm, 'audit events', function() {
+            RealmAuditEvents.remove({ id : $scope.realm.realm }, function() {
+                Notifications.success("The audit events has been cleared.");
+            });
+        });
+    };
+});
+
+module.controller('RealmAuditEventsCtrl', function($scope, RealmAuditEvents, realm) {
     $scope.realm = realm;
     $scope.page = 0;
 
@@ -1038,8 +1096,7 @@ module.controller('RealmAuditCtrl', function($scope, RealmAudit, realm) {
                 delete $scope.query[i];
            }
         }
-        console.debug($scope.query.first);
-        $scope.events = RealmAudit.query($scope.query);
+        $scope.events = RealmAuditEvents.query($scope.query);
     }
 
     $scope.firstPage = function() {
diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/js/loaders.js b/admin-ui/src/main/resources/META-INF/resources/admin/js/loaders.js
index 84f8dbc..e1e6a78 100755
--- a/admin-ui/src/main/resources/META-INF/resources/admin/js/loaders.js
+++ b/admin-ui/src/main/resources/META-INF/resources/admin/js/loaders.js
@@ -47,6 +47,14 @@ module.factory('RealmLoader', function(Loader, Realm, $route, $q) {
 	});
 });
 
+module.factory('RealmAuditLoader', function(Loader, RealmAudit, $route, $q) {
+    return Loader.get(RealmAudit, function() {
+        return {
+            id : $route.current.params.realm
+        }
+    });
+});
+
 module.factory('UserListLoader', function(Loader, User, $route, $q) {
     return Loader.query(User, function() {
         return {
diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/js/services.js b/admin-ui/src/main/resources/META-INF/resources/admin/js/services.js
index eabc83a..81af19b 100755
--- a/admin-ui/src/main/resources/META-INF/resources/admin/js/services.js
+++ b/admin-ui/src/main/resources/META-INF/resources/admin/js/services.js
@@ -145,6 +145,16 @@ module.factory('Realm', function($resource) {
 module.factory('RealmAudit', function($resource) {
     return $resource(authUrl + '/rest/admin/realms/:id/audit', {
         id : '@realm'
+    }, {
+        update : {
+            method : 'PUT'
+        }
+    });
+});
+
+module.factory('RealmAuditEvents', function($resource) {
+    return $resource(authUrl + '/rest/admin/realms/:id/audit/events', {
+        id : '@realm'
     });
 });
 
diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/partials/realm-audit.html b/admin-ui/src/main/resources/META-INF/resources/admin/partials/realm-audit.html
index f31ab36..0154320 100755
--- a/admin-ui/src/main/resources/META-INF/resources/admin/partials/realm-audit.html
+++ b/admin-ui/src/main/resources/META-INF/resources/admin/partials/realm-audit.html
@@ -1,6 +1,11 @@
 <div class="bs-sidebar col-sm-3 " data-ng-include data-src="'partials/realm-menu.html'"></div>
 <div id="content-area" class="col-sm-9" role="main">
-    <data-kc-navigation data-kc-current="social" data-kc-realm="realm.realm" data-kc-social="realm.social"></data-kc-navigation>
+
+    <ul class="nav nav-tabs nav-tabs-pf">
+        <li data-ng-class="(path[2] == 'audit') && 'active'"><a href="#/realms/{{realm.realm}}/audit">View</a></li>
+        <li data-ng-class="(path[2] == 'audit-settings') && 'active'"><a href="#/realms/{{realm.realm}}/audit-settings">Config</a></li>
+    </ul>
+
     <div id="content">
         <ol class="breadcrumb">
             <li><a href="#/realms/{{realm.realm}}">{{realm.realm}}</a></li>
diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/partials/realm-audit-config.html b/admin-ui/src/main/resources/META-INF/resources/admin/partials/realm-audit-config.html
new file mode 100755
index 0000000..befe2dd
--- /dev/null
+++ b/admin-ui/src/main/resources/META-INF/resources/admin/partials/realm-audit-config.html
@@ -0,0 +1,71 @@
+<div class="bs-sidebar col-sm-3 " data-ng-include data-src="'partials/realm-menu.html'"></div>
+<div id="content-area" class="col-sm-9" role="main">
+
+    <ul class="nav nav-tabs nav-tabs-pf">
+        <li data-ng-class="(path[2] == 'audit') && 'active'"><a href="#/realms/{{realm.realm}}/audit">View</a></li>
+        <li data-ng-class="(path[2] == 'audit-settings') && 'active'"><a href="#/realms/{{realm.realm}}/audit-settings">Config</a></li>
+    </ul>
+
+    <div id="content">
+        <ol class="breadcrumb">
+            <li><a href="#/realms/{{realm.realm}}">{{realm.realm}}</a></li>
+            <li><a href="#/realms/{{realm.realm}}">Audit</a></li>
+            <li class="active">Social</li>
+        </ol>
+        <h2><span>{{realm.realm}}</span> Audit Config</h2>
+
+        <form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageAudit">
+            <fieldset>
+                <div class="form-group" data-ng-show="access.manageAudit">
+                    <label class="col-sm-2 control-label" for="password">Clear Audit</label>
+                    <div class="col-sm-4">
+                        <button class="btn btn-danger" type="submit" data-ng-click="clearAudit()" >Clear Audit</button>
+                    </div>
+                </div>
+            </fieldset>
+            <fieldset>
+                <div class="form-group">
+                    <label class="col-sm-2 control-label" for="enabled">Enabled</label>
+                    <div class="col-sm-4">
+                        <input ng-model="auditConfig.auditEnabled" name="enabled" id="enabled" onoffswitch />
+                    </div>
+                </div>
+
+                <div class="form-group input-select">
+                    <label class="col-sm-2 control-label" for="expiration">Expiration</label>
+                    <div class="col-sm-10">
+                        <div class="row">
+                            <div class="col-sm-2">
+                                <input class="form-control" type="number"
+                                       data-ng-model="auditConfig.auditExpiration"
+                                       id="expiration" name="expiration"/>
+                            </div>
+                            <div class="col-sm-2 select-kc">
+                                <select name="expirationUnit" data-ng-model="auditConfig.expirationUnit" >
+                                    <option>Hours</option>
+                                    <option>Days</option>
+                                </select>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+
+                <div class="form-group">
+                    <label class="col-sm-2 control-label" for="auditListeners" class="control-label">Audit Listeners</label>
+
+                    <div class="col-sm-4">
+                        <select ui-select2 ng-model="auditConfig.auditListeners" data-placeholder="Select an action..." multiple>
+                            <option ng-repeat="listener in auditListeners" value="{{listener}}">{{listener}}</option>
+                        </select>
+                    </div>
+                </div>
+            </fieldset>
+
+            <div class="pull-right form-actions" data-ng-show="access.manageAudit">
+                <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>
+</div>
diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/partials/realm-menu.html b/admin-ui/src/main/resources/META-INF/resources/admin/partials/realm-menu.html
index 5d929c4..6deefac 100755
--- a/admin-ui/src/main/resources/META-INF/resources/admin/partials/realm-menu.html
+++ b/admin-ui/src/main/resources/META-INF/resources/admin/partials/realm-menu.html
@@ -9,5 +9,5 @@
     <li data-ng-show="access.viewApplications" data-ng-class="(path[2] == 'applications' || path[1] == 'application' || path[3] == 'applications') && 'active'"><a href="#/realms/{{realm.realm}}/applications">Applications</a></li>
     <li data-ng-show="access.viewClients" data-ng-class="(path[2] == 'oauth-clients' || path[1] == 'oauth-client') && 'active'"><a href="#/realms/{{realm.realm}}/oauth-clients">OAuth Clients</a></li>
     <li data-ng-show="access.viewRealm" data-ng-class="(path[2] == 'sessions') && 'active'"><a href="#/realms/{{realm.realm}}/sessions/revocation">Sessions</a></li>
-    <li data-ng-show="access.viewAudit" data-ng-class="(path[2] == 'audit') && 'active'"><a href="#/realms/{{realm.realm}}/audit">Audit</a></li>
+    <li data-ng-show="access.viewAudit" data-ng-class="(path[2] == 'audit' || path[2] == 'audit-settings') && 'active'"><a href="#/realms/{{realm.realm}}/audit">Audit</a></li>
 </ul>
\ No newline at end of file
diff --git a/audit/api/src/main/java/org/keycloak/audit/AuditProvider.java b/audit/api/src/main/java/org/keycloak/audit/AuditProvider.java
index 738b79f..9b43c72 100644
--- a/audit/api/src/main/java/org/keycloak/audit/AuditProvider.java
+++ b/audit/api/src/main/java/org/keycloak/audit/AuditProvider.java
@@ -7,8 +7,8 @@ public interface AuditProvider extends AuditListener {
 
     public EventQuery createQuery();
 
-    public void clear();
+    public void clear(String realmId);
 
-    public void clear(long olderThan);
+    public void clear(String realmId, long olderThan);
 
 }
diff --git a/audit/jpa/src/main/java/org/keycloak/audit/jpa/JpaAuditProvider.java b/audit/jpa/src/main/java/org/keycloak/audit/jpa/JpaAuditProvider.java
index 0432f17..c54c1b8 100644
--- a/audit/jpa/src/main/java/org/keycloak/audit/jpa/JpaAuditProvider.java
+++ b/audit/jpa/src/main/java/org/keycloak/audit/jpa/JpaAuditProvider.java
@@ -29,15 +29,15 @@ public class JpaAuditProvider implements AuditProvider {
     }
 
     @Override
-    public void clear() {
+    public void clear(String realmId) {
         beginTx();
-        em.createQuery("delete from EventEntity").executeUpdate();
+        em.createQuery("delete from EventEntity where realmId = :realmId").setParameter("realmId", realmId).executeUpdate();
     }
 
     @Override
-    public void clear(long olderThan) {
+    public void clear(String realmId, long olderThan) {
         beginTx();
-        em.createQuery("delete from EventEntity where time < :time").setParameter("time", olderThan).executeUpdate();
+        em.createQuery("delete from EventEntity where realmId = :realmId and time < :time").setParameter("realmId", realmId).setParameter("time", olderThan).executeUpdate();
     }
 
     @Override
diff --git a/audit/mongo/src/main/java/org/keycloak/audit/mongo/MongoAuditProvider.java b/audit/mongo/src/main/java/org/keycloak/audit/mongo/MongoAuditProvider.java
index 2aa1176..3d44a14 100644
--- a/audit/mongo/src/main/java/org/keycloak/audit/mongo/MongoAuditProvider.java
+++ b/audit/mongo/src/main/java/org/keycloak/audit/mongo/MongoAuditProvider.java
@@ -1,5 +1,6 @@
 package org.keycloak.audit.mongo;
 
+import com.mongodb.BasicDBList;
 import com.mongodb.BasicDBObject;
 import com.mongodb.DBCollection;
 import com.mongodb.DBObject;
@@ -27,13 +28,16 @@ public class MongoAuditProvider implements AuditProvider {
     }
 
     @Override
-    public void clear() {
-        audit.remove(new BasicDBObject());
+    public void clear(String realmId) {
+        audit.remove(new BasicDBObject("realmId", realmId));
     }
 
     @Override
-    public void clear(long olderThan) {
-        audit.remove(new BasicDBObject("time", new BasicDBObject("$lt", olderThan)));
+    public void clear(String realmId, long olderThan) {
+        BasicDBObject q = new BasicDBObject();
+        q.put("realmId", realmId);
+        q.put("time", new BasicDBObject("$lt", olderThan));
+        audit.remove(q);
     }
 
     @Override
diff --git a/audit/tests/src/main/java/org/keycloak/audit/tests/AbstractAuditProviderTest.java b/audit/tests/src/main/java/org/keycloak/audit/tests/AbstractAuditProviderTest.java
index c3ebdaa..7dada9f 100644
--- a/audit/tests/src/main/java/org/keycloak/audit/tests/AbstractAuditProviderTest.java
+++ b/audit/tests/src/main/java/org/keycloak/audit/tests/AbstractAuditProviderTest.java
@@ -34,7 +34,8 @@ public abstract class AbstractAuditProviderTest {
 
     @After
     public void after() {
-        provider.clear();
+        provider.clear("realmId");
+        provider.clear("realmId2");
         provider.close();
         factory.close();
     }
@@ -51,6 +52,7 @@ public abstract class AbstractAuditProviderTest {
 
         provider.onEvent(create("event", "realmId", "clientId", "userId", "127.0.0.1", "error"));
         provider.onEvent(create(newest, "event2", "realmId", "clientId", "userId", "127.0.0.1", "error"));
+        provider.onEvent(create(newest, "event2", "realmId", "clientId", "userId2", "127.0.0.1", "error"));
         provider.onEvent(create("event", "realmId2", "clientId", "userId", "127.0.0.1", "error"));
         provider.onEvent(create(oldest, "event", "realmId", "clientId2", "userId", "127.0.0.1", "error"));
         provider.onEvent(create("event", "realmId", "clientId", "userId2", "127.0.0.1", "error"));
@@ -58,19 +60,19 @@ public abstract class AbstractAuditProviderTest {
         provider.close();
         provider = factory.create();
 
-        Assert.assertEquals(4, provider.createQuery().client("clientId").getResultList().size());
-        Assert.assertEquals(4, provider.createQuery().realm("realmId").getResultList().size());
+        Assert.assertEquals(5, provider.createQuery().client("clientId").getResultList().size());
+        Assert.assertEquals(5, provider.createQuery().realm("realmId").getResultList().size());
         Assert.assertEquals(4, provider.createQuery().event("event").getResultList().size());
-        Assert.assertEquals(5, provider.createQuery().event("event", "event2").getResultList().size());
+        Assert.assertEquals(6, provider.createQuery().event("event", "event2").getResultList().size());
         Assert.assertEquals(4, provider.createQuery().user("userId").getResultList().size());
 
         Assert.assertEquals(1, provider.createQuery().user("userId").event("event2").getResultList().size());
 
         Assert.assertEquals(2, provider.createQuery().maxResults(2).getResultList().size());
-        Assert.assertEquals(1, provider.createQuery().firstResult(4).getResultList().size());
+        Assert.assertEquals(1, provider.createQuery().firstResult(5).getResultList().size());
 
         Assert.assertEquals(newest, provider.createQuery().maxResults(1).getResultList().get(0).getTime());
-        Assert.assertEquals(oldest, provider.createQuery().firstResult(4).maxResults(1).getResultList().get(0).getTime());
+        Assert.assertEquals(oldest, provider.createQuery().firstResult(5).maxResults(1).getResultList().get(0).getTime());
     }
 
     @Test
@@ -79,13 +81,14 @@ public abstract class AbstractAuditProviderTest {
         provider.onEvent(create(System.currentTimeMillis() - 20000, "event", "realmId", "clientId", "userId", "127.0.0.1", "error"));
         provider.onEvent(create(System.currentTimeMillis(), "event", "realmId", "clientId", "userId", "127.0.0.1", "error"));
         provider.onEvent(create(System.currentTimeMillis(), "event", "realmId", "clientId", "userId", "127.0.0.1", "error"));
+        provider.onEvent(create(System.currentTimeMillis() - 30000, "event", "realmId2", "clientId", "userId", "127.0.0.1", "error"));
 
         provider.close();
         provider = factory.create();
 
-        provider.clear();
+        provider.clear("realmId");
 
-        Assert.assertEquals(0, provider.createQuery().getResultList().size());
+        Assert.assertEquals(1, provider.createQuery().getResultList().size());
     }
 
     @Test
@@ -94,13 +97,14 @@ public abstract class AbstractAuditProviderTest {
         provider.onEvent(create(System.currentTimeMillis() - 20000, "event", "realmId", "clientId", "userId", "127.0.0.1", "error"));
         provider.onEvent(create(System.currentTimeMillis(), "event", "realmId", "clientId", "userId", "127.0.0.1", "error"));
         provider.onEvent(create(System.currentTimeMillis(), "event", "realmId", "clientId", "userId", "127.0.0.1", "error"));
+        provider.onEvent(create(System.currentTimeMillis() - 30000, "event", "realmId2", "clientId", "userId", "127.0.0.1", "error"));
 
         provider.close();
         provider = factory.create();
 
-        provider.clear(System.currentTimeMillis() - 10000);
+        provider.clear("realmId", System.currentTimeMillis() - 10000);
 
-        Assert.assertEquals(2, provider.createQuery().getResultList().size());
+        Assert.assertEquals(3, provider.createQuery().getResultList().size());
     }
 
     private Event create(String event, String realmId, String clientId, String userId, String ipAddress, String error) {
diff --git a/core/src/main/java/org/keycloak/representations/idm/RealmAuditRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/RealmAuditRepresentation.java
new file mode 100755
index 0000000..9a4d019
--- /dev/null
+++ b/core/src/main/java/org/keycloak/representations/idm/RealmAuditRepresentation.java
@@ -0,0 +1,37 @@
+package org.keycloak.representations.idm;
+
+import java.util.List;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class RealmAuditRepresentation {
+    protected boolean auditEnabled;
+    protected Long auditExpiration;
+    protected List<String> auditListeners;
+
+    public boolean isAuditEnabled() {
+        return auditEnabled;
+    }
+
+    public void setAuditEnabled(boolean auditEnabled) {
+        this.auditEnabled = auditEnabled;
+    }
+
+    public Long getAuditExpiration() {
+        return auditExpiration;
+    }
+
+    public void setAuditExpiration(Long auditExpiration) {
+        this.auditExpiration = auditExpiration;
+    }
+
+    public List<String> getAuditListeners() {
+        return auditListeners;
+    }
+
+    public void setAuditListeners(List<String> auditListeners) {
+        this.auditListeners = auditListeners;
+    }
+}
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 a4278d2..ac57c7e 100755
--- a/core/src/main/java/org/keycloak/representations/idm/RealmRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/RealmRepresentation.java
@@ -1,6 +1,7 @@
 package org.keycloak.representations.idm;
 
 import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -47,6 +48,9 @@ public class RealmRepresentation {
     protected List<AuthenticationProviderRepresentation> authenticationProviders;
     protected String loginTheme;
     protected String accountTheme;
+    protected boolean auditEnabled;
+    protected long auditExpiration;
+    protected List<String> auditListeners;
 
     public String getSelf() {
         return self;
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 db271f2..6e20605 100755
--- a/model/api/src/main/java/org/keycloak/models/RealmModel.java
+++ b/model/api/src/main/java/org/keycloak/models/RealmModel.java
@@ -208,6 +208,14 @@ public interface RealmModel extends RoleContainerModel, RoleMapperModel, ScopeMa
 
     boolean removeRoleById(String id);
 
+    boolean isAuditEnabled();
+
+    void setAuditEnabled(boolean enabled);
+
+    long getAuditExpiration();
+
+    void setAuditExpiration(long expiration);
+
     Set<String> getAuditListeners();
 
     void setAuditListeners(Set<String> listeners);
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 1e3a9d7..6885a70 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
@@ -99,8 +99,11 @@ public class RealmEntity {
     @JoinTable(name="RealmDefaultRoles")
     Collection<RoleEntity> defaultRoles = new ArrayList<RoleEntity>();
 
+    private boolean auditEnabled;
+    private long auditExpiration;
+
     @ElementCollection
-    protected Set<String> auditListeners= new HashSet<String>();
+    private Set<String> auditListeners= new HashSet<String>();
 
     public String getId() {
         return id;
@@ -349,6 +352,22 @@ public class RealmEntity {
         this.bruteForceProtected = bruteForceProtected;
     }
 
+    public boolean isAuditEnabled() {
+        return auditEnabled;
+    }
+
+    public void setAuditEnabled(boolean auditEnabled) {
+        this.auditEnabled = auditEnabled;
+    }
+
+    public long getAuditExpiration() {
+        return auditExpiration;
+    }
+
+    public void setAuditExpiration(long auditExpiration) {
+        this.auditExpiration = auditExpiration;
+    }
+
     public Set<String> getAuditListeners() {
         return auditListeners;
     }
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 0275f40..fce9a3d 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
@@ -1185,6 +1185,28 @@ public class RealmAdapter implements RealmModel {
     }
 
     @Override
+    public boolean isAuditEnabled() {
+        return realm.isAuditEnabled();
+    }
+
+    @Override
+    public void setAuditEnabled(boolean enabled) {
+        realm.setAuditEnabled(enabled);
+        em.flush();
+    }
+
+    @Override
+    public long getAuditExpiration() {
+        return realm.getAuditExpiration();
+    }
+
+    @Override
+    public void setAuditExpiration(long expiration) {
+        realm.setAuditExpiration(expiration);
+        em.flush();
+    }
+
+    @Override
     public Set<String> getAuditListeners() {
         return realm.getAuditListeners();
     }
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 10a748f..3fc43f2 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
@@ -1142,6 +1142,28 @@ public class RealmAdapter extends AbstractMongoAdapter<RealmEntity> implements R
     }
 
     @Override
+    public boolean isAuditEnabled() {
+        return realm.isAuditEnabled();
+    }
+
+    @Override
+    public void setAuditEnabled(boolean enabled) {
+        realm.setAuditEnabled(enabled);
+        updateRealm();
+    }
+
+    @Override
+    public long getAuditExpiration() {
+        return realm.getAuditExpiration();
+    }
+
+    @Override
+    public void setAuditExpiration(long expiration) {
+        realm.setAuditExpiration(expiration);
+        updateRealm();
+    }
+
+    @Override
     public Set<String> getAuditListeners() {
         return new HashSet<String>(realm.getAuditListeners());
     }
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/RealmEntity.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/RealmEntity.java
index 627af22..6786c90 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/RealmEntity.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/RealmEntity.java
@@ -57,6 +57,8 @@ public class RealmEntity extends AbstractMongoIdentifiableEntity implements Mong
     private Map<String, String> socialConfig = new HashMap<String, String>();
     private Map<String, String> ldapServerConfig;
 
+    private boolean auditEnabled;
+    private long auditExpiration;
     private List<String> auditListeners = new ArrayList<String>();
 
     @MongoField
@@ -303,6 +305,24 @@ public class RealmEntity extends AbstractMongoIdentifiableEntity implements Mong
     }
 
     @MongoField
+    public boolean isAuditEnabled() {
+        return auditEnabled;
+    }
+
+    public void setAuditEnabled(boolean auditEnabled) {
+        this.auditEnabled = auditEnabled;
+    }
+
+    @MongoField
+    public long getAuditExpiration() {
+        return auditExpiration;
+    }
+
+    public void setAuditExpiration(long auditExpiration) {
+        this.auditExpiration = auditExpiration;
+    }
+
+    @MongoField
     public List<String> getAuditListeners() {
         return auditListeners;
     }
diff --git a/services/src/main/java/org/keycloak/services/managers/AuditManager.java b/services/src/main/java/org/keycloak/services/managers/AuditManager.java
new file mode 100644
index 0000000..ae90e1c
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/managers/AuditManager.java
@@ -0,0 +1,57 @@
+package org.keycloak.services.managers;
+
+import org.jboss.logging.Logger;
+import org.keycloak.audit.Audit;
+import org.keycloak.audit.AuditListener;
+import org.keycloak.audit.AuditProvider;
+import org.keycloak.models.RealmModel;
+import org.keycloak.services.ClientConnection;
+import org.keycloak.services.ProviderSession;
+
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class AuditManager {
+
+    private Logger log = Logger.getLogger(AuditManager.class);
+
+    private RealmModel realm;
+    private ProviderSession providers;
+    private ClientConnection clientConnection;
+
+    public AuditManager(RealmModel realm, ProviderSession providers, ClientConnection clientConnection) {
+        this.realm = realm;
+        this.providers = providers;
+        this.clientConnection = clientConnection;
+    }
+
+    public Audit createAudit() {
+        List<AuditListener> listeners = new LinkedList<AuditListener>();
+
+        if (realm.isAuditEnabled()) {
+            AuditProvider auditProvider = providers.getProvider(AuditProvider.class);
+            if (auditProvider != null) {
+                listeners.add(auditProvider);
+            } else {
+                log.error("Audit enabled, but no audit provider configured");
+            }
+        }
+
+        if (realm.getAuditListeners() != null) {
+            for (String id : realm.getAuditListeners()) {
+                AuditListener listener = providers.getProvider(AuditListener.class, id);
+                if (listener != null) {
+                    listeners.add(listener);
+                } else {
+                    log.error("Audit listener '" + id + "' registered, but not found");
+                }
+            }
+        }
+
+        return new Audit(listeners, realm, clientConnection.getRemoteAddr());
+    }
+
+}
diff --git a/services/src/main/java/org/keycloak/services/managers/ModelToRepresentation.java b/services/src/main/java/org/keycloak/services/managers/ModelToRepresentation.java
index 5f43390..8562bd8 100755
--- a/services/src/main/java/org/keycloak/services/managers/ModelToRepresentation.java
+++ b/services/src/main/java/org/keycloak/services/managers/ModelToRepresentation.java
@@ -13,6 +13,7 @@ import org.keycloak.models.UserModel;
 import org.keycloak.representations.idm.AuthenticationProviderRepresentation;
 import org.keycloak.representations.idm.ClaimRepresentation;
 import org.keycloak.representations.idm.CredentialRepresentation;
+import org.keycloak.representations.idm.RealmAuditRepresentation;
 import org.keycloak.representations.idm.RealmRepresentation;
 import org.keycloak.representations.idm.RoleRepresentation;
 import org.keycloak.representations.idm.UserRepresentation;
@@ -20,6 +21,7 @@ import org.keycloak.representations.idm.UserRepresentation;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 
@@ -124,6 +126,20 @@ public class ModelToRepresentation {
         return rep;
     }
 
+    public static RealmAuditRepresentation toAuditReprensetation(RealmModel realm) {
+        RealmAuditRepresentation rep = new RealmAuditRepresentation();
+        rep.setAuditEnabled(realm.isAuditEnabled());
+
+        if (realm.getAuditExpiration() != 0) {
+            rep.setAuditExpiration(realm.getAuditExpiration());
+        }
+
+        if (realm.getAuditListeners() != null) {
+            rep.setAuditListeners(new LinkedList<String>(realm.getAuditListeners()));
+        }
+        return rep;
+    }
+
     public static CredentialRepresentation toRepresentation(UserCredentialModel cred) {
         CredentialRepresentation rep = new CredentialRepresentation();
         rep.setType(CredentialRepresentation.SECRET);
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 d9d40d2..bed8867 100755
--- a/services/src/main/java/org/keycloak/services/managers/RealmManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/RealmManager.java
@@ -24,6 +24,7 @@ import org.keycloak.representations.idm.AuthenticationLinkRepresentation;
 import org.keycloak.representations.idm.AuthenticationProviderRepresentation;
 import org.keycloak.representations.idm.CredentialRepresentation;
 import org.keycloak.representations.idm.OAuthClientRepresentation;
+import org.keycloak.representations.idm.RealmAuditRepresentation;
 import org.keycloak.representations.idm.RealmRepresentation;
 import org.keycloak.representations.idm.RoleRepresentation;
 import org.keycloak.representations.idm.ScopeMappingRepresentation;
@@ -39,6 +40,7 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 
@@ -166,6 +168,14 @@ public class RealmManager {
         }
     }
 
+    public void updateRealmAudit(RealmAuditRepresentation rep, RealmModel realm) {
+        realm.setAuditEnabled(rep.isAuditEnabled());
+        realm.setAuditExpiration(rep.getAuditExpiration());
+        if (rep.getAuditListeners() != null) {
+            realm.setAuditListeners(new HashSet<String>(rep.getAuditListeners()));
+        }
+    }
+
     private void setupAdminManagement(RealmModel realm) {
         RealmModel adminRealm;
         RoleModel adminRole;
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 4194e61..76fa388 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
@@ -9,6 +9,7 @@ import org.keycloak.models.ApplicationModel;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
 import org.keycloak.representations.adapters.action.SessionStats;
+import org.keycloak.representations.idm.RealmAuditRepresentation;
 import org.keycloak.representations.idm.RealmRepresentation;
 import org.keycloak.services.ProviderSession;
 import org.keycloak.services.managers.ModelToRepresentation;
@@ -148,7 +149,26 @@ public class RealmAdminResource {
         return stats;
     }
 
+    @GET
+    @Path("audit")
+    @Produces("application/json")
+    public RealmAuditRepresentation getRealmAudit() {
+        auth.init(RealmAuth.Resource.AUDIT).requireView();
+
+        return ModelToRepresentation.toAuditReprensetation(realm);
+    }
+
+    @PUT
     @Path("audit")
+    @Consumes("application/json")
+    public void updateRealmAudit(final RealmAuditRepresentation rep) {
+        auth.init(RealmAuth.Resource.AUDIT).requireManage();
+
+        logger.debug("updating realm audit: " + realm.getName());
+        new RealmManager(session).updateRealmAudit(rep, realm);
+    }
+
+    @Path("audit/events")
     @GET
     @NoCache
     @Produces(MediaType.APPLICATION_JSON)
@@ -181,4 +201,12 @@ public class RealmAdminResource {
         return query.getResultList();
     }
 
+    @Path("audit/events")
+    @DELETE
+    public void clearAudit() {
+        auth.init(RealmAuth.Resource.AUDIT).requireManage();
+
+        AuditProvider audit = providers.getProvider(AuditProvider.class);
+        audit.clear(realm.getId());
+    }
 }
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 46449f3..d0ffbfa 100644
--- a/services/src/main/java/org/keycloak/services/resources/admin/ServerInfoAdminResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/ServerInfoAdminResource.java
@@ -1,30 +1,38 @@
 package org.keycloak.services.resources.admin;
 
+import org.keycloak.audit.AuditListener;
 import org.keycloak.freemarker.Theme;
 import org.keycloak.freemarker.ThemeProvider;
+import org.keycloak.services.ProviderSession;
 import org.keycloak.social.SocialProvider;
 import org.keycloak.spi.authentication.AuthenticationProvider;
 import org.keycloak.spi.authentication.AuthenticationProviderManager;
 import org.keycloak.util.ProviderLoader;
 
 import javax.ws.rs.GET;
+import javax.ws.rs.core.Context;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 /**
  * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
  */
 public class ServerInfoAdminResource {
 
+    @Context
+    private ProviderSession providers;
+
     @GET
     public ServerInfoRepresentation getInfo() {
         ServerInfoRepresentation info = new ServerInfoRepresentation();
         setSocialProviders(info);
         setThemes(info);
         setAuthProviders(info);
+        setAuditListeners(info);
         return info;
     }
 
@@ -57,6 +65,15 @@ public class ServerInfoAdminResource {
         }
     }
 
+    private void setAuditListeners(ServerInfoRepresentation info) {
+        info.auditListeners = new LinkedList<String>();
+
+        Set<String> providers = this.providers.listProviderIds(AuditListener.class);
+        if (providers != null) {
+            info.auditListeners.addAll(providers);
+        }
+    }
+
     public static class ServerInfoRepresentation {
 
         private Map<String, List<String>> themes;
@@ -65,6 +82,8 @@ public class ServerInfoAdminResource {
 
         private Map<String, List<String>> authProviders;
 
+        private List<String> auditListeners;
+
         public ServerInfoRepresentation() {
         }
 
@@ -79,6 +98,10 @@ public class ServerInfoAdminResource {
         public Map<String, List<String>> getAuthProviders() {
             return authProviders;
         }
+
+        public List<String> getAuditListeners() {
+            return auditListeners;
+        }
     }
 
 }
diff --git a/services/src/main/java/org/keycloak/services/resources/RealmsResource.java b/services/src/main/java/org/keycloak/services/resources/RealmsResource.java
index cb11ed5..1056378 100755
--- a/services/src/main/java/org/keycloak/services/resources/RealmsResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/RealmsResource.java
@@ -10,6 +10,7 @@ import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
 import org.keycloak.services.ClientConnection;
 import org.keycloak.services.ProviderSession;
+import org.keycloak.services.managers.AuditManager;
 import org.keycloak.services.managers.RealmManager;
 import org.keycloak.services.managers.SocialRequestManager;
 import org.keycloak.services.managers.TokenManager;
@@ -68,7 +69,7 @@ public class RealmsResource {
     public TokenService getTokenService(final @PathParam("realm") String name) {
         RealmManager realmManager = new RealmManager(session);
         RealmModel realm = locateRealm(name, realmManager);
-        Audit audit = createAudit(realm);
+        Audit audit = new AuditManager(realm, providers, clientConnection).createAudit();
         TokenService tokenService = new TokenService(realm, tokenManager, audit);
         resourceContext.initResource(tokenService);
         return tokenService;
@@ -93,7 +94,7 @@ public class RealmsResource {
             throw new NotFoundException();
         }
 
-        Audit audit = createAudit(realm);
+        Audit audit = new AuditManager(realm, providers, clientConnection).createAudit();
         AccountService accountService = new AccountService(realm, application, tokenManager, socialRequestManager, audit);
         resourceContext.initResource(accountService);
         accountService.init();
@@ -109,24 +110,4 @@ public class RealmsResource {
         return realmResource;
     }
 
-    private Audit createAudit(RealmModel realm) {
-        List<AuditListener> listeners = new LinkedList<AuditListener>();
-
-        AuditProvider auditProvider = providers.getProvider(AuditProvider.class);
-        if (auditProvider != null) {
-            listeners.add(auditProvider);
-        }
-
-        if (realm.getAuditListeners() != null) {
-            for (String id : realm.getAuditListeners()) {
-                AuditListener listener = providers.getProvider(AuditListener.class, id);
-                if (listener != null) {
-                    listeners.add(listener);
-                }
-            }
-        }
-
-        return new Audit(listeners, realm, clientConnection.getRemoteAddr());
-    }
-
 }
diff --git a/services/src/main/java/org/keycloak/services/resources/SocialResource.java b/services/src/main/java/org/keycloak/services/resources/SocialResource.java
index 4204ff9..74d3889 100755
--- a/services/src/main/java/org/keycloak/services/resources/SocialResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/SocialResource.java
@@ -40,6 +40,7 @@ import org.keycloak.models.UserModel;
 import org.keycloak.models.utils.KeycloakModelUtils;
 import org.keycloak.services.ClientConnection;
 import org.keycloak.services.ProviderSession;
+import org.keycloak.services.managers.AuditManager;
 import org.keycloak.services.managers.AuthenticationManager;
 import org.keycloak.services.managers.RealmManager;
 import org.keycloak.services.managers.SocialRequestManager;
@@ -129,7 +130,7 @@ public class SocialResource {
         RealmManager realmManager = new RealmManager(session);
         RealmModel realm = realmManager.getRealmByName(realmName);
 
-        Audit audit = createAudit(realm)
+        Audit audit = new AuditManager(realm, providers, clientConnection).createAudit()
                 .event(Events.LOGIN)
                 .detail(Details.RESPONSE_TYPE, "code")
                 .detail(Details.AUTH_METHOD, "social@" + provider.getId());
@@ -268,7 +269,7 @@ public class SocialResource {
         RealmManager realmManager = new RealmManager(session);
         RealmModel realm = realmManager.getRealmByName(realmName);
 
-        Audit audit = createAudit(realm)
+        Audit audit = new AuditManager(realm, providers, clientConnection).createAudit()
                 .event(Events.LOGIN).client(clientId)
                 .detail(Details.REDIRECT_URI, redirectUri)
                 .detail(Details.RESPONSE_TYPE, "code")
@@ -335,24 +336,4 @@ public class SocialResource {
         return queryParams;
     }
 
-    private Audit createAudit(RealmModel realm) {
-        List<AuditListener> listeners = new LinkedList<AuditListener>();
-
-        AuditProvider auditProvider = providers.getProvider(AuditProvider.class);
-        if (auditProvider != null) {
-            listeners.add(auditProvider);
-        }
-
-        if (realm.getAuditListeners() != null) {
-            for (String id : realm.getAuditListeners()) {
-                AuditListener listener = providers.getProvider(AuditListener.class, id);
-                if (listener != null) {
-                    listeners.add(listener);
-                }
-            }
-        }
-
-        return new Audit(listeners, realm, clientConnection.getRemoteAddr());
-    }
-
 }
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java
index 981781f..5300cfa 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java
@@ -334,41 +334,56 @@ public class AccountTest {
 
     @Test
     public void viewLog() {
-        List<Event> e = new LinkedList<Event>();
+        keycloakRule.configure(new KeycloakSetup() {
+            @Override
+            public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
+                appRealm.setAuditEnabled(true);
+            }
+        });
 
-        loginPage.open();
-        loginPage.clickRegister();
+        try {
+            List<Event> e = new LinkedList<Event>();
 
-        registerPage.register("view", "log", "view-log@localhost", "view-log", "password", "password");
+            loginPage.open();
+            loginPage.clickRegister();
 
-        e.add(events.poll());
-        e.add(events.poll());
+            registerPage.register("view", "log", "view-log@localhost", "view-log", "password", "password");
 
-        profilePage.open();
-        profilePage.updateProfile("view", "log2", "view-log@localhost");
+            e.add(events.poll());
+            e.add(events.poll());
 
-        e.add(events.poll());
+            profilePage.open();
+            profilePage.updateProfile("view", "log2", "view-log@localhost");
 
-        logPage.open();
+            e.add(events.poll());
 
-        e.add(events.poll());
+            logPage.open();
 
-        Collections.reverse(e);
+            e.add(events.poll());
 
-        Assert.assertTrue(logPage.isCurrent());
+            Collections.reverse(e);
 
-        List<List<String>> actual = logPage.getEvents();
+            Assert.assertTrue(logPage.isCurrent());
 
-        Assert.assertEquals(e.size(), actual.size());
+            List<List<String>> actual = logPage.getEvents();
 
-        Iterator<List<String>> itr = actual.iterator();
-        for (Event event : e) {
-            List<String> a = itr.next();
-            Assert.assertEquals(event.getEvent().replace('_', ' '), a.get(1));
-            Assert.assertEquals(event.getIpAddress(), a.get(2));
-            Assert.assertEquals(event.getClientId(), a.get(3));
-        }
+            Assert.assertEquals(e.size(), actual.size());
 
+            Iterator<List<String>> itr = actual.iterator();
+            for (Event event : e) {
+                List<String> a = itr.next();
+                Assert.assertEquals(event.getEvent().replace('_', ' '), a.get(1));
+                Assert.assertEquals(event.getIpAddress(), a.get(2));
+                Assert.assertEquals(event.getClientId(), a.get(3));
+            }
+        } finally {
+            keycloakRule.configure(new KeycloakSetup() {
+                @Override
+                public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
+                    appRealm.setAuditEnabled(false);
+                }
+            });
+        }
     }
 
 }