restrict flags interface so that moderators only see flags from cids they can actually moderate

v1.18.x
Julian Lam 8 years ago
parent 05fdd35818
commit 541f51e947

@ -27,6 +27,7 @@ modsController.flags.list = function (req, res, next) {
}
// Parse query string params for filters
var hasFilter = false;
var valid = ['assignee', 'state', 'reporterId', 'type', 'targetUid', 'cid', 'quick'];
var filters = valid.reduce(function (memo, cur) {
if (req.query.hasOwnProperty(cur)) {
@ -35,6 +36,24 @@ modsController.flags.list = function (req, res, next) {
return memo;
}, {});
hasFilter = !!Object.keys(filters).length;
if (res.locals.cids) {
if (!filters.cid) {
// If mod and no cid filter, add filter for their modded categories
filters.cid = res.locals.cids;
} else {
// Remove cids they do not moderate
if (Array.isArray(filters.cid)) {
filters.cid = filters.cid.filter(function (cid) {
return res.locals.cids.indexOf(String(cid)) !== -1;
});
} else if (res.locals.cids.indexOf(String(filters.cid)) === -1) {
filters.cid = res.locals.cids;
hasFilter = false;
}
}
}
async.parallel({
flags: async.apply(flags.list, filters, req.uid),
@ -45,9 +64,25 @@ modsController.flags.list = function (req, res, next) {
return next(err);
}
// If res.locals.cids is populated, then slim down the categories list
if (res.locals.cids) {
data.categories = data.categories.filter(function (category) {
return res.locals.cids.indexOf(String(category.cid)) !== -1;
});
}
// Minimal returned set for templates.js
data.categories = data.categories.reduce(function (memo, cur) {
memo[cur.cid] = cur.name;
if (!res.locals.cids) {
memo[cur.cid] = cur.name;
return memo;
}
// If mod, remove categories they can't moderate
if (res.locals.cids.indexOf(String(cur.cid)) !== -1) {
memo[cur.cid] = cur.name;
}
return memo;
}, {});
@ -55,7 +90,7 @@ modsController.flags.list = function (req, res, next) {
flags: data.flags,
analytics: data.analytics,
categories: data.categories,
hasFilter: !!Object.keys(filters).length,
hasFilter: hasFilter,
filters: filters,
title: '[[pages:flags]]'
});

@ -53,31 +53,45 @@ Flags.list = function (filters, uid, callback) {
}
var sets = [];
var orSets = [];
var prepareSets = function (setPrefix, value) {
if (!Array.isArray(value)) {
sets.push(setPrefix + value);
} else if (value.length) {
value.forEach(function (x) {
orSets.push(setPrefix + x);
});
} else {
// Empty array, do nothing
return;
}
};
if (Object.keys(filters).length > 0) {
for (var type in filters) {
switch (type) {
case 'type':
sets.push('flags:byType:' + filters[type]);
prepareSets('flags:byType:', filters[type]);
break;
case 'state':
sets.push('flags:byState:' + filters[type]);
prepareSets('flags:byState:', filters[type]);
break;
case 'reporterId':
sets.push('flags:byReporter:' + filters[type]);
prepareSets('flags:byReporter:', filters[type]);
break;
case 'assignee':
sets.push('flags:byAssignee:' + filters[type]);
prepareSets('flags:byAssignee:', filters[type]);
break;
case 'targetUid':
sets.push('flags:byTargetUid:' + filters[type]);
prepareSets('flags:byTargetUid:', filters[type]);
break;
case 'cid':
sets.push('flags:byCid:' + filters[type]);
prepareSets('flags:byCid:', filters[type]);
break;
case 'quick':
@ -90,14 +104,36 @@ Flags.list = function (filters, uid, callback) {
}
}
}
sets = sets.length ? sets : ['flags:datetime']; // No filter default
sets = (sets.length || orSets.length) ? sets : ['flags:datetime']; // No filter default
async.waterfall([
function (next) {
if (sets.length === 1) {
db.getSortedSetRevRange(sets[0], 0, -1, next);
} else if (sets.length > 1) {
db.getSortedSetRevIntersect({ sets: sets, start: 0, stop: -1, aggregate: 'MAX' }, next);
} else {
next(null, []);
}
},
function (flagIds, next) {
// Find flags according to "or" rules, if any
if (orSets.length) {
db.getSortedSetRevUnion({ sets: orSets, start: 0, stop: -1, aggregate: 'MAX' }, function (err, _flagIds) {
if (err) {
return next(err);
}
if (sets.length) {
// If flag ids are already present, return a subset of flags that are in both sets
next(null, _.intersection(flagIds, _flagIds));
} else {
// Otherwise, return all flags returned via orSets
next(null, _.union(flagIds, _flagIds));
}
});
} else {
db.getSortedSetRevIntersect({sets: sets, start: 0, stop: -1, aggregate: 'MAX'}, next);
setImmediate(next, null, flagIds);
}
},
function (flagIds, next) {

@ -203,6 +203,41 @@ describe('Flags', function () {
done();
});
});
it('should return a flag when filtered by both cid 1 and 2', function (done) {
Flags.list({
cid: [1, 2]
}, 1, function (err, flags) {
assert.ifError(err);
assert.ok(Array.isArray(flags));
assert.strictEqual(1, flags.length);
done();
});
});
it('should return one flag if filtered by both cid 1 and 2 and open state', function (done) {
Flags.list({
cid: [1, 2],
state: 'open'
}, 1, function (err, flags) {
assert.ifError(err);
assert.ok(Array.isArray(flags));
assert.strictEqual(1, flags.length);
done();
});
});
it('should return no flag if filtered by both cid 1 and 2 and non-open state', function (done) {
Flags.list({
cid: [1, 2],
state: 'resolved'
}, 1, function (err, flags) {
assert.ifError(err);
assert.ok(Array.isArray(flags));
assert.strictEqual(0, flags.length);
done();
});
});
});
});

Loading…
Cancel
Save