import assert from "node:assert/strict"; import { readFileSync } from "node:fs"; import test from "node:test"; const migration = readFileSync( new URL("../supabase/migrations/20260805010000_reconcile_admin_redemption_audit.sql", import.meta.url), "utf8", ); const auth = readFileSync(new URL("../src/lib/admin/auth.ts", import.meta.url), "utf8"); const authPolicy = readFileSync(new URL("../src/lib/admin/auth-policy.ts", import.meta.url), "utf8"); const authFactory = readFileSync(new URL("../src/modules/identity/auth-factory.ts", import.meta.url), "utf8"); const adminHttp = readFileSync(new URL("../src/lib/admin/http.ts", import.meta.url), "utf8"); const rbacMigration = readFileSync(new URL("../supabase/migrations/20260806010000_admin_rbac.sql", import.meta.url), "utf8"); const bootstrapRoles = readFileSync(new URL("../../deploy/postgres/001-bootstrap-roles.sh", import.meta.url), "utf8"); const compatibilityRoles = readFileSync(new URL("../../deploy/postgres/002-ensure-business-compatibility-roles.sql", import.meta.url), "utf8"); const administratorsRoute = readFileSync(new URL("../src/app/api/admin/administrators/route.ts", import.meta.url), "utf8"); const reauthRoute = readFileSync(new URL("../src/app/api/admin/reauth/route.ts", import.meta.url), "utf8"); const mfaRoute = readFileSync(new URL("../src/app/api/admin/mfa/route.ts", import.meta.url), "utf8"); const mfaSecurity = readFileSync(new URL("../src/components/admin/mfa-security.tsx", import.meta.url), "utf8"); const reasonActionModal = readFileSync(new URL("../src/components/admin/reason-action-modal.tsx", import.meta.url), "utf8"); const customersRoute = readFileSync(new URL("../src/app/api/admin/customers/route.ts", import.meta.url), "utf8"); const adminUser = readFileSync(new URL("../src/lib/supabase/admin.ts", import.meta.url), "utf8"); const codesRoute = readFileSync(new URL("../src/app/api/admin/codes/route.ts", import.meta.url), "utf8"); const codeRoute = readFileSync(new URL("../src/app/api/admin/codes/[id]/route.ts", import.meta.url), "utf8"); const providers = readFileSync(new URL("../src/lib/admin/providers.ts", import.meta.url), "utf8"); const adminLayout = readFileSync(new URL("../src/app/admin/layout.tsx", import.meta.url), "utf8"); const adminApp = readFileSync(new URL("../src/components/admin/admin-app.tsx", import.meta.url), "utf8"); const adminRootRoute = readFileSync(new URL("../src/app/admin/route.ts", import.meta.url), "utf8"); const readonlyRoutes = ["customers", "credit-transactions", "consultations", "audit-logs"].map((resource) => readFileSync(new URL(`../src/app/api/admin/${resource}/route.ts`, import.meta.url), "utf8"), ); const packageJson = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8")); test("admin APIs use persisted Better Auth roles with admin-only boundaries", () => { assert.match(auth, /requireIdentityServerSession/); assert.match(auth, /getIdentityAuthServices\(\)\.user\.api/); assert.match(authPolicy, /permissions\.includes\(required\)/); assert.match(auth, /admin_permission_keys\(\$1\)/); assert.doesNotMatch(auth, /ADMIN_EMAILS|isAdminEmail/); assert.match(auth, /AUTH_PROVIDER\?\.trim\(\) !== "self-hosted"/); assert.match(codesRoute, /requireHighRiskAdminMutation\(\s*request,\s*"billing\.adjustments\.write",?\s*\)/); assert.match(codeRoute, /requireHighRiskAdminMutation\(\s*request,\s*"billing\.adjustments\.write",?\s*\)/g); }); test("self-hosted account entry uses the database permission graph", () => { const selfHostedBranch = adminUser.slice( adminUser.indexOf('process.env.AUTH_PROVIDER?.trim() === "self-hosted"'), adminUser.indexOf("if (isAdminEmail"), ); assert.match(selfHostedBranch, /queryAdminRows/); assert.match(selfHostedBranch, /admin_has_permission\(\$1, 'admin\.access'\)/); assert.doesNotMatch(selfHostedBranch, /role === "admin"|viewer|isAdminEmail|ADMIN_EMAILS/); assert.match(auth, /authorizeAdminAccess\([\s\S]*user,[\s\S]*session\.permissions,[\s\S]*permission/); }); test("admin navigation exposes separated RBAC and billing resources", () => { assert.match(adminApp, /name: "administrators", list: "\/admin\/administrators"/); assert.match(adminApp, /name: "customers", list: "\/admin\/customers"/); assert.match(adminApp, /name: "products", list: "\/admin\/products"/); assert.match(adminApp, /name: "subscriptions", list: "\/admin\/subscriptions"/); assert.match(adminApp, /name: "orders", list: "\/admin\/orders"/); assert.match(adminApp, /name: "security", list: "\/admin\/security"/); assert.match(adminApp, /CreditCardOutlined/); assert.match(adminApp, /ShoppingOutlined/); }); test("admin sider replaces logout with a collapsed-aware return-to-chat link", () => { assert.match(adminApp, /ThemedLayout Sider=\{AdminSider\}/); assert.match(adminApp, /ThemedSider/); assert.match(adminApp, /render=\{\(\{ items, collapsed \}\)/); assert.match(adminApp, /\{items\}/); assert.match(adminApp, /ArrowLeftOutlined/); assert.match(adminApp, /\{collapsed \? null : "返回对话"\}<\/Link>/); assert.doesNotMatch(adminApp, /logout\s*[,(}]|authProvider\.logout/); }); test("admin pages and root route are server-gated before rendering or redirecting", () => { assert.match(adminLayout, /await requireAdminSession\("read"\)/); assert.match(adminLayout, /error\.status === 401 \? "\/login" : "\/"/); assert.match(adminLayout, /redirect\(/); assert.match(adminRootRoute, /await requireAdminSession\("read"\)/); assert.match(adminRootRoute, /error\.status === 401 \? "\/login" : "\/"/); assert.match(adminRootRoute, /headers: \{ location: "\/admin\/codes" \}/); }); test("readonly resources cannot be mutated through Refine access control", () => { for (const resource of ["customers", "credit-transactions", "consultations", "audit-logs"]) { assert.match(providers, new RegExp(resource.includes("-") ? `"${resource}"` : `${resource}:`)); } assert.match(providers, /const resourcePermissions/); assert.match(providers, /permission && identity\.permissions\.includes\(permission\)/); for (const route of readonlyRoutes) { assert.match(route, /export const POST = readonlyAdminMutation/); assert.match(route, /export const PATCH = readonlyAdminMutation/); assert.match(route, /export const DELETE = readonlyAdminMutation/); } }); test("redemption code writes are atomic with append-only redacted audit", () => { assert.match(migration, /create table if not exists audit\.admin_audit_logs/); assert.match(migration, /admin_audit_logs_append_only/); assert.match(migration, /redemption_code\.create/); assert.match(migration, /redemption_code\.update/); assert.match(migration, /redemption_code\.revoke/); assert.match(migration, /before_value is null or not \(before_value \?\| array\['code', 'code_hash', 'token', 'secret', 'key'\]\)/); assert.match(migration, /insert into audit\.admin_audit_logs/); assert.match(migration, /redeemed codes are immutable/); assert.match(migration, /revoked codes are immutable/); assert.match(migration, /v_code\.revoked_at is not null/); assert.match(migration, /'revoked_code'/); assert.match(migration, /set local role service_role|profiles_admin_read/); assert.match(migration, /p_codes is null or jsonb_typeof\(p_codes\) is distinct from 'array'/); assert.match(migration, /admin_verified_actor_email/); }); test("plaintext code is returned only by create and never enters audit snapshots", () => { assert.match(codesRoute, /plainCodes\.map/); assert.match(codesRoute, /code,/); assert.doesNotMatch(codeRoute, /codeHash|code_hash|plainCodes/); const snapshot = migration.match(/create or replace function public\.admin_redemption_code_snapshot[\s\S]*?revoke all on function/); assert.ok(snapshot); assert.doesNotMatch(snapshot[0], /code_hash|'code'/); assert.match(snapshot[0], /'mask'/); }); test("Refine dependencies and same-origin admin data provider are present", () => { for (const dependency of ["@refinedev/core", "@refinedev/antd", "@refinedev/nextjs-router", "antd"]) { assert.ok(packageJson.dependencies[dependency], `${dependency} missing`); } assert.match(providers, /const apiBase = "\/api\/admin"/); assert.doesNotMatch(providers, /https?:\/\//); }); test("administrator writes require current-session MFA before scoped email OTP reauthentication", () => { assert.match(adminHttp, /isSameOriginAdminMutation/); assert.match(administratorsRoute, /requireHighRiskAdminMutation\(request, "admin\.users\.manage_roles"\)/); assert.match(adminHttp, /requireAdminMfaIfRequired\(request, session\)[\s\S]*verifyHighRiskAdminProof/); assert.match(adminHttp, /verifyHighRiskAdminProof/); assert.match(reauthRoute, /requireAdminMfaIfRequired\(request, session\)[\s\S]*sendVerificationOTP/); assert.match(reauthRoute, /sendVerificationOTP/); assert.match(reauthRoute, /verifyEmailOTP/); assert.match(reauthRoute, /httpOnly: true/); assert.match(authFactory, /twoFactor\(/); assert.match(authFactory, /schema: identityModelMapping\.twoFactor/); assert.doesNotMatch(authFactory, /skipVerificationOnEnable\s*:\s*true/); assert.match(mfaRoute, /enableTwoFactor/); assert.match(mfaRoute, /verifyTOTP/); assert.match(mfaRoute, /verifyBackupCode/); assert.match(mfaRoute, /generateBackupCodes/); assert.match(mfaRoute, /disableTwoFactor/); assert.match(mfaSecurity, /\/api\/admin\/mfa/); assert.match(reasonActionModal, /action: mfaFactor === "totp" \? "verify" : "recover"/); assert.match(reasonActionModal, /action: "request"/); assert.doesNotMatch(administratorsRoute, /ADMIN_MFA_CAPABLE|process\.env/); assert.doesNotMatch(reauthRoute, /ADMIN_MFA_CAPABLE|process\.env/); }); test("customer birth data uses a narrow permission, is masked by default, and audits sensitive reads", () => { assert.match(authPolicy, /"admin\.customers\.read"/); assert.match(authPolicy, /"admin\.customers\.birth_data\.read"/); assert.match(customersRoute, /requirePermission\("admin\.customers\.read"\)/); assert.doesNotMatch(customersRoute, /requirePermission\("billing\.orders\.read"\)/); assert.match(customersRoute, /admin_read_customer_birth_data/); assert.match(customersRoute, /revealCustomerBirthData/); assert.match(customersRoute, /birthDataMasked: true/); assert.match(rbacMigration, /'owner', 'admin\.customers\.birth_data\.read'/); for (const role of ["support", "operations", "auditor"]) { assert.doesNotMatch(rbacMigration, new RegExp(`'${role}', 'admin\\.customers\\.birth_data\\.read'`)); } assert.match(rbacMigration, /admin\.customer\.birth_data\.read/); assert.match(rbacMigration, /permission_used[\s\S]*admin\.customers\.birth_data\.read/); assert.match(rbacMigration, /revoke select \(birth_date, birth_time_status, birth_place_label\)/); }); test("admin runtime cannot assume service_role and keeps explicit RBAC grants", () => { assert.match(bootstrapRoles, /GRANT service_role TO service_runtime/); assert.match(bootstrapRoles, /REVOKE service_role FROM admin_runtime/); assert.doesNotMatch(bootstrapRoles, /GRANT service_role TO admin_runtime/); assert.match(compatibilityRoles, /grant service_role to service_runtime/); assert.match(compatibilityRoles, /revoke service_role from admin_runtime/); assert.doesNotMatch(compatibilityRoles, /grant service_role to admin_runtime/); assert.match(rbacMigration, /admin_runtime_service_role_membership_must_be_revoked_by_bootstrap/); assert.match(rbacMigration, /grant execute on function public\.admin_has_permission/); assert.match(rbacMigration, /public\.admin_read_customer_birth_data\(uuid, uuid\[\], text\)[\s\S]*to admin_runtime/); }); test("last Owner revocations are serialized by one transaction advisory lock", () => { assert.match(rbacMigration, /pg_advisory_xact_lock\(1096040772, 1\)[\s\S]*v_owner_count/); });