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);
+ }
+ });
+ }
}
}