@simpleworkjs/ldap
v1.0.2
Published
Shared ldapts client + read queries + RFC 4515/4514 escaping for the theta42 apps.
Downloads
3,869
Maintainers
Readme
@simpleworkjs/ldap
Shared ldapts client + read queries + RFC 4515/4514 escaping for the theta42 apps (sso-manager-node, proxy, jump-host). The connection and read surface that all three copy-paste; write-side and posix logic stay app-local (sso owns those).
What it provides
escapeFilter(value)— RFC 4515. Escapes* \ ( ) NULin a value interpolated into a search filter. This is the fix for the proxy's latent filter-injection bug (User.getinterpolateddata.usernameraw).escapeDN(value)— RFC 4514. Escapes the reserved DN chars (sso had this asescapeLDAPDNValue; jump-host never built a DN by interpolation so didn't).makeClient(conf, {Client?})—new Client({ url, tlsOptions }).tlsOptionsis forwarded as-is (no default): sso omits it and relies on ldapts' cert validation, so the shared client must not silently weaken it. Jump-host applies its own{ rejectUnauthorized: false }default in its app-local wrapper.withClient(conf, fn, {Client?})— admin-bind,fn(client), alwaysunbind()in finally.createLdapClient(conf, {Client?, Change?, Attribute?})— factory returning the jump-host API shape bound to aconf.ldapblock:
{
makeClient, withClient,
getUser(uid), // -> { dn, uid, sshPublicKeys: [] } | null
getGroups(dn), // -> [cn, ...] (groupOfNames membership)
checkPassword(dn, pw), // -> bool (simple bind as the user)
addSshKey(dn, keyLine), // -> void (idempotent multi-value add)
escapeFilter, escapeDN,
}conf is injected, not read from @simpleworkjs/conf here — so the package is unit-testable with a plain object and a fake Client. Apps pass their conf.ldap block.
conf.ldap reconciliation
The three apps name things slightly differently; the factory normalizes:
| field | sso | jump-host | proxy | default |
|---|---|---|---|---|
| user base | userBase | userBase | searchBase | userBase \|\| searchBase |
| user filter | userFilter (posixAccount) | (hardcoded) | userFilter (inetOrgPerson) | (objectClass=posixAccount) |
| user attr | userNameAttribute | userNameAttribute | userNameAttribute | uid |
| group base | groupBase | groupBase | (none — uses memberOf overlay) | — |
So jump-host's hardcoded (objectClass=posixAccount) becomes the default, and the proxy's inetOrgPerson is expressed via conf.userFilter rather than code.
Install
npm install @simpleworkjs/ldapUsage (jump-host)
const { createLdapClient } = require('@simpleworkjs/ldap');
const conf = require('@simpleworkjs/conf');
const ldap = createLdapClient(conf.ldap);
const user = await ldap.getUser('alice'); // { dn, uid, sshPublicKeys }
const groups = await ldap.getGroups(user.dn); // ['host_web01_access', ...]Why
Two things this exists to fix:
- Security — the proxy built
(&${conf.userFilter}(${attr}=${data.username}))with the username interpolated raw.escapeFiltercloses that. - Duplication — sso's
user_ldap.jsandgroup_ldap.jsalready byte-duplicatemakeClient/withClient/escapeLDAPDNValue; jump-host reimplements the same pattern. One package, one source of truth.
License
MIT © William Mantly
