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-12-11 14:35:04 -05:00
|
|
|
const validator = require('validator');
|
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
|
|
|
|
2023-12-11 14:35:04 -05:00
|
|
|
Helpers.resolveLocalUid = async (input) => {
|
|
|
|
|
let slug;
|
2023-12-13 13:38:52 -05:00
|
|
|
const protocols = ['https'];
|
|
|
|
|
if (process.env.CI === 'true') {
|
|
|
|
|
protocols.push('http');
|
|
|
|
|
}
|
|
|
|
|
console.log(input, nconf.get('url'), nconf.get('url_parsed'), protocols, validator.isURL(input, {
|
|
|
|
|
require_protocol: true,
|
|
|
|
|
require_host: true,
|
|
|
|
|
require_tld: false,
|
|
|
|
|
protocols,
|
|
|
|
|
require_valid_protocol: true,
|
|
|
|
|
}), nconf.get('ci'));
|
2023-12-11 14:35:04 -05:00
|
|
|
|
|
|
|
|
if (validator.isURL(input, {
|
|
|
|
|
require_protocol: true,
|
|
|
|
|
require_host: true,
|
|
|
|
|
require_tld: false,
|
2023-12-13 13:38:52 -05:00
|
|
|
protocols,
|
2023-12-11 14:35:04 -05:00
|
|
|
require_valid_protocol: true,
|
|
|
|
|
})) {
|
|
|
|
|
const { host, pathname } = new URL(input);
|
|
|
|
|
|
|
|
|
|
if (host === nconf.get('url_parsed').host) {
|
2023-12-13 13:38:52 -05:00
|
|
|
slug = pathname.replace(nconf.get('relative_path'), '').split('/').filter(Boolean)[1];
|
2023-12-11 14:35:04 -05:00
|
|
|
} else {
|
|
|
|
|
throw new Error('[[activitypub:invalid-id]]');
|
|
|
|
|
}
|
|
|
|
|
} else if (input.indexOf('@') !== -1) { // Webfinger
|
|
|
|
|
([slug] = input.replace(/^acct:/, '').split('@'));
|
|
|
|
|
} else {
|
2023-06-28 14:59:39 -04:00
|
|
|
throw new Error('[[activitypub:invalid-id]]');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return await user.getUidByUserslug(slug);
|
|
|
|
|
};
|