mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-10-26 16:46:12 +01:00
feat: instance-level allow andd deny list for federatioN
This commit is contained in:
@@ -193,5 +193,6 @@
|
|||||||
"activitypubEnabled": 1,
|
"activitypubEnabled": 1,
|
||||||
"activitypubAllowLoopback": 0,
|
"activitypubAllowLoopback": 0,
|
||||||
"activitypubContentPruneDays": 30,
|
"activitypubContentPruneDays": 30,
|
||||||
"activitypubUserPruneDays": 7
|
"activitypubUserPruneDays": 7,
|
||||||
|
"activitypubFilter": 0
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,5 +15,6 @@
|
|||||||
"server-filtering": "Filtering",
|
"server-filtering": "Filtering",
|
||||||
"count": "This NodeBB is currently aware of <strong>%1</strong> server(s)",
|
"count": "This NodeBB is currently aware of <strong>%1</strong> server(s)",
|
||||||
"server.filter-help": "Specify servers you would like to bar from federating with your NodeBB. Alternatively, you may opt to selectively <em>allow</em> federation with specific servers, instead. Both options are supported, although they are mutually exclusive.",
|
"server.filter-help": "Specify servers you would like to bar from federating with your NodeBB. Alternatively, you may opt to selectively <em>allow</em> federation with specific servers, instead. Both options are supported, although they are mutually exclusive.",
|
||||||
|
"server.filter-help-hostname": "Enter just the instance hostname below (e.g. <code>example.org</code>), separated by line breaks.",
|
||||||
"server.filter-allow-list": "Use this as an Allow List instead"
|
"server.filter-allow-list": "Use this as an Allow List instead"
|
||||||
}
|
}
|
||||||
@@ -41,6 +41,7 @@ ActivityPub.inbox = require('./inbox');
|
|||||||
ActivityPub.mocks = require('./mocks');
|
ActivityPub.mocks = require('./mocks');
|
||||||
ActivityPub.notes = require('./notes');
|
ActivityPub.notes = require('./notes');
|
||||||
ActivityPub.actors = require('./actors');
|
ActivityPub.actors = require('./actors');
|
||||||
|
ActivityPub.instances = require('./instances');
|
||||||
|
|
||||||
ActivityPub.startJobs = () => {
|
ActivityPub.startJobs = () => {
|
||||||
// winston.verbose('[activitypub/jobs] Registering jobs.');
|
// winston.verbose('[activitypub/jobs] Registering jobs.');
|
||||||
|
|||||||
19
src/activitypub/instances.js
Normal file
19
src/activitypub/instances.js
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const meta = require('../meta');
|
||||||
|
const db = require('../database');
|
||||||
|
|
||||||
|
const Instances = module.exports;
|
||||||
|
|
||||||
|
Instances.log = async (domain) => {
|
||||||
|
await db.sortedSetAdd('instances:lastSeen', Date.now(), domain);
|
||||||
|
};
|
||||||
|
|
||||||
|
Instances.getCount = async () => db.sortedSetCard('instances:lastSeen');
|
||||||
|
|
||||||
|
Instances.isAllowed = async (domain) => {
|
||||||
|
let { activitypubFilter: type, activitypubFilterList: list } = meta.config;
|
||||||
|
list = new Set(list.split('\n'));
|
||||||
|
// eslint-disable-next-line no-bitwise
|
||||||
|
return list.has(domain) ^ !type;
|
||||||
|
};
|
||||||
@@ -9,6 +9,7 @@ const groups = require('../../groups');
|
|||||||
const languages = require('../../languages');
|
const languages = require('../../languages');
|
||||||
const navigationAdmin = require('../../navigation/admin');
|
const navigationAdmin = require('../../navigation/admin');
|
||||||
const social = require('../../social');
|
const social = require('../../social');
|
||||||
|
const activitypub = require('../../activitypub');
|
||||||
const api = require('../../api');
|
const api = require('../../api');
|
||||||
const pagination = require('../../pagination');
|
const pagination = require('../../pagination');
|
||||||
const helpers = require('../helpers');
|
const helpers = require('../helpers');
|
||||||
@@ -123,3 +124,12 @@ settingsController.api = async (req, res) => {
|
|||||||
pagination: pagination.create(page, pageCount, req.query),
|
pagination: pagination.create(page, pageCount, req.query),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
settingsController.activitypub = async (req, res) => {
|
||||||
|
const instanceCount = await activitypub.instances.getCount();
|
||||||
|
|
||||||
|
res.render('admin/settings/activitypub', {
|
||||||
|
title: `[[admin/menu:settings/activitypub]]`,
|
||||||
|
instanceCount,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|||||||
@@ -70,6 +70,11 @@ middleware.validate = async function (req, res, next) {
|
|||||||
|
|
||||||
// Domain check
|
// Domain check
|
||||||
const { hostname } = new URL(actor);
|
const { hostname } = new URL(actor);
|
||||||
|
const allowed = await activitypub.instances.isAllowed(hostname);
|
||||||
|
if (!allowed) {
|
||||||
|
// winston.verbose(`[middleware/activitypub] Blocked incoming activity from ${hostname}.`);
|
||||||
|
return res.sendStatus(403);
|
||||||
|
}
|
||||||
await db.sortedSetAdd('instances:lastSeen', Date.now(), hostname);
|
await db.sortedSetAdd('instances:lastSeen', Date.now(), hostname);
|
||||||
|
|
||||||
// Origin checking
|
// Origin checking
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ module.exports = function (app, name, middleware, controllers) {
|
|||||||
helpers.setupAdminPageRoute(app, `/${name}/settings/advanced`, middlewares, controllers.admin.settings.advanced);
|
helpers.setupAdminPageRoute(app, `/${name}/settings/advanced`, middlewares, controllers.admin.settings.advanced);
|
||||||
helpers.setupAdminPageRoute(app, `/${name}/settings/navigation`, middlewares, controllers.admin.settings.navigation);
|
helpers.setupAdminPageRoute(app, `/${name}/settings/navigation`, middlewares, controllers.admin.settings.navigation);
|
||||||
helpers.setupAdminPageRoute(app, `/${name}/settings/api`, middlewares, controllers.admin.settings.api);
|
helpers.setupAdminPageRoute(app, `/${name}/settings/api`, middlewares, controllers.admin.settings.api);
|
||||||
|
helpers.setupAdminPageRoute(app, `/${name}/settings/activitypub`, middlewares, controllers.admin.settings.activitypub);
|
||||||
helpers.setupAdminPageRoute(app, `/${name}/settings/:term?`, middlewares, controllers.admin.settings.get);
|
helpers.setupAdminPageRoute(app, `/${name}/settings/:term?`, middlewares, controllers.admin.settings.get);
|
||||||
|
|
||||||
helpers.setupAdminPageRoute(app, `/${name}/appearance/:term?`, middlewares, controllers.admin.appearance.get);
|
helpers.setupAdminPageRoute(app, `/${name}/appearance/:term?`, middlewares, controllers.admin.appearance.get);
|
||||||
|
|||||||
@@ -52,14 +52,14 @@
|
|||||||
<form>
|
<form>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<p>[[admin/settings/activitypub:server.filter-help]]</p>
|
<p>[[admin/settings/activitypub:server.filter-help]]</p>
|
||||||
<p>[[admin/settings/activitypub:count, 0]]</p>
|
<p>[[admin/settings/activitypub:server.filter-help-hostname]]</p>
|
||||||
<p class="text-danger fst-italic small">This feature is not available yet</p>
|
<p>[[admin/settings/activitypub:count, {instanceCount}]]</p>
|
||||||
<label for="activitypubFilterList" class="form-label">Filtering list</label>
|
<label for="activitypubFilterList" class="form-label">Filtering list</label>
|
||||||
<textarea class="form-control" id="activitypubFilterList" rows="10" disabled="disabled"></textarea>
|
<textarea class="form-control" id="activitypubFilterList" data-field="activitypubFilterList" rows="10"></textarea>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-check form-switch mb-3">
|
<div class="form-check form-switch mb-3">
|
||||||
<input class="form-check-input" type="checkbox" data-field="activitypubFilter" disabled="disabled" />
|
<input class="form-check-input" type="checkbox" id="activitypubFilter" data-field="activitypubFilter" />
|
||||||
<label class="form-check-label">[[admin/settings/activitypub:server.filter-allow-list]]</label>
|
<label class="form-check-label" for="activitypubFilter">[[admin/settings/activitypub:server.filter-allow-list]]</label>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user