diff --git a/apps/webapp/test/envJwtActorClaim.test.ts b/apps/webapp/test/envJwtActorClaim.test.ts index 97348f06b..f48a4df99 100644 --- a/apps/webapp/test/envJwtActorClaim.test.ts +++ b/apps/webapp/test/envJwtActorClaim.test.ts @@ -1,12 +1,17 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; const mocks = vi.hoisted(() => ({ + authenticateRequest: vi.fn<(...args: any[]) => Promise>(), + verifyUserActorToken: vi.fn<(...args: any[]) => Promise>(), + isUserActorToken: vi.fn<(value: string) => boolean>(), authenticateUatOrApiRequest: vi.fn<(...args: any[]) => Promise>(), authorizePatEnvironmentAccess: vi.fn<(...args: any[]) => Promise>(), })); -vi.mock("~/services/uatRoutePreamble.server", () => ({ - authenticateUatOrApiRequest: mocks.authenticateUatOrApiRequest, +vi.mock("@trigger.dev/rbac", async (importOriginal) => ({ + ...(await importOriginal>()), + isUserActorToken: mocks.isUserActorToken, + verifyUserActorToken: mocks.verifyUserActorToken, })); vi.mock("~/services/environmentVariableApiAccess.server", () => ({ authorizePatEnvironmentAccess: mocks.authorizePatEnvironmentAccess, @@ -14,10 +19,13 @@ vi.mock("~/services/environmentVariableApiAccess.server", () => ({ vi.mock("~/services/apiAuth.server", () => ({ authenticatedEnvironmentForAuthentication: vi.fn(async () => environment), branchNameFromRequest: () => undefined, + authenticateRequest: mocks.authenticateRequest, })); vi.mock("~/services/logger.server", () => ({ - logger: { debug: vi.fn(), error: vi.fn(), warn: vi.fn() }, + logger: { debug: vi.fn(), error: vi.fn(), warn: vi.fn(), info: vi.fn() }, })); +vi.mock("~/env.server", () => ({ env: { SESSION_SECRET: "test-session-secret" } })); +vi.mock("~/db.server", () => ({ prisma: {}, $replica: {} })); import { validateJWT } from "@trigger.dev/core/v3/jwt"; import { action } from "~/routes/api.v1.projects.$projectRef.$env.jwt"; @@ -32,10 +40,10 @@ const environment = { const params = { projectRef: "proj_abc", env: "prod" }; -function request(body: unknown = {}) { +function request(body: unknown = {}, bearer = "tr_pat_test") { return new Request("https://example.com/api/v1/projects/proj_abc/prod/jwt", { method: "POST", - headers: { "Content-Type": "application/json" }, + headers: { "Content-Type": "application/json", Authorization: `Bearer ${bearer}` }, body: JSON.stringify(body), }); } @@ -50,14 +58,18 @@ async function mintedClaims(body?: unknown) { describe("env JWT exchange — act claim", () => { beforeEach(() => { - mocks.authenticateUatOrApiRequest.mockReset(); + mocks.authenticateRequest.mockReset(); + mocks.verifyUserActorToken.mockReset(); + mocks.isUserActorToken.mockReset(); + mocks.isUserActorToken.mockReturnValue(false); mocks.authorizePatEnvironmentAccess.mockReset(); mocks.authorizePatEnvironmentAccess.mockResolvedValue(undefined); }); it("stamps the PAT's user with the personal-access-token client", async () => { - mocks.authenticateUatOrApiRequest.mockResolvedValue({ - authenticationResult: { type: "personalAccessToken", result: { userId: "usr_42" } }, + mocks.authenticateRequest.mockResolvedValue({ + type: "personalAccessToken", + result: { userId: "usr_42" }, }); const claims = await mintedClaims(); @@ -67,9 +79,13 @@ describe("env JWT exchange — act claim", () => { }); it("passes through a user-actor token's own client", async () => { - mocks.authenticateUatOrApiRequest.mockResolvedValue({ - authenticationResult: { type: "personalAccessToken", result: { userId: "usr_7" } }, - userActor: { userId: "usr_7", client: "dashboard-agent", cap: ["read:runs"] }, + mocks.isUserActorToken.mockReturnValue(true); + mocks.verifyUserActorToken.mockResolvedValue({ + userId: "usr_7", + client: "dashboard-agent", + // An agent token always carries the environment it was minted for. + environmentId: environment.id, + cap: ["read:runs"], }); const claims = await mintedClaims({ claims: { scopes: ["read:runs"] } }); @@ -79,11 +95,9 @@ describe("env JWT exchange — act claim", () => { }); it("omits act for an org access token (no user)", async () => { - mocks.authenticateUatOrApiRequest.mockResolvedValue({ - authenticationResult: { - type: "organizationAccessToken", - result: { organizationId: "org_1" }, - }, + mocks.authenticateRequest.mockResolvedValue({ + type: "organizationAccessToken", + result: { organizationId: "org_1" }, }); const claims = await mintedClaims(); @@ -93,7 +107,7 @@ describe("env JWT exchange — act claim", () => { }); it("401s without a token", async () => { - mocks.authenticateUatOrApiRequest.mockResolvedValue(undefined); + mocks.authenticateRequest.mockResolvedValue(undefined); const response = await action({ request: request(), params, context: {} as any });