I created in LDAP a user with uid user<.
After a successful login, the user (and user's folders) user_ and user< are created.
In user< I see the correct display name of the user but in user_ it's just user<.
I did some research and I understood where the problem came from, let's make an example with a user called user<
LDAP will call the updateUserDetails --> updateUserDetails methods when authenticating, which lead to the method hudson.model.User.get() which will try to resolve the canonicalIdresolve with the 3 existing types (UserIDCanonicalIdResolver, FullNameIdResolver and DefaultUserCanonicalIdResolver) and stop as soon as it gets a non-null id.
But the first one to be called is UserIDCanonicalIdResolver which will call the method loadUserByUsername that will set the resolving variable to true and loop back to the LDAP's updateUserDetails method and thus recall hudson.model.User.get() and retry to resolve the canonicalId.
But this time with the resolving variable set to true and therefore the first try with the UserIDCanonicalIdResolver will directly return null, the second resolver (FullNameIdResolver) will also return null. And the last one (DefaultUserCanonicalIdResolver) will replace special characters by underscore (DefaultUserCanonicalIdResolver.java#L46-L49)
This leads to the call of the method getOrCreateById, which will save our first user (user_).
Then we will return to our first call of UserIDCanonicalIdResolver#loadUserByUsername but LDAP return his LdapUserDetails (security/LDAPSecurityRealm.java#L824) where the username is still user< so it will continue with the getOrCreateById method and save our second user (user<).
LDAP shoudn't use hudson.model.User.get() (LDAPSecurityRealm.java#L782) but hudson.model.User.getById() since we know we're passing a User ID.