[KEYCLOAK-8253] When syncing flat (all groups being the top-level ones) structure
of LDAP groups from federation provider to Keycloak, perform the search if the currently processed group already exists in Keycloak in log(N) time Signed-off-by: Jan Lieskovsky <jlieskov@redhat.com>
This commit is contained in:
parent
cfb225b499
commit
7ab854fecf
2 changed files with 60 additions and 11 deletions
|
@ -45,6 +45,8 @@ import org.keycloak.storage.ldap.mappers.membership.MembershipType;
|
|||
import org.keycloak.storage.ldap.mappers.membership.UserRolesRetrieveStrategy;
|
||||
import org.keycloak.storage.user.SynchronizationResult;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
|
@ -213,21 +215,68 @@ public class GroupLDAPStorageMapper extends AbstractLDAPStorageMapper implements
|
|||
// due to the realm cache being bloated with huge amount of (temporary) realm entities
|
||||
RealmModel currentRealm = session.realms().getRealm(realm.getId());
|
||||
|
||||
// List of top-level groups known to the whole transaction
|
||||
ArrayList<GroupModel> transactionTopLevelGroups = new ArrayList<GroupModel>(currentRealm.getTopLevelGroups());
|
||||
String[] transactionBinarySearchTopLevelGroupsArray = transactionTopLevelGroups.parallelStream().map(g -> g.getName()).toArray(String[]::new);
|
||||
|
||||
for (Map.Entry<String, LDAPObject> groupEntry : groupsInTransaction.entrySet()) {
|
||||
|
||||
String groupName = groupEntry.getKey();
|
||||
GroupModel kcExistingGroup = KeycloakModelUtils.findGroupByPath(currentRealm, "/" + groupName);
|
||||
|
||||
// Binary search the list of top-level groups known to the outer transaction for presence of the currently processed group
|
||||
int transactionBinarySearchResult = Arrays.binarySearch(transactionBinarySearchTopLevelGroupsArray, groupName);
|
||||
GroupModel kcExistingGroup = (transactionBinarySearchResult > 0) ? transactionTopLevelGroups.get(transactionBinarySearchResult) : null;
|
||||
|
||||
if (kcExistingGroup != null) {
|
||||
updateAttributesOfKCGroup(kcExistingGroup, groupEntry.getValue());
|
||||
syncResult.increaseUpdated();
|
||||
visitedGroupIds.add(kcExistingGroup.getId());
|
||||
|
||||
try {
|
||||
|
||||
// Update each existing group to be synced in its own inner transaction to prevent race condition when
|
||||
// the groups intended to be updated was already deleted via other channel in the meantime
|
||||
KeycloakModelUtils.runJobInTransaction(ldapProvider.getSession().getKeycloakSessionFactory(), new KeycloakSessionTask() {
|
||||
|
||||
@Override
|
||||
public void run(KeycloakSession session) {
|
||||
|
||||
updateAttributesOfKCGroup(kcExistingGroup, groupEntry.getValue());
|
||||
syncResult.increaseUpdated();
|
||||
visitedGroupIds.add(kcExistingGroup.getId());
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
} catch (ModelException me) {
|
||||
logger.error(String.format("Failed to update attributes of LDAP group %s: ", groupName), me);
|
||||
syncResult.increaseFailed();
|
||||
}
|
||||
|
||||
} else {
|
||||
GroupModel kcGroup = currentRealm.createGroup(groupName);
|
||||
updateAttributesOfKCGroup(kcGroup, groupEntry.getValue());
|
||||
currentRealm.moveGroup(kcGroup, null);
|
||||
syncResult.increaseAdded();
|
||||
visitedGroupIds.add(kcGroup.getId());
|
||||
|
||||
try {
|
||||
|
||||
// Create each non-existing group to be synced in its own inner transaction to prevent race condition when
|
||||
// the roup intended to be created was already created via other channel in the meantime
|
||||
KeycloakModelUtils.runJobInTransaction(ldapProvider.getSession().getKeycloakSessionFactory(), new KeycloakSessionTask() {
|
||||
|
||||
@Override
|
||||
public void run(KeycloakSession session) {
|
||||
|
||||
RealmModel innerTransactionRealm = session.realms().getRealm(realm.getId());
|
||||
GroupModel kcGroup = innerTransactionRealm.createGroup(groupName);
|
||||
updateAttributesOfKCGroup(kcGroup, groupEntry.getValue());
|
||||
innerTransactionRealm.moveGroup(kcGroup, null);
|
||||
syncResult.increaseAdded();
|
||||
visitedGroupIds.add(kcGroup.getId());
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
} catch (ModelException me) {
|
||||
logger.error(String.format("Failed to sync group %s from LDAP: ", groupName), me);
|
||||
syncResult.increaseFailed();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -422,9 +422,9 @@ public class LDAPGroupMapperSyncTest extends AbstractLDAPTest {
|
|||
LDAPTestUtils.createLDAPGroup(session,
|
||||
appRealm,
|
||||
ctx.getLdapModel(),
|
||||
String.format("group-%s", i),
|
||||
String.format("group-%s", j),
|
||||
descriptionAttrName,
|
||||
String.format("Testing group-%s, created at: %s", i, new Date().toString())
|
||||
String.format("Testing group-%s, created at: %s", j, new Date().toString())
|
||||
);
|
||||
}
|
||||
logger.debugf("Done creating %s LDAP groups!", groupsPerIteration);
|
||||
|
|
Loading…
Reference in a new issue