This commit is contained in:
Bryan Ashby 2023-01-13 23:55:05 -07:00
parent 4841823d67
commit b075e25330
No known key found for this signature in database
GPG Key ID: C2C1B501E4EFD994
2 changed files with 43 additions and 83 deletions

View File

@ -10,15 +10,9 @@ exports.getJson = getJson;
exports.postJson = postJson; exports.postJson = postJson;
function getJson(url, options, cb) { function getJson(url, options, cb) {
const defaultOptions = { options = Object.assign({}, { method: 'GET' }, options);
method: 'GET',
};
options = Object.assign({}, defaultOptions, options); return _makeRequest(url, options, (err, body, res) => {
// :TODO: signing -- DRY this.
_makeRequest(url, options, (err, body, res) => {
if (err) { if (err) {
return cb(err); return cb(err);
} }
@ -39,26 +33,28 @@ function postJson(url, json, options, cb) {
json = JSON.stringify(json); json = JSON.stringify(json);
} }
// :TODO: DRY this method with _makeRequest() options = Object.assign({}, { method: 'POST', body: json }, options);
if (
const defaultOptions = { !options.headers ||
method: 'POST', !Object.keys(options.headers).find(h => h.toLowerCase() === 'content-type')
body: json, ) {
headers: { options.headers['Content-Type'] = 'application/json';
'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.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 => { const req = https.request(url, options, res => {
let body = []; let body = [];
@ -73,9 +69,7 @@ function postJson(url, json, options, cb) {
return cb( return cb(
Errors.HttpError( Errors.HttpError(
`HTTP error ${res.statusCode}: ${truncate(body, { length: 128 })}` `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')); 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(); req.end();
} }

View File

@ -53,7 +53,10 @@ exports.getModule = class ActivityPubWebHandler extends WebHandlerModule {
this.webServer.addRoute({ this.webServer.addRoute({
method: 'GET', method: 'GET',
path: /^\/_enig\/ap\/users\/.+\/outbox$/, path: /^\/_enig\/ap\/users\/.+\/outbox$/,
handler: this._outboxGetHandler.bind(this), handler: this._enforceSigningPolicy.bind(
this,
this._outboxGetHandler.bind(this)
),
}); });
this.webServer.addRoute({ this.webServer.addRoute({
@ -72,6 +75,16 @@ exports.getModule = class ActivityPubWebHandler extends WebHandlerModule {
return cb(null); 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) { _selfUrlRequestHandler(req, resp) {
this.log.trace({ url: req.url }, 'Request for "self"'); this.log.trace({ url: req.url }, 'Request for "self"');
@ -295,9 +308,8 @@ exports.getModule = class ActivityPubWebHandler extends WebHandlerModule {
return keyId.endsWith('#main-key'); return keyId.endsWith('#main-key');
} }
_inboxFollowRequestHandler(activity, user, resp) { _inboxFollowRequestHandler(activity, remoteActor, user, resp) {
this.log.debug({ user: user, type: activity.type }, 'Got a follow request!'); this.log.debug({ user_id: user.userId, actor: activity.actor }, 'Follow request');
// :TODO: return OK and kick off a async job of persisting and sending and 'Accepted'
// //
// If the user blindly accepts Followers, we can persist // 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 // request for the user to review and decide what to do with
// at a later time. // at a later time.
// //
// :TODO: Implement the queue
const activityPubSettings = ActivityPubSettings.fromUser(user); const activityPubSettings = ActivityPubSettings.fromUser(user);
if (!activityPubSettings.manuallyApproveFollowers) { 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' }); resp.writeHead(200, { 'Content-Type': 'text/html' });
@ -358,7 +371,7 @@ exports.getModule = class ActivityPubWebHandler extends WebHandlerModule {
return this.webServer.accessDenied(resp); return this.webServer.accessDenied(resp);
} }
return activityHandler(activity, user, resp); return activityHandler(activity, actor, user, resp);
}); });
}); });
} }