1
0
Fork 0

feat: transfer bots, reset bot token

also slightly adjusted ACLs
lea-dev
Lea 2023-09-21 17:45:53 +02:00 committed by insert
parent 77075dad23
commit bc48204410
5 changed files with 184 additions and 2 deletions

View File

@ -23,8 +23,10 @@ import { Input } from "../../ui/input";
import {
banUser,
closeReportsByUser,
resetBotToken,
sendAlert,
suspendUser,
transferBot,
unsuspendUser,
updateBotDiscoverability,
updateUserBadges,
@ -37,6 +39,7 @@ import { Card, CardHeader } from "../../ui/card";
import { cn } from "@/lib/utils";
import { decodeTime } from "ulid";
import { Checkbox } from "@/components/ui/checkbox";
import UserSelector from "@/components/ui/user-selector";
const badges = [1, 2, 4, 8, 16, 32, 128, 0, 256, 512, 1024];
@ -53,6 +56,8 @@ export function UserActions({ user, bot }: { user: User; bot?: Bot }) {
displayName: false,
status: false,
});
const [transferTarget, setTransferTarget] = useState<User | null>(null);
const [transferResetToken, setTransferResetToken] = useState(true);
const userInaccessible = userDraft.flags === 4 || userDraft.flags === 2;
@ -416,6 +421,86 @@ export function UserActions({ user, bot }: { user: User; bot?: Bot }) {
</AlertDialogContent>
</AlertDialog>
<AlertDialog>
<AlertDialogTrigger asChild>
<Button variant="ghost" disabled={!user.bot?.owner}>Reset bot token</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Reset token</AlertDialogTitle>
<AlertDialogDescription className="flex flex-col gap-2">
<span>
Re-roll this bot&apos;s authentication token. This will not disconnect active connections.
</span>
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction
onClick={() => resetBotToken(user._id)
.then(() => toast({
title: "Reset bot token",
}))
.catch((e) => toast({
title: "Failed to reset token",
description: String(e),
variant: "destructive",
}))
}
>
Reset
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
<AlertDialog>
<AlertDialogTrigger asChild>
<Button variant="ghost" disabled={!user.bot?.owner}>Transfer bot</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Transfer bot</AlertDialogTitle>
<AlertDialogDescription className="flex flex-col gap-2">
<span>
Transfer this bot to a new owner.
</span>
<UserSelector
onChange={setTransferTarget}
/>
<Checkbox
checked={transferResetToken}
onChange={(e) => setTransferResetToken(!!e)}
>
Also reset token
</Checkbox>
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction
disabled={!transferTarget}
onClick={() => transferBot(user._id, transferTarget!._id, transferResetToken)
.then(() => toast({
title: "Reset bot token",
}))
.catch((e) => toast({
title: "Failed to reset token",
description: String(e),
variant: "destructive",
}))
.finally(() => {
setTransferResetToken(true);
setTransferTarget(null);
})
}
>
Transfer
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
<AlertDialog>
<AlertDialogTrigger asChild>
<Button variant="ghost">Close Open Reports</Button>

View File

@ -24,7 +24,7 @@ type Permission =
| `bots${
| ""
| `/fetch${"" | "/by-id" | "/by-user"}`
| `/update${"" | "/discoverability"}`}`
| `/update${"" | "/discoverability" | "/owner" | "/reset-token"}`}`
| `channels${
| ""
| `/fetch${"" | "/by-id" | "/by-server" | "/dm" | "/invites"}`
@ -139,7 +139,10 @@ const PermissionSets = {
"users/update/badges",
"servers/update/owner",
"servers/update/add-member",
"bots/fetch/by-user",
"bots/update/reset-token",
"bots/update/owner",
"accounts/fetch/by-id",
"accounts/fetch/by-email",
@ -164,6 +167,9 @@ const PermissionSets = {
"users/fetch/notices",
"bots/fetch/by-user",
"bots/update/reset-token",
"bots/update/owner",
// "messages/fetch/by-user",
// "users/fetch/memberships",
"servers/fetch",
@ -175,6 +181,8 @@ const PermissionSets = {
"channels/create/dm",
"servers/update/quarantine",
"servers/update/owner",
"servers/update/add-member",
"backup/fetch",
"reports/fetch/related/by-user",

View File

@ -28,6 +28,7 @@ import {
} from "revolt-api";
import { checkPermission } from "./accessPermissions";
import { Long } from "mongodb";
import { nanoid } from "nanoid";
export async function sendAlert(userId: string, content: string) {
await checkPermission("users/create/alert", userId, { content });
@ -819,6 +820,84 @@ export async function updateBotDiscoverability(botId: string, state: boolean) {
);
}
export async function resetBotToken(botId: string) {
await checkPermission("bots/update/reset-token", { botId });
// Should generate tokens the exact same as the backend generates them:
// https://github.com/revoltchat/backend/blob/41f20c2239ed6307ad821b321d13240dc6ff3327/crates/core/database/src/models/bots/model.rs#L106
await mongo()
.db("revolt")
.collection<Bot>("bots")
.updateOne(
{
_id: botId,
},
{
$set: {
token: nanoid(64),
},
},
);
}
export async function transferBot(botId: string, ownerId: string, resetToken: boolean) {
await checkPermission("bots/update/owner", { botId, ownerId, resetToken });
if (resetToken) {
await checkPermission("bots/update/reset-token", { botId });
}
await mongo()
.db("revolt")
.collection<Bot>("bots")
.updateOne(
{
_id: botId,
},
{
$set: {
owner: ownerId,
...(
resetToken
? {
token: nanoid(64),
}
: {}
),
},
},
);
await mongo()
.db("revolt")
.collection<User>("users")
.updateOne(
{
_id: botId,
},
{
$set: {
"bot.owner": ownerId,
},
},
);
// This doesn't appear to work, maybe Revite can't handle it. I'll leave it in regardless.
await publishMessage(
botId,
{
type: "UserUpdate",
id: botId,
data: {
bot: {
owner: ownerId,
},
},
},
);
}
export async function restoreAccount(accountId: string) {
if (RESTRICT_ACCESS_LIST.includes(accountId)) throw "restricted access";
await checkPermission("accounts/restore", accountId);

View File

@ -34,6 +34,7 @@
"lodash.debounce": "^4.0.8",
"lucide-react": "^0.263.0",
"mongodb": "^5.7.0",
"nanoid": "^5.0.1",
"next": "13.4.12",
"next-auth": "^4.22.3",
"ntfy": "^1.3.1",

View File

@ -80,6 +80,9 @@ dependencies:
mongodb:
specifier: ^5.7.0
version: 5.7.0
nanoid:
specifier: ^5.0.1
version: 5.0.1
next:
specifier: 13.4.12
version: 13.4.12(react-dom@18.2.0)(react@18.2.0)(sass@1.64.1)
@ -3754,6 +3757,12 @@ packages:
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
hasBin: true
/nanoid@5.0.1:
resolution: {integrity: sha512-vWeVtV5Cw68aML/QaZvqN/3QQXc6fBfIieAlu05m7FZW2Dgb+3f0xc0TTxuJW+7u30t7iSDTV/j3kVI0oJqIfQ==}
engines: {node: ^18 || >=20}
hasBin: true
dev: false
/natural-compare@1.4.0:
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
dev: false