diff --git a/connections/mongo/src/main/java/org/keycloak/connections/mongo/DefaultMongoConnectionFactoryProvider.java b/connections/mongo/src/main/java/org/keycloak/connections/mongo/DefaultMongoConnectionFactoryProvider.java
index dab99e081d..36be680a66 100755
--- a/connections/mongo/src/main/java/org/keycloak/connections/mongo/DefaultMongoConnectionFactoryProvider.java
+++ b/connections/mongo/src/main/java/org/keycloak/connections/mongo/DefaultMongoConnectionFactoryProvider.java
@@ -46,7 +46,8 @@ public class DefaultMongoConnectionFactoryProvider implements MongoConnectionPro
"org.keycloak.models.mongo.keycloak.entities.MongoMigrationModelEntity",
"org.keycloak.models.entities.AuthenticationExecutionEntity",
"org.keycloak.models.entities.AuthenticationFlowEntity",
- "org.keycloak.models.entities.AuthenticatorEntity",
+ "org.keycloak.models.entities.AuthenticatorConfigEntity",
+ "org.keycloak.models.entities.RequiredActionProviderEntity",
};
private static final Logger logger = Logger.getLogger(DefaultMongoConnectionFactoryProvider.class);
diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProvider.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProvider.java
index 7ad05d34d7..cd857ff63d 100755
--- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProvider.java
+++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProvider.java
@@ -232,7 +232,7 @@ public class LDAPFederationProvider implements UserFederationProvider {
if (ldapUser.getUuid().equals(local.getAttribute(LDAPConstants.LDAP_ID))) {
return ldapUser;
} else {
- logger.warnf("LDAP User invalid. ID doesn't match. ID from LDAP [%s], ID from local DB: [%s]", ldapUser.getUuid(), local.getAttribute(LDAPConstants.LDAP_ID));
+ logger.warnf("LDAP User invalid. ID doesn't match. ID from LDAP [%s], LDAP ID from local DB: [%s]", ldapUser.getUuid(), local.getAttribute(LDAPConstants.LDAP_ID));
return null;
}
}
diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProviderFactory.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProviderFactory.java
index 876a96d5fc..98ee688663 100755
--- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProviderFactory.java
+++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProviderFactory.java
@@ -84,14 +84,17 @@ public class LDAPFederationProviderFactory extends UserFederationEventAwareProvi
boolean activeDirectory = ldapConfig.isActiveDirectory();
UserFederationProvider.EditMode editMode = ldapConfig.getEditMode();
- String readOnly = String.valueOf(editMode==UserFederationProvider.EditMode.READ_ONLY || editMode== UserFederationProvider.EditMode.UNSYNCED);
+ String readOnly = String.valueOf(editMode == UserFederationProvider.EditMode.READ_ONLY || editMode == UserFederationProvider.EditMode.UNSYNCED);
String usernameLdapAttribute = ldapConfig.getUsernameLdapAttribute();
+ String alwaysReadValueFromLDAP = String.valueOf(editMode==UserFederationProvider.EditMode.READ_ONLY || editMode== UserFederationProvider.EditMode.WRITABLE);
+
UserFederationMapperModel mapperModel;
mapperModel = KeycloakModelUtils.createUserFederationMapperModel("username", newProviderModel.getId(), UserAttributeLDAPFederationMapperFactory.PROVIDER_ID,
UserAttributeLDAPFederationMapper.USER_MODEL_ATTRIBUTE, UserModel.USERNAME,
UserAttributeLDAPFederationMapper.LDAP_ATTRIBUTE, usernameLdapAttribute,
- UserAttributeLDAPFederationMapper.READ_ONLY, readOnly);
+ UserAttributeLDAPFederationMapper.READ_ONLY, readOnly,
+ UserAttributeLDAPFederationMapper.ALWAYS_READ_VALUE_FROM_LDAP, "false");
realm.addUserFederationMapper(mapperModel);
// CN is typically used as RDN for Active Directory deployments
@@ -103,7 +106,8 @@ public class LDAPFederationProviderFactory extends UserFederationEventAwareProvi
mapperModel = KeycloakModelUtils.createUserFederationMapperModel("first name", newProviderModel.getId(), UserAttributeLDAPFederationMapperFactory.PROVIDER_ID,
UserAttributeLDAPFederationMapper.USER_MODEL_ATTRIBUTE, UserModel.FIRST_NAME,
UserAttributeLDAPFederationMapper.LDAP_ATTRIBUTE, LDAPConstants.GIVENNAME,
- UserAttributeLDAPFederationMapper.READ_ONLY, readOnly);
+ UserAttributeLDAPFederationMapper.READ_ONLY, readOnly,
+ UserAttributeLDAPFederationMapper.ALWAYS_READ_VALUE_FROM_LDAP, alwaysReadValueFromLDAP);
realm.addUserFederationMapper(mapperModel);
} else {
@@ -113,13 +117,15 @@ public class LDAPFederationProviderFactory extends UserFederationEventAwareProvi
mapperModel = KeycloakModelUtils.createUserFederationMapperModel("first name", newProviderModel.getId(), UserAttributeLDAPFederationMapperFactory.PROVIDER_ID,
UserAttributeLDAPFederationMapper.USER_MODEL_ATTRIBUTE, UserModel.FIRST_NAME,
UserAttributeLDAPFederationMapper.LDAP_ATTRIBUTE, LDAPConstants.GIVENNAME,
- UserAttributeLDAPFederationMapper.READ_ONLY, readOnly);
+ UserAttributeLDAPFederationMapper.READ_ONLY, readOnly,
+ UserAttributeLDAPFederationMapper.ALWAYS_READ_VALUE_FROM_LDAP, alwaysReadValueFromLDAP);
realm.addUserFederationMapper(mapperModel);
mapperModel = KeycloakModelUtils.createUserFederationMapperModel("username-cn", newProviderModel.getId(), UserAttributeLDAPFederationMapperFactory.PROVIDER_ID,
UserAttributeLDAPFederationMapper.USER_MODEL_ATTRIBUTE, UserModel.USERNAME,
UserAttributeLDAPFederationMapper.LDAP_ATTRIBUTE, LDAPConstants.CN,
- UserAttributeLDAPFederationMapper.READ_ONLY, readOnly);
+ UserAttributeLDAPFederationMapper.READ_ONLY, readOnly,
+ UserAttributeLDAPFederationMapper.ALWAYS_READ_VALUE_FROM_LDAP, "false");
realm.addUserFederationMapper(mapperModel);
} else {
@@ -134,20 +140,23 @@ public class LDAPFederationProviderFactory extends UserFederationEventAwareProvi
mapperModel = KeycloakModelUtils.createUserFederationMapperModel("first name", newProviderModel.getId(), UserAttributeLDAPFederationMapperFactory.PROVIDER_ID,
UserAttributeLDAPFederationMapper.USER_MODEL_ATTRIBUTE, UserModel.FIRST_NAME,
UserAttributeLDAPFederationMapper.LDAP_ATTRIBUTE, LDAPConstants.CN,
- UserAttributeLDAPFederationMapper.READ_ONLY, readOnly);
+ UserAttributeLDAPFederationMapper.READ_ONLY, readOnly,
+ UserAttributeLDAPFederationMapper.ALWAYS_READ_VALUE_FROM_LDAP, alwaysReadValueFromLDAP);
realm.addUserFederationMapper(mapperModel);
}
mapperModel = KeycloakModelUtils.createUserFederationMapperModel("last name", newProviderModel.getId(), UserAttributeLDAPFederationMapperFactory.PROVIDER_ID,
UserAttributeLDAPFederationMapper.USER_MODEL_ATTRIBUTE, UserModel.LAST_NAME,
UserAttributeLDAPFederationMapper.LDAP_ATTRIBUTE, LDAPConstants.SN,
- UserAttributeLDAPFederationMapper.READ_ONLY, readOnly);
+ UserAttributeLDAPFederationMapper.READ_ONLY, readOnly,
+ UserAttributeLDAPFederationMapper.ALWAYS_READ_VALUE_FROM_LDAP, alwaysReadValueFromLDAP);
realm.addUserFederationMapper(mapperModel);
mapperModel = KeycloakModelUtils.createUserFederationMapperModel("email", newProviderModel.getId(), UserAttributeLDAPFederationMapperFactory.PROVIDER_ID,
UserAttributeLDAPFederationMapper.USER_MODEL_ATTRIBUTE, UserModel.EMAIL,
UserAttributeLDAPFederationMapper.LDAP_ATTRIBUTE, LDAPConstants.EMAIL,
- UserAttributeLDAPFederationMapper.READ_ONLY, readOnly);
+ UserAttributeLDAPFederationMapper.READ_ONLY, readOnly,
+ UserAttributeLDAPFederationMapper.ALWAYS_READ_VALUE_FROM_LDAP, alwaysReadValueFromLDAP);
realm.addUserFederationMapper(mapperModel);
String createTimestampLdapAttrName = activeDirectory ? "whenCreated" : LDAPConstants.CREATE_TIMESTAMP;
@@ -157,14 +166,16 @@ public class LDAPFederationProviderFactory extends UserFederationEventAwareProvi
mapperModel = KeycloakModelUtils.createUserFederationMapperModel("creation date", newProviderModel.getId(), UserAttributeLDAPFederationMapperFactory.PROVIDER_ID,
UserAttributeLDAPFederationMapper.USER_MODEL_ATTRIBUTE, LDAPConstants.CREATE_TIMESTAMP,
UserAttributeLDAPFederationMapper.LDAP_ATTRIBUTE, createTimestampLdapAttrName,
- UserAttributeLDAPFederationMapper.READ_ONLY, "true");
+ UserAttributeLDAPFederationMapper.READ_ONLY, "true",
+ UserAttributeLDAPFederationMapper.ALWAYS_READ_VALUE_FROM_LDAP, alwaysReadValueFromLDAP);
realm.addUserFederationMapper(mapperModel);
// map modifyTimeStamp as read-only
mapperModel = KeycloakModelUtils.createUserFederationMapperModel("modify date", newProviderModel.getId(), UserAttributeLDAPFederationMapperFactory.PROVIDER_ID,
UserAttributeLDAPFederationMapper.USER_MODEL_ATTRIBUTE, LDAPConstants.MODIFY_TIMESTAMP,
UserAttributeLDAPFederationMapper.LDAP_ATTRIBUTE, modifyTimestampLdapAttrName,
- UserAttributeLDAPFederationMapper.READ_ONLY, "true");
+ UserAttributeLDAPFederationMapper.READ_ONLY, "true",
+ UserAttributeLDAPFederationMapper.ALWAYS_READ_VALUE_FROM_LDAP, alwaysReadValueFromLDAP);
realm.addUserFederationMapper(mapperModel);
}
diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPUtils.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPUtils.java
index 396d997dbe..27e8df3850 100755
--- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPUtils.java
+++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPUtils.java
@@ -1,6 +1,5 @@
package org.keycloak.federation.ldap;
-import java.util.List;
import java.util.Set;
import org.keycloak.federation.ldap.idm.model.LDAPDn;
@@ -61,7 +60,7 @@ public class LDAPUtils {
// ldapUser has filled attributes, but doesn't have filled dn.
private static void computeAndSetDn(LDAPConfig config, LDAPObject ldapUser) {
String rdnLdapAttrName = config.getRdnLdapAttribute();
- String rdnLdapAttrValue = ldapUser.getAttributeAsString(rdnLdapAttrName);
+ String rdnLdapAttrValue = ldapUser.getAttributeAsStringCaseInsensitive(rdnLdapAttrName);
if (rdnLdapAttrValue == null) {
throw new ModelException("RDN Attribute [" + rdnLdapAttrName + "] is not filled. Filled attributes: " + ldapUser.getAttributes());
}
@@ -73,6 +72,6 @@ public class LDAPUtils {
public static String getUsername(LDAPObject ldapUser, LDAPConfig config) {
String usernameAttr = config.getUsernameLdapAttribute();
- return ldapUser.getAttributeAsString(usernameAttr);
+ return ldapUser.getAttributeAsStringCaseInsensitive(usernameAttr);
}
}
diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/model/LDAPObject.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/model/LDAPObject.java
index b7e6c0e36d..c449484cef 100644
--- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/model/LDAPObject.java
+++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/model/LDAPObject.java
@@ -6,18 +6,29 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Map;
+import org.jboss.logging.Logger;
+
/**
* @author Marek Posolda
*/
public class LDAPObject {
+ private static final Logger logger = Logger.getLogger(LDAPObject.class);
+
private String uuid;
private LDAPDn dn;
private String rdnAttributeName;
- private final List objectClasses = new LinkedList();
- private final List readOnlyAttributeNames = new LinkedList();
- private final Map attributes = new HashMap();
+ private final List objectClasses = new LinkedList<>();
+
+ // NOTE: names of read-only attributes are lower-cased to avoid case sensitivity issues
+ private final List readOnlyAttributeNames = new LinkedList<>();
+
+ private final Map attributes = new HashMap<>();
+
+ // Copy of "attributes" containing lower-cased keys
+ private final Map lowerCasedAttributes = new HashMap<>();
+
public String getUuid() {
return uuid;
@@ -49,7 +60,7 @@ public class LDAPObject {
}
public void addReadOnlyAttributeName(String readOnlyAttribute) {
- readOnlyAttributeNames.add(readOnlyAttribute);
+ readOnlyAttributeNames.add(readOnlyAttribute.toLowerCase());
}
public String getRdnAttributeName() {
@@ -62,21 +73,23 @@ public class LDAPObject {
public void setAttribute(String attributeName, Object attributeValue) {
attributes.put(attributeName, attributeValue);
+ lowerCasedAttributes.put(attributeName.toLowerCase(), attributeValue);
}
- public void removeAttribute(String name) {
- attributes.remove(name);
+ public Object getAttributeCaseInsensitive(String name) {
+ return lowerCasedAttributes.get(name.toLowerCase());
}
-
- public Object getAttribute(String name) {
- return attributes.get(name);
- }
-
- public String getAttributeAsString(String name) {
- Object attrValue = attributes.get(name);
+ public String getAttributeAsStringCaseInsensitive(String name) {
+ Object attrValue = lowerCasedAttributes.get(name.toLowerCase());
if (attrValue != null && !(attrValue instanceof String)) {
- throw new IllegalStateException("Expected String but attribute was " + attrValue + " of type " + attrValue.getClass().getName());
+ logger.warnf("Expected String but attribute '%s' has value '%s' of type '%s' ", name, attrValue, attrValue.getClass().getName());
+
+ if (attrValue instanceof Collection) {
+ Collection attrValues = (Collection) attrValue;
+ attrValue = attrValues.iterator().next();
+ logger.warnf("Returning just first founded value '%s' from the collection", attrValue);
+ }
}
return (String) attrValue;
diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/query/internal/LDAPIdentityQuery.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/query/internal/LDAPIdentityQuery.java
index b3fe0f7f27..ee292b17e7 100644
--- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/query/internal/LDAPIdentityQuery.java
+++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/query/internal/LDAPIdentityQuery.java
@@ -40,6 +40,7 @@ public class LDAPIdentityQuery {
private final Set returningLdapAttributes = new LinkedHashSet();
// Contains just those returningLdapAttributes, which are read-only. They will be marked as read-only in returned LDAPObject instances as well
+ // NOTE: names of attributes are lower-cased to avoid case sensitivity issues (LDAP searching is usually case-insensitive, so we want to be as well)
private final Set returningReadOnlyLdapAttributes = new LinkedHashSet();
private final Set objectClasses = new LinkedHashSet();
@@ -77,7 +78,7 @@ public class LDAPIdentityQuery {
}
public LDAPIdentityQuery addReturningReadOnlyLdapAttribute(String ldapAttributeName) {
- this.returningReadOnlyLdapAttributes.add(ldapAttributeName);
+ this.returningReadOnlyLdapAttributes.add(ldapAttributeName.toLowerCase());
return this;
}
diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/store/ldap/LDAPIdentityStore.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/store/ldap/LDAPIdentityStore.java
index 3dbfd0a399..64087ad139 100644
--- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/store/ldap/LDAPIdentityStore.java
+++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/store/ldap/LDAPIdentityStore.java
@@ -4,11 +4,11 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
-import java.util.TreeSet;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
@@ -382,12 +382,6 @@ public class LDAPIdentityStore implements IdentityStore {
NamingEnumeration extends Attribute> ldapAttributes = attributes.getAll();
- // Exact name of attributes might be different
- List uppercasedReadOnlyAttrNames = new ArrayList<>();
- for (String readonlyAttr : readOnlyAttrNames) {
- uppercasedReadOnlyAttrNames.add(readonlyAttr.toUpperCase());
- }
-
while (ldapAttributes.hasMore()) {
Attribute ldapAttribute = ldapAttributes.next();
@@ -403,7 +397,7 @@ public class LDAPIdentityStore implements IdentityStore {
Object uuidValue = ldapAttribute.get();
ldapObject.setUuid(this.operationManager.decodeEntryUUID(uuidValue));
} else {
- Set attrValues = new TreeSet<>();
+ Set attrValues = new LinkedHashSet<>();
NamingEnumeration> enumm = ldapAttribute.getAll();
while (enumm.hasMoreElements()) {
String attrVal = enumm.next().toString();
@@ -419,7 +413,8 @@ public class LDAPIdentityStore implements IdentityStore {
ldapObject.setAttribute(ldapAttributeName, attrValues);
}
- if (uppercasedReadOnlyAttrNames.contains(ldapAttributeName.toUpperCase())) {
+ // readOnlyAttrNames are lower-cased
+ if (readOnlyAttrNames.contains(ldapAttributeName.toLowerCase())) {
ldapObject.addReadOnlyAttributeName(ldapAttributeName);
}
}
@@ -443,7 +438,9 @@ public class LDAPIdentityStore implements IdentityStore {
for (Map.Entry attrEntry : ldapObject.getAttributes().entrySet()) {
String attrName = attrEntry.getKey();
Object attrValue = attrEntry.getValue();
- if (!ldapObject.getReadOnlyAttributeNames().contains(attrName) && (isCreate || !ldapObject.getRdnAttributeName().equalsIgnoreCase(attrName))) {
+
+ // ldapObject.getReadOnlyAttributeNames() are lower-cased
+ if (!ldapObject.getReadOnlyAttributeNames().contains(attrName.toLowerCase()) && (isCreate || !ldapObject.getRdnAttributeName().equalsIgnoreCase(attrName))) {
if (String.class.isInstance(attrValue)) {
if (attrValue.toString().trim().length() == 0) {
diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/FullNameLDAPFederationMapper.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/FullNameLDAPFederationMapper.java
index 7466778b5b..483cc06b68 100644
--- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/FullNameLDAPFederationMapper.java
+++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/FullNameLDAPFederationMapper.java
@@ -28,7 +28,7 @@ public class FullNameLDAPFederationMapper extends AbstractLDAPFederationMapper {
@Override
public void onImportUserFromLDAP(UserFederationMapperModel mapperModel, LDAPFederationProvider ldapProvider, LDAPObject ldapUser, UserModel user, RealmModel realm, boolean isCreate) {
String ldapFullNameAttrName = getLdapFullNameAttrName(mapperModel);
- String fullName = ldapUser.getAttributeAsString(ldapFullNameAttrName);
+ String fullName = ldapUser.getAttributeAsStringCaseInsensitive(ldapFullNameAttrName);
fullName = fullName.trim();
if (fullName != null && !fullName.trim().isEmpty()) {
int lastSpaceIndex = fullName.lastIndexOf(" ");
diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/RoleLDAPFederationMapper.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/RoleLDAPFederationMapper.java
index 165309c1e7..bc9fb05160 100644
--- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/RoleLDAPFederationMapper.java
+++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/RoleLDAPFederationMapper.java
@@ -74,7 +74,7 @@ public class RoleLDAPFederationMapper extends AbstractLDAPFederationMapper {
// Import role mappings from LDAP into Keycloak DB
String roleNameAttr = getRoleNameLdapAttribute(mapperModel);
for (LDAPObject ldapRole : ldapRoles) {
- String roleName = ldapRole.getAttributeAsString(roleNameAttr);
+ String roleName = ldapRole.getAttributeAsStringCaseInsensitive(roleNameAttr);
RoleContainerModel roleContainer = getTargetRoleContainer(mapperModel, realm);
RoleModel role = roleContainer.getRole(roleName);
@@ -103,7 +103,7 @@ public class RoleLDAPFederationMapper extends AbstractLDAPFederationMapper {
RoleContainerModel roleContainer = getTargetRoleContainer(mapperModel, realm);
String rolesRdnAttr = getRoleNameLdapAttribute(mapperModel);
for (LDAPObject ldapRole : ldapRoles) {
- String roleName = ldapRole.getAttributeAsString(rolesRdnAttr);
+ String roleName = ldapRole.getAttributeAsStringCaseInsensitive(rolesRdnAttr);
if (roleContainer.getRole(roleName) == null) {
logger.infof("Syncing role [%s] from LDAP to keycloak DB", roleName);
@@ -249,7 +249,7 @@ public class RoleLDAPFederationMapper extends AbstractLDAPFederationMapper {
protected Set getExistingMemberships(UserFederationMapperModel mapperModel, LDAPObject ldapRole) {
String memberAttrName = getMembershipLdapAttribute(mapperModel);
Set memberships = new TreeSet();
- Object existingMemberships = ldapRole.getAttribute(memberAttrName);
+ Object existingMemberships = ldapRole.getAttributeCaseInsensitive(memberAttrName);
if (existingMemberships != null) {
if (existingMemberships instanceof String) {
@@ -411,7 +411,7 @@ public class RoleLDAPFederationMapper extends AbstractLDAPFederationMapper {
Set roles = new HashSet();
String roleNameLdapAttr = getRoleNameLdapAttribute(mapperModel);
for (LDAPObject role : ldapRoles) {
- String roleName = role.getAttributeAsString(roleNameLdapAttr);
+ String roleName = role.getAttributeAsStringCaseInsensitive(roleNameLdapAttr);
RoleModel modelRole = roleContainer.getRole(roleName);
if (modelRole == null) {
// Add role to local DB
diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/UserAttributeLDAPFederationMapper.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/UserAttributeLDAPFederationMapper.java
index dd139b0b1f..c372769575 100644
--- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/UserAttributeLDAPFederationMapper.java
+++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/UserAttributeLDAPFederationMapper.java
@@ -1,6 +1,7 @@
package org.keycloak.federation.ldap.mappers;
import java.lang.reflect.Method;
+import java.util.HashMap;
import java.util.Map;
import org.keycloak.federation.ldap.LDAPFederationProvider;
@@ -12,6 +13,7 @@ import org.keycloak.models.RealmModel;
import org.keycloak.models.UserFederationMapperModel;
import org.keycloak.models.UserFederationProvider;
import org.keycloak.models.UserModel;
+import org.keycloak.models.utils.UserModelDelegate;
import org.keycloak.models.utils.reflection.Property;
import org.keycloak.models.utils.reflection.PropertyCriteria;
import org.keycloak.models.utils.reflection.PropertyQueries;
@@ -41,6 +43,7 @@ public class UserAttributeLDAPFederationMapper extends AbstractLDAPFederationMap
public static final String USER_MODEL_ATTRIBUTE = "user.model.attribute";
public static final String LDAP_ATTRIBUTE = "ldap.attribute";
public static final String READ_ONLY = "read.only";
+ public static final String ALWAYS_READ_VALUE_FROM_LDAP = "always.read.value.from.ldap";
@Override
@@ -48,7 +51,7 @@ public class UserAttributeLDAPFederationMapper extends AbstractLDAPFederationMap
String userModelAttrName = mapperModel.getConfig().get(USER_MODEL_ATTRIBUTE);
String ldapAttrName = mapperModel.getConfig().get(LDAP_ATTRIBUTE);
- Object ldapAttrValue = ldapUser.getAttribute(ldapAttrName);
+ Object ldapAttrValue = ldapUser.getAttributeCaseInsensitive(ldapAttrName);
if (ldapAttrValue != null && !ldapAttrValue.toString().trim().isEmpty()) {
Property