From 4d5ab818e95ba785c98054a646de7ac6d8960297 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=BChn?= Date: Thu, 17 Sep 2026 13:15:27 +0200 Subject: [PATCH] Robustheits-Fix: RFC-5321-konforme E-Mail-Local-Parts (keine Rand-Punkte/Doppelpunkte) + sAMAccountName-Fallback bei Nicht-Latin-Namen + Release v11 --- backend/ldapOperations.js | 8 +++++++- backend/routes/ad.js | 8 ++++++++ backend/umlauts.js | 7 ++++++- docker-compose.stack.yml | 2 +- frontend/src/components/FillModal.jsx | 3 ++- frontend/src/utils/umlauts.js | 6 +++++- 6 files changed, 29 insertions(+), 5 deletions(-) diff --git a/backend/ldapOperations.js b/backend/ldapOperations.js index 9a44e55..5c38286 100644 --- a/backend/ldapOperations.js +++ b/backend/ldapOperations.js @@ -168,7 +168,8 @@ async function createADUser({ ou, vorname, nachname, email, username, password, // Build UPN — FIX: Username umlautfrei normalisieren (ä→ae etc.), // damit kein ungültiger UPN wie müller@... entsteht. const upnSuffix = LDAP_UPN_SUFFIX || LDAP_BIND_USER.split('@')[1] || LDAP_DOMAIN.toLowerCase() + '.intra'; - const userPrincipalName = toSamAccountName(username) + '@' + upnSuffix; + const normalizedUsername = toSamAccountName(username) || 'user' + Date.now().toString(36); + const userPrincipalName = normalizedUsername + '@' + upnSuffix; // sAMAccountName: max 20 chars let sAMAccountName = toSamAccountName(username); @@ -176,6 +177,11 @@ async function createADUser({ ou, vorname, nachname, email, username, password, sAMAccountName = toSamAccountName(nachname + vorname.charAt(0)); } sAMAccountName = sAMAccountName.substring(0, 20); + // Fallback: Name nur aus Sonderzeichen → generischer Name (AD lehnt leeren sAMAccountName ab) + if (!sAMAccountName) { + sAMAccountName = ('user' + Date.now().toString(36)).substring(0, 20); + console.warn('[LDAP] sAMAccountName war nach Normalisierung leer — Fallback:', sAMAccountName); + } // userAccountControl: 514 = NORMAL_ACCOUNT + ACCOUNTDISABLE // ldapts requires attribute values as strings (numbers cause "The string argument must be of type string" error) diff --git a/backend/routes/ad.js b/backend/routes/ad.js index 5a6add1..1c3b240 100644 --- a/backend/routes/ad.js +++ b/backend/routes/ad.js @@ -78,6 +78,14 @@ router.post('/create-user', adminMiddleware, validate(createADUserSchema), async } sAMAccountName = sAMAccountName.substring(0, 20); + // Fallback: Wenn der Name nur aus Sonderzeichen bestand (z.B. rein chinesisch), + // wäre sAMAccountName leer — AD würde den User ablehnen. Dann generischen + // Namen aus Zeitstempel verwenden und den Original-Username als displayName behalten. + if (!sAMAccountName) { + sAMAccountName = 'user' + Date.now().toString(36); + console.warn('[AD] sAMAccountName war nach Normalisierung leer — Fallback:', sAMAccountName); + } + const existing = await checkADUserExists(username, sAMAccountName); if (existing) { return res.status(409).json({ error: 'Benutzername "' + username + '" existiert bereits im Active Directory.', dn: existing.distinguishedName }); diff --git a/backend/umlauts.js b/backend/umlauts.js index 0558a73..04ecf93 100644 --- a/backend/umlauts.js +++ b/backend/umlauts.js @@ -19,13 +19,18 @@ function transliterateUmlauts(str) { * Normalisiert einen Namen für den lokalen Teil einer E-Mail-Adresse: * Umlaute → ASCII, Kleinbuchstaben, Leerzeichen → Punkte, nur a-z0-9. erlaubt. * Beispiel: "Jürgen Schröder" → "juergen.schroeder" + * + * RFC 5321-konform: Kein führender/abschließender Punkt, keine Doppelpunkte + * (".."), kein leerer Local-Part (gibt '' zurück — Aufrufer muss Fallback haben). */ function toEmailLocalPart(str) { if (typeof str !== 'string') return ''; return transliterateUmlauts(str) .toLowerCase() .replace(/\s+/g, '.') - .replace(/[^a-z0-9.]/g, ''); + .replace(/[^a-z0-9.]/g, '') + .replace(/\.{2,}/g, '.') // ".." → "." (durch aufeinanderfolgende Trenner) + .replace(/^\.+|\.+$/g, ''); // führende/abschließende Punkte entfernen (RFC 5321) } /** diff --git a/docker-compose.stack.yml b/docker-compose.stack.yml index 5cdf9ff..b52293f 100644 --- a/docker-compose.stack.yml +++ b/docker-compose.stack.yml @@ -6,7 +6,7 @@ services: app: - image: gitea.leonkn.de/lxe/workflow-app:DEV2.0-v10 + image: gitea.leonkn.de/lxe/workflow-app:DEV2.0-v11 ports: - "3900:5000" volumes: diff --git a/frontend/src/components/FillModal.jsx b/frontend/src/components/FillModal.jsx index cac50f4..695455b 100644 --- a/frontend/src/components/FillModal.jsx +++ b/frontend/src/components/FillModal.jsx @@ -98,9 +98,10 @@ export default function FillModal({ template, onSubmit, onClose }) { if (!sourceStep || !values[sourceStep.id]) return ''; // FIX: Umlaute konvertieren (ä→ae etc.) — sonst ungültige E-Mails wie j.schröder@... return toEmailLocalPart(values[sourceStep.id]); - }).filter(Boolean); + }).filter(Boolean); // leere Teile (z.B. Name nur aus Sonderzeichen) fallen weg if (parts.length === 0) return ''; const domain = step.email_domain.startsWith('@') ? step.email_domain : '@' + step.email_domain; + // parts sind durch toEmailLocalPart bereits punkten- und leer-part-bereinigt return parts.join('.') + domain; }; diff --git a/frontend/src/utils/umlauts.js b/frontend/src/utils/umlauts.js index 2123f2f..4b87ee4 100644 --- a/frontend/src/utils/umlauts.js +++ b/frontend/src/utils/umlauts.js @@ -22,13 +22,17 @@ export function transliterateUmlauts(str) { * Normalisiert einen Namen für den lokalen Teil einer E-Mail-Adresse: * Umlaute → ASCII, Kleinbuchstaben, Leerzeichen → Punkte, nur a-z0-9. erlaubt. * Beispiel: "Jürgen Schröder" → "juergen.schroeder" + * RFC 5321: kein führender/abschließender Punkt, keine Doppelpunkte. */ export function toEmailLocalPart(str) { if (typeof str !== 'string') return ''; return transliterateUmlauts(str) .toLowerCase() .replace(/\s+/g, '.') - .replace(/[^a-z0-9.]/g, ''); + .replace(/[^a-z0-9.]/g, '') + .replace(/\.{2,}/g, '.') // ".." → "." (durch aufeinanderfolgende Trenner) + .replace(/\.+$/g, '') // abschließende Punkte entfernen + .replace(/^\.+/, ''); // führende Punkte entfernen } /**