mirror of
https://github.com/Hessenuk/DiscordTickets.git
synced 2025-01-10 18:06:27 +02:00
fix(security): transcript access control (closes #555)
This commit is contained in:
parent
2d2f350284
commit
b8b5ac946a
@ -18,12 +18,13 @@ module.exports = class TicketCompleter extends Autocompleter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async getOptions(value, {
|
async getOptions(value, {
|
||||||
guildId,
|
interaction,
|
||||||
open,
|
open,
|
||||||
userId,
|
userId,
|
||||||
}) {
|
}) {
|
||||||
/** @type {import("client")} */
|
/** @type {import("client")} */
|
||||||
const client = this.client;
|
const client = this.client;
|
||||||
|
const guildId = interaction.guild.id;
|
||||||
const cacheKey = [guildId, userId, open].join('/');
|
const cacheKey = [guildId, userId, open].join('/');
|
||||||
|
|
||||||
let tickets = await this.cache.get(cacheKey);
|
let tickets = await this.cache.get(cacheKey);
|
||||||
@ -34,21 +35,16 @@ module.exports = class TicketCompleter extends Autocompleter {
|
|||||||
where: { id: guildId },
|
where: { id: guildId },
|
||||||
});
|
});
|
||||||
tickets = await client.prisma.ticket.findMany({
|
tickets = await client.prisma.ticket.findMany({
|
||||||
include: {
|
include: { category: true },
|
||||||
category: {
|
|
||||||
select: {
|
|
||||||
emoji: true,
|
|
||||||
name: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
where: {
|
where: {
|
||||||
createdById: userId,
|
createdById: userId,
|
||||||
guildId,
|
guildId,
|
||||||
open,
|
open,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
tickets = tickets.map(ticket => {
|
tickets = tickets
|
||||||
|
.filter(ticket => client.commands.commands.slash.get('transcript').shouldAllowAccess(interaction, ticket))
|
||||||
|
.map(ticket => {
|
||||||
const date = new Date(ticket.createdAt).toLocaleString([locale, 'en-GB'], { dateStyle: 'short' });
|
const date = new Date(ticket.createdAt).toLocaleString([locale, 'en-GB'], { dateStyle: 'short' });
|
||||||
const topic = ticket.topic ? '- ' + decrypt(ticket.topic).replace(/\n/g, ' ').substring(0, 50) : '';
|
const topic = ticket.topic ? '- ' + decrypt(ticket.topic).replace(/\n/g, ' ').substring(0, 50) : '';
|
||||||
const category = emoji.hasEmoji(ticket.category.emoji) ? emoji.get(ticket.category.emoji) + ' ' + ticket.category.name : ticket.category.name;
|
const category = emoji.hasEmoji(ticket.category.emoji) ? emoji.get(ticket.category.emoji) + ' ' + ticket.category.name : ticket.category.name;
|
||||||
@ -77,7 +73,7 @@ module.exports = class TicketCompleter extends Autocompleter {
|
|||||||
const userId = otherMember || interaction.user.id;
|
const userId = otherMember || interaction.user.id;
|
||||||
await interaction.respond(
|
await interaction.respond(
|
||||||
await this.getOptions(value, {
|
await this.getOptions(value, {
|
||||||
guildId: interaction.guild.id,
|
interaction,
|
||||||
open: ['add', 'close', 'force-close', 'remove'].includes(command.name), // false for `new`, `transcript` etc
|
open: ['add', 'close', 'force-close', 'remove'].includes(command.name), // false for `new`, `transcript` etc
|
||||||
userId,
|
userId,
|
||||||
}),
|
}),
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
const { SlashCommand } = require('@eartharoid/dbf');
|
const { SlashCommand } = require('@eartharoid/dbf');
|
||||||
const { ApplicationCommandOptionType } = require('discord.js');
|
const {
|
||||||
|
ApplicationCommandOptionType,
|
||||||
|
PermissionsBitField,
|
||||||
|
} = require('discord.js');
|
||||||
const { isStaff } = require('../../lib/users');
|
const { isStaff } = require('../../lib/users');
|
||||||
const ExtendedEmbedBuilder = require('../../lib/embed');
|
const ExtendedEmbedBuilder = require('../../lib/embed');
|
||||||
const Cryptr = require('cryptr');
|
const Cryptr = require('cryptr');
|
||||||
@ -60,12 +63,45 @@ module.exports = class TicketsSlashCommand extends SlashCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const fields = [];
|
const fields = [];
|
||||||
|
let base_filter;
|
||||||
|
|
||||||
|
if (member.id === interaction.member.id) {
|
||||||
|
base_filter = {
|
||||||
|
createdById: member.id,
|
||||||
|
guildId: interaction.guild.id,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
const { categories } = await client.prisma.guild.findUnique({
|
||||||
|
select: {
|
||||||
|
categories: {
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
staffRoles: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
where: { id: interaction.guild.id },
|
||||||
|
});
|
||||||
|
const allow_category_ids = (
|
||||||
|
(
|
||||||
|
client.supers.includes(interaction.member.id) ||
|
||||||
|
interaction.member.permissions.has(PermissionsBitField.Flags.ManageGuild)
|
||||||
|
)
|
||||||
|
? categories
|
||||||
|
: categories.filter(c => c.staffRoles.some(id => interaction.member.roles.cache.has(id)))
|
||||||
|
)
|
||||||
|
.map(c => c.id);
|
||||||
|
base_filter = {
|
||||||
|
categoryId: { in: allow_category_ids },
|
||||||
|
createdById: member.id,
|
||||||
|
guildId: interaction.guild.id,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const open = await client.prisma.ticket.findMany({
|
const open = await client.prisma.ticket.findMany({
|
||||||
include: { category: true },
|
include: { category: true },
|
||||||
where: {
|
where: {
|
||||||
createdById: member.id,
|
...base_filter,
|
||||||
guildId: interaction.guild.id,
|
|
||||||
open: true,
|
open: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -75,8 +111,7 @@ module.exports = class TicketsSlashCommand extends SlashCommand {
|
|||||||
orderBy: { createdAt: 'desc' },
|
orderBy: { createdAt: 'desc' },
|
||||||
take: 10, // max 10 rows
|
take: 10, // max 10 rows
|
||||||
where: {
|
where: {
|
||||||
createdById: member.id,
|
...base_filter,
|
||||||
guildId: interaction.guild.id,
|
|
||||||
open: false,
|
open: false,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -84,8 +119,8 @@ module.exports = class TicketsSlashCommand extends SlashCommand {
|
|||||||
if (open.length >= 1) {
|
if (open.length >= 1) {
|
||||||
fields.push({
|
fields.push({
|
||||||
name: getMessage('commands.slash.tickets.response.fields.open.name'),
|
name: getMessage('commands.slash.tickets.response.fields.open.name'),
|
||||||
value: open.map(ticket =>{
|
value: open.map(ticket => {
|
||||||
const topic = ticket.topic ? `- \`${decrypt(ticket.topic).replace(/\n/g, ' ').slice(0, 30) }\`` : '';
|
const topic = ticket.topic ? `- \`${decrypt(ticket.topic).replace(/\n/g, ' ').slice(0, 30)}\`` : '';
|
||||||
return `> <#${ticket.id}> ${topic}`;
|
return `> <#${ticket.id}> ${topic}`;
|
||||||
}).join('\n'),
|
}).join('\n'),
|
||||||
});
|
});
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
const { SlashCommand } = require('@eartharoid/dbf');
|
const { SlashCommand } = require('@eartharoid/dbf');
|
||||||
const { ApplicationCommandOptionType } = require('discord.js');
|
const {
|
||||||
|
ApplicationCommandOptionType,
|
||||||
|
PermissionsBitField,
|
||||||
|
} = require('discord.js');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const { join } = require('path');
|
const { join } = require('path');
|
||||||
const Mustache = require('mustache');
|
const Mustache = require('mustache');
|
||||||
const { AttachmentBuilder } = require('discord.js');
|
const { AttachmentBuilder } = require('discord.js');
|
||||||
const Cryptr = require('cryptr');
|
const Cryptr = require('cryptr');
|
||||||
const { decrypt } = new Cryptr(process.env.ENCRYPTION_KEY);
|
const { decrypt } = new Cryptr(process.env.ENCRYPTION_KEY);
|
||||||
const { isStaff } = require('../../lib/users');
|
|
||||||
const ExtendedEmbedBuilder = require('../../lib/embed');
|
const ExtendedEmbedBuilder = require('../../lib/embed');
|
||||||
|
|
||||||
module.exports = class TranscriptSlashCommand extends SlashCommand {
|
module.exports = class TranscriptSlashCommand extends SlashCommand {
|
||||||
@ -46,6 +48,15 @@ module.exports = class TranscriptSlashCommand extends SlashCommand {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
shouldAllowAccess(interaction, ticket) {
|
||||||
|
if (interaction.guild.id !== ticket.guildId) return false;
|
||||||
|
if (ticket.createdById === interaction.member.id) return true;
|
||||||
|
if (interaction.client.supers.includes(interaction.member.id)) return true;
|
||||||
|
if (interaction.member.permissions.has(PermissionsBitField.Flags.ManageGuild)) return true;
|
||||||
|
if (interaction.member.roles.cache.filter(role => ticket.category.staffRoles.includes(role.id)).size > 0) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
async fillTemplate(ticket) {
|
async fillTemplate(ticket) {
|
||||||
/** @type {import("client")} */
|
/** @type {import("client")} */
|
||||||
const client = this.client;
|
const client = this.client;
|
||||||
@ -146,10 +157,7 @@ module.exports = class TranscriptSlashCommand extends SlashCommand {
|
|||||||
|
|
||||||
if (!ticket) throw new Error(`Ticket ${ticketId} does not exist`);
|
if (!ticket) throw new Error(`Ticket ${ticketId} does not exist`);
|
||||||
|
|
||||||
if (
|
if (!this.shouldAllowAccess(interaction, ticket)) {
|
||||||
ticket.createdById !== interaction.member.id &&
|
|
||||||
!(await isStaff(interaction.guild, interaction.member.id))
|
|
||||||
) {
|
|
||||||
const settings = await client.prisma.guild.findUnique({ where: { id: interaction.guild.id } });
|
const settings = await client.prisma.guild.findUnique({ where: { id: interaction.guild.id } });
|
||||||
const getMessage = client.i18n.getLocale(settings.locale);
|
const getMessage = client.i18n.getLocale(settings.locale);
|
||||||
return await interaction.editReply({
|
return await interaction.editReply({
|
||||||
|
Loading…
Reference in New Issue
Block a user