Files
HiveGrid-Wireframes/03 Quellen und Referenzen/hivegrid-cloud/apps/web/app/utils/db/seed/localCredentials.server.test.ts
T

222 lines
10 KiB
TypeScript

import { describe, expect, it, vi } from "vitest";
import { LOCAL_ADMIN_EMAIL, LOCAL_ADMIN_PASSWORD, ensureLocalCredentials, type LocalCredentialsClient } from "./localCredentials.server";
import { assertLocalCredentialsEnvironment } from "./localCredentialsEnvironment.server";
const localEnv = {
LOCAL_DEV_CREDENTIALS: "1",
NODE_ENV: "development",
DATABASE_URL: "postgresql://local:local@127.0.0.1:55432/hivegrid_local",
ADMIN_APP_HOST: "admin.localhost:3000",
};
describe("assertLocalCredentialsEnvironment", () => {
it("accepts only the canonical local launcher with a loopback PostgreSQL database", () => {
expect(assertLocalCredentialsEnvironment(localEnv).hostname).toBe("127.0.0.1");
expect(assertLocalCredentialsEnvironment({ ...localEnv, DATABASE_URL: "postgresql://local:local@localhost:5432/hivegrid" }).hostname).toBe("localhost");
});
it.each([
[{ ...localEnv, LOCAL_DEV_CREDENTIALS: undefined }, "canonical local launcher"],
[{ ...localEnv, NODE_ENV: "production" }, "NODE_ENV=development"],
[{ ...localEnv, CI: "true" }, "disabled in CI"],
[{ ...localEnv, SENTRY_ENVIRONMENT: "staging" }, "deployed Sentry environments"],
[{ ...localEnv, SENTRY_ENVIRONMENT: "canary" }, "deployed Sentry environments"],
[{ ...localEnv, SENTRY_ENVIRONMENT: "preview" }, "deployed Sentry environments"],
[{ ...localEnv, SENTRY_ENVIRONMENT: "prod" }, "deployed Sentry environments"],
[{ ...localEnv, RLS_RUNTIME_MODE: "required" }, "RLS runtime mode"],
[{ ...localEnv, ADMIN_APP_HOST: undefined }, "admin.localhost"],
[{ ...localEnv, ADMIN_APP_HOST: "admin.hivegrid.de" }, "admin.localhost"],
[{ ...localEnv, ADMIN_APP_HOST: "admin.localhost.evil" }, "admin.localhost"],
[{ ...localEnv, DATABASE_URL: "postgresql://app:secret@staging-db:5432/hivegrid" }, "loopback PostgreSQL"],
[{ ...localEnv, DATABASE_URL: "not-a-url" }, "valid DATABASE_URL"],
[{ ...localEnv, DATABASE_URL: "postgresql://local:local@localhost:5432/hivegrid?host=remote-db" }, "connection query parameters"],
[{ ...localEnv, DATABASE_URL: "postgresql://local:local@localhost:5432/hivegrid?HOSTADDR=203.0.113.10" }, "connection query parameters"],
[{ ...localEnv, DATABASE_URL: "postgresql://local:local@localhost:5432/hivegrid?host=%2Fvar%2Frun%2Fpostgresql" }, "connection query parameters"],
[{ ...localEnv, DATABASE_URL: "postgresql://local:local@localhost:5432/hivegrid?SeRvIcE=production" }, "connection query parameters"],
])("rejects unsafe environment %#", (env, message) => {
expect(() => assertLocalCredentialsEnvironment(env)).toThrow(message);
});
});
describe("ensureLocalCredentials", () => {
it("upserts the canonical platform admin and normalizes every password-backed local identity", async () => {
const transaction = {
user: {
upsert: vi.fn().mockResolvedValue({ id: "admin-user" }),
updateMany: vi.fn().mockResolvedValue({ count: 51 }),
},
portalUser: {
updateMany: vi.fn().mockResolvedValue({ count: 3 }),
},
adminUser: {
updateMany: vi.fn().mockResolvedValue({ count: 2 }),
upsert: vi.fn().mockResolvedValue({ id: "admin-user" }),
},
adminRole: {
findUnique: vi.fn().mockResolvedValue({ id: "super-admin-role" }),
},
adminUserRole: {
deleteMany: vi.fn().mockResolvedValue({ count: 2 }),
create: vi.fn().mockResolvedValue({ id: "admin-user-role" }),
},
};
const client = {
$queryRaw: vi.fn().mockResolvedValue([{ marked: true }]),
$transaction: vi.fn(async (callback) => callback(transaction as never)),
} as unknown as LocalCredentialsClient;
const hashPassword = vi.fn().mockResolvedValue("argon2-hash");
const syncPermissions = vi.fn().mockResolvedValue(undefined);
const syncAdminPermissions = vi.fn().mockResolvedValue(undefined);
await expect(ensureLocalCredentials({ client, env: localEnv, hashPassword, syncPermissions, syncAdminPermissions })).resolves.toEqual({
adminUserId: "admin-user",
normalizedPasswordIdentityCount: 54,
});
expect(client.$queryRaw).toHaveBeenCalledOnce();
expect(hashPassword).toHaveBeenCalledWith(LOCAL_ADMIN_PASSWORD);
expect(syncPermissions).toHaveBeenCalledOnce();
expect(syncAdminPermissions).toHaveBeenCalledOnce();
expect(transaction.user.upsert).toHaveBeenCalledWith(
expect.objectContaining({
where: { email: LOCAL_ADMIN_EMAIL },
create: expect.objectContaining({ email: LOCAL_ADMIN_EMAIL, passwordHash: "argon2-hash", active: true }),
update: expect.objectContaining({ passwordHash: "argon2-hash", active: true, totpEnabledAt: null }),
})
);
expect(transaction.adminUser.updateMany).toHaveBeenCalledWith({
where: { email: { not: LOCAL_ADMIN_EMAIL }, status: "active" },
data: expect.objectContaining({ status: "disabled" }),
});
expect(transaction.adminUser.upsert).toHaveBeenCalledWith({
where: { email: LOCAL_ADMIN_EMAIL },
create: expect.objectContaining({ id: "admin-user", email: LOCAL_ADMIN_EMAIL, status: "active" }),
update: expect.objectContaining({ status: "active", disabledAt: null, disabledReason: null }),
select: { id: true },
});
expect(transaction.adminRole.findUnique).toHaveBeenCalledWith({
where: { systemKey: "super_admin" },
select: { id: true },
});
expect(transaction.adminUserRole.deleteMany).toHaveBeenCalledWith({
where: { adminUserId: "admin-user" },
});
expect(transaction.adminUserRole.create).toHaveBeenCalledWith({
data: { adminUserId: "admin-user", adminRoleId: "super-admin-role", grantedByAdminUserId: "admin-user" },
});
expect(transaction.user.updateMany).toHaveBeenCalledWith({
where: { passwordHash: { not: null } },
data: { passwordHash: "argon2-hash" },
});
expect(transaction.portalUser.updateMany).toHaveBeenCalledWith({
where: { passwordHash: { not: null } },
data: { passwordHash: "argon2-hash" },
});
});
it("fails before hashing or opening a transaction outside the local contract", async () => {
const client = { $queryRaw: vi.fn(), $transaction: vi.fn() } as unknown as LocalCredentialsClient;
const hashPassword = vi.fn();
const syncPermissions = vi.fn();
await expect(
ensureLocalCredentials({
client,
env: { ...localEnv, DATABASE_URL: "postgresql://app:secret@staging-db:5432/hivegrid" },
hashPassword,
syncPermissions,
})
).rejects.toThrow("loopback PostgreSQL");
expect(hashPassword).not.toHaveBeenCalled();
expect(syncPermissions).not.toHaveBeenCalled();
expect(client.$queryRaw).not.toHaveBeenCalled();
expect(client.$transaction).not.toHaveBeenCalled();
});
it("fails before hashing, permission sync, or writes when the database lacks the disposable-local marker", async () => {
const client = {
$queryRaw: vi.fn().mockResolvedValue([{ marked: false }]),
$transaction: vi.fn(),
} as unknown as LocalCredentialsClient;
const hashPassword = vi.fn();
const syncPermissions = vi.fn();
await expect(ensureLocalCredentials({ client, env: localEnv, hashPassword, syncPermissions })).rejects.toThrow(
"database marked as disposable local development data"
);
expect(hashPassword).not.toHaveBeenCalled();
expect(syncPermissions).not.toHaveBeenCalled();
expect(client.$transaction).not.toHaveBeenCalled();
});
it("fails closed when the canonical admin role is unavailable after permission sync", async () => {
const transaction = {
user: {
upsert: vi.fn().mockResolvedValue({ id: "admin-user" }),
updateMany: vi.fn(),
},
portalUser: { updateMany: vi.fn() },
adminUser: { updateMany: vi.fn(), upsert: vi.fn().mockResolvedValue({ id: "admin-user" }) },
adminRole: { findUnique: vi.fn().mockResolvedValue(null) },
adminUserRole: { deleteMany: vi.fn(), create: vi.fn() },
};
const client = {
$queryRaw: vi.fn().mockResolvedValue([{ marked: true }]),
$transaction: vi.fn(async (callback) => callback(transaction as never)),
} as unknown as LocalCredentialsClient;
await expect(
ensureLocalCredentials({
client,
env: localEnv,
hashPassword: vi.fn().mockResolvedValue("argon2-hash"),
syncPermissions: vi.fn().mockResolvedValue(undefined),
syncAdminPermissions: vi.fn().mockResolvedValue(undefined),
})
).rejects.toThrow("canonical SuperAdmin role");
expect(transaction.adminUserRole.deleteMany).not.toHaveBeenCalled();
expect(transaction.adminUserRole.create).not.toHaveBeenCalled();
expect(transaction.user.updateMany).not.toHaveBeenCalled();
expect(transaction.portalUser.updateMany).not.toHaveBeenCalled();
});
it("keeps exactly one canonical SuperAdmin assignment across repeated local starts", async () => {
const transaction = {
user: {
upsert: vi.fn().mockResolvedValue({ id: "admin-user" }),
updateMany: vi.fn().mockResolvedValue({ count: 1 }),
},
portalUser: { updateMany: vi.fn().mockResolvedValue({ count: 0 }) },
adminUser: {
updateMany: vi.fn().mockResolvedValue({ count: 0 }),
upsert: vi.fn().mockResolvedValue({ id: "admin-user" }),
},
adminRole: { findUnique: vi.fn().mockResolvedValue({ id: "super-admin-role" }) },
adminUserRole: {
deleteMany: vi.fn().mockResolvedValue({ count: 1 }),
create: vi.fn().mockResolvedValue({ id: "admin-user-role" }),
},
};
const client = {
$queryRaw: vi.fn().mockResolvedValue([{ marked: true }]),
$transaction: vi.fn(async (callback) => callback(transaction as never)),
} as unknown as LocalCredentialsClient;
const options = {
client,
env: localEnv,
hashPassword: vi.fn().mockResolvedValue("argon2-hash"),
syncPermissions: vi.fn().mockResolvedValue(undefined),
syncAdminPermissions: vi.fn().mockResolvedValue(undefined),
};
await ensureLocalCredentials(options);
await ensureLocalCredentials(options);
expect(transaction.adminUserRole.deleteMany).toHaveBeenCalledTimes(2);
expect(transaction.adminUserRole.create).toHaveBeenCalledTimes(2);
expect(transaction.adminUserRole.create).toHaveBeenLastCalledWith({
data: { adminUserId: "admin-user", adminRoleId: "super-admin-role", grantedByAdminUserId: "admin-user" },
});
});
});