From b075e25330ff95f4f795b202862af695e142edd5 Mon Sep 17 00:00:00 2001 From: Bryan Ashby Date: Fri, 13 Jan 2023 23:55:05 -0700 Subject: [PATCH] Cleanup --- core/http_util.js | 99 +++++-------------- .../content/web_handlers/activitypub.js | 27 +++-- 2 files changed, 43 insertions(+), 83 deletions(-) diff --git a/core/http_util.js b/core/http_util.js index e1d0733b..5458198b 100644 --- a/core/http_util.js +++ b/core/http_util.js @@ -10,15 +10,9 @@ exports.getJson = getJson; exports.postJson = postJson; function getJson(url, options, cb) { - const defaultOptions = { - method: 'GET', - }; + options = Object.assign({}, { method: 'GET' }, options); - options = Object.assign({}, defaultOptions, options); - - // :TODO: signing -- DRY this. - - _makeRequest(url, options, (err, body, res) => { + return _makeRequest(url, options, (err, body, res) => { if (err) { return cb(err); } @@ -39,26 +33,28 @@ function postJson(url, json, options, cb) { json = JSON.stringify(json); } - // :TODO: DRY this method with _makeRequest() - - const defaultOptions = { - method: 'POST', - body: json, - headers: { - 'Content-Type': 'application/json', - }, - }; - - options = Object.assign({}, defaultOptions, options); - - if (options?.sign?.headers?.includes('digest')) { - options.headers['Digest'] = `SHA-256=${crypto - .createHash('sha256') - .update(json) - .digest('base64')}`; + options = Object.assign({}, { method: 'POST', body: json }, options); + if ( + !options.headers || + !Object.keys(options.headers).find(h => h.toLowerCase() === 'content-type') + ) { + options.headers['Content-Type'] = 'application/json'; } - options.headers['Content-Length'] = json.length; + return _makeRequest(url, options, cb); +} + +function _makeRequest(url, options, cb) { + if (options.body) { + options.headers['Content-Length'] = options.body.length; + + if (options?.sign?.headers?.includes('digest')) { + options.headers['Digest'] = `SHA-256=${crypto + .createHash('sha256') + .update(options.body) + .digest('base64')}`; + } + } const req = https.request(url, options, res => { let body = []; @@ -73,9 +69,7 @@ function postJson(url, json, options, cb) { return cb( Errors.HttpError( `HTTP error ${res.statusCode}: ${truncate(body, { length: 128 })}` - ), - body, - res + ) ); } @@ -101,52 +95,5 @@ function postJson(url, json, options, cb) { return cb(Errors.Timeout('Timeout making HTTP request')); }); - req.write(json); - req.end(); -} - -function _makeRequest(url, options, cb) { - if (options.body && options?.sign?.headers?.includes('digest')) { - options.headers['Digest'] = `SHA-256=${crypto - .createHash('sha256') - .update(options.body) - .digest('base64')}`; - } - - const req = https.request(url, options, res => { - let body = []; - res.on('data', d => { - body.push(d); - }); - - res.on('end', () => { - body = Buffer.concat(body).toString(); - - if (res.statusCode < 200 || res.statusCode > 299) { - return cb(Errors.HttpError(`HTTP error ${res.statusCode}: ${body}`)); - } - - return cb(null, body, res); - }); - }); - - if (isObject(options.sign)) { - try { - httpSignature.sign(req, options.sign); - } catch (e) { - req.destroy(); - return cb(Errors.Invalid(`Invalid signing material: ${e}`)); - } - } - - req.on('error', err => { - return cb(err); - }); - - req.on('timeout', () => { - req.destroy(); - return cb(Errors.Timeout('Timeout making HTTP request')); - }); - req.end(); } diff --git a/core/servers/content/web_handlers/activitypub.js b/core/servers/content/web_handlers/activitypub.js index 4f61616b..b98951be 100644 --- a/core/servers/content/web_handlers/activitypub.js +++ b/core/servers/content/web_handlers/activitypub.js @@ -53,7 +53,10 @@ exports.getModule = class ActivityPubWebHandler extends WebHandlerModule { this.webServer.addRoute({ method: 'GET', path: /^\/_enig\/ap\/users\/.+\/outbox$/, - handler: this._outboxGetHandler.bind(this), + handler: this._enforceSigningPolicy.bind( + this, + this._outboxGetHandler.bind(this) + ), }); this.webServer.addRoute({ @@ -72,6 +75,16 @@ exports.getModule = class ActivityPubWebHandler extends WebHandlerModule { return cb(null); } + _enforceSigningPolicy(req, resp, next) { + // the request must be signed, and the signature must be valid + const signature = this._parseAndValidateSignature(req); + if (!signature) { + return this.webServer.accessDenied(resp); + } + + return next(req, resp); + } + _selfUrlRequestHandler(req, resp) { this.log.trace({ url: req.url }, 'Request for "self"'); @@ -295,9 +308,8 @@ exports.getModule = class ActivityPubWebHandler extends WebHandlerModule { return keyId.endsWith('#main-key'); } - _inboxFollowRequestHandler(activity, user, resp) { - this.log.debug({ user: user, type: activity.type }, 'Got a follow request!'); - // :TODO: return OK and kick off a async job of persisting and sending and 'Accepted' + _inboxFollowRequestHandler(activity, remoteActor, user, resp) { + this.log.debug({ user_id: user.userId, actor: activity.actor }, 'Follow request'); // // If the user blindly accepts Followers, we can persist @@ -305,10 +317,11 @@ exports.getModule = class ActivityPubWebHandler extends WebHandlerModule { // request for the user to review and decide what to do with // at a later time. // - // :TODO: Implement the queue const activityPubSettings = ActivityPubSettings.fromUser(user); if (!activityPubSettings.manuallyApproveFollowers) { - this._recordAcceptedFollowRequest(user, activity.actor, activity); + this._recordAcceptedFollowRequest(user, remoteActor, activity); + } else { + // :TODO: queue the request } resp.writeHead(200, { 'Content-Type': 'text/html' }); @@ -358,7 +371,7 @@ exports.getModule = class ActivityPubWebHandler extends WebHandlerModule { return this.webServer.accessDenied(resp); } - return activityHandler(activity, user, resp); + return activityHandler(activity, actor, user, resp); }); }); }