pull/32851/merge
Emelia Smith 2025-01-08 17:07:05 +00:00 committed by GitHub
commit 021547c5fc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 107 additions and 21 deletions

View File

@ -2,6 +2,7 @@
import fs from 'node:fs';
import http from 'node:http';
import net from 'node:net';
import path from 'node:path';
import url from 'node:url';
@ -9,6 +10,7 @@ import cors from 'cors';
import dotenv from 'dotenv';
import express from 'express';
import { JSDOM } from 'jsdom';
import proxyaddr from 'proxy-addr';
import { WebSocketServer } from 'ws';
import * as Database from './database.js';
@ -146,8 +148,13 @@ const startServer = async () => {
});
const app = express();
const trustProxy = proxyaddr.compile(
process.env.TRUSTED_PROXY_IP ?
process.env.TRUSTED_PROXY_IP.split(/(?:\s*,\s*|\s+)/) :
['loopback', 'uniquelocal']
);
app.set('trust proxy', process.env.TRUSTED_PROXY_IP ? process.env.TRUSTED_PROXY_IP.split(/(?:\s*,\s*|\s+)/) : 'loopback,uniquelocal');
app.set('trust proxy', trustProxy);
app.use(httpLogger);
app.use(cors());
@ -161,6 +168,15 @@ const startServer = async () => {
// logger. This decorates the `request` object.
attachWebsocketHttpLogger(request);
// Define the `request.ip` property
Object.defineProperty(request, 'ip', {
configurable: true,
enumerable: true,
get() {
return proxyaddr(this, trustProxy);
}
});
request.log.info("HTTP Upgrade Requested");
/** @param {Error} err */
@ -349,28 +365,53 @@ const startServer = async () => {
const isInScope = (req, necessaryScopes) =>
req.scopes.some(scope => necessaryScopes.includes(scope));
const ACCESS_TOKEN_UPDATE_FREQUENCY = 24 * 60 * 60 * 1000;
/**
* Fetches the account from the access token, updating access token usage if necessary.
* `req.ip` comes from `proxyaddr`
* @param {string} token
* @param {any} req
* @param {http.IncomingMessage & { ip: string } & ResolvedAccount} req
* @returns {Promise<ResolvedAccount>}
*/
const accountFromToken = async (token, req) => {
const result = await pgPool.query('SELECT oauth_access_tokens.id, oauth_access_tokens.resource_owner_id, users.account_id, users.chosen_languages, oauth_access_tokens.scopes FROM oauth_access_tokens INNER JOIN users ON oauth_access_tokens.resource_owner_id = users.id WHERE oauth_access_tokens.token = $1 AND oauth_access_tokens.revoked_at IS NULL LIMIT 1', [token]);
const result = await pgPool.query('SELECT oauth_access_tokens.id, oauth_access_tokens.resource_owner_id, users.account_id, users.chosen_languages, oauth_access_tokens.scopes, oauth_access_tokens.last_used_at FROM oauth_access_tokens INNER JOIN users ON oauth_access_tokens.resource_owner_id = users.id WHERE oauth_access_tokens.token = $1 AND oauth_access_tokens.revoked_at IS NULL LIMIT 1', [token]);
if (result.rows.length === 0) {
if (result.rows.length === 0 || result.rows.length > 1) {
throw new AuthenticationError('Invalid access token');
}
req.accessTokenId = result.rows[0].id;
req.scopes = result.rows[0].scopes.split(' ');
req.accountId = result.rows[0].account_id;
req.chosenLanguages = result.rows[0].chosen_languages;
const accessToken = result.rows[0];
// Track the usage of the access token if necessary:
// This is the same code as: app/controllers/concerns/api/access_token_tracking_concern.rb
if (accessToken.last_used_at === null || accessToken.last_used_at < Date.now() - ACCESS_TOKEN_UPDATE_FREQUENCY) {
let query, variables = [];
if (req.ip && net.isIP(req.ip)) {
query = 'UPDATE "oauth_access_tokens" SET "last_used_at" = $2, "last_used_ip" = $3 WHERE "oauth_access_tokens"."id" = $1';
variables = [ accessToken.id, new Date(), req.ip ];
} else {
query = 'UPDATE "oauth_access_tokens" SET "last_used_at" = $2 WHERE "oauth_access_tokens"."id" = $1';
variables = [ accessToken.id, new Date() ];
}
try {
await pgPool.query(query, variables);
} catch (err) {
req.log.error(err, 'Error updating Access Token usage tracking');
}
}
req.accessTokenId = accessToken.id;
req.scopes = accessToken.scopes.split(' ');
req.accountId = accessToken.account_id;
req.chosenLanguages = accessToken.chosen_languages;
return {
accessTokenId: result.rows[0].id,
scopes: result.rows[0].scopes.split(' '),
accountId: result.rows[0].account_id,
chosenLanguages: result.rows[0].chosen_languages,
accessTokenId: accessToken.id,
scopes: accessToken.scopes.split(' '),
accountId: accessToken.account_id,
chosenLanguages: accessToken.chosen_languages,
};
};

View File

@ -27,6 +27,7 @@
"pino": "^9.0.0",
"pino-http": "^10.0.0",
"prom-client": "^15.0.0",
"proxy-addr": "~2.0.7",
"uuid": "^11.0.0",
"ws": "^8.12.1"
},
@ -34,11 +35,13 @@
"@types/cors": "^2.8.16",
"@types/express": "^4.17.17",
"@types/pg": "^8.6.6",
"@types/proxy-addr": "^2.0.3",
"@types/uuid": "^10.0.0",
"@types/ws": "^8.5.9",
"eslint-define-config": "^2.0.0",
"pino-pretty": "^11.0.0",
"typescript": "^5.0.4"
"typescript": "^5.0.4",
"wscat": "^6.0.1"
},
"optionalDependencies": {
"bufferutil": "^4.0.7",

View File

@ -3053,6 +3053,7 @@ __metadata:
"@types/cors": "npm:^2.8.16"
"@types/express": "npm:^4.17.17"
"@types/pg": "npm:^8.6.6"
"@types/proxy-addr": "npm:^2.0.3"
"@types/uuid": "npm:^10.0.0"
"@types/ws": "npm:^8.5.9"
bufferutil: "npm:^4.0.7"
@ -3068,10 +3069,12 @@ __metadata:
pino-http: "npm:^10.0.0"
pino-pretty: "npm:^11.0.0"
prom-client: "npm:^15.0.0"
proxy-addr: "npm:~2.0.7"
typescript: "npm:^5.0.4"
utf-8-validate: "npm:^6.0.3"
uuid: "npm:^11.0.0"
ws: "npm:^8.12.1"
wscat: "npm:^6.0.1"
dependenciesMeta:
bufferutil:
optional: true
@ -4073,6 +4076,15 @@ __metadata:
languageName: node
linkType: hard
"@types/proxy-addr@npm:^2.0.3":
version: 2.0.3
resolution: "@types/proxy-addr@npm:2.0.3"
dependencies:
"@types/node": "npm:*"
checksum: 10c0/680f5eeaf434461daf856d694fd6b228c9850f736f377b8fc1d373ba282feca25bc642eeed93f3a9511d9406a9b93014b94c4d7cb8488646482ca0610d931e30
languageName: node
linkType: hard
"@types/punycode@npm:^2.1.0":
version: 2.1.4
resolution: "@types/punycode@npm:2.1.4"
@ -6402,6 +6414,13 @@ __metadata:
languageName: node
linkType: hard
"commander@npm:^12.1.0, commander@npm:~12.1.0":
version: 12.1.0
resolution: "commander@npm:12.1.0"
checksum: 10c0/6e1996680c083b3b897bfc1cfe1c58dfbcd9842fd43e1aaf8a795fbc237f65efcc860a3ef457b318e73f29a4f4a28f6403c3d653d021d960e4632dd45bde54a9
languageName: node
linkType: hard
"commander@npm:^2.20.0":
version: 2.20.3
resolution: "commander@npm:2.20.3"
@ -6416,13 +6435,6 @@ __metadata:
languageName: node
linkType: hard
"commander@npm:~12.1.0":
version: 12.1.0
resolution: "commander@npm:12.1.0"
checksum: 10c0/6e1996680c083b3b897bfc1cfe1c58dfbcd9842fd43e1aaf8a795fbc237f65efcc860a3ef457b318e73f29a4f4a28f6403c3d653d021d960e4632dd45bde54a9
languageName: node
linkType: hard
"comment-parser@npm:1.4.1":
version: 1.4.1
resolution: "comment-parser@npm:1.4.1"
@ -12390,6 +12402,13 @@ __metadata:
languageName: node
linkType: hard
"mute-stream@npm:^2.0.0":
version: 2.0.0
resolution: "mute-stream@npm:2.0.0"
checksum: 10c0/2cf48a2087175c60c8dcdbc619908b49c07f7adcfc37d29236b0c5c612d6204f789104c98cc44d38acab7b3c96f4a3ec2cfdc4934d0738d876dbefa2a12c69f4
languageName: node
linkType: hard
"nan@npm:^2.12.1":
version: 2.17.0
resolution: "nan@npm:2.17.0"
@ -15060,6 +15079,15 @@ __metadata:
languageName: node
linkType: hard
"read@npm:^4.0.0":
version: 4.0.0
resolution: "read@npm:4.0.0"
dependencies:
mute-stream: "npm:^2.0.0"
checksum: 10c0/448dd2cb8163fa7004dbe9e7fc9b0814cedd55028e2d45fbebd774f6b05e3ac046b092f3910a4eff942471187afa0b56b5db6caf2cd230d264d8d8fe22f9af6f
languageName: node
linkType: hard
"readable-stream@npm:^2.0.1, readable-stream@npm:^2.0.2, readable-stream@npm:^2.3.3, readable-stream@npm:^2.3.6":
version: 2.3.8
resolution: "readable-stream@npm:2.3.8"
@ -18777,7 +18805,7 @@ __metadata:
languageName: node
linkType: hard
"ws@npm:^8.11.0, ws@npm:^8.12.1, ws@npm:^8.18.0":
"ws@npm:^8.0.0, ws@npm:^8.11.0, ws@npm:^8.12.1, ws@npm:^8.18.0":
version: 8.18.0
resolution: "ws@npm:8.18.0"
peerDependencies:
@ -18792,6 +18820,20 @@ __metadata:
languageName: node
linkType: hard
"wscat@npm:^6.0.1":
version: 6.0.1
resolution: "wscat@npm:6.0.1"
dependencies:
commander: "npm:^12.1.0"
https-proxy-agent: "npm:^7.0.5"
read: "npm:^4.0.0"
ws: "npm:^8.0.0"
bin:
wscat: bin/wscat
checksum: 10c0/8245b6cdebb6bde8bc6bf647991d289107bdbda24f51cf820683f40a06b72226e606fbc8d263e4812018928315200377e615035918399bdcaca8b0b0182ecbd1
languageName: node
linkType: hard
"xml-name-validator@npm:^4.0.0":
version: 4.0.0
resolution: "xml-name-validator@npm:4.0.0"