keycloak-uncached
Changes
core/src/main/java/org/keycloak/representations/idm/UserFederationProviderRepresentation.java 27(+27 -0)
examples/providers/federation-provider/src/main/java/org/keycloak/examples/federation/properties/ClasspathPropertiesFederationFactory.java 4(+3 -1)
model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/RealmAdapter.java 4(+2 -2)
model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserFederationProviderEntity.java 31(+31 -0)
testsuite/integration/src/main/java/org/keycloak/testutils/DummyUserFederationProviderFactory.java 23(+22 -1)
Details
diff --git a/core/src/main/java/org/keycloak/representations/idm/UserFederationProviderRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/UserFederationProviderRepresentation.java
index 19f1d0c..7229435 100755
--- a/core/src/main/java/org/keycloak/representations/idm/UserFederationProviderRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/UserFederationProviderRepresentation.java
@@ -12,6 +12,9 @@ public class UserFederationProviderRepresentation {
private String providerName;
private Map<String, String> config;
private int priority;
+ private int fullSyncPeriod;
+ private int changedSyncPeriod;
+ private int lastSync;
public String getId() {
return id;
@@ -54,6 +57,30 @@ public class UserFederationProviderRepresentation {
this.priority = priority;
}
+ public int getFullSyncPeriod() {
+ return fullSyncPeriod;
+ }
+
+ public void setFullSyncPeriod(int fullSyncPeriod) {
+ this.fullSyncPeriod = fullSyncPeriod;
+ }
+
+ public int getChangedSyncPeriod() {
+ return changedSyncPeriod;
+ }
+
+ public void setChangedSyncPeriod(int changedSyncPeriod) {
+ this.changedSyncPeriod = changedSyncPeriod;
+ }
+
+ public int getLastSync() {
+ return lastSync;
+ }
+
+ public void setLastSync(int lastSync) {
+ this.lastSync = lastSync;
+ }
+
@Override
public boolean equals(Object o) {
if (this == o) return true;
diff --git a/examples/providers/federation-provider/src/main/java/org/keycloak/examples/federation/properties/ClasspathPropertiesFederationFactory.java b/examples/providers/federation-provider/src/main/java/org/keycloak/examples/federation/properties/ClasspathPropertiesFederationFactory.java
index f079e8c..f06250a 100755
--- a/examples/providers/federation-provider/src/main/java/org/keycloak/examples/federation/properties/ClasspathPropertiesFederationFactory.java
+++ b/examples/providers/federation-provider/src/main/java/org/keycloak/examples/federation/properties/ClasspathPropertiesFederationFactory.java
@@ -12,6 +12,8 @@ import java.util.Properties;
*/
public class ClasspathPropertiesFederationFactory extends BasePropertiesFederationFactory {
+ public static final String PROVIDER_NAME = "classpath-properties";
+
@Override
protected BasePropertiesFederationProvider createProvider(KeycloakSession session, UserFederationProviderModel model, Properties props) {
return new ClasspathPropertiesFederationProvider(session, model, props);
@@ -30,6 +32,6 @@ public class ClasspathPropertiesFederationFactory extends BasePropertiesFederati
@Override
public String getId() {
- return "classpath-properties";
+ return PROVIDER_NAME;
}
}
diff --git a/model/api/src/main/java/org/keycloak/models/entities/UserFederationProviderEntity.java b/model/api/src/main/java/org/keycloak/models/entities/UserFederationProviderEntity.java
index 6d3916e..fe00465 100755
--- a/model/api/src/main/java/org/keycloak/models/entities/UserFederationProviderEntity.java
+++ b/model/api/src/main/java/org/keycloak/models/entities/UserFederationProviderEntity.java
@@ -12,6 +12,9 @@ public class UserFederationProviderEntity {
protected Map<String, String> config;
protected int priority;
protected String displayName;
+ private int fullSyncPeriod;
+ private int changedSyncPeriod;
+ private int lastSync;
public String getId() {
@@ -53,4 +56,28 @@ public class UserFederationProviderEntity {
public void setDisplayName(String displayName) {
this.displayName = displayName;
}
+
+ public int getFullSyncPeriod() {
+ return fullSyncPeriod;
+ }
+
+ public void setFullSyncPeriod(int fullSyncPeriod) {
+ this.fullSyncPeriod = fullSyncPeriod;
+ }
+
+ public int getChangedSyncPeriod() {
+ return changedSyncPeriod;
+ }
+
+ public void setChangedSyncPeriod(int changedSyncPeriod) {
+ this.changedSyncPeriod = changedSyncPeriod;
+ }
+
+ public int getLastSync() {
+ return lastSync;
+ }
+
+ public void setLastSync(int lastSync) {
+ this.lastSync = lastSync;
+ }
}
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 633756f..4ae280f 100755
--- a/model/api/src/main/java/org/keycloak/models/RealmModel.java
+++ b/model/api/src/main/java/org/keycloak/models/RealmModel.java
@@ -161,7 +161,7 @@ public interface RealmModel extends RoleContainerModel {
List<UserFederationProviderModel> getUserFederationProviders();
- UserFederationProviderModel addUserFederationProvider(String providerName, Map<String, String> config, int priority, String displayName);
+ UserFederationProviderModel addUserFederationProvider(String providerName, Map<String, String> config, int priority, String displayName, int fullSyncPeriod, int changedSyncPeriod, int lastSync);
void updateUserFederationProvider(UserFederationProviderModel provider);
void removeUserFederationProvider(UserFederationProviderModel provider);
void setUserFederationProviders(List<UserFederationProviderModel> providers);
diff --git a/model/api/src/main/java/org/keycloak/models/UserFederationProviderModel.java b/model/api/src/main/java/org/keycloak/models/UserFederationProviderModel.java
index 3f6c451..9c3bf1c 100755
--- a/model/api/src/main/java/org/keycloak/models/UserFederationProviderModel.java
+++ b/model/api/src/main/java/org/keycloak/models/UserFederationProviderModel.java
@@ -16,10 +16,13 @@ public class UserFederationProviderModel {
private Map<String, String> config = new HashMap<String, String>();
private int priority;
private String displayName;
+ private int fullSyncPeriod = -1; // In seconds. -1 means that periodic full sync is disabled
+ private int changedSyncPeriod = -1; // In seconds. -1 means that periodic changed sync is disabled
+ private int lastSync; // Date when last sync was done for this provider
public UserFederationProviderModel() {};
- public UserFederationProviderModel(String id, String providerName, Map<String, String> config, int priority, String displayName) {
+ public UserFederationProviderModel(String id, String providerName, Map<String, String> config, int priority, String displayName, int fullSyncPeriod, int changedSyncPeriod, int lastSync) {
this.id = id;
this.providerName = providerName;
if (config != null) {
@@ -27,6 +30,9 @@ public class UserFederationProviderModel {
}
this.priority = priority;
this.displayName = displayName;
+ this.fullSyncPeriod = fullSyncPeriod;
+ this.changedSyncPeriod = changedSyncPeriod;
+ this.lastSync = lastSync;
}
public String getId() {
@@ -64,4 +70,28 @@ public class UserFederationProviderModel {
public void setDisplayName(String displayName) {
this.displayName = displayName;
}
+
+ public int getFullSyncPeriod() {
+ return fullSyncPeriod;
+ }
+
+ public void setFullSyncPeriod(int fullSyncPeriod) {
+ this.fullSyncPeriod = fullSyncPeriod;
+ }
+
+ public int getChangedSyncPeriod() {
+ return changedSyncPeriod;
+ }
+
+ public void setChangedSyncPeriod(int changedSyncPeriod) {
+ this.changedSyncPeriod = changedSyncPeriod;
+ }
+
+ public int getLastSync() {
+ return lastSync;
+ }
+
+ public void setLastSync(int lastSync) {
+ this.lastSync = lastSync;
+ }
}
diff --git a/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java b/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
index ab3c865..4499465 100755
--- a/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
+++ b/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
@@ -262,6 +262,9 @@ public class ModelToRepresentation {
rep.setProviderName(model.getProviderName());
rep.setPriority(model.getPriority());
rep.setDisplayName(model.getDisplayName());
+ rep.setFullSyncPeriod(model.getFullSyncPeriod());
+ rep.setChangedSyncPeriod(model.getChangedSyncPeriod());
+ rep.setLastSync(model.getLastSync());
return rep;
}
}
diff --git a/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java b/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
index 66c8be9..c7d7c26 100755
--- a/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
+++ b/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
@@ -288,7 +288,8 @@ public class RepresentationToModel {
for (UserFederationProviderRepresentation representation : providers) {
UserFederationProviderModel model = new UserFederationProviderModel(representation.getId(), representation.getProviderName(),
- representation.getConfig(), representation.getPriority(), representation.getDisplayName());
+ representation.getConfig(), representation.getPriority(), representation.getDisplayName(),
+ representation.getFullSyncPeriod(), representation.getChangedSyncPeriod(), representation.getLastSync());
result.add(model);
}
return result;
diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/RealmAdapter.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/RealmAdapter.java
index 5268441..9fb8a1e 100755
--- a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/RealmAdapter.java
+++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/RealmAdapter.java
@@ -594,9 +594,9 @@ public class RealmAdapter implements RealmModel {
}
@Override
- public UserFederationProviderModel addUserFederationProvider(String providerName, Map<String, String> config, int priority, String displayName) {
+ public UserFederationProviderModel addUserFederationProvider(String providerName, Map<String, String> config, int priority, String displayName, int fullSyncPeriod, int changedSyncPeriod, int lastSync) {
getDelegateForUpdate();
- return updated.addUserFederationProvider(providerName, config, priority, displayName);
+ return updated.addUserFederationProvider(providerName, config, priority, displayName, fullSyncPeriod, changedSyncPeriod, lastSync);
}
@Override
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserFederationProviderEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserFederationProviderEntity.java
index aec144e..22370f3 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserFederationProviderEntity.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserFederationProviderEntity.java
@@ -42,6 +42,13 @@ public class UserFederationProviderEntity {
@Column(name="DISPLAY_NAME")
private String displayName;
+ @Column(name="FULL_SYNC_PERIOD")
+ private int fullSyncPeriod;
+ @Column(name="CHANGED_SYNC_PERIOD")
+ private int changedSyncPeriod;
+ @Column(name="LAST_SYNC")
+ private int lastSync;
+
public String getId() {
return id;
}
@@ -89,4 +96,28 @@ public class UserFederationProviderEntity {
public void setDisplayName(String displayName) {
this.displayName = displayName;
}
+
+ public int getFullSyncPeriod() {
+ return fullSyncPeriod;
+ }
+
+ public void setFullSyncPeriod(int fullSyncPeriod) {
+ this.fullSyncPeriod = fullSyncPeriod;
+ }
+
+ public int getChangedSyncPeriod() {
+ return changedSyncPeriod;
+ }
+
+ public void setChangedSyncPeriod(int changedSyncPeriod) {
+ this.changedSyncPeriod = changedSyncPeriod;
+ }
+
+ public int getLastSync() {
+ return lastSync;
+ }
+
+ public void setLastSync(int lastSync) {
+ this.lastSync = lastSync;
+ }
}
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 c53354e..4f0da3c 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
@@ -676,14 +676,15 @@ public class RealmAdapter implements RealmModel {
});
List<UserFederationProviderModel> result = new ArrayList<UserFederationProviderModel>();
for (UserFederationProviderEntity entity : copy) {
- result.add(new UserFederationProviderModel(entity.getId(), entity.getProviderName(), entity.getConfig(), entity.getPriority(), entity.getDisplayName()));
+ result.add(new UserFederationProviderModel(entity.getId(), entity.getProviderName(), entity.getConfig(), entity.getPriority(), entity.getDisplayName(),
+ entity.getFullSyncPeriod(), entity.getChangedSyncPeriod(), entity.getLastSync()));
}
return result;
}
@Override
- public UserFederationProviderModel addUserFederationProvider(String providerName, Map<String, String> config, int priority, String displayName) {
+ public UserFederationProviderModel addUserFederationProvider(String providerName, Map<String, String> config, int priority, String displayName, int fullSyncPeriod, int changedSyncPeriod, int lastSync) {
String id = KeycloakModelUtils.generateId();
UserFederationProviderEntity entity = new UserFederationProviderEntity();
entity.setId(id);
@@ -695,10 +696,13 @@ public class RealmAdapter implements RealmModel {
displayName = id;
}
entity.setDisplayName(displayName);
+ entity.setFullSyncPeriod(fullSyncPeriod);
+ entity.setChangedSyncPeriod(changedSyncPeriod);
+ entity.setLastSync(lastSync);
em.persist(entity);
realm.getUserFederationProviders().add(entity);
em.flush();
- return new UserFederationProviderModel(entity.getId(), providerName, config, priority, displayName);
+ return new UserFederationProviderModel(entity.getId(), providerName, config, priority, displayName, fullSyncPeriod, changedSyncPeriod, lastSync);
}
@Override
@@ -728,6 +732,9 @@ public class RealmAdapter implements RealmModel {
entity.setPriority(model.getPriority());
entity.setProviderName(model.getProviderName());
entity.setPriority(model.getPriority());
+ entity.setFullSyncPeriod(model.getFullSyncPeriod());
+ entity.setChangedSyncPeriod(model.getChangedSyncPeriod());
+ entity.setLastSync(model.getLastSync());
break;
}
}
@@ -750,13 +757,17 @@ public class RealmAdapter implements RealmModel {
if (displayName != null) {
entity.setDisplayName(model.getDisplayName());
}
+ entity.setFullSyncPeriod(model.getFullSyncPeriod());
+ entity.setChangedSyncPeriod(model.getChangedSyncPeriod());
+ entity.setLastSync(model.getLastSync());
found = true;
break;
}
}
if (found) continue;
- session.users().preRemove(this, new UserFederationProviderModel(entity.getId(), entity.getProviderName(), entity.getConfig(), entity.getPriority(), entity.getDisplayName()));
+ session.users().preRemove(this, new UserFederationProviderModel(entity.getId(), entity.getProviderName(), entity.getConfig(), entity.getPriority(), entity.getDisplayName(),
+ entity.getFullSyncPeriod(), entity.getChangedSyncPeriod(), entity.getLastSync()));
it.remove();
em.remove(entity);
}
@@ -786,6 +797,9 @@ public class RealmAdapter implements RealmModel {
displayName = entity.getId();
}
entity.setDisplayName(displayName);
+ entity.setFullSyncPeriod(model.getFullSyncPeriod());
+ entity.setChangedSyncPeriod(model.getChangedSyncPeriod());
+ entity.setLastSync(model.getLastSync());
em.persist(entity);
realm.getUserFederationProviders().add(entity);
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 f275dee..893f52e 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
@@ -757,7 +757,7 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
}
@Override
- public UserFederationProviderModel addUserFederationProvider(String providerName, Map<String, String> config, int priority, String displayName) {
+ public UserFederationProviderModel addUserFederationProvider(String providerName, Map<String, String> config, int priority, String displayName, int fullSyncPeriod, int changedSyncPeriod, int lastSync) {
UserFederationProviderEntity entity = new UserFederationProviderEntity();
entity.setId(KeycloakModelUtils.generateId());
entity.setPriority(priority);
@@ -767,10 +767,13 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
displayName = entity.getId();
}
entity.setDisplayName(displayName);
+ entity.setFullSyncPeriod(fullSyncPeriod);
+ entity.setChangedSyncPeriod(changedSyncPeriod);
+ entity.setLastSync(lastSync);
realm.getUserFederationProviders().add(entity);
updateRealm();
- return new UserFederationProviderModel(entity.getId(), providerName, config, priority, displayName);
+ return new UserFederationProviderModel(entity.getId(), providerName, config, priority, displayName, fullSyncPeriod, changedSyncPeriod, lastSync);
}
@Override
@@ -779,7 +782,8 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
while (it.hasNext()) {
UserFederationProviderEntity entity = it.next();
if (entity.getId().equals(provider.getId())) {
- session.users().preRemove(this, new UserFederationProviderModel(entity.getId(), entity.getProviderName(), entity.getConfig(), entity.getPriority(), entity.getDisplayName()));
+ session.users().preRemove(this, new UserFederationProviderModel(entity.getId(), entity.getProviderName(), entity.getConfig(), entity.getPriority(), entity.getDisplayName(),
+ entity.getFullSyncPeriod(), entity.getChangedSyncPeriod(), entity.getLastSync()));
it.remove();
}
}
@@ -799,6 +803,9 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
if (displayName != null) {
entity.setDisplayName(model.getDisplayName());
}
+ entity.setFullSyncPeriod(model.getFullSyncPeriod());
+ entity.setChangedSyncPeriod(model.getChangedSyncPeriod());
+ entity.setLastSync(model.getLastSync());
}
}
updateRealm();
@@ -822,7 +829,8 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
});
List<UserFederationProviderModel> result = new LinkedList<UserFederationProviderModel>();
for (UserFederationProviderEntity entity : copy) {
- result.add(new UserFederationProviderModel(entity.getId(), entity.getProviderName(), entity.getConfig(), entity.getPriority(), entity.getDisplayName()));
+ result.add(new UserFederationProviderModel(entity.getId(), entity.getProviderName(), entity.getConfig(), entity.getPriority(), entity.getDisplayName(),
+ entity.getFullSyncPeriod(), entity.getChangedSyncPeriod(), entity.getLastSync()));
}
return result;
@@ -843,6 +851,9 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
entity.setDisplayName(entity.getId());
}
entity.setDisplayName(displayName);
+ entity.setFullSyncPeriod(model.getFullSyncPeriod());
+ entity.setChangedSyncPeriod(model.getChangedSyncPeriod());
+ entity.setLastSync(model.getLastSync());
entities.add(entity);
}
diff --git a/services/src/main/java/org/keycloak/services/managers/PeriodicSyncManager.java b/services/src/main/java/org/keycloak/services/managers/PeriodicSyncManager.java
new file mode 100644
index 0000000..7a86529
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/managers/PeriodicSyncManager.java
@@ -0,0 +1,105 @@
+package org.keycloak.services.managers;
+
+import java.util.List;
+
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.models.KeycloakSessionTask;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserFederationProvider;
+import org.keycloak.models.UserFederationProviderFactory;
+import org.keycloak.models.UserFederationProviderModel;
+import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.timer.TimerProvider;
+import org.keycloak.util.Time;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class PeriodicSyncManager {
+
+ /**
+ * Check federationProviderModel of all realms and possibly start periodic sync for them
+ *
+ * @param sessionFactory
+ * @param timer
+ */
+ public void bootstrap(final KeycloakSessionFactory sessionFactory, final TimerProvider timer) {
+ KeycloakModelUtils.runJobInTransaction(sessionFactory, new KeycloakSessionTask() {
+
+ @Override
+ public void run(KeycloakSession session) {
+ List<RealmModel> realms = session.realms().getRealms();
+ for (final RealmModel realm : realms) {
+ List<UserFederationProviderModel> federationProviders = realm.getUserFederationProviders();
+ for (final UserFederationProviderModel fedProvider : federationProviders) {
+ startPeriodicSyncForProvider(sessionFactory, timer, fedProvider, realm.getId());
+ }
+ }
+ }
+ });
+ }
+
+ public void startPeriodicSyncForProvider(final KeycloakSessionFactory sessionFactory, TimerProvider timer, final UserFederationProviderModel fedProvider, final String realmId) {
+ final UserFederationProviderFactory fedProviderFactory = (UserFederationProviderFactory) sessionFactory.getProviderFactory(UserFederationProvider.class, fedProvider.getProviderName());
+
+ if (fedProvider.getFullSyncPeriod() > 0) {
+ // We want periodic full sync for this provider
+ timer.schedule(new Runnable() {
+
+ @Override
+ public void run() {
+ updateLastSyncInterval(sessionFactory, fedProvider, realmId);
+ fedProviderFactory.syncAllUsers(sessionFactory, realmId, fedProvider);
+ }
+
+ }, fedProvider.getFullSyncPeriod() * 1000, fedProvider.getId() + "-FULL");
+ }
+
+ if (fedProvider.getChangedSyncPeriod() > 0) {
+ // We want periodic sync of just changed users for this provider
+ timer.schedule(new Runnable() {
+
+ @Override
+ public void run() {
+ // See when we did last sync.
+ int oldLastSync = fedProvider.getLastSync();
+ updateLastSyncInterval(sessionFactory, fedProvider, realmId);
+ fedProviderFactory.syncChangedUsers(sessionFactory, realmId, fedProvider, Time.toDate(oldLastSync));
+ }
+
+ }, fedProvider.getChangedSyncPeriod() * 1000, fedProvider.getId() + "-CHANGED");
+
+ }
+ }
+
+ public void removePeriodicSyncForProvider(TimerProvider timer, final UserFederationProviderModel fedProvider) {
+ timer.cancelTask(fedProvider.getId() + "-FULL");
+ timer.cancelTask(fedProvider.getId() + "-CHANGED");
+ }
+
+ // Update interval of last sync for given UserFederationProviderModel. Do it in separate transaction
+ private void updateLastSyncInterval(final KeycloakSessionFactory sessionFactory, final UserFederationProviderModel fedProvider, final String realmId) {
+ KeycloakModelUtils.runJobInTransaction(sessionFactory, new KeycloakSessionTask() {
+
+ @Override
+ public void run(KeycloakSession session) {
+ RealmModel persistentRealm = session.realms().getRealm(realmId);
+ List<UserFederationProviderModel> persistentFedProviders = persistentRealm.getUserFederationProviders();
+ for (UserFederationProviderModel persistentFedProvider : persistentFedProviders) {
+ if (fedProvider.getId().equals(persistentFedProvider.getId())) {
+ // Update persistent provider in DB
+ int lastSync = Time.currentTime();
+ persistentFedProvider.setLastSync(lastSync);
+ persistentRealm.updateUserFederationProvider(persistentFedProvider);
+
+ // Update "cached" reference
+ fedProvider.setLastSync(lastSync);
+ }
+ }
+ }
+
+ });
+ }
+
+}
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 ae1e116..c8b613c 100755
--- a/services/src/main/java/org/keycloak/services/managers/RealmManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/RealmManager.java
@@ -12,12 +12,14 @@ import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RealmProvider;
import org.keycloak.models.RoleModel;
+import org.keycloak.models.UserFederationProviderModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionProvider;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.models.utils.RepresentationToModel;
import org.keycloak.representations.idm.RealmAuditRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.timer.TimerProvider;
import java.util.Collections;
import java.util.HashSet;
@@ -127,6 +129,8 @@ public class RealmManager {
}
public boolean removeRealm(RealmModel realm) {
+ List<UserFederationProviderModel> federationProviders = realm.getUserFederationProviders();
+
boolean removed = model.removeRealm(realm.getId());
if (removed) {
new ApplicationManager(this).removeApplication(getKeycloakAdminstrationRealm(), realm.getMasterAdminApp());
@@ -135,6 +139,12 @@ public class RealmManager {
if (sessions != null) {
sessions.onRealmRemoved(realm);
}
+
+ // Remove all periodic syncs for configured federation providers
+ PeriodicSyncManager periodicSyncManager = new PeriodicSyncManager();
+ for (final UserFederationProviderModel fedProvider : federationProviders) {
+ periodicSyncManager.removePeriodicSyncForProvider(session.getProvider(TimerProvider.class), fedProvider);
+ }
}
return removed;
}
@@ -202,6 +212,13 @@ public class RealmManager {
public void importRealm(RealmRepresentation rep, RealmModel newRealm) {
RepresentationToModel.importRealm(session, rep, newRealm);
+
+ // Refresh periodic sync tasks for configured federationProviders
+ List<UserFederationProviderModel> federationProviders = newRealm.getUserFederationProviders();
+ PeriodicSyncManager periodicSyncManager = new PeriodicSyncManager();
+ for (final UserFederationProviderModel fedProvider : federationProviders) {
+ periodicSyncManager.startPeriodicSyncForProvider(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 9344d28..b6af7a4 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
@@ -12,6 +12,7 @@ import org.keycloak.models.ApplicationModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ModelDuplicateException;
import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserFederationProviderModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.models.cache.CacheRealmProvider;
import org.keycloak.models.cache.CacheUserProvider;
@@ -21,10 +22,12 @@ 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.RealmManager;
import org.keycloak.services.managers.ResourceAdminManager;
import org.keycloak.services.managers.TokenManager;
import org.keycloak.services.resources.flows.Flows;
+import org.keycloak.timer.TimerProvider;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
@@ -160,6 +163,13 @@ public class RealmAdminResource {
cache.setEnabled(rep.isUserCacheEnabled());
}
+ // Refresh periodic sync tasks for configured federationProviders
+ List<UserFederationProviderModel> federationProviders = realm.getUserFederationProviders();
+ PeriodicSyncManager periodicSyncManager = new PeriodicSyncManager();
+ for (final UserFederationProviderModel fedProvider : federationProviders) {
+ periodicSyncManager.startPeriodicSyncForProvider(session.getKeycloakSessionFactory(), session.getProvider(TimerProvider.class), fedProvider, realm.getId());
+ }
+
return Response.noContent().build();
} catch (ModelDuplicateException e) {
return Flows.errors().exists("Realm " + rep.getRealm() + " already exists");
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 c156c4b..6039ccb 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,6 +12,8 @@ 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.timer.TimerProvider;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
@@ -116,7 +118,10 @@ public class UserFederationResource {
if (displayName != null && displayName.trim().equals("")) {
displayName = null;
}
- UserFederationProviderModel model = realm.addUserFederationProvider(rep.getProviderName(), rep.getConfig(), rep.getPriority(), displayName);
+ 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());
+
return Response.created(uriInfo.getAbsolutePathBuilder().path(model.getId()).build()).build();
}
@@ -136,8 +141,10 @@ public class UserFederationResource {
if (displayName != null && displayName.trim().equals("")) {
displayName = null;
}
- UserFederationProviderModel model = new UserFederationProviderModel(id, rep.getProviderName(), rep.getConfig(), rep.getPriority(), displayName);
+ 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());
}
/**
@@ -170,9 +177,9 @@ public class UserFederationResource {
public void deleteProviderInstance(@PathParam("id") String id) {
logger.info("deleteProvider");
auth.requireManage();
- UserFederationProviderModel model = new UserFederationProviderModel(id, null, null, -1, null);
+ UserFederationProviderModel model = new UserFederationProviderModel(id, null, null, -1, null, -1, -1, 0);
realm.removeUserFederationProvider(model);
-
+ new PeriodicSyncManager().removePeriodicSyncForProvider(session.getProvider(TimerProvider.class), model);
}
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 3426336..ca8db44 100755
--- a/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java
+++ b/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java
@@ -84,8 +84,8 @@ public class KeycloakApplication extends Application {
setupDefaultRealm(context.getContextPath());
- setupScheduledTasks(sessionFactory);
importRealms(context);
+ setupScheduledTasks(sessionFactory);
}
public String getContextPath() {
diff --git a/testsuite/integration/src/main/java/org/keycloak/testutils/DummyUserFederationProviderFactory.java b/testsuite/integration/src/main/java/org/keycloak/testutils/DummyUserFederationProviderFactory.java
index d20ef87..2809544 100755
--- a/testsuite/integration/src/main/java/org/keycloak/testutils/DummyUserFederationProviderFactory.java
+++ b/testsuite/integration/src/main/java/org/keycloak/testutils/DummyUserFederationProviderFactory.java
@@ -1,5 +1,6 @@
package org.keycloak.testutils;
+import org.jboss.logging.Logger;
import org.keycloak.Config;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
@@ -9,12 +10,20 @@ import org.keycloak.models.UserFederationProviderModel;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class DummyUserFederationProviderFactory implements UserFederationProviderFactory {
+
+ private static final Logger logger = Logger.getLogger(DummyUserFederationProviderFactory.class);
+ public static final String PROVIDER_NAME = "dummy";
+
+ private AtomicInteger fullSyncCounter = new AtomicInteger();
+ private AtomicInteger changedSyncCounter = new AtomicInteger();
+
@Override
public UserFederationProvider getInstance(KeycloakSession session, UserFederationProviderModel model) {
return new DummyUserFederationProvider();
@@ -44,14 +53,26 @@ public class DummyUserFederationProviderFactory implements UserFederationProvide
@Override
public String getId() {
- return "dummy";
+ return PROVIDER_NAME;
}
@Override
public void syncAllUsers(KeycloakSessionFactory sessionFactory, String realmId, UserFederationProviderModel model) {
+ logger.info("syncAllUsers invoked");
+ fullSyncCounter.incrementAndGet();
}
@Override
public void syncChangedUsers(KeycloakSessionFactory sessionFactory, String realmId, UserFederationProviderModel model, Date lastSync) {
+ logger.info("syncChangedUsers invoked");
+ changedSyncCounter.incrementAndGet();
+ }
+
+ public int getFullSyncCounter() {
+ return fullSyncCounter.get();
+ }
+
+ public int getChangedSyncCounter() {
+ return changedSyncCounter.get();
}
}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/FederationProvidersIntegrationTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/FederationProvidersIntegrationTest.java
index 1e8e9ce..99f6558 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/FederationProvidersIntegrationTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/FederationProvidersIntegrationTest.java
@@ -60,7 +60,7 @@ public class FederationProvidersIntegrationTest {
ldapConfig.put(LDAPFederationProvider.SYNC_REGISTRATIONS, "true");
ldapConfig.put(LDAPFederationProvider.EDIT_MODE, UserFederationProvider.EditMode.WRITABLE.toString());
- ldapModel = appRealm.addUserFederationProvider(LDAPFederationProviderFactory.PROVIDER_NAME, ldapConfig, 0, "test-ldap");
+ ldapModel = appRealm.addUserFederationProvider(LDAPFederationProviderFactory.PROVIDER_NAME, ldapConfig, 0, "test-ldap", -1, -1, 0);
// Delete all LDAP users and add some new for testing
PartitionManager partitionManager = getPartitionManager(manager.getSession(), ldapModel);
@@ -166,7 +166,7 @@ public class FederationProvidersIntegrationTest {
RealmManager manager = new RealmManager(session);
RealmModel appRealm = manager.getRealm("test");
- ldapModel = appRealm.addUserFederationProvider(ldapModel.getProviderName(), ldapModel.getConfig(), ldapModel.getPriority(), ldapModel.getDisplayName());
+ ldapModel = appRealm.addUserFederationProvider(ldapModel.getProviderName(), ldapModel.getConfig(), ldapModel.getPriority(), ldapModel.getDisplayName(), -1, -1, 0);
} finally {
keycloakRule.stopSession(session, true);
}
@@ -233,7 +233,8 @@ public class FederationProvidersIntegrationTest {
try {
RealmModel appRealm = session.realms().getRealmByName("test");
- UserFederationProviderModel model = new UserFederationProviderModel(ldapModel.getId(), ldapModel.getProviderName(), ldapModel.getConfig(), ldapModel.getPriority(), ldapModel.getDisplayName());
+ UserFederationProviderModel model = new UserFederationProviderModel(ldapModel.getId(), ldapModel.getProviderName(), ldapModel.getConfig(),
+ ldapModel.getPriority(), ldapModel.getDisplayName(), -1, -1, 0);
model.getConfig().put(LDAPFederationProvider.EDIT_MODE, UserFederationProvider.EditMode.READ_ONLY.toString());
appRealm.updateUserFederationProvider(model);
UserModel user = session.users().getUserByUsername("johnkeycloak", appRealm);
@@ -284,7 +285,8 @@ public class FederationProvidersIntegrationTest {
try {
RealmModel appRealm = session.realms().getRealmByName("test");
- UserFederationProviderModel model = new UserFederationProviderModel(ldapModel.getId(), ldapModel.getProviderName(), ldapModel.getConfig(), ldapModel.getPriority(), ldapModel.getDisplayName());
+ UserFederationProviderModel model = new UserFederationProviderModel(ldapModel.getId(), ldapModel.getProviderName(), ldapModel.getConfig(), ldapModel.getPriority(),
+ ldapModel.getDisplayName(), -1, -1, 0);
model.getConfig().put(LDAPFederationProvider.EDIT_MODE, UserFederationProvider.EditMode.UNSYNCED.toString());
appRealm.updateUserFederationProvider(model);
UserModel user = session.users().getUserByUsername("johnkeycloak", appRealm);
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 8105769..21ac290 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
@@ -1,6 +1,7 @@
package org.keycloak.testsuite.forms;
import java.util.Date;
+import java.util.HashMap;
import java.util.Map;
import org.junit.Assert;
@@ -10,6 +11,7 @@ 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;
@@ -21,10 +23,14 @@ 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.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;
import org.picketlink.idm.PartitionManager;
import org.picketlink.idm.model.basic.User;
@@ -37,6 +43,7 @@ public class SyncProvidersTest {
private static LDAPRule ldapRule = new LDAPRule();
private static UserFederationProviderModel ldapModel = null;
+ private static UserFederationProviderModel dummyModel = null;
private static KeycloakRule keycloakRule = new KeycloakRule(new KeycloakRule.KeycloakSetup() {
@@ -47,7 +54,8 @@ public class SyncProvidersTest {
ldapConfig.put(LDAPFederationProvider.SYNC_REGISTRATIONS, "false");
ldapConfig.put(LDAPFederationProvider.EDIT_MODE, UserFederationProvider.EditMode.UNSYNCED.toString());
- ldapModel = appRealm.addUserFederationProvider(LDAPFederationProviderFactory.PROVIDER_NAME, ldapConfig, 0, "test-ldap");
+ ldapModel = appRealm.addUserFederationProvider(LDAPFederationProviderFactory.PROVIDER_NAME, ldapConfig, 0, "test-ldap",
+ -1, -1, 0);
// Delete all LDAP users and add 5 new users for testing
PartitionManager partitionManager = FederationProvidersIntegrationTest.getPartitionManager(manager.getSession(), ldapModel);
@@ -65,9 +73,7 @@ public class SyncProvidersTest {
LDAPUtils.updatePassword(partitionManager, user5, "password5");
// Add properties provider
-// Map<String,String> filePropertiesConfig = new HashMap<String, String>();
-// filePropertiesConfig.put("path", );
-// appRealm.addUserFederationProvider(FilePropertiesFederationFactory.PROVIDER_NAME, filePropertiesConfig, 1, "test-fileProps");
+ dummyModel = appRealm.addUserFederationProvider(DummyUserFederationProviderFactory.PROVIDER_NAME, new HashMap<String, String>(), 1, "test-dummy", -1, 1, 0);
}
});
@@ -99,11 +105,7 @@ public class SyncProvidersTest {
assertUserImported(userProvider, testRealm, "user5", "User5FN", "User5LN", "user5@email.org");
// wait a bit
- try {
- Thread.sleep(1000);
- } catch (InterruptedException ie) {
- throw new RuntimeException(ie);
- }
+ sleep(1000);
Date beforeLDAPUpdate = new Date();
// Add user to LDAP and update 'user5' in LDAP
@@ -135,6 +137,45 @@ public class SyncProvidersTest {
}
}
+ @Test
+ public void testPeriodicSync() {
+ KeycloakSession session = keycloakRule.startSession();
+ try {
+ KeycloakSessionFactory sessionFactory = session.getKeycloakSessionFactory();
+ DummyUserFederationProviderFactory dummyFedFactory = (DummyUserFederationProviderFactory)sessionFactory.getProviderFactory(UserFederationProvider.class, DummyUserFederationProviderFactory.PROVIDER_NAME);
+ int full = dummyFedFactory.getFullSyncCounter();
+ int changed = dummyFedFactory.getChangedSyncCounter();
+
+ // Assert that after some period was DummyUserFederationProvider triggered
+ PeriodicSyncManager periodicSyncManager = new PeriodicSyncManager();
+ periodicSyncManager.bootstrap(sessionFactory, session.getProvider(TimerProvider.class));
+ sleep(1800);
+
+ // Cancel timer
+ periodicSyncManager.removePeriodicSyncForProvider(session.getProvider(TimerProvider.class), dummyModel);
+
+ // Assert that DummyUserFederationProviderFactory.syncChangedUsers was invoked
+ int newChanged = dummyFedFactory.getChangedSyncCounter();
+ Assert.assertEquals(full, dummyFedFactory.getFullSyncCounter());
+ Assert.assertTrue(newChanged > changed);
+
+ // Assert that dummy provider won't be invoked anymore
+ sleep(1800);
+ Assert.assertEquals(full, dummyFedFactory.getFullSyncCounter());
+ Assert.assertEquals(newChanged, dummyFedFactory.getChangedSyncCounter());
+ } finally {
+ keycloakRule.stopSession(session, false);
+ }
+ }
+
+ private void sleep(int time) {
+ try {
+ Thread.sleep(time);
+ } catch (InterruptedException ie) {
+ throw new RuntimeException(ie);
+ }
+ }
+
private void assertUserImported(UserProvider userProvider, RealmModel realm, String username, String expectedFirstName, String expectedLastName, String expectedEmail) {
UserModel user = userProvider.getUserByUsername(username, realm);
Assert.assertNotNull(user);