From 86d70eca746783f35317d0d217a5cd34f34d8fbd Mon Sep 17 00:00:00 2001 From: barisusakli Date: Mon, 25 Jan 2016 13:36:10 +0200 Subject: [PATCH] closes #4103 --- public/src/admin/manage/users.js | 4 +- public/src/client/account/header.js | 4 +- src/controllers/accounts/helpers.js | 5 ++ src/groups/create.js | 12 ++-- src/install.js | 26 +++++++++ src/privileges/topics.js | 17 +++--- src/privileges/users.js | 87 +++++++++++++++++------------ src/socket.io/admin/user.js | 47 +++------------- src/socket.io/user.js | 3 +- src/socket.io/user/ban.js | 70 +++++++++++++++++++++++ src/upgrade.js | 42 +++++++++++++- src/user.js | 4 ++ 12 files changed, 223 insertions(+), 98 deletions(-) create mode 100644 src/socket.io/user/ban.js diff --git a/public/src/admin/manage/users.js b/public/src/admin/manage/users.js index 57be3f327c..09c9e3a01e 100644 --- a/public/src/admin/manage/users.js +++ b/public/src/admin/manage/users.js @@ -52,7 +52,7 @@ define('admin/manage/users', ['admin/modules/selectable'], function(selectable) bootbox.confirm('Do you really want to ban?', function(confirm) { if (confirm) { - socket.emit('admin.user.banUsers', uids, done('User(s) banned!', '.ban', true)); + socket.emit('user.banUsers', uids, done('User(s) banned!', '.ban', true)); } }); return false; @@ -64,7 +64,7 @@ define('admin/manage/users', ['admin/modules/selectable'], function(selectable) return; } - socket.emit('admin.user.unbanUsers', uids, done('User(s) unbanned!', '.ban', false)); + socket.emit('user.unbanUsers', uids, done('User(s) unbanned!', '.ban', false)); return false; }); diff --git a/public/src/client/account/header.js b/public/src/client/account/header.js index fdd27207c9..95bd0fb0c2 100644 --- a/public/src/client/account/header.js +++ b/public/src/client/account/header.js @@ -103,7 +103,7 @@ define('forum/account/header', [ if (!confirm) { return; } - socket.emit('admin.user.banUsers', [ajaxify.data.theirid], function(err) { + socket.emit('user.banUsers', [ajaxify.data.theirid], function(err) { if (err) { return app.alertError(err.message); } @@ -115,7 +115,7 @@ define('forum/account/header', [ } function unbanAccount() { - socket.emit('admin.user.unbanUsers', [ajaxify.data.theirid], function(err) { + socket.emit('user.unbanUsers', [ajaxify.data.theirid], function(err) { if (err) { return app.alertError(err.message); } diff --git a/src/controllers/accounts/helpers.js b/src/controllers/accounts/helpers.js index a1dcbbfb65..f7a0fca24d 100644 --- a/src/controllers/accounts/helpers.js +++ b/src/controllers/accounts/helpers.js @@ -32,6 +32,9 @@ helpers.getUserDataByUserSlug = function(userslug, callerUID, callback) { isAdmin : function(next) { user.isAdministrator(callerUID, next); }, + isGlobalModerator: function(next) { + user.isGlobalModerator(callerUID, next); + }, ips: function(next) { user.getIPs(uid, 4, next); }, @@ -78,6 +81,8 @@ helpers.getUserDataByUserSlug = function(userslug, callerUID, callback) { userData.yourid = callerUID; userData.theirid = userData.uid; userData.isAdmin = isAdmin; + userData.isGlobalModerator = results.isGlobalModerator; + userData.canBan = isAdmin || results.isGlobalModerator; userData.isSelf = self; userData.showHidden = self || isAdmin; userData.groups = Array.isArray(results.groups) && results.groups.length ? results.groups[0] : []; diff --git a/src/groups/create.js b/src/groups/create.js index 3eeed9a893..5a27361d04 100644 --- a/src/groups/create.js +++ b/src/groups/create.js @@ -9,7 +9,7 @@ var async = require('async'), module.exports = function(Groups) { Groups.create = function(data, callback) { - var system = data.name === 'administrators' || data.name === 'registered-users' || Groups.isPrivilegeGroup(data.name); + var system = data.name === 'administrators' || data.name === 'registered-users' || data.name === 'Global Moderators' || Groups.isPrivilegeGroup(data.name); var groupData; var timestamp = data.timestamp || Date.now(); @@ -26,6 +26,7 @@ module.exports = function(Groups) { } var memberCount = data.hasOwnProperty('ownerUid') ? 1 : 0; + var isPrivate = data.hasOwnProperty('private') ? parseInt(data.private, 10) : 1; var slug = utils.slugify(data.name); groupData = { name: data.name, @@ -34,10 +35,11 @@ module.exports = function(Groups) { userTitle: data.name, description: data.description || '', memberCount: memberCount, - deleted: '0', - hidden: data.hidden || '0', - system: system ? '1' : '0', - private: data.private || '1' + deleted: 0, + hidden: parseInt(data.hidden, 10) === 1 ? 1 : 0, + system: system ? 1 : 0, + private: isPrivate, + disableJoinRequests: parseInt(data.disableJoinRequests, 10) === 1 ? 1 : 0 }; plugins.fireHook('filter:group.create', {group: groupData, data: data}, next); }, diff --git a/src/install.js b/src/install.js index bc6fb2a433..de436737f7 100644 --- a/src/install.js +++ b/src/install.js @@ -330,6 +330,31 @@ function createAdmin(callback) { } } +function createGlobalModeratorsGroup(next) { + var groups = require('./groups'); + async.waterfall([ + function (next) { + groups.exists('Global Moderators', next); + }, + function (exists, next) { + if (exists) { + winston.info('Global Moderators group found, skipping creation!'); + return next(); + } + groups.create({ + name: 'Global Moderators', + description: 'Forum wide moderators', + hidden: 0, + private: 1, + disableJoinRequests: 1 + }, next); + }, + function (groupData, next) { + groups.show('Global Moderators', next); + } + ], next); +} + function createCategories(next) { var Categories = require('./categories'); @@ -476,6 +501,7 @@ install.setup = function (callback) { enableDefaultTheme, createCategories, createAdministrator, + createGlobalModeratorsGroup, createMenuItems, createWelcomePost, enableDefaultPlugins, diff --git a/src/privileges/topics.js b/src/privileges/topics.js index ab0ee8fe74..6cc66483e1 100644 --- a/src/privileges/topics.js +++ b/src/privileges/topics.js @@ -1,16 +1,13 @@ 'use strict'; -var async = require('async'), - winston = require('winston'), - - db = require('../database'), - topics = require('../topics'), - user = require('../user'), - helpers = require('./helpers'), - groups = require('../groups'), - categories = require('../categories'), - plugins = require('../plugins'); +var async = require('async'); + +var topics = require('../topics'); +var user = require('../user'); +var helpers = require('./helpers'); +var categories = require('../categories'); +var plugins = require('../plugins'); module.exports = function(privileges) { diff --git a/src/privileges/users.js b/src/privileges/users.js index 4562beb4cd..4ee2d69461 100644 --- a/src/privileges/users.js +++ b/src/privileges/users.js @@ -1,16 +1,10 @@ 'use strict'; -var async = require('async'), - winston = require('winston'), +var async = require('async'); - db = require('../database'), - topics = require('../topics'), - user = require('../user'), - helpers = require('./helpers'), - groups = require('../groups'), - categories = require('../categories'), - plugins = require('../plugins'); +var groups = require('../groups'); +var plugins = require('../plugins'); module.exports = function(privileges) { @@ -24,6 +18,14 @@ module.exports = function(privileges) { } }; + privileges.users.isGlobalModerator = function(uid, callback) { + if (Array.isArray(uid)) { + groups.isMembers(uid, 'Global Moderators', callback); + } else { + groups.isMember(uid, 'Global Moderators', callback); + } + }; + privileges.users.isModerator = function(uid, cid, callback) { if (Array.isArray(cid)) { isModeratorOfCategories(cid, uid, callback); @@ -41,44 +43,56 @@ module.exports = function(privileges) { return filterIsModerator(cids, uid, cids.map(function() {return false;}), callback); } - var uniqueCids = cids.filter(function(cid, index, array) { - return array.indexOf(cid) === index; - }); - - var groupNames = uniqueCids.map(function(cid) { - return 'cid:' + cid + ':privileges:mods'; // At some point we should *probably* change this to "moderate" as well - }), - groupListNames = uniqueCids.map(function(cid) { - return 'cid:' + cid + ':privileges:groups:moderate'; - }); - - async.parallel({ - user: async.apply(groups.isMemberOfGroups, uid, groupNames), - group: async.apply(groups.isMemberOfGroupsList, uid, groupListNames) - }, function(err, checks) { + privileges.users.isGlobalModerator(uid, function(err, isGlobalModerator) { if (err) { return callback(err); } + if (isGlobalModerator) { + return filterIsModerator(cids, uid, cids.map(function() {return true;}), callback); + } - var isMembers = checks.user.map(function(isMember, idx) { - return isMember || checks.group[idx]; - }), - map = {}; - uniqueCids.forEach(function(cid, index) { - map[cid] = isMembers[index]; + var uniqueCids = cids.filter(function(cid, index, array) { + return array.indexOf(cid) === index; }); - var isModerator = cids.map(function(cid) { - return map[cid]; + var groupNames = uniqueCids.map(function(cid) { + return 'cid:' + cid + ':privileges:mods'; // At some point we should *probably* change this to "moderate" as well + }); + + var groupListNames = uniqueCids.map(function(cid) { + return 'cid:' + cid + ':privileges:groups:moderate'; }); - filterIsModerator(cids, uid, isModerator, callback); + async.parallel({ + user: async.apply(groups.isMemberOfGroups, uid, groupNames), + group: async.apply(groups.isMemberOfGroupsList, uid, groupListNames) + }, function(err, checks) { + if (err) { + return callback(err); + } + + var isMembers = checks.user.map(function(isMember, idx) { + return isMember || checks.group[idx]; + }), + map = {}; + + uniqueCids.forEach(function(cid, index) { + map[cid] = isMembers[index]; + }); + + var isModerator = cids.map(function(cid) { + return map[cid]; + }); + + filterIsModerator(cids, uid, isModerator, callback); + }); }); } function isModeratorsOfCategory(cid, uids, callback) { async.parallel([ + async.apply(privileges.users.isGlobalModerator, uids), async.apply(groups.isMembers, uids, 'cid:' + cid + ':privileges:mods'), async.apply(groups.isMembersOfGroupList, uids, 'cid:' + cid + ':privileges:groups:moderate') ], function(err, checks) { @@ -87,8 +101,8 @@ module.exports = function(privileges) { } var isModerator = checks[0].map(function(isMember, idx) { - return isMember || checks[1][idx]; - }); + return isMember || checks[1][idx] || checks[2][idx]; + }); filterIsModerator(cid, uids, isModerator, callback); }); @@ -96,6 +110,7 @@ module.exports = function(privileges) { function isModeratorOfCategory(cid, uid, callback) { async.parallel([ + async.apply(privileges.users.isGlobalModerator, uid), async.apply(groups.isMember, uid, 'cid:' + cid + ':privileges:mods'), async.apply(groups.isMemberOfGroupList, uid, 'cid:' + cid + ':privileges:groups:moderate') ], function(err, checks) { @@ -103,7 +118,7 @@ module.exports = function(privileges) { return callback(err); } - var isModerator = checks[0] || checks[1]; + var isModerator = checks[0] || checks[1] || checks[2]; filterIsModerator(cid, uid, isModerator, callback); }); } diff --git a/src/socket.io/admin/user.js b/src/socket.io/admin/user.js index e8e2fe9eda..a241affb90 100644 --- a/src/socket.io/admin/user.js +++ b/src/socket.io/admin/user.js @@ -1,15 +1,14 @@ "use strict"; -var async = require('async'), - db = require('../../database'), - groups = require('../../groups'), - user = require('../../user'), - events = require('../../events'), - meta = require('../../meta'), - websockets = require('../index'), - User = {}; +var async = require('async'); +var db = require('../../database'); +var groups = require('../../groups'); +var user = require('../../user'); +var events = require('../../events'); +var meta = require('../../meta'); +var User = {}; User.makeAdmins = function(socket, uids, callback) { if(!Array.isArray(uids)) { @@ -60,38 +59,6 @@ User.createUser = function(socket, userData, callback) { user.create(userData, callback); }; -User.banUsers = function(socket, uids, callback) { - toggleBan(uids, User.banUser, callback); -}; - -User.unbanUsers = function(socket, uids, callback) { - toggleBan(uids, user.unban, callback); -}; - -function toggleBan(uids, method, callback) { - if(!Array.isArray(uids)) { - return callback(new Error('[[error:invalid-data]]')); - } - async.each(uids, method, callback); -} - -User.banUser = function(uid, callback) { - user.isAdministrator(uid, function(err, isAdmin) { - if (err || isAdmin) { - return callback(err || new Error('[[error:cant-ban-other-admins]]')); - } - - user.ban(uid, function(err) { - if (err) { - return callback(err); - } - - websockets.in('uid_' + uid).emit('event:banned'); - - callback(); - }); - }); -}; User.resetLockouts = function(socket, uids, callback) { if (!Array.isArray(uids)) { diff --git a/src/socket.io/user.js b/src/socket.io/user.js index d97bad7222..67068fea95 100644 --- a/src/socket.io/user.js +++ b/src/socket.io/user.js @@ -8,7 +8,6 @@ var topics = require('../topics'); var notifications = require('../notifications'); var messaging = require('../messaging'); var plugins = require('../plugins'); -var websockets = require('./index'); var meta = require('../meta'); var events = require('../events'); var emailer = require('../emailer'); @@ -16,11 +15,11 @@ var db = require('../database'); var SocketUser = {}; - require('./user/profile')(SocketUser); require('./user/search')(SocketUser); require('./user/status')(SocketUser); require('./user/picture')(SocketUser); +require('./user/ban')(SocketUser); SocketUser.exists = function(socket, data, callback) { if (data && data.username) { diff --git a/src/socket.io/user/ban.js b/src/socket.io/user/ban.js new file mode 100644 index 0000000000..9d7a8cf72e --- /dev/null +++ b/src/socket.io/user/ban.js @@ -0,0 +1,70 @@ +'use strict'; + +var async = require('async'); +var user = require('../../user'); +var websockets = require('../index'); +var events = require('../../events'); + +module.exports = function(SocketUser) { + + SocketUser.banUsers = function(socket, uids, callback) { + toggleBan(socket.uid, uids, SocketUser.banUser, function(err) { + if (err) { + return callback(err); + } + async.each(uids, function(uid, next) { + events.log({ + type: 'user-ban', + uid: socket.uid, + targetUid: uid, + ip: socket.ip + }, next); + }, callback); + }); + }; + + SocketUser.unbanUsers = function(socket, uids, callback) { + toggleBan(socket.uid, uids, user.unban, callback); + }; + + function toggleBan(uid, uids, method, callback) { + if (!Array.isArray(uids)) { + return callback(new Error('[[error:invalid-data]]')); + } + + async.waterfall([ + function (next) { + async.parallel({ + isAdmin: async.apply(user.isAdministrator, uid), + isGlobalMod: async.apply(user.isGlobalModerator, uid) + }, next); + }, + function (results, next) { + if (!results.isAdmin && !results.isGlobalMod) { + return next(new Error('[[error:no-privileges]]')); + } + async.each(uids, method, next); + } + ], callback); + } + + SocketUser.banUser = function(uid, callback) { + async.waterfall([ + function (next) { + user.isAdministrator(uid, next); + }, + function (isAdmin, next) { + if (isAdmin) { + return next(new Error('[[error:cant-ban-other-admins]]')); + } + user.ban(uid, next); + }, + function (next) { + websockets.in('uid_' + uid).emit('event:banned'); + next(); + } + ], callback); + }; + +}; + diff --git a/src/upgrade.js b/src/upgrade.js index 9cc1d6c2d1..b2dc5535aa 100644 --- a/src/upgrade.js +++ b/src/upgrade.js @@ -10,7 +10,7 @@ var db = require('./database'), schemaDate, thisSchemaDate, // IMPORTANT: REMEMBER TO UPDATE VALUE OF latestSchema - latestSchema = Date.UTC(2016, 0, 20); + latestSchema = Date.UTC(2016, 0, 23); Upgrade.check = function(callback) { db.get('schemaDate', function(err, value) { @@ -369,6 +369,46 @@ Upgrade.upgrade = function(callback) { winston.info('[2016/01/20] Creating users:notvalidated skipped!'); next(); } + }, + function(next) { + thisSchemaDate = Date.UTC(2016, 0, 23); + + if (schemaDate < thisSchemaDate) { + updatesMade = true; + winston.info('[2016/01/23] Creating Global moderators group'); + + var groups = require('./groups'); + async.waterfall([ + function (next) { + groups.exists('Global Moderators', next); + }, + function (exists, next) { + if (exists) { + return next(); + } + groups.create({ + name: 'Global Moderators', + description: 'Forum wide moderators', + hidden: 0, + private: 1, + disableJoinRequests: 1 + }, next); + }, + function (groupData, next) { + groups.show('Global Moderators', next); + } + ], function(err) { + if (err) { + return next(err); + } + + winston.info('[2016/01/23] Creating Global moderators group done!'); + Upgrade.update(thisSchemaDate, next); + }); + } else { + winston.info('[2016/01/23] Creating Global moderators group skipped!'); + next(); + } } // Add new schema updates here // IMPORTANT: REMEMBER TO UPDATE VALUE OF latestSchema IN LINE 24!!! diff --git a/src/user.js b/src/user.js index cbdf7966e1..8bb6ff28f2 100644 --- a/src/user.js +++ b/src/user.js @@ -230,6 +230,10 @@ var async = require('async'), privileges.users.isAdministrator(uid, callback); }; + User.isGlobalModerator = function(uid, callback) { + privileges.users.isGlobalModerator(uid, callback); + }; + User.isAdminOrSelf = function(callerUid, uid, callback) { if (parseInt(callerUid, 10) === parseInt(uid, 10)) { return callback();