2023-05-29 17:42:44 -04:00
|
|
|
'use strict';
|
|
|
|
|
|
|
|
|
|
const request = require('request-promise-native');
|
2023-06-23 14:59:47 -04:00
|
|
|
const { generateKeyPairSync } = require('crypto');
|
2023-06-19 17:29:22 -04:00
|
|
|
const winston = require('winston');
|
2023-06-28 14:59:39 -04:00
|
|
|
const nconf = require('nconf');
|
2023-05-29 17:42:44 -04:00
|
|
|
|
2023-06-19 17:29:22 -04:00
|
|
|
const db = require('../database');
|
2023-06-16 11:26:25 -04:00
|
|
|
const ttl = require('../cache/ttl');
|
2023-06-28 14:59:39 -04:00
|
|
|
const user = require('../user');
|
2023-06-16 11:26:25 -04:00
|
|
|
|
|
|
|
|
const webfingerCache = ttl({ ttl: 1000 * 60 * 60 * 24 }); // 24 hours
|
|
|
|
|
|
2023-05-29 17:42:44 -04:00
|
|
|
const Helpers = module.exports;
|
|
|
|
|
|
|
|
|
|
Helpers.query = async (id) => {
|
|
|
|
|
const [username, hostname] = id.split('@');
|
|
|
|
|
if (!username || !hostname) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-16 11:26:25 -04:00
|
|
|
if (webfingerCache.has(id)) {
|
|
|
|
|
return webfingerCache.get(id);
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-29 17:42:44 -04:00
|
|
|
// Make a webfinger query to retrieve routing information
|
|
|
|
|
const response = await request(`https://${hostname}/.well-known/webfinger?resource=acct:${id}`, {
|
|
|
|
|
simple: false,
|
|
|
|
|
resolveWithFullResponse: true,
|
|
|
|
|
json: true,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (response.statusCode !== 200 || !response.body.hasOwnProperty('links')) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Parse links to find actor endpoint
|
|
|
|
|
let actorUri = response.body.links.filter(link => link.type === 'application/activity+json' && link.rel === 'self');
|
|
|
|
|
if (actorUri.length) {
|
|
|
|
|
actorUri = actorUri.pop();
|
|
|
|
|
({ href: actorUri } = actorUri);
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-19 17:29:22 -04:00
|
|
|
const { publicKey } = response.body;
|
|
|
|
|
|
|
|
|
|
webfingerCache.set(id, { username, hostname, actorUri, publicKey });
|
|
|
|
|
return { username, hostname, actorUri, publicKey };
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Helpers.generateKeys = async (uid) => {
|
|
|
|
|
winston.verbose(`[activitypub] Generating RSA key-pair for uid ${uid}`);
|
|
|
|
|
const {
|
|
|
|
|
publicKey,
|
|
|
|
|
privateKey,
|
|
|
|
|
} = generateKeyPairSync('rsa', {
|
|
|
|
|
modulusLength: 2048,
|
|
|
|
|
publicKeyEncoding: {
|
|
|
|
|
type: 'spki',
|
|
|
|
|
format: 'pem',
|
|
|
|
|
},
|
|
|
|
|
privateKeyEncoding: {
|
|
|
|
|
type: 'pkcs8',
|
|
|
|
|
format: 'pem',
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
await db.setObject(`uid:${uid}:keys`, { publicKey, privateKey });
|
|
|
|
|
return { publicKey, privateKey };
|
2023-05-29 17:42:44 -04:00
|
|
|
};
|
2023-06-28 14:59:39 -04:00
|
|
|
|
|
|
|
|
Helpers.resolveLocalUid = async (id) => {
|
|
|
|
|
const [slug, host] = id.split('@');
|
|
|
|
|
|
|
|
|
|
if (id.indexOf('@') === -1 || host !== nconf.get('url_parsed').host) {
|
|
|
|
|
throw new Error('[[activitypub:invalid-id]]');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return await user.getUidByUserslug(slug);
|
|
|
|
|
};
|