fix(compliance): port DSR/consent/audit/retention audit fixes
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
This commit is contained in:
50
packages/core-shared/src/trpc/require-authenticated.test.ts
Normal file
50
packages/core-shared/src/trpc/require-authenticated.test.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
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");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user