Files
test/backend/__tests__/middlewares/permission.spec.ts
Benjamin Falch 2bc741fb78
Some checks failed
Mark Stale PRs / stale (push) Has been cancelled
adding monkeytype
2026-04-23 13:53:44 +02:00

339 lines
9.3 KiB
TypeScript

import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
import { Response } from "express";
import { verifyPermissions } from "../../src/middlewares/permission";
import { EndpointMetadata } from "@monkeytype/contracts/util/api";
import * as Misc from "../../src/utils/misc";
import * as AdminUids from "../../src/dal/admin-uids";
import * as UserDal from "../../src/dal/user";
import MonkeyError from "../../src/utils/error";
import { DecodedToken } from "../../src/middlewares/auth";
import { TsRestRequest } from "../../src/api/types";
import { enableMonkeyErrorExpects } from "../__testData__/monkey-error";
enableMonkeyErrorExpects();
const uid = "123456789";
describe("permission middleware", () => {
const handler = verifyPermissions();
const res: Response = {} as any;
const next = vi.fn();
const getPartialUserMock = vi.spyOn(UserDal, "getPartialUser");
const isAdminMock = vi.spyOn(AdminUids, "isAdmin");
const isDevMock = vi.spyOn(Misc, "isDevEnvironment");
beforeEach(() => {
next.mockClear();
getPartialUserMock.mockClear().mockResolvedValue({} as any);
isDevMock.mockClear().mockReturnValue(false);
isAdminMock.mockClear().mockResolvedValue(false);
});
afterEach(() => {
//next function must only be called once
expect(next).toHaveBeenCalledOnce();
});
it("should bypass without requiredPermission", async () => {
//GIVEN
const req = givenRequest({});
//WHEN
await handler(req, res, next);
//THEN
expect(next).toHaveBeenCalledWith();
});
it("should bypass with empty requiredPermission", async () => {
//GIVEN
const req = givenRequest({ requirePermission: [] });
//WHEN
await handler(req, res, next);
//THE
expect(next).toHaveBeenCalledWith();
});
describe("admin check", () => {
const requireAdminPermission: EndpointMetadata = {
requirePermission: "admin",
};
it("should fail without authentication", async () => {
//GIVEN
const req = givenRequest(requireAdminPermission);
//WHEN
await handler(req, res, next);
//THEN
expect(next).toHaveBeenCalledWith(
expect.toMatchMonkeyError(
new MonkeyError(403, "You don't have permission to do this."),
),
);
});
it("should pass without authentication if publicOnDev on dev", async () => {
//GIVEN
isDevMock.mockReturnValue(true);
const req = givenRequest(
{
...requireAdminPermission,
authenticationOptions: { isPublicOnDev: true },
},
{ uid },
);
//WHEN
await handler(req, res, next);
//THEN
expect(next).toHaveBeenCalledWith();
});
it("should fail without authentication if publicOnDev on prod ", async () => {
//GIVEN
const req = givenRequest(
{
...requireAdminPermission,
authenticationOptions: { isPublicOnDev: true },
},
{ uid },
);
//WHEN
await handler(req, res, next);
//THEN
expect(next).toHaveBeenCalledWith(
expect.toMatchMonkeyError(
new MonkeyError(403, "You don't have permission to do this."),
),
);
});
it("should fail without admin permissions", async () => {
//GIVEN
const req = givenRequest(requireAdminPermission, { uid });
//WHEN
await handler(req, res, next);
//THEN
expect(next).toHaveBeenCalledWith(
expect.toMatchMonkeyError(
new MonkeyError(403, "You don't have permission to do this."),
),
);
expect(isAdminMock).toHaveBeenCalledWith(uid);
});
});
describe("user checks", () => {
it("should fetch user only once", async () => {
//GIVEN
const req = givenRequest(
{
requirePermission: ["canReport", "canManageApeKeys"],
},
{ uid },
);
//WHEN
await handler(req, res, next);
//THEN
expect(getPartialUserMock).toHaveBeenCalledOnce();
expect(getPartialUserMock).toHaveBeenCalledWith(
uid,
"check user permissions",
["canReport", "canManageApeKeys"],
);
});
it("should fail if authentication is missing", async () => {
//GIVEN
const req = givenRequest({
requirePermission: ["canReport", "canManageApeKeys"],
});
//WHEN
await handler(req, res, next);
//THEN
expect(next).toHaveBeenCalledWith(
expect.toMatchMonkeyError(
new MonkeyError(
403,
"Failed to check permissions, authentication required.",
),
),
);
});
});
describe("quoteMod check", () => {
const requireQuoteMod: EndpointMetadata = {
requirePermission: "quoteMod",
};
it("should pass for quoteAdmin", async () => {
//GIVEN
getPartialUserMock.mockResolvedValue({ quoteMod: true } as any);
const req = givenRequest(requireQuoteMod, { uid });
//WHEN
await handler(req, res, next);
//THEN
expect(next).toHaveBeenCalledWith();
expect(getPartialUserMock).toHaveBeenCalledWith(
uid,
"check user permissions",
["quoteMod"],
);
});
it("should pass for specific language", async () => {
//GIVEN
getPartialUserMock.mockResolvedValue({ quoteMod: "english" } as any);
const req = givenRequest(requireQuoteMod, { uid });
//WHEN
await handler(req, res, next);
//THEN
expect(next).toHaveBeenCalledWith();
expect(getPartialUserMock).toHaveBeenCalledWith(
uid,
"check user permissions",
["quoteMod"],
);
});
it("should fail for empty string", async () => {
//GIVEN
getPartialUserMock.mockResolvedValue({ quoteMod: "" } as any);
const req = givenRequest(requireQuoteMod, { uid });
//WHEN
await handler(req, res, next);
//THEN
expect(next).toHaveBeenCalledWith(
expect.toMatchMonkeyError(
new MonkeyError(403, "You don't have permission to do this."),
),
);
});
it("should fail for missing quoteMod", async () => {
//GIVEN
getPartialUserMock.mockResolvedValue({} as any);
const req = givenRequest(requireQuoteMod, { uid });
//WHEN
await handler(req, res, next);
//THEN
expect(next).toHaveBeenCalledWith(
expect.toMatchMonkeyError(
new MonkeyError(403, "You don't have permission to do this."),
),
);
});
});
describe("canReport check", () => {
const requireCanReport: EndpointMetadata = {
requirePermission: "canReport",
};
it("should fail if user cannot report", async () => {
//GIVEN
getPartialUserMock.mockResolvedValue({ canReport: false } as any);
const req = givenRequest(requireCanReport, { uid });
//WHEN
await handler(req, res, next);
//THEN
expect(next).toHaveBeenCalledWith(
expect.toMatchMonkeyError(
new MonkeyError(403, "You don't have permission to do this."),
),
);
expect(getPartialUserMock).toHaveBeenCalledWith(
uid,
"check user permissions",
["canReport"],
);
});
it("should pass if user can report", async () => {
//GIVEN
getPartialUserMock.mockResolvedValue({ canReport: true } as any);
const req = givenRequest(requireCanReport, { uid });
//WHEN
await handler(req, res, next);
//THEN
expect(next).toHaveBeenCalledWith();
});
it("should pass if canReport is not set", async () => {
//GIVEN
getPartialUserMock.mockResolvedValue({} as any);
const req = givenRequest(requireCanReport, { uid });
//WHEN
await handler(req, res, next);
//THEN
expect(next).toHaveBeenCalledWith();
});
});
describe("canManageApeKeys check", () => {
const requireCanReport: EndpointMetadata = {
requirePermission: "canManageApeKeys",
};
it("should fail if user cannot report", async () => {
//GIVEN
getPartialUserMock.mockResolvedValue({ canManageApeKeys: false } as any);
const req = givenRequest(requireCanReport, { uid });
//WHEN
await handler(req, res, next);
//THEN
expect(next).toHaveBeenCalledWith(
expect.toMatchMonkeyError(
new MonkeyError(
403,
"You have lost access to ape keys, please contact support",
),
),
);
expect(getPartialUserMock).toHaveBeenCalledWith(
uid,
"check user permissions",
["canManageApeKeys"],
);
});
it("should pass if user can report", async () => {
//GIVEN
getPartialUserMock.mockResolvedValue({ canManageApeKeys: true } as any);
const req = givenRequest(requireCanReport, { uid });
//WHEN
await handler(req, res, next);
//THEN
expect(next).toHaveBeenCalledWith();
});
it("should pass if canManageApeKeys is not set", async () => {
//GIVEN
getPartialUserMock.mockResolvedValue({} as any);
const req = givenRequest(requireCanReport, { uid });
//WHEN
await handler(req, res, next);
//THEN
expect(next).toHaveBeenCalledWith();
});
});
});
function givenRequest(
metadata: EndpointMetadata,
decodedToken?: Partial<DecodedToken>,
): TsRestRequest {
return { tsRestRoute: { metadata }, ctx: { decodedToken } } as any;
}