59 lines
2.1 KiB
JavaScript
59 lines
2.1 KiB
JavaScript
// Multi-tenant LDAP helpers. The LDAP listener is process-level and runs in the
|
|
// process's default tenant context, so each bind/search must establish its own
|
|
// tenant context. The tenant is encoded as an extra dc component in the DN
|
|
// (uid=<email>,ou=people,dc=<tenant>,dc=saltcorn,dc=local); the bare base
|
|
// (dc=saltcorn,dc=local) means the default tenant (no wrap). resolveTenant
|
|
// validates the token against the live tenant set and DENIES unknown tenants so
|
|
// a crafted DN cannot reach another tenant's schema.
|
|
|
|
const db = require("@saltcorn/data/db");
|
|
|
|
// Capture the dc=<tenant> immediately preceding the dc=saltcorn,dc=local base.
|
|
const TENANT_RE = /dc=([^,]+),\s*dc=saltcorn,\s*dc=local\s*$/i;
|
|
|
|
|
|
const tenantFromDn = (dn) => {
|
|
const s = typeof dn === "string" ? dn : (dn ? dn.toString() : "");
|
|
const m = s.match(TENANT_RE);
|
|
return m ? m[1].trim().toLowerCase() : null;
|
|
};
|
|
|
|
|
|
// Resolve a parsed tenant token to a context decision:
|
|
// { tenant: null } -> run in the default context (no wrap)
|
|
// { tenant: "t1" } -> run inside runWithTenant("t1")
|
|
// { deny: true } -> reject (unknown tenant / illegal on single-tenant)
|
|
const resolveTenant = (token) => {
|
|
const def = String((db.connectObj && db.connectObj.default_schema) || "public").toLowerCase();
|
|
if (!token || token === def) {
|
|
return { tenant: null };
|
|
}
|
|
if (!db.is_it_multi_tenant || !db.is_it_multi_tenant()) {
|
|
return { deny: true };
|
|
}
|
|
let known;
|
|
try {
|
|
// getAllTenants is a MODULE-level export (returns the live {subdomain:State}
|
|
// map), NOT a method on the State instance.
|
|
const { getAllTenants } = require("@saltcorn/data/db/state");
|
|
known = new Set(Object.keys(getAllTenants() || {}).map((t) => String(t).toLowerCase()));
|
|
} catch (e) {
|
|
known = new Set();
|
|
}
|
|
if (known.has(token)) {
|
|
return { tenant: token };
|
|
}
|
|
return { deny: true };
|
|
};
|
|
|
|
|
|
const withTenant = (tenant, fn) => {
|
|
return tenant ? db.runWithTenant(tenant, fn) : fn();
|
|
};
|
|
|
|
|
|
module.exports = {
|
|
tenantFromDn,
|
|
resolveTenant,
|
|
withTenant
|
|
};
|