diff --git a/public/language/en-GB/error.json b/public/language/en-GB/error.json index edeb03b21d..caa23964b4 100644 --- a/public/language/en-GB/error.json +++ b/public/language/en-GB/error.json @@ -289,6 +289,7 @@ "api.503": "The route you are trying to call is not currently available due to a server configuration", "api.reauth-required": "The resource you are trying to access requires (re-)authentication.", + "activitypub.not-enabled": "Federation is not enabled on this server", "activitypub.invalid-id": "Unable to resolve the input id, likely as it is malformed.", "activitypub.get-failed": "Unable to retrieve the specified resource.", "activitypub.pubKey-not-found": "Unable to resolve public key, so payload verification cannot take place.", diff --git a/src/activitypub/helpers.js b/src/activitypub/helpers.js index 9a5ac54e33..dd0b0c05ff 100644 --- a/src/activitypub/helpers.js +++ b/src/activitypub/helpers.js @@ -28,7 +28,13 @@ const sha256 = payload => crypto.createHash('sha256').update(payload).digest('he const Helpers = module.exports; +let _lastLog; Helpers.log = (message) => { + if (!message) { + return _lastLog; + } + + _lastLog = message; if (process.env.NODE_ENV === 'development') { winston.verbose(message); } diff --git a/src/activitypub/index.js b/src/activitypub/index.js index 2043f127a6..4a6d95b48b 100644 --- a/src/activitypub/index.js +++ b/src/activitypub/index.js @@ -263,6 +263,10 @@ ActivityPub.verify = async (req) => { }; ActivityPub.get = async (type, id, uri, options) => { + if (!meta.config.activitypubEnabled) { + throw new Error('[[error:activitypub.not-enabled]]'); + } + options = { cache: true, ...options, @@ -358,6 +362,10 @@ async function sendMessage(uri, id, type, payload, attempts = 1) { } ActivityPub.send = async (type, id, targets, payload) => { + if (!meta.config.activitypubEnabled) { + return ActivityPub.helpers.log('[activitypub/send] Federation not enabled; not sending.'); + } + if (!Array.isArray(targets)) { targets = [targets]; } diff --git a/src/controllers/activitypub/index.js b/src/controllers/activitypub/index.js index ad18451d04..ccf85b2012 100644 --- a/src/controllers/activitypub/index.js +++ b/src/controllers/activitypub/index.js @@ -3,6 +3,7 @@ const nconf = require('nconf'); const winston = require('winston'); +const meta = require('../../meta'); const user = require('../../user'); const activitypub = require('../../activitypub'); const helpers = require('../helpers'); @@ -14,7 +15,7 @@ Controller.topics = require('./topics'); Controller.fetch = async (req, res, next) => { // Given a `resource` query parameter, attempts to retrieve and parse it - if (!req.query.resource) { + if (!meta.config.activitypubEnabled || !req.query.resource) { return next(); } diff --git a/src/controllers/api.js b/src/controllers/api.js index 9b85dd2d30..3f676803cf 100644 --- a/src/controllers/api.js +++ b/src/controllers/api.js @@ -99,7 +99,7 @@ apiController.loadConfig = async function (req) { version: fontawesome_version, }, activitypub: { - probe: meta.config.activitypubProbe, + probe: meta.config.activitypubEnabled && meta.config.activitypubProbe, }, }; diff --git a/test/activitypub.js b/test/activitypub.js index 3465707203..be7323ad3f 100644 --- a/test/activitypub.js +++ b/test/activitypub.js @@ -11,7 +11,6 @@ const request = require('../src/request'); const file = require('../src/file'); const install = require('../src/install'); -const privileges = require('../src/privileges'); const meta = require('../src/meta'); const user = require('../src/user'); const categories = require('../src/categories'); @@ -29,6 +28,52 @@ describe('ActivityPub integration', () => { delete meta.config.activitypubEnabled; }); + describe.only('Master toggle', () => { + before(async () => { + delete meta.config.activitypubEnabled; + }); + + it('calls to activitypub.get should throw', async () => { + await assert.rejects( + activitypub.get('uid', 0, 'https://example.org'), + { message: '[[error:activitypub.not-enabled]]' }, + ); + }); + + it('calls to activitypub.send should silently log', async () => { + await activitypub.send('uid', 0, ['https://example.org'], { foo: 'bar' }); + assert.strictEqual(activitypub.helpers.log(), '[activitypub/send] Federation not enabled; not sending.') + }); + + it('request for an activitypub route should return 404 Not Found', async () => { + const uid = user.create({ username: utils.generateUUID() }); + const { response } = await request.get(`${nconf.get('url')}/uid/${uid}`, { + headers: { + Accept: 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"', + }, + }); + + assert.strictEqual(response.statusCode, 404); + }); + + it('requests to the /ap endpoint should return 404 Not Found', async () => { + const { response } = await request.get(`${nconf.get('url')}/ap?resource=${encodeURIComponent('https://example.org')}`); + assert.strictEqual(response.statusCode, 404); + }); + + it('webfinger requests to a local user should not indicate an application/activity+json endpoint', async () => { + const username = utils.generateUUID().slice(0, 8); + user.create({ username }); + const { response, body } = await request.get(`${nconf.get('url')}/.well-known/webfinger?resource=acct:${username}@${nconf.get('url_parsed').host}`); + + assert.strictEqual(response.statusCode, 200); + }); + + after(() => { + meta.config.activitypubEnabled = 1; + }); + }); + describe('Helpers', () => { describe('.query()', () => {