191 lines
5.9 KiB
JavaScript
191 lines
5.9 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import { randomUUID } from "node:crypto";
|
|
import { readFile, readdir } from "node:fs/promises";
|
|
import { PGlite } from "../.data/sql-verification/node_modules/@electric-sql/pglite/dist/index.js";
|
|
const db = new PGlite();
|
|
try {
|
|
await db.exec(`create role anon; create role authenticated; create role service_role;
|
|
create schema auth; create table auth.users(id uuid primary key);
|
|
create function auth.uid() returns uuid language sql as $$ select null::uuid $$;
|
|
create schema storage; create table storage.buckets(id text primary key,name text,public boolean,file_size_limit bigint);`);
|
|
for (const file of (await readdir("supabase/migrations"))
|
|
.filter((f) => f.endsWith(".sql"))
|
|
.sort())
|
|
await db.exec(await readFile(`supabase/migrations/${file}`, "utf8"));
|
|
const player = randomUUID(),
|
|
moderator = randomUUID(),
|
|
admin = randomUUID(),
|
|
missing = randomUUID();
|
|
for (const [id, role] of [
|
|
[player, "contributor"],
|
|
[moderator, "moderator"],
|
|
[admin, "admin"],
|
|
]) {
|
|
await db.query("insert into auth.users values($1)", [id]);
|
|
await db.query("insert into public.profiles(id,role) values($1,$2)", [
|
|
id,
|
|
role,
|
|
]);
|
|
}
|
|
const makeMap = async () => {
|
|
const { rows } = await db.query(
|
|
`insert into public.datasets(owner_id,name,source,bounds,state,panorama_count)
|
|
values($1,'Permissions fixture','plugin','{"minX":0,"minZ":0,"maxX":64,"maxZ":64}','review',5) returning id`,
|
|
[player],
|
|
);
|
|
const id = rows[0].id;
|
|
await db.query(
|
|
"insert into public.dataset_inputs(dataset_id,manifest,validated) values($1,$2,true)",
|
|
[
|
|
id,
|
|
JSON.stringify({
|
|
name: "Permissions fixture",
|
|
description: "Role verification",
|
|
viewpoints: Array.from({ length: 5 }, (_, i) => ({
|
|
id: `p${i}`,
|
|
assetId: `p${i}`,
|
|
})),
|
|
}),
|
|
],
|
|
);
|
|
return id;
|
|
};
|
|
const id = await makeMap();
|
|
const decide = (map, reviewer, decision, reason = null) =>
|
|
db.query("select public.publish_dataset($1,$2,$3,$4)", [
|
|
map,
|
|
reviewer,
|
|
decision,
|
|
reason,
|
|
]);
|
|
await assert.rejects(
|
|
decide(id, player, "approve"),
|
|
/Moderator permission required/,
|
|
);
|
|
await decide(id, moderator, "approve");
|
|
assert.equal(
|
|
(await db.query("select state from public.datasets where id=$1", [id]))
|
|
.rows[0].state,
|
|
"published",
|
|
);
|
|
await assert.rejects(
|
|
decide(id, moderator, "unpublish"),
|
|
/Admin permission required/,
|
|
);
|
|
await decide(id, admin, "unpublish");
|
|
assert.equal(
|
|
(await db.query("select state from public.datasets where id=$1", [id]))
|
|
.rows[0].state,
|
|
"unpublished",
|
|
);
|
|
const rejected = await makeMap();
|
|
await decide(
|
|
rejected,
|
|
moderator,
|
|
"reject",
|
|
"Please fix the missing terrain.",
|
|
);
|
|
assert.equal(
|
|
(
|
|
await db.query("select state from public.datasets where id=$1", [
|
|
rejected,
|
|
])
|
|
).rows[0].state,
|
|
"rejected",
|
|
);
|
|
const updated = await makeMap();
|
|
await decide(updated, admin, "approve");
|
|
await db.query(
|
|
"update public.datasets set update_state='review' where id=$1",
|
|
[updated],
|
|
);
|
|
await decide(updated, moderator, "approve");
|
|
assert.equal(
|
|
(
|
|
await db.query("select update_state from public.datasets where id=$1", [
|
|
updated,
|
|
])
|
|
).rows[0].update_state,
|
|
null,
|
|
);
|
|
for (const actor of [player, moderator]) {
|
|
await assert.rejects(
|
|
db.query("select public.set_beta_badge($1,false)", [actor]),
|
|
/Admin permission required/,
|
|
);
|
|
await assert.rejects(
|
|
db.query("select public.set_user_prefix($1,$2,'Spoof','crown')", [
|
|
actor,
|
|
player,
|
|
]),
|
|
/Admin permission required/,
|
|
);
|
|
await assert.rejects(
|
|
db.query(
|
|
"select public.review_content_report($1,$2,'dismiss','Valid feedback here')",
|
|
[missing, actor],
|
|
),
|
|
/Admin permission required/,
|
|
);
|
|
await assert.rejects(
|
|
db.query(
|
|
"select public.review_map_appeal($1,$2,'approve','Valid feedback here')",
|
|
[missing, actor],
|
|
),
|
|
/Admin permission required/,
|
|
);
|
|
}
|
|
await db.query("select public.set_beta_badge($1,false)", [admin]);
|
|
assert.equal(
|
|
(await db.query("select beta_badge_enabled from public.site_settings"))
|
|
.rows[0].beta_badge_enabled,
|
|
false,
|
|
);
|
|
await db.query("select public.set_user_prefix($1,$2,'Builder','gem')", [
|
|
admin,
|
|
player,
|
|
]);
|
|
assert.equal(
|
|
(
|
|
await db.query("select custom_prefix from public.profiles where id=$1", [
|
|
player,
|
|
])
|
|
).rows[0].custom_prefix,
|
|
"Builder",
|
|
);
|
|
await db.exec(
|
|
"create role identity_probe bypassrls; grant select,update on public.profiles to identity_probe; grant usage on schema public to identity_probe; set role identity_probe",
|
|
);
|
|
await assert.rejects(
|
|
db.query("update public.profiles set role='admin' where id=$1", [player]),
|
|
/Moderator permission required/,
|
|
);
|
|
await db.exec("reset role");
|
|
for (const role of ["anon", "authenticated"]) {
|
|
await db.exec(`set role ${role}`);
|
|
for (const query of [
|
|
[
|
|
"select public.publish_dataset($1,$2,'unpublish',null)",
|
|
[updated, admin],
|
|
],
|
|
["select public.set_beta_badge($1,true)", [admin]],
|
|
["select public.set_user_prefix($1,$2,'Spoof','star')", [admin, player]],
|
|
[
|
|
"select public.review_content_report($1,$2,'dismiss','Valid feedback here')",
|
|
[missing, admin],
|
|
],
|
|
[
|
|
"select public.review_map_appeal($1,$2,'approve','Valid feedback here')",
|
|
[missing, admin],
|
|
],
|
|
])
|
|
await assert.rejects(db.query(...query), /permission denied/);
|
|
await db.exec("reset role");
|
|
}
|
|
console.log(
|
|
"Admin permissions SQL passed: moderators approve/reject maps and updates; administrative actions and role spoofing are blocked; admins retain full access.",
|
|
);
|
|
} finally {
|
|
await db.close();
|
|
}
|