mirror of
https://github.com/NodeBB/NodeBB.git
synced 2026-01-12 18:42:56 +01:00
443 lines
13 KiB
JavaScript
443 lines
13 KiB
JavaScript
'use strict';
|
|
|
|
const assert = require('assert');
|
|
const nconf = require('nconf');
|
|
|
|
const db = require('../mocks/databasemock');
|
|
const request = require('../../src/request');
|
|
const user = require('../../src/user');
|
|
const topics = require('../../src/topics');
|
|
const posts = require('../../src/posts');
|
|
const categories = require('../../src/categories');
|
|
const privileges = require('../../src/privileges');
|
|
const meta = require('../../src/meta');
|
|
const install = require('../../src/install');
|
|
const utils = require('../../src/utils');
|
|
const activitypub = require('../../src/activitypub');
|
|
|
|
const helpers = require('./helpers');
|
|
|
|
describe('Privilege logic for remote users/content (ActivityPub)', () => {
|
|
before(async () => {
|
|
meta.config.activitypubEnabled = 1;
|
|
// await install.giveWorldPrivileges();
|
|
});
|
|
|
|
describe('"fediverse" pseudo-user', () => {
|
|
describe('no privileges given', () => {
|
|
let uid;
|
|
let cid;
|
|
let topicData;
|
|
let postData;
|
|
let mainPid;
|
|
let handle;
|
|
|
|
before(async () => {
|
|
uid = await user.create({ username: utils.generateUUID() });
|
|
({ cid } = await categories.create({ name: utils.generateUUID() }));
|
|
({ topicData, postData } = await topics.post({
|
|
cid,
|
|
uid,
|
|
title: utils.generateUUID(),
|
|
content: utils.generateUUID(),
|
|
}));
|
|
handle = await categories.getCategoryField(cid, 'handle');
|
|
const privsToRemove = await privileges.categories.getGroupPrivilegeList();
|
|
await privileges.categories.rescind(privsToRemove, cid, ['fediverse']);
|
|
});
|
|
|
|
describe('incoming requests', () => {
|
|
it('should not respond to a webfinger request to a category\'s handle', async () => {
|
|
const response = await activitypub.helpers.query(`${handle}@${nconf.get('url_parsed').hostname}`);
|
|
assert.strictEqual(response, false);
|
|
});
|
|
|
|
it('should not respond to a request for the category actor', async () => {
|
|
await assert.rejects(
|
|
activitypub.get('uid', uid, `${nconf.get('url')}/category/${cid}`),
|
|
{ message: '[[error:activitypub.get-failed]]' }
|
|
);
|
|
});
|
|
|
|
it('should not respond to a request for a topic collection', async () => {
|
|
await assert.rejects(
|
|
activitypub.get('uid', uid, `${nconf.get('url')}/topic/${topicData.tid}`),
|
|
{ message: '[[error:activitypub.get-failed]]' }
|
|
);
|
|
});
|
|
|
|
it('should not respond to a request for a post', async () => {
|
|
await assert.rejects(
|
|
activitypub.get('uid', uid, `${nconf.get('url')}/post/${topicData.mainPid}`),
|
|
{ message: '[[error:activitypub.get-failed]]' }
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('incoming activities', () => {
|
|
describe('Create(Note)', () => {
|
|
let note;
|
|
let activity;
|
|
|
|
before(async () => {
|
|
({ note } = helpers.mocks.note({
|
|
cc: [`${nconf.get('url')}/category/${cid}`],
|
|
}));
|
|
({ activity } = helpers.mocks.create(note));
|
|
await activitypub.inbox.create({ body: activity });
|
|
});
|
|
|
|
it('should not assert the note', async () => {
|
|
const exists = await posts.exists(note.id);
|
|
assert.strictEqual(exists, false);
|
|
});
|
|
});
|
|
|
|
describe('Update(Note)', () => {
|
|
let note;
|
|
let activity;
|
|
|
|
before(async () => {
|
|
({ note } = helpers.mocks.note({
|
|
cc: [`${nconf.get('url')}/category/${cid}`],
|
|
}));
|
|
({ activity } = helpers.mocks.create(note));
|
|
await privileges.categories.give(['groups:topics:create'], cid, ['fediverse']);
|
|
await activitypub.inbox.create({ body: activity });
|
|
});
|
|
|
|
after(async () => {
|
|
await privileges.categories.rescind(['groups:topics:create'], cid, ['fediverse']);
|
|
});
|
|
|
|
it('should assert the note', async () => {
|
|
const exists = await posts.exists(note.id);
|
|
assert.strictEqual(exists, true);
|
|
});
|
|
|
|
it('should not allow edits to the note', async () => {
|
|
const oldContent = note.content;
|
|
note.content = 'new content';
|
|
({ activity } = helpers.mocks.update({
|
|
object: note,
|
|
}));
|
|
|
|
await activitypub.inbox.update({ body: activity });
|
|
|
|
const postData = await posts.getPostData(note.id);
|
|
assert.strictEqual(postData.content, oldContent);
|
|
assert.strictEqual(postData.edited, 0);
|
|
});
|
|
});
|
|
|
|
describe('Delete(Note)', () => {
|
|
let note;
|
|
let activity;
|
|
|
|
before(async () => {
|
|
({ note } = helpers.mocks.note({
|
|
cc: [`${nconf.get('url')}/category/${cid}`],
|
|
}));
|
|
({ activity } = helpers.mocks.create(note));
|
|
await privileges.categories.give(['groups:topics:create'], cid, ['fediverse']);
|
|
await activitypub.inbox.create({ body: activity });
|
|
});
|
|
|
|
after(async () => {
|
|
await privileges.categories.rescind(['groups:topics:create'], cid, ['fediverse']);
|
|
});
|
|
|
|
it('should assert the note', async () => {
|
|
const exists = await posts.exists(note.id);
|
|
assert.strictEqual(exists, true);
|
|
});
|
|
|
|
it('should ignore remote deletion of said note', async () => {
|
|
({ activity } = helpers.mocks.delete({ object: note }));
|
|
await activitypub.inbox.delete({ body: activity });
|
|
|
|
const exists = await posts.exists(note.id);
|
|
assert.strictEqual(exists, true);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('outgoing requests', () => {
|
|
it('should not federate out a new post', async () => {
|
|
|
|
});
|
|
|
|
it('should not federate out a post edit', async () => {
|
|
|
|
});
|
|
|
|
it('should not federate out a post deletion', async () => {
|
|
|
|
});
|
|
|
|
it('should not federate out a post announce', async () => {
|
|
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('regular privilege set', () => {
|
|
let cid;
|
|
let handle;
|
|
|
|
before(async () => {
|
|
({ cid } = await categories.create({ name: utils.generateUUID() }));
|
|
handle = await categories.getCategoryField(cid, 'handle');
|
|
const privsToRemove = await privileges.categories.getGroupPrivilegeList();
|
|
});
|
|
|
|
describe('groups:find', () => {
|
|
it('should return webfinger response to a category\'s handle', async () => {
|
|
const { response, body } = await request.get(`${nconf.get('url')}/.well-known/webfinger?resource=acct:${handle}@${nconf.get('url_parsed').host}`);
|
|
|
|
assert(response);
|
|
assert.strictEqual(response.statusCode, 200);
|
|
assert(body.links && body.links.length);
|
|
assert.strictEqual(body.subject, `acct:${handle}@${nconf.get('url_parsed').host}`);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('Privilege masking', () => {
|
|
before(async () => {
|
|
// Grant default fediverse privileges
|
|
await install.giveWorldPrivileges();
|
|
});
|
|
|
|
describe('control', () => {
|
|
let cid;
|
|
let uid;
|
|
|
|
before(async () => {
|
|
// Set up a standard mock group
|
|
({ id: cid } = helpers.mocks.group());
|
|
|
|
// Unprivileged user for testing
|
|
uid = await user.create({ username: utils.generateUUID().slice(0, 10) });
|
|
});
|
|
|
|
it('should properly assert the remote category', async () => {
|
|
const assertion = await activitypub.actors.assertGroup([cid]);
|
|
const exists = await categories.exists(cid);
|
|
|
|
assert(assertion);
|
|
assert(exists);
|
|
});
|
|
|
|
it('should not set up a privilege mask for that category', async () => {
|
|
const exists = await db.exists(`cid:${cid}:privilegeMask`);
|
|
assert(!exists);
|
|
});
|
|
|
|
it('should pass the privileges .can() check if requested', async () => {
|
|
const set = await privileges.categories.get(cid, uid);
|
|
const can = await privileges.categories.can('topics:create', cid, uid);
|
|
assert(can);
|
|
});
|
|
|
|
it('should return true in the privilege set when requested', async () => {
|
|
const set = await privileges.categories.get(cid, uid);
|
|
|
|
assert(set);
|
|
assert(set['topics:create']);
|
|
});
|
|
});
|
|
|
|
describe('postingRestrictedToMods (true on assert)', () => {
|
|
let cid;
|
|
let uid;
|
|
|
|
before(async () => {
|
|
// Set up a mock group with `postingRestrictedToMods` bit set
|
|
({ id: cid } = helpers.mocks.group({
|
|
postingRestrictedToMods: true,
|
|
}));
|
|
|
|
// Unprivileged user for testing
|
|
uid = await user.create({ username: utils.generateUUID().slice(0, 10) });
|
|
});
|
|
|
|
it('should properly assert the remote category', async () => {
|
|
const assertion = await activitypub.actors.assertGroup([cid]);
|
|
const exists = await categories.exists(cid);
|
|
|
|
assert(assertion);
|
|
assert(exists);
|
|
});
|
|
|
|
it('should set up a privilege mask for that category', async () => {
|
|
const exists = await db.exists(`cid:${cid}:privilegeMask`);
|
|
assert(exists);
|
|
});
|
|
|
|
it('should contain a single mask', async () => {
|
|
const members = await db.getSetMembers(`cid:${cid}:privilegeMask`);
|
|
|
|
assert(members);
|
|
assert.strictEqual(members.length, 1);
|
|
assert.strictEqual(members[0], 'topics:create');
|
|
});
|
|
|
|
it('should fail the privileges .can() check if requested', async () => {
|
|
const can = await privileges.categories.can('topics:create', cid, uid);
|
|
assert(!can);
|
|
});
|
|
|
|
it('should return false in the privilege set when requested', async () => {
|
|
const set = await privileges.categories.get(cid, uid);
|
|
|
|
assert(set);
|
|
assert(!set['topics:create']);
|
|
});
|
|
});
|
|
|
|
describe('postingRestrictedToMods (true on assert and re-assertion)', () => {
|
|
let cid;
|
|
let uid;
|
|
|
|
before(async () => {
|
|
// Set up a mock group with `postingRestrictedToMods` bit set
|
|
({ id: cid } = helpers.mocks.group({
|
|
postingRestrictedToMods: true,
|
|
}));
|
|
|
|
// Unprivileged user for testing
|
|
uid = await user.create({ username: utils.generateUUID().slice(0, 10) });
|
|
|
|
// Assert group, then re-assert again
|
|
await activitypub.actors.assertGroup([cid]);
|
|
await activitypub.actors.assertGroup([cid], { update: true });
|
|
});
|
|
|
|
it('should fail the privileges .can() check if requested', async () => {
|
|
const can = await privileges.categories.can('topics:create', cid, uid);
|
|
assert(!can);
|
|
});
|
|
|
|
it('should return false in the privilege set when requested', async () => {
|
|
const set = await privileges.categories.get(cid, uid);
|
|
|
|
assert(set);
|
|
assert(!set['topics:create']);
|
|
});
|
|
});
|
|
|
|
describe('postingRestrictedToMods (true on assert, false on update)', () => {
|
|
let cid;
|
|
let uid;
|
|
|
|
before(async () => {
|
|
// Set up a mock group with `postingRestrictedToMods` bit set
|
|
({ id: cid } = helpers.mocks.group({
|
|
postingRestrictedToMods: true,
|
|
}));
|
|
|
|
// Unprivileged user for testing
|
|
uid = await user.create({ username: utils.generateUUID().slice(0, 10) });
|
|
|
|
await activitypub.actors.assertGroup([cid]);
|
|
|
|
// Group updated "remotely"
|
|
helpers.mocks.group({
|
|
id: cid,
|
|
postingRestrictedToMods: false,
|
|
});
|
|
});
|
|
|
|
it('should remove the privilege mask if the bit is not present on group actor update', async () => {
|
|
let can = await privileges.categories.can('topics:create', cid, uid);
|
|
assert(!can, 'Initial state should be denied due to mask.');
|
|
|
|
// Group re-assertion
|
|
await activitypub.actors.assertGroup([cid], { update: true });
|
|
|
|
// Ensure mask is gone from db
|
|
const memberCount = await db.setCount(`cid:${cid}:privilegeMask`);
|
|
assert.strictEqual(memberCount, 0);
|
|
|
|
can = await privileges.categories.can('topics:create', cid, uid);
|
|
assert(can, 'Privilege should be restored after mask removal.');
|
|
});
|
|
});
|
|
|
|
describe('postingRestrictedToMods (true on assert, property missing on update)', () => {
|
|
let cid;
|
|
let uid;
|
|
|
|
before(async () => {
|
|
// Set up a mock group with `postingRestrictedToMods` bit set
|
|
({ id: cid } = helpers.mocks.group({
|
|
postingRestrictedToMods: true,
|
|
}));
|
|
|
|
// Unprivileged user for testing
|
|
uid = await user.create({ username: utils.generateUUID().slice(0, 10) });
|
|
|
|
await activitypub.actors.assertGroup([cid]);
|
|
|
|
// Group updated "remotely"
|
|
helpers.mocks.group({
|
|
id: cid,
|
|
});
|
|
});
|
|
|
|
it('should remove the privilege mask if the bit is not present on group actor update', async () => {
|
|
let can = await privileges.categories.can('topics:create', cid, uid);
|
|
assert(!can, 'Initial state should be denied due to mask.');
|
|
|
|
// Group re-assertion
|
|
await activitypub.actors.assertGroup([cid], { update: true });
|
|
|
|
// Ensure mask is gone from db
|
|
const memberCount = await db.setCount(`cid:${cid}:privilegeMask`);
|
|
assert.strictEqual(memberCount, 0);
|
|
|
|
can = await privileges.categories.can('topics:create', cid, uid);
|
|
assert(can, 'Privilege should be restored after mask removal.');
|
|
});
|
|
});
|
|
|
|
describe('postingRestrictedToMods (false on assert, true on update)', () => {
|
|
let cid;
|
|
let uid;
|
|
|
|
before(async () => {
|
|
// Set up a mock group with `postingRestrictedToMods` bit set to false
|
|
({ id: cid } = helpers.mocks.group({
|
|
postingRestrictedToMods: false,
|
|
}));
|
|
|
|
// Unprivileged user for testing
|
|
uid = await user.create({ username: utils.generateUUID().slice(0, 10) });
|
|
|
|
await activitypub.actors.assertGroup([cid]);
|
|
|
|
|
|
// Update group "remotely", re-assert
|
|
helpers.mocks.group({
|
|
id: cid,
|
|
postingRestrictedToMods: true,
|
|
});
|
|
await activitypub.actors.assertGroup([cid], { update: true });
|
|
});
|
|
|
|
it('should fail the privileges .can() check if requested', async () => {
|
|
const can = await privileges.categories.can('topics:create', cid, uid);
|
|
assert(!can);
|
|
});
|
|
|
|
it('should return false in the privilege set when requested', async () => {
|
|
const set = await privileges.categories.get(cid, uid);
|
|
|
|
assert(set);
|
|
assert(!set['topics:create']);
|
|
});
|
|
});
|
|
}); |