From e092efca205494f016010f7e74d2a34d8600361f Mon Sep 17 00:00:00 2001 From: undefined Date: Mon, 29 Jun 2020 13:33:44 +0800 Subject: [PATCH] priv (unstable) --- hydro/entry/master.js | 1 - hydro/entry/worker.js | 1 - hydro/error.js | 12 + hydro/handler/contest.js | 18 +- hydro/handler/discussion.js | 26 +- hydro/handler/domain.js | 2 +- hydro/handler/home.js | 32 +- hydro/handler/homework.js | 6 +- hydro/handler/import.js | 2 +- hydro/handler/judge.js | 5 +- hydro/handler/misc.js | 6 +- hydro/handler/problem.js | 29 +- hydro/handler/record.js | 4 +- hydro/handler/system.js | 2 +- hydro/handler/training.js | 20 +- hydro/handler/user.js | 41 ++- hydro/interface.js | 2 +- hydro/lib/md5.js | 3 + hydro/lib/nav.js | 34 +- hydro/lib/template.js | 11 +- hydro/model/builtin.js | 307 +++++++++++++++++- hydro/model/contest.js | 4 +- hydro/model/discussion.js | 2 +- hydro/model/index.js | 19 -- hydro/model/problem.js | 86 +++-- hydro/model/setting.js | 2 - hydro/model/user.js | 35 +- hydro/permission.js | 129 -------- hydro/service/server.js | 61 +++- module/migrate-vijos/hydro.json | 2 +- module/migrate-vijos/script.js | 2 +- module/module-pastebin/handler.js | 10 +- templates/components/comments_discussion.html | 14 +- templates/components/comments_solution.html | 16 +- templates/components/problem.html | 7 +- templates/components/record.html | 2 +- templates/contest_detail.html | 6 +- templates/contest_main.html | 2 +- templates/discussion_detail.html | 12 +- templates/discussion_edit.html | 2 +- templates/discussion_main_or_node.html | 4 +- templates/homework_detail.html | 6 +- templates/homework_main.html | 2 +- templates/main.html | 4 +- templates/partials/contest_sidebar.html | 8 +- templates/partials/discussion_edit_form.html | 2 +- templates/partials/homework_sidebar.html | 8 +- templates/partials/nav.html | 2 +- templates/partials/problem_list.html | 6 +- .../partials/problem_sidebar_contest.html | 2 +- .../partials/problem_sidebar_homework.html | 2 +- .../partials/problem_sidebar_normal.html | 22 +- templates/problem_main.html | 2 +- templates/problem_settings.html | 2 +- templates/record_detail.html | 6 +- templates/record_detail_status.html | 4 +- templates/record_detail_summary.html | 4 +- templates/record_main.html | 2 +- templates/record_main_tr.html | 8 +- templates/training_detail.html | 14 +- templates/training_main.html | 6 +- templates/user_detail.html | 2 +- wiki/development/development.md | 8 +- 63 files changed, 706 insertions(+), 397 deletions(-) delete mode 100644 hydro/model/index.js delete mode 100644 hydro/permission.js diff --git a/hydro/entry/master.js b/hydro/entry/master.js index 00206f972..951957adc 100644 --- a/hydro/entry/master.js +++ b/hydro/entry/master.js @@ -27,7 +27,6 @@ async function load(call) { require('../lib/i18n'); require('../utils'); require('../error'); - require('../permission'); try { require('../options'); } catch (e) { diff --git a/hydro/entry/worker.js b/hydro/entry/worker.js index cb314fc05..c4b9dc31b 100644 --- a/hydro/entry/worker.js +++ b/hydro/entry/worker.js @@ -12,7 +12,6 @@ async function load() { require('../lib/i18n'); require('../utils'); require('../error'); - require('../permission'); require('../options'); await Promise.all([locale(pending, fail), template(pending, fail)]); const bus = require('../service/bus'); diff --git a/hydro/error.js b/hydro/error.js index e1c4cf51b..f4762f0af 100644 --- a/hydro/error.js +++ b/hydro/error.js @@ -112,6 +112,17 @@ class PermissionError extends ForbiddenError { } } +class PrivilegeError extends ForbiddenError { + constructor(priv) { + if (priv === global.Hydro.model.builtin.PRIV.PRIV_USER_PROFILE) { + super("You're not logged in."); + } else { + super("You don't have the required privilege."); + } + this.params = [priv]; + } +} + class ValidationError extends ForbiddenError { constructor(field0, field1) { super('ValidationError'); @@ -245,6 +256,7 @@ global.Hydro.error = module.exports = { ProblemDataNotFoundError, OpcountExceededError, PermissionError, + PrivilegeError, NoProblemError, ValidationError, ProblemNotFoundError, diff --git a/hydro/handler/contest.js b/hydro/handler/contest.js index cff10ee4a..fe2d5fa18 100644 --- a/hydro/handler/contest.js +++ b/hydro/handler/contest.js @@ -5,11 +5,16 @@ const { ContestNotLiveError, ValidationError, ProblemNotFoundError, ContestNotAttendedError, } = require('../error'); -const { - PERM_CREATE_CONTEST, PERM_EDIT_CONTEST, PERM_READ_RECORD_CODE, - PERM_VIEW_CONTEST, -} = require('../permission'); const paginate = require('../lib/paginate'); +const { + PERM: { + PERM_CREATE_CONTEST, PERM_EDIT_CONTEST, PERM_READ_RECORD_CODE, + PERM_VIEW_CONTEST, PERM_EDIT_CONTEST_SELF, + }, + PRIV: { + PRIV_READ_RECORD_CODE + } +} = require('../model/builtin'); const contest = require('../model/contest'); const document = require('../model/document'); const problem = require('../model/problem'); @@ -58,7 +63,7 @@ class ContestDetailHandler extends ContestHandler { this.response.template = 'contest_detail.html'; const [tsdoc, pdict] = await Promise.all([ contest.getStatus(domainId, this.tdoc.docId, this.user._id), - problem.getList(domainId, this.tdoc.pids), + problem.getList(domainId, this.tdoc.pids, true), ]); const psdict = {}; let rdict = {}; @@ -123,6 +128,7 @@ class ContestScoreboardDownloadHandler extends ContestDetailHandler { class ContestEditHandler extends ContestDetailHandler { async prepare() { if (this.tdoc.owner !== this.user._id) this.checkPerm(PERM_EDIT_CONTEST); + else this.checkPerm(PERM_EDIT_CONTEST_SELF); } async get() { @@ -266,7 +272,7 @@ class ContestProblemSubmitHandler extends ContestProblemHandler { class ContestCodeHandler extends ContestDetailHandler { async get({ domainId, tid }) { - this.checkPerm(PERM_READ_RECORD_CODE); + if (!this.user.hasPriv(PRIV_READ_RECORD_CODE)) this.checkPerm(PERM_READ_RECORD_CODE); this.limitRate('homework_code', 3600, 60); const [tdoc, tsdocs] = await contest.getAndListStatus(domainId, tid); const rnames = {}; diff --git a/hydro/handler/discussion.js b/hydro/handler/discussion.js index b061593a2..2bc2e281d 100644 --- a/hydro/handler/discussion.js +++ b/hydro/handler/discussion.js @@ -4,13 +4,18 @@ const system = require('../model/system'); const user = require('../model/user'); const discussion = require('../model/discussion'); const document = require('../model/document'); +const { + PERM: { + PERM_VIEW_DISCUSSION, PERM_EDIT_DISCUSSION, PERM_EDIT_DISCUSSION_REPLY, + PERM_DELETE_DISCUSSION, PERM_DELETE_DISCUSSION_REPLY, PERM_HIGHLIGHT_DISCUSSION, + PERM_CREATE_DISCUSSION, PERM_REPLY_DISCUSSION, + }, + PRIV: { + PRIV_USER_PROFILE, + }, +} = require('../model/builtin'); const { Route, Handler } = require('../service/server'); const { DiscussionNotFoundError, DocumentNotFoundError } = require('../error'); -const { - PERM_VIEW_DISCUSSION, PERM_EDIT_DISCUSSION, PERM_EDIT_DISCUSSION_REPLY, - PERM_DELETE_DISCUSSION, PERM_DELETE_DISCUSSION_REPLY, PERM_HIGHLIGHT_DISCUSSION, - PERM_LOGGEDIN, PERM_CREATE_DISCUSSION, PERM_REPLY_DISCUSSION, -} = require('../permission'); const typeMapper = { problem: document.TYPE_PROBLEM, @@ -119,7 +124,7 @@ class DiscussionNodeHandler extends DiscussionHandler { class DiscussionCreateHandler extends DiscussionHandler { async prepare() { - this.checkPerm(PERM_LOGGEDIN); + this.checkPriv(PRIV_USER_PROFILE); this.checkPerm(PERM_CREATE_DISCUSSION); } @@ -151,7 +156,7 @@ class DiscussionCreateHandler extends DiscussionHandler { class DiscussionDetailHandler extends DiscussionHandler { async get({ domainId, did, page = 1 }) { - const dsdoc = this.user.hasPerm(PERM_LOGGEDIN) + const dsdoc = this.user.hasPriv(PRIV_USER_PROFILE) ? await discussion.getStatus(domainId, did, this.user._id) : null; const [drdocs, pcount, drcount] = await paginate( @@ -181,8 +186,11 @@ class DiscussionDetailHandler extends DiscussionHandler { }; } + async post() { + this.checkPriv(PRIV_USER_PROFILE); + } + async postReply({ domainId, did, content }) { - this.checkPerm(PERM_LOGGEDIN); this.checkPerm(PERM_REPLY_DISCUSSION); this.limitRate('add_discussion', 3600, 30); await discussion.addReply(domainId, did, this.user._id, content, this.request.ip); @@ -190,7 +198,6 @@ class DiscussionDetailHandler extends DiscussionHandler { } async postTailReply({ domainId, drid, content }) { - this.checkPerm(PERM_LOGGEDIN); this.checkPerm(PERM_REPLY_DISCUSSION); this.limitRate('add_discussion', 3600, 30); await discussion.addTailReply(domainId, drid, this.user._id, content, this.request.ip); @@ -257,7 +264,6 @@ class DiscussionTailReplyRawHandler extends DiscussionHandler { class DiscussionEditHandler extends DiscussionHandler { async get() { - if (this.ddoc.owner !== this.user._id) this.checkPerm(PERM_EDIT_DISCUSSION); const path = [ ['Hydro', 'homepage'], ['discussion_main', 'discussion_main'], diff --git a/hydro/handler/domain.js b/hydro/handler/domain.js index 5c4ae6d9d..82d142d59 100644 --- a/hydro/handler/domain.js +++ b/hydro/handler/domain.js @@ -4,7 +4,7 @@ const system = require('../model/system'); const { DOMAIN_SETTINGS, DOMAIN_SETTINGS_BY_KEY } = require('../model/setting'); const paginate = require('../lib/paginate'); const { Route, Handler } = require('../service/server'); -const { PERM_MANAGE } = require('../permission'); +const { PERM_MANAGE } = require('../model/builtin').PERM; const { RoleAlreadyExistError, ValidationError, PermissionError } = require('../error'); class DomainRankHandler extends Handler { diff --git a/hydro/handler/home.js b/hydro/handler/home.js index 50a99f223..18061c0dd 100644 --- a/hydro/handler/home.js +++ b/hydro/handler/home.js @@ -21,9 +21,14 @@ const discussion = require('../model/discussion'); const token = require('../model/token'); const training = require('../model/training'); const { - PERM_VIEW_TRAINING, PERM_VIEW_CONTEST, PERM_VIEW_DISCUSSION, - PERM_LOGGEDIN, PERM_VIEW_HOMEWORK, -} = require('../permission'); + PERM: { + PERM_VIEW_TRAINING, PERM_VIEW_CONTEST, PERM_VIEW_DISCUSSION, + PERM_VIEW_HOMEWORK, + }, + PRIV: { + PRIV_USER_PROFILE, PRIV_DELETE_FILE, PRIV_DELETE_FILE_SELF, + }, +} = require('../model/builtin'); const { geoip, useragent } = global.Hydro.lib; @@ -113,10 +118,6 @@ class HomeHandler extends Handler { } class HomeSecurityHandler extends Handler { - async prepare() { - this.checkPerm(PERM_LOGGEDIN); - } - async get() { // TODO(iceboy): pagination? or limit session count for uid? const sessions = await token.getSessionListByUid(this.user._id); @@ -172,10 +173,6 @@ class HomeSecurityHandler extends Handler { } class HomeSettingsHandler extends Handler { - async prepare() { - this.checkPerm(PERM_LOGGEDIN); - } - async get({ category }) { // eslint-disable-next-line prefer-destructuring category = category[0]; // Category would be splitted into array @@ -284,7 +281,6 @@ class HomeMessagesConnectionHandler extends ConnectionHandler { } } -// TODO draft class HomeFileHandler extends Handler { async get() { const ufdocs = await file.getMulti({ owner: this.user._id }).toArray(); @@ -296,6 +292,7 @@ class HomeFileHandler extends Handler { async postDelete(ufid) { const ufdoc = await file.get(ufid); if (ufdoc.owner !== this.user._id) this.checkPriv(PRIV_DELETE_FILE); + else this.checkPriv(PRIV_DELETE_FILE_SELF); const result = await file.del(ufdoc._id); if (result) await file.decUsage(this.user._id, ufdoc.length); this.back(); @@ -304,11 +301,12 @@ class HomeFileHandler extends Handler { async function apply() { Route('homepage', '/', HomeHandler); - Route('home_security', '/home/security', HomeSecurityHandler, PERM_LOGGEDIN); - Route('user_changemail_with_code', '/home/changeMail/:code', UserChangemailWithCodeHandler, PERM_LOGGEDIN); - Route('home_settings', '/home/settings/:category', HomeSettingsHandler, PERM_LOGGEDIN); - Route('home_messages', '/home/messages', HomeMessagesHandler, PERM_LOGGEDIN); - Connection('home_messages_conn', '/home/messages-conn', HomeMessagesConnectionHandler); + Route('home_security', '/home/security', HomeSecurityHandler, PRIV_USER_PROFILE); + Route('user_changemail_with_code', '/home/changeMail/:code', UserChangemailWithCodeHandler, PRIV_USER_PROFILE); + Route('home_settings', '/home/settings/:category', HomeSettingsHandler, PRIV_USER_PROFILE); + Route('home_messages', '/home/messages', HomeMessagesHandler, PRIV_USER_PROFILE); + Route('home_file', '/home/file', HomeFileHandler, PRIV_USER_PROFILE); + Connection('home_messages_conn', '/home/messages-conn', HomeMessagesConnectionHandler, PRIV_USER_PROFILE); } global.Hydro.handler.home = module.exports = apply; diff --git a/hydro/handler/homework.js b/hydro/handler/homework.js index 91a3ac759..9bf269dfc 100644 --- a/hydro/handler/homework.js +++ b/hydro/handler/homework.js @@ -6,12 +6,12 @@ const { ValidationError, HomeworkNotLiveError, ProblemNotFoundError, HomeworkNotAttendedError, } = require('../error'); +const { Route, Handler } = require('../service/server'); const { PERM_VIEW_HOMEWORK, PERM_ATTEND_HOMEWORK, PERM_VIEW_PROBLEM, PERM_SUBMIT_PROBLEM, PERM_CREATE_HOMEWORK, PERM_EDIT_HOMEWORK, PERM_VIEW_HOMEWORK_SCOREBOARD, PERM_READ_RECORD_CODE, -} = require('../permission'); -const { Route, Handler } = require('../service/server'); +} = require('../model/builtin').PERM; const system = require('../model/system'); const user = require('../model/user'); const contest = require('../model/contest'); @@ -53,7 +53,7 @@ class HomeworkDetailHandler extends HomeworkHandler { const tdoc = await contest.get(domainId, tid, document.TYPE_HOMEWORK); const [tsdoc, pdict] = await Promise.all([ contest.getStatus(domainId, tdoc.docId, this.user._id, document.TYPE_HOMEWORK), - problem.getList(domainId, tdoc.pids), + problem.getList(domainId, tdoc.pids, true), ]); const psdict = {}; let rdict = {}; diff --git a/hydro/handler/import.js b/hydro/handler/import.js index 9aac00ef0..03fa8cae6 100644 --- a/hydro/handler/import.js +++ b/hydro/handler/import.js @@ -2,8 +2,8 @@ const fs = require('fs'); const path = require('path'); const os = require('os'); const problem = require('../model/problem'); +const { PERM_CREATE_PROBLEM } = require('../model/builtin').PERM; const { Route, Handler } = require('../service/server'); -const { PERM_CREATE_PROBLEM } = require('../permission'); const { ValidationError } = require('../error'); class ProblemImportHandler extends Handler { diff --git a/hydro/handler/judge.js b/hydro/handler/judge.js index 4cd397109..824ddc7f2 100644 --- a/hydro/handler/judge.js +++ b/hydro/handler/judge.js @@ -1,5 +1,4 @@ const yaml = require('js-yaml'); -const { PERM_JUDGE } = require('../permission'); const { parseTimeMS, parseMemoryMB } = require('../utils'); const record = require('../model/record'); const problem = require('../model/problem'); @@ -164,8 +163,8 @@ class JudgeConnectionHandler extends ConnectionHandler { async function apply() { Route('pretest', '/pretest', PretestHandler); - Route('judge', '/judge', JudgeHandler, PERM_JUDGE); - Connection('judge_conn', '/judge/conn', JudgeConnectionHandler, PERM_JUDGE); + Route('judge', '/judge', JudgeHandler, builtin.PERM.PERM_JUDGE); + Connection('judge_conn', '/judge/conn', JudgeConnectionHandler, builtin.PERM.PERM_JUDGE); } apply.next = next; diff --git a/hydro/handler/misc.js b/hydro/handler/misc.js index 801af120d..593fce709 100644 --- a/hydro/handler/misc.js +++ b/hydro/handler/misc.js @@ -1,4 +1,4 @@ -const { PERM_JUDGE, PERM_LOGGEDIN } = require('../permission'); +const { PRIV_JUDGE, PRIV_USER_PROFILE } = require('../model/builtin').PRIV; const file = require('../model/file'); const user = require('../model/user'); const markdown = require('../lib/markdown'); @@ -37,7 +37,7 @@ class StatusHandler extends Handler { class StatusUpdateHandler extends Handler { async post(args) { - this.checkPerm(PERM_JUDGE); + this.checkPerm(PRIV_JUDGE); args.type = 'judger'; return coll.updateOne( { mid: args.mid, type: 'judger' }, @@ -49,7 +49,7 @@ class StatusUpdateHandler extends Handler { class SwitchLanguageHandler extends Handler { async get({ lang }) { - if (this.user.hasPerm(PERM_LOGGEDIN)) await user.setById(this.user._id, { viewLang: lang }); + if (this.user.hasPriv(PRIV_USER_PROFILE)) await user.setById(this.user._id, { viewLang: lang }); else this.session.viewLang = lang; this.back(); } diff --git a/hydro/handler/problem.js b/hydro/handler/problem.js index 5b3a002ad..47d5c0a1a 100644 --- a/hydro/handler/problem.js +++ b/hydro/handler/problem.js @@ -6,6 +6,18 @@ const record = require('../model/record'); const user = require('../model/user'); const solution = require('../model/solution'); const system = require('../model/system'); +const { + PERM: { + PERM_VIEW_PROBLEM, PERM_VIEW_PROBLEM_HIDDEN, PERM_SUBMIT_PROBLEM, + PERM_CREATE_PROBLEM, PERM_READ_PROBLEM_DATA, PERM_EDIT_PROBLEM, + PERM_VIEW_PROBLEM_SOLUTION, PERM_CREATE_PROBLEM_SOLUTION, + PERM_EDIT_PROBLEM_SOLUTION, PERM_DELETE_PROBLEM_SOLUTION, PERM_EDIT_PROBLEM_SOLUTION_REPLY, + PERM_REPLY_PROBLEM_SOLUTION, + }, + PRIV: { + PRIV_USER_PROFILE, PRIV_JUDGE, + }, +} = require('../model/builtin'); const bus = require('../service/bus'); const { Route, Connection, Handler, ConnectionHandler, @@ -14,13 +26,6 @@ const { NoProblemError, ProblemDataNotFoundError, BadRequestError, SolutionNotFoundError, } = require('../error'); -const { - PERM_VIEW_PROBLEM, PERM_VIEW_PROBLEM_HIDDEN, PERM_SUBMIT_PROBLEM, - PERM_CREATE_PROBLEM, PERM_READ_PROBLEM_DATA, PERM_EDIT_PROBLEM, - PERM_JUDGE, PERM_VIEW_PROBLEM_SOLUTION, PERM_CREATE_PROBLEM_SOLUTION, - PERM_EDIT_PROBLEM_SOLUTION, PERM_DELETE_PROBLEM_SOLUTION, PERM_EDIT_PROBLEM_SOLUTION_REPLY, - PERM_REPLY_PROBLEM_SOLUTION, PERM_LOGGEDIN, -} = require('../permission'); class ProblemHandler extends Handler { async _prepare() { @@ -47,7 +52,7 @@ class ProblemHandler extends Handler { page, await system.get('PROBLEM_PER_PAGE'), ); - if (this.user.hasPerm(PERM_LOGGEDIN)) { + if (this.user.hasPriv(PRIV_USER_PROFILE)) { psdict = await problem.getListStatus( domainId, this.user._id, pdocs.map((pdoc) => pdoc.docId), ); @@ -109,7 +114,7 @@ class ProblemCategoryHandler extends ProblemHandler { page, await system.get('PROBLEM_PER_PAGE'), ); - if (this.user.hasPerm(PERM_LOGGEDIN)) { + if (this.user.hasPriv(PRIV_USER_PROFILE)) { psdict = await problem.getListStatus( domainId, this.user._id, pdocs.map((pdoc) => pdoc.docId), ); @@ -302,7 +307,7 @@ class ProblemEditHandler extends ProblemManageHandler { const pid = validator.checkPid(this.request.body.pid); const pdoc = await problem.get(domainId, this.request.params.pid); await problem.edit(domainId, pdoc.docId, { title, content, pid }); - this.response.redirect = `/p/${pid}`; + this.back(); } } @@ -332,7 +337,9 @@ class ProblemDataUploadHandler extends ProblemManageHandler { class ProblemDataDownloadHandler extends ProblemDetailHandler { async get({ pid }) { - if (this.user._id !== this.pdoc.owner) this.checkPerm([PERM_READ_PROBLEM_DATA, PERM_JUDGE]); + if (this.user._id !== this.pdoc.owner) { + if (!this.user.hasPerm(PERM_READ_PROBLEM_DATA)) this.checkPriv(PRIV_JUDGE); + } if (!this.pdoc.data) throw new ProblemDataNotFoundError(pid); else if (typeof this.pdoc.data === 'string') [, this.response.redirect] = this.pdoc.data.split('from:'); this.response.redirect = await file.url(this.pdoc.data, `${this.pdoc.title}.zip`); diff --git a/hydro/handler/record.js b/hydro/handler/record.js index c0c6bfc53..02b105cb8 100644 --- a/hydro/handler/record.js +++ b/hydro/handler/record.js @@ -1,6 +1,6 @@ const { ObjectID } = require('bson'); -const { PERM_READ_RECORD_CODE, PERM_REJUDGE, PERM_VIEW_PROBLEM_HIDDEN } = require('../permission'); const { PermissionError } = require('../error'); +const { PERM_READ_RECORD_CODE, PERM_REJUDGE, PERM_VIEW_PROBLEM_HIDDEN } = require('../model/builtin').PERM; const problem = require('../model/problem'); const record = require('../model/record'); const contest = require('../model/contest'); @@ -34,7 +34,7 @@ class RecordListHandler extends RecordHandler { ); const [udict, pdict] = await Promise.all([ user.getList(domainId, rdocs.map((rdoc) => rdoc.uid)), - problem.getList(domainId, rdocs.map((rdoc) => rdoc.pid), false), + problem.getList(domainId, rdocs.map((rdoc) => rdoc.pid), false, false), ]); const path = [ ['Hydro', 'homepage'], diff --git a/hydro/handler/system.js b/hydro/handler/system.js index f3e002c78..52cf5e7d4 100644 --- a/hydro/handler/system.js +++ b/hydro/handler/system.js @@ -2,7 +2,7 @@ const domain = require('../model/domain'); const system = require('../model/system'); const setting = require('../model/setting'); const { Route, Handler } = require('../service/server'); -const { PERM_MANAGE } = require('../permission'); +const { PERM_MANAGE } = require('../model/builtin').PERM; const hpm = require('../lib/hpm'); const loader = require('../loader'); diff --git a/hydro/handler/training.js b/hydro/handler/training.js index 981d03879..fb6fc017f 100644 --- a/hydro/handler/training.js +++ b/hydro/handler/training.js @@ -1,10 +1,15 @@ const assert = require('assert'); const { ValidationError, ProblemNotFoundError } = require('../error'); -const { - PERM_LOGGEDIN, PERM_VIEW_TRAINING, PERM_VIEW_PROBLEM_HIDDEN, - PERM_CREATE_TRAINING, PERM_EDIT_TRAINING, -} = require('../permission'); const paginate = require('../lib/paginate'); +const { + PERM: { + PERM_VIEW_TRAINING, PERM_VIEW_PROBLEM_HIDDEN, PERM_CREATE_TRAINING, + PERM_EDIT_TRAINING, + }, + PRIV: { + PRIV_USER_PROFILE, + }, +} = require('../model/builtin'); const problem = require('../model/problem'); const builtin = require('../model/builtin'); const training = require('../model/training'); @@ -70,7 +75,7 @@ class TrainingMainHandler extends TrainingHandler { for (const tdoc of tdocs) tids.add(tdoc.docId); const tsdict = {}; let tdict = {}; - if (this.user.hasPerm(PERM_LOGGEDIN)) { + if (this.user.hasPriv(PRIV_USER_PROFILE)) { const enrolledTids = new Set(); const tsdocs = await training.getMultiStatus(domainId, { uid: this.user._id, @@ -101,10 +106,9 @@ class TrainingDetailHandler extends TrainingHandler { async get({ domainId, tid }) { const tdoc = await training.get(domainId, tid); const pids = training.getPids(tdoc); - const f = this.user.hasPerm(PERM_VIEW_PROBLEM_HIDDEN) ? {} : { hidden: false }; const [owner, pdict] = await Promise.all([ user.getById(domainId, tdoc.owner), - problem.getList(domainId, pids, f), + problem.getList(domainId, pids, this.user.hasPerm(PERM_VIEW_PROBLEM_HIDDEN)), ]); const psdict = await problem.getListStatus(domainId, this.user._id, pids); const donePids = new Set(); @@ -151,7 +155,7 @@ class TrainingDetailHandler extends TrainingHandler { } async postEnroll({ domainId, tid }) { - this.checkPerm(PERM_LOGGEDIN); + this.checkPriv(PRIV_USER_PROFILE); const tdoc = await training.get(domainId, tid); await training.enroll(domainId, tdoc.docId, this.user._id); this.back(); diff --git a/hydro/handler/user.js b/hydro/handler/user.js index cab9db9b6..24971b5a2 100644 --- a/hydro/handler/user.js +++ b/hydro/handler/user.js @@ -12,9 +12,16 @@ const misc = require('../lib/misc'); const { UserAlreadyExistError, InvalidTokenError, VerifyPasswordError, UserNotFoundError, LoginError, SystemError, - PermissionError, BlacklistedError, UserFacingError, + BlacklistedError, UserFacingError, } = require('../error'); -const { PERM_LOGGEDIN } = require('../permission.js'); +const { + PERM: { + PERM_VIEW_PROBLEM_HIDDEN, + }, + PRIV: { + PRIV_USER_PROFILE, PRIV_REGISTER_USER, + }, +} = require('../model/builtin'); class UserLoginHandler extends Handler { async get() { @@ -47,17 +54,11 @@ class UserLogoutHandler extends Handler { } async post() { - this.session.uid = 1; + this.session.uid = 0; } } class UserRegisterHandler extends Handler { - async prepare() { // eslint-disable-line class-methods-use-this - if (!await system.get('user.register')) { - throw new PermissionError('Register'); - } - } - async get() { this.response.template = 'user_register.html'; } @@ -78,7 +79,7 @@ class UserRegisterHandler extends Handler { await sendMail(mail, 'Sign Up', 'user_register_mail', m); this.response.template = 'user_register_mail_sent.html'; } else { - this.response.redirect = `/register/${t[0]}`; + this.response.redirect = this.url('user_register_with_code', { code: t[0] }); } } } @@ -103,7 +104,7 @@ class UserRegisterWithCodeHandler extends Handler { }); await token.del(code, token.TYPE_REGISTRATION); this.session.uid = uid; - this.response.redirect = '/'; + this.response.redirect = this.url('homepage'); } } @@ -142,7 +143,7 @@ class UserLostPassWithCodeHandler extends Handler { if (password !== verifyPassword) throw new VerifyPasswordError(); await user.setPassword(tdoc.uid, password); await token.del(code, token.TYPE_LOSTPASS); - this.response.redirect = '/'; + this.response.redirect = this.url('homepage'); } } @@ -152,9 +153,12 @@ class UserDetailHandler extends Handler { const udoc = await user.getById(domainId, uid, true); const sdoc = await token.getMostRecentSessionByUid(uid); const rdocs = await record.getByUid(domainId, uid); - const pdict = await problem.getList(domainId, rdocs.map((rdoc) => rdoc.pid), false); + const pdict = await problem.getList( + domainId, rdocs.map((rdoc) => rdoc.pid), + this.user.hasPerm(PERM_VIEW_PROBLEM_HIDDEN), false, + ); // Remove sensitive data - if (!isSelfProfile) sdoc.createIp = sdoc.updateIp = sdoc._id = ''; + if (!isSelfProfile && sdoc) sdoc.createIp = sdoc.updateIp = sdoc._id = ''; this.response.template = 'user_detail.html'; this.response.body = { isSelfProfile, udoc, sdoc, rdocs, pdict, @@ -296,6 +300,7 @@ class OauthCallbackHandler extends Handler { if (udoc) { this.session.uid = udoc._id; } else { + this.checkPriv(PRIV_REGISTER_USER); let username = ''; r.uname = r.uname || []; r.uname.push(String.random(16)); @@ -325,13 +330,13 @@ async function apply() { Route('user_login', '/login', UserLoginHandler); Route('user_oauth', '/oauth/:type', OauthHandler); Route('user_oauth_callback', '/oauth/:type/callback', OauthCallbackHandler); - Route('user_register', '/register', UserRegisterHandler); - Route('user_register_with_code', '/register/:code', UserRegisterWithCodeHandler); - Route('user_logout', '/logout', UserLogoutHandler); + Route('user_register', '/register', UserRegisterHandler, PRIV_REGISTER_USER); + Route('user_register_with_code', '/register/:code', UserRegisterWithCodeHandler, PRIV_REGISTER_USER); + Route('user_logout', '/logout', UserLogoutHandler, PRIV_USER_PROFILE); Route('user_lostpass', '/lostpass', UserLostPassHandler); Route('user_lostpass_with_code', '/lostpass/:code', UserLostPassWithCodeHandler); Route('user_search', '/user/search', UserSearchHandler); - Route('user_delete', '/user/delete', UserDeleteHandler, PERM_LOGGEDIN); + Route('user_delete', '/user/delete', UserDeleteHandler, PRIV_USER_PROFILE); Route('user_detail', '/user/:uid', UserDetailHandler); } diff --git a/hydro/interface.js b/hydro/interface.js index 3075b3b39..a38985de3 100644 --- a/hydro/interface.js +++ b/hydro/interface.js @@ -20,7 +20,7 @@ exports.udoc = { viewLang: 'zh_CN', loginat: new Date(), loginip: '0.0.0.0', - priv: 0, + priv: '', }; exports.pdoc = { diff --git a/hydro/lib/md5.js b/hydro/lib/md5.js index e13e7e236..27cff470a 100644 --- a/hydro/lib/md5.js +++ b/hydro/lib/md5.js @@ -1,3 +1,6 @@ +// From https://github.com/pvorb/node-md5/blob/master/md5.js +// BSD 3-Clause "New" or "Revised" License + /* eslint-disable no-bitwise */ /* eslint-disable max-len */ function safeAdd(x, y) { const lsw = (x & 0xffff) + (y & 0xffff); diff --git a/hydro/lib/nav.js b/hydro/lib/nav.js index cbda158ad..e109130d4 100644 --- a/hydro/lib/nav.js +++ b/hydro/lib/nav.js @@ -1,13 +1,35 @@ -const permission = require('../permission'); +const { isNull } = require('lodash'); + +const permission = require('../model/builtin').PERM; global.Hydro.ui.nav = []; const trueChecker = () => true; -const Item = (name, args, prefix, perm, checker) => { - if (perm && checker) { - checker = ((_chk) => (handler) => _chk(handler) && handler.user.hasPerm(perm))(checker); - } else if (perm) { - checker = (handler) => handler.user.hasPerm(perm); +const Item = (name, args, prefix, ...permPrivChecker) => { + let _priv; + let _perm; + let checker; + for (const item of permPrivChecker) { + if (typeof item === 'object' && !isNull(item)) { + if (typeof item.call !== 'undefined') { + checker = item; + } if (typeof item[0] === 'number') { + _priv = item; + } else if (typeof item[0] === 'string') { + _perm = item; + } + } else if (typeof item === 'number') { + _priv = item; + } else if (typeof item === 'string') { + _perm = item; + } + } + if ((_perm || _priv) && checker) { + checker = ((_chk) => (handler) => _chk(handler) + && (_perm ? handler.user.hasPerm(_perm) : true) + && (_priv ? handler.user.hasPriv(_priv) : true))(checker); + } else if (_perm) { + checker = (handler) => handler.user.hasPerm(_perm); } else if (!checker) checker = trueChecker; global.Hydro.ui.nav.push({ name, args: args || {}, prefix, checker, diff --git a/hydro/lib/template.js b/hydro/lib/template.js index ce9315e24..27438b29c 100644 --- a/hydro/lib/template.js +++ b/hydro/lib/template.js @@ -1,8 +1,6 @@ const nunjucks = require('nunjucks'); const markdown = require('./markdown'); -const perm = require('../permission'); -const builtin = require('../model/builtin'); -const model = require('../model'); +const { STATUS, PERM, PRIV } = require('../model/builtin'); const misc = require('./misc'); function Loader() { } @@ -38,11 +36,12 @@ function render(name, state) { static_url: (str) => `/${str}`, datetimeSpan: misc.datetimeSpan, paginate: misc.paginate, - perm, - status: builtin.status, + perm: PERM, + PRIV, + STATUS, size: misc.size, gravatar: misc.gravatar, - model, + model: global.Hydro.model, Context: global.Hydro.ui, isIE: (str) => str.includes('MSIE') || str.includes('rv:11.0'), }, (err, res) => { diff --git a/hydro/model/builtin.js b/hydro/model/builtin.js index b005ec493..1bf700455 100644 --- a/hydro/model/builtin.js +++ b/hydro/model/builtin.js @@ -1,9 +1,293 @@ -const perm = require('../permission'); +/* eslint-disable no-bitwise */ +const PERM = { + PERM_NONE: '', + + // Domain Settings + PERM_VIEW: '0', + PERM_EDIT_DOMAIN: '1', + PERM_MOD_BADGE: '2', + + // Problem and Record + PERM_CREATE_PROBLEM: '3', + PERM_EDIT_PROBLEM: '4', + PERM_EDIT_PROBLEM_SELF: '5', + PERM_VIEW_PROBLEM: '6', + PERM_VIEW_PROBLEM_HIDDEN: '7', + PERM_SUBMIT_PROBLEM: '8', + PERM_READ_PROBLEM_DATA: '9', + PERM_READ_PROBLEM_DATA_SELF: 'A', + PERM_READ_RECORD_CODE: 'B', + PERM_REJUDGE_PROBLEM: 'C', + PERM_REJUDGE: 'D', + + // Problem Solution + PERM_VIEW_PROBLEM_SOLUTION: 'E', + PERM_CREATE_PROBLEM_SOLUTION: 'F', + PERM_VOTE_PROBLEM_SOLUTION: 'G', + PERM_EDIT_PROBLEM_SOLUTION: 'H', + PERM_EDIT_PROBLEM_SOLUTION_SELF: 'I', + PERM_DELETE_PROBLEM_SOLUTION: 'J', + PERM_DELETE_PROBLEM_SOLUTION_SELF: 'K', + PERM_REPLY_PROBLEM_SOLUTION: 'L', + PERM_EDIT_PROBLEM_SOLUTION_REPLY: 'M', + PERM_EDIT_PROBLEM_SOLUTION_REPLY_SELF: 'N', + PERM_DELETE_PROBLEM_SOLUTION_REPLY: 'O', + PERM_DELETE_PROBLEM_SOLUTION_REPLY_SELF: 'P', + + // Discussion + PERM_VIEW_DISCUSSION: 'Q', + PERM_CREATE_DISCUSSION: 'R', + PERM_HIGHLIGHT_DISCUSSION: 'S', + PERM_EDIT_DISCUSSION: 'T', + PERM_EDIT_DISCUSSION_SELF: 'U', + PERM_DELETE_DISCUSSION: 'V', + PERM_DELETE_DISCUSSION_SELF: 'W', + PERM_REPLY_DISCUSSION: 'X', + PERM_EDIT_DISCUSSION_REPLY: 'Y', + PERM_EDIT_DISCUSSION_REPLY_SELF: 'Z', + PERM_EDIT_DISCUSSION_REPLY_SELF_DISCUSSION: 'a', + PERM_DELETE_DISCUSSION_REPLY: 'b', + PERM_DELETE_DISCUSSION_REPLY_SELF: 'c', + PERM_DELETE_DISCUSSION_REPLY_SELF_DISCUSSION: 'd', + + // Contest + PERM_VIEW_CONTEST: 'e', + PERM_VIEW_CONTEST_SCOREBOARD: 'f', + PERM_VIEW_CONTEST_HIDDEN_SCOREBOARD: 'g', + PERM_CREATE_CONTEST: 'h', + PERM_ATTEND_CONTEST: 'i', + PERM_EDIT_CONTEST: 'j', + PERM_EDIT_CONTEST_SELF: 'k', + + // Homework + PERM_VIEW_HOMEWORK: 'l', + PERM_VIEW_HOMEWORK_SCOREBOARD: 'm', + PERM_VIEW_HOMEWORK_HIDDEN_SCOREBOARD: 'n', + PERM_CREATE_HOMEWORK: 'o', + PERM_ATTEND_HOMEWORK: 'p', + PERM_EDIT_HOMEWORK: 'q', + PERM_EDIT_HOMEWORK_SELF: 'r', + + // Training + PERM_VIEW_TRAINING: 's', + PERM_CREATE_TRAINING: 't', + PERM_EDIT_TRAINING: 'u', + PERM_EDIT_TRAINING_SELF: 'v', +}; + +const Permission = (family, key, desc) => ({ family, key, desc }); + +const PERMS = [ + Permission('perm_general', PERM.PERM_VIEW, 'View this domain'), + Permission('perm_general', PERM.PERM_EDIT_DOMAIN, 'Edit domain settings'), + 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_EDIT_PROBLEM_SELF, 'Edit own 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_problem', PERM.PERM_READ_PROBLEM_DATA_SELF, 'Read data of own problems'), + 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_EDIT_PROBLEM_SOLUTION_SELF, 'Edit own problem solutions'), + Permission('perm_problem_solution', PERM.PERM_DELETE_PROBLEM_SOLUTION, 'Delete problem solutions'), + Permission('perm_problem_solution', PERM.PERM_DELETE_PROBLEM_SOLUTION_SELF, 'Delete own 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_EDIT_PROBLEM_SOLUTION_REPLY_SELF, 'Edit own problem solution replies'), + Permission('perm_problem_solution', PERM.PERM_DELETE_PROBLEM_SOLUTION_REPLY, 'Delete problem solution replies'), + Permission('perm_problem_solution', PERM.PERM_DELETE_PROBLEM_SOLUTION_REPLY_SELF, 'Delete own 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_EDIT_DISCUSSION_SELF, 'Edit own discussions'), + Permission('perm_discussion', PERM.PERM_DELETE_DISCUSSION, 'Delete discussions'), + Permission('perm_discussion', PERM.PERM_DELETE_DISCUSSION_SELF, 'Delete own 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_EDIT_DISCUSSION_REPLY_SELF, 'Edit own discussion replies'), + Permission('perm_discussion', PERM.PERM_EDIT_DISCUSSION_REPLY_SELF_DISCUSSION, 'Edit discussion replies of own discussion'), + Permission('perm_discussion', PERM.PERM_DELETE_DISCUSSION_REPLY, 'Delete discussion replies'), + Permission('perm_discussion', PERM.PERM_DELETE_DISCUSSION_REPLY_SELF, 'Delete own discussion replies'), + Permission('perm_discussion', PERM.PERM_DELETE_DISCUSSION_REPLY_SELF_DISCUSSION, 'Delete discussion replies of own discussion'), + 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_contest', PERM.PERM_EDIT_CONTEST_SELF, 'Edit own contests'), + Permission('perm_homework', PERM.PERM_VIEW_HOMEWORK, 'View homework'), + Permission('perm_homework', PERM.PERM_VIEW_HOMEWORK_SCOREBOARD, 'View homework scoreboard'), + Permission('perm_homework', PERM.PERM_VIEW_HOMEWORK_HIDDEN_SCOREBOARD, 'View hidden homework submission status and scoreboard'), + Permission('perm_homework', PERM.PERM_CREATE_HOMEWORK, 'Create homework'), + Permission('perm_homework', PERM.PERM_ATTEND_HOMEWORK, 'Claim homework'), + Permission('perm_homework', PERM.PERM_EDIT_HOMEWORK, 'Edit any homework'), + Permission('perm_homework', PERM.PERM_EDIT_HOMEWORK_SELF, 'Edit own homework'), + 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'), + Permission('perm_training', PERM.PERM_EDIT_TRAINING_SELF, 'Edit own training plans'), + Permission('perm_ranking', PERM.PERM_VIEW_RANKING, 'View ranking'), +]; + +let PERM_ALL = ''; +for (const p in PERM) PERM_ALL += PERM[p]; +PERM.PERM_ALL = PERM_ALL; + +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.PERM_BASIC = PERM.PERM_VIEW + + PERM.PERM_VIEW_PROBLEM + + PERM.PERM_VIEW_PROBLEM_SOLUTION + + PERM.PERM_VIEW_DISCUSSION + + PERM.PERM_VIEW_CONTEST + + PERM.PERM_VIEW_CONTEST_SCOREBOARD + + PERM.PERM_VIEW_HOMEWORK + + PERM.PERM_VIEW_HOMEWORK_SCOREBOARD + + PERM.PERM_VIEW_TRAINING; + +PERM.PERM_DEFAULT = PERM.PERM_VIEW + + PERM.PERM_VIEW_PROBLEM + + PERM.PERM_EDIT_PROBLEM_SELF + + PERM.PERM_SUBMIT_PROBLEM + + PERM.PERM_READ_PROBLEM_DATA_SELF + + PERM.PERM_VIEW_PROBLEM_SOLUTION + + PERM.PERM_CREATE_PROBLEM_SOLUTION + + PERM.PERM_VOTE_PROBLEM_SOLUTION + + PERM.PERM_EDIT_PROBLEM_SOLUTION_SELF + + PERM.PERM_DELETE_PROBLEM_SOLUTION_SELF + + PERM.PERM_REPLY_PROBLEM_SOLUTION + + PERM.PERM_EDIT_PROBLEM_SOLUTION_REPLY_SELF + + PERM.PERM_DELETE_PROBLEM_SOLUTION_REPLY_SELF + + PERM.PERM_VIEW_DISCUSSION + + PERM.PERM_CREATE_DISCUSSION + + PERM.PERM_EDIT_DISCUSSION_SELF + + PERM.PERM_REPLY_DISCUSSION + + PERM.PERM_EDIT_DISCUSSION_REPLY_SELF + // PERM_EDIT_DISCUSSION_REPLY_SELF_DISCUSSION + + PERM.PERM_DELETE_DISCUSSION_REPLY_SELF + + PERM.PERM_DELETE_DISCUSSION_REPLY_SELF_DISCUSSION + + PERM.PERM_VIEW_CONTEST + + PERM.PERM_VIEW_CONTEST_SCOREBOARD + + PERM.PERM_ATTEND_CONTEST + + PERM.PERM_EDIT_CONTEST_SELF + + PERM.PERM_VIEW_HOMEWORK + + PERM.PERM_VIEW_HOMEWORK_SCOREBOARD + + PERM.PERM_ATTEND_HOMEWORK + + PERM.PERM_EDIT_HOMEWORK_SELF + + PERM.PERM_VIEW_TRAINING + + PERM.PERM_CREATE_TRAINING + + PERM.PERM_EDIT_TRAINING_SELF + + PERM.PERM_VIEW_RANKING + + 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_ATTEND_HOMEWORK; + +PERM.PERM_ADMIN = PERM.PERM_ALL; + +const PRIV = { + PRIV_NONE: 0, + PRIV_SET_PRIV: 1 << 0, + PRIV_SET_PERM: 1 << 1, + PRIV_USER_PROFILE: 1 << 2, + PRIV_REGISTER_USER: 1 << 3, + PRIV_READ_PROBLEM_DATA: 1 << 4, + PRIV_READ_PRETEST_DATA: 1 << 5, // deprecated + PRIV_READ_PRETEST_DATA_SELF: 1 << 6, // deprecated + PRIV_READ_RECORD_CODE: 1 << 7, + PRIV_VIEW_HIDDEN_RECORD: 1 << 8, + PRIV_JUDGE: 1 << 9, // (renamed) + PRIV_CREATE_DOMAIN: 1 << 10, + PRIV_VIEW_ALL_DOMAIN: 1 << 11, + PRIV_MANAGE_ALL_DOMAIN: 1 << 12, + PRIV_REJUDGE: 1 << 13, + PRIV_VIEW_USER_SECRET: 1 << 14, + PRIV_VIEW_JUDGE_STATISTICS: 1 << 15, + PRIV_CREATE_FILE: 1 << 16, + PRIV_UNLIMITED_QUOTA: 1 << 17, + PRIV_DELETE_FILE: 1 << 18, + PRIV_DELETE_FILE_SELF: 1 << 19, + PRIV_ALL: -1, +}; + +PRIV.PRIV_DETAULT = PRIV.PRIV_USER_PROFILE + + PRIV.PRIV_REGISTER_USER + + PRIV.PRIV_READ_PRETEST_DATA_SELF + + PRIV.PRIV_CREATE_DOMAIN + + PRIV.PRIV_CREATE_FILE + + PRIV.PRIV_DELETE_FILE_SELF + + PRIV.PRIV_USERFILE; + +// [10, 1] means that people whose rank is less than 1% will get Level 10 +const LEVELS = [ + [10, 1], + [9, 2], + [8, 10], + [7, 20], + [6, 30], + [5, 40], + [4, 70], + [3, 90], + [2, 95], + [1, 100], +]; + +const BUILTIN_USERS = [ + { + _id: 0, + mail: 'Guest@hydro.local', + mailLower: 'guest@hydro.local', + uname: 'Guest', + unameLower: 'guest', + salt: '', + hash: '', + hashType: 'hydro', + regat: new Date(0), + regip: '127.0.0.1', + loginat: new Date(0), + loginip: '127.0.0.1', + priv: PRIV.PRIV_NONE, + }, + { + _id: 1, + mail: 'Hydro@hydro.local', + mailLower: 'hydro@hydro.local', + uname: 'Hydro', + unameLower: 'hydro', + salt: '', + hash: '', + hashType: 'hydro', + regat: new Date(0), + regip: '127.0.0.1', + loginat: new Date(0), + loginip: '127.0.0.1', + priv: PRIV.PRIV_USER_PROFILE, + }, +]; const BUILTIN_ROLES = { - guest: { perm: perm.PERM_BASIC }, - default: { perm: perm.PERM_DEFAULT }, - admin: { perm: perm.PERM_ADMIN }, + guest: { perm: PERM.PERM_BASIC }, + default: { perm: PERM.PERM_DEFAULT }, + admin: { perm: PERM.PERM_ADMIN }, }; const DEFAULT_NODES = { @@ -13,7 +297,7 @@ const DEFAULT_NODES = { { pic: 'solution', name: '题解' }, ], Vijos: [ - { pic: 'vijos', name: 'Vijos' }, + { pic: 'hydro', name: 'Hydro' }, { pic: null, name: '团队' }, { pic: null, name: '月赛' }, { pic: 'advice', name: '建议' }, @@ -201,12 +485,15 @@ const CATEGORIES = { '构造', ], }; + const FOOTER_EXTRA_HTMLS = []; + const VIEW_LANGS = [ { code: 'zh_CN', name: '简体中文' }, { code: 'zh_TW', name: '正體中文' }, { code: 'en', name: 'English' }, ]; + const LANG_TEXTS = { c: 'C', cc: 'C++', @@ -222,6 +509,7 @@ const LANG_TEXTS = { go: 'Go', rb: 'Ruby', }; + const LANG_HIGHLIGHT_ID = { c: 'c', cc: 'cpp', @@ -237,6 +525,7 @@ const LANG_HIGHLIGHT_ID = { go: 'go', rb: 'ruby', }; + const STATUS = { STATUS_WAITING: 0, STATUS_ACCEPTED: 1, @@ -254,6 +543,7 @@ const STATUS = { STATUS_FETCHED: 22, STATUS_IGNORED: 30, }; + const STATUS_TEXTS = { [STATUS.STATUS_WAITING]: 'Waiting', [STATUS.STATUS_ACCEPTED]: 'Accepted', @@ -271,6 +561,7 @@ const STATUS_TEXTS = { [STATUS.STATUS_FETCHED]: 'Fetched', [STATUS.STATUS_IGNORED]: 'Ignored', }; + const STATUS_CODES = { 0: 'pending', 1: 'pass', @@ -290,6 +581,12 @@ const STATUS_CODES = { }; global.Hydro.model.builtin = module.exports = { + PERM, + PERMS, + PERMS_BY_FAMILY, + PRIV, + LEVELS, + BUILTIN_USERS, BUILTIN_ROLES, DEFAULT_NODES, CATEGORIES, diff --git a/hydro/model/contest.js b/hydro/model/contest.js index d310988b2..da71746a4 100644 --- a/hydro/model/contest.js +++ b/hydro/model/contest.js @@ -4,7 +4,7 @@ const { ValidationError, ContestNotFoundError, ContestAlreadyAttendedError, ContestNotAttendedError, ContestScoreboardHiddenError, } = require('../error'); -const { PERM_VIEW_CONTEST_HIDDEN_SCOREBOARD } = require('../permission'); +const { PERM_VIEW_CONTEST_HIDDEN_SCOREBOARD } = require('./builtin').PERM; const validator = require('../lib/validator'); const ranked = require('../lib/rank'); const document = require('./document'); @@ -339,7 +339,7 @@ const ContestHandlerMixin = (c) => class extends c { for (const tsdoc of tsdocs) uids.push(tsdoc.uid); const [udict, pdict] = await Promise.all([ user.getList(domainId, uids), - problem.getList(domainId, tdoc.pids), + problem.getList(domainId, tdoc.pids, true), ]); const rankedTsdocs = RULES[tdoc.rule].rank(tsdocs); const rows = RULES[tdoc.rule].scoreboard(isExport, (str) => (str ? str.toString().translate(this.user.language) : ''), tdoc, rankedTsdocs, udict, pdict); diff --git a/hydro/model/discussion.js b/hydro/model/discussion.js index efe61cd70..0a96a38b3 100644 --- a/hydro/model/discussion.js +++ b/hydro/model/discussion.js @@ -3,7 +3,7 @@ const problem = require('./problem'); const document = require('./document'); const contest = require('./contest'); const training = require('./training'); -const { PERM_VIEW_PROBLEM_HIDDEN } = require('../permission'); +const { PERM_VIEW_PROBLEM_HIDDEN } = require('./builtin').PERM; const { DocumentNotFoundError } = require('../error'); const typeDisplay = { diff --git a/hydro/model/index.js b/hydro/model/index.js deleted file mode 100644 index 45eb42bcf..000000000 --- a/hydro/model/index.js +++ /dev/null @@ -1,19 +0,0 @@ -module.exports = { - blacklist: require('./blacklist'), - builtin: require('./builtin'), - contest: require('./contest'), - document: require('./document'), - opcount: require('./opcount'), - problem: require('./problem'), - record: require('./record'), - solution: require('./solution'), - system: require('./system'), - token: require('./token'), - training: require('./training'), - user: require('./user'), - task: require('./task'), - message: require('./message'), - setting: require('./setting'), - discussion: require('./discussion'), - file: require('./file'), -}; diff --git a/hydro/model/problem.js b/hydro/model/problem.js index 300b126e2..30bd5417a 100644 --- a/hydro/model/problem.js +++ b/hydro/model/problem.js @@ -4,6 +4,7 @@ const document = require('./document'); const domain = require('./domain'); const { ProblemNotFoundError } = require('../error'); const readConfig = require('../lib/readConfig'); +const { ObjectId } = require('bson'); /** * @typedef {import('../interface').Pdoc} Pdoc @@ -11,16 +12,50 @@ const readConfig = require('../lib/readConfig'); * @typedef {import('mongodb').Cursor} Cursor */ +class Problem { + constructor(pdoc) { + this.docType = document.TYPE_PROBLEM; + if (pdoc) { + this._id = pdoc._id; + this.docId = pdoc.docId; + this.pid = pdoc.pid; + this.title = pdoc.title; + this.content = pdoc.content; + this.owner = pdoc.owner; + this.config = pdoc.config; + this.data = pdoc.data; + this.nSubmit = pdoc.nSubmit; + this.nAccept = pdoc.nAccept; + this.difficulty = pdoc.difficulty || 0; + this.tag = pdoc.tag || []; + this.category = pdoc.category || []; + this.hidden = pdoc.hidden || false; + } else { + this._id = new ObjectId(); + this.docId = this._id; + this.pid = String.random(8); + this.title = '*'; + this.content = ''; + this.owner = 1; + this.config = ''; + this.data = null; + this.nSubmit = 0; + this.nAccept = 0; + this.difficulty = 0; + this.tag = []; + this.category = []; + this.hidden = true; + } + } +} + /** * @param {string} domainId * @param {string} title * @param {string} content * @param {number} owner - * @param {number} pid - * @param {import('bson').ObjectID} data - * @param {string[]} category - * @param {string[]} tag - * @param {boolean} hidden + * @param {object} args + * @returns {Promise} docId */ async function add(domainId, title, content, owner, { pid = null, @@ -43,7 +78,7 @@ async function add(domainId, title, content, owner, { * @param {string} domainId * @param {string|number} pid * @param {number} uid - * @returns {Promise} + * @returns {Promise} */ async function get(domainId, pid, uid = null, doThrow = true) { if (!Number.isNaN(parseInt(pid, 10))) pid = parseInt(pid, 10); @@ -57,7 +92,7 @@ async function get(domainId, pid, uid = null, doThrow = true) { if (uid) { pdoc.psdoc = await document.getStatus(domainId, document.TYPE_PROBLEM, pdoc.docId, uid); } - return pdoc; + return new Problem(pdoc); } /** @@ -66,11 +101,16 @@ async function get(domainId, pid, uid = null, doThrow = true) { * @param {object} sort * @param {number} page * @param {number} limit - * @returns {Promise} + * @returns {Promise} */ -function getMany(domainId, query, sort, page, limit) { - return document.getMulti(domainId, query).sort(sort).skip((page - 1) * limit).limit(limit) +async function getMany(domainId, query, sort, page, limit) { + const pdocs = await document.getMulti(domainId, query) + .sort(sort).skip((page - 1) * limit).limit(limit) .toArray(); + for (let i = 0; i < pdocs.length; i++) { + pdocs[i] = new Problem(pdocs[i]); + } + return pdocs; } /** @@ -95,10 +135,11 @@ function getMultiStatus(domainId, query) { * @param {string} domainId * @param {ObjectID} _id * @param {object} query - * @returns {Promise} + * @returns {Promise} */ -function edit(domainId, _id, $set) { - return document.set(domainId, document.TYPE_PROBLEM, _id, $set); +async function edit(domainId, _id, $set) { + const pdoc = await document.set(domainId, document.TYPE_PROBLEM, _id, $set); + return new Problem(pdoc); } function inc(domainId, _id, field, n) { @@ -118,20 +159,23 @@ async function random(domainId, query) { } return null; } -async function getList(domainId, pids, doThrow = true) { +async function getList(domainId, pids, getHidden = false, doThrow = true) { pids = Array.from(new Set(pids)); const r = {}; - const pdocs = await document.getMulti( - domainId, document.TYPE_PROBLEM, - { $or: [{ docId: { $in: pids } }, { pid: { $in: pids } }] }, - ).toArray(); + const q = { $or: [{ docId: { $in: pids } }, { pid: { $in: pids } }] }; + if (!getHidden) q.hidden = false; + const pdocs = await document.getMulti(domainId, document.TYPE_PROBLEM, q).toArray(); for (const pdoc of pdocs) { r[pdoc.docId] = r[pdoc.pid] = pdoc; } if (pdocs.length !== pids.length) { - if (doThrow) { - for (const pid of pids) { - if (!r[pid]) throw new ProblemNotFoundError(domainId, pid); + for (const pid of pids) { + if (!r[pid]) { + if (doThrow) { + throw new ProblemNotFoundError(domainId, pid); + } else { + r[pid] = new Problem(); + } } } } diff --git a/hydro/model/setting.js b/hydro/model/setting.js index 921cf65a6..5cafd0a63 100644 --- a/hydro/model/setting.js +++ b/hydro/model/setting.js @@ -88,8 +88,6 @@ DomainSetting( ); SystemSetting( - Setting('setting_basic', 'user.register', null, false, 'checkbox', 'Allow register'), - Setting('setting_basic', 'user.create_domain', null, true, 'checkbox', 'Allow domain creation'), Setting('setting_smtp', 'smtp.user', null, null, 'text', 'SMTP Username'), Setting('setting_smtp', 'smtp.pass', null, null, 'password', 'SMTP Password'), Setting('setting_smtp', 'smtp.host', null, null, 'text', 'SMTP Server Host'), diff --git a/hydro/model/user.js b/hydro/model/user.js index b1df6da15..f19bd1fdc 100644 --- a/hydro/model/user.js +++ b/hydro/model/user.js @@ -4,9 +4,9 @@ const system = require('./system'); const token = require('./token'); const setting = require('./setting'); const { UserNotFoundError, UserAlreadyExistError, LoginError } = require('../error'); -const perm = require('../permission'); const pwhash = require('../lib/hash.hydro'); const db = require('../service/db'); +const { PRIV } = require('./builtin'); const coll = db.collection('user'); @@ -42,6 +42,11 @@ class USER { return this.perm.includes(p); } + hasPriv(p) { + // eslint-disable-next-line no-bitwise + return this.priv & p; + } + checkPassword(password) { const h = global.Hydro.lib[`hash.${this.hashType || 'hydro'}`]; if (!h) throw new Error('Unknown hash method'); @@ -55,14 +60,17 @@ async function getInDomain(domainId, udoc) { let dudoc = await document.getStatus(domainId, document.TYPE_DOMAIN_USER, 0, udoc._id); dudoc = dudoc || {}; if (udoc._id === 1) dudoc.role = 'guest'; - if (udoc.priv === 1) dudoc.role = 'admin'; + // eslint-disable-next-line no-bitwise + if (udoc.priv & PRIV.PRIV_MANAGE_ALL_DOMAIN) dudoc.role = 'admin'; const p = await document.get(domainId, document.TYPE_DOMAIN_USER, dudoc.role || 'default'); dudoc.perm = p ? p.content : builtin.BUILTIN_ROLES[dudoc.role || 'default'].perm; return dudoc; } async function getById(domainId, _id, throwError = false) { - const udoc = await coll.findOne({ _id }); + const udoc = _id === 0 || _id === 1 + ? builtin.BUILTIN_USERS[_id] + : await coll.findOne({ _id }); if (!udoc) { if (throwError) throw new UserNotFoundError(_id); else return null; @@ -81,7 +89,11 @@ async function getList(domainId, uids) { async function getByUname(domainId, uname, ignoreMissing = false) { const unameLower = uname.trim().toLowerCase(); - const udoc = await coll.findOne({ unameLower }); + const udoc = (unameLower === 'guest') + ? builtin.BUILTIN_USERS[0] + : unameLower === 'hydro' + ? builtin.BUILTIN_USERS[1] + : await coll.findOne({ unameLower }); if (!udoc) { if (ignoreMissing) return null; throw new UserNotFoundError(uname); @@ -92,7 +104,11 @@ async function getByUname(domainId, uname, ignoreMissing = false) { async function getByEmail(domainId, mail, ignoreMissing = false) { const mailLower = mail.trim().toLowerCase(); - const udoc = await coll.findOne({ mailLower }); + const udoc = (mailLower === 'guest@hydro.local') + ? builtin.BUILTIN_USERS[0] + : mailLower === 'hydro@hydro.local' + ? builtin.BUILTIN_USERS[1] + : await coll.findOne({ mailLower }); if (!udoc) { if (ignoreMissing) return null; throw new UserNotFoundError(mail); @@ -121,7 +137,7 @@ function setEmail(uid, mail) { } async function changePassword(uid, currentPassword, newPassword) { - const udoc = await getById(uid); + const udoc = await getById('system', uid); udoc.checkPassword(currentPassword); const salt = String.random(); return await coll.findOneAndUpdate( @@ -153,7 +169,7 @@ async function incDomain(domainId, uid, field, n = 1) { * @returns {Promise} uid */ async function create({ - uid, mail, uname, password, regip = '127.0.0.1', priv = perm.PRIV_NONE, + uid, mail, uname, password, regip = '127.0.0.1', priv = PRIV.PRIV_DEFAULT, }) { const salt = String.random(); if (!uid) uid = await system.inc('user'); @@ -195,6 +211,10 @@ async function getPrefixList(prefix, limit = 50) { return udocs; } +function setPriv(uid, priv) { + return coll.findOneAndUpdate({ _id: uid }, { $set: priv }, { returnOriginal: false }); +} + function setRole(domainId, uid, role) { return document.setStatus(domainId, document.TYPE_DOMAIN_USER, 0, uid, { role }); } @@ -271,6 +291,7 @@ global.Hydro.model.user = module.exports = { setMultiInDomain, getMultiInDomain, getPrefixList, + setPriv, setRole, setRoles, getRole, diff --git a/hydro/permission.js b/hydro/permission.js deleted file mode 100644 index 9403cf60e..000000000 --- a/hydro/permission.js +++ /dev/null @@ -1,129 +0,0 @@ -const perm = { - PERM_MANAGE: '#', - PERM_LOGGEDIN: '0', - PERM_MOD_BADGE: '3', - PERM_CREATE_PROBLEM: '5', - PERM_EDIT_PROBLEM: '6', - PERM_VIEW_PROBLEM: '7', - PERM_VIEW_PROBLEM_HIDDEN: '8', - PERM_SUBMIT_PROBLEM: '9', - PERM_READ_PROBLEM_DATA: 'A', - PERM_READ_RECORD_CODE: 'B', - PERM_REJUDGE_PROBLEM: 'C', - PERM_REJUDGE: 'D', - PERM_VIEW_PROBLEM_SOLUTION: 'E', - PERM_CREATE_PROBLEM_SOLUTION: 'F', - PERM_VOTE_PROBLEM_SOLUTION: 'G', - PERM_EDIT_PROBLEM_SOLUTION: 'H', - PERM_DELETE_PROBLEM_SOLUTION: 'I', - PERM_REPLY_PROBLEM_SOLUTION: 'J', - PERM_EDIT_PROBLEM_SOLUTION_REPLY: 'K', - PERM_DELETE_PROBLEM_SOLUTION_REPLY: 'L', - PERM_VIEW_DISCUSSION: 'M', - PERM_CREATE_DISCUSSION: 'N', - PERM_HIGHLIGHT_DISCUSSION: 'O', - PERM_EDIT_DISCUSSION: 'P', - PERM_DELETE_DISCUSSION: 'Q', - PERM_REPLY_DISCUSSION: 'R', - PERM_EDIT_DISCUSSION_REPLY: 'S', - PERM_DELETE_DISCUSSION_REPLY: 'T', - PERM_VIEW_CONTEST: 'U', - PERM_VIEW_CONTEST_SCOREBOARD: 'V', - PERM_VIEW_CONTEST_HIDDEN_SCOREBOARD: 'W', - PERM_CREATE_CONTEST: 'X', - PERM_ATTEND_CONTEST: 'Y', - PERM_EDIT_CONTEST: 'Z', - PERM_VIEW_TRAINING: 'a', - PERM_CREATE_TRAINING: 'b', - PERM_EDIT_TRAINING: 'c', - PERM_VIEW_HOMEWORK: 'e', - PERM_CREATE_HOMEWORK: 'f', - PERM_EDIT_HOMEWORK: 'g', - PERM_ATTEND_HOMEWORK: 'h', - PERM_VIEW_HOMEWORK_SCOREBOARD: 'i', - PERM_VIEW_HOMEWORK_HIDDEN_SCOREBOARD: 'j', - PERM_JUDGE: 'd', -}; - -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_MANAGE, 'Manage domain'), - 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_homework', perm.PERM_VIEW_HOMEWORK, 'View homework'), - Permission('perm_homework', perm.PERM_VIEW_HOMEWORK_SCOREBOARD, 'View homework scoreboard'), - Permission('perm_homework', perm.PERM_VIEW_HOMEWORK_HIDDEN_SCOREBOARD, 'View hidden homework submission status and scoreboard'), - Permission('perm_homework', perm.PERM_CREATE_HOMEWORK, 'Create homework'), - Permission('perm_homework', perm.PERM_ATTEND_HOMEWORK, 'Claim homework'), - Permission('perm_homework', perm.PERM_EDIT_HOMEWORK, 'Edit any homework'), -]; - -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_VIEW_HOMEWORK - + perm.PERM_VIEW_HOMEWORK_SCOREBOARD; - -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_ATTEND_HOMEWORK; - -perm.PERM_ADMIN = perm.PERM_ALL; - -perm.PRIV_NONE = 0; -perm.PRIV_ADMIN = 1; - -global.Hydro.permission = module.exports = perm; diff --git a/hydro/service/server.js b/hydro/service/server.js index ff39ebe11..a1fef27a8 100644 --- a/hydro/service/server.js +++ b/hydro/service/server.js @@ -21,7 +21,7 @@ const token = require('../model/token'); const opcount = require('../model/opcount'); const { UserNotFoundError, BlacklistedError, PermissionError, - UserFacingError, ValidationError, + UserFacingError, ValidationError, PrivilegeError, } = require('../error'); let enableLog = true; @@ -196,6 +196,23 @@ class Handler { } } + checkPriv(...args) { + for (const i in args) { + if (args[i] instanceof Array) { + let p = false; + for (const j in args) { + if (this.user.hasPriv(args[i][j])) { + p = true; + break; + } + } + if (!p) throw new PrivilegeError([args[i]]); + } else if (!this.user.hasPriv(args[i])) { + throw new PrivilegeError([[args[i]]]); + } + } + } + async limitRate(op, periodSecs, maxOperations) { await opcount.inc(op, this.request.ip, periodSecs, maxOperations); } @@ -252,7 +269,7 @@ class Handler { updateUa: this.request.headers['user-agent'] || '', }, ) : { uid: 1 }; - if (!this.session) this.session = { uid: 1 }; + if (!this.session) this.session = { uid: 0 }; const bdoc = await blacklist.get(this.request.ip); if (bdoc) throw new BlacklistedError(this.request.ip); this.user = await user.getById(domainId, this.session.uid); @@ -355,7 +372,7 @@ class Handler { } } -async function handle(ctx, HandlerClass, permission) { +async function handle(ctx, HandlerClass, checker) { global.Hydro.stat.reqCount++; const h = new HandlerClass(ctx); try { @@ -366,7 +383,7 @@ async function handle(ctx, HandlerClass, permission) { h.args = args; if (h.___prepare) await h.___prepare(args); - if (permission) h.checkPerm(permission); + if (checker) checker.call(h); let checking = ''; try { @@ -404,9 +421,38 @@ async function handle(ctx, HandlerClass, permission) { } } -function Route(name, route, RouteHandler, permission = null) { - router.all(name, route, (ctx) => handle(ctx, RouteHandler, permission)); - router.all(`${name}_with_domainId`, `/d/:domainId${route}`, (ctx) => handle(ctx, RouteHandler, permission)); +function Route(name, route, RouteHandler, ...permPrivChecker) { + let _priv; + let _perm; + let checker; + for (const item of permPrivChecker) { + if (typeof item === 'object') { + if (typeof item.call !== 'undefined') { + checker = item; + } if (typeof item[0] === 'number') { + _priv = item; + } else if (typeof item[0] === 'string') { + _perm = item; + } + } else if (typeof item === 'number') { + _priv = item; + } else if (typeof item === 'string') { + _perm = item; + } + } + if ((_perm || _priv) && checker) { + checker = ((_chk) => function __() { + _chk(); + if (_perm) this.checkPerm(_perm); + if (_priv) this.checkPriv(_priv); + })(checker); + } else if (_perm) { + checker = function __() { + this.checkPerm(_perm); + }; + } + router.all(name, route, (ctx) => handle(ctx, RouteHandler, checker)); + router.all(`${name}_with_domainId`, `/d/:domainId${route}`, (ctx) => handle(ctx, RouteHandler, checker)); } class ConnectionHandler { @@ -444,7 +490,6 @@ class ConnectionHandler { } async renderHTML(name, context) { - this.hasPerm = (perm) => this.user.hasPerm(perm); this._user = { ...this.user, gravatar: misc.gravatar(this.user.gravatar, 128) }; const res = await template.render(name, Object.assign(context, { handler: this, diff --git a/module/migrate-vijos/hydro.json b/module/migrate-vijos/hydro.json index 8f1fa9aa4..434c30fb2 100644 --- a/module/migrate-vijos/hydro.json +++ b/module/migrate-vijos/hydro.json @@ -1,5 +1,5 @@ { "id": "migrate-vijos", - "version": "1.0.2", + "version": "1.0.3", "description": "Migrate From Vijos" } \ No newline at end of file diff --git a/module/migrate-vijos/script.js b/module/migrate-vijos/script.js index 2b09f337c..e15268f58 100644 --- a/module/migrate-vijos/script.js +++ b/module/migrate-vijos/script.js @@ -24,7 +24,7 @@ const tasks = { salt: doc.salt, hash: doc.hash.split('|')[1], hashType: doc.hash.split('|')[0] === 'vj4' ? 'hydro' : doc.hash.split('|')[0], - priv: doc.priv === -1 ? 1 : 0, + priv: doc.priv, gravatar: doc.gravatar, mail: doc.mail, mailLower: doc.mail_lower, diff --git a/module/module-pastebin/handler.js b/module/module-pastebin/handler.js index ed6a85ff3..eefe83719 100644 --- a/module/module-pastebin/handler.js +++ b/module/module-pastebin/handler.js @@ -1,5 +1,5 @@ const { Route, Handler } = global.Hydro.service.server; -const { PERM_LOGGEDIN } = global.Hydro.permission; +const { PRIV: { PRIV_USER_PROFILE } } = global.Hydro.model.builtin; const { pastebin } = global.Hydro.model; const { nav } = global.Hydro.lib; @@ -23,10 +23,6 @@ class PasteShowHandler extends Handler { } class PasteCreateHandler extends Handler { - async prepare() { - this.checkPerm(PERM_LOGGEDIN); - } - async get() { const path = [ ['Hydro', 'homepage'], @@ -49,9 +45,9 @@ class PasteCreateHandler extends Handler { async function apply() { Route('pastebin', '/paste', PasteMainHandler); - Route('paste_create', '/paste/create', PasteCreateHandler); + Route('paste_create', '/paste/create', PasteCreateHandler, PRIV_USER_PROFILE); Route('paste_show', '/paste/:docId', PasteShowHandler); - nav('pastebin', null, 'pastebin', PERM_LOGGEDIN); + nav('pastebin', null, 'pastebin', PRIV_USER_PROFILE); } global.Hydro.handler.pastebin = module.exports = apply; diff --git a/templates/components/comments_discussion.html b/templates/components/comments_discussion.html index 3b3bf170c..1c1fe87fa 100644 --- a/templates/components/comments_discussion.html +++ b/templates/components/comments_discussion.html @@ -28,15 +28,15 @@ {% macro reply_operations(doc, rdoc) %}
-{% if handler.hasPerm(reply_post_perm) %} +{% if handler.user.hasPerm(reply_post_perm) %} {% endif %} -{% if doc.owner == handler.state.user._id or handler.hasPerm(reply_edit_perm) %} +{% if doc.owner == handler.state.user._id or handler.user.hasPerm(reply_edit_perm) %} {% endif %} -{% if doc.owner == handler.state.user._id or handler.hasPerm(reply_delete_perm) %} +{% if doc.owner == handler.state.user._id or handler.user.hasPerm(reply_delete_perm) %} @@ -71,7 +71,7 @@ reply_delete_op = 'delete_tail_reply' ) %}
    -{% if handler.hasPerm(comment_post_perm) %} +{% if handler.user.hasPerm(comment_post_perm) %}
  • @@ -116,17 +116,17 @@ @ {{ datetimeSpan(doc['_id'])|safe }}
    - {% if handler.hasPerm(reply_post_perm) %} + {% if handler.user.hasPerm(reply_post_perm) %} {% endif %} - {% if doc.owner == handler.state.user._id or handler.hasPerm(comment_edit_perm) %} + {% if doc.owner == handler.state.user._id or handler.user.hasPerm(comment_edit_perm) %} {% endif %} - {% if doc.owner == handler.state.user._id or handler.hasPerm(comment_delete_perm) %} + {% if doc.owner == handler.state.user._id or handler.user.hasPerm(comment_delete_perm) %} diff --git a/templates/components/comments_solution.html b/templates/components/comments_solution.html index ffcc888c3..06bc895b7 100644 --- a/templates/components/comments_solution.html +++ b/templates/components/comments_solution.html @@ -30,7 +30,7 @@ reply_delete_op = 'delete_tail_reply' ) %}
      -{% if handler.hasPerm(comment_post_perm) %} +{% if handler.user.hasPerm(comment_post_perm) %}
    • @@ -85,7 +85,7 @@
      - {% if handler.hasPerm(perm.PERM_VOTE_PROBLEM_SOLUTION) %} + {% if handler.user.hasPerm(perm.PERM_VOTE_PROBLEM_SOLUTION) %}
      {{ doc.vote }}
      @@ -113,17 +113,17 @@ @ {{ datetimeSpan(doc._id)|safe }}
      - {% if handler.hasPerm(reply_post_perm) %} + {% if handler.user.hasPerm(reply_post_perm) %} {% endif %} - {% if doc.owner == handler.state.user._id or handler.hasPerm(comment_edit_perm) %} + {% if doc.owner == handler.state.user._id or handler.user.hasPerm(comment_edit_perm) %} {% endif %} - {% if doc.owner == handler.state.user._id or handler.hasPerm(comment_delete_perm) %} + {% if doc.owner == handler.state.user._id or handler.user.hasPerm(comment_delete_perm) %} @@ -145,15 +145,15 @@ {{ user.render_inline(udoc, badge=false) }} @ {{ datetimeSpan(rdoc['_id'])|safe }}
      - {% if handler.hasPerm(reply_post_perm) %} + {% if handler.user.hasPerm(reply_post_perm) %} {% endif %} - {% if doc.owner == handler.state.user._id or handler.hasPerm(reply_edit_perm) %} + {% if doc.owner == handler.state.user._id or handler.user.hasPerm(reply_edit_perm) %} {% endif %} - {% if doc.owner == handler.state.user._id or handler.hasPerm(reply_delete_perm) %} + {% if doc.owner == handler.state.user._id or handler.user.hasPerm(reply_delete_perm) %} diff --git a/templates/components/problem.html b/templates/components/problem.html index 44d460365..2b4d95296 100644 --- a/templates/components/problem.html +++ b/templates/components/problem.html @@ -1,4 +1,4 @@ -{% macro render_problem_title(pdoc, tdoc=none, show_tags=true, show_invisible_flag=true, rp=none, invalid=false) %} +{% macro render_problem_title(pdoc, tdoc=none, show_tags=true, show_invisible_flag=true, invalid=false) %} {%- if not invalid %} ({{ _('Hidden') }}) {%- endif %} -{%- if not rp is none %} - RP+{{ rp|round|int }} -{%- endif %} {%- if show_tags %} {{ render_problem_tags(pdoc) }} {%- endif %} @@ -29,7 +26,7 @@ {%- if pdoc['tag']|length > 0 %} {%- else %} diff --git a/templates/components/record.html b/templates/components/record.html index 8df2e5ed7..e09a88c67 100644 --- a/templates/components/record.html +++ b/templates/components/record.html @@ -6,7 +6,7 @@ {{ model.builtin.STATUS_TEXTS[rdoc['status']] }}
      -{% if rdoc['status'] == status.STATUS_JUDGING %} +{% if rdoc['status'] == STATUS.STATUS_JUDGING %}
      diff --git a/templates/contest_detail.html b/templates/contest_detail.html index 3dcb906f3..24a893505 100644 --- a/templates/contest_detail.html +++ b/templates/contest_detail.html @@ -24,7 +24,7 @@
      - {% if handler.hasPerm(perm.PERM_LOGGEDIN) %} + {% if handler.user.hasPriv(PRIV.PRIV_USER_PROFILE) %} {% endif %} @@ -32,7 +32,7 @@ - {% if handler.hasPerm(perm.PERM_LOGGEDIN) %} + {% if handler.user.hasPriv(PRIV.PRIV_USER_PROFILE) %} {% endif %} @@ -42,7 +42,7 @@ {% for pid in tdoc.pids %} - {% if handler.hasPerm(perm.PERM_LOGGEDIN) %} + {% if handler.user.hasPriv(PRIV.PRIV_USER_PROFILE) %} {% if psdict[pid] and psdict[pid].rid %} {% set rdoc = rdict[psdict[pid].rid] %} {% if handler.canShowRecord(tdoc) %} diff --git a/templates/contest_main.html b/templates/contest_main.html index 74f719a06..e209b099e 100644 --- a/templates/contest_main.html +++ b/templates/contest_main.html @@ -115,7 +115,7 @@
      - {% if handler.hasPerm(perm.PERM_CREATE_CONTEST) %} + {% if handler.user.hasPerm(perm.PERM_CREATE_CONTEST) %}

      diff --git a/templates/discussion_detail.html b/templates/discussion_detail.html index 000502cfe..a443b66b2 100644 --- a/templates/discussion_detail.html +++ b/templates/discussion_detail.html @@ -29,7 +29,7 @@

      {% if vnode %} - {% if handler.hasPerm(perm.PERM_CREATE_DISCUSSION) %} + {% if handler.user.hasPerm(perm.PERM_CREATE_DISCUSSION) %}

      {{ _('Create a Discussion') }}

      {% else %} - {% if not handler.hasPerm(perm.PERM_LOGGEDIN) %} + {% if not handler.user.hasPriv(PRIV.PRIV_USER_PROFILE) %}

      {{ _('Login to Create a Discussion') }}

      {% else %}

      {{ _('No Permission to Create a Discussion') }}

      diff --git a/templates/homework_detail.html b/templates/homework_detail.html index 51353386a..cba9a3c53 100644 --- a/templates/homework_detail.html +++ b/templates/homework_detail.html @@ -23,7 +23,7 @@
      {{ _('Status') }} {{ _('Last Submit At') }}
      - {% if handler.hasPerm(perm.PERM_LOGGEDIN) %} + {% if handler.user.hasPriv(PRIV.PRIV_USER_PROFILE) %} {% endif %} @@ -31,7 +31,7 @@ - {% if handler.hasPerm(perm.PERM_LOGGEDIN) %} + {% if handler.user.hasPriv(PRIV.PRIV_USER_PROFILE) %} {% endif %} @@ -41,7 +41,7 @@ {% for pid in tdoc['pids'] %} - {% if handler.hasPerm(perm.PERM_LOGGEDIN) %} + {% if handler.user.hasPriv(PRIV.PRIV_USER_PROFILE) %} {% if psdict[pid] and psdict[pid]['rid'] %} {% set rdoc = rdict[psdict[pid]['rid']] %} {% if handler.canShowRecord(tdoc) %} diff --git a/templates/homework_main.html b/templates/homework_main.html index 777abdc1f..50ec92ec7 100644 --- a/templates/homework_main.html +++ b/templates/homework_main.html @@ -18,7 +18,7 @@ - {% if handler.hasPerm(perm.PERM_CREATE_HOMEWORK) %} + {% if handler.user.hasPerm(perm.PERM_CREATE_HOMEWORK) %} {{ _('Create Homework') }} {% endif %} diff --git a/templates/main.html b/templates/main.html index b985adea3..e7d50b02b 100644 --- a/templates/main.html +++ b/templates/main.html @@ -135,7 +135,7 @@ {{ _('Completed') }} 100% {% endif %} - {% elif handler.hasPerm(perm.PERM_LOGGEDIN) %} + {% elif handler.user.hasPriv(PRIV.PRIV_USER_PROFILE) %} {{ _('Not Enrolled') }} {% endif %} @@ -157,7 +157,7 @@ {% endif %}
      - {% if handler.hasPerm(perm.PERM_VIEW_DISCUSSION) %} + {% if handler.user.hasPerm(perm.PERM_VIEW_DISCUSSION) %} {% include 'partials/discussion_nodes_widget.html' %} {% endif %}
      diff --git a/templates/partials/contest_sidebar.html b/templates/partials/contest_sidebar.html index e374d7add..4623c9660 100644 --- a/templates/partials/contest_sidebar.html +++ b/templates/partials/contest_sidebar.html @@ -24,7 +24,7 @@ {% endif %} {% if not attended and not model.contest.isDone(tdoc) %} {% endif %} - {% if user._id == tdoc.owner or handler.hasPerm(perm.PERM_EDIT_CONTEST) %} + {% if user._id == tdoc.owner or handler.user.hasPerm(perm.PERM_EDIT_CONTEST) %} {% endif %} - {% if handler.hasPerm(perm.PERM_READ_RECORD_CODE) %} + {% if handler.user.hasPerm(perm.PERM_READ_RECORD_CODE) %} diff --git a/templates/partials/discussion_edit_form.html b/templates/partials/discussion_edit_form.html index b1123bdab..f3c1c68e8 100644 --- a/templates/partials/discussion_edit_form.html +++ b/templates/partials/discussion_edit_form.html @@ -1,5 +1,5 @@ {% import "components/form.html" as form with context %} -{% if ddoc.highlight or handler.hasPerm(perm.PERM_HIGHLIGHT_DISCUSSION) %} +{% if ddoc.highlight or handler.user.hasPerm(perm.PERM_HIGHLIGHT_DISCUSSION) %} {{ form.form_checkbox({ label:'Highlight', name:'highlight', diff --git a/templates/partials/homework_sidebar.html b/templates/partials/homework_sidebar.html index 889c447ed..8347d2194 100644 --- a/templates/partials/homework_sidebar.html +++ b/templates/partials/homework_sidebar.html @@ -24,7 +24,7 @@ {% endif %} {% if not attended and not model.contest.isDone(tdoc) %} {% endif %} - {% if tdoc.owner == handler.user._id or handler.hasPerm(perm.PERM_EDIT_HOMEWORK) %} + {% if tdoc.owner == handler.user._id or handler.user.hasPerm(perm.PERM_EDIT_HOMEWORK) %} {% endif %} - {% if handler.hasPerm(perm.PERM_READ_RECORD_CODE) %} + {% if handler.user.hasPerm(perm.PERM_READ_RECORD_CODE) %} diff --git a/templates/partials/nav.html b/templates/partials/nav.html index c6773eb25..0488d4682 100644 --- a/templates/partials/nav.html +++ b/templates/partials/nav.html @@ -17,7 +17,7 @@ {% endfor %}
      {{ _('Status') }} {{ _('Last Submit At') }}
      - {% if handler.hasPerm(perm.PERM_LOGGEDIN) %} + {% if handler.user.hasPriv(PRIV.PRIV_USER_PROFILE) %} {% endif %} @@ -18,7 +18,7 @@ - {% if handler.hasPerm(perm.PERM_LOGGEDIN) %} + {% if handler.user.hasPriv(PRIV.PRIV_USER_PROFILE) %} {% endif %} @@ -31,7 +31,7 @@ {% for pdoc in pdocs %} {% set psdoc = psdict[pdoc['docId']] %} - {% if handler.hasPerm(perm.PERM_LOGGEDIN) %} + {% if handler.user.hasPriv(PRIV.PRIV_USER_PROFILE) %} {% if psdoc['rid'] %} {{ record.render_status_td(psdoc, rid_key='rid') }} {% else %} diff --git a/templates/partials/problem_sidebar_contest.html b/templates/partials/problem_sidebar_contest.html index 09891cd3c..b3f5c253b 100644 --- a/templates/partials/problem_sidebar_contest.html +++ b/templates/partials/problem_sidebar_contest.html @@ -28,7 +28,7 @@ {{ _('Open in Problem Set') }} {% endif %} - {% if pdoc.owner == handler.user._id or handler.hasPerm(perm.PERM_EDIT_PROBLEM) %} + {% if pdoc.owner == handler.user._id or handler.user.hasPerm(perm.PERM_EDIT_PROBLEM) %} {% endif %} - {% if pdoc.owner == handler.user._id or handler.hasPerm(perm.PERM_EDIT_PROBLEM) %} + {% if pdoc.owner == handler.user._id or handler.user.hasPerm(perm.PERM_EDIT_PROBLEM) %} - + + {% endfor %} diff --git a/templates/record_detail_summary.html b/templates/record_detail_summary.html index 65cff0fc8..507749c25 100644 --- a/templates/record_detail_summary.html +++ b/templates/record_detail_summary.html @@ -4,7 +4,7 @@
      {{ rdoc['score'] }}
      {% endif %}
      {{ _('Total Time') }}
      -
      {% if rdoc['status'] == status.STATUS_TIME_LIMIT_EXCEEDED or rdoc['status'] == status.STATUS_MEMORY_LIMIT_EXCEEDED or rdoc['status'] == status.STATUS_OUTPUT_LIMIT_EXCEEDED %}≥{% endif %}{{ rdoc.time }}ms
      +
      {% if rdoc['status'] == STATUS.STATUS_TIME_LIMIT_EXCEEDED or rdoc['status'] == STATUS.STATUS_MEMORY_LIMIT_EXCEEDED or rdoc['status'] == STATUS.STATUS_OUTPUT_LIMIT_EXCEEDED %}≥{% endif %}{{ rdoc.time }}ms
      {{ _('Peak Memory') }}
      -
      {% if rdoc['status'] == status.STATUS_TIME_LIMIT_EXCEEDED or rdoc['status'] == status.STATUS_MEMORY_LIMIT_EXCEEDED or rdoc['status'] == status.STATUS_OUTPUT_LIMIT_EXCEEDED %}≥{% endif %}{{ size(rdoc.memory, 1024) }}
      +
      {% if rdoc['status'] == STATUS.STATUS_TIME_LIMIT_EXCEEDED or rdoc['status'] == STATUS.STATUS_MEMORY_LIMIT_EXCEEDED or rdoc['status'] == STATUS.STATUS_OUTPUT_LIMIT_EXCEEDED %}≥{% endif %}{{ size(rdoc.memory, 1024) }}
      diff --git a/templates/record_main.html b/templates/record_main.html index e92f26d88..27135eb1d 100644 --- a/templates/record_main.html +++ b/templates/record_main.html @@ -82,7 +82,7 @@ {% endfor %}
      {{ _('Status') }}{{ _('Problem') }}
      {% if rcdoc['status'] == status.STATUS_TIME_LIMIT_EXCEEDED or rcdoc['status'] == status.STATUS_MEMORY_LIMIT_EXCEEDED or rcdoc['status'] == status.STATUS_OUTPUT_LIMIT_EXCEEDED %}≥{% endif %}{{ rcdoc.time }}ms{% if rcdoc['status'] == status.STATUS_TIME_LIMIT_EXCEEDED or rcdoc['status'] == status.STATUS_MEMORY_LIMIT_EXCEEDED or rcdoc['status'] == status.STATUS_OUTPUT_LIMIT_EXCEEDED %}≥{% endif %}{{ size(rcdoc.memory, 1024) }}{% if rcdoc['status'] == STATUS.STATUS_TIME_LIMIT_EXCEEDED or rcdoc['status'] == STATUS.STATUS_MEMORY_LIMIT_EXCEEDED or rcdoc['status'] == STATUS.STATUS_OUTPUT_LIMIT_EXCEEDED %}≥{% endif %}{{ rcdoc.time }}ms{% if rcdoc['status'] == STATUS.STATUS_TIME_LIMIT_EXCEEDED or rcdoc['status'] == STATUS.STATUS_MEMORY_LIMIT_EXCEEDED or rcdoc['status'] == STATUS.STATUS_OUTPUT_LIMIT_EXCEEDED %}≥{% endif %}{{ size(rcdoc.memory, 1024) }}
      - {% if handler.hasPerm(perm.PERM_VIEW_JUDGE_STATISTICS) %} + {% if handler.user.hasPerm(perm.PERM_VIEW_JUDGE_STATISTICS) %}
      • diff --git a/templates/record_main_tr.html b/templates/record_main_tr.html index 74dcf9816..80d94589b 100644 --- a/templates/record_main_tr.html +++ b/templates/record_main_tr.html @@ -3,7 +3,7 @@ {{ record.render_status_td(rdoc) }} - {% if handler.hasPerm(perm.PERM_REJUDGE) %} + {% if handler.user.hasPerm(perm.PERM_REJUDGE) %}
        |
        {% endif %} - {% if pdoc and (not pdoc.hidden or handler.hasPerm(perm.PERM_VIEW_PROBLEM_HIDDEN)) %} + {% if pdoc and (not pdoc.hidden or handler.user.hasPerm(perm.PERM_VIEW_PROBLEM_HIDDEN)) %} {{ problem.render_problem_title(pdoc, show_tags=false) }} {% else %} * @@ -23,8 +23,8 @@ {{ udoc['uname'] }} - {% if rdoc['status'] == status.STATUS_TIME_LIMIT_EXCEEDED or rdoc['status'] == status.STATUS_MEMORY_LIMIT_EXCEEDED or rdoc['status'] == status.STATUS_OUTPUT_LIMIT_EXCEEDED %}≥{% endif %}{{ rdoc.time }}ms - {% if rdoc['status'] == status.STATUS_TIME_LIMIT_EXCEEDED or rdoc['status'] == status.STATUS_MEMORY_LIMIT_EXCEEDED or rdoc['status'] == status.STATUS_OUTPUT_LIMIT_EXCEEDED %}≥{% endif %}{{ size(rdoc.memory, 1024) }} + {% if rdoc['status'] == STATUS.STATUS_TIME_LIMIT_EXCEEDED or rdoc['status'] == STATUS.STATUS_MEMORY_LIMIT_EXCEEDED or rdoc['status'] == STATUS.STATUS_OUTPUT_LIMIT_EXCEEDED %}≥{% endif %}{{ rdoc.time }}ms + {% if rdoc['status'] == STATUS.STATUS_TIME_LIMIT_EXCEEDED or rdoc['status'] == STATUS.STATUS_MEMORY_LIMIT_EXCEEDED or rdoc['status'] == STATUS.STATUS_OUTPUT_LIMIT_EXCEEDED %}≥{% endif %}{{ size(rdoc.memory, 1024) }} {{ model.builtin.LANG_TEXTS[rdoc['lang']] }} {{ datetimeSpan(rdoc['_id'])|safe }} diff --git a/templates/training_detail.html b/templates/training_detail.html index 390fe122a..3082e7c6f 100644 --- a/templates/training_detail.html +++ b/templates/training_detail.html @@ -79,7 +79,7 @@
        - {% if handler.hasPerm(perm.PERM_LOGGEDIN) %} + {% if handler.user.hasPriv(PRIV.PRIV_USER_PROFILE) %} {% endif %} @@ -89,7 +89,7 @@ - {% if handler.hasPerm(perm.PERM_LOGGEDIN) %} + {% if handler.user.hasPriv(PRIV.PRIV_USER_PROFILE) %} {% endif %} @@ -104,7 +104,7 @@ {% set pdoc=pdict[pid] %} {% set psdoc=psdict[pid] %} - {% if handler.hasPerm(perm.PERM_LOGGEDIN) %} + {% if handler.user.hasPriv(PRIV.PRIV_USER_PROFILE) %} {% if psdoc['rid'] %} {{ record.render_status_td(psdoc, rid_key='rid') }} {% else %} @@ -112,7 +112,7 @@ {% endif %} {% endif %}
        {{ _('Status') }}{{ _('Problem') }}
        - {% if handler.hasPerm(perm.PERM_LOGGEDIN) %} + {% if handler.user.hasPriv(PRIV.PRIV_USER_PROFILE) %}
        @@ -160,7 +160,7 @@
        - {% if handler.hasPerm(perm.PERM_LOGGEDIN) %} + {% if handler.user.hasPriv(PRIV.PRIV_USER_PROFILE) %}
        {{ _('Status') }}
        {% if tsdoc['enroll'] %}{{ _('Completed' if tsdoc['done'] else 'In Progress') }}{% else %}{{ _('Not Enrolled') }}{% endif %}
        {% endif %} {% if tsdoc['enroll'] %} diff --git a/templates/training_main.html b/templates/training_main.html index fdc724f40..932481f94 100644 --- a/templates/training_main.html +++ b/templates/training_main.html @@ -37,7 +37,7 @@ {{ _('Completed') }} 100% {% endif %} - {% elif handler.hasPerm(perm.PERM_LOGGEDIN) %} + {% elif handler.user.hasPriv(PRIV.PRIV_USER_PROFILE) %} {{ _('Not Enrolled') }} {% endif %} @@ -52,7 +52,7 @@
        - {% if handler.hasPerm(perm.PERM_LOGGEDIN) %} + {% if handler.user.hasPriv(PRIV.PRIV_USER_PROFILE) %}

        {{ _('Enrolled') }}

        @@ -76,7 +76,7 @@
        {% endif %} - {% if handler.hasPerm(perm.PERM_CREATE_TRAINING) %} + {% if handler.user.hasPerm(perm.PERM_CREATE_TRAINING) %}

        {{ _('Create Training Plan') }}

        diff --git a/templates/user_detail.html b/templates/user_detail.html index 24eeca521..499b8f769 100644 --- a/templates/user_detail.html +++ b/templates/user_detail.html @@ -62,7 +62,7 @@ {{ model.builtin.USER_GENDER_ICONS[udoc.gender] }} {% endif %} - {% if handler.hasPerm(perm.PERM_MOD_BADGE) %} + {% if handler.user.hasPerm(perm.PERM_MOD_BADGE) %}