feat: add member sync between groups of ldap and homarr (#1149)

* feat: add member sync between groups of ldap and homarr

* chore: remove temporary console statement

* test: add unit tests for adding and removing ldap group members
This commit is contained in:
Meier Lukas
2024-10-01 16:46:18 +02:00
committed by GitHub
parent 4a8ed13a87
commit 61333094df
2 changed files with 217 additions and 13 deletions

View File

@@ -3,10 +3,9 @@ import { describe, expect, test, vi } from "vitest";
import type { Database } from "@homarr/db";
import { and, createId, eq } from "@homarr/db";
import { users } from "@homarr/db/schema/sqlite";
import { groupMembers, groups, users } from "@homarr/db/schema/sqlite";
import { createDb } from "@homarr/db/test";
import { createSaltAsync, hashPasswordAsync } from "../../security";
import { authorizeWithLdapCredentialsAsync } from "../credentials/authorization/ldap-authorization";
import * as ldapClient from "../credentials/ldap-client";
@@ -15,6 +14,7 @@ vi.mock("../../env.mjs", () => ({
AUTH_LDAP_BIND_DN: "bind_dn",
AUTH_LDAP_BIND_PASSWORD: "bind_password",
AUTH_LDAP_USER_MAIL_ATTRIBUTE: "mail",
AUTH_LDAP_GROUP_CLASS: "group",
},
}));
@@ -171,7 +171,6 @@ describe("authorizeWithLdapCredentials", () => {
// Arrange
const db = createDb();
const spy = vi.spyOn(ldapClient, "LdapClient");
const salt = await createSaltAsync();
spy.mockImplementation(
() =>
({
@@ -190,8 +189,6 @@ describe("authorizeWithLdapCredentials", () => {
await db.insert(users).values({
id: createId(),
name: "test",
salt,
password: await hashPasswordAsync("test", salt),
email: "test@gmail.com",
provider: "credentials",
});
@@ -224,14 +221,28 @@ describe("authorizeWithLdapCredentials", () => {
test("should authorize user with correct credentials and update name", async () => {
// Arrange
const spy = vi.spyOn(ldapClient, "LdapClient");
spy.mockImplementation(
() =>
({
bindAsync: vi.fn(() => Promise.resolve()),
searchAsync: vi.fn(() =>
Promise.resolve([
{
dn: "test55",
mail: "test@gmail.com",
},
]),
),
disconnectAsync: vi.fn(),
}) as unknown as ldapClient.LdapClient,
);
const userId = createId();
const db = createDb();
const salt = await createSaltAsync();
await db.insert(users).values({
id: userId,
name: "test-old",
salt,
password: await hashPasswordAsync("test", salt),
email: "test@gmail.com",
provider: "ldap",
});
@@ -256,4 +267,127 @@ describe("authorizeWithLdapCredentials", () => {
expect(dbUser?.email).toBe("test@gmail.com");
expect(dbUser?.provider).toBe("ldap");
});
test("should authorize user with correct credentials and add him to the groups that he is in LDAP but not in Homar", async () => {
// Arrange
const spy = vi.spyOn(ldapClient, "LdapClient");
spy.mockImplementation(
() =>
({
bindAsync: vi.fn(() => Promise.resolve()),
searchAsync: vi.fn((argument: { options: { filter: string } }) =>
argument.options.filter.includes("group")
? Promise.resolve([
{
cn: "homarr_example",
},
])
: Promise.resolve([
{
dn: "test55",
mail: "test@gmail.com",
},
]),
),
disconnectAsync: vi.fn(),
}) as unknown as ldapClient.LdapClient,
);
const db = createDb();
const userId = createId();
await db.insert(users).values({
id: userId,
name: "test",
email: "test@gmail.com",
provider: "ldap",
});
const groupId = createId();
await db.insert(groups).values({
id: groupId,
name: "homarr_example",
});
// Act
const result = await authorizeWithLdapCredentialsAsync(db, {
name: "test",
password: "test",
credentialType: "ldap",
});
// Assert
expect(result).toEqual({ id: userId, name: "test" });
const dbGroupMembers = await db.query.groupMembers.findMany();
expect(dbGroupMembers).toHaveLength(1);
});
test("should authorize user with correct credentials and remove him from groups he is in Homarr but not in LDAP", async () => {
// Arrange
const spy = vi.spyOn(ldapClient, "LdapClient");
spy.mockImplementation(
() =>
({
bindAsync: vi.fn(() => Promise.resolve()),
searchAsync: vi.fn((argument: { options: { filter: string } }) =>
argument.options.filter.includes("group")
? Promise.resolve([
{
cn: "homarr_example",
},
])
: Promise.resolve([
{
dn: "test55",
mail: "test@gmail.com",
},
]),
),
disconnectAsync: vi.fn(),
}) as unknown as ldapClient.LdapClient,
);
const db = createDb();
const userId = createId();
await db.insert(users).values({
id: userId,
name: "test",
email: "test@gmail.com",
provider: "ldap",
});
const groupIds = [createId(), createId()] as const;
await db.insert(groups).values([
{
id: groupIds[0],
name: "homarr_example",
},
{
id: groupIds[1],
name: "homarr_no_longer_member",
},
]);
await db.insert(groupMembers).values([
{
userId,
groupId: groupIds[0],
},
{
userId,
groupId: groupIds[1],
},
]);
// Act
const result = await authorizeWithLdapCredentialsAsync(db, {
name: "test",
password: "test",
credentialType: "ldap",
});
// Assert
expect(result).toEqual({ id: userId, name: "test" });
const dbGroupMembers = await db.query.groupMembers.findMany();
expect(dbGroupMembers).toHaveLength(1);
expect(dbGroupMembers[0]?.groupId).toBe(groupIds[0]);
});
});