Robustheits-Fix: RFC-5321-konforme E-Mail-Local-Parts (keine Rand-Punkte/Doppelpunkte) + sAMAccountName-Fallback bei Nicht-Latin-Namen + Release v11
This commit is contained in:
@@ -168,7 +168,8 @@ async function createADUser({ ou, vorname, nachname, email, username, password,
|
|||||||
// Build UPN — FIX: Username umlautfrei normalisieren (ä→ae etc.),
|
// Build UPN — FIX: Username umlautfrei normalisieren (ä→ae etc.),
|
||||||
// damit kein ungültiger UPN wie müller@... entsteht.
|
// damit kein ungültiger UPN wie müller@... entsteht.
|
||||||
const upnSuffix = LDAP_UPN_SUFFIX || LDAP_BIND_USER.split('@')[1] || LDAP_DOMAIN.toLowerCase() + '.intra';
|
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
|
// sAMAccountName: max 20 chars
|
||||||
let sAMAccountName = toSamAccountName(username);
|
let sAMAccountName = toSamAccountName(username);
|
||||||
@@ -176,6 +177,11 @@ async function createADUser({ ou, vorname, nachname, email, username, password,
|
|||||||
sAMAccountName = toSamAccountName(nachname + vorname.charAt(0));
|
sAMAccountName = toSamAccountName(nachname + vorname.charAt(0));
|
||||||
}
|
}
|
||||||
sAMAccountName = sAMAccountName.substring(0, 20);
|
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
|
// userAccountControl: 514 = NORMAL_ACCOUNT + ACCOUNTDISABLE
|
||||||
// ldapts requires attribute values as strings (numbers cause "The string argument must be of type string" error)
|
// ldapts requires attribute values as strings (numbers cause "The string argument must be of type string" error)
|
||||||
|
|||||||
@@ -78,6 +78,14 @@ router.post('/create-user', adminMiddleware, validate(createADUserSchema), async
|
|||||||
}
|
}
|
||||||
sAMAccountName = sAMAccountName.substring(0, 20);
|
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);
|
const existing = await checkADUserExists(username, sAMAccountName);
|
||||||
if (existing) {
|
if (existing) {
|
||||||
return res.status(409).json({ error: 'Benutzername "' + username + '" existiert bereits im Active Directory.', dn: existing.distinguishedName });
|
return res.status(409).json({ error: 'Benutzername "' + username + '" existiert bereits im Active Directory.', dn: existing.distinguishedName });
|
||||||
|
|||||||
@@ -19,13 +19,18 @@ function transliterateUmlauts(str) {
|
|||||||
* Normalisiert einen Namen für den lokalen Teil einer E-Mail-Adresse:
|
* Normalisiert einen Namen für den lokalen Teil einer E-Mail-Adresse:
|
||||||
* Umlaute → ASCII, Kleinbuchstaben, Leerzeichen → Punkte, nur a-z0-9. erlaubt.
|
* Umlaute → ASCII, Kleinbuchstaben, Leerzeichen → Punkte, nur a-z0-9. erlaubt.
|
||||||
* Beispiel: "Jürgen Schröder" → "juergen.schroeder"
|
* 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) {
|
function toEmailLocalPart(str) {
|
||||||
if (typeof str !== 'string') return '';
|
if (typeof str !== 'string') return '';
|
||||||
return transliterateUmlauts(str)
|
return transliterateUmlauts(str)
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
.replace(/\s+/g, '.')
|
.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)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
services:
|
services:
|
||||||
app:
|
app:
|
||||||
image: gitea.leonkn.de/lxe/workflow-app:DEV2.0-v10
|
image: gitea.leonkn.de/lxe/workflow-app:DEV2.0-v11
|
||||||
ports:
|
ports:
|
||||||
- "3900:5000"
|
- "3900:5000"
|
||||||
volumes:
|
volumes:
|
||||||
|
|||||||
@@ -98,9 +98,10 @@ export default function FillModal({ template, onSubmit, onClose }) {
|
|||||||
if (!sourceStep || !values[sourceStep.id]) return '';
|
if (!sourceStep || !values[sourceStep.id]) return '';
|
||||||
// FIX: Umlaute konvertieren (ä→ae etc.) — sonst ungültige E-Mails wie j.schröder@...
|
// FIX: Umlaute konvertieren (ä→ae etc.) — sonst ungültige E-Mails wie j.schröder@...
|
||||||
return toEmailLocalPart(values[sourceStep.id]);
|
return toEmailLocalPart(values[sourceStep.id]);
|
||||||
}).filter(Boolean);
|
}).filter(Boolean); // leere Teile (z.B. Name nur aus Sonderzeichen) fallen weg
|
||||||
if (parts.length === 0) return '';
|
if (parts.length === 0) return '';
|
||||||
const domain = step.email_domain.startsWith('@') ? step.email_domain : '@' + step.email_domain;
|
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;
|
return parts.join('.') + domain;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -22,13 +22,17 @@ export function transliterateUmlauts(str) {
|
|||||||
* Normalisiert einen Namen für den lokalen Teil einer E-Mail-Adresse:
|
* Normalisiert einen Namen für den lokalen Teil einer E-Mail-Adresse:
|
||||||
* Umlaute → ASCII, Kleinbuchstaben, Leerzeichen → Punkte, nur a-z0-9. erlaubt.
|
* Umlaute → ASCII, Kleinbuchstaben, Leerzeichen → Punkte, nur a-z0-9. erlaubt.
|
||||||
* Beispiel: "Jürgen Schröder" → "juergen.schroeder"
|
* Beispiel: "Jürgen Schröder" → "juergen.schroeder"
|
||||||
|
* RFC 5321: kein führender/abschließender Punkt, keine Doppelpunkte.
|
||||||
*/
|
*/
|
||||||
export function toEmailLocalPart(str) {
|
export function toEmailLocalPart(str) {
|
||||||
if (typeof str !== 'string') return '';
|
if (typeof str !== 'string') return '';
|
||||||
return transliterateUmlauts(str)
|
return transliterateUmlauts(str)
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
.replace(/\s+/g, '.')
|
.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
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user