56 lines
1.7 KiB
JavaScript
56 lines
1.7 KiB
JavaScript
// LDAP service account (search-then-bind binder). A configured service DN +
|
|
// sealed password lets an application bind as a non-user principal, search for a
|
|
// user by mail, then re-bind as that user DN to validate the password -- the
|
|
// standard enterprise flow -- without exposing a real user as the binder. The
|
|
// password is sealed at rest with the same KEK pattern as client secrets and is
|
|
// never returned to the wire or echoed in the admin UI. Single-row table,
|
|
// tenant-scoped (all db.* calls resolve the current tenant schema).
|
|
|
|
const db = require("@saltcorn/data/db");
|
|
|
|
const idpCrypto = require("../crypto");
|
|
|
|
const { TABLE_LDAP_SERVICE } = require("../constants");
|
|
|
|
|
|
const getServiceAccount = async () => {
|
|
const row = await db.selectMaybeOne(TABLE_LDAP_SERVICE, {});
|
|
if (!row || !row.dn) {
|
|
return null;
|
|
}
|
|
const password = idpCrypto.openText({
|
|
ciphertext: row.secret_ciphertext,
|
|
iv: row.secret_iv,
|
|
tag: row.secret_tag
|
|
}).toString("utf8");
|
|
return { dn: row.dn, password: password };
|
|
};
|
|
|
|
|
|
const getServiceDn = async () => {
|
|
const row = await db.selectMaybeOne(TABLE_LDAP_SERVICE, {});
|
|
return row && row.dn ? row.dn : null;
|
|
};
|
|
|
|
|
|
const setServiceAccount = async (dn, plaintext) => {
|
|
await db.deleteWhere(TABLE_LDAP_SERVICE, {});
|
|
if (!dn || !plaintext) {
|
|
return;
|
|
}
|
|
const sealed = idpCrypto.sealText(plaintext);
|
|
await db.insert(TABLE_LDAP_SERVICE, {
|
|
dn: dn,
|
|
secret_ciphertext: sealed.ciphertext,
|
|
secret_iv: sealed.iv,
|
|
secret_tag: sealed.tag,
|
|
created_at: new Date().toISOString()
|
|
}, { noid: true });
|
|
};
|
|
|
|
|
|
module.exports = {
|
|
getServiceAccount,
|
|
getServiceDn,
|
|
setServiceAccount
|
|
};
|