commit
c4d5a4138c
45 changed files with 906 additions and 138 deletions
|
@ -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;
|
||||
|
|
|
@ -2,12 +2,19 @@ package org.keycloak.examples.federation.properties;
|
|||
|
||||
import org.keycloak.Config;
|
||||
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.UserModel;
|
||||
import org.keycloak.models.UserProvider;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
@ -84,4 +91,44 @@ public abstract class BasePropertiesFederationFactory implements UserFederationP
|
|||
public void close() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void syncAllUsers(KeycloakSessionFactory sessionFactory, final String realmId, final UserFederationProviderModel model) {
|
||||
KeycloakModelUtils.runJobInTransaction(sessionFactory, new KeycloakSessionTask() {
|
||||
|
||||
@Override
|
||||
public void run(KeycloakSession session) {
|
||||
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);
|
||||
|
||||
if (localUser == null) {
|
||||
// New user, let's import him
|
||||
federationProvider.getUserByUsername(realm, username);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,8 @@ import java.util.Properties;
|
|||
*/
|
||||
public class FilePropertiesFederationFactory extends BasePropertiesFederationFactory {
|
||||
|
||||
public static final String PROVIDER_NAME = "file-properties";
|
||||
|
||||
@Override
|
||||
protected BasePropertiesFederationProvider createProvider(KeycloakSession session, UserFederationProviderModel model, Properties props) {
|
||||
return new FilePropertiesFederationProvider(session, props, model);
|
||||
|
@ -35,6 +37,6 @@ public class FilePropertiesFederationFactory extends BasePropertiesFederationFac
|
|||
*/
|
||||
@Override
|
||||
public String getId() {
|
||||
return "file-properties";
|
||||
return PROVIDER_NAME;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
package org.keycloak.exportimport.util;
|
||||
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Task to be executed inside transaction
|
||||
*
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public interface ExportImportJob {
|
||||
|
||||
public void run(KeycloakSession session) throws IOException;
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
package org.keycloak.exportimport.util;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionTask;
|
||||
|
||||
/**
|
||||
* Just to wrap {@link IOException}
|
||||
*
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public abstract class ExportImportSessionTask implements KeycloakSessionTask {
|
||||
|
||||
@Override
|
||||
public void run(KeycloakSession session) {
|
||||
try {
|
||||
runExportImportTask(session);
|
||||
} catch (IOException ioe) {
|
||||
throw new RuntimeException("Error during export/import: " + ioe.getMessage(), ioe);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void runExportImportTask(KeycloakSession session) throws IOException;
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
package org.keycloak.exportimport.util;
|
||||
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.models.KeycloakTransaction;
|
||||
import org.keycloak.models.RealmModel;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class ExportImportUtils {
|
||||
|
||||
/**
|
||||
* Wrap given runnable job into KeycloakTransaction.
|
||||
*
|
||||
* @param factory
|
||||
* @param job
|
||||
*/
|
||||
public static void runJobInTransaction(KeycloakSessionFactory factory, ExportImportJob job) throws IOException {
|
||||
KeycloakSession session = factory.create();
|
||||
KeycloakTransaction tx = session.getTransaction();
|
||||
try {
|
||||
tx.begin();
|
||||
job.run(session);
|
||||
|
||||
if (tx.isActive()) {
|
||||
if (tx.getRollbackOnly()) {
|
||||
tx.rollback();
|
||||
} else {
|
||||
tx.commit();
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (tx.isActive()) {
|
||||
tx.rollback();
|
||||
}
|
||||
session.close();
|
||||
}
|
||||
}
|
||||
|
||||
public static String getMasterRealmAdminApplicationName(RealmModel realm) {
|
||||
return realm.getName() + "-realm";
|
||||
}
|
||||
}
|
|
@ -77,7 +77,7 @@ public class ImportUtils {
|
|||
// We just imported master realm. All 'masterAdminApps' need to be refreshed
|
||||
RealmModel adminRealm = realm;
|
||||
for (RealmModel currentRealm : model.getRealms()) {
|
||||
ApplicationModel masterApp = adminRealm.getApplicationByName(ExportImportUtils.getMasterRealmAdminApplicationName(currentRealm));
|
||||
ApplicationModel masterApp = adminRealm.getApplicationByName(KeycloakModelUtils.getMasterRealmAdminApplicationName(currentRealm));
|
||||
if (masterApp != null) {
|
||||
currentRealm.setMasterAdminApp(masterApp);
|
||||
} else {
|
||||
|
@ -87,7 +87,7 @@ public class ImportUtils {
|
|||
} else {
|
||||
// Need to refresh masterApp for current realm
|
||||
RealmModel adminRealm = model.getRealm(adminRealmId);
|
||||
ApplicationModel masterApp = adminRealm.getApplicationByName(ExportImportUtils.getMasterRealmAdminApplicationName(realm));
|
||||
ApplicationModel masterApp = adminRealm.getApplicationByName(KeycloakModelUtils.getMasterRealmAdminApplicationName(realm));
|
||||
if (masterApp != null) {
|
||||
realm.setMasterAdminApp(masterApp);
|
||||
} else {
|
||||
|
@ -113,7 +113,7 @@ public class ImportUtils {
|
|||
adminRole = adminRealm.getRole(AdminRoles.ADMIN);
|
||||
}
|
||||
|
||||
ApplicationModel realmAdminApp = KeycloakModelUtils.createApplication(adminRealm, ExportImportUtils.getMasterRealmAdminApplicationName(realm));
|
||||
ApplicationModel realmAdminApp = KeycloakModelUtils.createApplication(adminRealm, KeycloakModelUtils.getMasterRealmAdminApplicationName(realm));
|
||||
realmAdminApp.setBearerOnly(true);
|
||||
realm.setMasterAdminApp(realmAdminApp);
|
||||
|
||||
|
|
|
@ -6,8 +6,10 @@ import org.keycloak.exportimport.ExportProvider;
|
|||
import org.keycloak.exportimport.UsersExportStrategy;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.models.KeycloakSessionTask;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -24,7 +26,7 @@ public abstract class MultipleStepsExportProvider implements ExportProvider {
|
|||
public void exportModel(KeycloakSessionFactory factory) throws IOException {
|
||||
final RealmsHolder holder = new RealmsHolder();
|
||||
|
||||
ExportImportUtils.runJobInTransaction(factory, new ExportImportJob() {
|
||||
KeycloakModelUtils.runJobInTransaction(factory, new KeycloakSessionTask() {
|
||||
|
||||
@Override
|
||||
public void run(KeycloakSession session) {
|
||||
|
@ -46,10 +48,10 @@ public abstract class MultipleStepsExportProvider implements ExportProvider {
|
|||
final UsersHolder usersHolder = new UsersHolder();
|
||||
final boolean exportUsersIntoRealmFile = usersExportStrategy == UsersExportStrategy.REALM_FILE;
|
||||
|
||||
ExportImportUtils.runJobInTransaction(factory, new ExportImportJob() {
|
||||
KeycloakModelUtils.runJobInTransaction(factory, new ExportImportSessionTask() {
|
||||
|
||||
@Override
|
||||
public void run(KeycloakSession session) throws IOException {
|
||||
protected void runExportImportTask(KeycloakSession session) throws IOException {
|
||||
RealmModel realm = session.realms().getRealmByName(realmName);
|
||||
RealmRepresentation rep = ExportUtils.exportRealm(session, realm, exportUsersIntoRealmFile);
|
||||
writeRealm(realmName + "-realm.json", rep);
|
||||
|
@ -77,10 +79,10 @@ public abstract class MultipleStepsExportProvider implements ExportProvider {
|
|||
usersHolder.currentPageEnd = usersHolder.totalCount;
|
||||
}
|
||||
|
||||
ExportImportUtils.runJobInTransaction(factory, new ExportImportJob() {
|
||||
KeycloakModelUtils.runJobInTransaction(factory, new ExportImportSessionTask() {
|
||||
|
||||
@Override
|
||||
public void run(KeycloakSession session) throws IOException {
|
||||
protected void runExportImportTask(KeycloakSession session) throws IOException {
|
||||
RealmModel realm = session.realms().getRealmByName(realmName);
|
||||
usersHolder.users = session.users().getUsers(realm, usersHolder.currentPageStart, usersHolder.currentPageEnd - usersHolder.currentPageStart);
|
||||
|
||||
|
|
|
@ -4,11 +4,12 @@ import org.jboss.logging.Logger;
|
|||
import org.keycloak.Config;
|
||||
import org.keycloak.exportimport.ImportProvider;
|
||||
import org.keycloak.exportimport.Strategy;
|
||||
import org.keycloak.exportimport.util.ExportImportJob;
|
||||
import org.keycloak.exportimport.util.ExportImportUtils;
|
||||
import org.keycloak.exportimport.util.ExportImportSessionTask;
|
||||
import org.keycloak.exportimport.util.ImportUtils;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.models.KeycloakSessionTask;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
||||
|
@ -91,10 +92,10 @@ public class DirImportProvider implements ImportProvider {
|
|||
FileInputStream is = new FileInputStream(realmFile);
|
||||
final RealmRepresentation realmRep = JsonSerialization.readValue(is, RealmRepresentation.class);
|
||||
|
||||
ExportImportUtils.runJobInTransaction(factory, new ExportImportJob() {
|
||||
KeycloakModelUtils.runJobInTransaction(factory, new ExportImportSessionTask() {
|
||||
|
||||
@Override
|
||||
public void run(KeycloakSession session) throws IOException {
|
||||
public void runExportImportTask(KeycloakSession session) throws IOException {
|
||||
ImportUtils.importRealm(session, realmRep, strategy);
|
||||
}
|
||||
|
||||
|
@ -103,10 +104,10 @@ public class DirImportProvider implements ImportProvider {
|
|||
// Import users
|
||||
for (File userFile : userFiles) {
|
||||
final FileInputStream fis = new FileInputStream(userFile);
|
||||
ExportImportUtils.runJobInTransaction(factory, new ExportImportJob() {
|
||||
KeycloakModelUtils.runJobInTransaction(factory, new ExportImportSessionTask() {
|
||||
|
||||
@Override
|
||||
public void run(KeycloakSession session) throws IOException {
|
||||
protected void runExportImportTask(KeycloakSession session) throws IOException {
|
||||
ImportUtils.importUsersFromStream(session, realmName, JsonSerialization.mapper, fis);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -3,12 +3,13 @@ package org.keycloak.exportimport.singlefile;
|
|||
import org.codehaus.jackson.map.ObjectMapper;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.exportimport.ExportProvider;
|
||||
import org.keycloak.exportimport.util.ExportImportJob;
|
||||
import org.keycloak.exportimport.util.ExportImportUtils;
|
||||
import org.keycloak.exportimport.util.ExportImportSessionTask;
|
||||
import org.keycloak.exportimport.util.ExportUtils;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.models.KeycloakSessionTask;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
||||
|
@ -38,10 +39,10 @@ public class SingleFileExportProvider implements ExportProvider {
|
|||
@Override
|
||||
public void exportModel(KeycloakSessionFactory factory) throws IOException {
|
||||
logger.infof("Exporting model into file %s", this.file.getAbsolutePath());
|
||||
ExportImportUtils.runJobInTransaction(factory, new ExportImportJob() {
|
||||
KeycloakModelUtils.runJobInTransaction(factory, new ExportImportSessionTask() {
|
||||
|
||||
@Override
|
||||
public void run(KeycloakSession session) throws IOException {
|
||||
protected void runExportImportTask(KeycloakSession session) throws IOException {
|
||||
List<RealmModel> realms = session.realms().getRealms();
|
||||
List<RealmRepresentation> reps = new ArrayList<RealmRepresentation>();
|
||||
for (RealmModel realm : realms) {
|
||||
|
@ -58,10 +59,10 @@ public class SingleFileExportProvider implements ExportProvider {
|
|||
@Override
|
||||
public void exportRealm(KeycloakSessionFactory factory, final String realmName) throws IOException {
|
||||
logger.infof("Exporting realm '%s' into file %s", realmName, this.file.getAbsolutePath());
|
||||
ExportImportUtils.runJobInTransaction(factory, new ExportImportJob() {
|
||||
KeycloakModelUtils.runJobInTransaction(factory, new ExportImportSessionTask() {
|
||||
|
||||
@Override
|
||||
public void run(KeycloakSession session) throws IOException {
|
||||
protected void runExportImportTask(KeycloakSession session) throws IOException {
|
||||
RealmModel realm = session.realms().getRealmByName(realmName);
|
||||
RealmRepresentation realmRep = ExportUtils.exportRealm(session, realm, true);
|
||||
writeToFile(realmRep);
|
||||
|
|
|
@ -3,16 +3,15 @@ package org.keycloak.exportimport.singlefile;
|
|||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.exportimport.ImportProvider;
|
||||
import org.keycloak.exportimport.Strategy;
|
||||
import org.keycloak.exportimport.util.ExportImportJob;
|
||||
import org.keycloak.exportimport.util.ExportImportUtils;
|
||||
import org.keycloak.exportimport.util.ImportUtils;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import org.keycloak.exportimport.util.ExportImportSessionTask;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
|
@ -30,10 +29,10 @@ public class SingleFileImportProvider implements ImportProvider {
|
|||
@Override
|
||||
public void importModel(KeycloakSessionFactory factory, final Strategy strategy) throws IOException {
|
||||
logger.infof("Full importing from file %s", this.file.getAbsolutePath());
|
||||
ExportImportUtils.runJobInTransaction(factory, new ExportImportJob() {
|
||||
KeycloakModelUtils.runJobInTransaction(factory, new ExportImportSessionTask() {
|
||||
|
||||
@Override
|
||||
public void run(KeycloakSession session) throws IOException {
|
||||
protected void runExportImportTask(KeycloakSession session) throws IOException {
|
||||
FileInputStream is = new FileInputStream(file);
|
||||
ImportUtils.importFromStream(session, JsonSerialization.mapper, is, strategy);
|
||||
}
|
||||
|
|
|
@ -8,11 +8,12 @@ import org.jboss.logging.Logger;
|
|||
import org.keycloak.Config;
|
||||
import org.keycloak.exportimport.ImportProvider;
|
||||
import org.keycloak.exportimport.Strategy;
|
||||
import org.keycloak.exportimport.util.ExportImportJob;
|
||||
import org.keycloak.exportimport.util.ExportImportUtils;
|
||||
import org.keycloak.exportimport.util.ExportImportSessionTask;
|
||||
import org.keycloak.exportimport.util.ImportUtils;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.models.KeycloakSessionTask;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
||||
|
@ -81,10 +82,10 @@ public class ZipImportProvider implements ImportProvider {
|
|||
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
|
||||
final RealmRepresentation realmRep = JsonSerialization.mapper.readValue(bis, RealmRepresentation.class);
|
||||
|
||||
ExportImportUtils.runJobInTransaction(factory, new ExportImportJob() {
|
||||
KeycloakModelUtils.runJobInTransaction(factory, new ExportImportSessionTask() {
|
||||
|
||||
@Override
|
||||
public void run(KeycloakSession session) throws IOException {
|
||||
protected void runExportImportTask(KeycloakSession session) throws IOException {
|
||||
ImportUtils.importRealm(session, realmRep, strategy);
|
||||
}
|
||||
|
||||
|
@ -99,10 +100,10 @@ public class ZipImportProvider implements ImportProvider {
|
|||
this.decrypter.extractEntry(entry, bos, this.password);
|
||||
final ByteArrayInputStream bis2 = new ByteArrayInputStream(bos.toByteArray());
|
||||
|
||||
ExportImportUtils.runJobInTransaction(factory, new ExportImportJob() {
|
||||
KeycloakModelUtils.runJobInTransaction(factory, new ExportImportSessionTask() {
|
||||
|
||||
@Override
|
||||
public void run(KeycloakSession session) throws IOException {
|
||||
protected void runExportImportTask(KeycloakSession session) throws IOException {
|
||||
ImportUtils.importUsersFromStream(session, realmName, JsonSerialization.mapper, bis2);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -292,4 +292,29 @@ public class LDAPFederationProvider implements UserFederationProvider {
|
|||
public void close() {
|
||||
//To change body of implemented methods use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
protected void importPicketlinkUsers(RealmModel realm, List<User> users, UserFederationProviderModel fedModel) {
|
||||
for (User picketlinkUser : users) {
|
||||
String username = picketlinkUser.getLoginName();
|
||||
UserModel currentUser = session.userStorage().getUserByUsername(username, realm);
|
||||
|
||||
if (currentUser == null) {
|
||||
// Add new user to Keycloak
|
||||
importUserFromPicketlink(realm, picketlinkUser);
|
||||
logger.infof("Added new user from LDAP: " + username);
|
||||
} else {
|
||||
if ((fedModel.getId().equals(currentUser.getFederationLink())) && (picketlinkUser.getId().equals(currentUser.getAttribute(LDAPFederationProvider.LDAP_ID)))) {
|
||||
// Update keycloak user
|
||||
String email = (picketlinkUser.getEmail() != null && picketlinkUser.getEmail().trim().length() > 0) ? picketlinkUser.getEmail() : null;
|
||||
currentUser.setEmail(email);
|
||||
currentUser.setFirstName(picketlinkUser.getFirstName());
|
||||
currentUser.setLastName(picketlinkUser.getLastName());
|
||||
logger.infof("Updated user from LDAP: " + currentUser.getUsername());
|
||||
} else {
|
||||
// TODO: We have local user of same username like LDAP user, but not linked. What to do? Delete him and import again?
|
||||
throw new IllegalStateException("User " + username + " has invalid LDAP ID or doesn't have federation link");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +1,26 @@
|
|||
package org.keycloak.federation.ldap;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.UserFederationProvider;
|
||||
import org.keycloak.models.UserFederationProviderFactory;
|
||||
import org.keycloak.models.UserFederationProviderModel;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.models.KeycloakSessionTask;
|
||||
import org.keycloak.models.LDAPConstants;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.picketlink.PartitionManagerProvider;
|
||||
import org.picketlink.idm.IdentityManager;
|
||||
import org.picketlink.idm.PartitionManager;
|
||||
import org.picketlink.idm.model.IdentityType;
|
||||
import org.picketlink.idm.model.basic.User;
|
||||
import org.picketlink.idm.query.IdentityQuery;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
|
@ -17,6 +29,7 @@ import java.util.Set;
|
|||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class LDAPFederationProviderFactory implements UserFederationProviderFactory {
|
||||
private static final Logger logger = Logger.getLogger(LDAPFederationProviderFactory.class);
|
||||
public static final String PROVIDER_NAME = "ldap";
|
||||
|
||||
@Override
|
||||
|
@ -25,7 +38,7 @@ public class LDAPFederationProviderFactory implements UserFederationProviderFact
|
|||
}
|
||||
|
||||
@Override
|
||||
public UserFederationProvider getInstance(KeycloakSession session, UserFederationProviderModel model) {
|
||||
public LDAPFederationProvider getInstance(KeycloakSession session, UserFederationProviderModel model) {
|
||||
PartitionManagerProvider idmProvider = session.getProvider(PartitionManagerProvider.class);
|
||||
PartitionManager partition = idmProvider.getPartitionManager(model);
|
||||
return new LDAPFederationProvider(session, model, partition);
|
||||
|
@ -49,4 +62,76 @@ public class LDAPFederationProviderFactory implements UserFederationProviderFact
|
|||
public Set<String> getConfigurationOptions() {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void syncAllUsers(KeycloakSessionFactory sessionFactory, String realmId, UserFederationProviderModel model) {
|
||||
logger.infof("Sync all users from LDAP to local store: realm: %s, federation provider: %s, current time: " + new Date(), realmId, model.getDisplayName());
|
||||
|
||||
PartitionManagerProvider idmProvider = sessionFactory.create().getProvider(PartitionManagerProvider.class);
|
||||
PartitionManager partitionMgr = idmProvider.getPartitionManager(model);
|
||||
IdentityQuery<User> userQuery = partitionMgr.createIdentityManager().createIdentityQuery(User.class);
|
||||
syncImpl(sessionFactory, userQuery, realmId, model);
|
||||
|
||||
// TODO: Remove all existing keycloak users, which have federation links, but are not in LDAP. Perhaps don't check users, which were just added or updated during this sync?
|
||||
}
|
||||
|
||||
@Override
|
||||
public void syncChangedUsers(KeycloakSessionFactory sessionFactory, String realmId, UserFederationProviderModel model, Date lastSync) {
|
||||
logger.infof("Sync changed users from LDAP to local store: realm: %s, federation provider: %s, current time: " + new Date() + ", last sync time: " + lastSync, realmId, model.getDisplayName());
|
||||
|
||||
PartitionManagerProvider idmProvider = sessionFactory.create().getProvider(PartitionManagerProvider.class);
|
||||
PartitionManager partitionMgr = idmProvider.getPartitionManager(model);
|
||||
|
||||
// Sync newly created users
|
||||
IdentityManager identityManager = partitionMgr.createIdentityManager();
|
||||
IdentityQuery<User> userQuery = identityManager.createIdentityQuery(User.class)
|
||||
.setParameter(IdentityType.CREATED_AFTER, lastSync);
|
||||
syncImpl(sessionFactory, userQuery, realmId, model);
|
||||
|
||||
// Sync updated users
|
||||
userQuery = identityManager.createIdentityQuery(User.class)
|
||||
.setParameter(IdentityType.MODIFIED_AFTER, lastSync);
|
||||
syncImpl(sessionFactory, userQuery, realmId, model);
|
||||
}
|
||||
|
||||
protected void syncImpl(KeycloakSessionFactory sessionFactory, IdentityQuery<User> userQuery, final String realmId, final UserFederationProviderModel fedModel) {
|
||||
boolean pagination = Boolean.parseBoolean(fedModel.getConfig().get(LDAPConstants.PAGINATION));
|
||||
|
||||
if (pagination) {
|
||||
String pageSizeConfig = fedModel.getConfig().get(LDAPConstants.BATCH_SIZE_FOR_SYNC);
|
||||
int pageSize = pageSizeConfig!=null ? Integer.parseInt(pageSizeConfig) : LDAPConstants.DEFAULT_BATCH_SIZE_FOR_SYNC;
|
||||
boolean nextPage = true;
|
||||
while (nextPage) {
|
||||
userQuery.setLimit(pageSize);
|
||||
final List<User> users = userQuery.getResultList();
|
||||
nextPage = userQuery.getPaginationContext() != null;
|
||||
|
||||
KeycloakModelUtils.runJobInTransaction(sessionFactory, new KeycloakSessionTask() {
|
||||
|
||||
@Override
|
||||
public void run(KeycloakSession session) {
|
||||
importPicketlinkUsers(session, realmId, fedModel, users);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// LDAP pagination not available. Do everything in single transaction
|
||||
final List<User> users = userQuery.getResultList();
|
||||
KeycloakModelUtils.runJobInTransaction(sessionFactory, new KeycloakSessionTask() {
|
||||
|
||||
@Override
|
||||
public void run(KeycloakSession session) {
|
||||
importPicketlinkUsers(session, realmId, fedModel, users);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
protected void importPicketlinkUsers(KeycloakSession session, String realmId, UserFederationProviderModel fedModel, List<User> users) {
|
||||
RealmModel realm = session.realms().getRealm(realmId);
|
||||
LDAPFederationProvider ldapFedProvider = getInstance(session, fedModel);
|
||||
ldapFedProvider.importPicketlinkUsers(realm, users, fedModel);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,6 +29,17 @@ public class LDAPUtils {
|
|||
return picketlinkUser;
|
||||
}
|
||||
|
||||
public static User updateUser(PartitionManager partitionManager, String username, String firstName, String lastName, String email) {
|
||||
IdentityManager idmManager = getIdentityManager(partitionManager);
|
||||
User picketlinkUser = BasicModel.getUser(idmManager, username);
|
||||
picketlinkUser.setFirstName(firstName);
|
||||
picketlinkUser.setLastName(lastName);
|
||||
picketlinkUser.setEmail(email);
|
||||
picketlinkUser.setAttribute(new Attribute("fullName", getFullName(username, firstName, lastName)));
|
||||
idmManager.update(picketlinkUser);
|
||||
return picketlinkUser;
|
||||
}
|
||||
|
||||
public static void updatePassword(PartitionManager partitionManager, User picketlinkUser, String password) {
|
||||
IdentityManager idmManager = getIdentityManager(partitionManager);
|
||||
idmManager.updateCredential(picketlinkUser, new Password(password.toCharArray()));
|
||||
|
@ -48,10 +59,6 @@ public class LDAPUtils {
|
|||
}
|
||||
}
|
||||
|
||||
public static boolean isUserExists(PartitionManager partitionManager, String username) {
|
||||
return getUser(partitionManager, username) != null;
|
||||
}
|
||||
|
||||
public static User getUser(PartitionManager partitionManager, String username) {
|
||||
IdentityManager idmManager = getIdentityManager(partitionManager);
|
||||
return BasicModel.getUser(idmManager, username);
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
package org.keycloak.models;
|
||||
|
||||
/**
|
||||
* Task to be executed inside transaction
|
||||
*
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public interface KeycloakSessionTask {
|
||||
|
||||
public void run(KeycloakSession session);
|
||||
|
||||
}
|
|
@ -22,5 +22,9 @@ public class LDAPConstants {
|
|||
public static final String CONNECTION_POOLING = "connectionPooling";
|
||||
public static final String PAGINATION = "pagination";
|
||||
|
||||
// Count of users processed per single transaction during sync process
|
||||
public static final String BATCH_SIZE_FOR_SYNC = "batchSizeForSync";
|
||||
public static final int DEFAULT_BATCH_SIZE_FOR_SYNC = 1000;
|
||||
|
||||
public static final String USER_ACCOUNT_CONTROLS_AFTER_PASSWORD_UPDATE = "userAccountControlsAfterPasswordUpdate";
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -2,6 +2,7 @@ package org.keycloak.models;
|
|||
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
|
@ -32,4 +33,26 @@ public interface UserFederationProviderFactory extends ProviderFactory<UserFeder
|
|||
*/
|
||||
@Override
|
||||
String getId();
|
||||
|
||||
/**
|
||||
* Sync all users from the provider storage to Keycloak storage.
|
||||
*
|
||||
* @param sessionFactory
|
||||
* @param realmId
|
||||
* @param model
|
||||
*/
|
||||
void syncAllUsers(KeycloakSessionFactory sessionFactory, String realmId, UserFederationProviderModel model);
|
||||
|
||||
/**
|
||||
* Sync just changed (added / updated / removed) users from the provider storage to Keycloak storage. This is useful in case
|
||||
* that your storage supports "changelogs" (Tracking what users changed since specified date). It's implementation specific to
|
||||
* decide what exactly will be changed (For example LDAP supports tracking of added / updated users, but not removed users. So
|
||||
* removed users are not synced)
|
||||
*
|
||||
* @param sessionFactory
|
||||
* @param realmId
|
||||
* @param model
|
||||
* @param lastSync
|
||||
*/
|
||||
void syncChangedUsers(KeycloakSessionFactory sessionFactory, String realmId, UserFederationProviderModel model, Date lastSync);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,9 @@ import org.keycloak.models.ApplicationModel;
|
|||
import org.keycloak.models.ClaimMask;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.models.KeycloakSessionTask;
|
||||
import org.keycloak.models.KeycloakTransaction;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.UserCredentialModel;
|
||||
|
@ -132,4 +135,36 @@ public final class KeycloakModelUtils {
|
|||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap given runnable job into KeycloakTransaction.
|
||||
*
|
||||
* @param factory
|
||||
* @param task
|
||||
*/
|
||||
public static void runJobInTransaction(KeycloakSessionFactory factory, KeycloakSessionTask task) {
|
||||
KeycloakSession session = factory.create();
|
||||
KeycloakTransaction tx = session.getTransaction();
|
||||
try {
|
||||
tx.begin();
|
||||
task.run(session);
|
||||
|
||||
if (tx.isActive()) {
|
||||
if (tx.getRollbackOnly()) {
|
||||
tx.rollback();
|
||||
} else {
|
||||
tx.commit();
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (tx.isActive()) {
|
||||
tx.rollback();
|
||||
}
|
||||
session.close();
|
||||
}
|
||||
}
|
||||
|
||||
public static String getMasterRealmAdminApplicationName(RealmModel realm) {
|
||||
return realm.getName() + "-realm";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -11,13 +11,17 @@ import org.picketlink.idm.config.LDAPMappingConfigurationBuilder;
|
|||
import org.picketlink.idm.config.LDAPStoreConfigurationBuilder;
|
||||
import org.picketlink.idm.internal.DefaultPartitionManager;
|
||||
import org.picketlink.idm.model.basic.User;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import static org.picketlink.common.constants.LDAPConstants.*;
|
||||
import static org.picketlink.common.constants.LDAPConstants.CN;
|
||||
import static org.picketlink.common.constants.LDAPConstants.EMAIL;
|
||||
import static org.picketlink.common.constants.LDAPConstants.SN;
|
||||
import static org.picketlink.common.constants.LDAPConstants.UID;
|
||||
import static org.picketlink.common.constants.LDAPConstants.CREATE_TIMESTAMP;
|
||||
import static org.picketlink.common.constants.LDAPConstants.MODIFY_TIMESTAMP;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
|
@ -80,6 +84,8 @@ public class PartitionManagerRegistry {
|
|||
}
|
||||
|
||||
String ldapFirstNameMapping = activeDirectory ? "givenName" : CN;
|
||||
String createTimestampMapping = activeDirectory ? "whenCreated" : CREATE_TIMESTAMP;
|
||||
String modifyTimestampMapping = activeDirectory ? "whenChanged" : MODIFY_TIMESTAMP;
|
||||
String[] userObjectClasses = getUserObjectClasses(ldapConfig);
|
||||
|
||||
boolean pagination = ldapConfig.containsKey(LDAPConstants.PAGINATION) ? Boolean.parseBoolean(ldapConfig.get(LDAPConstants.PAGINATION)) : false;
|
||||
|
@ -112,7 +118,9 @@ public class PartitionManagerRegistry {
|
|||
.attribute("loginName", ldapLoginNameMapping, true)
|
||||
.attribute("firstName", ldapFirstNameMapping)
|
||||
.attribute("lastName", SN)
|
||||
.attribute("email", EMAIL);
|
||||
.attribute("email", EMAIL)
|
||||
.readOnlyAttribute("createdDate", createTimestampMapping)
|
||||
.readOnlyAttribute("modifyDate", modifyTimestampMapping);
|
||||
|
||||
if (activeDirectory && ldapLoginNameMapping.equals("sAMAccountName")) {
|
||||
ldapUserMappingBuilder.bindingAttribute("fullName", CN);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -84,8 +84,8 @@ public class KeycloakApplication extends Application {
|
|||
|
||||
setupDefaultRealm(context.getContextPath());
|
||||
|
||||
setupScheduledTasks(sessionFactory);
|
||||
importRealms(context);
|
||||
setupScheduledTasks(sessionFactory);
|
||||
}
|
||||
|
||||
public String getContextPath() {
|
||||
|
@ -146,8 +146,8 @@ public class KeycloakApplication extends Application {
|
|||
long interval = Config.scope("scheduled").getLong("interval", 60L) * 1000;
|
||||
|
||||
TimerProvider timer = sessionFactory.create().getProvider(TimerProvider.class);
|
||||
timer.schedule(new ScheduledTaskRunner(sessionFactory, new ClearExpiredAuditEvents()), interval);
|
||||
timer.schedule(new ScheduledTaskRunner(sessionFactory, new ClearExpiredUserSessions()), interval);
|
||||
timer.schedule(new ScheduledTaskRunner(sessionFactory, new ClearExpiredAuditEvents()), interval, "ClearExpiredAuditEvents");
|
||||
timer.schedule(new ScheduledTaskRunner(sessionFactory, new ClearExpiredUserSessions()), interval, "ClearExpiredUserSessions");
|
||||
}
|
||||
|
||||
public KeycloakSessionFactory getSessionFactory() {
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ public class DummyUserFederationProvider implements UserFederationProvider {
|
|||
|
||||
@Override
|
||||
public boolean removeUser(RealmModel realm, UserModel user) {
|
||||
return true;
|
||||
return users.remove(user.getUsername()) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,19 +1,29 @@
|
|||
package org.keycloak.testutils;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.models.UserFederationProvider;
|
||||
import org.keycloak.models.UserFederationProviderFactory;
|
||||
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();
|
||||
|
@ -43,6 +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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,6 +42,7 @@ public class LDAPEmbeddedServer extends AbstractLDAPTest {
|
|||
protected String vendor = LDAPConstants.VENDOR_OTHER;
|
||||
protected boolean connectionPooling = true;
|
||||
protected boolean pagination = true;
|
||||
protected int batchSizeForSync = LDAPConstants.DEFAULT_BATCH_SIZE_FOR_SYNC;
|
||||
|
||||
public static String IDM_TEST_LDAP_CONNECTION_URL = "idm.test.ldap.connection.url";
|
||||
public static String IDM_TEST_LDAP_BASE_DN = "idm.test.ldap.base.dn";
|
||||
|
@ -55,6 +56,7 @@ public class LDAPEmbeddedServer extends AbstractLDAPTest {
|
|||
public static String IDM_TEST_LDAP_VENDOR = "idm.test.ldap.vendor";
|
||||
public static String IDM_TEST_LDAP_CONNECTION_POOLING = "idm.test.ldap.connection.pooling";
|
||||
public static String IDM_TEST_LDAP_PAGINATION = "idm.test.ldap.pagination";
|
||||
public static String IDM_TEST_LDAP_BATCH_SIZE_FOR_SYNC = "idm.test.ldap.batch.size.for.sync";
|
||||
|
||||
|
||||
public LDAPEmbeddedServer() {
|
||||
|
@ -84,6 +86,7 @@ public class LDAPEmbeddedServer extends AbstractLDAPTest {
|
|||
vendor = p.getProperty(IDM_TEST_LDAP_VENDOR);
|
||||
connectionPooling = Boolean.parseBoolean(p.getProperty(IDM_TEST_LDAP_CONNECTION_POOLING, "true"));
|
||||
pagination = Boolean.parseBoolean(p.getProperty(IDM_TEST_LDAP_PAGINATION, "true"));
|
||||
batchSizeForSync = Integer.parseInt(p.getProperty(IDM_TEST_LDAP_BATCH_SIZE_FOR_SYNC, String.valueOf(batchSizeForSync)));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -134,6 +137,7 @@ public class LDAPEmbeddedServer extends AbstractLDAPTest {
|
|||
ldapConfig.put(LDAPConstants.VENDOR, getVendor());
|
||||
ldapConfig.put(LDAPConstants.CONNECTION_POOLING, String.valueOf(isConnectionPooling()));
|
||||
ldapConfig.put(LDAPConstants.PAGINATION, String.valueOf(isPagination()));
|
||||
ldapConfig.put(LDAPConstants.BATCH_SIZE_FOR_SYNC, String.valueOf(getBatchSizeForSync()));
|
||||
return ldapConfig;
|
||||
}
|
||||
|
||||
|
@ -216,6 +220,10 @@ public class LDAPEmbeddedServer extends AbstractLDAPTest {
|
|||
return pagination;
|
||||
}
|
||||
|
||||
public int getBatchSizeForSync() {
|
||||
return batchSizeForSync;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void importLDIF(String fileName) throws Exception {
|
||||
// import LDIF only in case we are running against embedded LDAP server
|
||||
|
|
|
@ -8,4 +8,5 @@ idm.test.ldap.start.embedded.ldap.server=true
|
|||
idm.test.ldap.bind.dn=uid\=admin,ou\=system
|
||||
idm.test.ldap.bind.credential=secret
|
||||
idm.test.ldap.connection.pooling=true
|
||||
idm.test.ldap.pagination=true
|
||||
idm.test.ldap.pagination=true
|
||||
idm.test.ldap.batch.size.for.sync=3
|
||||
|
|
|
@ -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);
|
||||
|
@ -102,7 +102,7 @@ public class FederationProvidersIntegrationTest {
|
|||
@WebResource
|
||||
protected AccountPasswordPage changePasswordPage;
|
||||
|
||||
private static UserModel addUser(KeycloakSession session, RealmModel realm, String username, String email, String password) {
|
||||
static UserModel addUser(KeycloakSession session, RealmModel realm, String username, String email, String password) {
|
||||
UserModel user = session.users().addUser(realm, username);
|
||||
user.setEmail(email);
|
||||
user.setEnabled(true);
|
||||
|
@ -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);
|
||||
|
@ -313,7 +315,7 @@ public class FederationProvidersIntegrationTest {
|
|||
}
|
||||
}
|
||||
|
||||
private static PartitionManager getPartitionManager(KeycloakSession keycloakSession, UserFederationProviderModel ldapFedModel) {
|
||||
static PartitionManager getPartitionManager(KeycloakSession keycloakSession, UserFederationProviderModel ldapFedModel) {
|
||||
PartitionManagerProvider partitionManagerProvider = keycloakSession.getProvider(PartitionManagerProvider.class);
|
||||
return partitionManagerProvider.getPartitionManager(ldapFedModel);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,186 @@
|
|||
package org.keycloak.testsuite.forms;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.FixMethodOrder;
|
||||
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;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserFederationProvider;
|
||||
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;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
|
||||
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() {
|
||||
|
||||
@Override
|
||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||
LDAPEmbeddedServer ldapServer = ldapRule.getEmbeddedServer();
|
||||
Map<String,String> ldapConfig = ldapServer.getLDAPConfig();
|
||||
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",
|
||||
-1, -1, 0);
|
||||
|
||||
// Delete all LDAP users and add 5 new users for testing
|
||||
PartitionManager partitionManager = FederationProvidersIntegrationTest.getPartitionManager(manager.getSession(), ldapModel);
|
||||
LDAPUtils.removeAllUsers(partitionManager);
|
||||
|
||||
User user1 = LDAPUtils.addUser(partitionManager, "user1", "User1FN", "User1LN", "user1@email.org");
|
||||
LDAPUtils.updatePassword(partitionManager, user1, "password1");
|
||||
User user2 = LDAPUtils.addUser(partitionManager, "user2", "User2FN", "User2LN", "user2@email.org");
|
||||
LDAPUtils.updatePassword(partitionManager, user2, "password2");
|
||||
User user3 = LDAPUtils.addUser(partitionManager, "user3", "User3FN", "User3LN", "user3@email.org");
|
||||
LDAPUtils.updatePassword(partitionManager, user3, "password3");
|
||||
User user4 = LDAPUtils.addUser(partitionManager, "user4", "User4FN", "User4LN", "user4@email.org");
|
||||
LDAPUtils.updatePassword(partitionManager, user4, "password4");
|
||||
User user5 = LDAPUtils.addUser(partitionManager, "user5", "User5FN", "User5LN", "user5@email.org");
|
||||
LDAPUtils.updatePassword(partitionManager, user5, "password5");
|
||||
|
||||
// Add properties provider
|
||||
dummyModel = appRealm.addUserFederationProvider(DummyUserFederationProviderFactory.PROVIDER_NAME, new HashMap<String, String>(), 1, "test-dummy", -1, 1, 0);
|
||||
}
|
||||
});
|
||||
|
||||
@ClassRule
|
||||
public static TestRule chain = RuleChain
|
||||
.outerRule(ldapRule)
|
||||
.around(keycloakRule);
|
||||
|
||||
@Test
|
||||
public void testLDAPSync() {
|
||||
KeycloakSession session = keycloakRule.startSession();
|
||||
try {
|
||||
KeycloakSessionFactory sessionFactory = session.getKeycloakSessionFactory();
|
||||
UserFederationProviderFactory ldapFedFactory = (UserFederationProviderFactory)sessionFactory.getProviderFactory(UserFederationProvider.class, LDAPFederationProviderFactory.PROVIDER_NAME);
|
||||
ldapFedFactory.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();
|
||||
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");
|
||||
|
||||
// wait a bit
|
||||
sleep(1000);
|
||||
Date beforeLDAPUpdate = new Date();
|
||||
|
||||
// Add user to LDAP and update 'user5' in LDAP
|
||||
PartitionManager partitionManager = FederationProvidersIntegrationTest.getPartitionManager(session, ldapModel);
|
||||
LDAPUtils.addUser(partitionManager, "user6", "User6FN", "User6LN", "user6@email.org");
|
||||
LDAPUtils.updateUser(partitionManager, "user5", "User5FNUpdated", "User5LNUpdated", "user5Updated@email.org");
|
||||
|
||||
// Assert still old users in local provider
|
||||
assertUserImported(userProvider, testRealm, "user5", "User5FN", "User5LN", "user5@email.org");
|
||||
Assert.assertNull(userProvider.getUserByUsername("user6", testRealm));
|
||||
|
||||
// Trigger partial sync
|
||||
KeycloakSessionFactory sessionFactory = session.getKeycloakSessionFactory();
|
||||
UserFederationProviderFactory ldapFedFactory = (UserFederationProviderFactory)sessionFactory.getProviderFactory(UserFederationProvider.class, LDAPFederationProviderFactory.PROVIDER_NAME);
|
||||
ldapFedFactory.syncChangedUsers(sessionFactory, "test", ldapModel, beforeLDAPUpdate);
|
||||
} finally {
|
||||
keycloakRule.stopSession(session, false);
|
||||
}
|
||||
|
||||
session = keycloakRule.startSession();
|
||||
try {
|
||||
RealmModel testRealm = session.realms().getRealm("test");
|
||||
UserProvider userProvider = session.userStorage();
|
||||
// Assert users updated in local provider
|
||||
assertUserImported(userProvider, testRealm, "user5", "User5FNUpdated", "User5LNUpdated", "user5Updated@email.org");
|
||||
assertUserImported(userProvider, testRealm, "user6", "User6FN", "User6LN", "user6@email.org");
|
||||
} finally {
|
||||
keycloakRule.stopSession(session, false);
|
||||
}
|
||||
}
|
||||
|
||||
@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);
|
||||
Assert.assertEquals(expectedFirstName, user.getFirstName());
|
||||
Assert.assertEquals(expectedLastName, user.getLastName());
|
||||
Assert.assertEquals(expectedEmail, user.getEmail());
|
||||
}
|
||||
}
|
|
@ -7,6 +7,8 @@ import org.keycloak.provider.Provider;
|
|||
*/
|
||||
public interface TimerProvider extends Provider {
|
||||
|
||||
public void schedule(Runnable runnable, long interval);
|
||||
public void schedule(Runnable runnable, long interval, String taskName);
|
||||
|
||||
public void cancelTask(String taskName);
|
||||
|
||||
}
|
||||
|
|
|
@ -31,6 +31,11 @@
|
|||
<version>${project.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jboss.logging</groupId>
|
||||
<artifactId>jboss-logging</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package org.keycloak.timer.basic;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.timer.TimerProvider;
|
||||
|
||||
import java.util.Timer;
|
||||
|
@ -10,15 +11,18 @@ import java.util.TimerTask;
|
|||
*/
|
||||
public class BasicTimerProvider implements TimerProvider {
|
||||
|
||||
private Timer timer;
|
||||
private static final Logger logger = Logger.getLogger(BasicTimerProvider.class);
|
||||
|
||||
public BasicTimerProvider(Timer timer) {
|
||||
private final Timer timer;
|
||||
private final BasicTimerProviderFactory factory;
|
||||
|
||||
public BasicTimerProvider(Timer timer, BasicTimerProviderFactory factory) {
|
||||
this.timer = timer;
|
||||
this.factory = factory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void schedule(final Runnable runnable, final long interval) {
|
||||
public void schedule(final Runnable runnable, final long interval, String taskName) {
|
||||
TimerTask task = new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
@ -26,9 +30,25 @@ public class BasicTimerProvider implements TimerProvider {
|
|||
}
|
||||
};
|
||||
|
||||
TimerTask existingTask = factory.putTask(taskName, task);
|
||||
if (existingTask != null) {
|
||||
logger.infof("Existing timer task '%s' found. Cancelling it", taskName);
|
||||
existingTask.cancel();
|
||||
}
|
||||
|
||||
logger.infof("Starting task '%s' with interval '%d'", taskName, interval);
|
||||
timer.schedule(task, interval, interval);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancelTask(String taskName) {
|
||||
TimerTask existingTask = factory.removeTask(taskName);
|
||||
if (existingTask != null) {
|
||||
logger.infof("Cancelling task '%s'", taskName);
|
||||
existingTask.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
// do nothing
|
||||
|
|
|
@ -6,6 +6,9 @@ import org.keycloak.timer.TimerProvider;
|
|||
import org.keycloak.timer.TimerProviderFactory;
|
||||
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
|
@ -14,9 +17,11 @@ public class BasicTimerProviderFactory implements TimerProviderFactory {
|
|||
|
||||
private Timer timer;
|
||||
|
||||
private ConcurrentMap<String, TimerTask> scheduledTasks = new ConcurrentHashMap<String, TimerTask>();
|
||||
|
||||
@Override
|
||||
public TimerProvider create(KeycloakSession session) {
|
||||
return new BasicTimerProvider(timer);
|
||||
return new BasicTimerProvider(timer, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -35,4 +40,12 @@ public class BasicTimerProviderFactory implements TimerProviderFactory {
|
|||
return "basic";
|
||||
}
|
||||
|
||||
protected TimerTask putTask(String taskName, TimerTask task) {
|
||||
return scheduledTasks.put(taskName, task);
|
||||
}
|
||||
|
||||
protected TimerTask removeTask(String taskName) {
|
||||
return scheduledTasks.remove(taskName);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue