Write out queue_worker for verify_inbox, plumb necessary data.

This commit is contained in:
Andrew Pietila 2023-04-14 00:06:52 -05:00
parent f71c7c36fc
commit c7cfef88ae
6 changed files with 161 additions and 11 deletions

17
app.js
View file

@ -5,9 +5,26 @@ const glob = require('glob');
const log4js = require('log4js');
const { match: createPathMatch } = require('path-to-regexp');
const log = require('./lib/log');
var bodyParser = require('body-parser');
(async () => {
const app = express();
app.use(bodyParser.json({ type: 'application/*+json',
verify: function (req, _res, buf, _encoding) {
req.rawBody = buf;
}
}));
app.use(bodyParser.json({
verify: function (req, _res, buf, _encoding) {
req.rawBody = buf;
}
}));
app.use(bodyParser.urlencoded({
extended: false,
verify: function (req, _res, buf, _encoding) {
req.rawBody = buf;
}
}));
const routes = await glob('**/*.js', {
cwd: './routes',
dot: true,

75
lib/queue_worker.js Normal file
View file

@ -0,0 +1,75 @@
'use strict';
var db = require('./db');
const http_agent = require('./http_agent');
const jsonld = require('jsonld');
var crypto = require('crypto');
module.exports = {
do_one_queue: async () => {
var result = await db('queue').orderBy('id', 'asc').limit(1);
if ( result[0] ) {
await db('queue').del().where('id', '=', result[0]["id"]);
if ( result[0]["type"] === "verify_inbox" ) {
var data = JSON.parse(result[0]["data"]);
var sig_header = data.sig_header;
var signature_split = sig_header.split(/,/);
var signature_elements = {};
signature_split.forEach((obj) => {
signature_elements[obj.split('"')[1].split(/=/)[0]] = obj.split('"')[1].split(/=/)[1];
});
var keyUrl = new URL(signature_elements["keyId"]);
var secondKeyUrl = new URL(keyUrl);
secondKeyUrl.hash = undefined;
var actor = (await db("object_box").where("origin", secondKeyUrl.toString()).andWhere("expires", ">=", (new Date()).getTime()))[0]["object"];
if ( !actor ) {
actor = http_agent.request(secondKeyUrl, {actor: data["actor"]})["data"];
await db("object_box").insert({
origin: secondKeyUrl.toString(),
to: null,
cc: null,
object: actor,
signedby: "fetch",
// TODO: We should respect the caching instructions provided by origin.
expires: (new Date()).getTime() + 604800000 // 7 days.
});
}
// TODO: Should we standardize on a primary schema at some point?
var actor_parsed = await jsonld.compact(JSON.parse(actor), ["https://www.w3.org/ns/activitystreams", { security: "https://w3id.org/security#"}]);
var publicKey = "";
if ( Array.isArray(actor_parsed["security:publicKey"]) ) {
actor_parsed["security:publicKey"].forEach((value) => {
if ( value["id"] === keyUrl.toString() ) {
publicKey = value["security:publicKeyPem"];
}
});
} else {
publicKey = actor_parsed["security:publicKey"]["security:publicKeyPem"];
}
var valid = crypto.verify("RSA-SHA256", data["body"], publicKey, signature_elements["signature"]);
var parsedBody = await jsonld.compact(JSON.parse(data["body"]), ["https://www.w3.org/ns/activitystreams", { security: "https://w3id.org/security#"}]);
var to = parsedBody.to;
if ( typeof to === "string" ) {
to = [to];
}
var cc = parsedBody.cc;
if ( typeof cc === "string" ) {
cc = [cc];
}
if ( valid ) {
// TODO: We should care that the key hostname and the object hostname match.
await db("object_box").insert({
origin: parsedBody.id,
to,
cc,
object: parsedBody,
signedby: keyUrl.toString(),
expires: (new Date()).getTime() + 604800000 // 7 days.
});
} else {
await db("queue").insert({task: "fetch_object", data: JSON.stringify({object: parsedBody.id, actor: data["actor"]})});
}
}
}
}
};

View file

@ -0,0 +1,19 @@
/* eslint-disable jsdoc/valid-types */
// eslint doesn't seem to understand, but vscode does.
'use strict';
/**
* @param { import("knex").Knex } knex
* @returns { Promise<void> }
*/
exports.up = function(knex) {
return knex.schema.renameTable("inbox", "object_box");
};
/**
* @param { import("knex").Knex } knex
* @returns { Promise<void> }
*/
exports.down = function(knex) {
return knex.schema.renameTable("object_box", "inbox");
};

54
package-lock.json generated
View file

@ -9,6 +9,7 @@
"version": "0.0.1",
"license": "WTFPL",
"dependencies": {
"body-parser": "^1.20.2",
"express": "^4.18.2",
"glob": "^9.3.0",
"jsonld": "^8.1.1",
@ -439,12 +440,12 @@
}
},
"node_modules/body-parser": {
"version": "1.20.1",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz",
"integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==",
"version": "1.20.2",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz",
"integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==",
"dependencies": {
"bytes": "3.1.2",
"content-type": "~1.0.4",
"content-type": "~1.0.5",
"debug": "2.6.9",
"depd": "2.0.0",
"destroy": "1.2.0",
@ -452,7 +453,7 @@
"iconv-lite": "0.4.24",
"on-finished": "2.4.1",
"qs": "6.11.0",
"raw-body": "2.5.1",
"raw-body": "2.5.2",
"type-is": "~1.6.18",
"unpipe": "1.0.0"
},
@ -1138,11 +1139,48 @@
"node": ">= 0.10.0"
}
},
"node_modules/express/node_modules/body-parser": {
"version": "1.20.1",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz",
"integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==",
"dependencies": {
"bytes": "3.1.2",
"content-type": "~1.0.4",
"debug": "2.6.9",
"depd": "2.0.0",
"destroy": "1.2.0",
"http-errors": "2.0.0",
"iconv-lite": "0.4.24",
"on-finished": "2.4.1",
"qs": "6.11.0",
"raw-body": "2.5.1",
"type-is": "~1.6.18",
"unpipe": "1.0.0"
},
"engines": {
"node": ">= 0.8",
"npm": "1.2.8000 || >= 1.4.16"
}
},
"node_modules/express/node_modules/path-to-regexp": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
"integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ=="
},
"node_modules/express/node_modules/raw-body": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
"integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==",
"dependencies": {
"bytes": "3.1.2",
"http-errors": "2.0.0",
"iconv-lite": "0.4.24",
"unpipe": "1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
@ -2522,9 +2560,9 @@
}
},
"node_modules/raw-body": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
"integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==",
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
"integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==",
"dependencies": {
"bytes": "3.1.2",
"http-errors": "2.0.0",

View file

@ -13,6 +13,7 @@
"author": "Andrew Pietila <a.pietila@protonmail.com>",
"license": "WTFPL",
"dependencies": {
"body-parser": "^1.20.2",
"express": "^4.18.2",
"glob": "^9.3.0",
"jsonld": "^8.1.1",

View file

@ -19,7 +19,7 @@ module.exports = {
var signature_split = req.header("signature").split(/,/);
var signature_elements = {};
signature_split.forEach((obj) => {
signature_elements[obj.split(/=/)[0]] = obj.split(/=/)[1];
signature_elements[obj.split('"')[1].split(/=/)[0]] = obj.split('"')[1].split(/=/)[1];
});
var headers_to_check = signature_elements["headers"].split(/ /);
if ( headers_to_check.includes("host") && headers_to_check.includes("date") && headers_to_check.includes("digest") && headers_to_check.includes("(request-target)")) {
@ -35,7 +35,7 @@ module.exports = {
}
}).join('\n');
// ... then lets dump this in the queue for now.
await db("queue").insert({task: "verify_inbox", data: JSON.stringify({sig_header: req.header("signature"), signed_block, body: req.body, date: (new Date()).toISOString()})});
await db("queue").insert({task: "verify_inbox", data: JSON.stringify({sig_header: req.header("signature"), signed_block, body: req.rawBody.toString("UTF-8"), date: (new Date()).toISOString(), actor: `${req.hostname}@${req.hostname}`})});
res.status(204);
return res.end();
}
@ -44,7 +44,7 @@ module.exports = {
if ( !take_at_face_value ) {
// 2. If the signature is not valid, or non-existant, then we fetch the resource manually.
var bodyParsed = await jsonld.compact(req.body, 'https://www.w3.org/ns/activitystreams');
await db("queue").insert({task: "fetch_object", data: bodyParsed.id});
await db("queue").insert({task: "fetch_object", data: JSON.stringify({object: bodyParsed.id, actor: `${req.hostname}@${req.hostname}`})});
res.status(204);
return res.end();
}