Allow clearing audit events through admin console, and added timer to clear expired events

This commit is contained in:
Stian Thorgersen 2014-04-08 11:29:59 +01:00
parent 9bc0bd452d
commit 7f0cf3eda8
18 changed files with 279 additions and 8 deletions

View file

@ -1028,10 +1028,6 @@ module.controller('RealmAuditCtrl', function($scope, auditConfig, RealmAudit, Re
$scope.auditConfig = auditConfig; $scope.auditConfig = auditConfig;
$scope.auditConfig.expirationUnit = TimeUnit.autoUnit(auditConfig.auditExpiration); $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.auditConfig.auditExpiration = TimeUnit.toUnit(auditConfig.auditExpiration, $scope.auditConfig.expirationUnit);
$scope.$watch('auditConfig.expirationUnit', function(to, from) { $scope.$watch('auditConfig.expirationUnit', function(to, from) {
if ($scope.auditConfig.auditExpiration) { if ($scope.auditConfig.auditExpiration) {

View file

@ -696,6 +696,10 @@ module.factory('TimeUnit', function() {
var t = {}; var t = {};
t.autoUnit = function(time) { t.autoUnit = function(time) {
if (!time) {
return 'Hours';
}
var unit = 'Seconds'; var unit = 'Seconds';
if (time % 60 == 0) { if (time % 60 == 0) {
unit = 'Minutes'; unit = 'Minutes';

View file

@ -42,6 +42,7 @@
</div> </div>
<div class="col-sm-2 select-kc"> <div class="col-sm-2 select-kc">
<select name="expirationUnit" data-ng-model="auditConfig.expirationUnit" > <select name="expirationUnit" data-ng-model="auditConfig.expirationUnit" >
<option>Minutes</option>
<option>Hours</option> <option>Hours</option>
<option>Days</option> <option>Days</option>
</select> </select>

View file

@ -1,6 +1,7 @@
package org.keycloak.models; package org.keycloak.models;
import java.io.File; import java.io.File;
import java.util.concurrent.TimeUnit;
/** /**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a> * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
@ -12,7 +13,9 @@ public class Config {
public static final String MODEL_PROVIDER_KEY = "keycloak.model"; public static final String MODEL_PROVIDER_KEY = "keycloak.model";
public static final String MODEL_AUDIT_KEY = "keycloak.audit"; public static final String AUDIT_KEY = "keycloak.audit";
public static final String AUDIT_EXPIRATION_SCHEDULE_KEY = "keycloak.audit.expirationSchedule";
public static final String AUDIT_EXPIRATION_SCHEDULE_DEFAULT = String.valueOf(TimeUnit.MINUTES.toMillis(15));
public static final String THEME_BASE_KEY = "keycloak.theme.base"; public static final String THEME_BASE_KEY = "keycloak.theme.base";
public static final String THEME_BASE_DEFAULT = "base"; public static final String THEME_BASE_DEFAULT = "base";
@ -21,6 +24,9 @@ public class Config {
public static final String THEME_DIR_KEY = "keycloak.theme.dir"; public static final String THEME_DIR_KEY = "keycloak.theme.dir";
public static final String JBOSS_SERVER_CONFIG_DIR_KEY = "jboss.server.config.dir"; public static final String JBOSS_SERVER_CONFIG_DIR_KEY = "jboss.server.config.dir";
public static final String TIMER_PROVIDER_KEY = "keycloak.timer";
public static final String TIMER_PROVIDER_DEFAULT = "basic";
public static String getAdminRealm() { public static String getAdminRealm() {
return System.getProperty(ADMIN_REALM_KEY, ADMIN_REALM_DEFAULT); return System.getProperty(ADMIN_REALM_KEY, ADMIN_REALM_DEFAULT);
} }
@ -30,13 +36,21 @@ public class Config {
} }
public static String getAuditProvider() { public static String getAuditProvider() {
return System.getProperty(MODEL_PROVIDER_KEY, "jpa"); return System.getProperty(AUDIT_KEY);
} }
public static void setAuditProvider(String provider) { public static void setAuditProvider(String provider) {
System.setProperty(MODEL_PROVIDER_KEY, provider); System.setProperty(MODEL_PROVIDER_KEY, provider);
} }
public static String getAuditExpirationSchedule() {
return System.getProperty(AUDIT_EXPIRATION_SCHEDULE_KEY, AUDIT_EXPIRATION_SCHEDULE_DEFAULT);
}
public static void setAuditExpirationSchedule(String schedule) {
System.setProperty(AUDIT_EXPIRATION_SCHEDULE_KEY, schedule);
}
public static String getModelProvider() { public static String getModelProvider() {
return System.getProperty(MODEL_PROVIDER_KEY); return System.getProperty(MODEL_PROVIDER_KEY);
} }
@ -45,6 +59,14 @@ public class Config {
System.setProperty(MODEL_PROVIDER_KEY, provider); System.setProperty(MODEL_PROVIDER_KEY, provider);
} }
public static String getTimerProvider() {
return System.getProperty(TIMER_PROVIDER_KEY, TIMER_PROVIDER_DEFAULT);
}
public static void setTimerProvider(String provider) {
System.setProperty(TIMER_PROVIDER_KEY, provider);
}
public static String getThemeDir() { public static String getThemeDir() {
String themeDir = System.getProperty(THEME_DIR_KEY); String themeDir = System.getProperty(THEME_DIR_KEY);
if (themeDir == null && System.getProperties().containsKey(JBOSS_SERVER_CONFIG_DIR_KEY)) { if (themeDir == null && System.getProperties().containsKey(JBOSS_SERVER_CONFIG_DIR_KEY)) {

View file

@ -98,6 +98,7 @@
<module>testsuite</module> <module>testsuite</module>
<module>server</module> <module>server</module>
<module>spi</module> <module>spi</module>
<module>timer</module>
</modules> </modules>
<dependencyManagement> <dependencyManagement>

View file

@ -142,6 +142,18 @@
<artifactId>keycloak-authentication-picketlink</artifactId> <artifactId>keycloak-authentication-picketlink</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-timer-api</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-timer-basic</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency> <dependency>
<groupId>junit</groupId> <groupId>junit</groupId>
<artifactId>junit</artifactId> <artifactId>junit</artifactId>

View file

@ -69,6 +69,18 @@
<version>${project.version}</version> <version>${project.version}</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-timer-api</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-timer-basic</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency> <dependency>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<artifactId>keycloak-authentication-spi</artifactId> <artifactId>keycloak-authentication-spi</artifactId>

View file

@ -170,7 +170,7 @@ public class RealmManager {
public void updateRealmAudit(RealmAuditRepresentation rep, RealmModel realm) { public void updateRealmAudit(RealmAuditRepresentation rep, RealmModel realm) {
realm.setAuditEnabled(rep.isAuditEnabled()); realm.setAuditEnabled(rep.isAuditEnabled());
realm.setAuditExpiration(rep.getAuditExpiration()); realm.setAuditExpiration(rep.getAuditExpiration() != null ? rep.getAuditExpiration() : 0);
if (rep.getAuditListeners() != null) { if (rep.getAuditListeners() != null) {
realm.setAuditListeners(new HashSet<String>(rep.getAuditListeners())); realm.setAuditListeners(new HashSet<String>(rep.getAuditListeners()));
} }

View file

@ -7,11 +7,16 @@ import org.keycloak.audit.AuditListenerFactory;
import org.keycloak.audit.AuditProvider; import org.keycloak.audit.AuditProvider;
import org.keycloak.audit.AuditProviderFactory; import org.keycloak.audit.AuditProviderFactory;
import org.keycloak.models.Config; import org.keycloak.models.Config;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.ModelProvider; import org.keycloak.models.ModelProvider;
import org.keycloak.models.RealmModel;
import org.keycloak.provider.ProviderFactory;
import org.keycloak.provider.ProviderFactoryLoader; import org.keycloak.provider.ProviderFactoryLoader;
import org.keycloak.services.DefaultProviderSessionFactory; import org.keycloak.services.DefaultProviderSessionFactory;
import org.keycloak.services.ProviderSessionFactory; import org.keycloak.services.ProviderSessionFactory;
import org.keycloak.timer.TimerProvider;
import org.keycloak.timer.TimerProviderFactory;
import org.keycloak.util.KeycloakRegistry; import org.keycloak.util.KeycloakRegistry;
import org.keycloak.services.managers.ApplianceBootstrap; import org.keycloak.services.managers.ApplianceBootstrap;
import org.keycloak.services.managers.SocialRequestManager; import org.keycloak.services.managers.SocialRequestManager;
@ -24,6 +29,7 @@ import javax.ws.rs.core.Application;
import javax.ws.rs.core.Context; import javax.ws.rs.core.Context;
import javax.ws.rs.core.UriInfo; import javax.ws.rs.core.UriInfo;
import java.net.URI; import java.net.URI;
import java.util.Date;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
@ -49,7 +55,9 @@ public class KeycloakApplication extends Application {
context.setAttribute(KeycloakRegistry.class.getName(), registry); context.setAttribute(KeycloakRegistry.class.getName(), registry);
//classes.add(KeycloakSessionCleanupFilter.class); //classes.add(KeycloakSessionCleanupFilter.class);
context.setAttribute(ProviderSessionFactory.class.getName(), createProviderSessionFactory()); DefaultProviderSessionFactory providerSessionFactory = createProviderSessionFactory();
context.setAttribute(ProviderSessionFactory.class.getName(), providerSessionFactory);
TokenManager tokenManager = new TokenManager(); TokenManager tokenManager = new TokenManager();
SocialRequestManager socialRequestManager = new SocialRequestManager(); SocialRequestManager socialRequestManager = new SocialRequestManager();
@ -62,6 +70,8 @@ public class KeycloakApplication extends Application {
classes.add(ThemeResource.class); classes.add(ThemeResource.class);
setupDefaultRealm(context.getContextPath()); setupDefaultRealm(context.getContextPath());
setupScheduledTasks(providerSessionFactory, factory);
} }
public String getContextPath() { public String getContextPath() {
@ -99,10 +109,45 @@ public class KeycloakApplication extends Application {
factory.registerLoader(AuditProvider.class, ProviderFactoryLoader.create(AuditProviderFactory.class), Config.getAuditProvider()); factory.registerLoader(AuditProvider.class, ProviderFactoryLoader.create(AuditProviderFactory.class), Config.getAuditProvider());
factory.registerLoader(AuditListener.class, ProviderFactoryLoader.create(AuditListenerFactory.class)); factory.registerLoader(AuditListener.class, ProviderFactoryLoader.create(AuditListenerFactory.class));
factory.registerLoader(TimerProvider.class, ProviderFactoryLoader.create(TimerProviderFactory.class), Config.getTimerProvider());
return factory; return factory;
} }
public static void setupScheduledTasks(final ProviderSessionFactory providerSessionFactory, final KeycloakSessionFactory keycloakSessionFactory) {
ProviderFactory<TimerProvider> timerFactory = providerSessionFactory.getProviderFactory(TimerProvider.class);
if (timerFactory == null) {
log.error("Can't setup schedule tasks, no timer provider found");
return;
}
TimerProvider timer = timerFactory.create();
final ProviderFactory<AuditProvider> auditFactory = providerSessionFactory.getProviderFactory(AuditProvider.class);
if (auditFactory != null) {
timer.schedule(new Runnable() {
@Override
public void run() {
KeycloakSession keycloakSession = keycloakSessionFactory.createSession();
AuditProvider audit = providerSessionFactory.getProviderFactory(AuditProvider.class).create();
try {
for (RealmModel realm : keycloakSession.getRealms()) {
if (realm.isAuditEnabled() && realm.getAuditExpiration() > 0) {
long olderThan = System.currentTimeMillis() - realm.getAuditExpiration() * 1000;
log.info("Expiring audit events for " + realm.getName() + " older than " + new Date(olderThan));
audit.clear(realm.getId(), olderThan);
}
}
} finally {
keycloakSession.close();
audit.close();
}
}
}, Config.getAuditExpirationSchedule());
} else {
log.info("Not scheduling audit expiration, no audit provider found");
}
}
public KeycloakSessionFactory getFactory() { public KeycloakSessionFactory getFactory() {
return factory; return factory;
} }

View file

@ -72,6 +72,16 @@
<artifactId>keycloak-model-jpa</artifactId> <artifactId>keycloak-model-jpa</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-timer-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-timer-basic</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<artifactId>keycloak-js-adapter</artifactId> <artifactId>keycloak-js-adapter</artifactId>

22
timer/api/pom.xml Executable file
View file

@ -0,0 +1,22 @@
<?xml version="1.0"?>
<project>
<parent>
<artifactId>keycloak-timer-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.0-beta-1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-timer-api</artifactId>
<name>Keycloak Timer API</name>
<description/>
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>

View file

@ -0,0 +1,12 @@
package org.keycloak.timer;
import org.keycloak.provider.Provider;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public interface TimerProvider extends Provider {
public void schedule(Runnable runnable, String config);
}

View file

@ -0,0 +1,9 @@
package org.keycloak.timer;
import org.keycloak.provider.ProviderFactory;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public interface TimerProviderFactory extends ProviderFactory<TimerProvider> {
}

23
timer/basic/pom.xml Executable file
View file

@ -0,0 +1,23 @@
<?xml version="1.0"?>
<project>
<parent>
<artifactId>keycloak-timer-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.0-beta-1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-timer-basic</artifactId>
<name>Keycloak Timer Basic Provider</name>
<description/>
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-timer-api</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>

View file

@ -0,0 +1,39 @@
package org.keycloak.timer.basic;
import org.keycloak.timer.TimerProvider;
import java.util.Timer;
import java.util.TimerTask;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class BasicTimerProvider implements TimerProvider {
private Timer timer;
public BasicTimerProvider(Timer timer) {
this.timer = timer;
}
@Override
public void schedule(final Runnable runnable, String config) {
long interval = Long.parseLong(config);
TimerTask task = new TimerTask() {
@Override
public void run() {
runnable.run();
}
};
timer.schedule(task, interval, interval);
}
@Override
public void close() {
// do nothing
}
}

View file

@ -0,0 +1,40 @@
package org.keycloak.timer.basic;
import org.keycloak.timer.TimerProvider;
import org.keycloak.timer.TimerProviderFactory;
import java.util.Timer;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class BasicTimerProviderFactory implements TimerProviderFactory {
private Timer timer;
@Override
public TimerProvider create() {
return new BasicTimerProvider(timer);
}
@Override
public void init() {
timer = new Timer();
}
@Override
public void close() {
timer.cancel();
timer = null;
}
@Override
public String getId() {
return "basic";
}
@Override
public boolean lazyLoad() {
return true;
}
}

View file

@ -0,0 +1 @@
org.keycloak.timer.basic.BasicTimerProviderFactory

22
timer/pom.xml Executable file
View file

@ -0,0 +1,22 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.0-beta-1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<name>Keycloak Timer Parent</name>
<description/>
<modelVersion>4.0.0</modelVersion>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-timer-parent</artifactId>
<packaging>pom</packaging>
<modules>
<module>api</module>
<module>basic</module>
</modules>
</project>