From 538776f9c793d16f466a421dadc0fac38a654129 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Thu, 18 Jan 2024 11:50:14 -0500 Subject: [PATCH] refactor: move activitypub-related middlewares to their own file --- src/controllers/activitypub/topics.js | 2 +- src/middleware/activitypub.js | 48 +++++++++++++++++++++++++++ src/middleware/index.js | 41 +---------------------- src/routes/activitypub.js | 8 +++-- 4 files changed, 55 insertions(+), 44 deletions(-) create mode 100644 src/middleware/activitypub.js diff --git a/src/controllers/activitypub/topics.js b/src/controllers/activitypub/topics.js index 36a7659baa..8ab34613a9 100644 --- a/src/controllers/activitypub/topics.js +++ b/src/controllers/activitypub/topics.js @@ -14,7 +14,7 @@ const helpers = require('../helpers'); const controller = module.exports; -controller.list = async function (req, res, next) { +controller.list = async function (req, res) { const { topicsPerPage } = await user.getSettings(req.uid); const page = parseInt(req.query.page, 10) || 1; const start = Math.max(0, (page - 1) * topicsPerPage); diff --git a/src/middleware/activitypub.js b/src/middleware/activitypub.js new file mode 100644 index 0000000000..12761763a1 --- /dev/null +++ b/src/middleware/activitypub.js @@ -0,0 +1,48 @@ +'use strict'; + +const meta = require('../meta'); +const activitypub = require('../activitypub'); + +const middleware = module.exports; + +middleware.enabled = async (req, res, next) => next(!meta.config.activitypubEnabled ? 'route' : undefined); + +middleware.assertS2S = async function (req, res, next) { + // For whatever reason, express accepts does not recognize "profile" as a valid differentiator + // Therefore, manual header parsing is used here. + const { accept, 'content-type': contentType } = req.headers; + if (!(accept || contentType)) { + return next('route'); + } + + const acceptable = [ + 'application/activity+json', + 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"', + ]; + const pass = (accept && accept.split(',').some((value) => { + const parts = value.split(';').map(v => v.trim()); + return acceptable.includes(value || parts[0]); + })) || (contentType && acceptable.includes(contentType)); + + if (!pass) { + return next('route'); + } + + next(); +}; + +middleware.validate = async function (req, res, next) { + // Checks the validity of the incoming payload against the sender and rejects on failure + const verified = await activitypub.verify(req); + if (!verified) { + return res.sendStatus(400); + } + + // Sanity-check payload schema + const required = ['type']; + if (!required.every(prop => req.body.hasOwnProperty(prop))) { + return res.sendStatus(400); + } + + next(); +}; diff --git a/src/middleware/index.js b/src/middleware/index.js index 37125e918c..4dc388b2c4 100644 --- a/src/middleware/index.js +++ b/src/middleware/index.js @@ -69,6 +69,7 @@ middleware.uploads = require('./uploads'); require('./headers')(middleware); require('./expose')(middleware); middleware.assert = require('./assert'); +middleware.activitypub = require('./activitypub'); middleware.stripLeadingSlashes = function stripLeadingSlashes(req, res, next) { const target = req.originalUrl.replace(relative_path, ''); @@ -306,43 +307,3 @@ middleware.handleMultipart = (req, res, next) => { multipartMiddleware(req, res, next); }; - -middleware.proceedOnActivityPub = (req, res, next) => { - // For whatever reason, express accepts does not recognize "profile" as a valid differentiator - // Therefore, manual header parsing is used here. - const { accept, 'content-type': contentType } = req.headers; - if (!meta.config.activitypubEnabled || !(accept || contentType)) { - return next('route'); - } - - const acceptable = [ - 'application/activity+json', - 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"', - ]; - const pass = (accept && accept.split(',').some((value) => { - const parts = value.split(';').map(v => v.trim()); - return acceptable.includes(value || parts[0]); - })) || (contentType && acceptable.includes(contentType)); - - if (!pass) { - return next('route'); - } - - next(); -}; - -middleware.validateActivity = helpers.try(async (req, res, next) => { - // Checks the validity of the incoming payload against the sender and rejects on failure - const verified = await activitypub.verify(req); - if (!verified) { - return res.sendStatus(400); - } - - // Sanity-check payload schema - const required = ['type']; - if (!required.every(prop => req.body.hasOwnProperty(prop))) { - return res.sendStatus(400); - } - - next(); -}); diff --git a/src/routes/activitypub.js b/src/routes/activitypub.js index cb0aab8268..63efeafc13 100644 --- a/src/routes/activitypub.js +++ b/src/routes/activitypub.js @@ -3,18 +3,20 @@ const helpers = require('./helpers'); module.exports = function (app, middleware, controllers) { - helpers.setupPageRoute(app, '/world/:view?', controllers.activitypub.topics.list); + helpers.setupPageRoute(app, '/world/:view?', [middleware.activitypub.enabled], controllers.activitypub.topics.list); /** * These controllers only respond if the sender is making an json+activitypub style call (i.e. S2S-only) + * + * - See middleware.activitypub.assertS2S */ - const middlewares = [middleware.proceedOnActivityPub, middleware.exposeUid]; + const middlewares = [middleware.activitypub.enabled, middleware.activitypub.assertS2S, middleware.exposeUid]; app.get('/user/:userslug', middlewares, controllers.activitypub.getActor); app.get('/user/:userslug/inbox', middlewares, controllers.activitypub.getInbox); - app.post('/user/:userslug/inbox', [...middlewares, middleware.validateActivity], controllers.activitypub.postInbox); + app.post('/user/:userslug/inbox', [...middlewares, middleware.activitypub.validate], controllers.activitypub.postInbox); app.get('/user/:userslug/outbox', middlewares, controllers.activitypub.getOutbox); app.post('/user/:userslug/outbox', middlewares, controllers.activitypub.postOutbox);