This commit is contained in:
Carsten Graf
2026-01-26 17:58:12 +01:00
parent dfbc7d8bbc
commit ca48cdf78f
9 changed files with 971 additions and 178 deletions

View File

@@ -104,26 +104,41 @@ class LDAPService {
/**
* Wert eines LDAP-Attributs extrahieren
*
* Die ldapjs-Bibliothek behandelt UTF-8-Zeichen automatisch korrekt.
* Diese Funktion stellt sicher, dass UTF-8-Zeichen wie ß, ä, ö, ü korrekt zurückgegeben werden.
*/
static getAttributeValue(entry, attributeName) {
const attr = entry.attributes.find(a => a.type === attributeName);
if (!attr) {
return null;
}
return Array.isArray(attr.values) ? attr.values[0] : attr.values;
const value = Array.isArray(attr.values) ? attr.values[0] : attr.values;
// Stelle sicher, dass der Wert als String zurückgegeben wird (UTF-8 wird automatisch korrekt behandelt)
return value != null ? String(value) : null;
}
/**
* Escaped einen Wert für LDAP-Filter (verhindert LDAP-Injection)
*
* WICHTIG: UTF-8-Zeichen wie ß, ä, ö, ü müssen NICHT escaped werden.
* LDAP-Filter unterstützen UTF-8 direkt nach RFC 4515.
* Nur die speziellen LDAP-Filter-Zeichen werden escaped.
*/
static escapeLDAPFilter(value) {
if (!value) return '';
return value
.replace(/\\/g, '\\5c')
.replace(/\*/g, '\\2a')
.replace(/\(/g, '\\28')
.replace(/\)/g, '\\29')
.replace(/\0/g, '\\00');
// Stelle sicher, dass der Wert als String behandelt wird
const str = String(value);
// Escape nur die speziellen LDAP-Filter-Zeichen
// UTF-8-Zeichen wie ß, ä, ö, ü werden direkt verwendet
return str
.replace(/\\/g, '\\5c') // Backslash
.replace(/\*/g, '\\2a') // Stern
.replace(/\(/g, '\\28') // Öffnende Klammer
.replace(/\)/g, '\\29') // Schließende Klammer
.replace(/\0/g, '\\00'); // Null-Byte
}
/**
@@ -145,12 +160,14 @@ class LDAPService {
}
const ldapUser = ldapUsers[index];
const username = ldapUser.username.trim();
const firstname = ldapUser.firstname.trim();
const lastname = ldapUser.lastname.trim();
// .trim() behält UTF-8-Zeichen wie ß, ä, ö, ü korrekt bei
// Stelle sicher, dass Werte als String behandelt werden
const username = String(ldapUser.username || '').trim();
const firstname = String(ldapUser.firstname || '').trim();
const lastname = String(ldapUser.lastname || '').trim();
// Prüfe ob Benutzer bereits existiert
db.get('SELECT id, role FROM users WHERE username = ?', [username], (err, existingUser) => {
// Prüfe ob Benutzer bereits existiert (case-insensitive)
db.get('SELECT id, role FROM users WHERE username = ? COLLATE NOCASE', [username], (err, existingUser) => {
if (err) {
errors.push(`Fehler beim Prüfen von ${username}: ${err.message}`);
errorCount++;
@@ -158,9 +175,9 @@ class LDAPService {
}
if (existingUser) {
// Benutzer existiert - aktualisiere nur Name, behalte Rolle
// Benutzer existiert - aktualisiere nur Name, behalte Rolle (case-insensitive)
db.run(
'UPDATE users SET firstname = ?, lastname = ? WHERE username = ?',
'UPDATE users SET firstname = ?, lastname = ? WHERE username = ? COLLATE NOCASE',
[firstname, lastname, username],
(err) => {
if (err) {
@@ -232,8 +249,18 @@ class LDAPService {
/**
* Benutzer gegen LDAP authentifizieren
*
* Unterstützt UTF-8-Zeichen wie ß, ä, ö, ü in Usernamen.
* Die ldapjs-Bibliothek behandelt UTF-8 automatisch korrekt.
*/
static authenticate(username, password, callback) {
// Stelle sicher, dass Username als String behandelt wird (UTF-8 wird korrekt unterstützt)
const usernameStr = String(username || '').trim();
if (!usernameStr) {
return callback(new Error('Benutzername darf nicht leer sein'), false);
}
// Konfiguration abrufen
this.getConfig((err, config) => {
if (err || !config || !config.enabled) {
@@ -249,7 +276,8 @@ class LDAPService {
// Suche nach dem Benutzer in LDAP
const baseDN = config.base_dn || '';
const usernameAttr = config.username_attribute || 'cn';
const escapedUsername = this.escapeLDAPFilter(username);
// escapeLDAPFilter behandelt UTF-8-Zeichen korrekt (escaped sie nicht)
const escapedUsername = this.escapeLDAPFilter(usernameStr);
const searchFilter = `(${usernameAttr}=${escapedUsername})`;
const searchOptions = {
filter: searchFilter,
@@ -262,7 +290,9 @@ class LDAPService {
client.search(baseDN, searchOptions, (err, res) => {
if (err) {
client.unbind();
return callback(err, false);
// Verbesserte Fehlermeldung für mögliche Encoding-Probleme
const errorMsg = err.message || String(err);
return callback(new Error(`LDAP-Suche fehlgeschlagen: ${errorMsg}. Hinweis: Prüfen Sie, ob der Benutzername UTF-8-Zeichen (wie ß, ä, ö, ü) korrekt enthält.`), false);
}
res.on('searchEntry', (entry) => {
@@ -271,7 +301,8 @@ class LDAPService {
res.on('error', (err) => {
client.unbind();
callback(err, false);
const errorMsg = err.message || String(err);
callback(new Error(`LDAP-Suchfehler: ${errorMsg}`), false);
});
res.on('end', (result) => {
@@ -279,7 +310,8 @@ class LDAPService {
client.unbind();
if (!userDN) {
return callback(new Error('Benutzer nicht gefunden'), false);
// Verbesserte Fehlermeldung: Hinweis auf mögliche Encoding-Probleme
return callback(new Error(`Benutzer "${usernameStr}" nicht gefunden. Hinweis: Prüfen Sie, ob der Benutzername korrekt ist und UTF-8-Zeichen (wie ß, ä, ö, ü) korrekt geschrieben sind.`), false);
}
// Versuche, sich mit den Benutzer-Credentials zu binden
@@ -297,7 +329,8 @@ class LDAPService {
authClient.bind(userDN, password, (err) => {
authClient.unbind();
if (err) {
return callback(new Error('Ungültiges Passwort'), false);
const errorMsg = err.message || String(err);
return callback(new Error(`Ungültiges Passwort oder Authentifizierungsfehler: ${errorMsg}`), false);
}
callback(null, true);
});