Iteration 1: Auth (lokale Accounts), RBAC, RLS-Policies, i18n
- Auth.js v5 mit Credentials-Provider: Argon2id-Verifikation, JWT-Session mit Mandanten-Kontext, Rollen und Permissions; Login-/Logout-Flow (deutsch) - RBAC-Katalog (28 Permissions, 5 Rollen-Blueprints) mit serverseitigem requirePermission; Seed legt Demo-Mandant und 4 Demo-Nutzer an - Route-Gate über Next-16-proxy.ts (UX-Ebene), autoritative Prüfung serverseitig via requireSession/requirePermission - Prisma-Migrationen: init + Row-Level-Security-Policies (zweite Verteidigungslinie, Scharfschaltung in Härtungs-Iteration dokumentiert) - i18n-Gerüst mit next-intl (de aktiv, en vorbereitet), Audit-Log-Helper - Login/Logout end-to-end im Browser verifiziert; Build/Lint/Typecheck grün Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
import "dotenv/config";
|
||||
import { PrismaClient } from "@prisma/client";
|
||||
import { PrismaPg } from "@prisma/adapter-pg";
|
||||
import { hash } from "@node-rs/argon2";
|
||||
import { PERMISSIONS, ROLE_DEFS } from "../src/server/rbac";
|
||||
|
||||
/**
|
||||
* Seed: global permission catalog + demo tenant with roles and users.
|
||||
* Idempotent (upserts) — safe to run repeatedly.
|
||||
*/
|
||||
|
||||
const prisma = new PrismaClient({
|
||||
adapter: new PrismaPg({ connectionString: process.env.DATABASE_URL }),
|
||||
});
|
||||
|
||||
const DEMO_PASSWORD = "Demo1234!";
|
||||
|
||||
async function main() {
|
||||
// 1. Global permission catalog
|
||||
for (const key of PERMISSIONS) {
|
||||
await prisma.permission.upsert({ where: { key }, update: {}, create: { key } });
|
||||
}
|
||||
console.log(`✔ ${PERMISSIONS.length} Permissions`);
|
||||
|
||||
// 2. Demo tenant
|
||||
const tenant = await prisma.tenant.upsert({
|
||||
where: { slug: "demo" },
|
||||
update: {},
|
||||
create: { name: "Demo GmbH", slug: "demo" },
|
||||
});
|
||||
|
||||
// 3. Roles from blueprints, with permission bundles
|
||||
for (const [key, def] of Object.entries(ROLE_DEFS)) {
|
||||
const role = await prisma.role.upsert({
|
||||
where: { tenantId_key: { tenantId: tenant.id, key } },
|
||||
update: { name: def.name },
|
||||
create: { tenantId: tenant.id, key, name: def.name },
|
||||
});
|
||||
const perms = await prisma.permission.findMany({
|
||||
where: { key: { in: [...def.permissions] } },
|
||||
});
|
||||
for (const p of perms) {
|
||||
await prisma.rolePermission.upsert({
|
||||
where: { roleId_permissionId: { roleId: role.id, permissionId: p.id } },
|
||||
update: {},
|
||||
create: { roleId: role.id, permissionId: p.id },
|
||||
});
|
||||
}
|
||||
}
|
||||
console.log(`✔ Mandant "demo" mit ${Object.keys(ROLE_DEFS).length} Rollen`);
|
||||
|
||||
// 4. Demo users
|
||||
const passwordHash = await hash(DEMO_PASSWORD);
|
||||
const demoUsers: { email: string; name: string; roles: string[] }[] = [
|
||||
{ email: "admin@demo.example", name: "Anna Admin", roles: ["tenant-admin", "isb"] },
|
||||
{ email: "auditor@demo.example", name: "Axel Auditor", roles: ["auditor"] },
|
||||
{ email: "owner@demo.example", name: "Oskar Owner", roles: ["owner"] },
|
||||
{ email: "user@demo.example", name: "Ulla User", roles: ["user"] },
|
||||
];
|
||||
|
||||
for (const u of demoUsers) {
|
||||
const user = await prisma.user.upsert({
|
||||
where: { tenantId_email: { tenantId: tenant.id, email: u.email } },
|
||||
update: {},
|
||||
create: {
|
||||
tenantId: tenant.id,
|
||||
email: u.email,
|
||||
name: u.name,
|
||||
passwordHash,
|
||||
},
|
||||
});
|
||||
const roles = await prisma.role.findMany({
|
||||
where: { tenantId: tenant.id, key: { in: u.roles } },
|
||||
});
|
||||
for (const r of roles) {
|
||||
await prisma.userRole.upsert({
|
||||
where: { userId_roleId: { userId: user.id, roleId: r.id } },
|
||||
update: {},
|
||||
create: { userId: user.id, roleId: r.id },
|
||||
});
|
||||
}
|
||||
}
|
||||
console.log(`✔ ${demoUsers.length} Demo-Nutzer (Passwort: ${DEMO_PASSWORD})`);
|
||||
}
|
||||
|
||||
main()
|
||||
.then(() => prisma.$disconnect())
|
||||
.catch(async (e) => {
|
||||
console.error(e);
|
||||
await prisma.$disconnect();
|
||||
process.exit(1);
|
||||
});
|
||||
Reference in New Issue
Block a user