From ffe40614192a9d1226778d9f62300df76e0d9432 Mon Sep 17 00:00:00 2001
From: srosse <stephane.rosse@frentix.com>
Date: Thu, 13 Aug 2020 17:39:01 +0200
Subject: [PATCH] OO-4794: some adaptation to LDAP for auto/manual generated
 id. name

Adapt LDAP for auto/manual generated id. name, add unit tests for
special workflow (user which are created without authentication and get
LDAP token afterwards on login)
---
 .../user/_i18n/LocalStrings_cs.properties     |   2 +-
 .../user/_i18n/LocalStrings_da.properties     |   2 +-
 .../user/_i18n/LocalStrings_de.properties     |   2 +-
 .../user/_i18n/LocalStrings_el.properties     |   2 +-
 .../user/_i18n/LocalStrings_en.properties     |   2 +-
 .../user/_i18n/LocalStrings_es.properties     |   2 +-
 .../user/_i18n/LocalStrings_fr.properties     |   2 +-
 .../user/_i18n/LocalStrings_it.properties     |   2 +-
 .../user/_i18n/LocalStrings_jp.properties     |   2 +-
 .../user/_i18n/LocalStrings_lt.properties     |   2 +-
 .../user/_i18n/LocalStrings_nl_NL.properties  |   2 +-
 .../user/_i18n/LocalStrings_pl.properties     |   2 +-
 .../user/_i18n/LocalStrings_pt_BR.properties  |   2 +-
 .../user/_i18n/LocalStrings_ru.properties     |   2 +-
 .../user/_i18n/LocalStrings_sq.properties     |   2 +-
 .../user/_i18n/LocalStrings_zh_CN.properties  |   2 +-
 .../user/_i18n/LocalStrings_zh_TW.properties  |   2 +-
 .../olat/basesecurity/BaseSecurityModule.java |  14 ++
 .../notifications/PersonalRSSServlet.java     |   4 +-
 .../ui/tool/AssessedIdentityListProvider.java |   2 +-
 .../IdentityListCourseNodeController.java     |   4 +-
 .../GTAIdentityListCourseNodeController.java  |   2 +-
 .../IQIdentityListCourseNodeController.java   |   4 +-
 .../STIdentityListCourseNodeController.java   |   2 +-
 .../olat/group/manager/MemberViewQueries.java |  25 +--
 .../java/org/olat/ldap/LDAPLoginManager.java  |   2 -
 .../org/olat/ldap/_spring/ldapContext.xml     |   1 +
 .../java/org/olat/ldap/manager/LDAPDAO.java   |   4 +-
 .../ldap/manager/LDAPLoginManagerImpl.java    |  74 ++++-----
 .../org/olat/ldap/ui/LDAPAdminController.java |   2 +-
 .../org/olat/login/auth/OLATAuthManager.java  |   2 +-
 .../login/validation/UsernameInUseRule.java   |   5 +-
 .../olat/modules/coach/ui/UserSearchForm.java |   8 +-
 .../olat/registration/PwChangeController.java |   4 +-
 .../resources/serviceconfig/olat.properties   |   1 +
 .../GetIdentitiesByPowerSearchTest.java       |  18 +--
 .../java/org/olat/ldap/LDAPLoginTest.java     |  27 +++-
 .../org/olat/ldap/junittestdata/olattest.ldif |  89 +++++++++++
 .../ldap/manager/LDAPLoginManagerTest.java    | 148 +++++++++++++++++-
 39 files changed, 349 insertions(+), 127 deletions(-)

diff --git a/src/main/java/org/olat/admin/user/_i18n/LocalStrings_cs.properties b/src/main/java/org/olat/admin/user/_i18n/LocalStrings_cs.properties
index 793d8de1bbf..5066ec12f6f 100644
--- a/src/main/java/org/olat/admin/user/_i18n/LocalStrings_cs.properties
+++ b/src/main/java/org/olat/admin/user/_i18n/LocalStrings_cs.properties
@@ -41,7 +41,7 @@ new.error.loginname.choosen=U\u017Eivatelsk\u00E9 jm\u00E9no u\u017E je pou\u017
 new.error.loginname.empty=$org.olat.user\:form.checkUsername
 new.error.password.nomatch=Hesla nejsou toto\u017En\u00E1.
 new.error.property.invalid=Chybn\u00FD vstup\!
-new.form.auth=Heslo do OLATu
+new.form.auth=Heslo do OpenOlatu
 new.form.auth.false=Nevytv\u00E1\u0159et nyn\u00ED
 new.form.auth.true=Vytvo\u0159it nyn\u00ED
 new.form.language=Jazyk
diff --git a/src/main/java/org/olat/admin/user/_i18n/LocalStrings_da.properties b/src/main/java/org/olat/admin/user/_i18n/LocalStrings_da.properties
index b8e431cfbfd..523a3e00d0d 100644
--- a/src/main/java/org/olat/admin/user/_i18n/LocalStrings_da.properties
+++ b/src/main/java/org/olat/admin/user/_i18n/LocalStrings_da.properties
@@ -34,7 +34,7 @@ new.error.email.choosen=Denne e-mailadresse findes allerede. Der er ingen grund
 new.error.loginname.choosen=Dette brugernavn er allerede brugt. V\u00E6lg venligst et andet.
 new.error.loginname.empty=$org.olat.user\:form.checkUsername
 new.error.password.nomatch=De indtastede kodeord passer ikke sammen.
-new.form.auth=OLAT kodeord
+new.form.auth=OpenOlat kodeord
 new.form.auth.false=Opret ikke nu
 new.form.auth.true=Opret nu
 new.form.language=Sprog
diff --git a/src/main/java/org/olat/admin/user/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/admin/user/_i18n/LocalStrings_de.properties
index 9725b337a70..8ea86bd3ded 100644
--- a/src/main/java/org/olat/admin/user/_i18n/LocalStrings_de.properties
+++ b/src/main/java/org/olat/admin/user/_i18n/LocalStrings_de.properties
@@ -72,7 +72,7 @@ new.error.loginname.choosen=Dieser Benutzername ist bereits vergeben. Versuchen
 new.error.loginname.empty=$org.olat.user\:form.checkUsername
 new.error.password.nomatch=Die beiden Passw\u00F6rter stimmen nicht \u00FCberein.
 new.error.property.invalid=Invalid input\!
-new.form.auth=OLAT-Passwort
+new.form.auth=OpenOlat-Passwort
 new.form.auth.false=nicht jetzt anlegen
 new.form.auth.true=anlegen
 new.form.language=Sprache
diff --git a/src/main/java/org/olat/admin/user/_i18n/LocalStrings_el.properties b/src/main/java/org/olat/admin/user/_i18n/LocalStrings_el.properties
index eb00d3303cd..d8608f8cbaf 100644
--- a/src/main/java/org/olat/admin/user/_i18n/LocalStrings_el.properties
+++ b/src/main/java/org/olat/admin/user/_i18n/LocalStrings_el.properties
@@ -48,7 +48,7 @@ new.error.loginname.choosen=\u03A4\u03BF \u03CC\u03BD\u03BF\u03BC\u03B1 \u03C7\u
 new.error.loginname.empty=$org.olat.user\:form.checkUsername
 new.error.password.nomatch=\u039F\u03B9 \u03B4\u03CD\u03BF \u03BA\u03C9\u03B4\u03B9\u03BA\u03BF\u03AF \u03C0\u03C1\u03CC\u03C3\u03B2\u03B1\u03C3\u03B7\u03C2 \u03B4\u03B5\u03BD \u03C4\u03B1\u03B9\u03C1\u03B9\u03AC\u03B6\u03BF\u03C5\u03BD.
 new.error.property.invalid=\u039C\u03B7 \u03AD\u03B3\u03BA\u03C5\u03C1\u03B7 \u03B5\u03B9\u03C3\u03B1\u03B3\u03C9\u03B3\u03AE\!
-new.form.auth=\u039A\u03C9\u03B4\u03B9\u03BA\u03CC\u03C2 \u03C0\u03C1\u03CC\u03C3\u03B2\u03B1\u03C3\u03B7\u03C2 \u03C3\u03C4\u03BF OLAT
+new.form.auth=\u039A\u03C9\u03B4\u03B9\u03BA\u03CC\u03C2 \u03C0\u03C1\u03CC\u03C3\u03B2\u03B1\u03C3\u03B7\u03C2 \u03C3\u03C4\u03BF OpenOlat
 new.form.auth.false=\u0394\u03B7\u03BC\u03B9\u03BF\u03C5\u03C1\u03B3\u03AF\u03B1 \u03B1\u03C1\u03B3\u03CC\u03C4\u03B5\u03C1\u03B1
 new.form.auth.true=\u0394\u03B7\u03BC\u03B9\u03BF\u03C5\u03C1\u03B3\u03AF\u03B1
 new.form.language=\u0393\u03BB\u03CE\u03C3\u03C3\u03B1
diff --git a/src/main/java/org/olat/admin/user/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/admin/user/_i18n/LocalStrings_en.properties
index 1f7a003053e..a95cf05b5cc 100644
--- a/src/main/java/org/olat/admin/user/_i18n/LocalStrings_en.properties
+++ b/src/main/java/org/olat/admin/user/_i18n/LocalStrings_en.properties
@@ -71,7 +71,7 @@ new.error.loginname.choosen=This user name has already been allocated. Please tr
 new.error.loginname.empty=$org.olat.user\:form.checkUsername
 new.error.password.nomatch=The two passwords do not match.
 new.error.property.invalid=Invalid input\!
-new.form.auth=OLAT password
+new.form.auth=OpenOlat password
 new.form.auth.false=Do not create now
 new.form.auth.true=Create now
 new.form.language=Language
diff --git a/src/main/java/org/olat/admin/user/_i18n/LocalStrings_es.properties b/src/main/java/org/olat/admin/user/_i18n/LocalStrings_es.properties
index deba5d7618f..f509a4ff485 100644
--- a/src/main/java/org/olat/admin/user/_i18n/LocalStrings_es.properties
+++ b/src/main/java/org/olat/admin/user/_i18n/LocalStrings_es.properties
@@ -42,7 +42,7 @@ new.error.loginname.choosen=Este nombre de usuario ya ha sido asignado. Por favo
 new.error.loginname.empty=$org.olat.user\:form.checkUsername
 new.error.password.nomatch=Las dos contrase\u00F1as no coinciden.
 new.error.property.invalid=\u00A1Entrada inv\u00E1lida\!
-new.form.auth=Contrase\u00F1a OLAT
+new.form.auth=Contrase\u00F1a OpenOlat
 new.form.auth.false=No crear ahora
 new.form.auth.true=Crear ahora
 new.form.language=Idioma
diff --git a/src/main/java/org/olat/admin/user/_i18n/LocalStrings_fr.properties b/src/main/java/org/olat/admin/user/_i18n/LocalStrings_fr.properties
index f31f97cbcd7..c31652aa7b4 100644
--- a/src/main/java/org/olat/admin/user/_i18n/LocalStrings_fr.properties
+++ b/src/main/java/org/olat/admin/user/_i18n/LocalStrings_fr.properties
@@ -72,7 +72,7 @@ new.error.loginname.choosen=Ce nom d'utilisateur est d\u00E9j\u00E0 utilis\u00E9
 new.error.loginname.empty=$org.olat.user\:form.checkUsername
 new.error.password.nomatch=Les deux mots de passe ne sont pas identiques.  
 new.error.property.invalid=Invalid input\!
-new.form.auth=mot de passe OLAT
+new.form.auth=Mot de passe OpenOlat
 new.form.auth.false=cr\u00E9er plus tard
 new.form.auth.true=cr\u00E9er
 new.form.language=Langue
diff --git a/src/main/java/org/olat/admin/user/_i18n/LocalStrings_it.properties b/src/main/java/org/olat/admin/user/_i18n/LocalStrings_it.properties
index 86dd81fd987..3e61682f082 100644
--- a/src/main/java/org/olat/admin/user/_i18n/LocalStrings_it.properties
+++ b/src/main/java/org/olat/admin/user/_i18n/LocalStrings_it.properties
@@ -57,7 +57,7 @@ new.error.loginname.choosen=Questo nome d'utente \u00E8 gi\u00E0 in uso. Provi c
 new.error.loginname.empty=$org.olat.user\:form.checkUsername
 new.error.password.nomatch=Le due password non coincidono.
 new.error.property.invalid=Immissione non valida\!
-new.form.auth=Password OLAT
+new.form.auth=Password OpenOlat
 new.form.auth.false=non stabilire ora
 new.form.auth.true=stabilire
 new.form.language=Lingua
diff --git a/src/main/java/org/olat/admin/user/_i18n/LocalStrings_jp.properties b/src/main/java/org/olat/admin/user/_i18n/LocalStrings_jp.properties
index d551847a898..7ee856e65a7 100644
--- a/src/main/java/org/olat/admin/user/_i18n/LocalStrings_jp.properties
+++ b/src/main/java/org/olat/admin/user/_i18n/LocalStrings_jp.properties
@@ -49,7 +49,7 @@ new.error.loginname.choosen=\u3053\u306E\u30E6\u30FC\u30B6\u306F\u3001\u3059\u30
 new.error.loginname.empty=$org.olat.user\:form.checkUsername
 new.error.password.nomatch=2\u3064\u306E\u30D1\u30B9\u30EF\u30FC\u30C9\u304C\u5408\u81F4\u3057\u307E\u305B\u3093\u3002
 new.error.property.invalid=\u7121\u52B9\u306A\u5165\u529B\u3067\u3059\!
-new.form.auth=OLAT\u30D1\u30B9\u30EF\u30FC\u30C9
+new.form.auth=OpenOlat\u30D1\u30B9\u30EF\u30FC\u30C9
 new.form.auth.false=\u3059\u3050\u306B\u4F5C\u6210\u3057\u306A\u3044
 new.form.auth.true=\u3059\u3050\u306B\u4F5C\u6210\u3059\u308B
 new.form.language=\u8A00\u8A9E
diff --git a/src/main/java/org/olat/admin/user/_i18n/LocalStrings_lt.properties b/src/main/java/org/olat/admin/user/_i18n/LocalStrings_lt.properties
index f114d7b352f..b58346a4eeb 100644
--- a/src/main/java/org/olat/admin/user/_i18n/LocalStrings_lt.properties
+++ b/src/main/java/org/olat/admin/user/_i18n/LocalStrings_lt.properties
@@ -31,7 +31,7 @@ new.error.email.choosen=Toks naudotojo el. pa\u0161to adresas jau egzistuoja. Pr
 new.error.loginname.choosen=Toks naudotojo vardas jau egzistuoja. Pra\u0161ome \u012Fvesti kit\u0105.
 new.error.loginname.empty=$org.olat.user\:form.checkUsername
 new.error.password.nomatch=Slapta\u017Eodiai nesutampa.
-new.form.auth=OLAT slapta\u017Eodis
+new.form.auth=OpenOlat slapta\u017Eodis
 new.form.auth.false=Nesukurti dabar
 new.form.auth.true=Sukurti dabar
 new.form.language=Kalba
diff --git a/src/main/java/org/olat/admin/user/_i18n/LocalStrings_nl_NL.properties b/src/main/java/org/olat/admin/user/_i18n/LocalStrings_nl_NL.properties
index 93c4e36359d..012fff3082b 100644
--- a/src/main/java/org/olat/admin/user/_i18n/LocalStrings_nl_NL.properties
+++ b/src/main/java/org/olat/admin/user/_i18n/LocalStrings_nl_NL.properties
@@ -51,7 +51,7 @@ new.error.loginname.choosen=Deze gebruikersnaam werd al toegewezen. Gelieve een
 new.error.loginname.empty=$org.olat.user\:form.checkUsername
 new.error.password.nomatch=De twee wachtwoorden zijn niet gelijk.
 new.error.property.invalid=Ongeldige input\!
-new.form.auth=OLAT wachtwoord
+new.form.auth=OpenOlat wachtwoord
 new.form.auth.false=Nu niet aanmaken
 new.form.auth.true=Nu aanmaken
 new.form.language=Taal
diff --git a/src/main/java/org/olat/admin/user/_i18n/LocalStrings_pl.properties b/src/main/java/org/olat/admin/user/_i18n/LocalStrings_pl.properties
index faa868a3d70..2beaa27996b 100644
--- a/src/main/java/org/olat/admin/user/_i18n/LocalStrings_pl.properties
+++ b/src/main/java/org/olat/admin/user/_i18n/LocalStrings_pl.properties
@@ -52,7 +52,7 @@ new.error.loginname.choosen=U\u017Cytkownik o podanej nazwie ju\u017C istnieje.
 new.error.loginname.empty=$org.olat.user\:form.checkUsername
 new.error.password.nomatch=Podane has\u0142a s\u0105 r\u00F3\u017Cne
 new.error.property.invalid=B\u0142\u0119dna warto\u015B\u0107\!
-new.form.auth=Has\u0142o OLAT
+new.form.auth=Has\u0142o OpenOlat
 new.form.auth.false=Nie tw\u00F3rz teraz
 new.form.auth.true=Utw\u00F3rz teraz
 new.form.language=J\u0119zyk
diff --git a/src/main/java/org/olat/admin/user/_i18n/LocalStrings_pt_BR.properties b/src/main/java/org/olat/admin/user/_i18n/LocalStrings_pt_BR.properties
index 0f6a7f506ba..50cd8ab9d4a 100644
--- a/src/main/java/org/olat/admin/user/_i18n/LocalStrings_pt_BR.properties
+++ b/src/main/java/org/olat/admin/user/_i18n/LocalStrings_pt_BR.properties
@@ -71,7 +71,7 @@ new.error.loginname.choosen=Este nome de usu\u00E1rio j\u00E1 foi alocado. Por f
 new.error.loginname.empty=$org.olat.user\:form.checkUsername
 new.error.password.nomatch=As duas senhas n\u00E3o combinam.
 new.error.property.invalid=Entrada invalida\!
-new.form.auth=OLAT password
+new.form.auth=OpenOlat password
 new.form.auth.false=N\u00E3o crie agora
 new.form.auth.true=Crie agora
 new.form.language=Idioma
diff --git a/src/main/java/org/olat/admin/user/_i18n/LocalStrings_ru.properties b/src/main/java/org/olat/admin/user/_i18n/LocalStrings_ru.properties
index be7d1cc6556..b6d50f2f1bb 100644
--- a/src/main/java/org/olat/admin/user/_i18n/LocalStrings_ru.properties
+++ b/src/main/java/org/olat/admin/user/_i18n/LocalStrings_ru.properties
@@ -34,7 +34,7 @@ new.error.email.choosen=\u0414\u0430\u043D\u043D\u044B\u0439 \u0430\u0434\u0440\
 new.error.loginname.choosen=\u0414\u0430\u043D\u043D\u043E\u0435 \u0438\u043C\u044F \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F \u0443\u0436\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442. \u041F\u043E\u043F\u0440\u043E\u0431\u0443\u0439\u0442\u0435 \u0437\u0430\u0434\u0430\u0442\u044C \u0434\u0440\u0443\u0433\u043E\u0435 \u0438\u043C\u044F \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F.  
 new.error.loginname.empty=$org.olat.user\:form.checkUsername
 new.error.password.nomatch=\u041E\u0431\u0430 \u043D\u043E\u0432\u044B\u0445 \u043F\u0430\u0440\u043E\u043B\u044F \u043D\u0435 \u0441\u043E\u0432\u043F\u0430\u0434\u0430\u044E\u0442.
-new.form.auth=\u041F\u0430\u0440\u043E\u043B\u044C \u0434\u043B\u044F OLAT
+new.form.auth=\u041F\u0430\u0440\u043E\u043B\u044C \u0434\u043B\u044F OpenOlat
 new.form.auth.false=\u043F\u043E\u0437\u0436\u0435 \u0437\u0430\u043D\u0435\u0441\u0442\u0438 \u0432 \u0441\u0438\u0441\u0442\u0435\u043C\u0443 
 new.form.auth.true=\u0437\u0430\u043D\u0435\u0441\u0442\u0438 \u0432 \u0441\u0438\u0441\u0442\u0435\u043C\u0443
 new.form.language=\u042F\u0437\u044B\u043A
diff --git a/src/main/java/org/olat/admin/user/_i18n/LocalStrings_sq.properties b/src/main/java/org/olat/admin/user/_i18n/LocalStrings_sq.properties
index a9967d307bd..656aedaddb0 100644
--- a/src/main/java/org/olat/admin/user/_i18n/LocalStrings_sq.properties
+++ b/src/main/java/org/olat/admin/user/_i18n/LocalStrings_sq.properties
@@ -35,7 +35,7 @@ new.error.loginname.choosen=Ky shfryt\u00EBzues tani \u00EBsht\u00EB i z\u00EBn\
 new.error.loginname.empty=$org.olat.user\:form.checkUsername
 new.error.password.nomatch=Dy fjal\u00EBkalimet nuk p\u00EBrputhen.
 new.error.property.invalid=E hyr\u00EB e pavlefshme\!
-new.form.auth=OLAT fjal\u00EBkalimi
+new.form.auth=OpenOlat fjal\u00EBkalimi
 new.form.auth.false=Mos e krijo q\u00EB tani
 new.form.auth.true=Krijoje tani
 new.form.language=Gjuha
diff --git a/src/main/java/org/olat/admin/user/_i18n/LocalStrings_zh_CN.properties b/src/main/java/org/olat/admin/user/_i18n/LocalStrings_zh_CN.properties
index ec8d97ecce1..8c3dc8ea765 100644
--- a/src/main/java/org/olat/admin/user/_i18n/LocalStrings_zh_CN.properties
+++ b/src/main/java/org/olat/admin/user/_i18n/LocalStrings_zh_CN.properties
@@ -50,7 +50,7 @@ new.error.loginname.choosen=\u8BE5\u7528\u6237\u540D\u5DF2\u6CE8\u518C\uFF0C\u8B
 new.error.loginname.empty=$org.olat.user\:form.checkUsername
 new.error.password.nomatch=\u5BC6\u7801\u4E0D\u4E00\u81F4\u3002
 new.error.property.invalid=\u65E0\u6548\u8F93\u5165\uFF01
-new.form.auth=OLAT\u5BC6\u7801
+new.form.auth=OpenOlat\u5BC6\u7801
 new.form.auth.false=\u73B0\u5728\u4E0D\u521B\u5EFA
 new.form.auth.true=\u73B0\u5728\u521B\u5EFA
 new.form.language=\u8BED\u8A00
diff --git a/src/main/java/org/olat/admin/user/_i18n/LocalStrings_zh_TW.properties b/src/main/java/org/olat/admin/user/_i18n/LocalStrings_zh_TW.properties
index 1796eef9ff4..b6bf6d4b0e2 100644
--- a/src/main/java/org/olat/admin/user/_i18n/LocalStrings_zh_TW.properties
+++ b/src/main/java/org/olat/admin/user/_i18n/LocalStrings_zh_TW.properties
@@ -47,7 +47,7 @@ new.error.loginname.choosen=\u9019\u500B\u4F7F\u7528\u8005\u540D\u7A31\u5DF2\u88
 new.error.loginname.empty=$org.olat.user\:form.checkUsername
 new.error.password.nomatch=\u9019\u5169\u500B\u5BC6\u78BC\u4E0D\u4E00\u81F4\u3002
 new.error.property.invalid=\u4E0D\u6B63\u78BA\u7684\u8F38\u5165\uFF01
-new.form.auth=OLAT \u5BC6\u78BC
+new.form.auth=OpenOlat \u5BC6\u78BC
 new.form.auth.false=\u4E0D\u8981\u73FE\u5728\u5EFA\u7ACB
 new.form.auth.true=\u73FE\u5728\u5EFA\u7ACB
 new.form.language=\u8A9E\u8A00
diff --git a/src/main/java/org/olat/basesecurity/BaseSecurityModule.java b/src/main/java/org/olat/basesecurity/BaseSecurityModule.java
index cc48eb0c429..83e9d4ae09e 100644
--- a/src/main/java/org/olat/basesecurity/BaseSecurityModule.java
+++ b/src/main/java/org/olat/basesecurity/BaseSecurityModule.java
@@ -113,6 +113,8 @@ public class BaseSecurityModule extends AbstractSpringModule {
 	
 	private static final String WIKI_ENABLED = "wiki";
 	
+	private static final String IDENTITY_NAME = "identity.name";
+	
 	/**
 	 * default values
 	 */
@@ -310,6 +312,9 @@ public class BaseSecurityModule extends AbstractSpringModule {
 		userSearchMaxResults = getStringPropertyValue(USERSEARCH_MAXRESULTS, userSearchMaxResults);
 		userInfosTunnelCourseBuildingBlock = getStringPropertyValue(USERINFOS_TUNNEL_CBB, userInfosTunnelCourseBuildingBlock);
 		wikiEnabled = getStringPropertyValue(WIKI_ENABLED, wikiEnabled);
+		
+		identityName = getStringPropertyValue(IDENTITY_NAME, identityName);
+		log.info("Identity.name generator: {}", identityName);
 	}
 	
 	public static final OrganisationRoles[] getUserAllowedRoles() {
@@ -322,6 +327,15 @@ public class BaseSecurityModule extends AbstractSpringModule {
 		return "auto".equals(identityName);
 	}
 	
+	public String getIdentityName() {
+		return identityName;
+	}
+
+	public void setIdentityName(String identityName) {
+		this.identityName = identityName;
+		setStringProperty(IDENTITY_NAME, identityName, true);
+	}
+
 	public boolean isUserAllowedAdminProps(Roles roles) {
 		if(roles == null || roles.isInvitee() || roles.isGuestOnly()) return false;
 
diff --git a/src/main/java/org/olat/core/commons/services/notifications/PersonalRSSServlet.java b/src/main/java/org/olat/core/commons/services/notifications/PersonalRSSServlet.java
index 488ec63e53b..c8f1b96a29f 100644
--- a/src/main/java/org/olat/core/commons/services/notifications/PersonalRSSServlet.java
+++ b/src/main/java/org/olat/core/commons/services/notifications/PersonalRSSServlet.java
@@ -111,9 +111,9 @@ public class PersonalRSSServlet extends HttpServlet {
 			if (encoding == null) {
 				encoding = DEFAULT_ENCODING;
 				if (log.isDebugEnabled()) {
-					log.debug("Feed encoding::" + encoding);
+					log.debug("Feed encoding::{}", encoding);
 				}
-				log.warn("No encoding provided by feed::" + feed.getClass().getCanonicalName() + " Using utf-8 as default.");
+				log.warn("No encoding provided by feed::{} Using utf-8 as default.", feed.getClass().getCanonicalName());
 			}
 			response.setCharacterEncoding(encoding);
 			response.setContentType("application/rss+xml");
diff --git a/src/main/java/org/olat/course/assessment/ui/tool/AssessedIdentityListProvider.java b/src/main/java/org/olat/course/assessment/ui/tool/AssessedIdentityListProvider.java
index c2c4b052eec..2d65f8cd4a8 100644
--- a/src/main/java/org/olat/course/assessment/ui/tool/AssessedIdentityListProvider.java
+++ b/src/main/java/org/olat/course/assessment/ui/tool/AssessedIdentityListProvider.java
@@ -84,7 +84,7 @@ public class AssessedIdentityListProvider implements ListProvider {
 			maxEntries--;
 			IdentityShort ident = it_res.next();
 			String key = ident.getKey().toString();
-			String displayKey = null;//TODO username
+			String displayKey = ident.getName();//TODO username
 			String displayText = userManager.getUserDisplayName(ident);
 			receiver.addEntry(key, displayKey, displayText, CSSHelper.CSS_CLASS_USER);
 		}					
diff --git a/src/main/java/org/olat/course/assessment/ui/tool/IdentityListCourseNodeController.java b/src/main/java/org/olat/course/assessment/ui/tool/IdentityListCourseNodeController.java
index 31ec7a23225..a98c2f5b867 100644
--- a/src/main/java/org/olat/course/assessment/ui/tool/IdentityListCourseNodeController.java
+++ b/src/main/java/org/olat/course/assessment/ui/tool/IdentityListCourseNodeController.java
@@ -307,7 +307,7 @@ public class IdentityListCourseNodeController extends FormBasicController
 		if(!extendedFilters.isEmpty()) {
 			tableEl.setExtendedFilterButton(translate("filter.groups"), extendedFilters);
 		}
-		tableEl.setAndLoadPersistedPreferences(ureq, getTableId());//TODO username
+		tableEl.setAndLoadPersistedPreferences(ureq, getTableId());
 	}
 	
 	protected List<FlexiTableFilter> getFilters() {
@@ -365,7 +365,7 @@ public class IdentityListCourseNodeController extends FormBasicController
 	}
 	
 	protected String getTableId() {
-		return "assessment-tool-identity-list";
+		return "assessment-tool-identity-list-v2";
 	}
 	
 	protected void initAssessmentColumns(FlexiTableColumnModel columnsModel, AssessmentConfig assessmentConfig) {
diff --git a/src/main/java/org/olat/course/nodes/gta/ui/GTAIdentityListCourseNodeController.java b/src/main/java/org/olat/course/nodes/gta/ui/GTAIdentityListCourseNodeController.java
index 096e1549a54..bc17786b8db 100644
--- a/src/main/java/org/olat/course/nodes/gta/ui/GTAIdentityListCourseNodeController.java
+++ b/src/main/java/org/olat/course/nodes/gta/ui/GTAIdentityListCourseNodeController.java
@@ -91,7 +91,7 @@ public class GTAIdentityListCourseNodeController extends IdentityListCourseNodeC
 
 	@Override
 	protected String getTableId() {
-		return "gta-assessment-tool-identity-list";
+		return "gta-assessment-tool-identity-list-v2";
 	}
 
 	@Override
diff --git a/src/main/java/org/olat/course/nodes/iq/IQIdentityListCourseNodeController.java b/src/main/java/org/olat/course/nodes/iq/IQIdentityListCourseNodeController.java
index ee13d1c46a4..9d947d602d9 100644
--- a/src/main/java/org/olat/course/nodes/iq/IQIdentityListCourseNodeController.java
+++ b/src/main/java/org/olat/course/nodes/iq/IQIdentityListCourseNodeController.java
@@ -147,9 +147,9 @@ public class IQIdentityListCourseNodeController extends IdentityListCourseNodeCo
 	@Override
 	protected String getTableId() {
 		if(isTestQTI21()) {
-			return"qti21-assessment-tool-identity-list";
+			return"qti21-assessment-tool-identity-list-v2";
 		}
-		return "qti-assessment-tool-identity-list";
+		return "qti-assessment-tool-identity-list-v2";
 	}
 
 	@Override
diff --git a/src/main/java/org/olat/course/nodes/st/STIdentityListCourseNodeController.java b/src/main/java/org/olat/course/nodes/st/STIdentityListCourseNodeController.java
index eeed8113435..9ed677d73eb 100644
--- a/src/main/java/org/olat/course/nodes/st/STIdentityListCourseNodeController.java
+++ b/src/main/java/org/olat/course/nodes/st/STIdentityListCourseNodeController.java
@@ -117,7 +117,7 @@ public class STIdentityListCourseNodeController extends IdentityListCourseNodeCo
 	
 	@Override
 	protected String getTableId() {
-		return "st-assessment-tool-identity-list";
+		return "st-assessment-tool-identity-list-v2";
 	}
 
 	@Override
diff --git a/src/main/java/org/olat/group/manager/MemberViewQueries.java b/src/main/java/org/olat/group/manager/MemberViewQueries.java
index f039714cc5a..37aba9ce786 100644
--- a/src/main/java/org/olat/group/manager/MemberViewQueries.java
+++ b/src/main/java/org/olat/group/manager/MemberViewQueries.java
@@ -296,18 +296,11 @@ public class MemberViewQueries {
 
 		// append query for login
 		boolean appendOr = false;
-		
-		if (params.getLogin() != null) {
+		if (params.getLogin() != null) {// backwards compatibility
 			appendOr = true;
-			if (params.getLogin().contains("_") && dbInstance.isOracle()) {
-				//oracle needs special ESCAPE sequence to search for escaped strings
-				sb.append("lower(ident.name) like :login ESCAPE '\\'");
-			} else if (dbInstance.isMySQL()) {
-				sb.append("ident.name like :login");
-			} else {
-				sb.append("lower(ident.name) like :login");
-			}
-			//TODO username
+			PersistenceHelper.appendFuzzyLike(sb, "ident.name", "login", dbInstance.getDbVendor());
+			sb.append(" or ");
+			PersistenceHelper.appendFuzzyLike(sb, "identUser.nickName", "login", dbInstance.getDbVendor());
 		}
 
 		// append queries for user fields
@@ -319,15 +312,7 @@ public class MemberViewQueries {
 				} else {
 					appendOr = true;
 				}
-				
-				if(dbInstance.isMySQL()) {
-					sb.append("identUser.").append(key).append(" like :").append(key).append("_value");
-				} else {
-					sb.append("lower(identUser.").append(key).append(") like :").append(key).append("_value");
-				}
-				if(dbInstance.isOracle()) {
-					sb.append(" escape '\\'");
-				}
+				PersistenceHelper.appendFuzzyLike(sb, "identUser.".concat(key), key.concat("_value"), dbInstance.getDbVendor());
 			}
 		}
 
diff --git a/src/main/java/org/olat/ldap/LDAPLoginManager.java b/src/main/java/org/olat/ldap/LDAPLoginManager.java
index eaa506bf01e..fb2fd84a067 100644
--- a/src/main/java/org/olat/ldap/LDAPLoginManager.java
+++ b/src/main/java/org/olat/ldap/LDAPLoginManager.java
@@ -69,8 +69,6 @@ public interface LDAPLoginManager {
 	
 	public void freeSyncLock();
 	
-	public void doSyncSingleUser(Identity ident);
-	
 	/**
 	 * A filter is build from the login attribute value and the resulting
 	 * attributes are sync to the specified identity.
diff --git a/src/main/java/org/olat/ldap/_spring/ldapContext.xml b/src/main/java/org/olat/ldap/_spring/ldapContext.xml
index fb227ac8447..fd143fa0c44 100644
--- a/src/main/java/org/olat/ldap/_spring/ldapContext.xml
+++ b/src/main/java/org/olat/ldap/_spring/ldapContext.xml
@@ -121,6 +121,7 @@
     			<entry key='${ldap.attributename.email}' value='email' />
     			<entry key='${ldap.attributename.firstName}' value='firstName' />
     			<entry key='${ldap.attributename.lastName}' value='lastName' />
+    			<entry key='${ldap.attributename.nickName}' value='nickName' />
     			
     			<!-- example for another mapping
     			<entry key='description' value='orgUnit' />
diff --git a/src/main/java/org/olat/ldap/manager/LDAPDAO.java b/src/main/java/org/olat/ldap/manager/LDAPDAO.java
index d4bb5b696f0..e35eef3dbbf 100644
--- a/src/main/java/org/olat/ldap/manager/LDAPDAO.java
+++ b/src/main/java/org/olat/ldap/manager/LDAPDAO.java
@@ -194,7 +194,7 @@ public class LDAPDAO {
 			} catch (Exception e) {
 				log.error("Exception when trying to search users from LDAP using ldapBase::" + ldapBase + " on row::" + counter, e);
 			}
-			log.debug("finished search for ldapBase:: " + ldapBase);
+			log.debug("finished search for ldapBase:: {}", ldapBase);
 		}
 	}
 	
@@ -368,7 +368,7 @@ public class LDAPDAO {
 	
 	private boolean isPagedResultControlSupported(LdapContext ctx) {
 		// FXOLAT-299, might return false on 2nd execution
-		if (pagingSupportedAlreadyFound == true) return true;
+		if (pagingSupportedAlreadyFound) return true;
 		try {
 			SearchControls ctl = new SearchControls();
 			ctl.setReturningAttributes(new String[]{"supportedControl"});
diff --git a/src/main/java/org/olat/ldap/manager/LDAPLoginManagerImpl.java b/src/main/java/org/olat/ldap/manager/LDAPLoginManagerImpl.java
index ac8760f3e63..9657a972412 100644
--- a/src/main/java/org/olat/ldap/manager/LDAPLoginManagerImpl.java
+++ b/src/main/java/org/olat/ldap/manager/LDAPLoginManagerImpl.java
@@ -476,7 +476,7 @@ public class LDAPLoginManagerImpl implements LDAPLoginManager, AuthenticationPro
 	public void deleteIdentities(List<Identity> identityList, Identity doer) {
 		for (Identity identity:  identityList) {
 			if(Identity.STATUS_PERMANENT.equals(identity.getStatus())) {
-				log.info(Tracing.M_AUDIT, identity.getKey() + " was not deleted because is status is permanent.");
+				log.info(Tracing.M_AUDIT, "{} was not deleted because is status is permanent.", identity.getKey());
 				continue;
 			}
 			
@@ -577,17 +577,20 @@ public class LDAPLoginManagerImpl implements LDAPLoginManager, AuthenticationPro
 		String email = getAttributeValue(userAttributes.get(syncConfiguration.getOlatPropertyToLdapAttribute(UserConstants.EMAIL)));
 		// Lookup user
 		if (securityManager.findIdentityByLogin(uid) != null) {
-			log.error("Can't create user with username='{}', this username does already exist in OLAT database", uid);
+			log.error("Can't create user with username='{}', this username does already exist in the database", uid);
+			return null;
+		}
+		if(!securityModule.isIdentityNameAutoGenerated() && securityManager.findIdentityByName(uid) != null) {
+			log.error("Can't create user with username='{}', this identity name does already exist in the database", uid);
 			return null;
 		}
-		//TODO username ()
 		if (!MailHelper.isValidEmailAddress(email)) {
 			// needed to prevent possibly an AssertException in findIdentityByEmail breaking the sync!
 			log.error("Cannot try to lookup user {} by email with an invalid email::{}", uid, email);
 			return null;
 		}
 		if (!userManager.isEmailAllowed(email)) {
-			log.error("Can't create user with email='{}', a user with that email does already exist in OLAT database", email);
+			log.error("Can't create user with email='{}', a user with that email does already exist in the database", email);
 			return null;
 		}
 		
@@ -794,7 +797,10 @@ public class LDAPLoginManagerImpl implements LDAPLoginManager, AuthenticationPro
 	}
 
 	/**
-	 * Searches for Identity in OLAT.
+	 * Searches for identity with the login attribute, with fallback to the uid attribute.
+	 * If not found and configured to, try to convert an identity with an OLAT authentication
+	 * to LDAP, or if the identity name are manually generated, try to convert an identity
+	 * with the right name to LDAP.
 	 * 
 	 * @param uid Name of Identity
 	 * @param errors LDAPError Object if user exits but not member of
@@ -811,15 +817,14 @@ public class LDAPLoginManagerImpl implements LDAPLoginManager, AuthenticationPro
 			return null;
 		}
 		
-		String uid = getAttributeValue(attrs.get(syncConfiguration
-				.getOlatPropertyToLdapAttribute(LDAPConstants.LDAP_USER_IDENTIFYER)));
 		String token = getAttributeValue(attrs.get(syncConfiguration.getLdapUserLoginAttribute()));
-
 		Authentication ldapAuth = authenticationDao.getAuthentication(token, LDAPAuthenticationController.PROVIDER_LDAP);
 		if(ldapAuth != null) {
 			return ldapAuth.getIdentity();
 		}
 
+		String uid = getAttributeValue(attrs.get(syncConfiguration
+				.getOlatPropertyToLdapAttribute(LDAPConstants.LDAP_USER_IDENTIFYER)));
 		ldapAuth = authenticationDao.getAuthentication(uid, LDAPAuthenticationController.PROVIDER_LDAP);
 		if(ldapAuth != null) {
 			if(StringHelper.containsNonWhitespace(token) && !token.equals(ldapAuth.getAuthusername())) {
@@ -837,6 +842,15 @@ public class LDAPLoginManagerImpl implements LDAPLoginManager, AuthenticationPro
 				log.info("Found identity by LDAP username that was not yet in LDAP security group. Converted user::{} to be an LDAP managed user", uid);
 				return defaultAuth.getIdentity();
 			}
+			
+			if(!securityModule.isIdentityNameAutoGenerated()) {
+				Identity identity = securityManager.findIdentityByName(uid);
+				if(identity != null) {
+					securityManager.createAndPersistAuthentication(identity, LDAPAuthenticationController.PROVIDER_LDAP, token, null, null);
+					log.info("Found identity by identity name that was not yet in LDAP security group. Converted user::{} to be an LDAP managed user", uid);
+					return identity;
+				}
+			}
 		}
 		return null;
 	}
@@ -1532,32 +1546,10 @@ public class LDAPLoginManagerImpl implements LDAPLoginManager, AuthenticationPro
 		}
 		return ldapUser;
 	}
-
-	@Override
-	public void doSyncSingleUser(Identity ident){
-		LdapContext ctx = bindSystem();
-		if (ctx == null) {
-			log.error("could not bind to ldap");
-		}		
-		String userDN = ldapDao.searchUserDNByUid(ident.getName(), ctx);
-
-		final List<Attributes> ldapUserList = new ArrayList<>();
-		ldapDao.searchInLdap(new LDAPVisitor() {
-			@Override
-			public void visit(SearchResult result) {
-				Attributes resAttribs = result.getAttributes();
-				log.debug("        found : " + resAttribs.size() + " attributes in result " + result.getName());
-				ldapUserList.add(resAttribs);
-			}
-		}, userDN, syncConfiguration.getUserAttributes(), ctx);
-		
-		Attributes attrs = ldapUserList.get(0);
-		Map<String, String> olatProToSync = prepareUserPropertyForSync(attrs, ident);
-		if (olatProToSync != null) {
-			syncUser(olatProToSync, ident);
-		}
-	}
 	
+	/**
+	 * Used by the Panther provider
+	 */
 	@Override
 	public void doSyncSingleUserWithLoginAttribute(Identity ident) {
 		LdapContext ctx = bindSystem();
@@ -1566,15 +1558,12 @@ public class LDAPLoginManagerImpl implements LDAPLoginManager, AuthenticationPro
 		}
 		
 		String ldapUserIDAttribute = syncConfiguration.getLdapUserLoginAttribute();
-		String filter = ldapDao.buildSearchUserFilter(ldapUserIDAttribute, ident.getName());
+		Authentication authentication = securityManager.findAuthentication(ident, LDAPAuthenticationController.PROVIDER_LDAP);
+		String filter = ldapDao.buildSearchUserFilter(ldapUserIDAttribute, authentication.getAuthusername());
 		
 		List<Attributes> ldapUserAttrs = new ArrayList<>();
-		ldapDao.searchInLdap(new LDAPVisitor() {
-			@Override
-			public void visit(SearchResult result) {
-				ldapUserAttrs.add(result.getAttributes());
-			}
-		}, filter, syncConfiguration.getUserAttributes(), ctx);
+		ldapDao.searchInLdap(result -> ldapUserAttrs.add(result.getAttributes()),
+				filter, syncConfiguration.getUserAttributes(), ctx);
 		
 		if(ldapUserAttrs.size() == 1) {
 			Attributes attrs = ldapUserAttrs.get(0);
@@ -1583,13 +1572,10 @@ public class LDAPLoginManagerImpl implements LDAPLoginManager, AuthenticationPro
 				syncUser(olatProToSync, ident);
 			}
 		} else {
-			log.error("Cannot sync the user because it was not found on LDAP server: " + ident);
+			log.error("Cannot sync the user because it was not found on LDAP server: {}", ident);
 		}
 	}
 
-	/**
-	 * @see org.olat.ldap.LDAPLoginManager#getLastSyncDate()
-	 */
 	@Override
 	public Date getLastSyncDate() {
 		return lastSyncDate;
diff --git a/src/main/java/org/olat/ldap/ui/LDAPAdminController.java b/src/main/java/org/olat/ldap/ui/LDAPAdminController.java
index 89cca444075..4c8cc68041f 100644
--- a/src/main/java/org/olat/ldap/ui/LDAPAdminController.java
+++ b/src/main/java/org/olat/ldap/ui/LDAPAdminController.java
@@ -168,7 +168,7 @@ public class LDAPAdminController extends BasicController implements GenericEvent
 			}	else if (source == userSearchCtrl) {
 				calloutCtr.deactivate();
 				Identity choosenIdent = ((SingleIdentityChosenEvent)event).getChosenIdentity();
-				ldapLoginManager.doSyncSingleUser(choosenIdent);
+				ldapLoginManager.doSyncSingleUserWithLoginAttribute(choosenIdent);
 			}
 		}
 	}
diff --git a/src/main/java/org/olat/login/auth/OLATAuthManager.java b/src/main/java/org/olat/login/auth/OLATAuthManager.java
index 712af194bd9..15a1a738db1 100644
--- a/src/main/java/org/olat/login/auth/OLATAuthManager.java
+++ b/src/main/java/org/olat/login/auth/OLATAuthManager.java
@@ -228,7 +228,7 @@ public class OLATAuthManager implements AuthenticationSPI {
 				String currentEmail = mails.get("currentEMail");
 				String changedEmail = mails.get("changedEMail");
 				if (login.equals(changedEmail) && StringHelper.containsNonWhitespace(currentEmail)) {
-					return securityManager.findIdentityByName(currentEmail);
+					return securityManager.findIdentityByName(currentEmail);//TODO username
 				}
 			}
 		}
diff --git a/src/main/java/org/olat/login/validation/UsernameInUseRule.java b/src/main/java/org/olat/login/validation/UsernameInUseRule.java
index 7da9cb6c384..7d9da891404 100644
--- a/src/main/java/org/olat/login/validation/UsernameInUseRule.java
+++ b/src/main/java/org/olat/login/validation/UsernameInUseRule.java
@@ -20,6 +20,7 @@
 package org.olat.login.validation;
 
 import org.olat.basesecurity.BaseSecurity;
+import org.olat.basesecurity.BaseSecurityModule;
 import org.olat.core.CoreSpringFactory;
 import org.olat.core.id.Identity;
 
@@ -38,7 +39,9 @@ class UsernameInUseRule extends DescriptionRule {
 	@Override
 	public boolean validate(String value, Identity identity) {
 		BaseSecurity securityManager = CoreSpringFactory.getImpl(BaseSecurity.class);
-		return securityManager.findIdentityByLogin(value) == null;
+		BaseSecurityModule securityModule = CoreSpringFactory.getImpl(BaseSecurityModule.class);
+		return securityManager.findIdentityByLogin(value) == null
+				&& (securityModule.isIdentityNameAutoGenerated() || securityManager.findIdentityByName(value) == null);
 	}
 
 	@Override
diff --git a/src/main/java/org/olat/modules/coach/ui/UserSearchForm.java b/src/main/java/org/olat/modules/coach/ui/UserSearchForm.java
index 13045f71f7b..9dd499f215e 100644
--- a/src/main/java/org/olat/modules/coach/ui/UserSearchForm.java
+++ b/src/main/java/org/olat/modules/coach/ui/UserSearchForm.java
@@ -50,7 +50,7 @@ import org.springframework.beans.factory.annotation.Autowired;
  */
 public class UserSearchForm extends FormBasicController {
 	
-	private static final String  PROPS_IDENTIFIER = UserSearchForm.class.getName();
+	private static final String PROPS_IDENTIFIER = UserSearchForm.class.getName();
 	private static final String[] activeKeys = new String[] { "on" };
 	private static final String[] activeValues = new String[] { "" };
 	
@@ -84,7 +84,7 @@ public class UserSearchForm extends FormBasicController {
 	@Override
 	protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) {
 		login = uifactory.addTextElement("login", "search.form.login", 128, "", formLayout);
-		login.setVisible(adminProps);//TODO username
+		login.setVisible(adminProps);
 		
 		userPropertyHandlers = userManager.getUserPropertyHandlersFor(PROPS_IDENTIFIER, adminProps);
 
@@ -139,7 +139,9 @@ public class UserSearchForm extends FormBasicController {
 	
 	@Override
 	protected boolean validateFormLogic(UserRequest ureq) {
-		return validate() & super.validateFormLogic(ureq);
+		boolean allOk = super.validateFormLogic(ureq);
+		allOk &= validate();
+		return allOk;
 	}
 
 	private boolean validate() {
diff --git a/src/main/java/org/olat/registration/PwChangeController.java b/src/main/java/org/olat/registration/PwChangeController.java
index be08efa826f..63d1acb38bb 100644
--- a/src/main/java/org/olat/registration/PwChangeController.java
+++ b/src/main/java/org/olat/registration/PwChangeController.java
@@ -317,8 +317,8 @@ public class PwChangeController extends BasicController {
 	 * @return Identity or null if not found.
 	 */
 	private Identity findIdentityByUsernameOrEmail(String emailOrUsername) {
-		// See if the entered value is a username
-		Identity identity = securityManager.findIdentityByName(emailOrUsername);
+		// See if the entered value is the authusername of an authentication
+		Identity identity = securityManager.findIdentityByLogin(emailOrUsername);
 		if (identity == null) {
 			// Try fallback with email, maybe user used his email address instead
 			identity = userManager.findUniqueIdentityByEmail(emailOrUsername);
diff --git a/src/main/resources/serviceconfig/olat.properties b/src/main/resources/serviceconfig/olat.properties
index 3567ac66a08..b5524828d3f 100644
--- a/src/main/resources/serviceconfig/olat.properties
+++ b/src/main/resources/serviceconfig/olat.properties
@@ -1229,6 +1229,7 @@ ldap.attributename.useridentifyer=sAMAccountName
 ldap.attributename.email=mail
 ldap.attributename.firstName=givenName
 ldap.attributename.lastName=sn
+ldap.attributename.nickName=${ldap.attributename.useridentifyer}
 # Attribute used as username to log in. Note: the configured login attribute MUST be one of the mapped attributes below, e.g. genericTextProperty1
 # If the login attribute should not be visible in OO, then disable the OO user property in all contexts.
 ldap.login.attribute=${ldap.attributename.useridentifyer}
diff --git a/src/test/java/org/olat/basesecurity/GetIdentitiesByPowerSearchTest.java b/src/test/java/org/olat/basesecurity/GetIdentitiesByPowerSearchTest.java
index de163287fae..d33831f5200 100644
--- a/src/test/java/org/olat/basesecurity/GetIdentitiesByPowerSearchTest.java
+++ b/src/test/java/org/olat/basesecurity/GetIdentitiesByPowerSearchTest.java
@@ -304,8 +304,7 @@ public class GetIdentitiesByPowerSearchTest extends OlatTestCase {
 
 		// authentication provider search
 		String[] authProviders = {BaseSecurityModule.getDefaultAuthProviderIdentifier(), "Shib"};
-		String[] authProvidersInvalid = { "nonexist" };// max length 8 !		
-		String[] authProvidersAll = { BaseSecurityModule.getDefaultAuthProviderIdentifier(), "Shib", null };
+		String[] authProvidersInvalid = { "nonexist" };// max length 8 !
 		
 		List<Identity> results = baseSecurityManager.getIdentitiesByPowerSearch(null, null, true, null, authProviders, null, null, null, null, null);
 		Assert.assertFalse(results.isEmpty());
@@ -364,21 +363,6 @@ public class GetIdentitiesByPowerSearchTest extends OlatTestCase {
 		Assert.assertFalse("Found no results", results.isEmpty());
 		checkIdentitiesAreInGroups(results, groups1);
 		checkIdentitiesHasAuthProvider(results,authProviders );
-
-		//TODO username
-		/*
-		results = baseSecurityManager.getIdentitiesByPowerSearch("%y", null, true, groups1, authProvidersAll, before, null, null, null, null);
-		Assert.assertTrue(results.contains(ident));
-		Assert.assertTrue(results.contains(ident2));
-		results = baseSecurityManager.getVisibleIdentitiesByPowerSearch("%y", null, true, groups1, authProvidersAll, before, null);
-		Assert.assertTrue(results.contains(ident));
-		Assert.assertTrue(results.contains(ident2));
-
-		results = baseSecurityManager.getIdentitiesByPowerSearch("%y", null, true, groups1, authProvidersAll, null, before, null, null, null);
-		Assert.assertTrue(results.isEmpty());
-		results = baseSecurityManager.getVisibleIdentitiesByPowerSearch("%y", null, true, groups1, authProvidersAll, null, before);
-		Assert.assertTrue(results.isEmpty());
-		*/
 	}
 	
 	@Test
diff --git a/src/test/java/org/olat/ldap/LDAPLoginTest.java b/src/test/java/org/olat/ldap/LDAPLoginTest.java
index b9eed1f6e86..9bd88973d6e 100644
--- a/src/test/java/org/olat/ldap/LDAPLoginTest.java
+++ b/src/test/java/org/olat/ldap/LDAPLoginTest.java
@@ -33,10 +33,8 @@ import org.zapodot.junit.ldap.EmbeddedLdapRuleBuilder;
 
 
 /**
- * Description:<br>
- * LDAP junit tests
- * 
- * please import "olattest.ldif" into your configured LDAP directory
+ * Low level LDAP authentication unit tests. this test must happend
+ * before LDAPLoginManagerTest (it will do a sync).
  * 
  * <P>
  * Initial Date:  June 30, 2008 <br>
@@ -48,6 +46,8 @@ public class LDAPLoginTest extends OlatTestCase {
 	private LDAPLoginManager ldapManager;
 	@Autowired
 	private LDAPLoginModule ldapLoginModule;
+	@Autowired
+	private LDAPSyncConfiguration syncConfiguration;
 	
 	@Rule
 	public EmbeddedLdapRule embeddedLdapRule = EmbeddedLdapRuleBuilder
@@ -67,7 +67,7 @@ public class LDAPLoginTest extends OlatTestCase {
 	}
 	
 	@Test
-	public void testUserBind() throws Exception {
+	public void userBind() throws Exception {
 		Assume.assumeTrue(ldapLoginModule.isLDAPEnabled());
 
 		LDAPError errors = new LDAPError();
@@ -99,4 +99,21 @@ public class LDAPLoginTest extends OlatTestCase {
 		Assert.assertNull(attrs);
 		Assert.assertEquals("Username and password must be selected", errors.get());
 	}
+	
+	@Test
+	public void userBindLoginWithAlternativeLoginAttribute() throws Exception {
+		Assume.assumeTrue(ldapLoginModule.isLDAPEnabled());
+		String currentLoginAttr = syncConfiguration.getLdapUserLoginAttribute();
+		
+		// use SN as login attribute
+		syncConfiguration.setLdapUserLoginAttribute("sn");
+
+		LDAPError errors = new LDAPError();
+		Attributes attrs = ldapManager.bindUser("Forster", "olat", errors);
+		Assert.assertNotNull(attrs);
+		Assert.assertEquals("Forster", attrs.get("sn").get());
+		Assert.assertTrue(errors.isEmpty());
+		
+		syncConfiguration.setLdapUserLoginAttribute(currentLoginAttr);
+	}
 }
diff --git a/src/test/java/org/olat/ldap/junittestdata/olattest.ldif b/src/test/java/org/olat/ldap/junittestdata/olattest.ldif
index 7a590048b0b..21c3f3d57f5 100644
--- a/src/test/java/org/olat/ldap/junittestdata/olattest.ldif
+++ b/src/test/java/org/olat/ldap/junittestdata/olattest.ldif
@@ -133,4 +133,93 @@ uid: krass
 userpassword:: e1NIQX1SWEhpU0lWZGN1SU1EdVNiVCtaaFpOcWFwYWs9
 modifyTimestamp: 20100630180000Z
 
+dn: uid=dforster,ou=person,dc=olattest,dc=org
+objectClass: organizationalPerson
+objectClass: person
+objectClass: inetOrgPerson
+objectClass: top
+cn: Dalia Forster
+givenname: Dalia
+labeleduri: https://www.openolat.com/dforster
+mail: dforster@openolat.com
+o: Informatik
+sn: Forster
+uid: dforster
+userPassword: olat
+modifyTimestamp: 20100630180000Z
+
+dn: uid=sramljak,ou=person,dc=olattest,dc=org
+objectClass: organizationalPerson
+objectClass: person
+objectClass: inetOrgPerson
+objectClass: top
+cn: Sandy Ramljak
+givenname: Sandy
+labeleduri: https://www.openolat.com/sramljak
+mail: sramljak@openolat.com
+o: Informatik
+sn: Ramljak
+uid: sramljak
+userPassword: olat
+modifyTimestamp: 20100630180000Z
+
+dn: uid=cguerrera,ou=person,dc=olattest,dc=org
+objectClass: organizationalPerson
+objectClass: person
+objectClass: inetOrgPerson
+objectClass: top
+cn: Carmen Guerrera
+givenname: Carmen
+labeleduri: https://www.openolat.com/cguerrera
+mail: cguerrera@openolat.com
+o: Informatik
+sn: Guerrera
+uid: cguerrera
+userPassword: olat
+modifyTimestamp: 20100630180000Z
+
+dn: uid=lsalathe,ou=person,dc=olattest,dc=org
+objectClass: organizationalPerson
+objectClass: person
+objectClass: inetOrgPerson
+objectClass: top
+cn: Leyla Salathe
+givenname: Leyla
+labeleduri: https://www.openolat.com/lsalathe
+mail: lsalathe@openolat.com
+o: Informatik
+sn: Salathe
+uid: lsalathe
+userPassword: olat
+modifyTimestamp: 20100630180000Z
+
+dn: uid=ahentschel,ou=person,dc=olattest,dc=org
+objectClass: organizationalPerson
+objectClass: person
+objectClass: inetOrgPerson
+objectClass: top
+cn: Alessandro	Hentschel
+givenname: Alessandro
+labeleduri: https://www.openolat.com/ahentschel
+mail: ahentschel@openolat.com
+o: Informatik
+sn: Hentschel
+uid: ahentschel
+userPassword: olat
+modifyTimestamp: 20100630180000Z
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
 
diff --git a/src/test/java/org/olat/ldap/manager/LDAPLoginManagerTest.java b/src/test/java/org/olat/ldap/manager/LDAPLoginManagerTest.java
index a681c79c81f..e107d4b668e 100644
--- a/src/test/java/org/olat/ldap/manager/LDAPLoginManagerTest.java
+++ b/src/test/java/org/olat/ldap/manager/LDAPLoginManagerTest.java
@@ -20,22 +20,32 @@
 package org.olat.ldap.manager;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
+import java.util.stream.Collectors;
 
 import javax.naming.directory.Attributes;
 
 import org.junit.Assert;
 import org.junit.Assume;
+import org.junit.FixMethodOrder;
 import org.junit.Rule;
 import org.junit.Test;
+import org.junit.runners.MethodSorters;
 import org.olat.basesecurity.Authentication;
 import org.olat.basesecurity.BaseSecurity;
+import org.olat.basesecurity.BaseSecurityModule;
+import org.olat.basesecurity.model.FindNamedIdentity;
 import org.olat.core.commons.persistence.DB;
 import org.olat.core.commons.services.webdav.manager.WebDAVAuthManager;
 import org.olat.core.id.Identity;
+import org.olat.core.id.User;
+import org.olat.core.id.UserConstants;
+import org.olat.core.util.Encoder.Algorithm;
 import org.olat.ldap.LDAPError;
 import org.olat.ldap.LDAPLoginManager;
 import org.olat.ldap.LDAPLoginModule;
+import org.olat.ldap.LDAPSyncConfiguration;
 import org.olat.ldap.ui.LDAPAuthenticationController;
 import org.olat.test.OlatTestCase;
 import org.olat.user.UserManager;
@@ -53,6 +63,7 @@ import com.unboundid.ldap.sdk.ModificationType;
  * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
  *
  */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
 public class LDAPLoginManagerTest extends OlatTestCase {
 	
 	@Autowired
@@ -60,11 +71,15 @@ public class LDAPLoginManagerTest extends OlatTestCase {
 	@Autowired
 	private UserManager userManager;
 	@Autowired
+	private BaseSecurity securityManager;
+	@Autowired
 	private LDAPLoginManager ldapManager;
 	@Autowired
 	private LDAPLoginModule ldapLoginModule;
 	@Autowired
-	private BaseSecurity securityManager;
+	private BaseSecurityModule securityModule;
+	@Autowired
+	private LDAPSyncConfiguration syncConfiguration;
 	
 	@Rule
 	public EmbeddedLdapRule embeddedLdapRule = EmbeddedLdapRuleBuilder
@@ -75,18 +90,51 @@ public class LDAPLoginManagerTest extends OlatTestCase {
 	        .bindingToPort(1389)
 	        .build();
 	
+	/**
+	 * a to be the first to be called.
+	 */
 	@Test
-	public void syncUsers() {
+	public void aSyncUsers() {
 		Assume.assumeTrue(ldapLoginModule.isLDAPEnabled());
 		
 		LDAPError errors = new LDAPError();
 		boolean allOk = ldapManager.doBatchSync(errors);
 		Assert.assertTrue(allOk);
-		
+	
+		// historic
 		Identity identity = userManager.findUniqueIdentityByEmail("hhuerlimann@openolat.com");
 		Assert.assertNotNull(identity);
 	}
 	
+	@Test
+	public void doSyncSingleUserWithLoginAttribute() throws LDAPException {
+		Assume.assumeTrue(ldapLoginModule.isLDAPEnabled());
+
+		// sync single user
+		Identity identity = userManager.findUniqueIdentityByEmail("ahentschel@openolat.com");
+		Assert.assertNotNull(identity);
+		Assert.assertNotEquals("ahentschel", identity.getName());
+		
+		// make a change
+		String dn = "uid=ahentschel,ou=person,dc=olattest,dc=org";
+		List<Modification> modifications = new ArrayList<>();
+		modifications.add(new Modification(ModificationType.REPLACE, "sn", "Herschell"));
+		embeddedLdapRule.ldapConnection().modify(dn, modifications);
+		
+		// simple sync
+		ldapManager.doSyncSingleUserWithLoginAttribute(identity);
+		dbInstance.commitAndCloseSession();
+		
+		Identity reloadIdentity = securityManager.loadIdentityByKey(identity.getKey());
+		Assert.assertNotNull(reloadIdentity);
+		Assert.assertNotEquals("ahentschel", reloadIdentity.getName());
+		Assert.assertEquals(identity, reloadIdentity);
+		Assert.assertEquals("Alessandro", reloadIdentity.getUser().getFirstName());
+		Assert.assertEquals("Herschell", reloadIdentity.getUser().getLastName());
+		Assert.assertEquals("ahentschel@openolat.com", reloadIdentity.getUser().getEmail());
+		Assert.assertEquals("ahentschel", reloadIdentity.getUser().getProperty(UserConstants.NICKNAME, null));
+	}
+	
 	@Test
 	public void testUserBindDigest() throws Exception {
 		Assume.assumeTrue(ldapLoginModule.isLDAPEnabled());
@@ -191,5 +239,99 @@ public class LDAPLoginManagerTest extends OlatTestCase {
 		Authentication validLdapAuthentication = securityManager.findAuthentication(identity, LDAPAuthenticationController.PROVIDER_LDAP);
 		Assert.assertNotNull(validLdapAuthentication);
 	}
+	
+	@Test
+	public void findIdentityByLdapAuthentication() {
+		LDAPError ldapError = new LDAPError();
+		
+		// use SN as login attribute
+		String currentLoginAttr = syncConfiguration.getLdapUserLoginAttribute();
+		syncConfiguration.setLdapUserLoginAttribute("sn");
+		
+		Attributes attrs = ldapManager.bindUser("Ramljak", "olat", ldapError);
+		Assert.assertNotNull(attrs);
+
+		LDAPError errors = new LDAPError();
+		Identity identity = ldapManager.findIdentityByLdapAuthentication(attrs, errors);
+		Assert.assertNotNull(identity);
+		Assert.assertEquals("Ramljak", identity.getUser().getLastName());
+		
+		syncConfiguration.setLdapUserLoginAttribute(currentLoginAttr);
+	}
+	
+	@Test
+	public void findIdentityByLdapAuthenticationConvertFromOlat() {
+		LDAPError ldapError = new LDAPError();
+		
+		// Take an LDAP user, delete her LDAP authentication
+		List<String> firstLastName = Collections.singletonList("Carmen Guerrera");
+		List<FindNamedIdentity> foundIdentities = securityManager.findIdentitiesBy(firstLastName);
+		Assert.assertEquals(1, foundIdentities.size());
+		
+		Identity identity = foundIdentities.get(0).getIdentity();
+		Authentication authentication = securityManager.findAuthentication(identity, LDAPAuthenticationController.PROVIDER_LDAP);
+		Assert.assertNotNull(authentication);
+		securityManager.deleteAuthentication(authentication);
+		securityManager.createAndPersistAuthentication(identity, "OLAT", "cguerrera", "secret", Algorithm.sha512);
+		dbInstance.commitAndCloseSession();
+		
+		// convert users to LDAP
+		boolean currentConvertExistingLocalUsers = ldapLoginModule.isConvertExistingLocalUsersToLDAPUsers();
+		ldapLoginModule.setConvertExistingLocalUsersToLDAPUsers(true);
+
+		// use uid as login attribute	
+		Attributes attrs = ldapManager.bindUser("cguerrera", "olat", ldapError);
+		Assert.assertNotNull(attrs);
+
+		LDAPError errors = new LDAPError();
+		Identity convertedIdentity = ldapManager.findIdentityByLdapAuthentication(attrs, errors);
+		Assert.assertNotNull(convertedIdentity);
+		Assert.assertEquals("Guerrera", convertedIdentity.getUser().getLastName());
+
+		// revert configuration
+		ldapLoginModule.setConvertExistingLocalUsersToLDAPUsers(currentConvertExistingLocalUsers);
+	}
+	
+	@Test
+	public void findIdentityByLdapAuthenticationConvertFromIdentityName() {
+		// Take an LDAP user, delete her LDAP authentication
+		List<String> firstLastName = Collections.singletonList("Leyla Salathe");
+		List<FindNamedIdentity> foundIdentities = securityManager.findIdentitiesBy(firstLastName);
+		List<Identity> deleteList = foundIdentities.stream()
+				.map(FindNamedIdentity::getIdentity)
+				.collect(Collectors.toList());
+		ldapManager.deleteIdentities(deleteList, null);
+		dbInstance.commitAndCloseSession();
+		
+		String currentIdentityNameGenerator = securityModule.getIdentityName();
+		securityModule.setIdentityName("manual");
+		
+		// Create the user without OLAT login
+		User user = userManager.createUser("Leyla", "Salathe", "lsalathe@openolat.com");
+		Identity identity = securityManager.createAndPersistIdentityAndUser("lsalathe", null, null, user, null, null, null);
+		
+		// convert users to LDAP
+		boolean currentConvertExistingLocalUsers = ldapLoginModule.isConvertExistingLocalUsersToLDAPUsers();
+		ldapLoginModule.setConvertExistingLocalUsersToLDAPUsers(true);
+		
+		// use uid as login attribute
+		LDAPError ldapError = new LDAPError();
+		Attributes attrs = ldapManager.bindUser("lsalathe", "olat", ldapError);
+		Assert.assertNotNull(attrs);
+
+		LDAPError errors = new LDAPError();
+		Identity convertedIdentity = ldapManager.findIdentityByLdapAuthentication(attrs, errors);
+		Assert.assertNotNull(convertedIdentity);
+		Assert.assertEquals("Salathe", convertedIdentity.getUser().getLastName());
+
+		// revert configuration
+		ldapLoginModule.setConvertExistingLocalUsersToLDAPUsers(currentConvertExistingLocalUsers);
+		securityModule.setIdentityName(currentIdentityNameGenerator);
+		
+		// check
+		Assert.assertEquals(identity, convertedIdentity);
+		Authentication authentication = securityManager.findAuthentication(convertedIdentity, LDAPAuthenticationController.PROVIDER_LDAP);
+		Assert.assertNotNull(authentication);
+	}
 
 }
-- 
GitLab