feat(core-audit): PayloadAuditLog.eraseSubject (pseudonymize + delete via overrideAccess)

Replaces the Phase-2 stub with a real impl. Mode "delete" issues a bulk
payload.delete with overrideAccess:true to bypass the append-only rule.
Mode "pseudonymize" fetches up to 10_000 matching docs and patches each
actorId to the token produced by pseudonymize(). Adds 3 eraseSubject unit
tests to the existing payload-audit-log test file.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-11 16:22:35 +02:00
parent 846b4c2511
commit 18fddcc45f
2 changed files with 121 additions and 4 deletions

View File

@@ -1,6 +1,7 @@
import type { SanitizedConfig } from "payload";
import type { AuditEntry } from "@repo/core-shared/audit";
import type { IAuditLog } from "./audit-log.interface";
import { pseudonymize } from "./pseudonymize";
type GetPayload = (args: { config: SanitizedConfig }) => Promise<{
create: (args: { collection: string; data: Record<string, unknown> }) => Promise<unknown>;
@@ -66,8 +67,37 @@ export class PayloadAuditLog implements IAuditLog {
});
}
async eraseSubject(_actorId: string, _mode: "pseudonymize" | "delete"): Promise<void> {
// Implemented in Phase 3.
throw new Error("PayloadAuditLog.eraseSubject not yet implemented (Phase 3)");
async eraseSubject(actorId: string, mode: "pseudonymize" | "delete"): Promise<void> {
const payload = await this.getPayload({ config: this.config });
if (mode === "delete") {
await payload.delete({
collection: "audit-logs",
where: { actorId: { equals: actorId } },
overrideAccess: true,
});
return;
}
// mode === "pseudonymize"
// Fetch all matching docs. Limit is 10_000 — a subject with more than
// 10k audit entries will not have all entries pseudonymized in one call.
// This is an accepted v1 limitation; callers may loop if needed.
const { docs } = await payload.find({
collection: "audit-logs",
where: { actorId: { equals: actorId } },
limit: 10_000,
overrideAccess: true,
});
const pseudonym = pseudonymize(actorId);
for (const doc of docs) {
await payload.update({
collection: "audit-logs",
id: doc.id,
data: { actorId: pseudonym },
overrideAccess: true,
});
}
}
}