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, ): TsRestRequest { return { tsRestRoute: { metadata }, ctx: { decodedToken } } as any; }