feat: acp privileges (WIP)

This commit is contained in:
Julian Lam
2020-06-01 09:58:31 -04:00
parent de7ec47f13
commit 25195464df
13 changed files with 276 additions and 24 deletions

View File

@@ -1,6 +1,7 @@
{
"global": "Global",
"global.no-users": "No user-specific global privileges.",
"admin": "Admin",
"group-privileges": "Group Privileges",
"user-privileges": "User Privileges",
"chat": "Chat",
@@ -31,5 +32,7 @@
"downvote-posts": "Downvote Posts",
"delete-topics": "Delete Topics",
"purge": "Purge",
"moderate": "Moderate"
"moderate": "Moderate",
"manage-categories": "Manage Categories"
}

View File

@@ -81,7 +81,13 @@ define('admin/manage/privileges', [
if (err) {
return app.alertError(err.message);
}
var tpl = cid ? 'admin/partials/categories/privileges' : 'admin/partials/global/privileges';
var tpl;
if (cid !== 'admin') {
tpl = cid ? 'admin/partials/privileges/category' : 'admin/partials/privileges/global';
} else {
tpl = 'admin/partials/privileges/admin';
}
Benchpress.parse(tpl, {
privileges: privileges,
}, function (html) {
@@ -143,7 +149,12 @@ define('admin/manage/privileges', [
inputEl.focus();
autocomplete.user(inputEl, function (ev, ui) {
var defaultPrivileges = cid ? ['find', 'read', 'topics:read'] : ['chat'];
var defaultPrivileges;
if (cid === 'admin') {
defaultPrivileges = ['manage:categories'];
} else {
defaultPrivileges = cid ? ['find', 'read', 'topics:read'] : ['chat'];
}
socket.emit('admin.categories.setPrivilege', {
cid: cid,
privilege: defaultPrivileges,
@@ -172,7 +183,14 @@ define('admin/manage/privileges', [
var inputEl = modal.find('input');
autocomplete.group(inputEl, function (ev, ui) {
var defaultPrivileges = cid ? ['groups:find', 'groups:read', 'groups:topics:read'] : ['groups:chat'];
var defaultPrivileges;
if (cid === 'admin') {
defaultPrivileges = ['groups:manage:categories'];
} else {
defaultPrivileges = cid ? ['groups:find', 'groups:read', 'groups:topics:read'] : ['groups:chat'];
}
console.log(cid, defaultPrivileges);
socket.emit('admin.categories.setPrivilege', {
cid: cid,
privilege: defaultPrivileges,

View File

@@ -6,9 +6,27 @@ const privileges = require('../../privileges');
const privilegesController = module.exports;
privilegesController.get = async function (req, res) {
const cid = req.params.cid ? parseInt(req.params.cid, 10) : 0;
const cid = req.params.cid ? parseInt(req.params.cid, 10) || 'admin' : 0;
let method;
const type = {
global: false,
admin: false,
cid: false,
};
if (cid > 0) {
method = privileges.categories.list.bind(null, cid);
type.cid = true;
} else if (cid === 0) {
method = privileges.global.list;
type.global = true;
} else {
method = privileges.admin.list;
type.admin = true;
}
const [privilegesData, categoriesData] = await Promise.all([
cid ? privileges.categories.list(cid) : privileges.global.list(),
method(),
categories.buildForSelectAll(),
]);
@@ -17,6 +35,11 @@ privilegesController.get = async function (req, res) {
name: '[[admin/manage/privileges:global]]',
icon: 'fa-list',
});
categoriesData.unshift({
cid: 'admin',
name: '[[admin/manage/privileges:admin]]',
icon: 'fa-lock',
});
let selectedCategory;
categoriesData.forEach(function (category) {
@@ -30,6 +53,7 @@ privilegesController.get = async function (req, res) {
});
res.render('admin/manage/privileges', {
type: type,
privileges: privilegesData,
categories: categoriesData,
selectedCategory: selectedCategory,

View File

@@ -73,6 +73,7 @@ module.exports = function (Groups) {
}
Groups.validateGroupName = function (name) {
console.log(name);
if (!name) {
throw new Error('[[error:group-name-too-short]]');
}

View File

@@ -46,7 +46,7 @@ Groups.removeEphemeralGroups = function (groups) {
return groups;
};
var isPrivilegeGroupRegex = /^cid:\d+:privileges:[\w:]+$/;
var isPrivilegeGroupRegex = /^cid:(?:\d+|admin):privileges:[\w:]+$/;
Groups.isPrivilegeGroup = function (groupName) {
return isPrivilegeGroupRegex.test(groupName);
};

107
src/privileges/admin.js Normal file
View File

@@ -0,0 +1,107 @@
'use strict';
// const _ = require('lodash');
// const user = require('../user');
const groups = require('../groups');
const helpers = require('./helpers');
const plugins = require('../plugins');
const utils = require('../utils');
module.exports = function (privileges) {
privileges.admin = {};
privileges.admin.privilegeLabels = [
{ name: '[[admin/manage/privileges:manage-categories]]' },
];
privileges.admin.userPrivilegeList = [
'manage:categories',
];
privileges.admin.groupPrivilegeList = privileges.admin.userPrivilegeList.map(privilege => 'groups:' + privilege);
privileges.admin.list = async function () {
async function getLabels() {
return await utils.promiseParallel({
users: plugins.fireHook('filter:privileges.admin.list_human', privileges.admin.privilegeLabels.slice()),
groups: plugins.fireHook('filter:privileges.admin.groups.list_human', privileges.admin.privilegeLabels.slice()),
});
}
const payload = await utils.promiseParallel({
labels: getLabels(),
users: helpers.getUserPrivileges('admin', 'filter:privileges.admin.list', privileges.admin.userPrivilegeList),
groups: helpers.getGroupPrivileges('admin', 'filter:privileges.admin.groups.list', privileges.admin.groupPrivilegeList),
});
// This is a hack because I can't do {labels.users.length} to echo the count in templates.js
payload.columnCount = payload.labels.users.length + 2;
return payload;
};
// privileges.admin.get = async function (uid) {
// const [userPrivileges, isAdministrator] = await Promise.all([
// helpers.isUserAllowedTo(privileges.admin.userPrivilegeList, uid, 0),
// user.isAdministrator(uid),
// ]);
// const privData = _.zipObject(privileges.admin.userPrivilegeList, userPrivileges);
// return await plugins.fireHook('filter:privileges.admin.get', {
// chat: privData.chat || isAdministrator,
// 'upload:post:image': privData['upload:post:image'] || isAdministrator,
// 'upload:post:file': privData['upload:post:file'] || isAdministrator,
// 'search:content': privData['search:content'] || isAdministrator,
// 'search:users': privData['search:users'] || isAdministrator,
// 'search:tags': privData['search:tags'] || isAdministrator,
// 'view:users': privData['view:users'] || isAdministrator,
// 'view:tags': privData['view:tags'] || isAdministrator,
// 'view:groups': privData['view:groups'] || isAdministrator,
// 'view:users:info': privData['view:users:info'] || isAdministrator,
// });
// };
// privileges.admin.can = async function (privilege, uid) {
// const [isAdministrator, isUserAllowedTo] = await Promise.all([
// user.isAdministrator(uid),
// helpers.isUserAllowedTo(privilege, uid, [0]),
// ]);
// return isAdministrator || isUserAllowedTo[0];
// };
// privileges.admin.canGroup = async function (privilege, groupName) {
// return await groups.isMember(groupName, 'cid:0:privileges:groups:' + privilege);
// };
privileges.admin.give = async function (privileges, groupName) {
await helpers.giveOrRescind(groups.join, privileges, 'admin', groupName);
plugins.fireHook('action:privileges.admin.give', {
privileges: privileges,
groupNames: Array.isArray(groupName) ? groupName : [groupName],
});
};
privileges.admin.rescind = async function (privileges, groupName) {
await helpers.giveOrRescind(groups.leave, privileges, 'admin', groupName);
plugins.fireHook('action:privileges.admin.rescind', {
privileges: privileges,
groupNames: Array.isArray(groupName) ? groupName : [groupName],
});
};
// privileges.admin.userPrivileges = async function (uid) {
// const tasks = {};
// privileges.admin.userPrivilegeList.forEach(function (privilege) {
// tasks[privilege] = groups.isMember(uid, 'cid:0:privileges:' + privilege);
// });
// return await utils.promiseParallel(tasks);
// };
// privileges.admin.groupPrivileges = async function (groupName) {
// const tasks = {};
// privileges.admin.groupPrivilegeList.forEach(function (privilege) {
// tasks[privilege] = groups.isMember(groupName, 'cid:0:privileges:' + privilege);
// });
// return await utils.promiseParallel(tasks);
// };
};

View File

@@ -101,18 +101,18 @@ module.exports = function (privileges) {
privileges.global.give = async function (privileges, groupName) {
await helpers.giveOrRescind(groups.join, privileges, 0, groupName);
plugins.fireHook('action:privileges.global.give', {
privileges: privileges,
groupNames: Array.isArray(groupName) ? groupName : [groupName],
});
// plugins.fireHook('action:privileges.global.give', {
// privileges: privileges,
// groupNames: Array.isArray(groupName) ? groupName : [groupName],
// });
};
privileges.global.rescind = async function (privileges, groupName) {
await helpers.giveOrRescind(groups.leave, privileges, 0, groupName);
plugins.fireHook('action:privileges.global.rescind', {
privileges: privileges,
groupNames: Array.isArray(groupName) ? groupName : [groupName],
});
// plugins.fireHook('action:privileges.global.rescind', {
// privileges: privileges,
// groupNames: Array.isArray(groupName) ? groupName : [groupName],
// });
};
privileges.global.userPrivileges = async function (uid) {

View File

@@ -43,6 +43,7 @@ privileges.groupPrivilegeList = privileges.userPrivilegeList.map(privilege => 'g
privileges.privilegeList = privileges.userPrivilegeList.concat(privileges.groupPrivilegeList);
require('./global')(privileges);
require('./admin')(privileges);
require('./categories')(privileges);
require('./topics')(privileges);
require('./posts')(privileges);

View File

@@ -61,9 +61,15 @@ Categories.setPrivilege = async function (socket, data) {
throw new Error('[[error:no-user-or-group]]');
}
await privileges.categories[data.set ? 'give' : 'rescind'](
Array.isArray(data.privilege) ? data.privilege : [data.privilege], data.cid, data.member
);
if (isNaN(parseInt(data.cid, 10))) {
await privileges[data.cid][data.set ? 'give' : 'rescind'](
Array.isArray(data.privilege) ? data.privilege : [data.privilege], data.member
);
} else {
await privileges.categories[data.set ? 'give' : 'rescind'](
Array.isArray(data.privilege) ? data.privilege : [data.privilege], data.cid, data.member
);
}
await events.log({
uid: socket.uid,
@@ -77,7 +83,9 @@ Categories.setPrivilege = async function (socket, data) {
};
Categories.getPrivilegeSettings = async function (socket, cid) {
if (!parseInt(cid, 10)) {
if (cid === 'admin') {
return await privileges.admin.list();
} else if (!parseInt(cid, 10)) {
return await privileges.global.list();
}
return await privileges.categories.list(cid);

View File

@@ -11,11 +11,15 @@
</div>
<div class="privilege-table-container">
<!-- IF cid -->
<!-- IMPORT admin/partials/categories/privileges.tpl -->
<!-- ELSE -->
<!-- IMPORT admin/partials/global/privileges.tpl -->
<!-- ENDIF cid -->
{{{ if type.global }}}
<!-- IMPORT admin/partials/privileges/global.tpl -->
{{{ end }}}
{{{ if type.admin }}}
<!-- IMPORT admin/partials/privileges/admin.tpl -->
{{{ end }}}
{{{ if type.cid }}}
<!-- IMPORT admin/partials/privileges/category.tpl -->
{{{ end }}}
</div>
</div>
</form>

View File

@@ -0,0 +1,86 @@
<label>[[admin/manage/privileges:group-privileges]]</label>
<table class="table table-striped privilege-table">
<thead>
<tr><!-- zebrastripe reset --></tr>
<tr>
<th colspan="2">[[admin/manage/categories:privileges.section-group]]</th>
<!-- BEGIN privileges.labels.groups -->
<th class="text-center">{privileges.labels.groups.name}</th>
<!-- END privileges.labels.groups -->
</tr>
</thead>
<tbody>
<!-- BEGIN privileges.groups -->
<tr data-group-name="{privileges.groups.nameEscaped}" data-private="<!-- IF privileges.groups.isPrivate -->1<!-- ELSE -->0<!-- ENDIF privileges.groups.isPrivate -->">
<td>
<!-- IF privileges.groups.isPrivate -->
<i class="fa fa-lock text-muted" title="[[admin/manage/categories:privileges.group-private]]"></i>
<!-- ENDIF privileges.groups.isPrivate -->
{privileges.groups.name}
</td>
<td></td>
{function.spawnPrivilegeStates, privileges.groups.name, ../privileges}
</tr>
<!-- END privileges.groups -->
<tr>
<td colspan="{privileges.columnCount}">
<div class="btn-toolbar">
<button type="button" class="btn btn-primary pull-right" data-ajaxify="false" data-action="search.group">
[[admin/manage/categories:privileges.search-group]]
</button>
</div>
</td>
</tr>
</tbody>
</table>
<div class="help-block">
[[admin/manage/categories:privileges.inherit]]
</div>
<hr/>
<label>[[admin/manage/privileges:user-privileges]]</label>
<table class="table table-striped privilege-table">
<thead>
<tr class="privilege-table-header">
<th colspan="15"></th>
</tr><tr><!-- zebrastripe reset --></tr>
<tr>
<th colspan="2">[[admin/manage/categories:privileges.section-user]]</th>
<!-- BEGIN privileges.labels.users -->
<th class="text-center">{privileges.labels.users.name}</th>
<!-- END privileges.labels.users -->
</tr>
</thead>
<tbody>
<!-- IF privileges.users.length -->
<!-- BEGIN privileges.users -->
<tr data-uid="{privileges.users.uid}">
<td>
<!-- IF ../picture -->
<img class="avatar avatar-sm" src="{privileges.users.picture}" title="{privileges.users.username}" />
<!-- ELSE -->
<div class="avatar avatar-sm" style="background-color: {../icon:bgColor};">{../icon:text}</div>
<!-- ENDIF ../picture -->
</td>
<td>{privileges.users.username}</td>
{function.spawnPrivilegeStates, privileges.users.username, ../privileges}
</tr>
<!-- END privileges.users -->
<tr>
<td colspan="{privileges.columnCount}">
<button type="button" class="btn btn-primary pull-right" data-ajaxify="false" data-action="search.user">
[[admin/manage/categories:privileges.search-user]]
</button>
</td>
</tr>
<!-- ELSE -->
<tr>
<td colspan="{privileges.columnCount}">
[[admin/manage/privileges:global.no-users]]
<button type="button" class="btn btn-primary pull-right" data-ajaxify="false" data-action="search.user">
[[admin/manage/categories:privileges.search-user]]
</button>
</td>
</tr>
<!-- ENDIF privileges.users.length -->
</tbody>
</table>