mirror of
https://github.com/ajnart/homarr.git
synced 2026-01-30 11:19:12 +01:00
316 lines
8.8 KiB
TypeScript
316 lines
8.8 KiB
TypeScript
import { CredentialsSignin } from "@auth/core/errors";
|
|
import { describe, expect, test, vi } from "vitest";
|
|
|
|
import type { Database } from "@homarr/db";
|
|
import { and, createId, eq } from "@homarr/db";
|
|
import { groups, users } from "@homarr/db/schema";
|
|
import { createDb } from "@homarr/db/test";
|
|
|
|
import { authorizeWithLdapCredentialsAsync } from "../credentials/authorization/ldap-authorization";
|
|
import * as ldapClient from "../credentials/ldap-client";
|
|
|
|
vi.mock("../../env.mjs", () => ({
|
|
env: {
|
|
AUTH_LDAP_BIND_DN: "bind_dn",
|
|
AUTH_LDAP_BIND_PASSWORD: "bind_password",
|
|
AUTH_LDAP_USER_MAIL_ATTRIBUTE: "mail",
|
|
AUTH_LDAP_GROUP_CLASS: "group",
|
|
},
|
|
}));
|
|
|
|
describe("authorizeWithLdapCredentials", () => {
|
|
test("should fail when wrong ldap base credentials", async () => {
|
|
// Arrange
|
|
const spy = vi.spyOn(ldapClient, "LdapClient");
|
|
spy.mockImplementation(
|
|
() =>
|
|
({
|
|
bindAsync: vi.fn(() => Promise.reject(new Error("bindAsync"))),
|
|
}) as unknown as ldapClient.LdapClient,
|
|
);
|
|
|
|
// Act
|
|
const act = () =>
|
|
authorizeWithLdapCredentialsAsync(null as unknown as Database, {
|
|
name: "test",
|
|
password: "test",
|
|
});
|
|
|
|
// Assert
|
|
await expect(act()).rejects.toThrow(CredentialsSignin);
|
|
});
|
|
|
|
test("should fail when user not found", async () => {
|
|
// Arrange
|
|
const spy = vi.spyOn(ldapClient, "LdapClient");
|
|
spy.mockImplementation(
|
|
() =>
|
|
({
|
|
bindAsync: vi.fn(() => Promise.resolve()),
|
|
searchAsync: vi.fn(() => Promise.resolve([])),
|
|
}) as unknown as ldapClient.LdapClient,
|
|
);
|
|
|
|
// Act
|
|
const act = () =>
|
|
authorizeWithLdapCredentialsAsync(null as unknown as Database, {
|
|
name: "test",
|
|
password: "test",
|
|
});
|
|
|
|
// Assert
|
|
await expect(act()).rejects.toThrow(CredentialsSignin);
|
|
});
|
|
|
|
test("should fail when user has invalid email", async () => {
|
|
// Arrange
|
|
const spy = vi.spyOn(ldapClient, "LdapClient");
|
|
spy.mockImplementation(
|
|
() =>
|
|
({
|
|
bindAsync: vi.fn(() => Promise.resolve()),
|
|
searchAsync: vi.fn(() =>
|
|
Promise.resolve([
|
|
{
|
|
dn: "test",
|
|
mail: "test",
|
|
},
|
|
]),
|
|
),
|
|
}) as unknown as ldapClient.LdapClient,
|
|
);
|
|
|
|
// Act
|
|
const act = () =>
|
|
authorizeWithLdapCredentialsAsync(null as unknown as Database, {
|
|
name: "test",
|
|
password: "test",
|
|
});
|
|
|
|
// Assert
|
|
await expect(act()).rejects.toThrow(CredentialsSignin);
|
|
});
|
|
|
|
test("should fail when user password is incorrect", async () => {
|
|
// Arrange
|
|
const searchSpy = vi.fn(() =>
|
|
Promise.resolve([
|
|
{
|
|
dn: "test",
|
|
mail: "test@gmail.com",
|
|
},
|
|
]),
|
|
);
|
|
const spy = vi.spyOn(ldapClient, "LdapClient");
|
|
spy.mockImplementation(
|
|
() =>
|
|
({
|
|
bindAsync: vi.fn((props: ldapClient.BindOptions) =>
|
|
props.distinguishedName === "test" ? Promise.reject(new Error("bindAsync")) : Promise.resolve(),
|
|
),
|
|
searchAsync: searchSpy,
|
|
}) as unknown as ldapClient.LdapClient,
|
|
);
|
|
|
|
// Act
|
|
const act = () =>
|
|
authorizeWithLdapCredentialsAsync(null as unknown as Database, {
|
|
name: "test",
|
|
password: "test",
|
|
});
|
|
|
|
// Assert
|
|
await expect(act()).rejects.toThrow(CredentialsSignin);
|
|
expect(searchSpy).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
test("should authorize user with correct credentials and create user", async () => {
|
|
// Arrange
|
|
const db = createDb();
|
|
const spy = vi.spyOn(ldapClient, "LdapClient");
|
|
spy.mockImplementation(
|
|
() =>
|
|
({
|
|
bindAsync: vi.fn(() => Promise.resolve()),
|
|
searchAsync: vi.fn(() =>
|
|
Promise.resolve([
|
|
{
|
|
dn: "test",
|
|
mail: "test@gmail.com",
|
|
},
|
|
]),
|
|
),
|
|
disconnectAsync: vi.fn(),
|
|
}) as unknown as ldapClient.LdapClient,
|
|
);
|
|
|
|
// Act
|
|
const result = await authorizeWithLdapCredentialsAsync(db, {
|
|
name: "test",
|
|
password: "test",
|
|
});
|
|
|
|
// Assert
|
|
expect(result.name).toBe("test");
|
|
expect(result.groups).toHaveLength(0); // Groups are needed in signIn events callback
|
|
const dbUser = await db.query.users.findFirst({
|
|
where: eq(users.name, "test"),
|
|
});
|
|
expect(dbUser).toBeDefined();
|
|
expect(dbUser?.id).toBe(result.id);
|
|
expect(dbUser?.email).toBe("test@gmail.com");
|
|
expect(dbUser?.emailVerified).not.toBeNull();
|
|
expect(dbUser?.provider).toBe("ldap");
|
|
});
|
|
|
|
test("should authorize user with correct credentials and create user with same email when credentials user already exists", async () => {
|
|
// Arrange
|
|
const db = createDb();
|
|
const spy = vi.spyOn(ldapClient, "LdapClient");
|
|
spy.mockImplementation(
|
|
() =>
|
|
({
|
|
bindAsync: vi.fn(() => Promise.resolve()),
|
|
searchAsync: vi.fn(() =>
|
|
Promise.resolve([
|
|
{
|
|
dn: "test",
|
|
mail: "test@gmail.com",
|
|
},
|
|
]),
|
|
),
|
|
disconnectAsync: vi.fn(),
|
|
}) as unknown as ldapClient.LdapClient,
|
|
);
|
|
await db.insert(users).values({
|
|
id: createId(),
|
|
name: "test",
|
|
email: "test@gmail.com",
|
|
provider: "credentials",
|
|
});
|
|
|
|
// Act
|
|
const result = await authorizeWithLdapCredentialsAsync(db, {
|
|
name: "test",
|
|
password: "test",
|
|
});
|
|
|
|
// Assert
|
|
expect(result.name).toBe("test");
|
|
expect(result.groups).toHaveLength(0); // Groups are needed in signIn events callback
|
|
const dbUser = await db.query.users.findFirst({
|
|
where: and(eq(users.name, "test"), eq(users.provider, "ldap")),
|
|
});
|
|
expect(dbUser).toBeDefined();
|
|
expect(dbUser?.id).toBe(result.id);
|
|
expect(dbUser?.email).toBe("test@gmail.com");
|
|
expect(dbUser?.emailVerified).not.toBeNull();
|
|
expect(dbUser?.provider).toBe("ldap");
|
|
|
|
const credentialsUser = await db.query.users.findFirst({
|
|
where: and(eq(users.name, "test"), eq(users.provider, "credentials")),
|
|
});
|
|
|
|
expect(credentialsUser).toBeDefined();
|
|
expect(credentialsUser?.id).not.toBe(result.id);
|
|
});
|
|
|
|
// The name update occurs in the signIn event callback
|
|
test("should authorize user with correct credentials and return updated 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();
|
|
await db.insert(users).values({
|
|
id: userId,
|
|
name: "test-old",
|
|
email: "test@gmail.com",
|
|
provider: "ldap",
|
|
});
|
|
|
|
// Act
|
|
const result = await authorizeWithLdapCredentialsAsync(db, {
|
|
name: "test",
|
|
password: "test",
|
|
});
|
|
|
|
// Assert
|
|
expect(result).toEqual({ id: userId, name: "test", groups: [] });
|
|
|
|
const dbUser = await db.query.users.findFirst({
|
|
where: eq(users.id, userId),
|
|
});
|
|
|
|
expect(dbUser).toBeDefined();
|
|
expect(dbUser?.id).toBe(userId);
|
|
expect(dbUser?.name).toBe("test-old");
|
|
expect(dbUser?.email).toBe("test@gmail.com");
|
|
expect(dbUser?.provider).toBe("ldap");
|
|
});
|
|
|
|
test("should authorize user with correct credentials and return his groups", 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",
|
|
});
|
|
|
|
// Assert
|
|
expect(result).toEqual({ id: userId, name: "test", groups: ["homarr_example"] });
|
|
});
|
|
});
|