mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-12-17 22:10:23 +01:00
Merge branch 'master' of github.com:NodeBB/NodeBB
This commit is contained in:
@@ -89,7 +89,7 @@
|
|||||||
"nodebb-plugin-spam-be-gone": "0.6.7",
|
"nodebb-plugin-spam-be-gone": "0.6.7",
|
||||||
"nodebb-rewards-essentials": "0.1.2",
|
"nodebb-rewards-essentials": "0.1.2",
|
||||||
"nodebb-theme-lavender": "5.0.11",
|
"nodebb-theme-lavender": "5.0.11",
|
||||||
"nodebb-theme-persona": "10.1.33",
|
"nodebb-theme-persona": "10.1.34",
|
||||||
"nodebb-theme-slick": "1.2.28",
|
"nodebb-theme-slick": "1.2.28",
|
||||||
"nodebb-theme-vanilla": "11.1.15",
|
"nodebb-theme-vanilla": "11.1.15",
|
||||||
"nodebb-widget-essentials": "4.0.18",
|
"nodebb-widget-essentials": "4.0.18",
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
const ipaddr = require('ipaddr.js');
|
const ipaddr = require('ipaddr.js');
|
||||||
const winston = require('winston');
|
const winston = require('winston');
|
||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
|
const validator = require('validator');
|
||||||
|
|
||||||
const db = require('../database');
|
const db = require('../database');
|
||||||
const pubsub = require('../pubsub');
|
const pubsub = require('../pubsub');
|
||||||
@@ -128,7 +129,7 @@ Blacklist.validate = function (rules) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!addr || whitelist.includes(rule)) {
|
if (!addr || whitelist.includes(rule)) {
|
||||||
invalid.push(rule);
|
invalid.push(validator.escape(rule));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -110,6 +110,7 @@ module.exports = function (privileges) {
|
|||||||
return await utils.promiseParallel({
|
return await utils.promiseParallel({
|
||||||
categories: categories.getCategoriesFields(cids, ['disabled']),
|
categories: categories.getCategoriesFields(cids, ['disabled']),
|
||||||
allowedTo: helpers.isUserAllowedTo(privilege, uid, cids),
|
allowedTo: helpers.isUserAllowedTo(privilege, uid, cids),
|
||||||
|
view_deleted: helpers.isUserAllowedTo('posts:view_deleted', uid, cids),
|
||||||
isAdmin: user.isAdministrator(uid),
|
isAdmin: user.isAdministrator(uid),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -88,16 +88,17 @@ module.exports = function (privileges) {
|
|||||||
cids = _.uniq(cids);
|
cids = _.uniq(cids);
|
||||||
|
|
||||||
const results = await privileges.categories.getBase(privilege, cids, uid);
|
const results = await privileges.categories.getBase(privilege, cids, uid);
|
||||||
cids = cids.filter(function (cid, index) {
|
const allowedCids = cids.filter(function (cid, index) {
|
||||||
return !results.categories[index].disabled &&
|
return !results.categories[index].disabled &&
|
||||||
(results.allowedTo[index] || results.isAdmin);
|
(results.allowedTo[index] || results.isAdmin);
|
||||||
});
|
});
|
||||||
|
|
||||||
const cidsSet = new Set(cids);
|
const cidsSet = new Set(allowedCids);
|
||||||
|
const canViewDeleted = _.zipObject(cids, results.view_deleted);
|
||||||
|
|
||||||
pids = postData.filter(function (post) {
|
pids = postData.filter(function (post) {
|
||||||
return post.topic && cidsSet.has(post.topic.cid) &&
|
return post.topic && cidsSet.has(post.topic.cid) &&
|
||||||
((!post.topic.deleted && !post.deleted) || results.isAdmin);
|
((!post.topic.deleted && !post.deleted) || canViewDeleted[post.topic.cid] || results.isAdmin);
|
||||||
}).map(post => post.pid);
|
}).map(post => post.pid);
|
||||||
|
|
||||||
const data = await plugins.fireHook('filter:privileges.posts.filter', {
|
const data = await plugins.fireHook('filter:privileges.posts.filter', {
|
||||||
|
|||||||
@@ -68,14 +68,15 @@ module.exports = function (privileges) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const topicsData = await topics.getTopicsFields(tids, ['tid', 'cid', 'deleted']);
|
const topicsData = await topics.getTopicsFields(tids, ['tid', 'cid', 'deleted']);
|
||||||
let cids = _.uniq(topicsData.map(topic => topic.cid));
|
const cids = _.uniq(topicsData.map(topic => topic.cid));
|
||||||
const results = await privileges.categories.getBase(privilege, cids, uid);
|
const results = await privileges.categories.getBase(privilege, cids, uid);
|
||||||
|
|
||||||
cids = cids.filter((cid, index) => !results.categories[index].disabled && (results.allowedTo[index] || results.isAdmin));
|
const allowedCids = cids.filter((cid, index) => !results.categories[index].disabled && (results.allowedTo[index] || results.isAdmin));
|
||||||
|
|
||||||
const cidsSet = new Set(cids);
|
const cidsSet = new Set(allowedCids);
|
||||||
|
const canViewDeleted = _.zipObject(cids, results.view_deleted);
|
||||||
|
|
||||||
tids = topicsData.filter(t => cidsSet.has(t.cid) && (!t.deleted || results.isAdmin)).map(t => t.tid);
|
tids = topicsData.filter(t => cidsSet.has(t.cid) && (!t.deleted || canViewDeleted[t.cid] || results.isAdmin)).map(t => t.tid);
|
||||||
|
|
||||||
const data = await plugins.fireHook('filter:privileges.topics.filter', {
|
const data = await plugins.fireHook('filter:privileges.topics.filter', {
|
||||||
privilege: privilege,
|
privilege: privilege,
|
||||||
@@ -115,7 +116,7 @@ module.exports = function (privileges) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
privileges.topics.canDelete = async function (tid, uid) {
|
privileges.topics.canDelete = async function (tid, uid) {
|
||||||
const topicData = await topics.getTopicFields(tid, ['cid', 'postcount']);
|
const topicData = await topics.getTopicFields(tid, ['uid', 'cid', 'postcount', 'deleterUid']);
|
||||||
const [isModerator, isAdministrator, isOwner, allowedTo] = await Promise.all([
|
const [isModerator, isAdministrator, isOwner, allowedTo] = await Promise.all([
|
||||||
user.isModerator(uid, topicData.cid),
|
user.isModerator(uid, topicData.cid),
|
||||||
user.isAdministrator(uid),
|
user.isAdministrator(uid),
|
||||||
@@ -135,7 +136,8 @@ module.exports = function (privileges) {
|
|||||||
throw new Error(langKey);
|
throw new Error(langKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
return allowedTo[0] && (isOwner || isModerator);
|
const deleterUid = topicData.deleterUid;
|
||||||
|
return allowedTo[0] && ((isOwner && (deleterUid === 0 || deleterUid === topicData.uid)) || isModerator);
|
||||||
};
|
};
|
||||||
|
|
||||||
privileges.topics.canEdit = async function (tid, uid) {
|
privileges.topics.canEdit = async function (tid, uid) {
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ const intFields = [
|
|||||||
'tid', 'cid', 'uid', 'mainPid', 'postcount',
|
'tid', 'cid', 'uid', 'mainPid', 'postcount',
|
||||||
'viewcount', 'deleted', 'locked', 'pinned',
|
'viewcount', 'deleted', 'locked', 'pinned',
|
||||||
'timestamp', 'upvotes', 'downvotes', 'lastposttime',
|
'timestamp', 'upvotes', 'downvotes', 'lastposttime',
|
||||||
|
'deleterUid',
|
||||||
];
|
];
|
||||||
|
|
||||||
module.exports = function (Topics) {
|
module.exports = function (Topics) {
|
||||||
|
|||||||
@@ -50,8 +50,7 @@ Topics.getTopics = async function (tids, options) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tids = await privileges.topics.filterTids('topics:read', tids, uid);
|
tids = await privileges.topics.filterTids('topics:read', tids, uid);
|
||||||
const topics = await Topics.getTopicsByTids(tids, options);
|
return await Topics.getTopicsByTids(tids, options);
|
||||||
return topics;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Topics.getTopicsByTids = async function (tids, options) {
|
Topics.getTopicsByTids = async function (tids, options) {
|
||||||
|
|||||||
@@ -30,11 +30,8 @@ module.exports = function (User) {
|
|||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
const isArray = Array.isArray(uid);
|
const isArray = Array.isArray(uid);
|
||||||
uid = isArray ? uid : [uid];
|
uid = isArray ? uid : [uid];
|
||||||
const lastonline = db.sortedSetScores('users:online', uid);
|
const lastonline = await db.sortedSetScores('users:online', uid);
|
||||||
const isOnline = uid.map(function (uid, index) {
|
const isOnline = uid.map((uid, index) => (now - lastonline[index]) < (meta.config.onlineCutoff * 60000));
|
||||||
return (now - lastonline[index]) < (meta.config.onlineCutoff * 60000);
|
|
||||||
});
|
|
||||||
|
|
||||||
return isArray ? isOnline : isOnline[0];
|
return isArray ? isOnline : isOnline[0];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -142,6 +142,14 @@ describe('Post\'s', function () {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should fail to change owner if user is not authorized', async function () {
|
||||||
|
try {
|
||||||
|
await socketPosts.changeOwner({ uid: voterUid }, { pids: [1, 2], toUid: voterUid });
|
||||||
|
} catch (err) {
|
||||||
|
assert.strictEqual(err.message, '[[error:no-privileges]]');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
it('should return falsy if post does not exist', function (done) {
|
it('should return falsy if post does not exist', function (done) {
|
||||||
posts.getPostData(9999, function (err, postData) {
|
posts.getPostData(9999, function (err, postData) {
|
||||||
assert.ifError(err);
|
assert.ifError(err);
|
||||||
|
|||||||
@@ -23,9 +23,11 @@ describe('Topic\'s', function () {
|
|||||||
var categoryObj;
|
var categoryObj;
|
||||||
var adminUid;
|
var adminUid;
|
||||||
var adminJar;
|
var adminJar;
|
||||||
|
var fooUid;
|
||||||
|
|
||||||
before(async function () {
|
before(async function () {
|
||||||
adminUid = await User.create({ username: 'admin', password: '123456' });
|
adminUid = await User.create({ username: 'admin', password: '123456' });
|
||||||
|
fooUid = await User.create({ username: 'foo' });
|
||||||
await groups.join('administrators', adminUid);
|
await groups.join('administrators', adminUid);
|
||||||
adminJar = await helpers.loginUser('admin', '123456');
|
adminJar = await helpers.loginUser('admin', '123456');
|
||||||
|
|
||||||
@@ -572,6 +574,21 @@ describe('Topic\'s', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should not allow user to restore their topic if it was deleted by an admin', async function () {
|
||||||
|
const result = await topics.post({
|
||||||
|
uid: fooUid,
|
||||||
|
title: 'topic for restore test',
|
||||||
|
content: 'topic content',
|
||||||
|
cid: categoryObj.cid,
|
||||||
|
});
|
||||||
|
await socketTopics.delete({ uid: adminUid }, { tids: [result.topicData.tid], cid: categoryObj.cid });
|
||||||
|
try {
|
||||||
|
await socketTopics.restore({ uid: fooUid }, { tids: [result.topicData.tid], cid: categoryObj.cid });
|
||||||
|
} catch (err) {
|
||||||
|
assert.strictEqual(err.message, '[[error:no-privileges]]');
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('order pinned topics', function () {
|
describe('order pinned topics', function () {
|
||||||
|
|||||||
18
test/user.js
18
test/user.js
@@ -2164,10 +2164,20 @@ describe('User', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return offline if user is guest', function (done) {
|
describe('status/online', function () {
|
||||||
var status = User.getStatus({ uid: 0 });
|
it('should return offline if user is guest', function (done) {
|
||||||
assert.strictEqual(status, 'offline');
|
var status = User.getStatus({ uid: 0 });
|
||||||
done();
|
assert.strictEqual(status, 'offline');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return offline if user is guest', async function () {
|
||||||
|
assert.strictEqual(await User.isOnline(0), false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return true', async function () {
|
||||||
|
assert.strictEqual(await User.isOnline(testUid), true);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('isPrivilegedOrSelf', function () {
|
describe('isPrivilegedOrSelf', function () {
|
||||||
|
|||||||
Reference in New Issue
Block a user