Giving up on encryption

and life
This commit is contained in:
Isaac 2022-07-19 15:57:19 +01:00
parent 28f1e85759
commit 5786c3598d
7 changed files with 95 additions and 121 deletions

View File

@ -53,13 +53,13 @@ model ArchivedUser {
bot Boolean @default(false) bot Boolean @default(false)
createdAt DateTime @default(now()) createdAt DateTime @default(now())
discriminator String @db.Char(4) discriminator String @db.Char(4)
displayName String @db.VarChar(512) displayName String @db.Text
role ArchivedRole @relation(fields: [ticketId, roleId], references: [ticketId, roleId], onDelete: Cascade) role ArchivedRole @relation(fields: [ticketId, roleId], references: [ticketId, roleId], onDelete: Cascade)
roleId String @db.VarChar(19) roleId String @db.VarChar(19)
ticket Ticket @relation(fields: [ticketId], references: [id], onDelete: Cascade) ticket Ticket @relation(fields: [ticketId], references: [id], onDelete: Cascade)
ticketId String @db.VarChar(19) ticketId String @db.VarChar(19)
userId String @db.VarChar(19) userId String @db.VarChar(19)
username String @db.VarChar(512) username String @db.Text
@@id([ticketId, userId]) @@id([ticketId, userId])
@@unique([ticketId, userId]) @@unique([ticketId, userId])
@ -70,7 +70,7 @@ model Category {
channelName String channelName String
claiming Boolean @default(false) claiming Boolean @default(false)
createdAt DateTime @default(now()) createdAt DateTime @default(now())
description String @db.VarChar(512) description String
discordCategory String @db.VarChar(19) discordCategory String @db.VarChar(19)
emoji String emoji String
enableFeedback Boolean @default(false) enableFeedback Boolean @default(false)
@ -79,7 +79,7 @@ model Category {
id Int @id @default(autoincrement()) id Int @id @default(autoincrement())
image String? image String?
memberLimit Int @default(1) memberLimit Int @default(1)
name String @db.VarChar(512) name String
openingMessage String @db.Text openingMessage String @db.Text
pingRoles Json @default("[]") pingRoles Json @default("[]")
questions Question[] questions Question[]
@ -134,14 +134,14 @@ model Guild {
model Question { model Question {
answers QuestionAnswer[] answers QuestionAnswer[]
createdAt DateTime @default(now()) createdAt DateTime @default(now())
id Int @id @default(autoincrement()) id String @id @default(uuid())
category Category? @relation(fields: [categoryId], references: [id]) category Category @relation(fields: [categoryId], references: [id], onDelete: Cascade)
categoryId Int? categoryId Int
label String @db.VarChar(512) label String
maxLength Int? @default(4000) maxLength Int? @default(4000)
minLength Int? @default(0) minLength Int? @default(0)
order Int order Int
placeholder String? @db.VarChar(512) placeholder String?
required Boolean @default(true) required Boolean @default(true)
style Int @default(2) style Int @default(2)
value String? @db.Text value String? @db.Text
@ -152,11 +152,11 @@ model Question {
model QuestionAnswer { model QuestionAnswer {
createdAt DateTime @default(now()) createdAt DateTime @default(now())
id Int @id @default(autoincrement()) id Int @id @default(autoincrement())
ticket Ticket @relation(fields: [ticketId], references: [id]) ticket Ticket @relation(fields: [ticketId], references: [id], onDelete: Cascade)
ticketId String @db.VarChar(19) ticketId String @db.VarChar(19)
question Question @relation(fields: [questionId], references: [id]) question Question @relation(fields: [questionId], references: [id], onDelete: Cascade)
questionId Int questionId String
user User @relation(fields: [userId], references: [id]) user User @relation(fields: [userId], references: [id], onDelete: Cascade)
userId String @db.VarChar(19) userId String @db.VarChar(19)
value String? @db.Text value String? @db.Text
@ -164,13 +164,13 @@ model QuestionAnswer {
} }
model Tag { model Tag {
content String @db.Text() content String @db.Text
createdAt DateTime @default(now()) createdAt DateTime @default(now())
guild Guild @relation(fields: [guildId], references: [id], onDelete: Cascade) guild Guild @relation(fields: [guildId], references: [id], onDelete: Cascade)
guildId String @db.VarChar(19) guildId String @db.VarChar(19)
id Int @id @default(autoincrement()) id Int @id @default(autoincrement())
name String @db.VarChar(512) name String
regex String? @db.VarChar(512) regex String?
@@unique([guildId, name]) @@unique([guildId, name])
@@map("tags") @@map("tags")
@ -188,7 +188,7 @@ model Ticket {
closedAt DateTime? closedAt DateTime?
closedBy User @relation(name: "TicketsClosedByUser", fields: [closedById], references: [id]) closedBy User @relation(name: "TicketsClosedByUser", fields: [closedById], references: [id])
closedById String @db.VarChar(19) closedById String @db.VarChar(19)
closedReason String? @db.VarChar(512) closedReason String?
createdAt DateTime @default(now()) createdAt DateTime @default(now())
createdBy User @relation(name: "TicketsCreatedByUser", fields: [createdById], references: [id]) createdBy User @relation(name: "TicketsCreatedByUser", fields: [createdById], references: [id])
createdById String @db.VarChar(19) createdById String @db.VarChar(19)
@ -210,7 +210,7 @@ model Ticket {
referencesMessageId String @db.VarChar(19) referencesMessageId String @db.VarChar(19)
referencesTicket Ticket? @relation(name: "TicketsReferencedByTicket", fields: [referencesTicketId], references: [id], onDelete: SetNull) referencesTicket Ticket? @relation(name: "TicketsReferencedByTicket", fields: [referencesTicketId], references: [id], onDelete: SetNull)
referencesTicketId String? @db.VarChar(19) referencesTicketId String? @db.VarChar(19)
topic String? @db.VarChar(512) topic String?
questionAnswers QuestionAnswer[] questionAnswers QuestionAnswer[]
@@unique([guildId, number]) @@unique([guildId, number])

View File

@ -6,7 +6,6 @@ const I18n = require('@eartharoid/i18n');
const fs = require('fs'); const fs = require('fs');
const { join } = require('path'); const { join } = require('path');
const YAML = require('yaml'); const YAML = require('yaml');
const middleware = require('./lib/prisma');
module.exports = class Client extends FrameworkClient { module.exports = class Client extends FrameworkClient {
constructor(config, log) { constructor(config, log) {
@ -37,7 +36,6 @@ module.exports = class Client extends FrameworkClient {
async login(token) { async login(token) {
/** @type {PrismaClient} */ /** @type {PrismaClient} */
this.prisma = new PrismaClient(); this.prisma = new PrismaClient();
this.prisma.$use(middleware(this.log));
this.keyv = new Keyv(); this.keyv = new Keyv();
return super.login(token); return super.login(token);
} }

View File

@ -1,4 +1,7 @@
const { EmbedBuilder } = require('discord.js'); const {
cleanCodeBlockContent,
EmbedBuilder,
} = require('discord.js');
const { diff: getDiff } = require('object-diffy'); const { diff: getDiff } = require('object-diffy');
function makeDiff({ function makeDiff({
@ -8,12 +11,12 @@ function makeDiff({
const fields = []; const fields = [];
for (const key in diff) { for (const key in diff) {
if (key === 'createdAt') continue; // object-diffy doesn't like dates if (key === 'createdAt') continue; // object-diffy doesn't like dates
const from = diff[key].from === null ? '' : `- ${diff[key].from}\n`; const from = diff[key].from === null ? '' : `- ${diff[key].from.toString().replace(/\n/g, '\\n')}\n`;
const to = diff[key].to === null ? '' : `+ ${diff[key].to}\n`; const to = diff[key].to === null ? '' : `+ ${diff[key].to.toString().replace(/\n/g, '\\n')}\n`;
fields.push({ fields.push({
inline: true, inline: true,
name: key, name: key,
value: `\`\`\`diff\n${from + to}\n\`\`\``, value: `\`\`\`diff\n${cleanCodeBlockContent(from + to)}\n\`\`\``,
}); });
} }
return fields; return fields;

View File

@ -1,69 +0,0 @@
const Cryptr = require('cryptr');
const cryptr = new Cryptr(process.env.ENCRYPTION_KEY);
const fields = [
'name',
'content',
'username',
'displayName',
// 'channelName',
'openingMessage',
'description',
'value',
'placeholder',
'closedReason',
'topic',
'comment',
'label',
'regex',
];
const shouldEncrypt = ['create', 'createMany', 'update', 'updateMany', 'upsert'];
// const shouldDecrypt = ['findUnique', 'findFirst', 'findMany'];
module.exports = log => {
const encrypt = obj => {
for (const prop in obj) {
if (obj.hasOwnProperty(prop)) {
if (typeof obj[prop] === 'object') {
obj[prop] = encrypt(obj[prop]);
} else if (typeof obj[prop] === 'string' && obj[prop].length !== 0 && fields.includes(prop)) {
try {
obj[prop] = cryptr.encrypt(obj[prop]);
} catch (error) {
log.warn(`Failed to encrypt ${prop}`);
log.debug(error);
}
}
}
}
return obj;
};
const decrypt = obj => {
for (const prop in obj) {
if (obj.hasOwnProperty(prop)) {
if (typeof obj[prop] === 'object') {
obj[prop] = decrypt(obj[prop]);
} else if (typeof obj[prop] === 'string' && obj[prop].length !== 0 && fields.includes(prop)) {
try {
obj[prop] = cryptr.decrypt(obj[prop]);
} catch (error) {
log.warn(`Failed to decrypt ${prop}`);
log.debug(error);
}
}
}
}
return obj;
};
return async (params, next) => {
if (params.args.data && shouldEncrypt.includes(params.action)) params.args = encrypt(params.args);
let result = await next(params);
// if (result && shouldDecrypt.includes(params.action)) result = decrypt(result);
if (result) result = decrypt(result);
return result;
};
};

View File

@ -1,10 +1,14 @@
const { logAdminEvent } = require('../../../../../../lib/logging'); const { logAdminEvent } = require('../../../../../../lib/logging');
const { randomUUID } = require('crypto');
module.exports.delete = fastify => ({ module.exports.delete = fastify => ({
handler: async (req, res) => { handler: async (req, res) => {
/** @type {import('client')} */ /** @type {import('client')} */
const client = res.context.config.client; const client = res.context.config.client;
const guildId = req.params.guild;
const categoryId = Number(req.params.category); const categoryId = Number(req.params.category);
const original = req.params.category && await client.prisma.category.findUnique({ where: { id: categoryId } });
if (!original || original.guildId !== guildId) return res.status(404).send(new Error('Not Found'));
const category = await client.prisma.category.delete({ where: { id: categoryId } }); const category = await client.prisma.category.delete({ where: { id: categoryId } });
logAdminEvent(client, { logAdminEvent(client, {
@ -27,12 +31,13 @@ module.exports.get = fastify => ({
handler: async (req, res) => { handler: async (req, res) => {
/** @type {import('client')} */ /** @type {import('client')} */
const client = res.context.config.client; const client = res.context.config.client;
const guildId = req.params.guild;
const categoryId = Number(req.params.category); const categoryId = Number(req.params.category);
const category = await client.prisma.category.findUnique({ const category = await client.prisma.category.findUnique({
include: { include: {
questions: { questions: {
select: { select: {
createdAt: true, // createdAt: true,
id: true, id: true,
label: true, label: true,
maxLength: true, maxLength: true,
@ -45,9 +50,11 @@ module.exports.get = fastify => ({
}, },
}, },
}, },
where: { id: categoryId }, where: { id: categoryId },
}); });
if (!category || category.guildId !== guildId) return res.status(404).send(new Error('Not Found'));
return category; return category;
}, },
onRequest: [fastify.authenticate, fastify.isAdmin], onRequest: [fastify.authenticate, fastify.isAdmin],
@ -57,11 +64,13 @@ module.exports.patch = fastify => ({
handler: async (req, res) => { handler: async (req, res) => {
/** @type {import('client')} */ /** @type {import('client')} */
const client = res.context.config.client; const client = res.context.config.client;
const guildId = req.params.guild;
const categoryId = Number(req.params.category); const categoryId = Number(req.params.category);
const guild = client.guilds.cache.get(req.params.guild); const guild = client.guilds.cache.get(req.params.guild);
const data = req.body; const data = req.body;
const original = req.params.category && await client.prisma.category.findUnique({ where: { id: categoryId } }); const original = req.params.category && await client.prisma.category.findUnique({ where: { id: categoryId } });
if (!original) return res.status(404); if (!original || original.guildId !== guildId) return res.status(404).send(new Error('Not Found'));
if (data.hasOwnProperty('id')) delete data.id; if (data.hasOwnProperty('id')) delete data.id;
if (data.hasOwnProperty('createdAt')) delete data.createdAt; if (data.hasOwnProperty('createdAt')) delete data.createdAt;
@ -70,11 +79,14 @@ module.exports.patch = fastify => ({
data: { data: {
...data, ...data,
questions: { questions: {
upsert: data.questions?.map(q => ({ upsert: data.questions?.map(q => {
create: q, if (!q.id) q.id = randomUUID();
update: q, return {
where: { id: q.id }, create: q,
})), update: q,
where: { id: q.id },
};
}),
}, },
}, },
where: { id: categoryId }, where: { id: categoryId },

View File

@ -8,29 +8,27 @@ module.exports.get = fastify => ({
const client = res.context.config.client; const client = res.context.config.client;
const { categories } = await client.prisma.guild.findUnique({ const { categories } = await client.prisma.guild.findUnique({
select: { select: { categories: true },
categories: {
include: {
questions: {
select: {
createdAt: true,
id: true,
label: true,
maxLength: true,
minLength: true,
order: true,
placeholder: true,
required: true,
style: true,
value: true,
},
},
},
},
},
where: { id: req.params.guild }, where: { id: req.params.guild },
}); });
// include: {
// questions: {
// select: {
// createdAt: true,
// id: true,
// label: true,
// maxLength: true,
// minLength: true,
// order: true,
// placeholder: true,
// required: true,
// style: true,
// value: true,
// },
// },
// },
return categories; return categories;
}, },
onRequest: [fastify.authenticate, fastify.isAdmin], onRequest: [fastify.authenticate, fastify.isAdmin],

View File

@ -0,0 +1,32 @@
module.exports.get = fastify => ({
handler: async (req, res) => {
/** @type {import('client')} */
const client = res.context.config.client;
const id = req.params.guild;
const guild = client.guilds.cache.get(id);
const settings = await client.prisma.guild.findUnique({ where: { id } }) ??
await client.prisma.guild.create({ data: { id } });
const problems = [];
if (settings.logChannel) {
const permissions = guild.members.me.permissionsIn(settings.logChannel);
if (!permissions.has('SendMessages')) {
problems.push({
id: 'logChannelMissingPermission',
permission: 'SendMessages',
});
}
if (!permissions.has('EmbedLinks')) {
problems.push({
id: 'logChannelMissingPermission',
permission: 'EmbedLinks',
});
}
}
return problems;
},
onRequest: [fastify.authenticate, fastify.isAdmin],
});