diff --git a/models/judge_state.js b/models/judge_state.js index cf61c5f..3bf928e 100644 --- a/models/judge_state.js +++ b/models/judge_state.js @@ -102,7 +102,7 @@ class JudgeState extends Model { async isAllowedSeeResultBy(user) { await this.loadRelationships(); - if (user && (user.is_admin || user.id === this.problem.user_id)) return true; + if (user && (await user.hasPrivilege('manage_problem') || user.id === this.problem.user_id)) return true; else if (this.type === 0) return true; else if (this.type === 1) { let contest = await Contest.fromID(this.type_info); @@ -117,7 +117,7 @@ class JudgeState extends Model { async isAllowedSeeCodeBy(user) { await this.loadRelationships(); - if (user && (user.is_admin || user.id === this.problem.user_id)) return true; + if (user && (await user.hasPrivilege('manage_problem') || user.id === this.problem.user_id)) return true; else if (this.type === 0) return this.problem.is_public; else if (this.type === 1) { let contest = await Contest.fromID(this.type_info); diff --git a/models/problem.js b/models/problem.js index 65dc5be..b638819 100644 --- a/models/problem.js +++ b/models/problem.js @@ -234,11 +234,21 @@ class Problem extends Model { } async isAllowedEditBy(user) { - return user && (user.is_admin || this.user_id === user.id); + if (!user) return false; + if (await user.hasPrivilege('manage_problem')) return true; + return this.user_id === user.id; } async isAllowedUseBy(user) { - return this.is_public || (user && (user.is_admin || this.user_id === user.id)); + if (!user) return false; + if (await user.hasPrivilege('manage_problem')) return true; + return this.is_public || this.user_id === user.id; + } + + async isAllowedManageBy(user) { + if (!user) return false; + if (await user.hasPrivilege('manage_problem')) return true; + return user.is_admin; } async updateTestdata(path) { diff --git a/models/user.js b/models/user.js index 2f3e774..3cbebea 100644 --- a/models/user.js +++ b/models/user.js @@ -82,6 +82,8 @@ class User extends Model { } async isAllowedEditBy(user) { + if (!user) return false; + if (await user.hasPrivilege('manage_user')) return true; return user && (user.is_admin || this.id === user.id); } @@ -179,6 +181,52 @@ class User extends Model { this.information = await syzoj.utils.markdown(this.information); } + async getPrivileges() { + let UserPrivilege = syzoj.model('user_privilege'); + let privileges = await UserPrivilege.query(null, { + user_id: this.id + }); + + return privileges.map(x => x.privilege); + } + + async setPrivileges(newPrivileges) { + let UserPrivilege = syzoj.model('user_privilege'); + + let oldPrivileges = await this.getPrivileges(); + + console.log(newPrivileges); + + let delPrivileges = oldPrivileges.filter(x => !newPrivileges.includes(x)); + let addPrivileges = newPrivileges.filter(x => !oldPrivileges.includes(x)); + + for (let privilege of delPrivileges) { + let obj = await UserPrivilege.findOne({ where: { + user_id: this.id, + privilege: privilege + } }); + + await obj.destroy(); + } + + for (let privilege of addPrivileges) { + let obj = await UserPrivilege.create({ + user_id: this.id, + privilege: privilege + }); + + await obj.save(); + } + } + + async hasPrivilege(privilege) { + if (this.is_admin) return true; + + let UserPrivilege = syzoj.model('user_privilege'); + let x = await UserPrivilege.findOne({ where: { user_id: this.id, privilege: privilege } }); + return !(!x); + } + getModel() { return model; } } diff --git a/models/user_privilege.js b/models/user_privilege.js new file mode 100644 index 0000000..5bb13a3 --- /dev/null +++ b/models/user_privilege.js @@ -0,0 +1,58 @@ +/* + * This file is part of SYZOJ. + * + * Copyright (c) 2016 Menci + * + * SYZOJ is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * SYZOJ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with SYZOJ. If not, see . + */ + +'use strict'; + +let Sequelize = require('sequelize'); +let db = syzoj.db; + +let model = db.define('user_privilege', { + user_id: { type: Sequelize.INTEGER, primaryKey: true }, + privilege: { + type: Sequelize.STRING, + primaryKey: true + } +}, { + timestamps: false, + tableName: 'user_privilege', + indexes: [ + { + fields: ['user_id'] + }, + { + fields: ['privilege'] + } + ] +}); + +let Model = require('./common'); +class UserPrivilege extends Model { + static async create(val) { + return UserPrivilege.fromRecord(UserPrivilege.model.build(Object.assign({ + user_id: 0, + privilege: '' + }, val))); + } + + getModel() { return model; } +} + +UserPrivilege.model = model; + +module.exports = UserPrivilege; diff --git a/modules/problem.js b/modules/problem.js index 4e80ebb..e2cb72c 100644 --- a/modules/problem.js +++ b/modules/problem.js @@ -38,6 +38,7 @@ app.get('/problems', async (req, res) => { }); res.render('problems', { + allowedManageTag: res.locals.user && await res.locals.user.hasPrivilege('manage_problem_tag'), problems: problems, paginate: paginate }); @@ -72,6 +73,7 @@ app.get('/problems/search', async (req, res) => { }); res.render('problems', { + allowedManageTag: res.locals.user && await res.locals.user.hasPrivilege('manage_problem_tag'), problems: problems, paginate: paginate }); @@ -114,6 +116,7 @@ app.get('/problems/tag/:tagIDs', async (req, res) => { }); res.render('problems', { + allowedManageTag: res.locals.user && await res.locals.user.hasPrivilege('manage_problem_tag'), problems: problems, tags: tags, paginate: paginate @@ -137,6 +140,7 @@ app.get('/problem/:id', async (req, res) => { } problem.allowedEdit = await problem.isAllowedEditBy(res.locals.user); + problem.allowedManage = await problem.isAllowedManageBy(res.locals.user); if (problem.is_public || problem.allowedEdit) { await syzoj.utils.markdown(problem, [ 'description', 'input_format', 'output_format', 'example', 'limit_and_hint' ]); @@ -210,6 +214,8 @@ app.get('/problem/:id/edit', async (req, res) => { problem.tags = await problem.getTags(); } + problem.allowedManage = await problem.isAllowedManageBy(res.locals.user); + res.render('problem_edit', { problem: problem }); @@ -241,7 +247,7 @@ app.post('/problem/:id/edit', async (req, res) => { if (!await problem.isAllowedUseBy(res.locals.user)) throw new ErrorMessage('您没有权限进行此操作。'); if (!await problem.isAllowedEditBy(res.locals.user)) throw new ErrorMessage('您没有权限进行此操作。'); - if (res.locals.user.is_admin) { + if (await res.locals.user.hasPrivilege('manage_problem')) { let customID = parseInt(req.body.id); if (customID && customID !== id) { if (await Problem.fromID(customID)) throw new ErrorMessage('ID 已被使用。'); @@ -442,8 +448,8 @@ async function setPublic(req, res, is_public) { let problem = await Problem.fromID(id); if (!problem) throw new ErrorMessage('无此题目。'); - let allowedEdit = await problem.isAllowedEditBy(res.locals.user); - if (!allowedEdit) throw new ErrorMessage('您没有权限进行此操作。'); + let allowedManage = await problem.isAllowedManageBy(res.locals.user); + if (!allowedManage) throw new ErrorMessage('您没有权限进行此操作。'); problem.is_public = is_public; await problem.save(); diff --git a/modules/problem_tag.js b/modules/problem_tag.js index 1d130e7..15ea58e 100644 --- a/modules/problem_tag.js +++ b/modules/problem_tag.js @@ -23,7 +23,7 @@ let ProblemTag = syzoj.model('problem_tag'); app.get('/problems/tag/:id/edit', async (req, res) => { try { - if (!res.locals.user && !res.locals.user.is_admin) throw new ErrorMessage('您没有权限进行此操作。'); + if (!res.locals.user || !await res.locals.user.hasPrivilege('manage_problem_tag')) throw new ErrorMessage('您没有权限进行此操作。'); let id = parseInt(req.params.id) || 0; let tag = await ProblemTag.fromID(id); @@ -46,7 +46,7 @@ app.get('/problems/tag/:id/edit', async (req, res) => { app.post('/problems/tag/:id/edit', async (req, res) => { try { - if (!res.locals.user || !res.locals.user.is_admin) throw new ErrorMessage('您没有权限进行此操作。'); + if (!res.locals.user || !await res.locals.user.hasPrivilege('manage_problem_tag')) throw new ErrorMessage('您没有权限进行此操作。'); let id = parseInt(req.params.id) || 0; let tag = await ProblemTag.fromID(id); diff --git a/modules/user.js b/modules/user.js index b80d4ef..de5739e 100644 --- a/modules/user.js +++ b/modules/user.js @@ -117,6 +117,10 @@ app.get('/user/:id/edit', async (req, res) => { throw new ErrorMessage('您没有权限进行此操作。'); } + user.privileges = await user.getPrivileges(); + + res.locals.user.allowedManage = await res.locals.user.hasPrivilege('manage_user'); + res.render('user_edit', { edited_user: user, error_info: null @@ -139,15 +143,26 @@ app.post('/user/:id/edit', async (req, res) => { if (!allowedEdit) throw new ErrorMessage('您没有权限进行此操作。'); if (req.body.old_password && req.body.new_password) { - if (user.password !== req.body.old_password && !res.locals.user.is_admin) throw new ErrorMessage('旧密码错误。'); + if (user.password !== req.body.old_password && !await res.locals.user.hasPrivilege('manage_user')) throw new ErrorMessage('旧密码错误。'); user.password = req.body.new_password; } - if (res.locals.user.is_admin) { + if (res.locals.user && await res.locals.user.hasPrivilege('manage_user')) { if (!syzoj.utils.isValidUsername(req.body.username)) throw new ErrorMessage('无效的用户名。'); user.username = req.body.username; } + if (res.locals.user && res.locals.user.is_admin) { + if (!req.body.privileges) { + req.body.privileges = []; + } else if (!Array.isArray(req.body.privileges)) { + req.body.privileges = [req.body.privileges]; + } + + let privileges = req.body.privileges; + await user.setPrivileges(privileges); + } + user.email = req.body.email; user.information = req.body.information; user.sex = req.body.sex; @@ -156,11 +171,18 @@ app.post('/user/:id/edit', async (req, res) => { if (user.id === res.locals.user.id) res.locals.user = user; + user.privileges = await user.getPrivileges(); + res.locals.user.allowedManage = await res.locals.user.hasPrivilege('manage_user'); + res.render('user_edit', { edited_user: user, error_info: '' }); } catch (e) { + console.log(e); + user.privileges = await user.getPrivileges(); + res.locals.user.allowedManage = await res.locals.user.hasPrivilege('manage_user'); + res.render('user_edit', { edited_user: user, error_info: e.message diff --git a/views/problem.ejs b/views/problem.ejs index d7d1d61..6541c1b 100644 --- a/views/problem.ejs +++ b/views/problem.ejs @@ -66,7 +66,7 @@ if (contest) { 编辑 管理测试数据 <% } %> - <% if (user && user.is_admin) { %> + <% if (problem.allowedManage) { %> <% if (problem.is_public) { %> 取消公开 <% } else { %> diff --git a/views/problem_edit.ejs b/views/problem_edit.ejs index dee8ff0..64b2dc7 100644 --- a/views/problem_edit.ejs +++ b/views/problem_edit.ejs @@ -13,7 +13,7 @@
- <% if (problem.new || user.is_admin) { %> + <% if (problem.new || problem.allowedManage) { %>
- <% if (user && user.is_admin) { %> + <% if (allowedManageTag) { %> <% if (typeof tags !== 'undefined' && tags.length === 1) { %> 编辑标签 <% } %> diff --git a/views/submission_content.ejs b/views/submission_content.ejs index d7bfdb4..408630e 100644 --- a/views/submission_content.ejs +++ b/views/submission_content.ejs @@ -98,7 +98,7 @@ else problemUrl = syzoj.utils.makeUrl(['problem', judge.problem_id]); <% } else if (judge.result.spj_compiler_output) { %>

Special Judge 编译信息

<%- syzoj.utils.ansiToHTML(judge.result.spj_compiler_output) %>
-<% } else if (judge.allowedSeeResult) { %> +<% } else if (judge.allowedSeeResult && judge.result.subtasks && (judge.result.subtasks.length !== 1 || judge.result.subtasks[0].case_num)) { %>
<% let subtask_count = 0; %> <% for (let subtask_cases of (judge.result.subtasks || [])) { %> diff --git a/views/user_edit.ejs b/views/user_edit.ejs index 2f94c56..9e36aa4 100644 --- a/views/user_edit.ejs +++ b/views/user_edit.ejs @@ -8,10 +8,10 @@

<%= error_info %>

<% } %>
-
+
- readonly<% } %>> + readonly<% } %>>
@@ -30,7 +30,7 @@
- +
@@ -41,8 +41,32 @@
- - 返回个人资料 + <% + let allowedManagePrivilege = user && user.is_admin; + %> +
+ +
+ checked<% } %>> + +
+
+ disabled="disabled" <% } %>type="checkbox"<% if (edited_user.privileges.includes('manage_problem')) { %> checked<% } %>> + +
+
+ disabled="disabled" <% } %>type="checkbox"<% if (edited_user.privileges.includes('manage_problem_tag')) { %> checked<% } %>> + +
+
+ disabled="disabled" <% } %>type="checkbox"<% if (edited_user.privileges.includes('manage_user')) { %> checked<% } %>> + +
+
+
+ + 返回个人资料 +
@@ -61,13 +85,28 @@ function check() { $("#error").removeClass("success"); $("#error").removeClass("error"); $("#error").addClass("error"); - $("#error_info").html("两次输入的密码不一致"); + $("#error_info").html("两次输入的密码不一致。"); $("#error").show(); return false; } make_md5(old_password); make_md5(password1); make_md5(password2); + +<% if (allowedManagePrivilege) { %> + $('.checkbox_privilege').each(function () { + if ($(this).checkbox('is checked')) { + var name = $(this).data('name'); + + var elem = document.createElement('input'); + elem.type = 'hidden'; + elem.value = name; + elem.name = 'privileges'; + document.getElementById('form').appendChild(elem); + } + }); +<% } %> + return true; }