plugins/permissions: check channel type and implicit permissions (#2468)

* plugins/permissions: check channel type and implicit permissions

* deno fmt

* refactor: plugins/permissions

Co-authored-by: Skillz4Killz <23035000+Skillz4Killz@users.noreply.github.com>
This commit is contained in:
LTS20050703
2022-09-15 11:22:52 -04:00
committed by GitHub
co-authored by Skillz4Killz
parent 9426f4e2de
commit d39abe147f
18 changed files with 176 additions and 46 deletions
@@ -1,5 +1,5 @@
import { BotWithCache, ChannelTypes } from "../../deps.ts";
import { requireBotGuildPermissions } from "../permissions.ts";
import { BotWithCache, ChannelTypes, PermissionStrings } from "../../deps.ts";
import { requireBotChannelPermissions } from "../permissions.ts";
export function deleteChannel(bot: BotWithCache) {
const deleteChannel = bot.helpers.deleteChannel;
@@ -15,10 +15,17 @@ export function deleteChannel(bot: BotWithCache) {
if (guild.publicUpdatesChannelId === channelId) throw new Error("UPDATES_CHANNEL_CANNOT_BE_DELETED");
const perms: PermissionStrings[] = ["VIEW_CHANNEL"];
const isThread = [ChannelTypes.AnnouncementThread, ChannelTypes.PublicThread, ChannelTypes.PrivateThread]
.includes(channel.type);
const isVoice = [ChannelTypes.GuildVoice, ChannelTypes.GuildStageVoice].includes(channel.type);
requireBotGuildPermissions(bot, guild, isThread ? ["MANAGE_THREADS"] : ["MANAGE_CHANNELS"]);
if (isThread) perms.push("MANAGE_THREADS");
else perms.push("MANAGE_CHANNELS");
if (isVoice) perms.push("CONNECT");
requireBotChannelPermissions(bot, channelId, perms);
}
return await deleteChannel(channelId, reason);
@@ -1,4 +1,4 @@
import { BotWithCache } from "../../deps.ts";
import { BotWithCache, ChannelTypes, PermissionStrings } from "../../deps.ts";
import { requireBotChannelPermissions } from "../permissions.ts";
export function deleteChannelPermissionOverride(bot: BotWithCache) {
@@ -7,7 +7,14 @@ export function deleteChannelPermissionOverride(bot: BotWithCache) {
bot.helpers.deleteChannelPermissionOverride = async function (channelId, overwriteId) {
const channel = bot.channels.get(channelId);
if (channel?.guildId) requireBotChannelPermissions(bot, channelId, ["MANAGE_ROLES"]);
if (channel?.guildId) {
const perms: PermissionStrings[] = ["VIEW_CHANNEL", "MANAGE_ROLES"];
const isVoice = [ChannelTypes.GuildVoice, ChannelTypes.GuildStageVoice].includes(channel.type);
if (isVoice) perms.push("CONNECT");
requireBotChannelPermissions(bot, channelId, perms);
}
return await deleteChannelPermissionOverride(channelId, overwriteId);
};
@@ -20,10 +20,13 @@ export function editChannel(bot: BotWithCache) {
}
}
const perms: PermissionStrings[] = ["VIEW_CHANNEL"];
const isThread = [ChannelTypes.AnnouncementThread, ChannelTypes.PublicThread, ChannelTypes.PrivateThread]
.includes(channel.type);
const isVoice = [ChannelTypes.GuildVoice, ChannelTypes.GuildStageVoice].includes(channel.type);
if (isVoice) perms.push("CONNECT");
const requiredPerms: PermissionStrings[] = [];
if (isThread) {
if (options.invitable !== undefined && channel.type !== ChannelTypes.PrivateThread) {
throw new Error("Invitable option is only allowed on private threads.");
@@ -31,16 +34,16 @@ export function editChannel(bot: BotWithCache) {
// UNARCHIVING AN UNLOCKED CHANNEL SIMPLY REQUIRES SEND
if (!channel.locked && options.archived === false) {
requiredPerms.push("SEND_MESSAGES");
perms.push("SEND_MESSAGES");
// MORE THAN ARCHIVE WAS MODIFIED
if (Object.keys(options).length > 1) requiredPerms.push("MANAGE_THREADS");
if (Object.keys(options).length > 1) perms.push("MANAGE_THREADS");
} else {
requiredPerms.push("MANAGE_THREADS");
perms.push("MANAGE_THREADS");
}
} else {
requiredPerms.push("MANAGE_CHANNELS");
perms.push("MANAGE_CHANNELS");
if (options.permissionOverwrites) requiredPerms.push("MANAGE_ROLES");
if (options.permissionOverwrites) perms.push("MANAGE_ROLES");
if (options.type) {
if ([ChannelTypes.GuildAnnouncement, ChannelTypes.GuildText].includes(options.type)) {
@@ -68,7 +71,7 @@ export function editChannel(bot: BotWithCache) {
}
}
requireBotChannelPermissions(bot, channel, requiredPerms);
requireBotChannelPermissions(bot, channel, perms);
}
return await editChannel(channelId, options);
@@ -1,4 +1,4 @@
import { BotWithCache } from "../../deps.ts";
import { BotWithCache, ChannelTypes, PermissionStrings } from "../../deps.ts";
import { requireBotChannelPermissions } from "../permissions.ts";
export function editChannelPermissionOverrides(bot: BotWithCache) {
@@ -6,7 +6,14 @@ export function editChannelPermissionOverrides(bot: BotWithCache) {
bot.helpers.editChannelPermissionOverrides = async function (channelId, overwrite) {
const channel = bot.channels.get(channelId);
if (channel?.guildId) requireBotChannelPermissions(bot, channelId, ["MANAGE_ROLES"]);
if (channel?.guildId) {
const perms: PermissionStrings[] = ["VIEW_CHANNEL", "MANAGE_ROLES"];
const isVoice = [ChannelTypes.GuildVoice, ChannelTypes.GuildStageVoice].includes(channel.type);
if (isVoice) perms.push("CONNECT");
requireBotChannelPermissions(bot, channelId, perms);
}
return await editChannelPermissionOverrides(channelId, overwrite);
};
@@ -1,13 +1,23 @@
import { BotWithCache } from "../../deps.ts";
import { BotWithCache, ChannelTypes } from "../../deps.ts";
import { requireBotChannelPermissions } from "../permissions.ts";
export function followAnnouncementChannel(bot: BotWithCache) {
const followAnnouncementChannel = bot.helpers.followAnnouncementChannel;
bot.helpers.followAnnouncementChannel = async function (sourceChannelId, targetChannelId) {
const channel = bot.channels.get(targetChannelId);
if (channel?.guildId) requireBotChannelPermissions(bot, channel, ["MANAGE_WEBHOOKS"]);
const sourceChannel = bot.channels.get(sourceChannelId);
if (sourceChannel && sourceChannel.type !== ChannelTypes.GuildAnnouncement) {
throw new Error("Source channel must be an announcement channel");
}
const targetChannel = bot.channels.get(targetChannelId);
if (targetChannel) {
const isWebhookParent = [ChannelTypes.GuildAnnouncement, ChannelTypes.GuildText].includes(targetChannel.type);
if (!isWebhookParent) {
throw new Error("Target channel must be a text channel or an announcement channel");
}
}
requireBotChannelPermissions(bot, sourceChannelId, ["VIEW_CHANNEL"]);
requireBotChannelPermissions(bot, targetChannelId, ["VIEW_CHANNEL", "MANAGE_WEBHOOKS"]);
return await followAnnouncementChannel(sourceChannelId, targetChannelId);
};
}
@@ -1,4 +1,4 @@
import { BotWithCache } from "../../../deps.ts";
import { BotWithCache, ChannelTypes } from "../../../deps.ts";
import { requireBotChannelPermissions } from "../../permissions.ts";
export function createForumThread(bot: BotWithCache) {
@@ -7,7 +7,10 @@ export function createForumThread(bot: BotWithCache) {
bot.helpers.createForumThread = async function (channelId, options) {
const channel = bot.channels.get(channelId);
if (channel) requireBotChannelPermissions(bot, channel, ["SEND_MESSAGES"]);
if (channel && channel.type !== ChannelTypes.GuildForum) {
throw new Error("Channel must be a forum channel");
}
requireBotChannelPermissions(bot, channelId, ["VIEW_CHANNEL", "SEND_MESSAGES"]);
return await createForumThread(channelId, options);
};
@@ -1,4 +1,4 @@
import { BotWithCache } from "../../deps.ts";
import { BotWithCache, ChannelTypes } from "../../deps.ts";
import { requireBotChannelPermissions } from "../permissions.ts";
export function getChannelWebhooks(bot: BotWithCache) {
@@ -6,7 +6,13 @@ export function getChannelWebhooks(bot: BotWithCache) {
bot.helpers.getChannelWebhooks = async function (channelId) {
const channel = bot.channels.get(channelId);
if (channel?.guildId) requireBotChannelPermissions(bot, channelId, ["MANAGE_WEBHOOKS"]);
if (channel) {
const isWebhookParent = [ChannelTypes.GuildAnnouncement, ChannelTypes.GuildText].includes(channel.type);
if (!isWebhookParent) {
throw new Error("Target channel must be a text channel or an announcement channel");
}
requireBotChannelPermissions(bot, channelId, ["VIEW_CHANNEL", "MANAGE_WEBHOOKS"]);
}
return await getChannelWebhooks(channelId);
};
@@ -1,14 +1,17 @@
import { BotWithCache, PermissionStrings } from "../../../deps.ts";
import { BotWithCache, ChannelTypes, PermissionStrings } from "../../../deps.ts";
import { requireBotChannelPermissions } from "../../permissions.ts";
export function createStageInstance(bot: BotWithCache) {
const createStageInstance = bot.helpers.createStageInstance;
bot.helpers.createStageInstance = async function (options) {
const perms: PermissionStrings[] = ["MANAGE_CHANNELS", "MUTE_MEMBERS", "MOVE_MEMBERS"];
const channel = bot.channels.get(options.channelId);
if (channel && channel.type !== ChannelTypes.GuildStageVoice) {
throw new Error("Channel must be a stage voice channel");
}
const perms: PermissionStrings[] = ["VIEW_CHANNEL", "CONNECT", "MANAGE_CHANNELS", "MUTE_MEMBERS", "MOVE_MEMBERS"];
if (options.sendStartNotification) perms.push("MENTION_EVERYONE");
requireBotChannelPermissions(bot, options.channelId, perms);
return await createStageInstance(options);
@@ -1,11 +1,21 @@
import { BotWithCache } from "../../../deps.ts";
import { BotWithCache, ChannelTypes } from "../../../deps.ts";
import { requireBotChannelPermissions } from "../../permissions.ts";
export function deleteStageInstance(bot: BotWithCache) {
const deleteStageInstance = bot.helpers.deleteStageInstance;
bot.helpers.deleteStageInstance = async function (channelId) {
requireBotChannelPermissions(bot, channelId, ["MANAGE_CHANNELS", "MUTE_MEMBERS", "MOVE_MEMBERS"]);
const channel = bot.channels.get(channelId);
if (channel && channel.type !== ChannelTypes.GuildStageVoice) {
throw new Error("Channel must be a stage voice channel");
}
requireBotChannelPermissions(bot, channelId, [
"VIEW_CHANNEL",
"CONNECT",
"MANAGE_CHANNELS",
"MUTE_MEMBERS",
"MOVE_MEMBERS",
]);
return await deleteStageInstance(channelId);
};
@@ -1,11 +1,22 @@
import { BotWithCache } from "../../../deps.ts";
import { BotWithCache, ChannelTypes } from "../../../deps.ts";
import { requireBotChannelPermissions } from "../../permissions.ts";
export function editStageInstance(bot: BotWithCache) {
const editStageInstance = bot.helpers.editStageInstance;
bot.helpers.editStageInstance = async function (channelId, data) {
requireBotChannelPermissions(bot, channelId, ["MANAGE_CHANNELS", "MUTE_MEMBERS", "MOVE_MEMBERS"]);
const channel = bot.channels.get(channelId);
if (channel && channel.type !== ChannelTypes.GuildStageVoice) {
throw new Error("Channel must be a stage voice channel");
}
requireBotChannelPermissions(bot, channelId, [
"VIEW_CHANNEL",
"CONNECT",
"MANAGE_CHANNELS",
"MUTE_MEMBERS",
"MOVE_MEMBERS",
]);
return await editStageInstance(channelId, data);
};
@@ -1,12 +1,19 @@
import { BotWithCache } from "../../deps.ts";
import { requireBotGuildPermissions } from "../permissions.ts";
import { BotWithCache, ChannelTypes, PermissionStrings } from "../../deps.ts";
import { requireBotChannelPermissions } from "../permissions.ts";
export function swapChannels(bot: BotWithCache) {
const swapChannels = bot.helpers.swapChannels;
bot.helpers.swapChannels = async function (guildId, channelPositions) {
requireBotGuildPermissions(bot, guildId, ["MANAGE_CHANNELS"]);
for (const channelPosition of channelPositions) {
const channel = bot.channels.get(BigInt(channelPosition.id));
if (channel) {
const perms: PermissionStrings[] = ["VIEW_CHANNEL", "MANAGE_CHANNELS"];
const isVoice = [ChannelTypes.GuildVoice, ChannelTypes.GuildStageVoice].includes(channel.type);
if (isVoice) perms.push("CONNECT");
requireBotChannelPermissions(bot, BigInt(channelPosition.id), perms);
}
}
return await swapChannels(guildId, channelPositions);
};
}
@@ -1,4 +1,4 @@
import { BotWithCache } from "../../../deps.ts";
import { BotWithCache, ChannelTypes } from "../../../deps.ts";
import { requireBotChannelPermissions } from "../../permissions.ts";
export function addThreadMember(bot: BotWithCache) {
@@ -8,9 +8,14 @@ export function addThreadMember(bot: BotWithCache) {
const channel = bot.channels.get(threadId);
if (channel) {
const isThread = ![ChannelTypes.PublicThread, ChannelTypes.PrivateThread, ChannelTypes.AnnouncementThread]
.includes(channel.type);
if (isThread) throw new Error("Channel must be a thread channel");
if (channel.archived) throw new Error("Cannot add user to thread if thread is archived.");
requireBotChannelPermissions(bot, channel, ["SEND_MESSAGES"]);
requireBotChannelPermissions(bot, channel, ["VIEW_CHANNEL", "SEND_MESSAGES"]);
}
return await addThreadMember(threadId, userId);
@@ -1,11 +1,21 @@
import { BotWithCache } from "../../../deps.ts";
import { BotWithCache, ChannelTypes } from "../../../deps.ts";
import { requireBotChannelPermissions } from "../../permissions.ts";
export function getPrivateArchivedThreads(bot: BotWithCache) {
const getPrivateArchivedThreads = bot.helpers.getPrivateArchivedThreads;
bot.helpers.getPrivateArchivedThreads = async function (channelId, options) {
const channel = bot.channels.get(channelId);
if (channel) requireBotChannelPermissions(bot, channel, ["READ_MESSAGE_HISTORY", "MANAGE_MESSAGES"]);
if (channel) {
const isThreadParent = [ChannelTypes.GuildText, ChannelTypes.GuildAnnouncement, ChannelTypes.GuildForum]
.includes(channel.type);
if (!isThreadParent) {
throw new Error("Channel must be a text channel, a forum channel, or an announcement channel");
}
}
requireBotChannelPermissions(bot, channelId, ["VIEW_CHANNEL", "READ_MESSAGE_HISTORY", "MANAGE_MESSAGES"]);
return await getPrivateArchivedThreads(channelId, options);
};
}
@@ -1,11 +1,20 @@
import { BotWithCache } from "../../../deps.ts";
import { BotWithCache, ChannelTypes } from "../../../deps.ts";
import { requireBotChannelPermissions } from "../../permissions.ts";
export function getPrivateJoinedArchivedThreads(bot: BotWithCache) {
const getPrivateJoinedArchivedThreads = bot.helpers.getPrivateJoinedArchivedThreads;
bot.helpers.getPrivateJoinedArchivedThreads = async function (channelId, options) {
const channel = bot.channels.get(channelId);
if (channel) requireBotChannelPermissions(bot, channel, ["READ_MESSAGE_HISTORY"]);
if (channel) {
const isThreadParent = [ChannelTypes.GuildText, ChannelTypes.GuildAnnouncement, ChannelTypes.GuildForum]
.includes(channel.type);
if (!isThreadParent) {
throw new Error("Channel must be a text channel, a forum channel, or an announcement channel");
}
}
requireBotChannelPermissions(bot, channelId, ["VIEW_CHANNEL", "READ_MESSAGE_HISTORY"]);
return await getPrivateJoinedArchivedThreads(channelId, options);
};
}
@@ -1,11 +1,20 @@
import { BotWithCache } from "../../../deps.ts";
import { BotWithCache, ChannelTypes } from "../../../deps.ts";
import { requireBotChannelPermissions } from "../../permissions.ts";
export function getPublicArchivedThreads(bot: BotWithCache) {
const getPublicArchivedThreads = bot.helpers.getPublicArchivedThreads;
bot.helpers.getPublicArchivedThreads = async function (channelId, options) {
const channel = bot.channels.get(channelId);
if (channel) requireBotChannelPermissions(bot, channel, ["READ_MESSAGE_HISTORY"]);
if (channel) {
const isThreadParent = [ChannelTypes.GuildText, ChannelTypes.GuildAnnouncement, ChannelTypes.GuildForum]
.includes(channel.type);
if (!isThreadParent) {
throw new Error("Channel must be a text channel, a forum channel, or an announcement channel");
}
}
requireBotChannelPermissions(bot, channelId, ["VIEW_CHANNEL", "READ_MESSAGE_HISTORY"]);
return await getPublicArchivedThreads(channelId, options);
};
}
@@ -1,4 +1,5 @@
import { BotWithCache } from "../../../deps.ts";
import { BotWithCache, ChannelTypes } from "../../../deps.ts";
import { requireBotChannelPermissions } from "../../permissions.ts";
export function joinThread(bot: BotWithCache) {
const joinThread = bot.helpers.joinThread;
@@ -6,7 +7,15 @@ export function joinThread(bot: BotWithCache) {
bot.helpers.joinThread = async function (threadId) {
const channel = bot.channels.get(threadId);
if (channel && !channel.archived) throw new Error("You can not join an archived channel.");
if (channel) {
const isThread = ![ChannelTypes.PublicThread, ChannelTypes.PrivateThread, ChannelTypes.AnnouncementThread]
.includes(channel.type);
if (isThread) throw new Error("Channel must be a thread channel");
if (channel.archived) throw new Error("You can not join an archived channel.");
}
requireBotChannelPermissions(bot, threadId, ["VIEW_CHANNEL"]);
return await joinThread(threadId);
};
@@ -1,4 +1,5 @@
import { BotWithCache } from "../../../deps.ts";
import { BotWithCache, ChannelTypes } from "../../../deps.ts";
import { requireBotChannelPermissions } from "../../permissions.ts";
export function leaveThread(bot: BotWithCache) {
const leaveThread = bot.helpers.leaveThread;
@@ -6,7 +7,15 @@ export function leaveThread(bot: BotWithCache) {
bot.helpers.leaveThread = async function (threadId) {
const channel = bot.channels.get(threadId);
if (channel && !channel.archived) throw new Error("You can not leave an archived channel.");
if (channel) {
const isThread = ![ChannelTypes.PublicThread, ChannelTypes.PrivateThread, ChannelTypes.AnnouncementThread]
.includes(channel.type);
if (isThread) throw new Error("Channel must be a thread channel");
if (channel.archived) throw new Error("You can not leave an archived channel.");
}
requireBotChannelPermissions(bot, threadId, ["VIEW_CHANNEL"]);
return await leaveThread(threadId);
};
@@ -8,10 +8,15 @@ export function removeThreadMember(bot: BotWithCache) {
const channel = bot.channels.get(threadId);
if (channel) {
const isThread = ![ChannelTypes.PublicThread, ChannelTypes.PrivateThread, ChannelTypes.AnnouncementThread]
.includes(channel.type);
if (isThread) throw new Error("Channel must be a thread channel");
if (channel.archived) throw new Error("Cannot remove user from thread if thread is archived.");
if (!(bot.id === channel.ownerId && channel.type === ChannelTypes.PrivateThread)) {
requireBotChannelPermissions(bot, channel, ["MANAGE_MESSAGES"]);
requireBotChannelPermissions(bot, channel, ["VIEW_CHANNEL", "MANAGE_MESSAGES"]);
}
}