mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-10-27 09:06:15 +01:00
feat: resolve objects from ids in middleware
This commit is contained in:
@@ -5,6 +5,7 @@ const winston = require('winston');
|
|||||||
const nconf = require('nconf');
|
const nconf = require('nconf');
|
||||||
const validator = require('validator');
|
const validator = require('validator');
|
||||||
|
|
||||||
|
const posts = require('../posts');
|
||||||
const request = require('../request');
|
const request = require('../request');
|
||||||
const db = require('../database');
|
const db = require('../database');
|
||||||
const ttl = require('../cache/ttl');
|
const ttl = require('../cache/ttl');
|
||||||
@@ -99,28 +100,34 @@ Helpers.generateKeys = async (type, id) => {
|
|||||||
|
|
||||||
Helpers.resolveLocalId = async (input) => {
|
Helpers.resolveLocalId = async (input) => {
|
||||||
if (Helpers.isUri(input)) {
|
if (Helpers.isUri(input)) {
|
||||||
const { host, pathname } = new URL(input);
|
const { host, pathname, hash } = new URL(input);
|
||||||
|
|
||||||
if (host === nconf.get('url_parsed').host) {
|
if (host === nconf.get('url_parsed').host) {
|
||||||
const [prefix, value] = pathname.replace(nconf.get('relative_path'), '').split('/').filter(Boolean);
|
const [prefix, value] = pathname.replace(nconf.get('relative_path'), '').split('/').filter(Boolean);
|
||||||
|
|
||||||
|
let activityData = {};
|
||||||
|
if (hash.startsWith('#activity')) {
|
||||||
|
const [, activity, data] = hash.split('/', 3);
|
||||||
|
activityData = { activity, data };
|
||||||
|
}
|
||||||
|
|
||||||
switch (prefix) {
|
switch (prefix) {
|
||||||
case 'uid':
|
case 'uid':
|
||||||
return { type: 'user', id: value };
|
return { type: 'user', id: value, ...activityData };
|
||||||
|
|
||||||
case 'post':
|
case 'post':
|
||||||
return { type: 'post', id: value };
|
return { type: 'post', id: value, ...activityData };
|
||||||
|
|
||||||
case 'category':
|
case 'category':
|
||||||
return { type: 'category', id: value };
|
return { type: 'category', id: value, ...activityData };
|
||||||
|
|
||||||
case 'user': {
|
case 'user': {
|
||||||
const uid = await user.getUidByUserslug(value);
|
const uid = await user.getUidByUserslug(value);
|
||||||
return { type: 'user', id: uid };
|
return { type: 'user', id: uid, ...activityData };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return { type: null, id: null };
|
return { type: null, id: null, ...activityData };
|
||||||
}
|
}
|
||||||
|
|
||||||
return { type: null, id: null };
|
return { type: null, id: null };
|
||||||
@@ -132,3 +139,75 @@ Helpers.resolveLocalId = async (input) => {
|
|||||||
|
|
||||||
return { type: null, id: null };
|
return { type: null, id: null };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Helpers.resolveActor = (type, id) => {
|
||||||
|
switch (type) {
|
||||||
|
case 'user':
|
||||||
|
case 'uid': {
|
||||||
|
return `${nconf.get('url')}${id > 0 ? `/uid/${id}` : '/actor'}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'category':
|
||||||
|
case 'cid': {
|
||||||
|
return `${nconf.get('url')}/category/${id}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new Error('[[error:activitypub.invalid-id]]');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Helpers.resolveActivity = async (activity, data, id, resolved) => {
|
||||||
|
switch (activity.toLowerCase()) {
|
||||||
|
case 'follow': {
|
||||||
|
const actor = await Helpers.resolveActor(resolved.type, resolved.id);
|
||||||
|
const { actorUri: targetUri } = await Helpers.query(data);
|
||||||
|
return {
|
||||||
|
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||||
|
actor,
|
||||||
|
id,
|
||||||
|
type: 'Follow',
|
||||||
|
object: targetUri,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
throw new Error('[[error:activitypub.not-implemented]]');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
Helpers.resolveObjects = async (ids) => {
|
||||||
|
if (!Array.isArray(ids)) {
|
||||||
|
ids = [ids];
|
||||||
|
}
|
||||||
|
const objects = await Promise.all(ids.map(async (id) => {
|
||||||
|
const { type, id: resolvedId, activity, data: activityData } = await Helpers.resolveLocalId(id);
|
||||||
|
if (activity) {
|
||||||
|
return Helpers.resolveActivity(activity, activityData, id, { type, id: resolvedId });
|
||||||
|
}
|
||||||
|
switch (type) {
|
||||||
|
case 'user': {
|
||||||
|
return activitypub.mocks.actors.user(resolvedId);
|
||||||
|
}
|
||||||
|
case 'post': {
|
||||||
|
const post = (await posts.getPostSummaryByPids(
|
||||||
|
[resolvedId],
|
||||||
|
activitypub._constants.uid,
|
||||||
|
{ stripTags: false }
|
||||||
|
)).pop();
|
||||||
|
if (!post) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return activitypub.mocks.note(post);
|
||||||
|
}
|
||||||
|
case 'category': {
|
||||||
|
return activitypub.mocks.category(resolvedId);
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
return activitypub.get('uid', 0, id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
return objects.length === 1 ? objects[0] : objects;
|
||||||
|
};
|
||||||
|
|||||||
@@ -55,8 +55,8 @@ middleware.validate = async function (req, res, next) {
|
|||||||
const actorHostname = new URL(actor).hostname;
|
const actorHostname = new URL(actor).hostname;
|
||||||
const objectHostname = new URL(object.id).hostname;
|
const objectHostname = new URL(object.id).hostname;
|
||||||
if (actorHostname !== objectHostname) {
|
if (actorHostname !== objectHostname) {
|
||||||
winston.verbose('[middleware/activitypub] Origin check failed.');
|
winston.verbose('[middleware/activitypub] Origin check failed, stripping object down to id.');
|
||||||
return res.sendStatus(403);
|
req.body.object = [object.id];
|
||||||
}
|
}
|
||||||
winston.verbose('[middleware/activitypub] Origin check passed.');
|
winston.verbose('[middleware/activitypub] Origin check passed.');
|
||||||
}
|
}
|
||||||
@@ -75,6 +75,21 @@ middleware.validate = async function (req, res, next) {
|
|||||||
next();
|
next();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
middleware.resolveObjects = async function (req, res, next) {
|
||||||
|
const { object } = req.body;
|
||||||
|
if (typeof object === 'string' || (Array.isArray(object) && object.every(o => typeof o === 'string'))) {
|
||||||
|
winston.verbose('[middleware/activitypub] Resolving object(s)...');
|
||||||
|
try {
|
||||||
|
req.body.object = await activitypub.helpers.resolveObjects(object);
|
||||||
|
winston.verbose('[middleware/activitypub] Object(s) successfully resolved.');
|
||||||
|
} catch (e) {
|
||||||
|
winston.verbose('[middleware/activitypub] Failed to resolve object(s).');
|
||||||
|
return res.sendStatus(400);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
next();
|
||||||
|
};
|
||||||
|
|
||||||
middleware.configureResponse = async function (req, res, next) {
|
middleware.configureResponse = async function (req, res, next) {
|
||||||
res.header('Content-Type', 'application/activity+json');
|
res.header('Content-Type', 'application/activity+json');
|
||||||
next();
|
next();
|
||||||
|
|||||||
@@ -17,13 +17,18 @@ module.exports = function (app, middleware, controllers) {
|
|||||||
middleware.activitypub.configureResponse,
|
middleware.activitypub.configureResponse,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const inboxMiddlewares = [
|
||||||
|
middleware.activitypub.validate,
|
||||||
|
middleware.activitypub.resolveObjects,
|
||||||
|
];
|
||||||
|
|
||||||
app.get('/actor', middlewares, controllers.activitypub.actors.application);
|
app.get('/actor', middlewares, controllers.activitypub.actors.application);
|
||||||
app.post('/inbox', [...middlewares, middleware.activitypub.validate], controllers.activitypub.postInbox);
|
app.post('/inbox', [...middlewares, ...inboxMiddlewares], controllers.activitypub.postInbox);
|
||||||
|
|
||||||
app.get('/uid/:uid', [...middlewares, middleware.assert.user], controllers.activitypub.actors.user);
|
app.get('/uid/:uid', [...middlewares, middleware.assert.user], controllers.activitypub.actors.user);
|
||||||
app.get('/user/:userslug', [...middlewares, middleware.exposeUid, middleware.assert.user], controllers.activitypub.actors.userBySlug);
|
app.get('/user/:userslug', [...middlewares, middleware.exposeUid, middleware.assert.user], controllers.activitypub.actors.userBySlug);
|
||||||
app.get('/uid/:uid/inbox', [...middlewares, middleware.assert.user], controllers.activitypub.getInbox);
|
app.get('/uid/:uid/inbox', [...middlewares, middleware.assert.user], controllers.activitypub.getInbox);
|
||||||
app.post('/uid/:uid/inbox', [...middlewares, middleware.assert.user, middleware.activitypub.validate], controllers.activitypub.postInbox);
|
app.post('/uid/:uid/inbox', [...middlewares, middleware.assert.user, ...inboxMiddlewares], controllers.activitypub.postInbox);
|
||||||
app.get('/uid/:uid/outbox', [...middlewares, middleware.assert.user], controllers.activitypub.getOutbox);
|
app.get('/uid/:uid/outbox', [...middlewares, middleware.assert.user], controllers.activitypub.getOutbox);
|
||||||
app.post('/uid/:uid/outbox', [...middlewares, middleware.assert.user], controllers.activitypub.postOutbox);
|
app.post('/uid/:uid/outbox', [...middlewares, middleware.assert.user], controllers.activitypub.postOutbox);
|
||||||
app.get('/uid/:uid/following', [...middlewares, middleware.assert.user], controllers.activitypub.getFollowing);
|
app.get('/uid/:uid/following', [...middlewares, middleware.assert.user], controllers.activitypub.getFollowing);
|
||||||
@@ -35,7 +40,7 @@ module.exports = function (app, middleware, controllers) {
|
|||||||
|
|
||||||
app.get('/category/:cid/:slug?', [...middlewares, middleware.assert.category], controllers.activitypub.actors.category);
|
app.get('/category/:cid/:slug?', [...middlewares, middleware.assert.category], controllers.activitypub.actors.category);
|
||||||
app.get('/category/:cid/inbox', [...middlewares, middleware.assert.category], controllers.activitypub.getInbox);
|
app.get('/category/:cid/inbox', [...middlewares, middleware.assert.category], controllers.activitypub.getInbox);
|
||||||
app.post('/category/:cid/inbox', [...middlewares, middleware.assert.category, middleware.activitypub.validate], controllers.activitypub.postInbox);
|
app.post('/category/:cid/inbox', [...inboxMiddlewares, middleware.assert.category, ...inboxMiddlewares], controllers.activitypub.postInbox);
|
||||||
app.get('/category/:cid/outbox', [...middlewares, middleware.assert.category], controllers.activitypub.getCategoryOutbox);
|
app.get('/category/:cid/outbox', [...middlewares, middleware.assert.category], controllers.activitypub.getCategoryOutbox);
|
||||||
app.post('/category/:cid/outbox', [...middlewares, middleware.assert.category], controllers.activitypub.postOutbox);
|
app.post('/category/:cid/outbox', [...middlewares, middleware.assert.category], controllers.activitypub.postOutbox);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user