diff --git a/app/panel/reports/[id]/page.tsx b/app/panel/reports/[id]/page.tsx index c88575c..3aeae2e 100644 --- a/app/panel/reports/[id]/page.tsx +++ b/app/panel/reports/[id]/page.tsx @@ -8,8 +8,8 @@ import { NavigationToolbar } from "@/components/common/NavigationToolbar"; import { ReportActions } from "@/components/pages/inspector/ReportActions"; import { Separator } from "@/components/ui/separator"; import { + fetchRelatedReports, fetchReportById, - fetchReports, fetchSnapshotsByReport, fetchUserById, } from "@/lib/db"; @@ -28,13 +28,9 @@ export default async function Reports({ params }: { params: { id: string } }) { snapshots .map((snapshot) => snapshot._id) .map((contentId) => - fetchReports({ - _id: { - $ne: report._id, - }, - status: "Created", - "content.id": contentId, - }) + fetchRelatedReports(contentId).then((reports) => + reports.filter((entry) => entry._id !== report._id) + ) ) ); diff --git a/app/panel/reports/page.tsx b/app/panel/reports/page.tsx index 8ca5f55..6901f3f 100644 --- a/app/panel/reports/page.tsx +++ b/app/panel/reports/page.tsx @@ -1,10 +1,10 @@ import { ReportCard } from "@/components/cards/ReportCard"; import { CardLink } from "@/components/common/CardLink"; import { Input } from "@/components/ui/input"; -import { fetchReports } from "@/lib/db"; +import { fetchOpenReports } from "@/lib/db"; export default async function Reports() { - const reports = (await fetchReports()) + const reports = (await fetchOpenReports()) .reverse() .sort((b, _) => (b.content.report_reason.includes("Illegal") ? -1 : 0)); diff --git a/lib/accessPermissions.ts b/lib/accessPermissions.ts new file mode 100644 index 0000000..e954839 --- /dev/null +++ b/lib/accessPermissions.ts @@ -0,0 +1,53 @@ +import { getServerSession } from "next-auth"; + +type Permission = + | "authifier" + | `accounts${"" | `/fetch${"" | "/by-id"}`}` + | `bots${"" | `/fetch${"" | "/by-id" | "/by-user"}`}` + | `channels${"" | `/fetch${"" | "/by-id" | "/dm"}` | `/create${"" | "/dm"}`}` + | `messages${"" | `/fetch${"" | "/by-id"}`}` + | `reports${ + | "" + | `/fetch${ + | "" + | "/by-id" + | "/open" + | `/related${"" | "/by-content" | "/by-user"}` + | `/snapshots${"" | "/by-report" | "/by-user"}`}`}` + | `sessions${"" | `/fetch${"" | "/by-account-id"}`}` + | `servers${"" | `/fetch${"" | "/by-id"}`}` + | `users${"" | `/fetch${"" | "/by-id" | "/memberships"}`}`; + +const ACL: Record> = { + "insert@revolt.chat": new Set([ + "users/fetch/by-id", + "reports/fetch/open", + "reports/fetch/by-id", + "reports/fetch/related", + "reports/fetch/snapshots/by-report", + ] as Permission[]), +}; + +function hasPermission(email: string, permission: Permission) { + const segments = permission.split("/"); + while (segments.length) { + if (ACL[email].has(segments.join("/") as Permission)) { + return true; + } + + segments.pop(); + } + + return false; +} + +export async function hasPermissionFromSession(permission: Permission) { + const session = await getServerSession(); + if (!session?.user?.email) throw "Not authenticated."; + return hasPermission(session.user.email, permission); +} + +export async function checkPermission(permission: Permission) { + if (!(await hasPermissionFromSession(permission))) + throw `Missing permission ${permission}`; +} diff --git a/lib/actions.ts b/lib/actions.ts index af90b37..b475a4c 100644 --- a/lib/actions.ts +++ b/lib/actions.ts @@ -10,7 +10,6 @@ import mongo, { fetchMessages, fetchUserById, findDM, - updateLastMessageId, } from "./db"; import { publishMessage, sendChatMessage } from "./redis"; import { ulid } from "ulid"; @@ -34,7 +33,6 @@ export async function sendAlert(userId: string, content: string) { let dm = await findDM(PLATFORM_MOD_ID, userId); if (!dm) dm = await createDM(PLATFORM_MOD_ID, userId, messageId); - else await updateLastMessageId(dm._id, messageId); await sendChatMessage({ _id: messageId, diff --git a/lib/db.ts b/lib/db.ts index ad6ae56..6f201ec 100644 --- a/lib/db.ts +++ b/lib/db.ts @@ -14,6 +14,7 @@ import type { } from "revolt-api"; import { ulid } from "ulid"; import { publishMessage } from "./redis"; +import { checkPermission } from "./accessPermissions"; let client: MongoClient; @@ -28,7 +29,7 @@ function mongo() { export default mongo; export async function fetchBotById(id: string) { - if (Math.random() > -1) throw "TODO: ACL"; + await checkPermission("bots/fetch/by-id"); return await mongo() .db("revolt") @@ -78,7 +79,7 @@ export type Account = { }; export async function fetchAccountById(id: string) { - if (Math.random() > -1) throw "TODO: ACL"; + await checkPermission("accounts/fetch/by-id"); return await mongo() .db("revolt") @@ -95,7 +96,7 @@ export async function fetchAccountById(id: string) { } export async function fetchSessionsByAccount(accountId: string) { - if (Math.random() > -1) throw "TODO: ACL"; + await checkPermission("sessions/fetch/by-account-id"); return await mongo() .db("revolt") @@ -115,7 +116,7 @@ export async function fetchSessionsByAccount(accountId: string) { } export async function fetchUserById(id: string) { - if (Math.random() > -1) throw "TODO: ACL"; + await checkPermission("users/fetch/by-id"); return await mongo() .db("revolt") @@ -124,7 +125,7 @@ export async function fetchUserById(id: string) { } export async function fetchUsersById(ids: string[]) { - if (Math.random() > -1) throw "TODO: ACL"; + await checkPermission("users/fetch/by-id"); return await mongo() .db("revolt") @@ -134,7 +135,7 @@ export async function fetchUsersById(ids: string[]) { } export async function fetchChannelById(id: string) { - if (Math.random() > -1) throw "TODO: ACL"; + await checkPermission("channels/fetch/by-id"); return await mongo() .db("revolt") @@ -143,7 +144,7 @@ export async function fetchChannelById(id: string) { } export async function fetchChannels(query: Filter) { - if (Math.random() > -1) throw "TODO: ACL"; + await checkPermission("channels/fetch"); return await mongo() .db("revolt") @@ -152,20 +153,8 @@ export async function fetchChannels(query: Filter) { .toArray(); } -export async function updateLastMessageId( - channelId: string, - messageId: string -) { - if (Math.random() > -1) throw "TODO: ACL"; - - return await mongo() - .db("revolt") - .collection("channels") - .updateOne({ _id: channelId }, { $set: { last_message_id: messageId } }); -} - export async function findDM(user_a: string, user_b: string) { - if (Math.random() > -1) throw "TODO: ACL"; + await checkPermission("channels/fetch/dm"); return await mongo() .db("revolt") @@ -183,7 +172,7 @@ export async function createDM( userB: string, lastMessageId?: string ) { - if (Math.random() > -1) throw "TODO: ACL"; + await checkPermission("channels/create/dm"); const newChannel: Channel & { channel_type: "DirectMessage" } = { _id: ulid(), @@ -211,7 +200,7 @@ export async function createDM( } export async function fetchServerById(id: string) { - if (Math.random() > -1) throw "TODO: ACL"; + await checkPermission("servers/fetch/by-id"); return await mongo() .db("revolt") @@ -220,7 +209,7 @@ export async function fetchServerById(id: string) { } export async function fetchServers(query: Filter) { - if (Math.random() > -1) throw "TODO: ACL"; + await checkPermission("servers/fetch"); return await mongo() .db("revolt") @@ -230,7 +219,7 @@ export async function fetchServers(query: Filter) { } export async function fetchMessageById(id: string) { - if (Math.random() > -1) throw "TODO: ACL"; + await checkPermission("messages/fetch/by-id"); return await mongo() .db("revolt") @@ -239,7 +228,7 @@ export async function fetchMessageById(id: string) { } export async function fetchMessages(query: Filter, limit = 50) { - if (Math.random() > -1) throw "TODO: ACL"; + await checkPermission("messages/fetch"); return await mongo() .db("revolt") @@ -248,10 +237,44 @@ export async function fetchMessages(query: Filter, limit = 50) { .toArray(); } +export async function fetchOpenReports() { + await checkPermission("reports/fetch/open"); + + return await mongo() + .db("revolt") + .collection("safety_reports") + .find( + { status: "Created" }, + { + sort: { + _id: -1, + }, + } + ) + .toArray(); +} + +export async function fetchRelatedReports(contentId: string) { + await checkPermission("reports/fetch/related/by-content"); + + return await mongo() + .db("revolt") + .collection("safety_reports") + .find( + { status: "Created", "content.id": contentId }, + { + sort: { + _id: -1, + }, + } + ) + .toArray(); +} + export async function fetchReports( query: Filter = { status: "Created" } ) { - if (Math.random() > -1) throw "TODO: ACL"; + await checkPermission("reports/fetch"); return await mongo() .db("revolt") @@ -265,7 +288,7 @@ export async function fetchReports( } export async function fetchReportById(id: string) { - if (Math.random() > -1) throw "TODO: ACL"; + await checkPermission("reports/fetch/by-id"); return await mongo() .db("revolt") @@ -274,7 +297,7 @@ export async function fetchReportById(id: string) { } export async function fetchMembershipsByUser(userId: string) { - if (Math.random() > -1) throw "TODO: ACL"; + await checkPermission("users/fetch/memberships"); return await mongo() .db("revolt") @@ -286,7 +309,7 @@ export async function fetchMembershipsByUser(userId: string) { export async function fetchSnapshots( query: Filter<{ _id: string; report_id: string; content: SnapshotContent }> ) { - if (Math.random() > -1) throw "TODO: ACL"; + await checkPermission("reports/fetch/snapshots"); return await mongo() .db("revolt") @@ -298,7 +321,7 @@ export async function fetchSnapshots( } export async function fetchSnapshotsByReport(reportId: string) { - if (Math.random() > -1) throw "TODO: ACL"; + await checkPermission("reports/fetch/snapshots/by-report"); return await mongo() .db("revolt") @@ -309,7 +332,7 @@ export async function fetchSnapshotsByReport(reportId: string) { } export async function fetchStrikesByUser(userId: string) { - if (Math.random() > -1) throw "TODO: ACL"; + await checkPermission("reports/fetch/snapshots/by-user"); return await mongo() .db("revolt") @@ -326,7 +349,7 @@ export async function fetchStrikesByUser(userId: string) { } export async function fetchBotsByUser(userId: string) { - if (Math.random() > -1) throw "TODO: ACL"; + await checkPermission("bots/fetch/by-user"); return await mongo() .db("revolt") @@ -341,7 +364,7 @@ export type EmailClassification = { }; export async function fetchAuthifierEmailClassification(provider: string) { - if (Math.random() > -1) throw "TODO: ACL"; + await checkPermission("authifier"); return await mongo() .db("authifier") diff --git a/lib/redis.ts b/lib/redis.ts index 75b2d8a..a1cedac 100644 --- a/lib/redis.ts +++ b/lib/redis.ts @@ -1,7 +1,7 @@ "use server"; import { createClient } from "redis"; -import { Message } from "revolt-api"; +import { Channel, Message } from "revolt-api"; import type { ProtocolV1 } from "revolt.js/lib/events/v1"; import mongo from "./db"; @@ -38,6 +38,14 @@ export async function sendChatMessage(message: Message, ephermal = false) { .db("revolt") .collection("messages") .insertOne(message); + + await mongo() + .db("revolt") + .collection("channels") + .updateOne( + { _id: message.channel }, + { $set: { last_message_id: message._id } } + ); } await publishMessage(message.channel, {