Compare commits

...

3 commits

View file

@ -12,6 +12,8 @@ const auth = sync.require("../auth")
const mreq = sync.require("../../matrix/mreq") const mreq = sync.require("../../matrix/mreq")
const {reg} = require("../../matrix/read-registration") const {reg} = require("../../matrix/read-registration")
const me = `@${reg.sender_localpart}:${reg.ooye.server_name}`
/** /**
* @param {H3Event} event * @param {H3Event} event
* @returns {import("../../matrix/api")} * @returns {import("../../matrix/api")}
@ -39,6 +41,48 @@ function getCreateSpace(event) {
return event.context.createSpace || sync.require("../../d2m/actions/create-space") return event.context.createSpace || sync.require("../../d2m/actions/create-space")
} }
async function validateUserHaveRightsOnGuild(event, guild_id) {
const managed = await auth.getManagedGuilds(event)
if (!managed.has(guild_id))
throw createError({status: 403, message: "Forbidden", data: "Can't edit a guild you don't have Manage Server permissions in"})
}
function validateBotIsInGuild(guild) {
if (!guild)
throw createError({status: 400, message: "Bad Request", data: "Discord guild does not exist or bot has not joined it"})
}
async function validateGuildAccess(event, guild_id) {
// Check guild ID or nonce
await validateUserHaveRightsOnGuild(event, guild_id)
// Check guild exists
const guild = discord.guilds.get(guild_id)
validateBotIsInGuild(guild)
return guild
}
async function doRoomUnlink(createRoom, channel_id, guild_id) {
// Check that the channel (if it exists) is part of this guild
/** @type {any} */
let channel = discord.channels.get(channel_id)
if (channel) {
if (!("guild_id" in channel) || channel.guild_id !== guild_id) throw createError({status: 400, message: "Bad Request", data: `Channel ID ${channel_id} is not part of guild ${guild_id}`})
} else {
// Otherwise, if the channel isn't cached, it must have been deleted.
// There's no other authentication here - it's okay for anyone to unlink a deleted channel just by knowing its ID.
channel = {id: channel_id}
}
// Check channel is currently bridged
const row = select("channel_room", "channel_id", {channel_id: channel_id}).get()
if (!row) throw createError({status: 400, message: "Bad Request", data: `Channel ID ${channel_id} is not currently bridged`})
// Do it
await createRoom.unbridgeDeletedChannel(channel, guild_id)
}
const schema = { const schema = {
linkSpace: z.object({ linkSpace: z.object({
guild_id: z.string(), guild_id: z.string(),
@ -52,18 +96,20 @@ const schema = {
unlink: z.object({ unlink: z.object({
guild_id: z.string(), guild_id: z.string(),
channel_id: z.string() channel_id: z.string()
}) }),
unlinkSpace: z.object({
guild_id: z.string(),
}),
} }
as.router.post("/api/link-space", defineEventHandler(async event => { as.router.post("/api/link-space", defineEventHandler(async event => {
const parsedBody = await readValidatedBody(event, schema.linkSpace.parse) const parsedBody = await readValidatedBody(event, schema.linkSpace.parse)
const session = await auth.useSession(event) const session = await auth.useSession(event)
const managed = await auth.getManagedGuilds(event)
const api = getAPI(event) const api = getAPI(event)
// Check guild ID // Check guild ID
const guildID = parsedBody.guild_id const guildID = parsedBody.guild_id
if (!managed.has(guildID)) throw createError({status: 403, message: "Forbidden", data: "Can't edit a guild you don't have Manage Server permissions in"}) await validateUserHaveRightsOnGuild(event, guildID)
// Check space ID // Check space ID
if (!session.data.mxid) throw createError({status: 403, message: "Forbidden", data: "Can't link with your Matrix space if you aren't logged in to Matrix"}) if (!session.data.mxid) throw createError({status: 403, message: "Forbidden", data: "Can't link with your Matrix space if you aren't logged in to Matrix"})
@ -83,7 +129,6 @@ as.router.post("/api/link-space", defineEventHandler(async event => {
} }
// Check bridge has PL 100 // Check bridge has PL 100
const me = `@${reg.sender_localpart}:${reg.ooye.server_name}`
/** @type {Ty.Event.M_Power_Levels?} */ /** @type {Ty.Event.M_Power_Levels?} */
let powerLevelsStateContent = null let powerLevelsStateContent = null
try { try {
@ -108,18 +153,12 @@ as.router.post("/api/link-space", defineEventHandler(async event => {
as.router.post("/api/link", defineEventHandler(async event => { as.router.post("/api/link", defineEventHandler(async event => {
const parsedBody = await readValidatedBody(event, schema.link.parse) const parsedBody = await readValidatedBody(event, schema.link.parse)
const managed = await auth.getManagedGuilds(event)
const api = getAPI(event) const api = getAPI(event)
const createRoom = getCreateRoom(event) const createRoom = getCreateRoom(event)
const createSpace = getCreateSpace(event) const createSpace = getCreateSpace(event)
// Check guild ID or nonce
const guildID = parsedBody.guild_id const guildID = parsedBody.guild_id
if (!managed.has(guildID)) throw createError({status: 403, message: "Forbidden", data: "Can't edit a guild you don't have Manage Server permissions in"}) guild = await validateGuildAccess(event, guildID)
// Check guild is bridged
const guild = discord.guilds.get(guildID)
if (!guild) throw createError({status: 400, message: "Bad Request", data: "Discord guild does not exist or bot has not joined it"})
const spaceID = await createSpace.ensureSpace(guild) const spaceID = await createSpace.ensureSpace(guild)
// Check channel exists // Check channel exists
@ -183,33 +222,38 @@ as.router.post("/api/link", defineEventHandler(async event => {
as.router.post("/api/unlink", defineEventHandler(async event => { as.router.post("/api/unlink", defineEventHandler(async event => {
const {channel_id, guild_id} = await readValidatedBody(event, schema.unlink.parse) const {channel_id, guild_id} = await readValidatedBody(event, schema.unlink.parse)
const managed = await auth.getManagedGuilds(event)
const createRoom = getCreateRoom(event) const createRoom = getCreateRoom(event)
await validateGuildAccess(event, guild_id)
// Check guild ID or nonce await doRoomUnlink(createRoom, channel_id, guild_id)
if (!managed.has(guild_id)) throw createError({status: 403, message: "Forbidden", data: "Can't edit a guild you don't have Manage Server permissions in"})
setResponseHeader(event, "HX-Refresh", "true")
// Check guild exists return null // 204
const guild = discord.guilds.get(guild_id) }))
if (!guild) throw createError({status: 400, message: "Bad Request", data: "Discord guild does not exist or bot has not joined it"})
as.router.post("/api/unlink-space", defineEventHandler(async event => {
// Check that the channel (if it exists) is part of this guild const {guild_id} = await readValidatedBody(event, schema.unlinkSpace.parse)
/** @type {any} */ const api = getAPI(event)
let channel = discord.channels.get(channel_id) await validateGuildAccess(event, guild_id)
if (channel) {
if (!("guild_id" in channel) || channel.guild_id !== guild_id) throw createError({status: 400, message: "Bad Request", data: `Channel ID ${channel_id} is not part of guild ${guild_id}`}) // TODO: check valid spaceID etc.
} else { const spaceID = select("guild_space", "space_id", {guild_id: guild_id}).pluck().get()
// Otherwise, if the channel isn't cached, it must have been deleted. const channelIDs = discord.guildChannelMap.get(guild_id)
// There's no other authentication here - it's okay for anyone to unlink a deleted channel just by knowing its ID. const linkedChannels = select("channel_room", ["channel_id", "room_id", "name", "nick"], {channel_id: channelIDs}).all()
channel = {id: channel_id}
} linkedChannels.forEach(async channel => {
// FIXME: not sure about how to create a proper createRoom.
// Check channel is currently bridged // Can we tinker with event to add channel_id information?
const row = select("channel_room", "channel_id", {channel_id: channel_id}).get() const createRoom = getCreateRoom(event)
if (!row) throw createError({status: 400, message: "Bad Request", data: `Channel ID ${channel_id} is not currently bridged`}) await doRoomUnlink(createRoom, channel.channel_id, guild_id)
});
// Do it
await createRoom.unbridgeDeletedChannel(channel, guild_id) // TODO: add a check that no remaining bridged rooms are in the space
await api.setUserPower(spaceID, me, 0)
await api.leaveRoom(spaceID)
db.prepare("DELETE FROM guild_space WHERE guild_id=? AND space_id=?").run(guild_id, spaceID)
setResponseHeader(event, "HX-Refresh", "true") setResponseHeader(event, "HX-Refresh", "true")
return null // 204 return null // 204