mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-10-31 07:35:24 +00:00
fix(ldap): preserve the directory-provided bind DN (#1486)
This commit is contained in:
1 parent
4897024324
commit
378be3405a
2 files changed
+37
-8
No files matched your search
@@ -141,12 +141,15 @@ export function createLdapLogin(ctx: PluginContext, store: ProviderStore) {
|
|||||||
config.displayNameAttribute || "cn",
|
config.displayNameAttribute || "cn",
|
||||||
"mail",
|
"mail",
|
||||||
"email",
|
"email",
|
||||||
|
"distinguishedName",
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
if (entries.length === 0) throw await refuse("user not found");
|
if (entries.length === 0) throw await refuse("user not found");
|
||||||
|
|
||||||
const userEntry = entries[0];
|
const userEntry = entries[0];
|
||||||
const userDN = userEntry.dn.toString();
|
// AD supplies its DN as an attribute; avoid re-serializing it through ldapjs.
|
||||||
|
const userDN =
|
||||||
|
firstValue(userEntry, "distinguishedName") || userEntry.dn.toString();
|
||||||
const uid =
|
const uid =
|
||||||
firstValue(userEntry, config.usernameAttribute || "uid") || username;
|
firstValue(userEntry, config.usernameAttribute || "uid") || username;
|
||||||
const displayName =
|
const displayName =
|
||||||
|
|||||||
@@ -11,7 +11,9 @@ const directory = vi.hoisted(() => ({
|
|||||||
binds: [] as string[],
|
binds: [] as string[],
|
||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock("ldapjs", () => {
|
vi.mock("ldapjs", async () => {
|
||||||
|
const { default: ldap } =
|
||||||
|
await vi.importActual<typeof import("ldapjs")>("ldapjs");
|
||||||
function createClient() {
|
function createClient() {
|
||||||
return {
|
return {
|
||||||
bind: (dn: string, password: string, cb: (err?: Error) => void) => {
|
bind: (dn: string, password: string, cb: (err?: Error) => void) => {
|
||||||
@@ -25,7 +27,7 @@ vi.mock("ldapjs", () => {
|
|||||||
},
|
},
|
||||||
search: (
|
search: (
|
||||||
base: string,
|
base: string,
|
||||||
options: { filter: string },
|
options: { filter: string; attributes: string[] },
|
||||||
cb: (err: Error | null, res: unknown) => void,
|
cb: (err: Error | null, res: unknown) => void,
|
||||||
) => {
|
) => {
|
||||||
const listeners: Record<string, (value?: unknown) => void> = {};
|
const listeners: Record<string, (value?: unknown) => void> = {};
|
||||||
@@ -49,11 +51,13 @@ vi.mock("ldapjs", () => {
|
|||||||
const user = match ? directory.users[match[1]] : undefined;
|
const user = match ? directory.users[match[1]] : undefined;
|
||||||
if (user) {
|
if (user) {
|
||||||
listeners.searchEntry?.({
|
listeners.searchEntry?.({
|
||||||
dn: { toString: () => user.dn },
|
dn: ldap.parseDN(user.dn),
|
||||||
attributes: Object.entries(user.attrs).map(([type, value]) => ({
|
attributes: Object.entries(user.attrs)
|
||||||
type,
|
.filter(([type]) => options.attributes.includes(type))
|
||||||
values: [value],
|
.map(([type, value]) => ({
|
||||||
})),
|
type,
|
||||||
|
values: [value],
|
||||||
|
})),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -121,6 +125,28 @@ function verify(
|
|||||||
}
|
}
|
||||||
|
|
||||||
describe("LDAP sign-in", () => {
|
describe("LDAP sign-in", () => {
|
||||||
|
it.each([
|
||||||
|
"CN=Bob,OU=Первый Департамент,DC=example,DC=com",
|
||||||
|
String.raw`CN=Bob\, Builder,OU=Первый Департамент,DC=example,DC=com`,
|
||||||
|
String.raw`CN=Bob\5cBuilder,OU=People,DC=example,DC=com`,
|
||||||
|
])(
|
||||||
|
"binds with the directory's original distinguishedName: %s",
|
||||||
|
async (dn) => {
|
||||||
|
const { s, id } = await withDirectory();
|
||||||
|
directory.users.bob.dn = dn;
|
||||||
|
directory.users.bob.attrs.distinguishedName = dn;
|
||||||
|
const identity = await verify(s, String(id), {
|
||||||
|
username: "bob",
|
||||||
|
password: "hunter2",
|
||||||
|
});
|
||||||
|
expect(identity).toMatchObject({ subject: "bob" });
|
||||||
|
expect(directory.binds).toEqual(["cn=service", dn]);
|
||||||
|
await expect(
|
||||||
|
verify(s, String(id), { username: "bob", password: "wrong" }),
|
||||||
|
).rejects.toMatchObject({ status: 401 });
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
it("binds as the service, then as the user, and returns the identity", async () => {
|
it("binds as the service, then as the user, and returns the identity", async () => {
|
||||||
const { s, id } = await withDirectory();
|
const { s, id } = await withDirectory();
|
||||||
directory.adminDns = ["uid=bob,ou=people"];
|
directory.adminDns = ["uid=bob,ou=people"];
|
||||||
|
|||||||
Reference in new issue
Block a user