Basis: Certvia dev@a48c5fb als Fundament für Craftvia
Unveränderter Stand von certvia/dev (a48c5fb) plus Craftvia-Spezifikation und Brandbook unter docs/craftvia/. ISMS-Module werden im Folgecommit entfernt. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
// AP2 — Provisionierung & Sichtbarkeits-Flags (docs/UEBERGABE-framework-iso27001.md §2).
|
||||
//
|
||||
// Provisioniert je einen Mandanten als reines TISAX, reines ISO und Doppel-Framework
|
||||
// und prüft:
|
||||
// - TenantFramework-Zeilen (Primär zuerst),
|
||||
// - Import je Framework (Anforderungszahlen, keine Cross-Archivierung, Dokumente
|
||||
// nicht dupliziert),
|
||||
// - FLAG_FW_TISAX / FLAG_FW_ISO27001 NACH dem Import korrekt gesetzt,
|
||||
// - AL-/Schutzbedarf-Flags nur bei TISAX (reiner ISO-Mandant: nicht gesetzt).
|
||||
// Räumt die Test-Mandanten (inkl. Identitäten) vor und nach dem Lauf ab.
|
||||
//
|
||||
// Lauf: npx tsx scripts/test-framework-provision.ts
|
||||
|
||||
import "dotenv/config";
|
||||
import { join } from "node:path";
|
||||
import { prisma } from "../src/server/db";
|
||||
import { provisionTenant } from "../src/server/provision";
|
||||
|
||||
const SEED_DIR = join(process.cwd(), "seed", "isms-vorlagenpaket-v2");
|
||||
let failures = 0;
|
||||
const ok = (c: boolean, m: string) => { console.log(`${c ? "✓" : "✗ FEHLER"} ${m}`); if (!c) failures++; };
|
||||
|
||||
const T = {
|
||||
tisax: { slug: "ap2-test-tisax", email: "admin@ap2-test-tisax.example" },
|
||||
iso: { slug: "ap2-test-iso", email: "admin@ap2-test-iso.example" },
|
||||
dual: { slug: "ap2-test-dual", email: "admin@ap2-test-dual.example" },
|
||||
};
|
||||
const SLUGS = Object.values(T).map((t) => t.slug);
|
||||
const EMAILS = Object.values(T).map((t) => t.email);
|
||||
|
||||
async function cleanup() {
|
||||
const tenants = await prisma.tenant.findMany({ where: { slug: { in: SLUGS } }, select: { id: true } });
|
||||
const ids = tenants.map((t) => t.id);
|
||||
if (ids.length) {
|
||||
// FK-sichere Reihenfolge: Verknüpfungen → Kinder → Stamm.
|
||||
await prisma.userRole.deleteMany({ where: { user: { tenantId: { in: ids } } } });
|
||||
await prisma.rolePermission.deleteMany({ where: { role: { tenantId: { in: ids } } } });
|
||||
await prisma.user.deleteMany({ where: { tenantId: { in: ids } } });
|
||||
await prisma.role.deleteMany({ where: { tenantId: { in: ids } } });
|
||||
for (const model of [
|
||||
"policyRequirement", "policyVariable", "policyDocument", "policyBaselineParam",
|
||||
"policyEvidence", "policyPackageState", "tenantFramework", "tenantModule",
|
||||
"managedRegister", "auditLog",
|
||||
] as const) {
|
||||
// @ts-expect-error dynamischer Modellzugriff (alle tragen tenantId)
|
||||
await prisma[model].deleteMany({ where: { tenantId: { in: ids } } });
|
||||
}
|
||||
await prisma.tenantSettings.deleteMany({ where: { tenantId: { in: ids } } });
|
||||
await prisma.tenant.deleteMany({ where: { id: { in: ids } } });
|
||||
}
|
||||
await prisma.identity.deleteMany({ where: { email: { in: EMAILS } } });
|
||||
}
|
||||
|
||||
const flag = async (tenantId: string, key: string) =>
|
||||
(await prisma.policyVariable.findFirst({ where: { tenantId, key }, select: { value: true } }))?.value ?? null;
|
||||
const reqCount = (tenantId: string, framework: "TISAX" | "ISO_27001") =>
|
||||
prisma.policyRequirement.count({ where: { tenantId, framework, archivedAt: null } });
|
||||
|
||||
async function provision(slug: string, email: string, frameworks: ("TISAX" | "ISO_27001")[]) {
|
||||
return provisionTenant(prisma, {
|
||||
name: slug, slug, admin: { email, name: "AP2 Test", password: "Str0ng-Passw0rt!" },
|
||||
frameworks, seedPoliciesDir: SEED_DIR, actorId: null,
|
||||
});
|
||||
}
|
||||
|
||||
async function main() {
|
||||
await cleanup();
|
||||
|
||||
// ── Reines TISAX ────────────────────────────────────────────────────────────
|
||||
const tisax = await provision(T.tisax.slug, T.tisax.email, ["TISAX"]);
|
||||
const tisaxFw = await prisma.tenantFramework.findMany({ where: { tenantId: tisax.id }, select: { framework: true, isPrimary: true } });
|
||||
ok(tisaxFw.length === 1 && tisaxFw[0].framework === "TISAX" && tisaxFw[0].isPrimary, "TISAX-Mandant: genau eine TenantFramework-Zeile (TISAX, primär)");
|
||||
ok((await reqCount(tisax.id, "TISAX")) === 321 && (await reqCount(tisax.id, "ISO_27001")) === 0, "TISAX-Mandant: 321 TISAX-, 0 ISO-Anforderungen");
|
||||
ok((await flag(tisax.id, "FLAG_FW_TISAX")) === "true" && (await flag(tisax.id, "FLAG_FW_ISO27001")) === "false", "TISAX-Mandant: FLAG_FW_TISAX=true, FLAG_FW_ISO27001=false");
|
||||
ok((await flag(tisax.id, "FLAG_HIGH_PROTECTION")) === "true", "TISAX-Mandant: AL-Flag FLAG_HIGH_PROTECTION gesetzt");
|
||||
|
||||
// ── Reines ISO ──────────────────────────────────────────────────────────────
|
||||
const iso = await provision(T.iso.slug, T.iso.email, ["ISO_27001"]);
|
||||
const isoFw = await prisma.tenantFramework.findMany({ where: { tenantId: iso.id }, select: { framework: true, isPrimary: true } });
|
||||
ok(isoFw.length === 1 && isoFw[0].framework === "ISO_27001" && isoFw[0].isPrimary, "ISO-Mandant: genau eine TenantFramework-Zeile (ISO, primär)");
|
||||
ok((await reqCount(iso.id, "ISO_27001")) === 120 && (await reqCount(iso.id, "TISAX")) === 0, "ISO-Mandant: 120 ISO-, 0 TISAX-Anforderungen");
|
||||
ok((await flag(iso.id, "FLAG_FW_ISO27001")) === "true" && (await flag(iso.id, "FLAG_FW_TISAX")) === "false", "ISO-Mandant: FLAG_FW_ISO27001=true, FLAG_FW_TISAX=false");
|
||||
ok((await flag(iso.id, "FLAG_HIGH_PROTECTION")) === "false", "ISO-Mandant: KEIN AL-Flag gesetzt (Default false, Übergabe §1.3)");
|
||||
const isoDocs = await prisma.policyDocument.count({ where: { tenantId: iso.id } });
|
||||
const isoDocCodes = (await prisma.policyDocument.findMany({ where: { tenantId: iso.id }, select: { code: true } })).map((d) => d.code);
|
||||
ok(isoDocs === new Set(isoDocCodes).size, "ISO-Mandant: Dokumente nicht dupliziert (eindeutige Codes)");
|
||||
|
||||
// ── Doppel-Framework ─────────────────────────────────────────────────────────
|
||||
const dual = await provision(T.dual.slug, T.dual.email, ["TISAX", "ISO_27001"]);
|
||||
const dualFw = await prisma.tenantFramework.findMany({ where: { tenantId: dual.id }, orderBy: { isPrimary: "desc" }, select: { framework: true, isPrimary: true } });
|
||||
ok(dualFw.length === 2 && dualFw[0].framework === "TISAX" && dualFw[0].isPrimary && !dualFw[1].isPrimary, "Doppel-Mandant: zwei Zeilen, TISAX primär");
|
||||
const dTisax = await reqCount(dual.id, "TISAX");
|
||||
const dIso = await reqCount(dual.id, "ISO_27001");
|
||||
ok(dTisax === 321 && dIso === 120, `Doppel-Mandant: 321 TISAX + 120 ISO koexistieren (${dTisax}+${dIso})`);
|
||||
ok((await flag(dual.id, "FLAG_FW_TISAX")) === "true" && (await flag(dual.id, "FLAG_FW_ISO27001")) === "true", "Doppel-Mandant: beide FLAG_FW_* = true");
|
||||
const dualDocs = await prisma.policyDocument.count({ where: { tenantId: dual.id } });
|
||||
const dualCodes = (await prisma.policyDocument.findMany({ where: { tenantId: dual.id }, select: { code: true } })).map((d) => d.code);
|
||||
ok(dualDocs === new Set(dualCodes).size, `Doppel-Mandant: Dokumente NICHT dupliziert (${dualDocs} eindeutige Codes)`);
|
||||
|
||||
await cleanup();
|
||||
console.log("\n✓ aufgeräumt (Test-Mandanten + Identitäten entfernt)");
|
||||
}
|
||||
|
||||
main()
|
||||
.then(() => { console.log(failures === 0 ? "\nAP2-Provisionierung grün." : `\n${failures} Prüfung(en) fehlgeschlagen.`); process.exit(failures === 0 ? 0 : 1); })
|
||||
.catch(async (e) => { console.error(e); await cleanup().catch(() => {}); process.exit(1); });
|
||||
Reference in New Issue
Block a user