Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions lib/tasks/__tests__/validateDeleteTaskRequest.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,29 @@ describe("validateDeleteTaskRequest", () => {
const res = await validateDeleteTaskRequest(request);
expect(res).toBe(forbidden);
});

it("forwards body account_id to validateAuthContext as the override (chat#1918)", async () => {
const OTHER = "999e4567-e89b-12d3-a456-426614174999";
const request = new NextRequest("http://localhost/api/tasks", {
method: "DELETE",
headers: { "Content-Type": "application/json", "x-api-key": "test-key" },
body: JSON.stringify({ id: TASK_ID, account_id: OTHER }),
});
await validateDeleteTaskRequest(request);
expect(validateAuthContext).toHaveBeenCalledWith(
expect.anything(),
expect.objectContaining({ accountId: OTHER }),
);
});

it("rejects a non-UUID account_id with 400", async () => {
const request = new NextRequest("http://localhost/api/tasks", {
method: "DELETE",
headers: { "Content-Type": "application/json", "x-api-key": "test-key" },
body: JSON.stringify({ id: TASK_ID, account_id: "not-a-uuid" }),
});
const result = await validateDeleteTaskRequest(request);
expect(result).toBeInstanceOf(NextResponse);
expect((result as NextResponse).status).toBe(400);
});
});
7 changes: 7 additions & 0 deletions lib/tasks/validateDeleteTaskBody.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@ import { z } from "zod";

export const deleteTaskBodySchema = z.object({
id: z.string().uuid("id must be a valid UUID").describe("UUID of the task to delete"),
account_id: z
.string()
.uuid("account_id must be a valid UUID")
.optional()
.describe(
"Account context for org/API-key rules; authorized via validateAuthContext. Not a raw column override.",
),
});

export type DeleteTaskBody = z.infer<typeof deleteTaskBodySchema>;
Expand Down
7 changes: 6 additions & 1 deletion lib/tasks/validateDeleteTaskRequest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,12 @@ export async function validateDeleteTaskRequest(
);
}

const authContext = await validateAuthContext(request);
// Mirror GET/POST/PATCH: an org or admin key may act in a member account's
// context. Without this, an admin key could create and edit a customer's task
// but got 403 deleting it (chat#1918).
const authContext = await validateAuthContext(request, {
accountId: validationResult.data.account_id,
});
if (authContext instanceof NextResponse) {
return authContext;
}
Expand Down
Loading