Read-Only attributes should be modified if creation is delayed for LDAP
Closes https://github.com/keycloak/keycloak/issues/16848
This commit is contained in:
parent
42f66f2c6f
commit
5cdf4d5791
2 changed files with 33 additions and 4 deletions
|
@ -550,8 +550,8 @@ public class LDAPIdentityStore implements IdentityStore {
|
||||||
// Ignore empty attributes on create (changetype: add)
|
// Ignore empty attributes on create (changetype: add)
|
||||||
!(isCreate && attrValue.isEmpty()) &&
|
!(isCreate && attrValue.isEmpty()) &&
|
||||||
|
|
||||||
// Since we're extracting for saving, skip read-only attributes. ldapObject.getReadOnlyAttributeNames() are lower-cased
|
// Skip read-only attributes for saving if not create. ldapObject.getReadOnlyAttributeNames() are lower-cased
|
||||||
!ldapObject.getReadOnlyAttributeNames().contains(attrNameLowercased) &&
|
(isCreate || !ldapObject.getReadOnlyAttributeNames().contains(attrNameLowercased)) &&
|
||||||
|
|
||||||
// Only extract RDN for create since it can't be changed on update
|
// Only extract RDN for create since it can't be changed on update
|
||||||
(isCreate || !rdnAttrNamesLowerCased.contains(attrNameLowercased))
|
(isCreate || !rdnAttrNamesLowerCased.contains(attrNameLowercased))
|
||||||
|
|
|
@ -57,6 +57,7 @@ import org.keycloak.storage.UserStorageUtil;
|
||||||
import org.keycloak.storage.ldap.LDAPConfig;
|
import org.keycloak.storage.ldap.LDAPConfig;
|
||||||
import org.keycloak.storage.ldap.LDAPStorageProvider;
|
import org.keycloak.storage.ldap.LDAPStorageProvider;
|
||||||
import org.keycloak.storage.ldap.idm.model.LDAPObject;
|
import org.keycloak.storage.ldap.idm.model.LDAPObject;
|
||||||
|
import org.keycloak.storage.ldap.idm.query.internal.LDAPQuery;
|
||||||
import org.keycloak.storage.ldap.mappers.HardcodedLDAPAttributeMapper;
|
import org.keycloak.storage.ldap.mappers.HardcodedLDAPAttributeMapper;
|
||||||
import org.keycloak.storage.ldap.mappers.HardcodedLDAPAttributeMapperFactory;
|
import org.keycloak.storage.ldap.mappers.HardcodedLDAPAttributeMapperFactory;
|
||||||
import org.keycloak.storage.ldap.mappers.HardcodedLDAPGroupStorageMapper;
|
import org.keycloak.storage.ldap.mappers.HardcodedLDAPGroupStorageMapper;
|
||||||
|
@ -83,6 +84,7 @@ import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
import javax.naming.directory.SearchControls;
|
||||||
import org.hamcrest.MatcherAssert;
|
import org.hamcrest.MatcherAssert;
|
||||||
import org.hamcrest.Matchers;
|
import org.hamcrest.Matchers;
|
||||||
|
|
||||||
|
@ -189,16 +191,33 @@ public class LDAPProvidersIntegrationTest extends AbstractLDAPTest {
|
||||||
Assert.assertTrue(testRealm().users().search("newuser1").isEmpty());
|
Assert.assertTrue(testRealm().users().search("newuser1").isEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static LDAPObject searchObjectInBase(LDAPStorageProvider ldapProvider, String dn, String... attrs) {
|
||||||
|
LDAPQuery q = new LDAPQuery(ldapProvider)
|
||||||
|
.setSearchDn(dn)
|
||||||
|
.setSearchScope(SearchControls.OBJECT_SCOPE);
|
||||||
|
if (attrs != null) {
|
||||||
|
for (String attr: attrs) {
|
||||||
|
q.addReturningLdapAttribute(attr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return q.getFirstResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSyncRegistrationEmailRDNNoDefault() {
|
public void testSyncRegistrationEmailRDNNoDefault() {
|
||||||
testingClient.server().run(session -> {
|
testingClient.server().run(session -> {
|
||||||
// configure mail as mandatory but not forcing default
|
// configure mail as mandatory but not forcing default and create a hardcoded attribute for description
|
||||||
LDAPTestContext ctx = LDAPTestContext.init(session);
|
LDAPTestContext ctx = LDAPTestContext.init(session);
|
||||||
ComponentModel ldapModel = LDAPTestUtils.getLdapProviderModel(ctx.getRealm());
|
ComponentModel ldapModel = LDAPTestUtils.getLdapProviderModel(ctx.getRealm());
|
||||||
ComponentModel emailMapper = LDAPTestUtils.getSubcomponentByName(ctx.getRealm(), ldapModel, "email");
|
ComponentModel emailMapper = LDAPTestUtils.getSubcomponentByName(ctx.getRealm(), ldapModel, "email");
|
||||||
emailMapper.getConfig().putSingle(UserAttributeLDAPStorageMapper.IS_MANDATORY_IN_LDAP, "true");
|
emailMapper.getConfig().putSingle(UserAttributeLDAPStorageMapper.IS_MANDATORY_IN_LDAP, "true");
|
||||||
emailMapper.getConfig().putSingle(UserAttributeLDAPStorageMapper.FORCE_DEFAULT_VALUE, "false");
|
emailMapper.getConfig().putSingle(UserAttributeLDAPStorageMapper.FORCE_DEFAULT_VALUE, "false");
|
||||||
ctx.getRealm().updateComponent(emailMapper);
|
ctx.getRealm().updateComponent(emailMapper);
|
||||||
|
ComponentModel hardcodedMapperModel = KeycloakModelUtils.createComponentModel("hardcodedAttr-description", ctx.getLdapModel().getId(), HardcodedLDAPAttributeMapperFactory.PROVIDER_ID, LDAPStorageMapper.class.getName(),
|
||||||
|
HardcodedLDAPAttributeMapper.LDAP_ATTRIBUTE_NAME, "description",
|
||||||
|
HardcodedLDAPAttributeMapper.LDAP_ATTRIBUTE_VALUE, "some-${RANDOM}");
|
||||||
|
ctx.getRealm().addComponentModel(hardcodedMapperModel);
|
||||||
});
|
});
|
||||||
try {
|
try {
|
||||||
// test the user cannot be created without email
|
// test the user cannot be created without email
|
||||||
|
@ -219,7 +238,8 @@ public class LDAPProvidersIntegrationTest extends AbstractLDAPTest {
|
||||||
newUser1 = testRealm().users().get(userId).toRepresentation();
|
newUser1 = testRealm().users().get(userId).toRepresentation();
|
||||||
assertFederatedUserLink(newUser1);
|
assertFederatedUserLink(newUser1);
|
||||||
Assert.assertNotNull(newUser1.getAttributes().get(LDAPConstants.LDAP_ID));
|
Assert.assertNotNull(newUser1.getAttributes().get(LDAPConstants.LDAP_ID));
|
||||||
MatcherAssert.assertThat(newUser1.firstAttribute(LDAPConstants.LDAP_ENTRY_DN), Matchers.containsString("=newuser1,"));
|
final String userDN = newUser1.firstAttribute(LDAPConstants.LDAP_ENTRY_DN);
|
||||||
|
MatcherAssert.assertThat(userDN, Matchers.containsString("=newuser1,"));
|
||||||
Assert.assertEquals("newuser1@keycloak.org", newUser1.getEmail());
|
Assert.assertEquals("newuser1@keycloak.org", newUser1.getEmail());
|
||||||
String emailValueInLdap = testingClient.server().fetch(session -> {
|
String emailValueInLdap = testingClient.server().fetch(session -> {
|
||||||
LDAPTestContext ctx = LDAPTestContext.init(session);
|
LDAPTestContext ctx = LDAPTestContext.init(session);
|
||||||
|
@ -229,6 +249,13 @@ public class LDAPProvidersIntegrationTest extends AbstractLDAPTest {
|
||||||
return userLdap.getAttributeAsString(emailMapper.get(UserAttributeLDAPStorageMapper.LDAP_ATTRIBUTE));
|
return userLdap.getAttributeAsString(emailMapper.get(UserAttributeLDAPStorageMapper.LDAP_ATTRIBUTE));
|
||||||
}, String.class);
|
}, String.class);
|
||||||
Assert.assertEquals("newuser1@keycloak.org", emailValueInLdap);
|
Assert.assertEquals("newuser1@keycloak.org", emailValueInLdap);
|
||||||
|
// check description is in ldap assigned
|
||||||
|
String description = testingClient.server().fetch(session -> {
|
||||||
|
LDAPTestContext ctx = LDAPTestContext.init(session);
|
||||||
|
LDAPObject ldapUser = searchObjectInBase(ctx.getLdapProvider(), userDN, "description");
|
||||||
|
return ldapUser.getAttributeAsString("description");
|
||||||
|
}, String.class);
|
||||||
|
MatcherAssert.assertThat(description, Matchers.startsWith("some-"));
|
||||||
|
|
||||||
// remove the created user
|
// remove the created user
|
||||||
try (Response resp = testRealm().users().delete(userId)) {
|
try (Response resp = testRealm().users().delete(userId)) {
|
||||||
|
@ -244,6 +271,8 @@ public class LDAPProvidersIntegrationTest extends AbstractLDAPTest {
|
||||||
emailMapper.getConfig().putSingle(UserAttributeLDAPStorageMapper.IS_MANDATORY_IN_LDAP, "false");
|
emailMapper.getConfig().putSingle(UserAttributeLDAPStorageMapper.IS_MANDATORY_IN_LDAP, "false");
|
||||||
emailMapper.getConfig().remove(UserAttributeLDAPStorageMapper.FORCE_DEFAULT_VALUE);
|
emailMapper.getConfig().remove(UserAttributeLDAPStorageMapper.FORCE_DEFAULT_VALUE);
|
||||||
ctx.getRealm().updateComponent(emailMapper);
|
ctx.getRealm().updateComponent(emailMapper);
|
||||||
|
ComponentModel hardcodedMapperModel = LDAPTestUtils.getSubcomponentByName(ctx.getRealm(), ctx.getLdapModel(), "hardcodedAttr-description");
|
||||||
|
ctx.getRealm().removeComponent(hardcodedMapperModel);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue