Ports the upstream compliance-core audit fixes onto the kept core-dsr, core-consent, core-audit, core-cms and core-shared packages (pristine template state here, so taken to the fixed end-state): - core-dsr: scope DSR operations to the caller's own subject (A11); include the subject's audit trail in exports; resolve the per-request binding from ctx instead of a throwing singleton proxy. - core-consent: build the consent router from the shared superjson transformer (A10); merge per-category on persist instead of replacing; validate migrated categories against an allow-list. - core-audit: keyed 128-bit pseudonyms + salted DSR certificate; add the audit-logs collection and the req-scoped GDPR audit-erasure afterDelete hook (A6). - core-shared: grace-purge soft-deleted rows via a retention-purge task + tombstone field and boot registration (A2/A3); add the require-authenticated tRPC helper; derive clientIp + resolve the session user in createTrpcContext (B2/A11). - core-cms: register audit-logs, wire the audit-erasure hook and retention-purge tasks; adapted to our collection set (users, workspaces). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016j8z4VHjedXDTjEDNg7qHK
51 lines
1.5 KiB
TypeScript
51 lines
1.5 KiB
TypeScript
import { describe, it, expect } from "vitest";
|
|
import { z } from "zod";
|
|
import { t } from "@/trpc/init";
|
|
import {
|
|
requireAuthenticated,
|
|
protectedProcedure,
|
|
} from "@/trpc/require-authenticated";
|
|
|
|
const echoRouter = t.router({
|
|
publicEcho: t.procedure
|
|
.input(z.object({ value: z.string() }).strict())
|
|
.query(({ input }) => input.value),
|
|
protectedEcho: protectedProcedure
|
|
.input(z.object({ value: z.string() }).strict())
|
|
.mutation(({ input, ctx }) => ({
|
|
value: input.value,
|
|
userId: (ctx as { user: { id: string } }).user.id,
|
|
})),
|
|
composedEcho: t.procedure
|
|
.use(requireAuthenticated)
|
|
.input(z.object({}).strict())
|
|
.mutation(() => "ok"),
|
|
});
|
|
|
|
describe("requireAuthenticated middleware (B7)", () => {
|
|
it("rejects anonymous callers with UNAUTHORIZED", async () => {
|
|
const caller = echoRouter.createCaller({});
|
|
await expect(caller.protectedEcho({ value: "x" })).rejects.toMatchObject({
|
|
code: "UNAUTHORIZED",
|
|
});
|
|
await expect(caller.composedEcho({})).rejects.toMatchObject({
|
|
code: "UNAUTHORIZED",
|
|
});
|
|
});
|
|
|
|
it("passes through authenticated callers and exposes ctx.user", async () => {
|
|
const caller = echoRouter.createCaller({
|
|
user: { id: "user-1", roles: [] },
|
|
});
|
|
await expect(caller.protectedEcho({ value: "x" })).resolves.toEqual({
|
|
value: "x",
|
|
userId: "user-1",
|
|
});
|
|
});
|
|
|
|
it("leaves public procedures untouched", async () => {
|
|
const caller = echoRouter.createCaller({});
|
|
await expect(caller.publicEcho({ value: "hi" })).resolves.toBe("hi");
|
|
});
|
|
});
|