feat: #7743, privileges

This commit is contained in:
Barış Soner Uşaklı
2019-07-20 22:12:22 -04:00
parent 627ecaf6bb
commit faccb191ec
7 changed files with 623 additions and 1028 deletions

View File

@@ -1,212 +1,137 @@
'use strict';
var async = require('async');
var _ = require('lodash');
const _ = require('lodash');
var groups = require('../groups');
var user = require('../user');
var plugins = require('../plugins');
const groups = require('../groups');
const user = require('../user');
const plugins = require('../plugins');
var helpers = module.exports;
const helpers = module.exports;
var uidToSystemGroup = {
const uidToSystemGroup = {
0: 'guests',
'-1': 'spiders',
};
helpers.some = function (tasks, callback) {
async.some(tasks, function (task, next) {
task(next);
}, callback);
};
helpers.isUserAllowedTo = function (privilege, uid, cid, callback) {
helpers.isUserAllowedTo = async function (privilege, uid, cid) {
if (Array.isArray(privilege) && !Array.isArray(cid)) {
isUserAllowedToPrivileges(privilege, uid, cid, callback);
return await isUserAllowedToPrivileges(privilege, uid, cid);
} else if (Array.isArray(cid) && !Array.isArray(privilege)) {
isUserAllowedToCids(privilege, uid, cid, callback);
} else {
return callback(new Error('[[error:invalid-data]]'));
return await isUserAllowedToCids(privilege, uid, cid);
}
throw new Error('[[error:invalid-data]]');
};
function isUserAllowedToCids(privilege, uid, cids, callback) {
async function isUserAllowedToCids(privilege, uid, cids) {
if (parseInt(uid, 10) <= 0) {
return isSystemGroupAllowedToCids(privilege, uid, cids, callback);
return await isSystemGroupAllowedToCids(privilege, uid, cids);
}
var userKeys = [];
var groupKeys = [];
const userKeys = [];
const groupKeys = [];
cids.forEach(function (cid) {
userKeys.push('cid:' + cid + ':privileges:' + privilege);
groupKeys.push('cid:' + cid + ':privileges:groups:' + privilege);
});
checkIfAllowed(uid, userKeys, groupKeys, callback);
return await checkIfAllowed(uid, userKeys, groupKeys);
}
function isUserAllowedToPrivileges(privileges, uid, cid, callback) {
async function isUserAllowedToPrivileges(privileges, uid, cid) {
if (parseInt(uid, 10) <= 0) {
return isSystemGroupAllowedToPrivileges(privileges, uid, cid, callback);
return await isSystemGroupAllowedToPrivileges(privileges, uid, cid);
}
var userKeys = [];
var groupKeys = [];
const userKeys = [];
const groupKeys = [];
privileges.forEach(function (privilege) {
userKeys.push('cid:' + cid + ':privileges:' + privilege);
groupKeys.push('cid:' + cid + ':privileges:groups:' + privilege);
});
checkIfAllowed(uid, userKeys, groupKeys, callback);
return await checkIfAllowed(uid, userKeys, groupKeys);
}
function checkIfAllowed(uid, userKeys, groupKeys, callback) {
async.waterfall([
function (next) {
async.parallel({
hasUserPrivilege: function (next) {
groups.isMemberOfGroups(uid, userKeys, next);
},
hasGroupPrivilege: function (next) {
groups.isMemberOfGroupsList(uid, groupKeys, next);
},
}, next);
},
function (results, next) {
var result = userKeys.map(function (key, index) {
return results.hasUserPrivilege[index] || results.hasGroupPrivilege[index];
});
next(null, result);
},
], callback);
async function checkIfAllowed(uid, userKeys, groupKeys) {
const [hasUserPrivilege, hasGroupPrivilege] = await Promise.all([
groups.isMemberOfGroups(uid, userKeys),
groups.isMemberOfGroupsList(uid, groupKeys),
]);
return userKeys.map((key, index) => hasUserPrivilege[index] || hasGroupPrivilege[index]);
}
helpers.isUsersAllowedTo = function (privilege, uids, cid, callback) {
async.waterfall([
function (next) {
async.parallel({
hasUserPrivilege: function (next) {
groups.isMembers(uids, 'cid:' + cid + ':privileges:' + privilege, next);
},
hasGroupPrivilege: function (next) {
groups.isMembersOfGroupList(uids, 'cid:' + cid + ':privileges:groups:' + privilege, next);
},
}, next);
},
function (results, next) {
var result = uids.map(function (uid, index) {
return results.hasUserPrivilege[index] || results.hasGroupPrivilege[index];
});
next(null, result);
},
], callback);
helpers.isUsersAllowedTo = async function (privilege, uids, cid) {
const [hasUserPrivilege, hasGroupPrivilege] = await Promise.all([
groups.isMembers(uids, 'cid:' + cid + ':privileges:' + privilege),
groups.isMembersOfGroupList(uids, 'cid:' + cid + ':privileges:groups:' + privilege),
]);
return uids.map((uid, index) => hasUserPrivilege[index] || hasGroupPrivilege[index]);
};
function isSystemGroupAllowedToCids(privilege, uid, cids, callback) {
async function isSystemGroupAllowedToCids(privilege, uid, cids) {
const groupKeys = cids.map(cid => 'cid:' + cid + ':privileges:groups:' + privilege);
groups.isMemberOfGroups(uidToSystemGroup[uid], groupKeys, callback);
return await groups.isMemberOfGroups(uidToSystemGroup[uid], groupKeys);
}
function isSystemGroupAllowedToPrivileges(privileges, uid, cid, callback) {
async function isSystemGroupAllowedToPrivileges(privileges, uid, cid) {
const groupKeys = privileges.map(privilege => 'cid:' + cid + ':privileges:groups:' + privilege);
groups.isMemberOfGroups(uidToSystemGroup[uid], groupKeys, callback);
return await groups.isMemberOfGroups(uidToSystemGroup[uid], groupKeys);
}
helpers.getUserPrivileges = function (cid, hookName, userPrivilegeList, callback) {
var userPrivileges;
var memberSets;
async.waterfall([
async.apply(plugins.fireHook, hookName, userPrivilegeList.slice()),
function (_privs, next) {
userPrivileges = _privs;
groups.getMembersOfGroups(userPrivileges.map(privilege => 'cid:' + cid + ':privileges:' + privilege), next);
},
function (_memberSets, next) {
memberSets = _memberSets.map(function (set) {
return set.map(uid => parseInt(uid, 10));
});
helpers.getUserPrivileges = async function (cid, hookName, userPrivilegeList) {
const userPrivileges = await plugins.fireHook(hookName, userPrivilegeList.slice());
let memberSets = await groups.getMembersOfGroups(userPrivileges.map(privilege => 'cid:' + cid + ':privileges:' + privilege));
memberSets = memberSets.map(function (set) {
return set.map(uid => parseInt(uid, 10));
});
var members = _.uniq(_.flatten(memberSets));
const members = _.uniq(_.flatten(memberSets));
const memberData = await user.getUsersFields(members, ['picture', 'username']);
user.getUsersFields(members, ['picture', 'username'], next);
},
function (memberData, next) {
memberData.forEach(function (member) {
member.privileges = {};
for (var x = 0, numPrivs = userPrivileges.length; x < numPrivs; x += 1) {
member.privileges[userPrivileges[x]] = memberSets[x].includes(parseInt(member.uid, 10));
}
});
memberData.forEach(function (member) {
member.privileges = {};
for (var x = 0, numPrivs = userPrivileges.length; x < numPrivs; x += 1) {
member.privileges[userPrivileges[x]] = memberSets[x].includes(parseInt(member.uid, 10));
}
});
next(null, memberData);
},
], callback);
return memberData;
};
helpers.getGroupPrivileges = function (cid, hookName, groupPrivilegeList, callback) {
var groupPrivileges;
async.waterfall([
async.apply(plugins.fireHook, hookName, groupPrivilegeList.slice()),
function (_privs, next) {
groupPrivileges = _privs;
async.parallel({
memberSets: function (next) {
groups.getMembersOfGroups(groupPrivileges.map(privilege => 'cid:' + cid + ':privileges:' + privilege), next);
},
groupNames: function (next) {
groups.getGroups('groups:createtime', 0, -1, next);
},
}, next);
},
function (results, next) {
var memberSets = results.memberSets;
var uniqueGroups = _.uniq(_.flatten(memberSets));
helpers.getGroupPrivileges = async function (cid, hookName, groupPrivilegeList) {
const groupPrivileges = await plugins.fireHook(hookName, groupPrivilegeList.slice());
const [memberSets, allGroupNames] = await Promise.all([
groups.getMembersOfGroups(groupPrivileges.map(privilege => 'cid:' + cid + ':privileges:' + privilege)),
groups.getGroups('groups:createtime', 0, -1),
]);
var groupNames = results.groupNames.filter(groupName => !groupName.includes(':privileges:') && uniqueGroups.includes(groupName));
const uniqueGroups = _.uniq(_.flatten(memberSets));
groupNames = groups.ephemeralGroups.concat(groupNames);
moveToFront(groupNames, 'Global Moderators');
moveToFront(groupNames, 'registered-users');
let groupNames = allGroupNames.filter(groupName => !groupName.includes(':privileges:') && uniqueGroups.includes(groupName));
var adminIndex = groupNames.indexOf('administrators');
if (adminIndex !== -1) {
groupNames.splice(adminIndex, 1);
}
groupNames = groups.ephemeralGroups.concat(groupNames);
moveToFront(groupNames, 'Global Moderators');
moveToFront(groupNames, 'registered-users');
var memberPrivs;
const adminIndex = groupNames.indexOf('administrators');
if (adminIndex !== -1) {
groupNames.splice(adminIndex, 1);
}
const groupData = await groups.getGroupsFields(groupNames, ['private']);
const memberData = groupNames.map(function (member, index) {
const memberPrivs = {};
var memberData = groupNames.map(function (member) {
memberPrivs = {};
for (var x = 0, numPrivs = groupPrivileges.length; x < numPrivs; x += 1) {
memberPrivs[groupPrivileges[x]] = memberSets[x].includes(member);
}
return {
name: member,
privileges: memberPrivs,
};
});
next(null, memberData);
},
function (memberData, next) {
// Grab privacy info for the groups as well
async.map(memberData, function (member, next) {
async.waterfall([
function (next) {
groups.isPrivate(member.name, next);
},
function (isPrivate, next) {
member.isPrivate = isPrivate;
next(null, member);
},
], next);
}, next);
},
], callback);
for (var x = 0, numPrivs = groupPrivileges.length; x < numPrivs; x += 1) {
memberPrivs[groupPrivileges[x]] = memberSets[x].includes(member);
}
return {
name: member,
privileges: memberPrivs,
isPrivate: groupData[index] && !!groupData[index].private,
};
});
return memberData;
};
function moveToFront(groupNames, groupToMove) {
@@ -218,16 +143,19 @@ function moveToFront(groupNames, groupToMove) {
}
}
helpers.giveOrRescind = function (method, privileges, cids, groupNames, callback) {
helpers.giveOrRescind = async function (method, privileges, cids, groupNames) {
groupNames = Array.isArray(groupNames) ? groupNames : [groupNames];
cids = Array.isArray(cids) ? cids : [cids];
async.eachSeries(groupNames, function (groupName, next) {
var groupKeys = [];
for (const groupName of groupNames) {
const groupKeys = [];
cids.forEach((cid) => {
privileges.forEach((privilege) => {
groupKeys.push('cid:' + cid + ':privileges:groups:' + privilege);
});
});
method(groupKeys, groupName, next);
}, callback);
/* eslint-disable no-await-in-loop */
await method(groupKeys, groupName);
}
};
require('../promisify')(helpers);