Fundament: ISMS-Module entfernt; Craftvia-Rollen, Module, Navigation, i18n-Split
- ISMS-Routen, Actions, Server-/Lib-Code, Komponenten, Prisma-Modelle, Seeds, Importer, Skripte und ISMS-Tests entfernt (Fundament bleibt: Auth, Identity, MFA/WebAuthn, RBAC, Audit, Mail, Storage, Backup/DSGVO, Plattform-Admin) - Schema auf Fundament-Modelle reduziert; TenantSettings generisch (+phone/email) - TENANT_MODELS (db.ts, backup/topology.ts) und PII-Felder ausgedünnt - RBAC: Rollen tenant-admin/backoffice/team-lead/technician + Craftvia-Permissions - Modul-Katalog (customers, sites, teams, work_orders, imports, field, reports, emergency, documents, notifications, lotse) + Navigation aus src/lib/nav.ts - Modul-Routen mit requireModule-Layout und Platzhalterseite - Message-Katalog je Namespace (messages/<locale>/<namespace>.json), fs-Loader - check-module-guards: Modul-Key aus src/server/actions/<moduleKey>/ - Provisionierung, Admin-Konsole, Einstellungen, Files-Route, Mail entkoppelt - Seed minimal (demo/demo2, Nutzer je Rolle); Fundament-Tests auf Role/ NotificationPreference-Fixtures umgestellt Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
// F-13: Ein `role:manage`-Inhaber (z. B. tenant-admin) konnte über `createRole`/
|
||||
// `updateRolePermissions` eine Rolle mit BELIEBIGEN Katalog-Rechten bauen und sie sich
|
||||
// über `setUserRoles` selbst zuweisen — obwohl der tenant-admin bewusst NICHT über
|
||||
// `policy:approve`/`risk:accept`/`soa:write` verfügt. Der Fix begrenzt vergebbare Rechte
|
||||
// `report:approve`/`work_order:release_billing`/`customer:merge` verfügt. Der Fix begrenzt vergebbare Rechte
|
||||
// auf die eigene effektive Rechtemenge (autoritativ aus der DB) und unterbindet die
|
||||
// Selbstzuweisung höher privilegierter Rollen.
|
||||
//
|
||||
@@ -62,10 +62,10 @@ async function main() {
|
||||
|
||||
const tenant = await prisma.tenant.create({ data: { name: "F13 AuthZ Test", slug: SLUG } });
|
||||
|
||||
// tenant-admin-typische Rechte (bewusst OHNE policy:approve / risk:accept / soa:write).
|
||||
// tenant-admin-typische Rechte (bewusst OHNE report:approve / work_order:release_billing / customer:merge).
|
||||
const adminPermKeys = ["tenant:manage", "user:read", "user:manage", "role:manage", "report:read"];
|
||||
const adminPerms = await Promise.all(adminPermKeys.map(ensurePerm));
|
||||
const escalationTargets = await Promise.all(["policy:approve", "risk:accept", "soa:write"].map(ensurePerm));
|
||||
const escalationTargets = await Promise.all(["report:approve", "work_order:release_billing", "customer:merge"].map(ensurePerm));
|
||||
|
||||
const adminRole = await prisma.role.create({
|
||||
data: {
|
||||
@@ -84,10 +84,10 @@ async function main() {
|
||||
},
|
||||
});
|
||||
|
||||
// Höher privilegierte Rolle (ISB-artig), die der Actor sich NICHT selbst geben darf.
|
||||
// Höher privilegierte Rolle (Backoffice-artig), die der Actor sich NICHT selbst geben darf.
|
||||
const isbRole = await prisma.role.create({
|
||||
data: {
|
||||
tenantId: tenant.id, key: "isb", name: "ISB / CISO",
|
||||
tenantId: tenant.id, key: "backoffice", name: "Backoffice",
|
||||
rolePermissions: { create: escalationTargets.map((p) => ({ permissionId: p.id })) },
|
||||
},
|
||||
});
|
||||
@@ -96,29 +96,29 @@ async function main() {
|
||||
|
||||
// (1) Effektive Menge stimmt und enthält bewusst NICHT die kritischen Rechte.
|
||||
ok(effective.has("role:manage"), "(1) Actor hat role:manage");
|
||||
ok(!effective.has("policy:approve") && !effective.has("risk:accept") && !effective.has("soa:write"),
|
||||
"(1) Actor hat bewusst KEIN policy:approve/risk:accept/soa:write");
|
||||
ok(!effective.has("report:approve") && !effective.has("work_order:release_billing") && !effective.has("customer:merge"),
|
||||
"(1) Actor hat bewusst KEIN report:approve/work_order:release_billing/customer:merge");
|
||||
|
||||
// (2) createRole/updateRolePermissions: Delegation ist bewusst ERLAUBT (pragmatische
|
||||
// F-13-Variante) — ein Admin darf Rollen mit Rechten oberhalb seines Niveaus für ANDERE
|
||||
// anlegen (z. B. ISB klonen+bearbeiten). Die Escalation-Abwehr sitzt in der Selbst-
|
||||
// anlegen (z. B. Backoffice klonen+bearbeiten). Die Escalation-Abwehr sitzt in der Selbst-
|
||||
// zuweisung (4). Hier nur festhalten, dass die kritischen Rechte tatsächlich außerhalb
|
||||
// des eigenen Niveaus liegen (sonst wäre der Test bedeutungslos).
|
||||
ok(excessOf(["policy:approve", "risk:accept"], effective).length === 2,
|
||||
"(2) policy:approve/risk:accept liegen außerhalb des Actor-Niveaus (Delegation dennoch erlaubt)");
|
||||
ok(excessOf(["report:approve", "work_order:release_billing"], effective).length === 2,
|
||||
"(2) report:approve/work_order:release_billing liegen außerhalb des Actor-Niveaus (Delegation dennoch erlaubt)");
|
||||
|
||||
// (3) Die für ANDERE delegierbaren Rechte sind nicht künstlich beschnitten.
|
||||
ok(excessOf(["user:read", "report:read"], effective).length === 0,
|
||||
"(3) Vergabe eigener Rechte (user:read/report:read) ist ohnehin zulässig");
|
||||
|
||||
// (4) Selbstzuweisung der höher privilegierten ISB-Rolle → blockiert (Kern-Abwehr).
|
||||
// (4) Selbstzuweisung der höher privilegierten Backoffice-Rolle → blockiert (Kern-Abwehr).
|
||||
const isbPerms = new Set(
|
||||
(await prisma.rolePermission.findMany({ where: { roleId: isbRole.id }, select: { permission: { select: { key: true } } } }))
|
||||
.map((r) => r.permission.key),
|
||||
);
|
||||
const selfAssignExcess = [...isbPerms].filter((p) => !effective.has(p));
|
||||
ok(selfAssignExcess.length > 0,
|
||||
"(4) Selbstzuweisung der ISB-Rolle bringt Rechte oberhalb des eigenen Niveaus → blockiert");
|
||||
"(4) Selbstzuweisung der Backoffice-Rolle bringt Rechte oberhalb des eigenen Niveaus → blockiert");
|
||||
|
||||
// (5) Selbstzuweisung der eigenen (bereits gehaltenen) Admin-Rolle → erlaubt (⊆ effektiv).
|
||||
const adminRolePerms = new Set(
|
||||
|
||||
Reference in New Issue
Block a user