From 7f0cf3eda81cbe743f827a8265dd1474840e405d Mon Sep 17 00:00:00 2001 From: Stian Thorgersen Date: Tue, 8 Apr 2014 11:29:59 +0100 Subject: [PATCH] Allow clearing audit events through admin console, and added timer to clear expired events --- .../resources/admin/js/controllers/realm.js | 4 -- .../META-INF/resources/admin/js/services.js | 4 ++ .../admin/partials/realm-audit-config.html | 1 + .../main/java/org/keycloak/models/Config.java | 26 +++++++++- pom.xml | 1 + server/pom.xml | 12 +++++ services/pom.xml | 12 +++++ .../services/managers/RealmManager.java | 2 +- .../resources/KeycloakApplication.java | 47 ++++++++++++++++++- testsuite/integration/pom.xml | 10 ++++ timer/api/pom.xml | 22 +++++++++ .../org/keycloak/timer/TimerProvider.java | 12 +++++ .../keycloak/timer/TimerProviderFactory.java | 9 ++++ timer/basic/pom.xml | 23 +++++++++ .../timer/basic/BasicTimerProvider.java | 39 +++++++++++++++ .../basic/BasicTimerProviderFactory.java | 40 ++++++++++++++++ .../org.keycloak.timer.TimerProviderFactory | 1 + timer/pom.xml | 22 +++++++++ 18 files changed, 279 insertions(+), 8 deletions(-) create mode 100755 timer/api/pom.xml create mode 100644 timer/api/src/main/java/org/keycloak/timer/TimerProvider.java create mode 100644 timer/api/src/main/java/org/keycloak/timer/TimerProviderFactory.java create mode 100755 timer/basic/pom.xml create mode 100644 timer/basic/src/main/java/org/keycloak/timer/basic/BasicTimerProvider.java create mode 100644 timer/basic/src/main/java/org/keycloak/timer/basic/BasicTimerProviderFactory.java create mode 100644 timer/basic/src/main/resources/META-INF/services/org.keycloak.timer.TimerProviderFactory create mode 100755 timer/pom.xml 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 dc54399968..9bbea17fc8 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 @@ -1028,10 +1028,6 @@ module.controller('RealmAuditCtrl', function($scope, auditConfig, RealmAudit, Re $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) { 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 81af19b992..b3d5964036 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 @@ -696,6 +696,10 @@ module.factory('TimeUnit', function() { var t = {}; t.autoUnit = function(time) { + if (!time) { + return 'Hours'; + } + var unit = 'Seconds'; if (time % 60 == 0) { unit = 'Minutes'; 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 index befe2dde89..3697e7e27a 100755 --- 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 @@ -42,6 +42,7 @@
diff --git a/model/api/src/main/java/org/keycloak/models/Config.java b/model/api/src/main/java/org/keycloak/models/Config.java index 7851ea8b78..6f57ad3d0c 100644 --- a/model/api/src/main/java/org/keycloak/models/Config.java +++ b/model/api/src/main/java/org/keycloak/models/Config.java @@ -1,6 +1,7 @@ package org.keycloak.models; import java.io.File; +import java.util.concurrent.TimeUnit; /** * @author Stian Thorgersen @@ -12,7 +13,9 @@ public class Config { 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_DEFAULT = "base"; @@ -21,6 +24,9 @@ public class Config { 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 TIMER_PROVIDER_KEY = "keycloak.timer"; + public static final String TIMER_PROVIDER_DEFAULT = "basic"; + public static String getAdminRealm() { return System.getProperty(ADMIN_REALM_KEY, ADMIN_REALM_DEFAULT); } @@ -30,13 +36,21 @@ public class Config { } public static String getAuditProvider() { - return System.getProperty(MODEL_PROVIDER_KEY, "jpa"); + return System.getProperty(AUDIT_KEY); } public static void setAuditProvider(String 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() { return System.getProperty(MODEL_PROVIDER_KEY); } @@ -45,6 +59,14 @@ public class Config { 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() { String themeDir = System.getProperty(THEME_DIR_KEY); if (themeDir == null && System.getProperties().containsKey(JBOSS_SERVER_CONFIG_DIR_KEY)) { diff --git a/pom.xml b/pom.xml index f6a7e087d0..9d07a30c78 100755 --- a/pom.xml +++ b/pom.xml @@ -98,6 +98,7 @@ testsuite server spi + timer diff --git a/server/pom.xml b/server/pom.xml index 9b333492c2..44c063e790 100755 --- a/server/pom.xml +++ b/server/pom.xml @@ -142,6 +142,18 @@ keycloak-authentication-picketlink ${project.version} + + org.keycloak + keycloak-timer-api + ${project.version} + provided + + + org.keycloak + keycloak-timer-basic + ${project.version} + provided + junit junit diff --git a/services/pom.xml b/services/pom.xml index 1c68b7124f..40be81edce 100755 --- a/services/pom.xml +++ b/services/pom.xml @@ -69,6 +69,18 @@ ${project.version} provided + + org.keycloak + keycloak-timer-api + ${project.version} + provided + + + org.keycloak + keycloak-timer-basic + ${project.version} + provided + org.keycloak keycloak-authentication-spi 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 bed88674ce..1e5bb84191 100755 --- a/services/src/main/java/org/keycloak/services/managers/RealmManager.java +++ b/services/src/main/java/org/keycloak/services/managers/RealmManager.java @@ -170,7 +170,7 @@ public class RealmManager { public void updateRealmAudit(RealmAuditRepresentation rep, RealmModel realm) { realm.setAuditEnabled(rep.isAuditEnabled()); - realm.setAuditExpiration(rep.getAuditExpiration()); + realm.setAuditExpiration(rep.getAuditExpiration() != null ? rep.getAuditExpiration() : 0); if (rep.getAuditListeners() != null) { realm.setAuditListeners(new HashSet(rep.getAuditListeners())); } diff --git a/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java b/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java index da644ac4b3..865be1dd5b 100755 --- a/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java +++ b/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java @@ -7,11 +7,16 @@ import org.keycloak.audit.AuditListenerFactory; import org.keycloak.audit.AuditProvider; import org.keycloak.audit.AuditProviderFactory; import org.keycloak.models.Config; +import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.ModelProvider; +import org.keycloak.models.RealmModel; +import org.keycloak.provider.ProviderFactory; import org.keycloak.provider.ProviderFactoryLoader; import org.keycloak.services.DefaultProviderSessionFactory; import org.keycloak.services.ProviderSessionFactory; +import org.keycloak.timer.TimerProvider; +import org.keycloak.timer.TimerProviderFactory; import org.keycloak.util.KeycloakRegistry; import org.keycloak.services.managers.ApplianceBootstrap; 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.UriInfo; import java.net.URI; +import java.util.Date; import java.util.HashSet; import java.util.Set; @@ -49,7 +55,9 @@ public class KeycloakApplication extends Application { context.setAttribute(KeycloakRegistry.class.getName(), registry); //classes.add(KeycloakSessionCleanupFilter.class); - context.setAttribute(ProviderSessionFactory.class.getName(), createProviderSessionFactory()); + DefaultProviderSessionFactory providerSessionFactory = createProviderSessionFactory(); + + context.setAttribute(ProviderSessionFactory.class.getName(), providerSessionFactory); TokenManager tokenManager = new TokenManager(); SocialRequestManager socialRequestManager = new SocialRequestManager(); @@ -62,6 +70,8 @@ public class KeycloakApplication extends Application { classes.add(ThemeResource.class); setupDefaultRealm(context.getContextPath()); + + setupScheduledTasks(providerSessionFactory, factory); } public String getContextPath() { @@ -99,10 +109,45 @@ public class KeycloakApplication extends Application { factory.registerLoader(AuditProvider.class, ProviderFactoryLoader.create(AuditProviderFactory.class), Config.getAuditProvider()); factory.registerLoader(AuditListener.class, ProviderFactoryLoader.create(AuditListenerFactory.class)); + factory.registerLoader(TimerProvider.class, ProviderFactoryLoader.create(TimerProviderFactory.class), Config.getTimerProvider()); return factory; } + public static void setupScheduledTasks(final ProviderSessionFactory providerSessionFactory, final KeycloakSessionFactory keycloakSessionFactory) { + ProviderFactory 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 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() { return factory; } diff --git a/testsuite/integration/pom.xml b/testsuite/integration/pom.xml index d4ef0104a6..b5fd3c8835 100755 --- a/testsuite/integration/pom.xml +++ b/testsuite/integration/pom.xml @@ -72,6 +72,16 @@ keycloak-model-jpa ${project.version} + + org.keycloak + keycloak-timer-api + ${project.version} + + + org.keycloak + keycloak-timer-basic + ${project.version} + org.keycloak keycloak-js-adapter diff --git a/timer/api/pom.xml b/timer/api/pom.xml new file mode 100755 index 0000000000..030f08905c --- /dev/null +++ b/timer/api/pom.xml @@ -0,0 +1,22 @@ + + + + keycloak-timer-parent + org.keycloak + 1.0-beta-1-SNAPSHOT + + + 4.0.0 + + keycloak-timer-api + Keycloak Timer API + + + + org.keycloak + keycloak-core + ${project.version} + + + + diff --git a/timer/api/src/main/java/org/keycloak/timer/TimerProvider.java b/timer/api/src/main/java/org/keycloak/timer/TimerProvider.java new file mode 100644 index 0000000000..e701b5fbea --- /dev/null +++ b/timer/api/src/main/java/org/keycloak/timer/TimerProvider.java @@ -0,0 +1,12 @@ +package org.keycloak.timer; + +import org.keycloak.provider.Provider; + +/** + * @author Stian Thorgersen + */ +public interface TimerProvider extends Provider { + + public void schedule(Runnable runnable, String config); + +} diff --git a/timer/api/src/main/java/org/keycloak/timer/TimerProviderFactory.java b/timer/api/src/main/java/org/keycloak/timer/TimerProviderFactory.java new file mode 100644 index 0000000000..5c73b8e7bb --- /dev/null +++ b/timer/api/src/main/java/org/keycloak/timer/TimerProviderFactory.java @@ -0,0 +1,9 @@ +package org.keycloak.timer; + +import org.keycloak.provider.ProviderFactory; + +/** + * @author Stian Thorgersen + */ +public interface TimerProviderFactory extends ProviderFactory { +} diff --git a/timer/basic/pom.xml b/timer/basic/pom.xml new file mode 100755 index 0000000000..8d5c5bff9a --- /dev/null +++ b/timer/basic/pom.xml @@ -0,0 +1,23 @@ + + + + keycloak-timer-parent + org.keycloak + 1.0-beta-1-SNAPSHOT + + + 4.0.0 + + keycloak-timer-basic + Keycloak Timer Basic Provider + + + + + org.keycloak + keycloak-timer-api + ${project.version} + + + + diff --git a/timer/basic/src/main/java/org/keycloak/timer/basic/BasicTimerProvider.java b/timer/basic/src/main/java/org/keycloak/timer/basic/BasicTimerProvider.java new file mode 100644 index 0000000000..bda150b0aa --- /dev/null +++ b/timer/basic/src/main/java/org/keycloak/timer/basic/BasicTimerProvider.java @@ -0,0 +1,39 @@ +package org.keycloak.timer.basic; + +import org.keycloak.timer.TimerProvider; + +import java.util.Timer; +import java.util.TimerTask; + +/** + * @author Stian Thorgersen + */ +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 + } + +} diff --git a/timer/basic/src/main/java/org/keycloak/timer/basic/BasicTimerProviderFactory.java b/timer/basic/src/main/java/org/keycloak/timer/basic/BasicTimerProviderFactory.java new file mode 100644 index 0000000000..accb42bd9e --- /dev/null +++ b/timer/basic/src/main/java/org/keycloak/timer/basic/BasicTimerProviderFactory.java @@ -0,0 +1,40 @@ +package org.keycloak.timer.basic; + +import org.keycloak.timer.TimerProvider; +import org.keycloak.timer.TimerProviderFactory; + +import java.util.Timer; + +/** + * @author Stian Thorgersen + */ +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; + } +} diff --git a/timer/basic/src/main/resources/META-INF/services/org.keycloak.timer.TimerProviderFactory b/timer/basic/src/main/resources/META-INF/services/org.keycloak.timer.TimerProviderFactory new file mode 100644 index 0000000000..94a4b8d73d --- /dev/null +++ b/timer/basic/src/main/resources/META-INF/services/org.keycloak.timer.TimerProviderFactory @@ -0,0 +1 @@ +org.keycloak.timer.basic.BasicTimerProviderFactory \ No newline at end of file diff --git a/timer/pom.xml b/timer/pom.xml new file mode 100755 index 0000000000..dc65caddc7 --- /dev/null +++ b/timer/pom.xml @@ -0,0 +1,22 @@ + + + keycloak-parent + org.keycloak + 1.0-beta-1-SNAPSHOT + ../pom.xml + + + Keycloak Timer Parent + + 4.0.0 + + org.keycloak + keycloak-timer-parent + pom + + + api + basic + +