mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-11-08 15:05:46 +01:00
Adding support for restricting category read access based on user groups
This commit is contained in:
@@ -211,6 +211,7 @@ define(['uploader'], function(uploader) {
|
|||||||
var modal = $('#category-permissions-modal'),
|
var modal = $('#category-permissions-modal'),
|
||||||
searchEl = modal.find('#permission-search'),
|
searchEl = modal.find('#permission-search'),
|
||||||
resultsEl = modal.find('.search-results'),
|
resultsEl = modal.find('.search-results'),
|
||||||
|
groupsResultsEl = modal.find('.groups-results'),
|
||||||
searchDelay;
|
searchDelay;
|
||||||
|
|
||||||
searchEl.off().on('keyup', function() {
|
searchEl.off().on('keyup', function() {
|
||||||
@@ -263,6 +264,40 @@ define(['uploader'], function(uploader) {
|
|||||||
searchEl.keyup();
|
searchEl.keyup();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// User Groups and privileges
|
||||||
|
socket.emit('api:admin.categories.groupsearch', cid, function(err, results) {
|
||||||
|
var groupsFrag = document.createDocumentFragment(),
|
||||||
|
liEl = document.createElement('li');
|
||||||
|
var numResults = results.length,
|
||||||
|
resultObj;
|
||||||
|
|
||||||
|
for(var x=0;x<numResults;x++) {
|
||||||
|
resultObj = results[x];
|
||||||
|
liEl.setAttribute('data-gid', resultObj.gid);
|
||||||
|
liEl.innerHTML = '<div class="pull-right">' +
|
||||||
|
'<div class="btn-group">' +
|
||||||
|
'<button type="button" data-gpriv="+gr" class="btn btn-default' + (resultObj.privileges['+gr'] ? ' active' : '') + '">Read</button>' +
|
||||||
|
'<button type="button" data-gpriv="+gw" class="btn btn-default' + (resultObj.privileges['+gw'] ? ' active' : '') + '">Write</button>' +
|
||||||
|
'</div>' +
|
||||||
|
'</div>' +
|
||||||
|
' '+resultObj.name;
|
||||||
|
|
||||||
|
groupsFrag.appendChild(liEl.cloneNode(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
groupsResultsEl.html(groupsFrag);
|
||||||
|
});
|
||||||
|
|
||||||
|
groupsResultsEl.off().on('click', '[data-gpriv]', function(e) {
|
||||||
|
var btnEl = $(this),
|
||||||
|
gid = btnEl.parents('li[data-gid]').attr('data-gid'),
|
||||||
|
privilege = this.getAttribute('data-gpriv');
|
||||||
|
e.preventDefault();
|
||||||
|
socket.emit('api:admin.categories.setGroupPrivilege', cid, gid, privilege, !btnEl.hasClass('active'), function(err, privileges) {
|
||||||
|
btnEl.toggleClass('active', privileges[privilege]);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
modal.modal();
|
modal.modal();
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -312,4 +347,4 @@ define(['uploader'], function(uploader) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return Categories;
|
return Categories;
|
||||||
});
|
});
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -65,4 +65,56 @@ CategoryTools.privileges = function(cid, uid, callback) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = CategoryTools;
|
CategoryTools.groupPrivileges = function(cid, gid, callback) {
|
||||||
|
async.parallel({
|
||||||
|
"+gr": function(next) {
|
||||||
|
var key = 'cid:' + cid + ':privileges:+gr';
|
||||||
|
Groups.exists(key, function(err, exists) {
|
||||||
|
if (exists) {
|
||||||
|
async.parallel({
|
||||||
|
isMember: function(next) {
|
||||||
|
Groups.isMemberByGroupName(gid, key, next);
|
||||||
|
},
|
||||||
|
isEmpty: function(next) {
|
||||||
|
Groups.isEmptyByGroupName(key, next);
|
||||||
|
}
|
||||||
|
}, next);
|
||||||
|
} else {
|
||||||
|
next(null, {
|
||||||
|
isMember: false,
|
||||||
|
isEmpty: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
"+gw": function(next) {
|
||||||
|
var key = 'cid:' + cid + ':privileges:+gw';
|
||||||
|
Groups.exists(key, function(err, exists) {
|
||||||
|
if (exists) {
|
||||||
|
async.parallel({
|
||||||
|
isMember: function(next) {
|
||||||
|
Groups.isMemberByGroupName(gid, key, next);
|
||||||
|
},
|
||||||
|
isEmpty: function(next) {
|
||||||
|
Groups.isEmptyByGroupName(key, next);
|
||||||
|
}
|
||||||
|
}, next);
|
||||||
|
} else {
|
||||||
|
next(null, {
|
||||||
|
isMember: false,
|
||||||
|
isEmpty: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, function(err, privileges) {
|
||||||
|
callback(err, !privileges ? null : {
|
||||||
|
"+gr": privileges['+gr'].isMember,
|
||||||
|
"+gw": privileges['+gw'].isMember,
|
||||||
|
read: (privileges['+gr'].isMember || privileges['+gr'].isEmpty),
|
||||||
|
write: (privileges['+gw'].isMember || privileges['+gw'].isEmpty),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = CategoryTools;
|
||||||
|
|||||||
@@ -263,4 +263,40 @@
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Groups.getCategoryAccess = function(cid, uid, callback){
|
||||||
|
var access = false;
|
||||||
|
// check user group read access level
|
||||||
|
async.series([function(callback){
|
||||||
|
// get groups with read permission
|
||||||
|
db.getObjectField('group:gid', 'cid:' + cid + ':privileges:+gr', function(err, gid){
|
||||||
|
// get the user groups that belong to this read group
|
||||||
|
db.getSetMembers('gid:' + gid + ':members', function (err, gids) {
|
||||||
|
// check if user belong to any of these user groups
|
||||||
|
var groups_check = new Array();
|
||||||
|
gids.forEach(function(cgid){
|
||||||
|
groups_check.push(function(callback){
|
||||||
|
Groups.isMember(uid, cgid, function(err, isMember){
|
||||||
|
if (isMember){
|
||||||
|
access = true;
|
||||||
|
}
|
||||||
|
callback(null, gids);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
});
|
||||||
|
// do a series check. We want to make sure we check all the groups before determining if the user
|
||||||
|
// has access or not.
|
||||||
|
async.series(groups_check, function(err, results){
|
||||||
|
callback(null, results);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
}],
|
||||||
|
function(err, results){
|
||||||
|
// if the read group is empty we will asume that read access has been granted to ALL
|
||||||
|
if (results[0].length == 0){ access = true; }
|
||||||
|
callback(false, access);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
}(module.exports));
|
}(module.exports));
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ var path = require('path'),
|
|||||||
|
|
||||||
db = require('../database'),
|
db = require('../database'),
|
||||||
user = require('../user'),
|
user = require('../user'),
|
||||||
|
groups = require('../groups'),
|
||||||
auth = require('./authentication'),
|
auth = require('./authentication'),
|
||||||
topics = require('../topics'),
|
topics = require('../topics'),
|
||||||
posts = require('../posts'),
|
posts = require('../posts'),
|
||||||
@@ -121,7 +122,15 @@ var path = require('path'),
|
|||||||
if (parseInt(data.deleted, 10) === 1 && parseInt(data.expose_tools, 10) === 0) {
|
if (parseInt(data.deleted, 10) === 1 && parseInt(data.expose_tools, 10) === 0) {
|
||||||
return res.json(404, {});
|
return res.json(404, {});
|
||||||
}
|
}
|
||||||
res.json(data);
|
// get the category this post belongs to and check category access
|
||||||
|
var cid = data.category_slug.split("/")[0];
|
||||||
|
groups.getCategoryAccess(cid, uid, function(err, access){
|
||||||
|
if (access){
|
||||||
|
res.json(data);
|
||||||
|
} else {
|
||||||
|
res.send(403);
|
||||||
|
}
|
||||||
|
})
|
||||||
} else next();
|
} else next();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -132,13 +141,20 @@ var path = require('path'),
|
|||||||
// Category Whitelisting
|
// Category Whitelisting
|
||||||
categoryTools.privileges(req.params.id, uid, function(err, privileges) {
|
categoryTools.privileges(req.params.id, uid, function(err, privileges) {
|
||||||
if (!err && privileges.read) {
|
if (!err && privileges.read) {
|
||||||
categories.getCategoryById(req.params.id, uid, function (err, data) {
|
groups.getCategoryAccess(req.params.id, uid, function(err, access){
|
||||||
if (!err && data && parseInt(data.disabled, 10) === 0) {
|
if (access){
|
||||||
res.json(data);
|
categories.getCategoryById(req.params.id, uid, function (err, data) {
|
||||||
|
if (!err && data && parseInt(data.disabled, 10) === 0) {
|
||||||
|
res.json(data);
|
||||||
|
} else {
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
}, req.params.id, uid);
|
||||||
} else {
|
} else {
|
||||||
next();
|
res.send(403);
|
||||||
}
|
}
|
||||||
}, req.params.id, uid);
|
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
res.send(403);
|
res.send(403);
|
||||||
}
|
}
|
||||||
@@ -303,4 +319,4 @@ var path = require('path'),
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}(exports));
|
}(exports));
|
||||||
|
|||||||
@@ -1084,6 +1084,37 @@ websockets.init = function(io) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
socket.on('api:admin.categories.setGroupPrivilege', function(cid, gid, privilege, set, callback) {
|
||||||
|
var cb = function(err) {
|
||||||
|
CategoryTools.groupPrivileges(cid, gid, callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (set) {
|
||||||
|
groups.joinByGroupName('cid:' + cid + ':privileges:' + privilege, gid, cb);
|
||||||
|
} else {
|
||||||
|
groups.leaveByGroupName('cid:' + cid + ':privileges:' + privilege, gid, cb);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on('api:admin.categories.groupsearch', function(cid, callback) {
|
||||||
|
groups.list({expand:false}, function(err, data){
|
||||||
|
async.map(data, function(groupObj, next) {
|
||||||
|
CategoryTools.groupPrivileges(cid, groupObj.gid, function(err, privileges) {
|
||||||
|
if (!err) {
|
||||||
|
groupObj.privileges = privileges;
|
||||||
|
} else {
|
||||||
|
winston.error('[socket api:admin.categories.groupsearch] Could not retrieve permissions');
|
||||||
|
}
|
||||||
|
|
||||||
|
next(null, groupObj);
|
||||||
|
});
|
||||||
|
}, function(err, data) {
|
||||||
|
if (!callback) socket.emit('api:admin.categories.groupsearch', data);
|
||||||
|
else callback(null, data);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
socket.on('api:admin.themes.getInstalled', function(callback) {
|
socket.on('api:admin.themes.getInstalled', function(callback) {
|
||||||
meta.themes.get(function(err, themeArr) {
|
meta.themes.get(function(err, themeArr) {
|
||||||
callback(themeArr);
|
callback(themeArr);
|
||||||
|
|||||||
Reference in New Issue
Block a user