keycloak-uncached
Changes
connections/mongo/src/main/java/org/keycloak/connections/mongo/MongoKeycloakTransaction.java 2(+2 -0)
examples/providers/federation-provider/src/main/java/org/keycloak/examples/federation/properties/BasePropertiesFederationFactory.java 22(+5 -17)
forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/users.js 173(+129 -44)
forms/common-themes/src/main/resources/theme/admin/base/resources/partials/federated-generic.html 30(+30 -0)
forms/common-themes/src/main/resources/theme/admin/base/resources/partials/federated-ldap.html 36(+36 -0)
model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/DefaultCacheRealmProvider.java 2(+2 -0)
Details
diff --git a/connections/mongo/src/main/java/org/keycloak/connections/mongo/MongoKeycloakTransaction.java b/connections/mongo/src/main/java/org/keycloak/connections/mongo/MongoKeycloakTransaction.java
index 705b888..a8dffc7 100644
--- a/connections/mongo/src/main/java/org/keycloak/connections/mongo/MongoKeycloakTransaction.java
+++ b/connections/mongo/src/main/java/org/keycloak/connections/mongo/MongoKeycloakTransaction.java
@@ -42,11 +42,13 @@ public class MongoKeycloakTransaction implements KeycloakTransaction {
} catch (MongoException e) {
throw MongoStoreImpl.convertException(e);
}
+ started = false;
}
@Override
public void rollback() {
invocationContext.rollback();
+ started = false;
}
@Override
diff --git a/examples/providers/federation-provider/src/main/java/org/keycloak/examples/federation/properties/BasePropertiesFederationFactory.java b/examples/providers/federation-provider/src/main/java/org/keycloak/examples/federation/properties/BasePropertiesFederationFactory.java
index b0d9925..d79a13b 100755
--- a/examples/providers/federation-provider/src/main/java/org/keycloak/examples/federation/properties/BasePropertiesFederationFactory.java
+++ b/examples/providers/federation-provider/src/main/java/org/keycloak/examples/federation/properties/BasePropertiesFederationFactory.java
@@ -101,24 +101,7 @@ public abstract class BasePropertiesFederationFactory implements UserFederationP
RealmModel realm = session.realms().getRealm(realmId);
BasePropertiesFederationProvider federationProvider = (BasePropertiesFederationProvider)getInstance(session, model);
Set<String> allUsernames = federationProvider.getProperties().stringPropertyNames();
- for (String username : allUsernames) {
- federationProvider.getUserByUsername(realm, username);
- }
- }
-
- });
- }
-
- @Override
- public void syncChangedUsers(KeycloakSessionFactory sessionFactory, final String realmId, final UserFederationProviderModel model, Date lastSync) {
- KeycloakModelUtils.runJobInTransaction(sessionFactory, new KeycloakSessionTask() {
-
- @Override
- public void run(KeycloakSession session) {
- RealmModel realm = session.realms().getRealm(realmId);
UserProvider localProvider = session.userStorage();
- BasePropertiesFederationProvider federationProvider = (BasePropertiesFederationProvider)getInstance(session, model);
- Set<String> allUsernames = federationProvider.getProperties().stringPropertyNames();
for (String username : allUsernames) {
UserModel localUser = localProvider.getUserByUsername(username, realm);
@@ -131,4 +114,9 @@ public abstract class BasePropertiesFederationFactory implements UserFederationP
});
}
+
+ @Override
+ public void syncChangedUsers(KeycloakSessionFactory sessionFactory, final String realmId, final UserFederationProviderModel model, Date lastSync) {
+ syncAllUsers(sessionFactory, realmId, model);
+ }
}
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/users.js b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/users.js
index 706cd0e..52a933f 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/users.js
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/users.js
@@ -356,25 +356,50 @@ module.controller('UserFederationCtrl', function($scope, $location, realm, UserF
});
-module.controller('GenericUserFederationCtrl', function($scope, $location, Notifications, Dialog, realm, instance, providerFactory, UserFederationInstances) {
+module.controller('GenericUserFederationCtrl', function($scope, $location, Notifications, Dialog, realm, instance, providerFactory, UserFederationInstances, UserFederationSync) {
console.log('GenericUserFederationCtrl');
- $scope.instance = angular.copy(instance);
$scope.create = !instance.providerName;
$scope.providerFactory = providerFactory;
console.log("providerFactory: " + providerFactory.id);
- if ($scope.create) {
- $scope.instance.providerName = providerFactory.id;
- $scope.instance.config = {};
- $scope.instance.priority = 0;
+ function initFederationSettings() {
+ if ($scope.create) {
+ instance.providerName = providerFactory.id;
+ instance.config = {};
+ instance.priority = 0;
+ $scope.fullSyncEnabled = false;
+ $scope.changedSyncEnabled = false;
+ } else {
+ $scope.fullSyncEnabled = (instance.fullSyncPeriod && instance.fullSyncPeriod > 0);
+ $scope.changedSyncEnabled = (instance.changedSyncPeriod && instance.changedSyncPeriod > 0);
+ }
+
+ $scope.changed = false;
}
+ initFederationSettings();
+ $scope.instance = angular.copy(instance);
$scope.realm = realm;
+ $scope.$watch('fullSyncEnabled', function(newVal, oldVal) {
+ if (oldVal == newVal) {
+ return;
+ }
+
+ $scope.instance.fullSyncPeriod = $scope.fullSyncEnabled ? 604800 : -1;
+ $scope.changed = true;
+ });
+
+ $scope.$watch('changedSyncEnabled', function(newVal, oldVal) {
+ if (oldVal == newVal) {
+ return;
+ }
- $scope.changed = false;
+ $scope.instance.changedSyncPeriod = $scope.changedSyncEnabled ? 86400 : -1;
+ $scope.changed = true;
+ });
$scope.$watch('instance', function() {
if (!angular.equals($scope.instance, instance)) {
@@ -405,13 +430,8 @@ module.controller('GenericUserFederationCtrl', function($scope, $location, Notif
};
$scope.reset = function() {
+ initFederationSettings();
$scope.instance = angular.copy(instance);
- if ($scope.create) {
- $scope.instance.providerName = providerFactory.id;
- $scope.instance.config = {};
- $scope.instance.priority = 0;
- }
- $scope.changed = false;
};
$scope.cancel = function() {
@@ -430,37 +450,70 @@ module.controller('GenericUserFederationCtrl', function($scope, $location, Notif
});
};
+ $scope.triggerFullSync = function() {
+ console.log('GenericCtrl: triggerFullSync');
+ triggerSync('triggerFullSync');
+ }
+
+ $scope.triggerChangedUsersSync = function() {
+ console.log('GenericCtrl: triggerChangedUsersSync');
+ triggerSync('triggerChangedUsersSync');
+ }
+ function triggerSync(action) {
+ UserFederationSync.get({ action: action, realm: $scope.realm.realm, provider: $scope.instance.id }, function() {
+ Notifications.success("Sync of users finished successfully");
+ }, function() {
+ Notifications.error("Error during sync of users");
+ });
+ }
});
-module.controller('LDAPCtrl', function($scope, $location, Notifications, Dialog, realm, instance, UserFederationInstances, RealmLDAPConnectionTester) {
+module.controller('LDAPCtrl', function($scope, $location, Notifications, Dialog, realm, instance, UserFederationInstances, UserFederationSync, RealmLDAPConnectionTester) {
console.log('LDAPCtrl');
+ var DEFAULT_BATCH_SIZE = "1000";
- $scope.instance = angular.copy(instance);
$scope.create = !instance.providerName;
- if ($scope.create) {
- $scope.instance.providerName = "ldap";
- $scope.instance.config = {};
- $scope.instance.priority = 0;
- $scope.syncRegistrations = false;
+ function initFederationSettings() {
+ if ($scope.create) {
+ instance.providerName = "ldap";
+ instance.config = {};
+ instance.priority = 0;
+ $scope.syncRegistrations = false;
- $scope.userAccountControlsAfterPasswordUpdate = true;
- $scope.instance.config.userAccountControlsAfterPasswordUpdate = "true";
+ $scope.userAccountControlsAfterPasswordUpdate = true;
+ instance.config.userAccountControlsAfterPasswordUpdate = "true";
- $scope.connectionPooling = true;
- $scope.instance.config.connectionPooling = "true";
+ $scope.connectionPooling = true;
+ instance.config.connectionPooling = "true";
- $scope.pagination = true;
- $scope.instance.config.pagination = "true";
- } else {
- $scope.syncRegistrations = instance.config.syncRegistrations && instance.config.syncRegistrations == "true";
- $scope.userAccountControlsAfterPasswordUpdate = instance.config.userAccountControlsAfterPasswordUpdate && instance.config.userAccountControlsAfterPasswordUpdate == "true";
- $scope.connectionPooling = instance.config.connectionPooling && instance.config.connectionPooling == "true";
- $scope.pagination = instance.config.pagination && instance.config.pagination == "true";
+ $scope.pagination = true;
+ instance.config.pagination = "true";
+ instance.config.batchSizeForSync = DEFAULT_BATCH_SIZE;
+
+ $scope.fullSyncEnabled = false;
+ $scope.changedSyncEnabled = false;
+ } else {
+ $scope.syncRegistrations = instance.config.syncRegistrations && instance.config.syncRegistrations == "true";
+ $scope.userAccountControlsAfterPasswordUpdate = instance.config.userAccountControlsAfterPasswordUpdate && instance.config.userAccountControlsAfterPasswordUpdate == "true";
+ $scope.connectionPooling = instance.config.connectionPooling && instance.config.connectionPooling == "true";
+ $scope.pagination = instance.config.pagination && instance.config.pagination == "true";
+ if (!instance.config.batchSizeForSync) {
+ instance.config.batchSizeForSync = DEFAULT_BATCH_SIZE;
+ }
+ $scope.fullSyncEnabled = (instance.fullSyncPeriod && instance.fullSyncPeriod > 0);
+ $scope.changedSyncEnabled = (instance.changedSyncPeriod && instance.changedSyncPeriod > 0);
+ }
+
+ $scope.changed = false;
+ $scope.lastVendor = instance.config.vendor;
}
+ initFederationSettings();
+ $scope.instance = angular.copy(instance);
+
$scope.ldapVendors = [
{ "id": "ad", "name": "Active Directory" },
{ "id": "rhds", "name": "Red Hat Directory Server" },
@@ -473,11 +526,6 @@ module.controller('LDAPCtrl', function($scope, $location, Notifications, Dialog,
$scope.realm = realm;
-
- $scope.changed = false;
-
- $scope.lastVendor = $scope.instance.config.vendor;
-
function watchBooleanProperty(propertyName) {
$scope.$watch(propertyName, function() {
if ($scope[propertyName]) {
@@ -493,6 +541,24 @@ module.controller('LDAPCtrl', function($scope, $location, Notifications, Dialog,
watchBooleanProperty('connectionPooling');
watchBooleanProperty('pagination');
+ $scope.$watch('fullSyncEnabled', function(newVal, oldVal) {
+ if (oldVal == newVal) {
+ return;
+ }
+
+ $scope.instance.fullSyncPeriod = $scope.fullSyncEnabled ? 604800 : -1;
+ $scope.changed = true;
+ });
+
+ $scope.$watch('changedSyncEnabled', function(newVal, oldVal) {
+ if (oldVal == newVal) {
+ return;
+ }
+
+ $scope.instance.changedSyncPeriod = $scope.changedSyncEnabled ? 86400 : -1;
+ $scope.changed = true;
+ });
+
$scope.$watch('instance', function() {
if (!angular.equals($scope.instance, instance)) {
$scope.changed = true;
@@ -514,6 +580,13 @@ module.controller('LDAPCtrl', function($scope, $location, Notifications, Dialog,
$scope.save = function() {
$scope.changed = false;
+
+ if (!parseInt($scope.instance.config.batchSizeForSync)) {
+ $scope.instance.config.batchSizeForSync = DEFAULT_BATCH_SIZE;
+ } else {
+ $scope.instance.config.batchSizeForSync = parseInt($scope.instance.config.batchSizeForSync).toString();
+ }
+
if ($scope.create) {
UserFederationInstances.save({realm: realm.realm}, $scope.instance, function () {
$scope.changed = false;
@@ -534,15 +607,8 @@ module.controller('LDAPCtrl', function($scope, $location, Notifications, Dialog,
};
$scope.reset = function() {
+ initFederationSettings();
$scope.instance = angular.copy(instance);
- if ($scope.create) {
- $scope.instance.providerName = "ldap";
- $scope.instance.config = {};
- $scope.instance.priority = 0;
- $scope.syncRegistrations = false;
- }
- $scope.changed = false;
- $scope.lastVendor = $scope.instance.config.vendor;
};
$scope.cancel = function() {
@@ -589,5 +655,24 @@ module.controller('LDAPCtrl', function($scope, $location, Notifications, Dialog,
Notifications.error("LDAP authentication failed. See server.log for details");
});
}
+
+ $scope.triggerFullSync = function() {
+ console.log('LDAPCtrl: triggerFullSync');
+ triggerSync('triggerFullSync');
+ }
+
+ $scope.triggerChangedUsersSync = function() {
+ console.log('LDAPCtrl: triggerChangedUsersSync');
+ triggerSync('triggerChangedUsersSync');
+ }
+
+ function triggerSync(action) {
+ UserFederationSync.get({ action: action, realm: $scope.realm.realm, provider: $scope.instance.id }, function() {
+ Notifications.success("Sync of users finished successfully");
+ }, function() {
+ Notifications.error("Error during sync of users");
+ });
+ }
+
});
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/services.js b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/services.js
index e8af664..efed6c7 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/services.js
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/services.js
@@ -217,6 +217,10 @@ module.factory('UserFederationProviders', function($resource) {
});
});
+module.factory('UserFederationSync', function($resource) {
+ return $resource(authUrl + '/admin/realms/:realm/user-federation/sync/:provider');
+});
+
module.factory('UserSessionStats', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/users/:user/session-stats', {
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/federated-generic.html b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/federated-generic.html
index b046180..7dcae0f 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/federated-generic.html
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/federated-generic.html
@@ -40,6 +40,34 @@
</fieldset>
+ <fieldset>
+ <legend><span class="text">Sync settings</span></legend>
+ <div class="form-group clearfix">
+ <label class="col-sm-2 control-label" for="fullSyncEnabled">Periodic full sync</label>
+ <div class="col-sm-4">
+ <input ng-model="fullSyncEnabled" name="fullSyncEnabled" id="fullSyncEnabled" onoffswitch />
+ </div>
+ </div>
+ <div class="form-group clearfix" data-ng-show="fullSyncEnabled">
+ <label class="col-sm-2 control-label" for="fullSyncPeriod">Full sync period</label>
+ <div class="col-sm-4">
+ <input class="form-control" type="number" ng-model="instance.fullSyncPeriod" id="fullSyncPeriod" />
+ </div>
+ </div>
+ <div class="form-group clearfix">
+ <label class="col-sm-2 control-label" for="changedSyncEnabled">Periodic changed users sync</label>
+ <div class="col-sm-4">
+ <input ng-model="changedSyncEnabled" name="changedSyncEnabled" id="changedSyncEnabled" onoffswitch />
+ </div>
+ </div>
+ <div class="form-group clearfix" data-ng-show="changedSyncEnabled">
+ <label class="col-sm-2 control-label" for="changedSyncPeriod">Changed users sync period</label>
+ <div class="col-sm-4">
+ <input class="form-control" type="number" ng-model="instance.changedSyncPeriod" id="changedSyncPeriod" />
+ </div>
+ </div>
+ </fieldset>
+
<div class="pull-right form-actions" data-ng-show="create && access.manageUsers">
<button kc-cancel data-ng-click="cancel()">Cancel</button>
<button kc-save data-ng-show="changed">Save</button>
@@ -49,6 +77,8 @@
<button kc-reset data-ng-show="changed">Clear changes</button>
<button kc-save data-ng-show="changed">Save</button>
<button kc-delete data-ng-click="remove()" data-ng-hide="changed">Delete</button>
+ <button kc-delete data-ng-click="triggerFullSync()" data-ng-hide="changed">Synchronize all users</button>
+ <button kc-delete data-ng-click="triggerChangedUsersSync()" data-ng-hide="changed">Synchronize changed users</button>
</div>
</form>
</div>
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/federated-ldap.html b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/federated-ldap.html
index c9dd366..da1e04c 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/federated-ldap.html
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/federated-ldap.html
@@ -133,6 +133,40 @@
</div>
</fieldset>
+ <fieldset>
+ <legend><span class="text">Sync settings</span></legend>
+ <div class="form-group clearfix">
+ <label class="col-sm-2 control-label" for="batchSizeForSync">Batch size</label>
+ <div class="col-sm-4">
+ <input class="form-control" type="text" ng-model="instance.config.batchSizeForSync" id="batchSizeForSync" />
+ </div>
+ </div>
+ <div class="form-group clearfix">
+ <label class="col-sm-2 control-label" for="fullSyncEnabled">Periodic full sync</label>
+ <div class="col-sm-4">
+ <input ng-model="fullSyncEnabled" name="fullSyncEnabled" id="fullSyncEnabled" onoffswitch />
+ </div>
+ </div>
+ <div class="form-group clearfix" data-ng-show="fullSyncEnabled">
+ <label class="col-sm-2 control-label" for="fullSyncPeriod">Full sync period</label>
+ <div class="col-sm-4">
+ <input class="form-control" type="number" ng-model="instance.fullSyncPeriod" id="fullSyncPeriod" />
+ </div>
+ </div>
+ <div class="form-group clearfix">
+ <label class="col-sm-2 control-label" for="changedSyncEnabled">Periodic changed users sync</label>
+ <div class="col-sm-4">
+ <input ng-model="changedSyncEnabled" name="changedSyncEnabled" id="changedSyncEnabled" onoffswitch />
+ </div>
+ </div>
+ <div class="form-group clearfix" data-ng-show="changedSyncEnabled">
+ <label class="col-sm-2 control-label" for="changedSyncPeriod">Changed users sync period</label>
+ <div class="col-sm-4">
+ <input class="form-control" type="number" ng-model="instance.changedSyncPeriod" id="changedSyncPeriod" />
+ </div>
+ </div>
+ </fieldset>
+
<div class="pull-right form-actions" data-ng-show="create && access.manageUsers">
<button kc-cancel data-ng-click="cancel()">Cancel</button>
<button kc-save data-ng-show="changed">Save</button>
@@ -142,6 +176,8 @@
<button kc-reset data-ng-show="changed">Clear changes</button>
<button kc-save data-ng-show="changed">Save</button>
<button kc-delete data-ng-click="remove()" data-ng-hide="changed">Delete</button>
+ <button kc-delete data-ng-click="triggerFullSync()" data-ng-hide="changed">Synchronize all users</button>
+ <button kc-delete data-ng-click="triggerChangedUsersSync()" data-ng-hide="changed">Synchronize changed users</button>
</div>
</form>
</div>
diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/DefaultCacheRealmProvider.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/DefaultCacheRealmProvider.java
index 607a10c..a1d232e 100755
--- a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/DefaultCacheRealmProvider.java
+++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/DefaultCacheRealmProvider.java
@@ -122,12 +122,14 @@ public class DefaultCacheRealmProvider implements CacheRealmProvider {
cache.clear();
}
runInvalidations();
+ transactionActive = false;
}
@Override
public void rollback() {
setRollbackOnly = true;
runInvalidations();
+ transactionActive = false;
}
@Override
diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/DefaultCacheUserProvider.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/DefaultCacheUserProvider.java
index 781a3de..678bf70 100755
--- a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/DefaultCacheUserProvider.java
+++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/DefaultCacheUserProvider.java
@@ -87,12 +87,14 @@ public class DefaultCacheUserProvider implements CacheUserProvider {
cache.clear();
}
runInvalidations();
+ transactionActive = false;
}
@Override
public void rollback() {
setRollbackOnly = true;
runInvalidations();
+ transactionActive = false;
}
@Override
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 c6ccb76..3574508 100755
--- a/services/src/main/java/org/keycloak/services/managers/RealmManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/RealmManager.java
@@ -144,9 +144,9 @@ public class RealmManager {
}
// Remove all periodic syncs for configured federation providers
- PeriodicSyncManager periodicSyncManager = new PeriodicSyncManager();
+ UsersSyncManager usersSyncManager = new UsersSyncManager();
for (final UserFederationProviderModel fedProvider : federationProviders) {
- periodicSyncManager.removePeriodicSyncForProvider(session.getProvider(TimerProvider.class), fedProvider);
+ usersSyncManager.removePeriodicSyncForProvider(session.getProvider(TimerProvider.class), fedProvider);
}
}
return removed;
@@ -218,9 +218,9 @@ public class RealmManager {
// Refresh periodic sync tasks for configured federationProviders
List<UserFederationProviderModel> federationProviders = newRealm.getUserFederationProviders();
- PeriodicSyncManager periodicSyncManager = new PeriodicSyncManager();
+ UsersSyncManager usersSyncManager = new UsersSyncManager();
for (final UserFederationProviderModel fedProvider : federationProviders) {
- periodicSyncManager.startPeriodicSyncForProvider(session.getKeycloakSessionFactory(), session.getProvider(TimerProvider.class), fedProvider, newRealm.getId());
+ usersSyncManager.refreshPeriodicSyncForProvider(session.getKeycloakSessionFactory(), session.getProvider(TimerProvider.class), fedProvider, newRealm.getId());
}
}
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 b6af7a4..7b41b55 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
@@ -22,7 +22,7 @@ import org.keycloak.representations.adapters.action.SessionStats;
import org.keycloak.representations.idm.RealmAuditRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.services.managers.LDAPConnectionTestManager;
-import org.keycloak.services.managers.PeriodicSyncManager;
+import org.keycloak.services.managers.UsersSyncManager;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.services.managers.ResourceAdminManager;
import org.keycloak.services.managers.TokenManager;
@@ -165,9 +165,9 @@ public class RealmAdminResource {
// Refresh periodic sync tasks for configured federationProviders
List<UserFederationProviderModel> federationProviders = realm.getUserFederationProviders();
- PeriodicSyncManager periodicSyncManager = new PeriodicSyncManager();
+ UsersSyncManager usersSyncManager = new UsersSyncManager();
for (final UserFederationProviderModel fedProvider : federationProviders) {
- periodicSyncManager.startPeriodicSyncForProvider(session.getKeycloakSessionFactory(), session.getProvider(TimerProvider.class), fedProvider, realm.getId());
+ usersSyncManager.refreshPeriodicSyncForProvider(session.getKeycloakSessionFactory(), session.getProvider(TimerProvider.class), fedProvider, realm.getId());
}
return Response.noContent().build();
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/UserFederationResource.java b/services/src/main/java/org/keycloak/services/resources/admin/UserFederationResource.java
index 6039ccb..554ed7d 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/UserFederationResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/UserFederationResource.java
@@ -12,7 +12,7 @@ import org.keycloak.models.utils.ModelToRepresentation;
import org.keycloak.provider.ProviderFactory;
import org.keycloak.representations.idm.UserFederationProviderFactoryRepresentation;
import org.keycloak.representations.idm.UserFederationProviderRepresentation;
-import org.keycloak.services.managers.PeriodicSyncManager;
+import org.keycloak.services.managers.UsersSyncManager;
import org.keycloak.timer.TimerProvider;
import javax.ws.rs.Consumes;
@@ -23,6 +23,7 @@ import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
@@ -120,7 +121,7 @@ public class UserFederationResource {
}
UserFederationProviderModel model = realm.addUserFederationProvider(rep.getProviderName(), rep.getConfig(), rep.getPriority(), displayName,
rep.getFullSyncPeriod(), rep.getChangedSyncPeriod(), rep.getLastSync());
- new PeriodicSyncManager().startPeriodicSyncForProvider(session.getKeycloakSessionFactory(), session.getProvider(TimerProvider.class), model, realm.getId());
+ new UsersSyncManager().refreshPeriodicSyncForProvider(session.getKeycloakSessionFactory(), session.getProvider(TimerProvider.class), model, realm.getId());
return Response.created(uriInfo.getAbsolutePathBuilder().path(model.getId()).build()).build();
}
@@ -144,7 +145,7 @@ public class UserFederationResource {
UserFederationProviderModel model = new UserFederationProviderModel(id, rep.getProviderName(), rep.getConfig(), rep.getPriority(), displayName,
rep.getFullSyncPeriod(), rep.getChangedSyncPeriod(), rep.getLastSync());
realm.updateUserFederationProvider(model);
- new PeriodicSyncManager().startPeriodicSyncForProvider(session.getKeycloakSessionFactory(), session.getProvider(TimerProvider.class), model, realm.getId());
+ new UsersSyncManager().refreshPeriodicSyncForProvider(session.getKeycloakSessionFactory(), session.getProvider(TimerProvider.class), model, realm.getId());
}
/**
@@ -179,7 +180,7 @@ public class UserFederationResource {
auth.requireManage();
UserFederationProviderModel model = new UserFederationProviderModel(id, null, null, -1, null, -1, -1, 0);
realm.removeUserFederationProvider(model);
- new PeriodicSyncManager().removePeriodicSyncForProvider(session.getProvider(TimerProvider.class), model);
+ new UsersSyncManager().removePeriodicSyncForProvider(session.getProvider(TimerProvider.class), model);
}
@@ -202,5 +203,32 @@ public class UserFederationResource {
return reps;
}
+ /**
+ * trigger sync of users
+ *
+ * @return
+ */
+ @GET
+ @Path("sync/{id}")
+ @NoCache
+ public Response syncUsers(@PathParam("id") String providerId, @QueryParam("action") String action) {
+ logger.info("triggerSync");
+ auth.requireManage();
+
+ for (UserFederationProviderModel model : realm.getUserFederationProviders()) {
+ if (model.getId().equals(providerId)) {
+ UsersSyncManager syncManager = new UsersSyncManager();
+ if ("triggerFullSync".equals(action)) {
+ syncManager.syncAllUsers(session.getKeycloakSessionFactory(), realm.getId(), model);
+ } else if ("triggerChangedUsersSync".equals(action)) {
+ syncManager.syncChangedUsers(session.getKeycloakSessionFactory(), realm.getId(), model);
+ }
+ return Response.noContent().build();
+ }
+ }
+
+ throw new NotFoundException("could not find provider");
+ }
+
}
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 27f9a2f..724ebe6 100755
--- a/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java
+++ b/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java
@@ -15,7 +15,7 @@ import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.services.DefaultKeycloakSessionFactory;
import org.keycloak.services.managers.ApplianceBootstrap;
import org.keycloak.services.managers.BruteForceProtector;
-import org.keycloak.services.managers.PeriodicSyncManager;
+import org.keycloak.services.managers.UsersSyncManager;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.services.managers.TokenManager;
import org.keycloak.services.resources.admin.AdminRoot;
@@ -149,7 +149,7 @@ public class KeycloakApplication extends Application {
TimerProvider timer = sessionFactory.create().getProvider(TimerProvider.class);
timer.schedule(new ScheduledTaskRunner(sessionFactory, new ClearExpiredAuditEvents()), interval, "ClearExpiredAuditEvents");
timer.schedule(new ScheduledTaskRunner(sessionFactory, new ClearExpiredUserSessions()), interval, "ClearExpiredUserSessions");
- new PeriodicSyncManager().bootstrap(sessionFactory, timer);
+ new UsersSyncManager().bootstrapPeriodic(sessionFactory, timer);
}
public KeycloakSessionFactory getSessionFactory() {
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/SyncProvidersTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/SyncProvidersTest.java
index 21ac290..f4dac01 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/SyncProvidersTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/SyncProvidersTest.java
@@ -11,7 +11,6 @@ import org.junit.Test;
import org.junit.rules.RuleChain;
import org.junit.rules.TestRule;
import org.junit.runners.MethodSorters;
-import org.keycloak.examples.federation.properties.ClasspathPropertiesFederationFactory;
import org.keycloak.federation.ldap.LDAPFederationProvider;
import org.keycloak.federation.ldap.LDAPFederationProviderFactory;
import org.keycloak.federation.ldap.LDAPUtils;
@@ -23,11 +22,10 @@ import org.keycloak.models.UserFederationProviderFactory;
import org.keycloak.models.UserFederationProviderModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserProvider;
-import org.keycloak.services.managers.PeriodicSyncManager;
+import org.keycloak.services.managers.UsersSyncManager;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.testsuite.rule.KeycloakRule;
import org.keycloak.testsuite.rule.LDAPRule;
-import org.keycloak.testutils.DummyUserFederationProvider;
import org.keycloak.testutils.DummyUserFederationProviderFactory;
import org.keycloak.testutils.LDAPEmbeddedServer;
import org.keycloak.timer.TimerProvider;
@@ -84,29 +82,43 @@ public class SyncProvidersTest {
@Test
public void testLDAPSync() {
+ UsersSyncManager usersSyncManager = new UsersSyncManager();
+
+ // wait a bit
+ sleep(1000);
+
KeycloakSession session = keycloakRule.startSession();
try {
KeycloakSessionFactory sessionFactory = session.getKeycloakSessionFactory();
- UserFederationProviderFactory ldapFedFactory = (UserFederationProviderFactory)sessionFactory.getProviderFactory(UserFederationProvider.class, LDAPFederationProviderFactory.PROVIDER_NAME);
- ldapFedFactory.syncAllUsers(sessionFactory, "test", ldapModel);
+ usersSyncManager.syncAllUsers(sessionFactory, "test", ldapModel);
} finally {
keycloakRule.stopSession(session, false);
}
- // Assert users imported (model test)
session = keycloakRule.startSession();
try {
RealmModel testRealm = session.realms().getRealm("test");
UserProvider userProvider = session.userStorage();
+ // Assert users imported
assertUserImported(userProvider, testRealm, "user1", "User1FN", "User1LN", "user1@email.org");
assertUserImported(userProvider, testRealm, "user2", "User2FN", "User2LN", "user2@email.org");
assertUserImported(userProvider, testRealm, "user3", "User3FN", "User3LN", "user3@email.org");
assertUserImported(userProvider, testRealm, "user4", "User4FN", "User4LN", "user4@email.org");
assertUserImported(userProvider, testRealm, "user5", "User5FN", "User5LN", "user5@email.org");
+ // Assert lastSync time updated
+ Assert.assertTrue(ldapModel.getLastSync() > 0);
+ for (UserFederationProviderModel persistentFedModel : testRealm.getUserFederationProviders()) {
+ if (LDAPFederationProviderFactory.PROVIDER_NAME.equals(persistentFedModel.getProviderName())) {
+ Assert.assertTrue(persistentFedModel.getLastSync() > 0);
+ } else {
+ // Dummy provider has still 0
+ Assert.assertEquals(0, persistentFedModel.getLastSync());
+ }
+ }
+
// wait a bit
sleep(1000);
- Date beforeLDAPUpdate = new Date();
// Add user to LDAP and update 'user5' in LDAP
PartitionManager partitionManager = FederationProvidersIntegrationTest.getPartitionManager(session, ldapModel);
@@ -119,8 +131,7 @@ public class SyncProvidersTest {
// Trigger partial sync
KeycloakSessionFactory sessionFactory = session.getKeycloakSessionFactory();
- UserFederationProviderFactory ldapFedFactory = (UserFederationProviderFactory)sessionFactory.getProviderFactory(UserFederationProvider.class, LDAPFederationProviderFactory.PROVIDER_NAME);
- ldapFedFactory.syncChangedUsers(sessionFactory, "test", ldapModel, beforeLDAPUpdate);
+ usersSyncManager.syncChangedUsers(sessionFactory, "test", ldapModel);
} finally {
keycloakRule.stopSession(session, false);
}
@@ -147,12 +158,12 @@ public class SyncProvidersTest {
int changed = dummyFedFactory.getChangedSyncCounter();
// Assert that after some period was DummyUserFederationProvider triggered
- PeriodicSyncManager periodicSyncManager = new PeriodicSyncManager();
- periodicSyncManager.bootstrap(sessionFactory, session.getProvider(TimerProvider.class));
+ UsersSyncManager usersSyncManager = new UsersSyncManager();
+ usersSyncManager.bootstrapPeriodic(sessionFactory, session.getProvider(TimerProvider.class));
sleep(1800);
// Cancel timer
- periodicSyncManager.removePeriodicSyncForProvider(session.getProvider(TimerProvider.class), dummyModel);
+ usersSyncManager.removePeriodicSyncForProvider(session.getProvider(TimerProvider.class), dummyModel);
// Assert that DummyUserFederationProviderFactory.syncChangedUsers was invoked
int newChanged = dummyFedFactory.getChangedSyncCounter();
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/TransactionsTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/TransactionsTest.java
new file mode 100644
index 0000000..c2c5da4
--- /dev/null
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/TransactionsTest.java
@@ -0,0 +1,32 @@
+package org.keycloak.testsuite.model;
+
+import org.junit.Assert;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.testsuite.rule.KeycloakRule;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class TransactionsTest {
+
+ @ClassRule
+ public static KeycloakRule kc = new KeycloakRule();
+
+ @Test
+ public void testTransactionActive() {
+ KeycloakSession session = kc.startSession();
+
+ Assert.assertTrue(session.getTransaction().isActive());
+ session.getTransaction().commit();
+ Assert.assertFalse(session.getTransaction().isActive());
+
+ session.getTransaction().begin();
+ Assert.assertTrue(session.getTransaction().isActive());
+ session.getTransaction().rollback();
+ Assert.assertFalse(session.getTransaction().isActive());
+
+ session.close();
+ }
+}