In Neos waren Login-Kennung (accountidentifier) und Profil-Mail
(email) getrennte Felder auf getrennten Tabellen. Die ursprüngliche
Migration hat beide Drupal-Felder — name und mail — fälschlich
aus der Profil-Mail gesetzt. Bei 1.751 Accounts weichen Login-Mail und Profil-Mail
voneinander ab; diese Nutzer fanden ihren Account weder beim Login noch beim
Passwort-Reset. Entschieden und lokal verifiziert am 09.07.2026 durch Marc (Site
Owner): Login läuft künftig ausschließlich über die Neos-Login-Mail.
Legacy-Neos modelliert einen Projektpartner-Account über zwei Tabellen mit einer
1:1-Beziehung: neos_flow_security_account.accountidentifier trägt die
Login-Kennung, contenance_solcom_domain_model_user.email die
Profil-/Zustelladresse. Verbunden über u.account =
a.persistence_object_identifier. Die Relation ist exakt 1:1 über den
gesamten Bestand — alle 38.111 accountidentifier-Werte sind
eindeutig — aber inhaltlich können Login-Mail und Profil-Mail
auseinanderfallen: ein Nutzer kann sich mit einer anderen Adresse einloggen, als
unter der er Post erhält (z. B. nach einer Firmen-Migration, einem
Alias-Wechsel oder weil ein Kollege den Account ursprünglich mit einer
Sammel-Mail-Adresse angelegt hat).
Die ursprüngliche Migration nach Drupal hat diesen Unterschied ignoriert: sowohl
name als auch mail wurden aus der Profil-Mail gesetzt.
Bei 1.751 der 38.111 Accounts (rund 4,6 %) weicht die Login-Mail von der
Profil-Mail ab — für diese Nutzer war der migrierte Drupal-Account faktisch
unauffindbar: das Login-Formular kennt nur die Profil-Mail (nicht die Adresse,
mit der der Nutzer es jahrelang gewohnt war einzuloggen), und auch der
Passwort-Reset griff ins Leere.
info@calmindon.de, Profil-Mail
tjark.schlegel@calmindon.de, uid 23264. Der Nutzer konnte sich
weder mit seiner gewohnten Login-Adresse einloggen noch per Passwort-Reset
wieder Zugriff erhalten, weil beide Drupal-Felder auf die Profil-Mail zeigten.
Marc hat am 09.07.2026 entschieden: Drupal-name wird künftig aus der
Neos-Login-Mail gesetzt (mb_strtolower(accountidentifier)),
mail bleibt unverändert die Profil-Mail. Diese Aufteilung ist kein
beliebiger Freiheitsgrad, sondern folgt zwingend aus der Drupal-Core-Mechanik:
/user/login, geteilter
Core-Login laut ADR-0053)
authentifiziert ausschließlich über das Feld name — nur die
Login-Mail funktioniert als Login-Kennung, die Profil-Mail nicht mehr.
user_pass) matcht dagegen
gegen mail oder name — ein Nutzer kann den
Reset also mit beiden Adressen anstoßen.
Damit ist die Login-Mail ab sofort die kanonische Identität für den
Authentifizierungs-Vorgang, während die Profil-Mail weiterhin die
Zustelladresse für Benachrichtigungen bleibt — unabhängig davon, ob beide
Adressen identisch sind oder nicht. Das Profil-Datenmodell aus
ADR-0052
(separate drupal/profile-Entity) ist von dieser Entscheidung nicht
betroffen.
Branch feature/neos-login-mail-as-username, Commit
abbe0362 — zum Zeitpunkt des Last-Sync noch nicht gemergt.
web/modules/custom/solcom_projektpartner/src/Plugin/migrate/source/NeosProjektpartnerUser.php
— Migrate-Source-Plugin bekommt einen Join auf
neos_flow_security_account und liefert das neue Source-Feld
login_email.
config/sync/migrate_plus.migration.solcom_projektpartner_neos_user.yml
(plus identische Kopie in
web/modules/custom/solcom_projektpartner/config/install/) —
process.name wird jetzt aus login_email abgeleitet
(skip_on_empty + mb_strtolower); mail
und init bleiben unverändert an email (Profil-Mail)
gebunden.
scripts/backfill-neos-login-names.php — idempotentes
drush php:script für den Bestand: lädt die betroffenen Accounts
über die neos-Datenbank-Connection, benennt
name per Entity-API um, mit Kollisions-Guard (Skip + Report bei
Zielnamen-Konflikt).
NeosSqlSourceTest,
NeosMigrationConfigTest.
renamed=0) wiederholt
laufen — pass-weise wird Y umbenannt und gibt damit X' Zielnamen frei.
| Kennzahl | Wert |
|---|---|
| Umbenannt via Backfill-Script | 1.750 von 1.751 |
| Verbleibender Skip (echte Kollision) | 1 (uid 36314 vs. uid 38184, beide wollen patrick.herb@hp-engineering.net) |
| Failed-Import-Rows vorher → nachher | 133 → 7 |
| User-Bestand vorher → nachher | 38.060 → 38.186 |
| Seit Go-Live (08.07.) bereits aktive Betroffene | 11 |
Die verbleibenden 7 Failed-Import-Rows sind echte
user__name- bzw. ppp_id_unique-Kollisionen und kein
Symptom dieser Migration. Die 126 vormals gescheiterten Profil-Mail-Duplikate
importieren jetzt als eigene Accounts mit korrekter rmsnumber —
das fixt nebenbei den alten Zufalls-Bug, bei dem pro Duplikat-Mail eine
zufällige Neos-Row den Import „gewann" und die anderen verlor.
Vorbereitender Ablaufplan für den produktiven Rollout — bewusst noch nicht scharf geschaltet:
neos-DB-Connection plus Entity-API) und kann bereits vor dem
PR-Merge laufen. Wiederholen, bis renamed=0 zurückkommt
(Fixpunkt der Cross-Kollisionen, siehe oben).
migrate:import retryt Status-3-Rows nicht automatisch — vorher
per SQL zurücksetzen:
UPDATE migrate_map_solcom_projektpartner_neos_user SET
source_row_status=1 WHERE source_row_status=3.
migrate:import --update ausführen.
Die Pass-Pipeline neos_expired_password würde damit alle
bereits neu gesetzten Passwörter zurücksetzen.
a-q7fxvm unter html/staging/tmp-dumps/.
name-Logik.
mail-Treffer vor dem name-Treffer.
| Strand | Ref | Inhalt |
|---|---|---|
| adr | 0052-projektpartner-profil-datenmodell | Profil-Datenmodell (drupal/profile) und die koordinierte User+Profil-Migration, in die diese Login-Mail-Korrektur eingreift. |
| adr | 0053-shared-login-clean-url-portal-integration | Geteilter Core-Login unter /user/login — Grundlage dafür, warum das Core-Feld name die Login-Kennung ist. |
| context | 0009-neos-projektportal-flow-inventory | Legacy-Neos-Flow-Inventar inkl. Flow-Security-Account-Objekt (1:1, trägt AccountIdentifier = Login-Mail) und Login-/Passwort-Reset-Flows. |
name gespeist wird.
feature/neos-login-mail-as-username.