use of org.keycloak.storage.user.SynchronizationResult in project keycloak by keycloak.
the class LDAPStorageProviderFactory method syncSince.
@Override
public SynchronizationResult syncSince(Date lastSync, KeycloakSessionFactory sessionFactory, String realmId, UserStorageProviderModel model) {
syncMappers(sessionFactory, realmId, model);
logger.infof("Sync changed users from LDAP to local store: realm: %s, federation provider: %s, last sync time: " + lastSync, realmId, model.getName());
// Sync newly created and updated users
LDAPQueryConditionsBuilder conditionsBuilder = new LDAPQueryConditionsBuilder();
Condition createCondition = conditionsBuilder.greaterThanOrEqualTo(LDAPConstants.CREATE_TIMESTAMP, lastSync);
Condition modifyCondition = conditionsBuilder.greaterThanOrEqualTo(LDAPConstants.MODIFY_TIMESTAMP, lastSync);
Condition orCondition = conditionsBuilder.orCondition(createCondition, modifyCondition);
try (LDAPQuery userQuery = createQuery(sessionFactory, realmId, model)) {
userQuery.addWhereCondition(orCondition);
SynchronizationResult result = syncImpl(sessionFactory, userQuery, realmId, model);
logger.infof("Sync changed users finished: %s", result.getStatus());
return result;
}
}
use of org.keycloak.storage.user.SynchronizationResult in project keycloak by keycloak.
the class LDAPStorageProviderFactory method importLdapUsers.
protected SynchronizationResult importLdapUsers(KeycloakSessionFactory sessionFactory, final String realmId, final ComponentModel fedModel, List<LDAPObject> ldapUsers) {
final SynchronizationResult syncResult = new SynchronizationResult();
class BooleanHolder {
private boolean value = true;
}
final BooleanHolder exists = new BooleanHolder();
for (final LDAPObject ldapUser : ldapUsers) {
try {
// Process each user in it's own transaction to avoid global fail
KeycloakModelUtils.runJobInTransaction(sessionFactory, new KeycloakSessionTask() {
@Override
public void run(KeycloakSession session) {
LDAPStorageProvider ldapFedProvider = (LDAPStorageProvider) session.getProvider(UserStorageProvider.class, fedModel);
RealmModel currentRealm = session.realms().getRealm(realmId);
session.getContext().setRealm(currentRealm);
String username = LDAPUtils.getUsername(ldapUser, ldapFedProvider.getLdapIdentityStore().getConfig());
exists.value = true;
LDAPUtils.checkUuid(ldapUser, ldapFedProvider.getLdapIdentityStore().getConfig());
UserModel currentUserLocal = session.userLocalStorage().getUserByUsername(currentRealm, username);
Optional<UserModel> userModelOptional = session.userLocalStorage().searchForUserByUserAttributeStream(currentRealm, LDAPConstants.LDAP_ID, ldapUser.getUuid()).findFirst();
if (!userModelOptional.isPresent() && currentUserLocal == null) {
// Add new user to Keycloak
exists.value = false;
ldapFedProvider.importUserFromLDAP(session, currentRealm, ldapUser);
syncResult.increaseAdded();
} else {
UserModel currentUser = userModelOptional.isPresent() ? userModelOptional.get() : currentUserLocal;
if ((fedModel.getId().equals(currentUser.getFederationLink())) && (ldapUser.getUuid().equals(currentUser.getFirstAttribute(LDAPConstants.LDAP_ID)))) {
// Update keycloak user
LDAPMappersComparator ldapMappersComparator = new LDAPMappersComparator(ldapFedProvider.getLdapIdentityStore().getConfig());
currentRealm.getComponentsStream(fedModel.getId(), LDAPStorageMapper.class.getName()).sorted(ldapMappersComparator.sortDesc()).forEachOrdered(mapperModel -> {
LDAPStorageMapper ldapMapper = ldapFedProvider.getMapperManager().getMapper(mapperModel);
ldapMapper.onImportUserFromLDAP(ldapUser, currentUser, currentRealm, false);
});
UserCache userCache = session.userCache();
if (userCache != null) {
userCache.evict(currentRealm, currentUser);
}
logger.debugf("Updated user from LDAP: %s", currentUser.getUsername());
syncResult.increaseUpdated();
} else {
logger.warnf("User with ID '%s' is not updated during sync as he already exists in Keycloak database but is not linked to federation provider '%s'", ldapUser.getUuid(), fedModel.getName());
syncResult.increaseFailed();
}
}
}
});
} catch (ModelException me) {
logger.error("Failed during import user from LDAP", me);
syncResult.increaseFailed();
// Remove user if we already added him during this transaction
if (!exists.value) {
KeycloakModelUtils.runJobInTransaction(sessionFactory, new KeycloakSessionTask() {
@Override
public void run(KeycloakSession session) {
LDAPStorageProvider ldapFedProvider = (LDAPStorageProvider) session.getProvider(UserStorageProvider.class, fedModel);
RealmModel currentRealm = session.realms().getRealm(realmId);
session.getContext().setRealm(currentRealm);
String username = null;
try {
username = LDAPUtils.getUsername(ldapUser, ldapFedProvider.getLdapIdentityStore().getConfig());
} catch (ModelException ignore) {
}
if (username != null) {
UserModel existing = session.userLocalStorage().getUserByUsername(currentRealm, username);
if (existing != null) {
UserCache userCache = session.userCache();
if (userCache != null) {
userCache.evict(currentRealm, existing);
}
session.userLocalStorage().removeUser(currentRealm, existing);
}
}
}
});
}
}
}
return syncResult;
}
use of org.keycloak.storage.user.SynchronizationResult in project keycloak by keycloak.
the class GroupLDAPStorageMapper method syncDataFromKeycloakToFederationProvider.
// Sync from Keycloak to LDAP
@Override
public SynchronizationResult syncDataFromKeycloakToFederationProvider(RealmModel realm) {
SynchronizationResult syncResult = new SynchronizationResult() {
@Override
public String getStatus() {
return String.format("%d groups imported to LDAP, %d groups updated to LDAP, %d groups removed from LDAP", getAdded(), getUpdated(), getRemoved());
}
};
if (config.getMode() != LDAPGroupMapperMode.LDAP_ONLY) {
logger.warnf("Ignored sync for federation mapper '%s' as it's mode is '%s'", mapperModel.getName(), config.getMode().toString());
return syncResult;
}
logger.debugf("Syncing groups from Keycloak into LDAP. Mapper is [%s], LDAP provider is [%s]", mapperModel.getName(), ldapProvider.getModel().getName());
// Query existing LDAP groups
List<LDAPObject> ldapGroups = getAllLDAPGroups(config.isPreserveGroupsInheritance());
// Convert them to Map<String, LDAPObject>
Map<String, LDAPObject> ldapGroupsMap = new HashMap<>();
String groupsRdnAttr = config.getGroupNameLdapAttribute();
for (LDAPObject ldapGroup : ldapGroups) {
String groupName = ldapGroup.getAttributeAsString(groupsRdnAttr);
ldapGroupsMap.put(groupName, ldapGroup);
}
// Map to track all LDAP groups also exist in Keycloak
Set<String> ldapGroupNames = new HashSet<>();
// Create or update KC groups to LDAP including their attributes
getKcSubGroups(realm, null).forEach(kcGroup -> processKeycloakGroupSyncToLDAP(kcGroup, ldapGroupsMap, ldapGroupNames, syncResult));
// If dropNonExisting, then drop all groups, which doesn't exist in KC from LDAP as well
if (config.isDropNonExistingGroupsDuringSync()) {
Set<String> copy = new HashSet<>(ldapGroupsMap.keySet());
for (String groupName : copy) {
if (!ldapGroupNames.contains(groupName)) {
LDAPObject ldapGroup = ldapGroupsMap.remove(groupName);
ldapProvider.getLdapIdentityStore().remove(ldapGroup);
syncResult.increaseRemoved();
}
}
}
// Finally process memberships,
if (config.isPreserveGroupsInheritance()) {
getKcSubGroups(realm, null).forEach(kcGroup -> processKeycloakGroupMembershipsSyncToLDAP(kcGroup, ldapGroupsMap));
}
return syncResult;
}
use of org.keycloak.storage.user.SynchronizationResult in project keycloak by keycloak.
the class SyncFederationTest method test03ConcurrentSync.
@Test
public void test03ConcurrentSync() throws Exception {
// Enable timer for SyncDummyUserFederationProvider
testingClient.server().run(session -> {
SyncDummyUserFederationProviderFactory.restartLatches();
RealmModel appRealm = session.realms().getRealmByName(AuthRealm.TEST);
UserStorageProviderModel model = new UserStorageProviderModel();
model.setProviderId(SyncDummyUserFederationProviderFactory.SYNC_PROVIDER_ID);
model.setPriority(1);
model.setName("test-sync-dummy");
model.setFullSyncPeriod(-1);
model.setChangedSyncPeriod(1);
model.setLastSync(0);
model.getConfig().putSingle(SyncDummyUserFederationProviderFactory.WAIT_TIME, "2000");
ComponentModel dummyModel = new UserStorageProviderModel(appRealm.addComponentModel(model));
});
testingClient.server().run(session -> {
RealmModel appRealm = session.realms().getRealmByName(AuthRealm.TEST);
UserStorageProviderModel dummyModel = findDummyProviderModel(appRealm);
KeycloakSessionFactory sessionFactory = session.getKeycloakSessionFactory();
// bootstrap periodic sync
UserStorageSyncManager usersSyncManager = new UserStorageSyncManager();
usersSyncManager.bootstrapPeriodic(sessionFactory, session.getProvider(TimerProvider.class));
// Wait and then trigger sync manually. Assert it will be ignored
sleep(1800);
SynchronizationResult syncResult = usersSyncManager.syncChangedUsers(sessionFactory, appRealm.getId(), dummyModel);
Assert.assertTrue(syncResult.isIgnored());
// Cancel timer
usersSyncManager.notifyToRefreshPeriodicSync(session, appRealm, dummyModel, true);
// Signal to factory to finish waiting
SyncDummyUserFederationProviderFactory.latch1.countDown();
try {
SyncDummyUserFederationProviderFactory.latch2.await(20000, TimeUnit.MILLISECONDS);
} catch (Exception e) {
throw new RuntimeException(e);
}
});
// remove provider
testingClient.server().run(session -> {
RealmModel appRealm = session.realms().getRealmByName(AuthRealm.TEST);
UserStorageProviderModel dummyModel = findDummyProviderModel(appRealm);
appRealm.removeComponent(dummyModel);
});
}
use of org.keycloak.storage.user.SynchronizationResult in project keycloak by keycloak.
the class LDAPSyncTest method test05MissingLDAPUsernameSync.
// KEYCLOAK-1728
@Test
public void test05MissingLDAPUsernameSync() {
String origUsernameAttrName = testingClient.server().fetch(session -> {
LDAPTestContext ctx = LDAPTestContext.init(session);
// Remove all users from model
session.userLocalStorage().getUsersStream(ctx.getRealm(), true).peek(user -> System.out.println("trying to delete user: " + user.getUsername())).collect(Collectors.toList()).forEach(user -> {
UserCache userCache = session.userCache();
if (userCache != null) {
userCache.evict(ctx.getRealm(), user);
}
session.userLocalStorage().removeUser(ctx.getRealm(), user);
});
// Add street mapper and add some user including street
ComponentModel streetMapper = LDAPTestUtils.addUserAttributeMapper(ctx.getRealm(), ctx.getLdapModel(), "streetMapper", "street", LDAPConstants.STREET);
LDAPObject streetUser = LDAPTestUtils.addLDAPUser(ctx.getLdapProvider(), ctx.getRealm(), "user8", "User8FN", "User8LN", "user8@email.org", "user8street", "126");
// Change name of username attribute name to street
String origUsernameAttrNamee = ctx.getLdapModel().get(LDAPConstants.USERNAME_LDAP_ATTRIBUTE);
ctx.getLdapModel().getConfig().putSingle(LDAPConstants.USERNAME_LDAP_ATTRIBUTE, "street");
// Need to change this due to ApacheDS pagination bug (For other LDAP servers, pagination works fine) TODO: Remove once ApacheDS upgraded and pagination is fixed
ctx.getLdapModel().put(LDAPConstants.BATCH_SIZE_FOR_SYNC, "10");
ctx.getRealm().updateComponent(ctx.getLdapModel());
return origUsernameAttrNamee;
}, String.class);
// Just user8 synced. All others failed to sync
testingClient.server().run(session -> {
LDAPTestContext ctx = LDAPTestContext.init(session);
KeycloakSessionFactory sessionFactory = session.getKeycloakSessionFactory();
SynchronizationResult syncResult = new UserStorageSyncManager().syncAllUsers(sessionFactory, "test", ctx.getLdapModel());
Assert.assertEquals(1, syncResult.getAdded());
Assert.assertTrue(syncResult.getFailed() > 0);
});
// Revert config changes
ComponentRepresentation ldapRep = testRealm().components().component(ldapModelId).toRepresentation();
if (origUsernameAttrName == null) {
ldapRep.getConfig().remove(LDAPConstants.USERNAME_LDAP_ATTRIBUTE);
} else {
ldapRep.getConfig().putSingle(LDAPConstants.USERNAME_LDAP_ATTRIBUTE, origUsernameAttrName);
}
testRealm().components().component(ldapModelId).update(ldapRep);
testingClient.server().run(session -> {
LDAPTestContext ctx = LDAPTestContext.init(session);
// Revert config changes
ComponentModel streetMapper = LDAPTestUtils.getSubcomponentByName(ctx.getRealm(), ctx.getLdapModel(), "streetMapper");
ctx.getRealm().removeComponent(streetMapper);
});
}
Aggregations