feat: add integration access settings (#725)
* feat: add integration access settings * fix: typecheck and test issues * fix: test timeout * chore: address pull request feedback * chore: add throw if action forbidden for integration permissions * fix: unable to create new migrations because of duplicate prevId in sqlite snapshots * chore: add sqlite migration for integration permissions * test: add unit tests for integration access * test: add permission checks to integration router tests * test: add unit test for integration permissions * chore: add mysql migration * fix: format issues
This commit is contained in:
1 parent
be711149f7
commit
408cdeb5c3
50 files changed
+4392
-615
No files matched your search
@@ -0,0 +1,155 @@
|
||||
import { describe, expect, test, vi } from "vitest";
|
||||
|
||||
import * as authShared from "@homarr/auth/shared";
|
||||
import { createId, eq } from "@homarr/db";
|
||||
import { integrations, users } from "@homarr/db/schema/sqlite";
|
||||
import { createDb } from "@homarr/db/test";
|
||||
|
||||
import { throwIfActionForbiddenAsync } from "../../integration/integration-access";
|
||||
|
||||
const defaultCreatorId = createId();
|
||||
|
||||
const expectActToBeAsync = async (act: () => Promise<void>, success: boolean) => {
|
||||
if (!success) {
|
||||
await expect(act()).rejects.toThrow("Integration not found");
|
||||
return;
|
||||
}
|
||||
|
||||
await expect(act()).resolves.toBeUndefined();
|
||||
};
|
||||
|
||||
describe("throwIfActionForbiddenAsync should check access to integration and return boolean", () => {
|
||||
test.each([
|
||||
["full" as const, true],
|
||||
["interact" as const, true],
|
||||
["use" as const, true],
|
||||
])("with permission %s should return %s when hasFullAccess is true", async (permission, expectedResult) => {
|
||||
// Arrange
|
||||
const db = createDb();
|
||||
const spy = vi.spyOn(authShared, "constructIntegrationPermissions");
|
||||
spy.mockReturnValue({
|
||||
hasFullAccess: true,
|
||||
hasInteractAccess: false,
|
||||
hasUseAccess: false,
|
||||
});
|
||||
|
||||
const integrationId = createId();
|
||||
await db.insert(integrations).values({
|
||||
id: integrationId,
|
||||
name: "test",
|
||||
kind: "adGuardHome",
|
||||
url: "http://localhost:3000",
|
||||
});
|
||||
|
||||
// Act
|
||||
const act = () =>
|
||||
throwIfActionForbiddenAsync({ db, session: null }, eq(integrations.id, integrationId), permission);
|
||||
|
||||
// Assert
|
||||
await expectActToBeAsync(act, expectedResult);
|
||||
});
|
||||
|
||||
test.each([
|
||||
["full" as const, false],
|
||||
["interact" as const, true],
|
||||
["use" as const, true],
|
||||
])("with permission %s should return %s when hasInteractAccess is true", async (permission, expectedResult) => {
|
||||
// Arrange
|
||||
const db = createDb();
|
||||
const spy = vi.spyOn(authShared, "constructIntegrationPermissions");
|
||||
spy.mockReturnValue({
|
||||
hasFullAccess: false,
|
||||
hasInteractAccess: true,
|
||||
hasUseAccess: false,
|
||||
});
|
||||
|
||||
await db.insert(users).values({ id: defaultCreatorId });
|
||||
const integrationId = createId();
|
||||
await db.insert(integrations).values({
|
||||
id: integrationId,
|
||||
name: "test",
|
||||
kind: "adGuardHome",
|
||||
url: "http://localhost:3000",
|
||||
});
|
||||
|
||||
// Act
|
||||
const act = () =>
|
||||
throwIfActionForbiddenAsync({ db, session: null }, eq(integrations.id, integrationId), permission);
|
||||
|
||||
// Assert
|
||||
await expectActToBeAsync(act, expectedResult);
|
||||
});
|
||||
|
||||
test.each([
|
||||
["full" as const, false],
|
||||
["interact" as const, false],
|
||||
["use" as const, true],
|
||||
])("with permission %s should return %s when hasUseAccess is true", async (permission, expectedResult) => {
|
||||
// Arrange
|
||||
const db = createDb();
|
||||
const spy = vi.spyOn(authShared, "constructIntegrationPermissions");
|
||||
spy.mockReturnValue({
|
||||
hasFullAccess: false,
|
||||
hasInteractAccess: false,
|
||||
hasUseAccess: true,
|
||||
});
|
||||
|
||||
await db.insert(users).values({ id: defaultCreatorId });
|
||||
const integrationId = createId();
|
||||
await db.insert(integrations).values({
|
||||
id: integrationId,
|
||||
name: "test",
|
||||
kind: "adGuardHome",
|
||||
url: "http://localhost:3000",
|
||||
});
|
||||
|
||||
// Act
|
||||
const act = () =>
|
||||
throwIfActionForbiddenAsync({ db, session: null }, eq(integrations.id, integrationId), permission);
|
||||
|
||||
// Assert
|
||||
await expectActToBeAsync(act, expectedResult);
|
||||
});
|
||||
|
||||
test.each([
|
||||
["full" as const, false],
|
||||
["interact" as const, false],
|
||||
["use" as const, false],
|
||||
])("with permission %s should return %s when hasUseAccess is false", async (permission, expectedResult) => {
|
||||
// Arrange
|
||||
const db = createDb();
|
||||
const spy = vi.spyOn(authShared, "constructIntegrationPermissions");
|
||||
spy.mockReturnValue({
|
||||
hasFullAccess: false,
|
||||
hasInteractAccess: false,
|
||||
hasUseAccess: false,
|
||||
});
|
||||
|
||||
await db.insert(users).values({ id: defaultCreatorId });
|
||||
const integrationId = createId();
|
||||
await db.insert(integrations).values({
|
||||
id: integrationId,
|
||||
name: "test",
|
||||
kind: "adGuardHome",
|
||||
url: "http://localhost:3000",
|
||||
});
|
||||
|
||||
// Act
|
||||
const act = () =>
|
||||
throwIfActionForbiddenAsync({ db, session: null }, eq(integrations.id, integrationId), permission);
|
||||
|
||||
// Assert
|
||||
await expectActToBeAsync(act, expectedResult);
|
||||
});
|
||||
|
||||
test("should throw when integration is not found", async () => {
|
||||
// Arrange
|
||||
const db = createDb();
|
||||
|
||||
// Act
|
||||
const act = () => throwIfActionForbiddenAsync({ db, session: null }, eq(integrations.id, createId()), "full");
|
||||
|
||||
// Assert
|
||||
await expect(act()).rejects.toThrow("Integration not found");
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user