import assert from "node:assert/strict"; import { randomUUID, randomBytes } from "node:crypto"; import { createClient } from "@supabase/supabase-js"; process.loadEnvFile(".env.local"); const origin = process.argv[2] || "https://minegeo.site"; const db = createClient( process.env.NEXT_PUBLIC_SUPABASE_URL, process.env.SUPABASE_SECRET_KEY, { auth: { persistSession: false, autoRefreshToken: false } }, ); const accounts = [], maps = []; const checked = (r) => { if (r.error) throw new Error(r.error.message); return r.data; }; async function request( account, path, body, method = body === undefined ? "GET" : "POST", status = 200, ) { const response = await fetch(`${origin}/api/v1/${path}`, { method, headers: { origin, "content-type": "application/json", ...(account?.cookie ? { cookie: account.cookie } : {}), }, ...(body === undefined ? {} : { body: JSON.stringify(body) }), signal: AbortSignal.timeout(60000), }); const data = await response.json(); assert.equal(response.status, status, `${path}: ${JSON.stringify(data)}`); return data; } async function account(role) { const email = `admin-permissions-${randomUUID()}@example.com`, password = randomBytes(24).toString("base64url"); const { user } = checked( await db.auth.admin.createUser({ email, password, email_confirm: true, user_metadata: { display_name: `Permissions ${role} fixture` }, }), ); const fixture = { id: user.id, cookie: "" }; accounts.push(fixture); checked( await db.from("profiles").insert({ id: user.id, role, display_name: `Permissions ${role} fixture`, }), ); const response = await fetch(`${origin}/api/v1/auth`, { method: "POST", headers: { origin, "content-type": "application/json" }, body: JSON.stringify({ action: "login", email, password, next: "/moderation", }), }); assert.equal(response.status, 200, "Fixture login failed"); fixture.cookie = response.headers .getSetCookie() .map((value) => value.split(";")[0]) .join("; "); assert.ok(fixture.cookie, "Login did not set a session cookie"); return fixture; } try { const player = await account("contributor"), moderator = await account("moderator"), admin = await account("admin"), missing = randomUUID(); const bounds = { minX: 0, minZ: 0, maxX: 64, maxZ: 64 }; for (const state of ["review", "unpublished"]) { const map = checked( await db .from("datasets") .insert({ owner_id: player.id, name: `Permissions ${state} fixture`, source: "plugin", bounds, state, panorama_count: 5, }) .select("id") .single(), ); maps.push(map.id); checked( await db.from("dataset_inputs").insert({ dataset_id: map.id, validated: true, manifest: { format: "minegeo-dataset/v1", minecraftVersion: "1.21.11", dimension: "minecraft:overworld", source: "plugin", name: "Permissions map fixture", description: "Disposable permission verification", serverName: "Fixture server", north: "-z", bounds, viewpoints: Array.from({ length: 5 }, (_, i) => ({ id: `p${i}`, x: i * 4, y: 64, z: i * 4, })), assets: [ { path: "terrain.json.gz", sha256: "a".repeat(64), bytes: 1 }, ], }, }), ); } await request(player, "moderation", undefined, "GET", 403); const queue = await request(moderator, "moderation"); assert.equal(queue.role, "moderator"); assert.deepEqual(queue.reports, []); assert.deepEqual(queue.appeals, []); assert.ok( queue.maps.every( (m) => m.state === "review" || (m.state === "published" && m.update_state === "review"), ), ); assert.ok(queue.maps.some((m) => m.id === maps[0])); assert.ok(!queue.maps.some((m) => m.id === maps[1])); assert.ok( (await request(moderator, `moderation/${maps[0]}/panoramas`)).locations .length === 5, ); await request( moderator, `moderation/${maps[1]}/panoramas`, undefined, "GET", 403, ); for (const actor of [player, moderator]) { for (const [path, body, method] of [ ["moderation/users", undefined, "GET"], [ `moderation/users/${player.id}`, { prefix: "Spoof", icon: "star" }, "PATCH", ], ["moderation/settings", { betaBadgeEnabled: false }, "PATCH"], [ `moderation/appeals/${missing}`, { decision: "approve", reason: "A valid reason here." }, "POST", ], [`moderation/reports/${missing}/panoramas`, undefined, "GET"], [ `reports/${missing}`, { decision: "confirm", reason: "A valid reason here." }, "PATCH", ], ["moderation", { datasetId: maps[0], decision: "unpublish" }, "POST"], ]) await request(actor, path, body, method, 403); } await request(moderator, "moderation", { datasetId: maps[0], decision: "reject", reason: "Returned by the permission verification fixture.", }); assert.equal( checked( await db.from("datasets").select("state").eq("id", maps[0]).single(), ).state, "rejected", ); const full = await request(admin, "moderation"); assert.equal(full.role, "admin"); assert.ok(full.maps.some((m) => m.id === maps[1])); assert.equal( (await request(admin, `moderation/users?q=${player.id}`)).users[0].id, player.id, ); await request( admin, `moderation/users/${player.id}`, { prefix: "Builder", icon: "gem" }, "PATCH", ); assert.equal( (await request(player, "profile")).profile.custom_prefix, "Builder", ); await request( admin, `moderation/users/${player.id}`, { prefix: null, icon: "star" }, "PATCH", ); await request( admin, "moderation/settings", { betaBadgeEnabled: "invalid" }, "PATCH", 400, ); await request( admin, `reports/${missing}`, { decision: "dismiss", reason: "A valid reason here." }, "PATCH", 400, ); await request( admin, `moderation/appeals/${missing}`, { decision: "approve", reason: "A valid reason here." }, "POST", 400, ); for (const actor of [player, moderator, admin]) await request(actor, "profile", { role: "admin" }, "PATCH", 400); assert.equal((await request(admin, "profile")).profile.role, "admin"); console.log( "Production permissions passed: signed-in players denied, moderator queue restricted, pending panoramas and feedback work, admin controls available, role spoofing denied.", ); } finally { if (maps.length) checked(await db.from("review_decisions").delete().in("dataset_id", maps)); for (const id of maps) checked(await db.from("datasets").delete().eq("id", id)); for (const fixture of accounts) { if (fixture.cookie) await request(fixture, "auth/logout", {}).catch(() => {}); checked(await db.from("profiles").delete().eq("id", fixture.id)); checked(await db.auth.admin.deleteUser(fixture.id)); } console.log("Disposable permission fixtures removed."); }