mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-20 01:38:19 +08:00
用户权限管理
This commit is contained in:
1 parent
c2f9bf8c53
commit
4ec2a46877
11 files changed
+172
-75
No files matched your search
+25
-16
@@ -1,13 +1,11 @@
|
||||
const system = require('../model/system');
|
||||
const user = require('../model/user');
|
||||
const builtin = require('../model/builtin');
|
||||
const { Route, Handler } = require('../service/server');
|
||||
const { PERM_ADMIN } = require('../permission');
|
||||
const { PERM_MANAGE } = require('../permission');
|
||||
const { RoleAlreadyExistError, ValidationError } = require('../error');
|
||||
|
||||
class ManageHandler extends Handler {
|
||||
async prepare() {
|
||||
this.checkPerm(PERM_ADMIN);
|
||||
this.checkPerm(PERM_MANAGE);
|
||||
this.system = await user.getById(0);
|
||||
}
|
||||
}
|
||||
@@ -43,20 +41,22 @@ class ManageEditHandler extends ManageHandler {
|
||||
class ManageUserHandler extends ManageHandler {
|
||||
async get() {
|
||||
const uids = [];
|
||||
const [rudocs, udocs, roles] = await Promise.all([
|
||||
system.get(),
|
||||
user.getMulti({ role: { $neq: 'default' } }).toArray(),
|
||||
const rudocs = {};
|
||||
const [udocs, roles, system] = await Promise.all([
|
||||
user.getMulti({ role: { $ne: 'default' } }).toArray(),
|
||||
user.getRoles(),
|
||||
user.getById(0),
|
||||
]);
|
||||
for (const role of roles) rudocs[role._id] = [];
|
||||
for (const udoc of udocs) {
|
||||
uids.push(udoc._id);
|
||||
rudocs[udoc.role].push(udoc);
|
||||
}
|
||||
const roles_with_text = roles.map((role) => [role, role]);
|
||||
const rolesSelect = roles.map((role) => [role._id, role._id]);
|
||||
const udict = await user.getList(uids);
|
||||
this.response.template = 'domain_manage_user.html';
|
||||
this.response.body = {
|
||||
roles, roles_with_text, rudocs, udict,
|
||||
roles, rolesSelect, rudocs, udict, system,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -68,9 +68,12 @@ class ManageUserHandler extends ManageHandler {
|
||||
|
||||
class ManagePermissionHandler extends ManageHandler {
|
||||
async get() {
|
||||
const roles = await user.getRoles();
|
||||
const [roles, system] = await Promise.all([
|
||||
user.getRoles(),
|
||||
user.getById(0),
|
||||
]);
|
||||
this.response.template = 'domain_manage_permission.html';
|
||||
this.response.body = { roles };
|
||||
this.response.body = { roles, system };
|
||||
}
|
||||
|
||||
async post({ roles }) {
|
||||
@@ -88,21 +91,27 @@ class ManagePermissionHandler extends ManageHandler {
|
||||
|
||||
class ManageRoleHandler extends ManageHandler {
|
||||
async get() {
|
||||
const roles = await user.getRoles();
|
||||
const [roles, system] = await Promise.all([
|
||||
user.getRoles(),
|
||||
user.getById(0),
|
||||
]);
|
||||
this.response.template = 'domain_manage_role.html';
|
||||
this.response.body = { roles };
|
||||
this.response.body = { roles, system };
|
||||
}
|
||||
|
||||
async postAdd({ role }) {
|
||||
const r = await user.getRole(role);
|
||||
const [r, u] = await Promise.all([
|
||||
user.getRole(role),
|
||||
user.getRole('default'),
|
||||
]);
|
||||
if (r) throw new RoleAlreadyExistError(role);
|
||||
await user.addRole(role, builtin.PERM_DEFAULT);
|
||||
await user.addRole(role, u.perm);
|
||||
this.back();
|
||||
}
|
||||
|
||||
async postDelete({ roles }) {
|
||||
for (const role of roles) {
|
||||
if (['admin', 'default', 'guest'].includes(role)) {
|
||||
if (['root', 'default', 'guest'].includes(role)) {
|
||||
throw new ValidationError('role');
|
||||
}
|
||||
}
|
||||
|
||||
+10
-3
@@ -30,6 +30,7 @@ class UserLoginHandler extends Handler {
|
||||
this.response.redirect = referer.endsWith('/login') ? '/' : referer;
|
||||
}
|
||||
}
|
||||
|
||||
class UserLogoutHandler extends Handler {
|
||||
async prepare() {
|
||||
this.checkPerm(PERM_LOGGEDIN);
|
||||
@@ -44,6 +45,7 @@ class UserLogoutHandler extends Handler {
|
||||
this.response.body = {};
|
||||
}
|
||||
}
|
||||
|
||||
class UserRegisterHandler extends Handler {
|
||||
async prepare() {
|
||||
this.checkPerm(PERM_REGISTER_USER);
|
||||
@@ -71,6 +73,7 @@ class UserRegisterHandler extends Handler {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class UserRegisterWithCodeHandler extends Handler {
|
||||
async prepare() {
|
||||
this.checkPerm(PERM_REGISTER_USER);
|
||||
@@ -99,6 +102,7 @@ class UserRegisterWithCodeHandler extends Handler {
|
||||
this.response.redirect = '/';
|
||||
}
|
||||
}
|
||||
|
||||
class UserLostPassHandler extends Handler {
|
||||
constructor(ctx) {
|
||||
if (!options.smtp.user) throw new SystemError('Cannot send mail');
|
||||
@@ -123,6 +127,7 @@ class UserLostPassHandler extends Handler {
|
||||
this.response.template = 'user_lostpass_mail_sent.html';
|
||||
}
|
||||
}
|
||||
|
||||
class UserLostPassWithCodeHandler extends Handler {
|
||||
async get({ code }) {
|
||||
const tdoc = await token.get(code, token.TYPE_LOSTPASS);
|
||||
@@ -140,6 +145,7 @@ class UserLostPassWithCodeHandler extends Handler {
|
||||
this.response.redirect = '/';
|
||||
}
|
||||
}
|
||||
|
||||
class UserDetailHandler extends Handler {
|
||||
constructor(ctx) {
|
||||
super(ctx);
|
||||
@@ -154,6 +160,7 @@ class UserDetailHandler extends Handler {
|
||||
this.response.body = { isSelfProfile, udoc, sdoc };
|
||||
}
|
||||
}
|
||||
|
||||
class UserSearchHandler extends Handler {
|
||||
async get({ q, exactMatch = false }) {
|
||||
let udocs;
|
||||
@@ -161,7 +168,7 @@ class UserSearchHandler extends Handler {
|
||||
else udocs = await user.getPrefixList(q, 20);
|
||||
try {
|
||||
const udoc = await user.getById(parseInt(q));
|
||||
if (udoc) udocs.insert(0, udoc);
|
||||
if (udoc) udocs.push(udoc);
|
||||
} catch (e) {
|
||||
/* Ignore */
|
||||
}
|
||||
@@ -170,7 +177,7 @@ class UserSearchHandler extends Handler {
|
||||
udocs[i].gravatar_url = misc.gravatar_url[udocs[i].gravatar];
|
||||
}
|
||||
}
|
||||
this.response.body = { udocs };
|
||||
this.response.body = udocs;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,5 +187,5 @@ Route('/register/:code', UserRegisterWithCodeHandler);
|
||||
Route('/logout', UserLogoutHandler);
|
||||
Route('/lostpass', UserLostPassHandler);
|
||||
Route('/lostpass/:code', UserLostPassWithCodeHandler);
|
||||
Route('/user/:uid', UserDetailHandler);
|
||||
Route('/user/search', UserSearchHandler);
|
||||
Route('/user/:uid', UserDetailHandler);
|
||||
+1
-1
@@ -44,7 +44,7 @@ async function run() {
|
||||
hash: pwhash.hash('rootroot', salt),
|
||||
salt,
|
||||
gravatar: 'root@hydro',
|
||||
role: 'admin',
|
||||
role: 'root',
|
||||
}, udoc),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ const perm = require('../permission');
|
||||
const BUILTIN_ROLES = [
|
||||
{ _id: 'guest', perm: perm.PERM_BASIC },
|
||||
{ _id: 'default', perm: perm.PERM_DEFAULT, count: 0 },
|
||||
{ _id: 'admin', perm: perm.PERM_ADMIN, count: 0 },
|
||||
{ _id: 'root', perm: perm.PERM_ALL, count: 0 },
|
||||
];
|
||||
const CATEGORIES = {
|
||||
动态规划: [
|
||||
|
||||
+21
-4
@@ -1,5 +1,6 @@
|
||||
const system = require('./system');
|
||||
const { UserNotFoundError, UserAlreadyExistError } = require('../error');
|
||||
const perm = require('../permission');
|
||||
const pwhash = require('../lib/pwhash');
|
||||
const db = require('../service/db');
|
||||
|
||||
@@ -25,7 +26,7 @@ class USER {
|
||||
}
|
||||
|
||||
hasPerm(perm) {
|
||||
return this.perm === '-' || (this.perm || '').includes(perm);
|
||||
return this.perm.includes(perm);
|
||||
}
|
||||
|
||||
checkPassword(password) {
|
||||
@@ -122,7 +123,7 @@ async function create({
|
||||
role,
|
||||
gravatar: mail,
|
||||
}),
|
||||
collRole.updateOne({ _id: role }, { $inc: { count: 1 } })
|
||||
collRole.updateOne({ _id: role }, { $inc: { count: 1 } }),
|
||||
]);
|
||||
} catch (e) {
|
||||
throw new UserAlreadyExistError([uid, uname, mail]);
|
||||
@@ -133,6 +134,13 @@ function getMulti(params) {
|
||||
return coll.find(params);
|
||||
}
|
||||
|
||||
async function getPrefixList(prefix, limit = 50) {
|
||||
prefix = prefix.toLowerCase();
|
||||
const $regex = new RegExp(`\\A\\Q${prefix.replace(/\\E/gmi, /\\E\\E\\Q/gmi)}\\E`, 'gmi');
|
||||
const udocs = await coll.find({ unameLower: { $regex } }).limit(limit).toArray();
|
||||
return udocs;
|
||||
}
|
||||
|
||||
async function setRole(uid, role) {
|
||||
const udoc = await getById(uid);
|
||||
return await Promise.all([
|
||||
@@ -143,7 +151,7 @@ async function setRole(uid, role) {
|
||||
}
|
||||
|
||||
function getRoles() {
|
||||
return collRole.find().toArray();
|
||||
return collRole.find().sort('_id', 1).toArray();
|
||||
}
|
||||
|
||||
function getRole(name) {
|
||||
@@ -155,7 +163,14 @@ function addRole(name, perm) {
|
||||
}
|
||||
|
||||
function deleteRoles(roles) {
|
||||
return collRole.deleteMany({ _id: { $in: roles } });
|
||||
return Promise.all([
|
||||
coll.updateMany({ role: { $in: roles } }, { $set: { role: 'default' } }),
|
||||
collRole.deleteMany({ _id: { $in: roles } }),
|
||||
]);
|
||||
}
|
||||
|
||||
function init() {
|
||||
return collRole.update({ _id: 'root' }, { $set: { perm: perm.PERM_ALL } });
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
@@ -169,10 +184,12 @@ module.exports = {
|
||||
setById,
|
||||
setEmail,
|
||||
setPassword,
|
||||
getPrefixList,
|
||||
setRole,
|
||||
getRole,
|
||||
getList,
|
||||
getRoles,
|
||||
addRole,
|
||||
deleteRoles,
|
||||
init,
|
||||
};
|
||||
+80
-33
@@ -1,12 +1,8 @@
|
||||
const permissions = {
|
||||
PERM_NONE: '0',
|
||||
PERM_ALL: '-',
|
||||
PERM_LOGGEDIN: '~',
|
||||
PERM_REGISTER_USER: '+',
|
||||
PERM_VIEW: '1',
|
||||
PERM_EDIT_PERM: '2',
|
||||
const perm = {
|
||||
PERM_MANAGE: '#',
|
||||
PERM_LOGGEDIN: '0',
|
||||
PERM_REGISTER_USER: '1',
|
||||
PERM_MOD_BADGE: '3',
|
||||
PERM_EDIT_DESCRIPTION: '4',
|
||||
PERM_CREATE_PROBLEM: '5',
|
||||
PERM_EDIT_PROBLEM: '6',
|
||||
PERM_VIEW_PROBLEM: '7',
|
||||
@@ -43,28 +39,79 @@ const permissions = {
|
||||
PERM_EDIT_TRAINING: 'c',
|
||||
PERM_JUDGE: 'd',
|
||||
};
|
||||
permissions.PERM_BASIC = permissions.PERM_VIEW
|
||||
+ permissions.PERM_VIEW_PROBLEM
|
||||
+ permissions.PERM_VIEW_PROBLEM_SOLUTION
|
||||
+ permissions.PERM_VIEW_DISCUSSION
|
||||
+ permissions.PERM_VIEW_CONTEST
|
||||
+ permissions.PERM_VIEW_CONTEST_SCOREBOARD
|
||||
+ permissions.PERM_REGISTER_USER;
|
||||
permissions.PERM_DEFAULT = permissions.PERM_VIEW
|
||||
+ permissions.PERM_VIEW_PROBLEM
|
||||
+ permissions.PERM_SUBMIT_PROBLEM
|
||||
+ permissions.PERM_VIEW_PROBLEM_SOLUTION
|
||||
+ permissions.PERM_CREATE_PROBLEM_SOLUTION
|
||||
+ permissions.PERM_VOTE_PROBLEM_SOLUTION
|
||||
+ permissions.PERM_REPLY_PROBLEM_SOLUTION
|
||||
+ permissions.PERM_VIEW_DISCUSSION
|
||||
+ permissions.PERM_CREATE_DISCUSSION
|
||||
+ permissions.PERM_REPLY_DISCUSSION
|
||||
+ permissions.PERM_VIEW_CONTEST
|
||||
+ permissions.PERM_VIEW_CONTEST_SCOREBOARD
|
||||
+ permissions.PERM_ATTEND_CONTEST
|
||||
+ permissions.PERM_VIEW_TRAINING
|
||||
+ permissions.PERM_CREATE_TRAINING
|
||||
+ permissions.PERM_LOGGEDIN;
|
||||
permissions.PERM_ADMIN = permissions.PERM_ALL;
|
||||
module.exports = permissions;
|
||||
|
||||
let PERM_ALL = '';
|
||||
for (const p in perm) PERM_ALL += perm[p];
|
||||
perm.PERM_ALL = PERM_ALL;
|
||||
|
||||
const Permission = (family, key, desc) => ({ family, key, desc });
|
||||
|
||||
const PERMS = [
|
||||
Permission('perm_general', perm.PERM_REGISTER_USER, 'Register new account'),
|
||||
Permission('perm_general', perm.PERM_MANAGE, 'Manage the system'),
|
||||
Permission('perm_general', perm.PERM_MOD_BADGE, 'Show MOD badge'),
|
||||
Permission('perm_problem', perm.PERM_CREATE_PROBLEM, 'Create problems'),
|
||||
Permission('perm_problem', perm.PERM_EDIT_PROBLEM, 'Edit problems'),
|
||||
Permission('perm_problem', perm.PERM_VIEW_PROBLEM, 'View problems'),
|
||||
Permission('perm_problem', perm.PERM_VIEW_PROBLEM_HIDDEN, 'View hidden problems'),
|
||||
Permission('perm_problem', perm.PERM_SUBMIT_PROBLEM, 'Submit problem'),
|
||||
Permission('perm_problem', perm.PERM_READ_PROBLEM_DATA, 'Read data of problem'),
|
||||
Permission('perm_record', perm.PERM_READ_RECORD_CODE, 'Read record codes'),
|
||||
Permission('perm_record', perm.PERM_REJUDGE_PROBLEM, 'Rejudge problems'),
|
||||
Permission('perm_record', perm.PERM_REJUDGE, 'Rejudge records'),
|
||||
Permission('perm_problem_solution', perm.PERM_VIEW_PROBLEM_SOLUTION, 'View problem solutions'),
|
||||
Permission('perm_problem_solution', perm.PERM_CREATE_PROBLEM_SOLUTION, 'Create problem solutions'),
|
||||
Permission('perm_problem_solution', perm.PERM_VOTE_PROBLEM_SOLUTION, 'Vote problem solutions'),
|
||||
Permission('perm_problem_solution', perm.PERM_EDIT_PROBLEM_SOLUTION, 'Edit problem solutions'),
|
||||
Permission('perm_problem_solution', perm.PERM_DELETE_PROBLEM_SOLUTION, 'Delete problem solutions'),
|
||||
Permission('perm_problem_solution', perm.PERM_REPLY_PROBLEM_SOLUTION, 'Reply problem solutions'),
|
||||
Permission('perm_problem_solution', perm.PERM_EDIT_PROBLEM_SOLUTION_REPLY, 'Edit problem solution replies'),
|
||||
Permission('perm_problem_solution', perm.PERM_DELETE_PROBLEM_SOLUTION_REPLY, 'Delete problem solution replies'),
|
||||
Permission('perm_discussion', perm.PERM_VIEW_DISCUSSION, 'View discussions'),
|
||||
Permission('perm_discussion', perm.PERM_CREATE_DISCUSSION, 'Create discussions'),
|
||||
Permission('perm_discussion', perm.PERM_HIGHLIGHT_DISCUSSION, 'Highlight discussions'),
|
||||
Permission('perm_discussion', perm.PERM_EDIT_DISCUSSION, 'Edit discussions'),
|
||||
Permission('perm_discussion', perm.PERM_DELETE_DISCUSSION, 'Delete discussions'),
|
||||
Permission('perm_discussion', perm.PERM_REPLY_DISCUSSION, 'Reply discussions'),
|
||||
Permission('perm_discussion', perm.PERM_EDIT_DISCUSSION_REPLY, 'Edit discussion replies'),
|
||||
Permission('perm_discussion', perm.PERM_DELETE_DISCUSSION_REPLY, 'Delete discussion replies'),
|
||||
Permission('perm_contest', perm.PERM_VIEW_CONTEST, 'View contests'),
|
||||
Permission('perm_contest', perm.PERM_VIEW_CONTEST_SCOREBOARD, 'View contest scoreboard'),
|
||||
Permission('perm_contest', perm.PERM_VIEW_CONTEST_HIDDEN_SCOREBOARD, 'View hidden contest submission status and scoreboard'),
|
||||
Permission('perm_contest', perm.PERM_CREATE_CONTEST, 'Create contests'),
|
||||
Permission('perm_contest', perm.PERM_ATTEND_CONTEST, 'Attend contests'),
|
||||
Permission('perm_contest', perm.PERM_EDIT_CONTEST, 'Edit any contests'),
|
||||
Permission('perm_training', perm.PERM_VIEW_TRAINING, 'View training plans'),
|
||||
Permission('perm_training', perm.PERM_CREATE_TRAINING, 'Create training plans'),
|
||||
Permission('perm_training', perm.PERM_EDIT_TRAINING, 'Edit training plans'),
|
||||
];
|
||||
|
||||
const PERMS_BY_FAMILY = {};
|
||||
for (const p of PERMS) {
|
||||
if (!PERMS_BY_FAMILY[p.family]) PERMS_BY_FAMILY[p.family] = [p];
|
||||
else PERMS_BY_FAMILY[p.family].push(p);
|
||||
}
|
||||
perm.PERMS_BY_FAMILY = PERMS_BY_FAMILY;
|
||||
|
||||
perm.PERM_BASIC = perm.PERM_VIEW_PROBLEM
|
||||
+ perm.PERM_VIEW_PROBLEM_SOLUTION
|
||||
+ perm.PERM_VIEW_DISCUSSION
|
||||
+ perm.PERM_VIEW_TRAINING
|
||||
+ perm.PERM_VIEW_CONTEST
|
||||
+ perm.PERM_VIEW_CONTEST_SCOREBOARD
|
||||
+ perm.PERM_REGISTER_USER;
|
||||
|
||||
perm.PERM_DEFAULT = perm.PERM_BASIC
|
||||
+ perm.PERM_SUBMIT_PROBLEM
|
||||
+ perm.PERM_CREATE_PROBLEM_SOLUTION
|
||||
+ perm.PERM_VOTE_PROBLEM_SOLUTION
|
||||
+ perm.PERM_REPLY_PROBLEM_SOLUTION
|
||||
+ perm.PERM_CREATE_DISCUSSION
|
||||
+ perm.PERM_REPLY_DISCUSSION
|
||||
+ perm.PERM_ATTEND_CONTEST
|
||||
+ perm.PERM_CREATE_TRAINING
|
||||
+ perm.PERM_LOGGEDIN;
|
||||
|
||||
perm.PERM_ADMIN = perm.PERM_ALL;
|
||||
|
||||
module.exports = perm;
|
||||
Reference in new issue
Block a user