Nutzernamen in TL sind zwar nicht case-sensitiv, wenn man allerdings eine andere Schreibweise nutzt, wird der Nutzer nicht gefunden. Außerdem sind bei der Eingabe keine sinnvollen Regeln definiert. So sind Leerzeichen erlaubt und werden auch vorne und hinten als Teil des Nutzernamens akzeptiert und angewendet. Der Umgang mit Nutzernamen ist zu überarbeiten. Dabei müssen u.a. folgende Punkte betrachtet werden:
- Welche Zeichen dürfen für Nutzernamen verwendet werden (z.B. kein Whitespace)
- Die Eingabe muss getrimmed werden
- Eingabe des Nutzernamens bei der Anlage eines neuen Nutzers
- Eingabe des Nutzernamens in der Loginmaske
- Eingabe des Nutzernamens im Dialg zum Passwort zurücksetzen
- Nutzernamen sollten case-insensitiv betrachtet werden
- Migrationskonzept falls es schon verschiedene Nutzer im System gibt deren Benutzernamen sich nur in der Groß/Kleinschreibung unterscheidet
- Beim Self-Provisioning wird ein Nutzername aus einer E-Mail-Adresse abgeleitet
- Es muss sichergestellt werden, dass E-Mail-Adressen die sich nur in der Groß/Kleinschreibung unterscheiden trotzdem im gleichen Nutzernamen resultieren.
Umsetzung
- Normalisierung: Person.normalizeName(String) entfernt führende und schließende Whitespaces (String.strip()); die Schreibweise bleibt erhalten. Person.byName(...) und Person.create(...) normalisieren ihre Eingabe, wodurch Loginmaske, Passwort-Reset-Dialog und Account-Anlage zentral abgedeckt sind -- es waren dafür keine Änderungen an den einzelnen Masken nötig.
- Case-insensitive Auflösung: Der Namens-Cache der Accounts (ItemByNameCache) wird mit dem kleingeschriebenen Namen (Locale.ROOT) als Schlüssel geführt, der gespeicherte Name behält seine Schreibweise. Zugriff über die neue Methode ItemByNameCache.lookup(K).
- Validierung: Person.create(...) prüft den Namen gegen das konfigurierte Muster und die Maximallänge und lehnt einen case-insensitiv bereits vorhandenen Namen ab (jeweils TopLogicException).
- Zeichenvorrat: Das Default-Muster person-name-pattern lautet jetzt [-_\p{L}\p{N}.@+~]+ (Unicode-Buchstaben und -Ziffern sowie . @ + ~ - _, kein Whitespace). Damit sind E-Mail-Adressen als Account-Name und nicht-ASCII-Namen aus Verzeichnissen zulässig.
- LDAP: Die Auflösung des Benutzer-DN und der Gruppenmitglieder erfolgt case-insensitiv. Ein einzelner Verzeichnis-Account mit ungültigem oder kollidierendem Namen bricht die Synchronisation nicht mehr ab, sondern wird übersprungen und als Fehler protokolliert.
- Bestand: Migration, die case-insensitiv kollidierende Account-Namen umbenennt.
Migration
Eine Datenbank-Migration ist erforderlich.
1. Migrationsskript der Engine
Das Skript Ticket_29423_resolve_person_name_collisions (Modul tl) führt den ResolvePersonNameCollisionsProcessor aus. Er benennt Accounts um, deren Namen sich nur in der Groß-/Kleinschreibung unterscheiden, damit zu jedem Zeitpunkt höchstens ein Account pro case-insensitivem Namen existiert:
- Betrachtet werden alle Accounts über ihre gesamte Historie (inklusive gelöschter, nur-historischer Accounts) und je Branch. Da ein Account seinen Namen im Laufe der Zeit ändern kann, wirkt eine Umbenennung nur im Revisionsintervall der kollidierenden Namensversion; ein Account, der lediglich seine eigene Schreibweise geändert hat, kollidiert nicht mit sich selbst.
- Innerhalb einer Kollision bleibt ein Account unverändert (Priorität: lebend, dann extern verwaltet, dann kleinste ID); die übrigen behalten ihre Schreibweise und erhalten den kleinsten freien Zahlensuffix (z.B. User -> User1).
- Auf Datenbanken, die Namen bereits case-insensitiv vergleichen, können solche Kollisionen nicht existieren -- dort ist der Prozessor ein No-op.
- Jede Umbenennung wird protokolliert; Umbenennungen verzeichnisverwalteter Accounts (mit echtem Authentifizierungs-Gerät) als Warnung.
2. Nach der Migration: Protokoll prüfen
Das Migrationsprotokoll ist auf Umbenennungen zu prüfen. Zwei Punkte sind dabei manuell nachzuziehen:
- Betroffene Benutzer informieren: Ein umbenannter Account meldet sich mit dem neuen Namen an.
- Verzeichnisverwaltete Accounts: Wurde ein LDAP-Account umbenannt (z.B. User -> User1), findet die nächste Synchronisation ihn unter dem Verzeichnisnamen nicht mehr und legt einen neuen Account an. Rollen, Gruppenmitgliedschaften und Bezüge hängen weiter am umbenannten Account und sind zu übertragen oder der überzählige Account ist zu entfernen.
3. Kollisionen dürfen nicht bestehen bleiben
Der Namens-Cache erkennt eine verbleibende Kollision und wirft beim Aufbau eine IllegalStateException -- die Applikation ist dann nicht betriebsfähig. Die Migration darf deshalb nicht übersprungen werden; eigene Migrationsschritte, die Accounts anlegen, müssen nach dem Skript der Engine laufen.
4. Geänderte Validierung bei der Account-Anlage
Person.create(...) wirft jetzt eine TopLogicException, wenn der Name
- nach dem Trimmen leer ist,
- nicht auf person-name-pattern passt,
- nicht kürzer als person-name-max-length ist (Default 128, also maximal 127 Zeichen) oder
- case-insensitiv bereits vergeben ist.
Jede programmatische Account-Anlage (Importe, Skripte, eigene Provisionierung, eigene Authentifizierungs-Geräte) ist daraufhin zu prüfen. Bei der LDAP-Synchronisation wird ein solcher Account übersprungen und als Fehler protokolliert -- das Log ist nach der Umstellung zu beobachten.
5. Eigene Konfiguration von person-name-pattern prüfen
Der Default hat sich geändert:
| vorher | jetzt |
| -_a-zA-Z0-9@\.+ | -_\p{L}\p{N}.@+~+ |
- Applikationen mit Logins, die weitere Zeichen enthalten -- insbesondere domänenqualifizierte Namen wie DOMAIN\user, aber auch Namen mit Leerzeichen oder Doppelpunkt -- müssen das Muster in ihrer Konfiguration erweitern. Andernfalls schlagen Anlage und Verzeichnissynchronisation dieser Accounts fehl.
- Applikationen mit eigenem person-name-pattern behalten ihr bisheriges, engeres Muster und lehnen damit weiterhin nicht-ASCII-Namen und E-Mail-Adressen mit + ab. Empfehlung: den Override entfernen oder auf das neue Muster umstellen.
6. Geänderte Schnittstellen
- Neu: Person.normalizeName(String).
- Person.byName(String) und Person.byName(KnowledgeBase, String) trimmen ihre Eingabe und suchen case-insensitiv. Code, der sich auf exakte Namensgleichheit verlassen hat, erhält jetzt andere Ergebnisse.
- ItemByNameCache hat einen zusätzlichen Konstruktor mit einer Schlüssel-Normalisierung sowie die neue Methode lookup(K). Bei einem normalisierenden Cache ist der direkte Zugriff über getValue().get(key) falsch -- eigene Verwendungen sind auf lookup(...) umzustellen. Der bisherige Konstruktor verhält sich unverändert (exakte Übereinstimmung).
- TLSecurityDeviceManager.DB_SECURITY ist jetzt public (Kennung des lokalen Datenbank-Authentifizierungs-Geräts; ein Account an einem anderen Gerät gilt als extern verwaltet).
Eine Änderung des Datenbankschemas (Spaltentypen, Indizes) ist nicht erforderlich; die Migration schreibt nur kollidierende Namenswerte um.