Skip to content

Commit

Permalink
no longer encrypt quote column (#40)
Browse files Browse the repository at this point in the history
  • Loading branch information
AlecM33 authored Nov 24, 2024
1 parent 9e9a64e commit b2fdf8c
Show file tree
Hide file tree
Showing 5 changed files with 26 additions and 36 deletions.
10 changes: 4 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,13 @@

The bot is written in javascript and makes heavy use of [discord.js](https://discord.js.org/#/), which is a wrapper around the Discord API (view the [developer documentation](https://discord.com/developers/docs/intro)).

I currently host the bot using the [Heroku Cloud Platform](https://heroku.com). The bot speaks to a PostgreSQL instance hosted for free on [ElephantSQL](https://www.elephantsql.com/), where I store quotes from the few servers where the bot operates. The bot's connection to the database is SSL-enabled. The "quotation" column is encrypted.
I currently host the bot using the [Heroku Cloud Platform](https://heroku.com). The bot speaks to a PostgreSQL instance hosted for free using [Aiven](https://aiven.io/).

# Running your own instance of the bot

Stored quotes are associated with a specific guild, so you can safely add the same instance of the bot to multiple servers.

For the bot to run, you need to populate 4 environment variables referenced within the code. These are `process.env.CLIENT_ID`, `process.env.DATABASE_URL`, `process.env.TOKEN`, and `process.env.ENCRYPTION_KEY`. Read below for how to obtain these.
For the bot to run, you need to populate 3 environment variables referenced within the code. These are `process.env.CLIENT_ID`, `process.env.DATABASE_URL`, and `process.env.TOKEN`. Read below for how to obtain these.

1. You'll need to [create a discord application through the developer portal](https://discord.com/developers/applications). Your application will be assigned an "Application ID" (aka client id), which can be referenced in the "General Information" section. This will be the value of `process.env.CLIENT_ID`.

Expand All @@ -41,10 +41,8 @@ For the bot to run, you need to populate 4 environment variables referenced with

`postgres://your-username:your-password@your-host:your-port/your-database-name`.

4. You'll need to populate the database with the appropriate schema. See the "Database Schema" section.

5. You'll need to set `process.env.ENCRYPTION_KEY` to a very strong password. This is used to encrypt several columns.

4. You'll need to populate the database with the appropriate schema. See the "Database Schema" section.

5. You'll need to add the bot to your desired server. Within the "Bot" section of your Discord application, you can create a permissions integer that will be added to the OAuth link for a given bot. I recommend, at minimum, the "Send Messages", "Send Messages in Threads", and "Use Slash Commands" permissions, which produce integer `277025392640`. The link to add the bot would then be the following, with your application ID substituted in:

`https://discord.com/oauth2/authorize?client_id=your-application-id&permissions=277025392640&scope=bot`
Expand Down
38 changes: 15 additions & 23 deletions database/queries.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
const pool = require('./db');
const encryptionKey = process.env.ENCRYPTION_KEY?.trim();

module.exports = {

fetchAllQuotes: (guildId) => {
return query({
text: `SELECT
id,
PGP_SYM_DECRYPT(quotation::bytea, $1) as quotation,
quotation,
author,
said_at
FROM quotes WHERE guild_id = $2;`,
values: [encryptionKey, guildId]
FROM quotes WHERE guild_id = $1;`,
values: [guildId]
});
},

Expand All @@ -27,24 +26,22 @@ module.exports = {
return query({
text: `INSERT INTO quotes VALUES (
DEFAULT,
PGP_SYM_ENCRYPT($1, $2)::text,
$1,
$2,
$3,
$4,
$5,
digest($6, 'sha256')
digest($5, 'sha256')
) RETURNING
id,
PGP_SYM_DECRYPT(quotation::bytea, $7) as quotation,
quotation,
author,
said_at;`,
values: [
quote,
encryptionKey,
author,
saidAt,
guildId,
guildId.toLowerCase() + quote.toLowerCase() + author.toLowerCase(),
encryptionKey
guildId.toLowerCase() + quote.toLowerCase() + author.toLowerCase()
]
});
},
Expand All @@ -62,12 +59,11 @@ module.exports = {
return query({
text: `SELECT
id,
PGP_SYM_DECRYPT(quotation::bytea, $1) as quotation,
quotation,
author,
said_at
FROM quotes WHERE author = $2 AND guild_id = $3;`,
values: [
encryptionKey,
author,
guildId
]
Expand All @@ -92,14 +88,12 @@ module.exports = {
return query({
text: `SELECT
id,
PGP_SYM_DECRYPT(quotation::bytea, $1) as quotation,
quotation,
author,
said_at FROM quotes
WHERE author = $2 AND LOWER(PGP_SYM_DECRYPT(quotation::bytea, $3)) LIKE LOWER($4) AND guild_id = $5;`,
WHERE author = $1 AND LOWER(quotation) LIKE LOWER($2) AND guild_id = $3;`,
values: [
encryptionKey,
author,
encryptionKey,
'%' + searchString + '%',
guildId
]
Expand All @@ -110,13 +104,11 @@ module.exports = {
return query({
text: `SELECT
id,
PGP_SYM_DECRYPT(quotation::bytea, $1) as quotation,
quotation,
author,
said_at FROM quotes
WHERE LOWER(PGP_SYM_DECRYPT(quotation::bytea, $2)) LIKE LOWER($3) AND guild_id = $4;`,
WHERE LOWER(quotation) LIKE LOWER($1) AND guild_id = $2;`,
values: [
encryptionKey,
encryptionKey,
'%' + searchString + '%',
guildId
]
Expand All @@ -127,10 +119,10 @@ module.exports = {
return query({
text: `DELETE FROM quotes WHERE id = $1 AND guild_id = $2 RETURNING
id,
PGP_SYM_DECRYPT(quotation::bytea, $3) as quotation,
quotation,
author,
said_at;`,
values: [id, guildId, encryptionKey]
values: [id, guildId]
});
}

Expand Down
4 changes: 2 additions & 2 deletions database/schema.sql
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
CREATE EXTENSION IF NOT EXISTS pgcrypto;
CREATE EXTENSION IF NOT EXISTS citext;

CREATE COLLATION ci (
Expand All @@ -13,5 +12,6 @@ CREATE TABLE quotes(
author character varying(280) COLLATE ci NOT NULL,
said_at date NOT NULL,
guild_id character varying(64) NOT NULL,
hash text NOT NULL PRIMARY KEY
hash text NOT NULL PRIMARY KEY,
created_at timestamp with time zone DEFAULT current_timestamp
);
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"main": "main.js",
"scripts": {
"test": "jasmine",
"test:db": "SET NODE_ENV=development && SET DATABASE_URL=<your-database-url> && SET ENCRYPTION_KEY=<your-test-key> && node ./spec/local-database-test.js",
"test:db": "SET NODE_ENV=development && SET DATABASE_URL=<your-database-url> && node ./spec/local-database-test.js",
"start": "NODE_ENV=production && node deploy-commands.js && node main.js",
"start:dev": "SET NODE_ENV=development && SET DATABASE_URL=<your-url-here> && SET TOKEN=<your-token-here> && SET CLIENT_ID=<your-client-id-here> && node deploy-commands.js && node main.js"
},
Expand Down
8 changes: 4 additions & 4 deletions spec/local-database-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ queries.addQuote('test', 'jane', '1').then((addResult) => {
console.log(addResult);
const addedQuoteId = addResult[0].id;
verify('quote was added to database', addResult.length > 0);
verify('query returned unencrypted quote', addResult.length > 0 && addResult[0].quotation && addResult[0].quotation === 'test');
verify('query returned unencrypted author', addResult.length > 0 && addResult[0].author && addResult[0].author === 'jane');
verify('query returned added quote', addResult.length > 0 && addResult[0].quotation && addResult[0].quotation === 'test');
verify('query returned added author', addResult.length > 0 && addResult[0].author && addResult[0].author === 'jane');
Promise.all(
[
queries.fetchAllQuotes('1'),
Expand All @@ -31,11 +31,11 @@ queries.addQuote('test', 'jane', '1').then((addResult) => {
console.log(deletionResult);
verify('the quote was deleted', deletionResult.length > 0);
verify(
'deletion returned unencrypted quote',
'deletion returned deleted quote',
deletionResult.length > 0 && deletionResult[0].quotation && deletionResult[0].quotation === 'test'
);
verify(
'deletion returned unencrypted author',
'deletion returned deleted author',
deletionResult.length > 0 && deletionResult[0].author && deletionResult[0].author === 'jane'
);
console.log('complete.');
Expand Down

0 comments on commit b2fdf8c

Please sign in to comment.