Delete user property from LDAP federation storage
#14.508 aperta il 20 set 2022
Metriche repository
- Star
- (34.398 stelle)
- Metriche merge PR
- (Merge medio 6g 19h) (384 PR mergiate in 30 g)
Descrizione
Describe the bug
If a user property mapped from LDAP User Federation Storage shall be updated/removed in the origin, Keycloak (KC) uses a LDAP replace operation
public void update(LDAPObject ldapObject)
checkRename(ldapObject);
BasicAttributes updatedAttributes = extractAttributesForSaving(ldapObject, false);
protected BasicAttributes extractAttributesForSaving(LDAPObject ldapObject, boolean isCreate)
...
} else {
// Text attribute
entryAttributes.put(createBasicAttribute(attrName, attrValue));
}
...
which is fine in principle, but if the attibute as no longer any value at all
private BasicAttribute createBasicAttribute(String attrName, Set attrValue)
BasicAttribute attr = new BasicAttribute(attrName);
for (String value : attrValue) {
if (value == null || value.trim().length() == 0) {
value = LDAPConstants.EMPTY_ATTRIBUTE_VALUE;
}
...
KC simply replaces the previous value with a single space as an empty value
public static final String EMPTY_ATTRIBUTE_VALUE = " ";
no matter if the attribute is required or not or has any format requirements or not.
So this causes a problem in situations, where the LDAP backend, e.g. ds389 is configured to check the uniqueness of attribute values, e.g the optional mail attribute of the inetOrgPerson Object Class. Instead of removing an erased email address value in the LDAP backend as well, KC sends a simple space value, which is fine for the first time, and the first time only. Trying to remove the email address of a second KC user again sends a single space value as email address update which violates the uniquness of the email attribute in the LDAP backend and therefore gets rejected.
Version
15.0.2
Expected behavior
If a non required LDAP attribute shall be removed
...
if (value == null || value.length() == 0) {
...
}
the attribute should get an DirContext.REMOVE_ATTRIBUTE context instead of the DirContext.REPLACE_ATTRIBUTE
public void modifyAttributes(String dn, NamingEnumeration attributes)
{
try {
List<ModificationItem> modItems = new ArrayList<ModificationItem>();
while (attributes.hasMore()) {
ModificationItem modItem = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, attributes.next());
modItems.add(modItem);
}
but at least, should not be replaced by an empty space.
Actual behavior
Keycloak sends a REPLACE operation with an empty space as attribute value invoking an exception:
17:29:17,875 WARN [org.keycloak.services.resources.admin.UserResource] (default task-10) Could not update user!: org.keycloak.models.ModelException: Could not modify attribute for DN [uid=user,ou=users,dc=iam,dc=company,dc=at]
at org.keycloak.storage.ldap.idm.store.ldap.LDAPOperationManager.modifyAttributes(LDAPOperationManager.java:616)
at org.keycloak.storage.ldap.idm.store.ldap.LDAPOperationManager.modifyAttributes(LDAPOperationManager.java:112)
at org.keycloak.storage.ldap.idm.store.ldap.LDAPIdentityStore.update(LDAPIdentityStore.java:156)
at org.keycloak.storage.ldap.mappers.LDAPTransaction.commitImpl(LDAPTransaction.java:53)
at org.keycloak.models.AbstractKeycloakTransaction.commit(AbstractKeycloakTransaction.java:48)
at org.keycloak.services.DefaultKeycloakTransactionManager.commit(DefaultKeycloakTransactionManager.java:146)
at org.keycloak.services.resources.admin.UserResource.updateUser(UserResource.java:194)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:138)
...
at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1377)
at org.xnio.XnioWorker$WorkerThreadFactory$1$1.run(XnioWorker.java:1280)
at java.lang.Thread.run(Thread.java:750)
Caused by: javax.naming.directory.InvalidAttributeValueException: [LDAP: error code 19 - Another entry with the same attribute value already exists (attribute: "mail ")]; remaining name 'uid=user,ou=users,dc=iam,dc=company,dc=at'
at com.sun.jndi.ldap.LdapCtx.mapErrorCode(LdapCtx.java:3274)
at com.sun.jndi.ldap.LdapCtx.processReturnCode(LdapCtx.java:3207)
at com.sun.jndi.ldap.LdapCtx.processReturnCode(LdapCtx.java:2998)
at com.sun.jndi.ldap.LdapCtx.c_modifyAttributes(LdapCtx.java:1503)
at com.sun.jndi.toolkit.ctx.ComponentDirContext.p_modifyAttributes(ComponentDirContext.java:277)
at com.sun.jndi.toolkit.ctx.PartialCompositeDirContext.modifyAttributes(PartialCompositeDirContext.java:192)
at javax.naming.directory.InitialDirContext.modifyAttributes(InitialDirContext.java:172)
at javax.naming.directory.InitialDirContext.modifyAttributes(InitialDirContext.java:172)
at org.keycloak.storage.ldap.idm.store.ldap.LDAPOperationManager$7.execute(LDAPOperationManager.java:597)
...
Furthermore, KC catches the exception and removes the attribute from its internal storage. So at least for some time (depending on the User Federation Storage Sync Settings), the LDAP backend and KC user storage, are out of sync!
How to Reproduce?
-
Create a new LDAP ds389 Backend
On a RHEL 7.9 system we are currently using
[user@host ~]$ rpm -qa 389* 389-console-1.1.19-6.el7.noarch 389-ds-base-1.3.10.2-16.el7_9.x86_64 389-adminutil-1.1.22-2.el7.x86_64 389-ds-base-libs-1.3.10.2-16.el7_9.x86_64 [user@host ~]$whereas, the server version shouldn't make any difference to reproduce the bug
-
Create a new KC realm with default settings
-
Add some
WRITEABLE,Red Hat Directory ServerVendor, User Federation Storage, with at least anemailuser-attribute-ldap-mapperand switch all its options, e.g.Is Mandatory in LDAPtoOFF. Leave all other non required User Federation Settings on default values, e.g.Sync Settingsshould beOFF -
Create 2 new users in the KC Admin Console including unique email addresses for both of them
-
Remove the email address of the first user
-
Try to remove the email address of the second user
Whereas operation nr. 6 succeeds, removing the email address of the second user should fail as described:
- email address is removed on KC side
- email address is NOT removed on LDAP side
If one clicks Removed imported users in the User Federation config and searches both users in the Admin Console again, the email address of the second user gets fetched again from the LDAP backend
Anything else?
This bug has already been reported at least once with KEYCLOAK-5117