Add production config with LDAP, fix LDAP auth flow

- Production port: 3389
- LDAP enabled with theta42.com config
- Proper bind -> search -> user bind flow
- Support service account bind for user search
- Add systemd service file
This commit is contained in:
2026-02-25 03:28:35 +00:00
parent 41833376f1
commit 48a876f34f
3 changed files with 72 additions and 24 deletions

View File

@@ -85,39 +85,37 @@ async function authenticateLDAP(username, password) {
reject(new Error('LDAP connection failed'));
});
// Construct user DN
const userDN = `uid=${username},${CONFIG.ldap.baseDN}`;
client.bind(userDN, password, (err) => {
if (err) {
client.destroy();
reject(new Error('Invalid credentials'));
return;
}
// Step 1: Bind with service account (if configured) to search for user
const doSearch = () => {
// Build search filter from config, replacing {{username}} with actual username
const filter = CONFIG.ldap.searchFilter.replace('{{username}}', username);
// Successfully authenticated - get user info
const searchOptions = {
scope: 'base',
filter: `(uid=${username})`,
scope: 'sub',
filter: filter,
attributes: ['dn', 'uid', 'cn', 'mail', 'displayName', 'memberOf']
};
client.search(userDN, searchOptions, (err, res) => {
let userDN = null;
const user = { username };
client.search(CONFIG.ldap.baseDN, searchOptions, (err, res) => {
if (err) {
client.destroy();
reject(err);
return;
}
const user = { username };
res.on('searchEntry', (entry) => {
userDN = entry.object.dn;
user.dn = entry.object.dn;
user.uid = entry.object.uid;
user.cn = entry.object.cn;
user.email = entry.object.mail;
user.displayName = entry.object.displayName || entry.object.cn;
user.groups = entry.object.memberOf || [];
user.groups = entry.object.memberOf
? (Array.isArray(entry.object.memberOf) ? entry.object.memberOf : [entry.object.memberOf])
: [];
});
res.on('error', (err) => {
@@ -126,11 +124,41 @@ async function authenticateLDAP(username, password) {
});
res.on('end', () => {
client.destroy();
resolve(user);
if (!userDN) {
client.destroy();
reject(new Error('User not found'));
return;
}
// Step 2: Bind as the user to verify password
client.bind(userDN, password, (err) => {
if (err) {
client.destroy();
reject(new Error('Invalid credentials'));
return;
}
client.destroy();
resolve(user);
});
});
});
});
};
// If we have a service account bind, use it first
if (CONFIG.ldap.bindDN && CONFIG.ldap.bindPassword) {
client.bind(CONFIG.ldap.bindDN, CONFIG.ldap.bindPassword, (err) => {
if (err) {
client.destroy();
reject(new Error('LDAP service bind failed'));
return;
}
doSearch();
});
} else {
// No service bind - search anonymously (may not work with all LDAP servers)
doSearch();
}
});
}