/** * RBAC catalog: granular permissions bundled into per-tenant roles. * See docs/SPEC.md §3. Permissions are global rows (no tenantId), * roles are created per tenant from ROLE_DEFS at tenant provisioning. */ export const PERMISSIONS = [ // Administration "tenant:manage", "user:read", "user:manage", // Assets & BIA (one module) "asset:read", "asset:write", "bia:read", "bia:write", // Risk "risk:read", "risk:write", "risk:accept", // Compliance "soa:read", "soa:write", // Measures / tasks "measure:read", "measure:write", // Policies "policy:read", "policy:write", "policy:approve", // Incidents "incident:report", "incident:manage", // Audits "audit:read", "audit:conduct", // Suppliers "supplier:read", "supplier:write", // Evidence / documents "evidence:read", "evidence:write", // Reporting / management review "report:read", "review:manage", // Chat "chat:use", ] as const; export type Permission = (typeof PERMISSIONS)[number]; /** Role blueprints instantiated for every tenant (key → name + permissions). */ export const ROLE_DEFS: Record< string, { name: string; permissions: readonly Permission[] } > = { "tenant-admin": { name: "Mandanten-Admin", permissions: ["tenant:manage", "user:read", "user:manage", "report:read", "chat:use"], }, isb: { name: "ISB / CISO", permissions: [ "user:read", "asset:read", "asset:write", "bia:read", "bia:write", "risk:read", "risk:write", "risk:accept", "soa:read", "soa:write", "measure:read", "measure:write", "policy:read", "policy:write", "policy:approve", "incident:report", "incident:manage", "audit:read", "supplier:read", "supplier:write", "evidence:read", "evidence:write", "report:read", "review:manage", "chat:use", ], }, auditor: { name: "Auditor", permissions: [ "asset:read", "bia:read", "risk:read", "soa:read", "measure:read", "policy:read", "audit:read", "audit:conduct", "supplier:read", "evidence:read", "report:read", ], }, owner: { name: "Asset-/Risk-Owner", permissions: [ "asset:read", "asset:write", "bia:read", "risk:read", "risk:write", "measure:read", "measure:write", "policy:read", "incident:report", "evidence:read", "evidence:write", "chat:use", ], }, user: { name: "Mitarbeiter", permissions: ["policy:read", "incident:report", "measure:read", "chat:use"], }, }; export class ForbiddenError extends Error { constructor(permission: Permission) { super(`Fehlende Berechtigung: ${permission}`); this.name = "ForbiddenError"; } } /** Server-side permission check — UI hiding is convenience, this is security. */ export function requirePermission( session: { user?: { permissions?: string[] } } | null, permission: Permission ): void { if (!session?.user?.permissions?.includes(permission)) { throw new ForbiddenError(permission); } } export function hasPermission( session: { user?: { permissions?: string[] } } | null, permission: Permission ): boolean { return session?.user?.permissions?.includes(permission) ?? false; }