From 4020e1be3529bdff2ff2f98b67278f5f77b86dac Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Tue, 2 Dec 2025 13:18:15 -0500 Subject: [PATCH] feat: patch low-level privilege query calls to accept privilege masks at the cid level --- src/privileges/helpers.js | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/src/privileges/helpers.js b/src/privileges/helpers.js index 7683264ef0..6ae2c7538d 100644 --- a/src/privileges/helpers.js +++ b/src/privileges/helpers.js @@ -4,6 +4,7 @@ const _ = require('lodash'); const validator = require('validator'); +const db = require('../database'); const groups = require('../groups'); const user = require('../user'); const categories = require('../categories'); @@ -25,16 +26,25 @@ helpers.isUsersAllowedTo = async function (privilege, uids, cid) { cid = -1; } - const [hasUserPrivilege, hasGroupPrivilege] = await Promise.all([ - groups.isMembers(uids, `cid:${cid}:privileges:${privilege}`), - groups.isMembersOfGroupList(uids, `cid:${cid}:privileges:groups:${privilege}`), - ]); - const allowed = uids.map((uid, index) => hasUserPrivilege[index] || hasGroupPrivilege[index]); - const result = await plugins.hooks.fire('filter:privileges:isUsersAllowedTo', { allowed: allowed, privilege: privilege, uids: uids, cid: cid }); + let allowed; + const masked = db.isSetMember(`cid:${cid}:privilegeMask`, privilege); + if (!masked) { + const [hasUserPrivilege, hasGroupPrivilege] = await Promise.all([ + groups.isMembers(uids, `cid:${cid}:privileges:${privilege}`), + groups.isMembersOfGroupList(uids, `cid:${cid}:privileges:groups:${privilege}`), + ]); + allowed = uids.map((uid, index) => hasUserPrivilege[index] || hasGroupPrivilege[index]); + } else { + allowed = uids.map(() => false); + } + + const result = await plugins.hooks.fire('filter:privileges:isUsersAllowedTo', { allowed, privilege, uids, cid }); return result.allowed; }; helpers.isAllowedTo = async function (privilege, uidOrGroupName, cid) { + const _cid = cid; // original passed-in cid needed for privilege mask checks + // Remote categories (non-numeric) inherit world privileges if (Array.isArray(cid)) { cid = cid.map(cid => (utils.isNumber(cid) ? cid : -1)); @@ -44,10 +54,15 @@ helpers.isAllowedTo = async function (privilege, uidOrGroupName, cid) { let allowed; if (Array.isArray(privilege) && !Array.isArray(cid)) { + const mask = await db.isSetMembers(`cid:${_cid}:privilegeMask`, privilege); allowed = await isAllowedToPrivileges(privilege, uidOrGroupName, cid); + allowed = allowed.map((allowed, idx) => mask[idx] ? false : allowed); } else if (Array.isArray(cid) && !Array.isArray(privilege)) { + const mask = await db.isMemberOfSets(_cid.map(cid => `cid:${cid}:privilegeMask`), privilege); allowed = await isAllowedToCids(privilege, uidOrGroupName, cid); + allowed = allowed.map((allowed, idx) => mask[idx] ? false : allowed); } + if (allowed) { ({ allowed } = await plugins.hooks.fire('filter:privileges:isAllowedTo', { allowed: allowed, privilege: privilege, uid: uidOrGroupName, cid: cid })); return allowed;