keycloakRoles = roleContainer.getRoles();
+
+ for (RoleModel keycloakRole : keycloakRoles) {
+ String roleName = keycloakRole.getName();
+ if (ldapRoleNames.contains(roleName)) {
+ syncResult.increaseUpdated();
+ } else {
+ logger.debugf("Syncing role [%s] from Keycloak to LDAP", roleName);
+ createLDAPRole(mapperModel, roleName, ldapProvider);
+ syncResult.increaseAdded();
+ }
+ }
+
+ return syncResult;
+ }
+
+
public LDAPQuery createRoleQuery(UserFederationMapperModel mapperModel, LDAPFederationProvider ldapProvider) {
LDAPQuery ldapQuery = new LDAPQuery(ldapProvider);
diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/RoleLDAPFederationMapperFactory.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/RoleLDAPFederationMapperFactory.java
index 5cc2997a4b..455c1dbf4b 100644
--- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/RoleLDAPFederationMapperFactory.java
+++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/RoleLDAPFederationMapperFactory.java
@@ -5,21 +5,13 @@ import java.util.LinkedList;
import java.util.List;
import org.jboss.logging.Logger;
-import org.keycloak.federation.ldap.LDAPFederationProvider;
import org.keycloak.mappers.MapperConfigValidationException;
import org.keycloak.mappers.UserFederationMapper;
import org.keycloak.models.KeycloakSession;
-import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.LDAPConstants;
-import org.keycloak.models.RealmModel;
import org.keycloak.models.UserFederationMapperModel;
-import org.keycloak.models.UserFederationProvider;
-import org.keycloak.models.UserFederationProviderFactory;
-import org.keycloak.models.UserFederationProviderModel;
-import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.provider.ProviderConfigProperty;
-import org.keycloak.provider.ProviderEvent;
-import org.keycloak.provider.ProviderEventListener;
+import org.keycloak.representations.idm.UserFederationMapperSyncConfigRepresentation;
/**
* @author Marek Posolda
@@ -58,15 +50,15 @@ public class RoleLDAPFederationMapperFactory extends AbstractLDAPFederationMappe
membershipTypes.add(membershipType.toString());
}
ProviderConfigProperty membershipType = createConfigProperty(RoleLDAPFederationMapper.MEMBERSHIP_ATTRIBUTE_TYPE, "Membership Attribute Type",
- "DN means that LDAP role has it's members declared in form of their full DN. For example ( 'member: uid=john,ou=users,dc=example,dc=com' . " +
- "UID means that LDAP role has it's members declared in form of pure user uids. For example ( 'memberUid: john' ))",
+ "DN means that LDAP role has it's members declared in form of their full DN. For example 'member: uid=john,ou=users,dc=example,dc=com' . " +
+ "UID means that LDAP role has it's members declared in form of pure user uids. For example 'memberUid: john' .",
ProviderConfigProperty.LIST_TYPE, membershipTypes);
configProperties.add(membershipType);
ProviderConfigProperty ldapFilter = createConfigProperty(RoleLDAPFederationMapper.ROLES_LDAP_FILTER,
"LDAP Filter",
- "LDAP Filter adds additional custom filter to the whole query. Make sure that it starts with '(' and ends with ')'",
+ "LDAP Filter adds additional custom filter to the whole query. Leave this empty if no additional filtering is needed. Otherwise make sure that filter starts with '(' and ends with ')'",
ProviderConfigProperty.STRING_TYPE, null);
configProperties.add(ldapFilter);
@@ -90,7 +82,7 @@ public class RoleLDAPFederationMapperFactory extends AbstractLDAPFederationMappe
ProviderConfigProperty retriever = createConfigProperty(RoleLDAPFederationMapper.USER_ROLES_RETRIEVE_STRATEGY, "User Roles Retrieve Strategy",
"Specify how to retrieve roles of user. LOAD_ROLES_BY_MEMBER_ATTRIBUTE means that roles of user will be retrieved by sending LDAP query to retrieve all roles where 'member' is our user. " +
"GET_ROLES_FROM_USER_MEMBEROF_ATTRIBUTE means that roles of user will be retrieved from 'memberOf' attribute of our user. " +
- "LOAD_ROLES_BY_MEMBER_ATTRIBUTE_RECURSIVELY is applicable just in Active Directory and it means that roles of user will be retrieved recursively with usage of LDAP_MATCHING_RULE_IN_CHAIN extension."
+ "LOAD_ROLES_BY_MEMBER_ATTRIBUTE_RECURSIVELY is applicable just in Active Directory and it means that roles of user will be retrieved recursively with usage of LDAP_MATCHING_RULE_IN_CHAIN Ldap extension."
,
ProviderConfigProperty.LIST_TYPE, roleRetrievers);
configProperties.add(retriever);
@@ -131,40 +123,9 @@ public class RoleLDAPFederationMapperFactory extends AbstractLDAPFederationMappe
return PROVIDER_ID;
}
- // Sync roles from LDAP to Keycloak DB during creation or update of mapperModel
@Override
- public void postInit(KeycloakSessionFactory factory) {
- factory.register(new ProviderEventListener() {
-
- @Override
- public void onEvent(ProviderEvent event) {
- if (event instanceof RealmModel.UserFederationMapperEvent) {
- RealmModel.UserFederationMapperEvent mapperEvent = (RealmModel.UserFederationMapperEvent)event;
- UserFederationMapperModel mapperModel = mapperEvent.getFederationMapper();
- RealmModel realm = mapperEvent.getRealm();
- KeycloakSession session = mapperEvent.getSession();
-
- if (mapperModel.getFederationMapperType().equals(PROVIDER_ID)) {
- try {
- String federationProviderId = mapperModel.getFederationProviderId();
- UserFederationProviderModel providerModel = KeycloakModelUtils.findUserFederationProviderById(federationProviderId, realm);
- if (providerModel == null) {
- throw new IllegalStateException("Can't find federation provider with ID [" + federationProviderId + "] in realm " + realm.getName());
- }
-
- UserFederationProviderFactory ldapFactory = (UserFederationProviderFactory) session.getKeycloakSessionFactory().getProviderFactory(UserFederationProvider.class, providerModel.getProviderName());
- LDAPFederationProvider ldapProvider = (LDAPFederationProvider) ldapFactory.getInstance(session, providerModel);
-
- // Sync roles
- new RoleLDAPFederationMapper().syncRolesFromLDAP(mapperModel, ldapProvider, realm);
- } catch (Exception e) {
- logger.warn("Exception during initial sync of roles from LDAP.", e);
- }
- }
- }
- }
-
- });
+ public UserFederationMapperSyncConfigRepresentation getSyncConfig() {
+ return new UserFederationMapperSyncConfigRepresentation(true, "sync-ldap-roles-to-keycloak", true, "sync-keycloak-roles-to-ldap");
}
@Override
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties b/forms/common-themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties
index 287abe48e2..f33d6b835a 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties
+++ b/forms/common-themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties
@@ -472,6 +472,12 @@ social.default-scopes.tooltip=The scopes to be sent when asking for authorizatio
key=Key
stackoverflow.key.tooltip=The Key obtained from Stack Overflow client registration.
+# User federation
+sync-ldap-roles-to-keycloak=Sync LDAP Roles To Keycloak
+sync-keycloak-roles-to-ldap=Sync Keycloak Roles To LDAP
+sync-ldap-groups-to-keycloak=Sync LDAP Groups To Keycloak
+sync-keycloak-groups-to-ldap=Sync Keycloak Groups To LDAP
+
realms=Realms
realm=Realm
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/users.js b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/users.js
index f42f8aa2fb..cb3aa08df1 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/users.js
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/users.js
@@ -986,7 +986,7 @@ module.controller('UserFederationMapperListCtrl', function($scope, $location, No
});
-module.controller('UserFederationMapperCtrl', function($scope, realm, provider, mapperTypes, mapper, clients, UserFederationMapper, Notifications, Dialog, $location) {
+module.controller('UserFederationMapperCtrl', function($scope, realm, provider, mapperTypes, mapper, clients, UserFederationMapper, UserFederationMapperSync, Notifications, Dialog, $location) {
console.log('UserFederationMapperCtrl');
$scope.realm = realm;
$scope.provider = provider;
@@ -1035,6 +1035,22 @@ module.controller('UserFederationMapperCtrl', function($scope, realm, provider,
});
};
+ $scope.triggerFedToKeycloakSync = function() {
+ triggerMapperSync("fedToKeycloak")
+ }
+
+ $scope.triggerKeycloakToFedSync = function() {
+ triggerMapperSync("keycloakToFed");
+ }
+
+ function triggerMapperSync(direction) {
+ UserFederationMapperSync.save({ direction: direction, realm: realm.realm, provider: provider.id, mapperId : $scope.mapper.id }, {}, function(syncResult) {
+ Notifications.success("Data synced successfully. " + syncResult.status);
+ }, function() {
+ Notifications.error("Error during sync of data");
+ });
+ }
+
});
module.controller('UserFederationMapperCreateCtrl', function($scope, realm, provider, mapperTypes, clients, UserFederationMapper, Notifications, Dialog, $location) {
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/services.js b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/services.js
index 7a47a83a0c..54f1cb5bf7 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/services.js
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/services.js
@@ -380,6 +380,10 @@ module.factory('UserFederationMapper', function($resource) {
});
});
+module.factory('UserFederationMapperSync', function($resource) {
+ return $resource(authUrl + '/admin/realms/:realm/user-federation/instances/:provider/mappers/:mapperId/sync');
+});
+
module.factory('UserSessionStats', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/users/:user/session-stats', {
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/federated-mapper-detail.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/federated-mapper-detail.html
index 2c91c929f8..04d25c064c 100644
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/federated-mapper-detail.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/federated-mapper-detail.html
@@ -53,6 +53,8 @@
+
+
diff --git a/model/api/src/main/java/org/keycloak/mappers/UserFederationMapper.java b/model/api/src/main/java/org/keycloak/mappers/UserFederationMapper.java
index 509ae64df6..2a03499262 100644
--- a/model/api/src/main/java/org/keycloak/mappers/UserFederationMapper.java
+++ b/model/api/src/main/java/org/keycloak/mappers/UserFederationMapper.java
@@ -1,5 +1,10 @@
package org.keycloak.mappers;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserFederationMapperModel;
+import org.keycloak.models.UserFederationProvider;
+import org.keycloak.models.UserFederationSyncResult;
import org.keycloak.provider.Provider;
/**
@@ -7,4 +12,28 @@ import org.keycloak.provider.Provider;
*/
public interface UserFederationMapper extends Provider {
+ /**
+ * Sync data from federation storage to Keycloak. It's useful just if mapper needs some data preloaded from federation storage (For example
+ * load roles from federation provider and sync them to Keycloak database)
+ *
+ * Applicable just if sync is supported (see UserFederationMapperFactory.getSyncConfig() )
+ *
+ * @see UserFederationMapperFactory#getSyncConfig()
+ * @param mapperModel
+ * @param federationProvider
+ * @param session
+ * @param realm
+ */
+ UserFederationSyncResult syncDataFromFederationProviderToKeycloak(UserFederationMapperModel mapperModel, UserFederationProvider federationProvider, KeycloakSession session, RealmModel realm);
+
+ /**
+ * Sync data from Keycloak back to federation storage
+ *
+ * @see UserFederationMapperFactory#getSyncConfig()
+ * @param mapperModel
+ * @param federationProvider
+ * @param session
+ * @param realm
+ */
+ UserFederationSyncResult syncDataFromKeycloakToFederationProvider(UserFederationMapperModel mapperModel, UserFederationProvider federationProvider, KeycloakSession session, RealmModel realm);
}
diff --git a/model/api/src/main/java/org/keycloak/mappers/UserFederationMapperFactory.java b/model/api/src/main/java/org/keycloak/mappers/UserFederationMapperFactory.java
index a4c877683e..7690e8a106 100644
--- a/model/api/src/main/java/org/keycloak/mappers/UserFederationMapperFactory.java
+++ b/model/api/src/main/java/org/keycloak/mappers/UserFederationMapperFactory.java
@@ -1,12 +1,9 @@
package org.keycloak.mappers;
-import java.util.List;
-
-import org.keycloak.models.RealmModel;
import org.keycloak.models.UserFederationMapperModel;
import org.keycloak.provider.ConfiguredProvider;
-import org.keycloak.provider.ProviderConfigProperty;
import org.keycloak.provider.ProviderFactory;
+import org.keycloak.representations.idm.UserFederationMapperSyncConfigRepresentation;
/**
* @author Marek Posolda
@@ -23,6 +20,14 @@ public interface UserFederationMapperFactory extends ProviderFactoryMarek Posolda
- */
-public class UserFederationMapperEventImpl implements RealmModel.UserFederationMapperEvent {
-
- private final UserFederationMapperModel mapperModel;
- private final RealmModel realm;
- private final KeycloakSession session;
-
- public UserFederationMapperEventImpl(UserFederationMapperModel mapperModel, RealmModel realm, KeycloakSession session) {
- this.mapperModel = mapperModel;
- this.realm = realm;
- this.session = session;
- }
-
- @Override
- public UserFederationMapperModel getFederationMapper() {
- return mapperModel;
- }
-
- @Override
- public RealmModel getRealm() {
- return realm;
- }
-
- public KeycloakSession getSession() {
- return session;
- }
-}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java
index 4f96829e61..bebe07990f 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java
@@ -1,6 +1,5 @@
package org.keycloak.models.jpa;
-import org.keycloak.Config;
import org.keycloak.connections.jpa.util.JpaUtils;
import org.keycloak.common.enums.SslRequired;
import org.keycloak.models.AuthenticationExecutionModel;
@@ -19,7 +18,6 @@ import org.keycloak.models.RealmModel;
import org.keycloak.models.RequiredActionProviderModel;
import org.keycloak.models.RequiredCredentialModel;
import org.keycloak.models.RoleModel;
-import org.keycloak.models.UserFederationMapperEventImpl;
import org.keycloak.models.UserFederationMapperModel;
import org.keycloak.models.UserFederationProviderCreationEventImpl;
import org.keycloak.models.UserFederationProviderModel;
@@ -1541,8 +1539,6 @@ public class RealmAdapter implements RealmModel {
this.realm.getUserFederationMappers().add(entity);
UserFederationMapperModel mapperModel = entityToModel(entity);
- session.getKeycloakSessionFactory().publish(new UserFederationMapperEventImpl(mapperModel, this, session));
-
return mapperModel;
}
@@ -1597,8 +1593,6 @@ public class RealmAdapter implements RealmModel {
entity.getConfig().putAll(mapper.getConfig());
}
em.flush();
-
- session.getKeycloakSessionFactory().publish(new UserFederationMapperEventImpl(mapper, this, session));
}
@Override
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java
index c3b159a327..6a1a84d0bf 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java
@@ -22,7 +22,6 @@ import org.keycloak.models.RealmProvider;
import org.keycloak.models.RequiredActionProviderModel;
import org.keycloak.models.RequiredCredentialModel;
import org.keycloak.models.RoleModel;
-import org.keycloak.models.UserFederationMapperEventImpl;
import org.keycloak.models.UserFederationMapperModel;
import org.keycloak.models.UserFederationProviderCreationEventImpl;
import org.keycloak.models.UserFederationProviderModel;
@@ -1930,8 +1929,6 @@ public class RealmAdapter extends AbstractMongoAdapter impleme
updateMongoEntity();
UserFederationMapperModel mapperModel = entityToModel(entity);
- session.getKeycloakSessionFactory().publish(new UserFederationMapperEventImpl(mapperModel, this, session));
-
return mapperModel;
}
@@ -1986,8 +1983,6 @@ public class RealmAdapter extends AbstractMongoAdapter impleme
entity.getConfig().putAll(mapper.getConfig());
}
updateMongoEntity();
-
- session.getKeycloakSessionFactory().publish(new UserFederationMapperEventImpl(mapper, this, session));
}
@Override
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/UserFederationProviderResource.java b/services/src/main/java/org/keycloak/services/resources/admin/UserFederationProviderResource.java
index 6f7bedfd82..3c4226a7aa 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/UserFederationProviderResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/UserFederationProviderResource.java
@@ -32,8 +32,11 @@ import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserFederationMapperModel;
+import org.keycloak.models.UserFederationProvider;
+import org.keycloak.models.UserFederationProviderFactory;
import org.keycloak.models.UserFederationProviderModel;
import org.keycloak.models.UserFederationSyncResult;
+import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.models.utils.ModelToRepresentation;
import org.keycloak.models.utils.RepresentationToModel;
import org.keycloak.provider.ProviderConfigProperty;
@@ -138,11 +141,13 @@ public class UserFederationProviderResource {
auth.requireManage();
UsersSyncManager syncManager = new UsersSyncManager();
- UserFederationSyncResult syncResult = null;
+ UserFederationSyncResult syncResult;
if ("triggerFullSync".equals(action)) {
syncResult = syncManager.syncAllUsers(session.getKeycloakSessionFactory(), realm.getId(), this.federationProviderModel);
} else if ("triggerChangedUsersSync".equals(action)) {
syncResult = syncManager.syncChangedUsers(session.getKeycloakSessionFactory(), realm.getId(), this.federationProviderModel);
+ } else {
+ throw new NotFoundException("Unknown action: " + action);
}
adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo).success();
@@ -172,6 +177,7 @@ public class UserFederationProviderResource {
rep.setCategory(mapperFactory.getDisplayCategory());
rep.setName(mapperFactory.getDisplayType());
rep.setHelpText(mapperFactory.getHelpText());
+ rep.setSyncConfig(mapperFactory.getSyncConfig());
List configProperties = mapperFactory.getConfigProperties();
for (ProviderConfigProperty prop : configProperties) {
ConfigPropertyRepresentation propRep = new ConfigPropertyRepresentation();
@@ -307,6 +313,41 @@ public class UserFederationProviderResource {
}
+ /**
+ * Trigger sync of mapper data related to federationMapper (roles, groups, ...)
+ *
+ * @return
+ */
+ @POST
+ @Path("mappers/{id}/sync")
+ @NoCache
+ public UserFederationSyncResult syncMapperData(@PathParam("id") String mapperId, @QueryParam("direction") String direction) {
+ auth.requireManage();
+
+ UserFederationMapperModel mapperModel = realm.getUserFederationMapperById(mapperId);
+ if (mapperModel == null) throw new NotFoundException("Mapper model not found");
+ UserFederationMapper mapper = session.getProvider(UserFederationMapper.class, mapperModel.getFederationMapperType());
+
+ UserFederationProviderModel providerModel = KeycloakModelUtils.findUserFederationProviderById(mapperModel.getFederationProviderId(), realm);
+ if (providerModel == null) throw new NotFoundException("Provider model not found");
+ UserFederationProviderFactory providerFactory = (UserFederationProviderFactory) session.getKeycloakSessionFactory().getProviderFactory(UserFederationProvider.class, providerModel.getProviderName());
+ UserFederationProvider federationProvider = providerFactory.getInstance(session, providerModel);
+
+ logger.infof("Syncing data for mapper '%s' of type '%s'. Direction: %s", mapperModel.getName(), mapperModel.getFederationMapperType(), direction);
+
+ UserFederationSyncResult syncResult;
+ if ("fedToKeycloak".equals(direction)) {
+ syncResult = mapper.syncDataFromFederationProviderToKeycloak(mapperModel, federationProvider, session, realm);
+ } else if ("keycloakToFed".equals(direction)) {
+ syncResult = mapper.syncDataFromKeycloakToFederationProvider(mapperModel, federationProvider, session, realm);
+ } else {
+ throw new NotFoundException("Unknown direction: " + direction);
+ }
+
+ adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo).success();
+ return syncResult;
+ }
+
private void validateModel(UserFederationMapperModel model) {
try {
UserFederationMapperFactory mapperFactory = (UserFederationMapperFactory) session.getKeycloakSessionFactory().getProviderFactory(UserFederationMapper.class, model.getFederationMapperType());
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/FederationTestUtils.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/FederationTestUtils.java
index 63a27e6ce7..580b3456c9 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/FederationTestUtils.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/FederationTestUtils.java
@@ -141,6 +141,16 @@ class FederationTestUtils {
}
}
+ public static void syncRolesFromLDAP(RealmModel realm, LDAPFederationProvider ldapProvider, UserFederationProviderModel providerModel) {
+ RoleLDAPFederationMapper roleMapper = new RoleLDAPFederationMapper();
+
+ UserFederationMapperModel mapperModel = realm.getUserFederationMapperByName(providerModel.getId(), "realmRolesMapper");
+ roleMapper.syncDataFromFederationProviderToKeycloak(mapperModel, ldapProvider, ldapProvider.getSession(), realm);
+
+ mapperModel = realm.getUserFederationMapperByName(providerModel.getId(), "financeRolesMapper");
+ roleMapper.syncDataFromFederationProviderToKeycloak(mapperModel, ldapProvider, ldapProvider.getSession(), realm);
+ }
+
public static void removeAllLDAPUsers(LDAPFederationProvider ldapProvider, RealmModel realm) {
LDAPIdentityStore ldapStore = ldapProvider.getLdapIdentityStore();
LDAPQuery ldapQuery = LDAPUtils.createQueryForUserSearch(ldapProvider, realm);
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/LDAPRoleMappingsTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/LDAPRoleMappingsTest.java
index 5bb2291547..2d6eac90dc 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/LDAPRoleMappingsTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/LDAPRoleMappingsTest.java
@@ -88,6 +88,9 @@ public class LDAPRoleMappingsTest {
FederationTestUtils.createLDAPRole(manager.getSession(), appRealm, ldapModel, "realmRolesMapper", "realmRole1");
FederationTestUtils.createLDAPRole(manager.getSession(), appRealm, ldapModel, "realmRolesMapper", "realmRole2");
FederationTestUtils.createLDAPRole(manager.getSession(), appRealm, ldapModel, "financeRolesMapper", "financeRole1");
+
+ // Sync LDAP roles to Keycloak DB
+ FederationTestUtils.syncRolesFromLDAP(appRealm, ldapFedProvider, ldapModel);
}
});