Added HTTP util: postJson(), post Accept Activity to server with signing
This commit is contained in:
parent
3a70cc6939
commit
44c67f5327
|
@ -1,4 +1,3 @@
|
||||||
const { selfUrl } = require('./activitypub_util');
|
|
||||||
const UserProps = require('./user_property');
|
const UserProps = require('./user_property');
|
||||||
|
|
||||||
module.exports = class ActivityPubSettings {
|
module.exports = class ActivityPubSettings {
|
||||||
|
|
|
@ -42,7 +42,7 @@ exports.Errors = {
|
||||||
UnexpectedState: (reason, reasonCode) =>
|
UnexpectedState: (reason, reasonCode) =>
|
||||||
new EnigError('Unexpected state', -32007, reason, reasonCode),
|
new EnigError('Unexpected state', -32007, reason, reasonCode),
|
||||||
MissingParam: (reason, reasonCode) =>
|
MissingParam: (reason, reasonCode) =>
|
||||||
new EnigError('Missing paramter(s)', -32008, reason, reasonCode),
|
new EnigError('Missing parameter(s)', -32008, reason, reasonCode),
|
||||||
MissingMci: (reason, reasonCode) =>
|
MissingMci: (reason, reasonCode) =>
|
||||||
new EnigError('Missing required MCI code(s)', -32009, reason, reasonCode),
|
new EnigError('Missing required MCI code(s)', -32009, reason, reasonCode),
|
||||||
BadLogin: (reason, reasonCode) =>
|
BadLogin: (reason, reasonCode) =>
|
||||||
|
@ -51,6 +51,9 @@ exports.Errors = {
|
||||||
new EnigError('User interrupted', -32011, reason, reasonCode),
|
new EnigError('User interrupted', -32011, reason, reasonCode),
|
||||||
NothingToDo: (reason, reasonCode) =>
|
NothingToDo: (reason, reasonCode) =>
|
||||||
new EnigError('Nothing to do', -32012, reason, reasonCode),
|
new EnigError('Nothing to do', -32012, reason, reasonCode),
|
||||||
|
HttpError: (reason, reasonCode) =>
|
||||||
|
new EnigError('HTTP error', -32013, reason, reasonCode),
|
||||||
|
Timeout: (reason, reasonCode) => new EnigError('Timeout', -32014, reason, reasonCode),
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.ErrorReasons = {
|
exports.ErrorReasons = {
|
||||||
|
|
|
@ -0,0 +1,72 @@
|
||||||
|
const { Errors } = require('./enig_error.js');
|
||||||
|
|
||||||
|
// deps
|
||||||
|
const { isString, isObject } = require('lodash');
|
||||||
|
const https = require('https');
|
||||||
|
const httpSignature = require('http-signature');
|
||||||
|
const crypto = require('crypto');
|
||||||
|
|
||||||
|
exports.postJson = postJson;
|
||||||
|
|
||||||
|
function postJson(url, json, options, cb) {
|
||||||
|
if (!isString(json)) {
|
||||||
|
json = JSON.stringify(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
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.headers['Content-Length'] = json.length;
|
||||||
|
|
||||||
|
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.write(json);
|
||||||
|
req.end();
|
||||||
|
}
|
|
@ -4,18 +4,20 @@ const {
|
||||||
getUserProfileTemplatedBody,
|
getUserProfileTemplatedBody,
|
||||||
DefaultProfileTemplate,
|
DefaultProfileTemplate,
|
||||||
accountFromSelfUrl,
|
accountFromSelfUrl,
|
||||||
|
selfUrl,
|
||||||
} = require('../../../activitypub_util');
|
} = require('../../../activitypub_util');
|
||||||
const Config = require('../../../config').get;
|
const Config = require('../../../config').get;
|
||||||
const Activity = require('../../../activitypub_activity');
|
const Activity = require('../../../activitypub_activity');
|
||||||
const ActivityPubSettings = require('../../../activitypub_settings');
|
const ActivityPubSettings = require('../../../activitypub_settings');
|
||||||
|
const Actor = require('../../../activitypub_actor');
|
||||||
|
const { postJson } = require('../../../http_util');
|
||||||
|
const UserProps = require('../../../user_property');
|
||||||
|
|
||||||
// deps
|
// deps
|
||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
const enigma_assert = require('../../../enigma_assert');
|
const enigma_assert = require('../../../enigma_assert');
|
||||||
const httpSignature = require('http-signature');
|
const httpSignature = require('http-signature');
|
||||||
const https = require('https');
|
const https = require('https');
|
||||||
const { Errors } = require('../../../enig_error');
|
|
||||||
const Actor = require('../../../activitypub_actor');
|
|
||||||
|
|
||||||
exports.moduleInfo = {
|
exports.moduleInfo = {
|
||||||
name: 'ActivityPub',
|
name: 'ActivityPub',
|
||||||
|
@ -220,12 +222,67 @@ exports.getModule = class ActivityPubWebHandler extends WebHandlerModule {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// user must have a Private Key
|
||||||
|
const privateKey = user.getProperty(UserProps.PrivateKeyMain);
|
||||||
|
if (_.isEmpty(privateKey)) {
|
||||||
|
// :TODO: Log me
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const accept = Activity.makeAccept(
|
const accept = Activity.makeAccept(
|
||||||
this.webServer,
|
this.webServer,
|
||||||
localActor,
|
localActor,
|
||||||
activity
|
activity
|
||||||
);
|
);
|
||||||
console.log(accept);
|
|
||||||
|
const keyId = selfUrl(this.webServer, user) + '#main-key';
|
||||||
|
|
||||||
|
const reqOpts = {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/activity+json',
|
||||||
|
},
|
||||||
|
sign: {
|
||||||
|
// :TODO: Make a helper for this
|
||||||
|
key: privateKey,
|
||||||
|
keyId,
|
||||||
|
authorizationHeaderName: 'Signature',
|
||||||
|
headers: [
|
||||||
|
'(request-target)',
|
||||||
|
'host',
|
||||||
|
'date',
|
||||||
|
'digest',
|
||||||
|
'content-type',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
postJson(
|
||||||
|
actor.inbox,
|
||||||
|
JSON.stringify(accept),
|
||||||
|
reqOpts,
|
||||||
|
(err, respBody, res) => {
|
||||||
|
if (err) {
|
||||||
|
return this.log.warn(
|
||||||
|
{
|
||||||
|
inbox: actor.inbox,
|
||||||
|
statusCode: res.statusCode,
|
||||||
|
error: err.message,
|
||||||
|
},
|
||||||
|
'Failed POSTing "Accept" to inbox'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res.statusCode != 202) {
|
||||||
|
return this.log.warn(
|
||||||
|
{
|
||||||
|
inbox: actor.inbox,
|
||||||
|
statusCode: res.statusCode,
|
||||||
|
},
|
||||||
|
'Unexpected status code'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue