keycloak-aplcache
Changes
federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProviderFactory.java 3(+2 -1)
federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/FullNameLDAPFederationMapper.java 14(+14 -0)
federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/FullNameLDAPFederationMapperFactory.java 31(+26 -5)
federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/HardcodedLDAPRoleMapperFactory.java 2(+1 -1)
federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/group/GroupLDAPFederationMapperFactory.java 2(+1 -1)
federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/role/RoleLDAPFederationMapperFactory.java 2(+1 -1)
federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/msad/MSADUserAccountControlMapperFactory.java 2(+1 -1)
federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/UserAttributeLDAPFederationMapperFactory.java 2(+1 -1)
services/src/main/java/org/keycloak/services/resources/admin/UserFederationProviderResource.java 2(+1 -1)
Details
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 0250b11..51a3c8c 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
@@ -165,7 +165,8 @@ public class LDAPFederationProviderFactory extends UserFederationEventAwareProvi
// For read-only LDAP, we map "cn" as full name
mapperModel = KeycloakModelUtils.createUserFederationMapperModel("full name", newProviderModel.getId(), FullNameLDAPFederationMapperFactory.PROVIDER_ID,
FullNameLDAPFederationMapper.LDAP_FULL_NAME_ATTRIBUTE, LDAPConstants.CN,
- UserAttributeLDAPFederationMapper.READ_ONLY, readOnly);
+ FullNameLDAPFederationMapper.READ_ONLY, readOnly,
+ FullNameLDAPFederationMapper.WRITE_ONLY, "false");
realm.addUserFederationMapper(mapperModel);
}
}
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 113a57d..b94b5b4 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
@@ -40,6 +40,8 @@ public class FullNameLDAPFederationMapper extends AbstractLDAPFederationMapper {
public static final String LDAP_FULL_NAME_ATTRIBUTE = "ldap.full.name.attribute";
public static final String READ_ONLY = "read.only";
+ public static final String WRITE_ONLY = "write.only";
+
public FullNameLDAPFederationMapper(UserFederationMapperModel mapperModel, LDAPFederationProvider ldapProvider, RealmModel realm) {
super(mapperModel, ldapProvider, realm);
@@ -47,6 +49,10 @@ public class FullNameLDAPFederationMapper extends AbstractLDAPFederationMapper {
@Override
public void onImportUserFromLDAP(LDAPObject ldapUser, UserModel user, boolean isCreate) {
+ if (isWriteOnly()) {
+ return;
+ }
+
String ldapFullNameAttrName = getLdapFullNameAttrName();
String fullName = ldapUser.getAttributeAsString(ldapFullNameAttrName);
if (fullName == null) {
@@ -117,6 +123,10 @@ public class FullNameLDAPFederationMapper extends AbstractLDAPFederationMapper {
@Override
public void beforeLDAPQuery(LDAPQuery query) {
+ if (isWriteOnly()) {
+ return;
+ }
+
String ldapFullNameAttrName = getLdapFullNameAttrName();
query.addReturningLdapAttribute(ldapFullNameAttrName);
@@ -178,4 +188,8 @@ public class FullNameLDAPFederationMapper extends AbstractLDAPFederationMapper {
private boolean isReadOnly() {
return parseBooleanParameter(mapperModel, READ_ONLY);
}
+
+ private boolean isWriteOnly() {
+ return parseBooleanParameter(mapperModel, WRITE_ONLY);
+ }
}
diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/FullNameLDAPFederationMapperFactory.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/FullNameLDAPFederationMapperFactory.java
index bc4b34a..32826b2 100755
--- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/FullNameLDAPFederationMapperFactory.java
+++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/FullNameLDAPFederationMapperFactory.java
@@ -43,12 +43,17 @@ public class FullNameLDAPFederationMapperFactory extends AbstractLDAPFederationM
static {
ProviderConfigProperty userModelAttribute = createConfigProperty(FullNameLDAPFederationMapper.LDAP_FULL_NAME_ATTRIBUTE, "LDAP Full Name Attribute",
- "Name of LDAP attribute, which contains fullName of user. In most cases it will be 'cn' ", ProviderConfigProperty.STRING_TYPE, null);
+ "Name of LDAP attribute, which contains fullName of user. Usually it will be 'cn' ", ProviderConfigProperty.STRING_TYPE, null);
configProperties.add(userModelAttribute);
- ProviderConfigProperty readOnly = createConfigProperty(UserAttributeLDAPFederationMapper.READ_ONLY, "Read Only",
+ ProviderConfigProperty readOnly = createConfigProperty(FullNameLDAPFederationMapper.READ_ONLY, "Read Only",
"For Read-only is data imported from LDAP to Keycloak DB, but it's not saved back to LDAP when user is updated in Keycloak.", ProviderConfigProperty.BOOLEAN_TYPE, null);
configProperties.add(readOnly);
+
+ ProviderConfigProperty writeOnly = createConfigProperty(FullNameLDAPFederationMapper.WRITE_ONLY, "Write Only",
+ "For Write-only is data propagated to LDAP when user is created or updated in Keycloak. But this mapper is not used to propagate data from LDAP back into Keycloak. " +
+ "This setting is useful if you configured separate firstName and lastName attribute mappers and you want to use those to read attribute from LDAP into Keycloak", ProviderConfigProperty.BOOLEAN_TYPE, null);
+ configProperties.add(writeOnly);
}
@Override
@@ -78,8 +83,11 @@ public class FullNameLDAPFederationMapperFactory extends AbstractLDAPFederationM
defaultValues.put(FullNameLDAPFederationMapper.LDAP_FULL_NAME_ATTRIBUTE, LDAPConstants.CN);
- String readOnly = config.getEditMode() == UserFederationProvider.EditMode.WRITABLE ? "false" : "true";
- defaultValues.put(UserAttributeLDAPFederationMapper.READ_ONLY, readOnly);
+ boolean readOnly = config.getEditMode() != UserFederationProvider.EditMode.WRITABLE;
+ defaultValues.put(FullNameLDAPFederationMapper.READ_ONLY, String.valueOf(readOnly));
+
+ String writeOnly = String.valueOf(!readOnly);
+ defaultValues.put(FullNameLDAPFederationMapper.WRITE_ONLY, writeOnly);
return defaultValues;
}
@@ -90,8 +98,21 @@ public class FullNameLDAPFederationMapperFactory extends AbstractLDAPFederationM
}
@Override
- public void validateConfig(RealmModel realm, UserFederationMapperModel mapperModel) throws FederationConfigValidationException {
+ public void validateConfig(RealmModel realm, UserFederationProviderModel fedProviderModel, UserFederationMapperModel mapperModel) throws FederationConfigValidationException {
checkMandatoryConfigAttribute(FullNameLDAPFederationMapper.LDAP_FULL_NAME_ATTRIBUTE, "LDAP Full Name Attribute", mapperModel);
+
+ boolean readOnly = AbstractLDAPFederationMapper.parseBooleanParameter(mapperModel, FullNameLDAPFederationMapper.READ_ONLY);
+ boolean writeOnly = AbstractLDAPFederationMapper.parseBooleanParameter(mapperModel, FullNameLDAPFederationMapper.WRITE_ONLY);
+
+ LDAPConfig cfg = new LDAPConfig(fedProviderModel.getConfig());
+ UserFederationProvider.EditMode editMode = cfg.getEditMode();
+
+ if (writeOnly && cfg.getEditMode() != UserFederationProvider.EditMode.WRITABLE) {
+ throw new FederationConfigValidationException("ldapErrorCantWriteOnlyForReadOnlyLdap");
+ }
+ if (writeOnly && readOnly) {
+ throw new FederationConfigValidationException("ldapErrorCantWriteOnlyAndReadOnly");
+ }
}
@Override
diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/HardcodedLDAPRoleMapperFactory.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/HardcodedLDAPRoleMapperFactory.java
index 73a4cef..1ca93f5 100644
--- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/HardcodedLDAPRoleMapperFactory.java
+++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/HardcodedLDAPRoleMapperFactory.java
@@ -77,7 +77,7 @@ public class HardcodedLDAPRoleMapperFactory extends AbstractLDAPFederationMapper
}
@Override
- public void validateConfig(RealmModel realm, UserFederationMapperModel mapperModel) throws FederationConfigValidationException {
+ public void validateConfig(RealmModel realm, UserFederationProviderModel fedProviderModel, UserFederationMapperModel mapperModel) throws FederationConfigValidationException {
String roleName = mapperModel.getConfig().get(HardcodedLDAPRoleMapper.ROLE);
if (roleName == null) {
throw new FederationConfigValidationException("Role can't be null");
diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/group/GroupLDAPFederationMapperFactory.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/group/GroupLDAPFederationMapperFactory.java
index e43a896..fb6056b 100644
--- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/group/GroupLDAPFederationMapperFactory.java
+++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/group/GroupLDAPFederationMapperFactory.java
@@ -186,7 +186,7 @@ public class GroupLDAPFederationMapperFactory extends AbstractLDAPFederationMapp
}
@Override
- public void validateConfig(RealmModel realm, UserFederationMapperModel mapperModel) throws FederationConfigValidationException {
+ public void validateConfig(RealmModel realm, UserFederationProviderModel fedProviderModel, UserFederationMapperModel mapperModel) throws FederationConfigValidationException {
checkMandatoryConfigAttribute(GroupMapperConfig.GROUPS_DN, "LDAP Groups DN", mapperModel);
checkMandatoryConfigAttribute(GroupMapperConfig.MODE, "Mode", mapperModel);
diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/role/RoleLDAPFederationMapperFactory.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/role/RoleLDAPFederationMapperFactory.java
index fa5b13d..25a7ad1 100644
--- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/role/RoleLDAPFederationMapperFactory.java
+++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/role/RoleLDAPFederationMapperFactory.java
@@ -180,7 +180,7 @@ public class RoleLDAPFederationMapperFactory extends AbstractLDAPFederationMappe
}
@Override
- public void validateConfig(RealmModel realm, UserFederationMapperModel mapperModel) throws FederationConfigValidationException {
+ public void validateConfig(RealmModel realm, UserFederationProviderModel fedProviderModel, UserFederationMapperModel mapperModel) throws FederationConfigValidationException {
checkMandatoryConfigAttribute(RoleMapperConfig.ROLES_DN, "LDAP Roles DN", mapperModel);
checkMandatoryConfigAttribute(RoleMapperConfig.MODE, "Mode", mapperModel);
diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/msad/MSADUserAccountControlMapperFactory.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/msad/MSADUserAccountControlMapperFactory.java
index 13c605b..36c494d 100644
--- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/msad/MSADUserAccountControlMapperFactory.java
+++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/msad/MSADUserAccountControlMapperFactory.java
@@ -75,7 +75,7 @@ public class MSADUserAccountControlMapperFactory extends AbstractLDAPFederationM
}
@Override
- public void validateConfig(RealmModel realm, UserFederationMapperModel mapperModel) throws FederationConfigValidationException {
+ public void validateConfig(RealmModel realm, UserFederationProviderModel fedProviderModel, UserFederationMapperModel mapperModel) throws FederationConfigValidationException {
}
@Override
diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/UserAttributeLDAPFederationMapperFactory.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/UserAttributeLDAPFederationMapperFactory.java
index f90eec3..b0a7faa 100755
--- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/UserAttributeLDAPFederationMapperFactory.java
+++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/UserAttributeLDAPFederationMapperFactory.java
@@ -101,7 +101,7 @@ public class UserAttributeLDAPFederationMapperFactory extends AbstractLDAPFedera
}
@Override
- public void validateConfig(RealmModel realm, UserFederationMapperModel mapperModel) throws FederationConfigValidationException {
+ public void validateConfig(RealmModel realm, UserFederationProviderModel fedProviderModel, UserFederationMapperModel mapperModel) throws FederationConfigValidationException {
checkMandatoryConfigAttribute(UserAttributeLDAPFederationMapper.USER_MODEL_ATTRIBUTE, "User Model Attribute", mapperModel);
checkMandatoryConfigAttribute(UserAttributeLDAPFederationMapper.LDAP_ATTRIBUTE, "LDAP Attribute", mapperModel);
}
diff --git a/server-spi/src/main/java/org/keycloak/mappers/UserFederationMapperFactory.java b/server-spi/src/main/java/org/keycloak/mappers/UserFederationMapperFactory.java
index dcf0b77..661462c 100644
--- a/server-spi/src/main/java/org/keycloak/mappers/UserFederationMapperFactory.java
+++ b/server-spi/src/main/java/org/keycloak/mappers/UserFederationMapperFactory.java
@@ -53,10 +53,11 @@ public interface UserFederationMapperFactory extends ProviderFactory<UserFederat
* Called when instance of mapperModel is created for this factory through admin endpoint
*
* @param realm
+ * @param fedProviderModel
* @param mapperModel
* @throws FederationConfigValidationException if configuration provided in mapperModel is not valid
*/
- void validateConfig(RealmModel realm, UserFederationMapperModel mapperModel) throws FederationConfigValidationException;
+ void validateConfig(RealmModel realm, UserFederationProviderModel fedProviderModel, UserFederationMapperModel mapperModel) throws FederationConfigValidationException;
/**
* Used to detect what are default values for ProviderConfigProperties specified during mapper creation
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 920fa57..29b4ae6 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
@@ -373,7 +373,7 @@ public class UserFederationProviderResource {
private void validateModel(UserFederationMapperModel model) {
try {
UserFederationMapperFactory mapperFactory = (UserFederationMapperFactory) session.getKeycloakSessionFactory().getProviderFactory(UserFederationMapper.class, model.getFederationMapperType());
- mapperFactory.validateConfig(realm, model);
+ mapperFactory.validateConfig(realm, federationProviderModel, model);
} catch (FederationConfigValidationException ex) {
logger.error(ex.getMessage());
Properties messages = AdminRoot.getMessages(session, realm, auth.getAuth().getToken().getLocale());
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/ldap/base/FederationProvidersIntegrationTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/ldap/base/FederationProvidersIntegrationTest.java
index 273a125..135c4f3 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/ldap/base/FederationProvidersIntegrationTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/ldap/base/FederationProvidersIntegrationTest.java
@@ -29,6 +29,7 @@ import org.keycloak.OAuth2Constants;
import org.keycloak.federation.ldap.LDAPConfig;
import org.keycloak.federation.ldap.LDAPFederationProvider;
import org.keycloak.federation.ldap.LDAPFederationProviderFactory;
+import org.keycloak.federation.ldap.LDAPUtils;
import org.keycloak.federation.ldap.idm.model.LDAPObject;
import org.keycloak.federation.ldap.mappers.FullNameLDAPFederationMapper;
import org.keycloak.federation.ldap.mappers.FullNameLDAPFederationMapperFactory;
@@ -521,7 +522,7 @@ public class FederationProvidersIntegrationTest {
UserFederationMapperModel fullNameMapperModel = KeycloakModelUtils.createUserFederationMapperModel("full name", ldapModel.getId(), FullNameLDAPFederationMapperFactory.PROVIDER_ID,
FullNameLDAPFederationMapper.LDAP_FULL_NAME_ATTRIBUTE, ldapFirstNameAttributeName,
- UserAttributeLDAPFederationMapper.READ_ONLY, "false");
+ FullNameLDAPFederationMapper.READ_ONLY, "false");
appRealm.addUserFederationMapper(fullNameMapperModel);
} finally {
keycloakRule.stopSession(session, true);
@@ -534,6 +535,36 @@ public class FederationProvidersIntegrationTest {
// Assert user is successfully imported in Keycloak DB now with correct firstName and lastName
FederationTestUtils.assertUserImported(session.users(), appRealm, "fullname", "James", "Dee", "fullname@email.org", "4578");
+ // change mapper to writeOnly
+ UserFederationMapperModel fullNameMapperModel = appRealm.getUserFederationMapperByName(ldapModel.getId(), "full name");
+ fullNameMapperModel.getConfig().put(FullNameLDAPFederationMapper.WRITE_ONLY, "true");
+ appRealm.updateUserFederationMapper(fullNameMapperModel);
+ } finally {
+ keycloakRule.stopSession(session, true);
+ }
+
+
+ // Assert changing user in Keycloak will change him in LDAP too...
+ session = keycloakRule.startSession();
+ try {
+ RealmModel appRealm = new RealmManager(session).getRealmByName("test");
+
+ UserModel fullnameUser = session.users().getUserByUsername("fullname", appRealm);
+ fullnameUser.setFirstName("James2");
+ fullnameUser.setLastName("Dee2");
+ } finally {
+ keycloakRule.stopSession(session, true);
+ }
+
+
+ // Assert changed user available in Keycloak
+ session = keycloakRule.startSession();
+ try {
+ RealmModel appRealm = new RealmManager(session).getRealmByName("test");
+
+ // Assert user is successfully imported in Keycloak DB now with correct firstName and lastName
+ FederationTestUtils.assertUserImported(session.users(), appRealm, "fullname", "James2", "Dee2", "fullname@email.org", "4578");
+
// Remove "fullnameUser" to assert he is removed from LDAP. Revert mappers to previous state
UserModel fullnameUser = session.users().getUserByUsername("fullname", appRealm);
session.users().removeUser(appRealm, fullnameUser);
diff --git a/themes/src/main/resources/theme/base/admin/messages/messages_en.properties b/themes/src/main/resources/theme/base/admin/messages/messages_en.properties
index dcb50c3..95e16db 100644
--- a/themes/src/main/resources/theme/base/admin/messages/messages_en.properties
+++ b/themes/src/main/resources/theme/base/admin/messages/messages_en.properties
@@ -10,3 +10,5 @@ invalidPasswordHistoryMessage=Invalid password: must not be equal to any of last
ldapErrorInvalidCustomFilter=Custom configured LDAP filter does not start with "(" or does not end with ")".
ldapErrorMissingClientId=Client ID needs to be provided in config when Realm Roles Mapping is not used.
ldapErrorCantPreserveGroupInheritanceWithUIDMembershipType=Not possible to preserve group inheritance and use UID membership type together.
+ldapErrorCantWriteOnlyForReadOnlyLdap=Can't set write only when LDAP provider mode is not WRITABLE
+ldapErrorCantWriteOnlyAndReadOnly=Can't set write-only and read-only together