Umlaut-Fix: zentrale Transliteration (ae/oe/ue/ss) fuer E-Mails, UPN und sAMAccountName + Release v10

This commit is contained in:
Kühn
2026-09-16 11:27:03 +02:00
parent b369ac955b
commit 1c28917bb5
6 changed files with 121 additions and 40 deletions

View File

@@ -1,4 +1,5 @@
const { Client, Attribute, Change } = require('ldapts');
const { transliterateUmlauts, toSamAccountName } = require('./umlauts');
/**
* LDAP Operations Module (ldapts)
@@ -133,12 +134,10 @@ function escapeLDAPDN(str) {
/**
* Replace German umlauts and ß for sAMAccountName compatibility.
* (Delegiert an zentrale umlauts.js — Logik dort gepflegt.)
*/
function replaceUmlauts(str) {
return str
.replace(/ä/g, 'ae').replace(/ö/g, 'oe').replace(/ü/g, 'ue')
.replace(/Ä/g, 'Ae').replace(/Ö/g, 'Oe').replace(/Ü/g, 'Ue')
.replace(/ß/g, 'ss');
return transliterateUmlauts(str);
}
/**
@@ -166,14 +165,15 @@ async function createADUser({ ou, vorname, nachname, email, username, password,
const escapedCN = escapeLDAPDN(cnValue);
const dn = 'CN=' + escapedCN + ',' + ou;
// Build UPN
// 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 = username + '@' + upnSuffix;
const userPrincipalName = toSamAccountName(username) + '@' + upnSuffix;
// sAMAccountName: max 20 chars
let sAMAccountName = username;
let sAMAccountName = toSamAccountName(username);
if (vorname && nachname) {
sAMAccountName = replaceUmlauts(nachname + vorname.charAt(0)).replace(/[^a-zA-Z0-9]/g, '');
sAMAccountName = toSamAccountName(nachname + vorname.charAt(0));
}
sAMAccountName = sAMAccountName.substring(0, 20);

View File

@@ -8,6 +8,7 @@ const { isLDAPConfigured } = require('../ldapSync');
const { browseOUTree, createADUser, checkADUserExists, deleteADUser } = require('../ldapOperations');
const { searchADGroups, addUserToGroups, browseADGroups } = require('../ldapOperations');
const { validate, createADUserSchema, deleteADUserSchema } = require('../middleware/validation');
const { toSamAccountName } = require('../umlauts');
const router = express.Router();
@@ -68,14 +69,14 @@ router.post('/create-user', adminMiddleware, validate(createADUserSchema), async
const { ou, vorname, nachname, email, username, password, department, telefon, titel, displayName, physicalDeliveryOfficeName, company, description, wWWHomePage, streetAddress, postOfficeBox, l, st, postalCode, c, groups } = req.validatedBody;
try {
let sAMAccountName = username;
// FIX: Umlaute zentral transliterieren (ä→ae etc.) — galt vorher nur für
// nachname/vorname einzeln; jetzt einheitlich über umlauts.js. Der Username
// (und damit UPN) wird ebenfalls umlautfrei normalisiert.
let sAMAccountName = toSamAccountName(username);
if (vorname && nachname) {
sAMAccountName = nachname.replace(/ä/g, 'ae').replace(/ö/g, 'oe').replace(/ü/g, 'ue')
.replace(/Ä/g, 'Ae').replace(/Ö/g, 'Oe').replace(/Ü/g, 'Ue').replace(/ß/g, 'ss')
+ vorname.charAt(0).replace(/ä/g, 'ae').replace(/ö/g, 'oe').replace(/ü/g, 'ue')
.replace(/Ä/g, 'Ae').replace(/Ö/g, 'Oe').replace(/Ü/g, 'Ue').replace(/ß/g, 'ss');
sAMAccountName = sAMAccountName.replace(/[^a-zA-Z0-9]/g, '').substring(0, 20);
sAMAccountName = toSamAccountName(nachname + vorname.charAt(0));
}
sAMAccountName = sAMAccountName.substring(0, 20);
const existing = await checkADUserExists(username, sAMAccountName);
if (existing) {

49
backend/umlauts.js Normal file
View File

@@ -0,0 +1,49 @@
/**
* Umlaut-Transliteration für AD-kompatible Namen (E-Mails, sAMAccountName, UPN).
*
* Wandelt deutsche Sonderzeichen in ASCII-Äquivalente um:
* ä→ae, ö→oe, ü→ue, ß→ss (und Großvarianten)
*
* WICHTIG: Reihenfolge — Großbuchstaben-Varianten VOR toLowerCase() behandeln,
* damit "Schröder" → "Schroeder" (nicht "schroder") und "Straße" → "Strasse".
*/
function transliterateUmlauts(str) {
if (typeof str !== 'string') return '';
return str
.replace(/ä/g, 'ae').replace(/ö/g, 'oe').replace(/ü/g, 'ue')
.replace(/Ä/g, 'Ae').replace(/Ö/g, 'Oe').replace(/Ü/g, 'Ue')
.replace(/ß/g, 'ss');
}
/**
* 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"
*/
function toEmailLocalPart(str) {
if (typeof str !== 'string') return '';
return transliterateUmlauts(str)
.toLowerCase()
.replace(/\s+/g, '.')
.replace(/[^a-z0-9.]/g, '');
}
/**
* Normalisiert einen Namen für sAMAccountName/UPN (Punkt-Format):
* wie toEmailLocalPart, aber ohne Punkt-Entfernung (Punkte sind erlaubt).
*/
function toAdAccountName(str) {
return toEmailLocalPart(str);
}
/**
* Normalisiert einen Namen für das Pre-Windows-2000-Format (Nachname+Initial):
* Umlaute → ASCII, nur a-zA-Z0-9 erlaubt, Groß-/Kleinschreibung bleibt erhalten.
* Beispiel: "Schröder" + "J" → "SchroederJ"
*/
function toSamAccountName(str) {
if (typeof str !== 'string') return '';
return transliterateUmlauts(str).replace(/[^a-zA-Z0-9]/g, '');
}
module.exports = { transliterateUmlauts, toEmailLocalPart, toAdAccountName, toSamAccountName };