From da32c79f21264255b1d47fa1297b9396910a6b01 Mon Sep 17 00:00:00 2001 From: undefined Date: Sat, 1 May 2021 12:14:11 +0800 Subject: [PATCH] core: add maintainer field for doc (#101) --- packages/hydrooj/src/handler/contest.ts | 10 +++---- packages/hydrooj/src/handler/discussion.ts | 16 +++++----- packages/hydrooj/src/handler/homework.ts | 6 ++-- packages/hydrooj/src/handler/problem.ts | 30 +++++++++---------- packages/hydrooj/src/handler/record.ts | 2 +- packages/hydrooj/src/handler/remote.ts | 4 +-- packages/hydrooj/src/handler/training.ts | 4 +-- packages/hydrooj/src/interface.ts | 12 ++++---- packages/hydrooj/src/model/user.ts | 19 ++++++++---- .../components/comments_discussion.html | 8 ++--- .../components/comments_solution.html | 8 ++--- .../ui-default/templates/contest_edit.html | 2 +- .../templates/discussion_detail.html | 6 ++-- .../ui-default/templates/discussion_edit.html | 2 +- .../ui-default/templates/homework_edit.html | 2 +- .../templates/partials/contest_sidebar.html | 2 +- .../templates/partials/homework_sidebar.html | 2 +- .../partials/problem_sidebar_contest.html | 2 +- .../partials/problem_sidebar_homework.html | 2 +- .../partials/problem_sidebar_normal.html | 4 +-- .../ui-default/templates/problem_edit.html | 2 +- .../ui-default/templates/problem_files.html | 10 +++---- .../ui-default/templates/training_detail.html | 2 +- .../ui-default/templates/training_edit.html | 2 +- 24 files changed, 85 insertions(+), 74 deletions(-) diff --git a/packages/hydrooj/src/handler/contest.ts b/packages/hydrooj/src/handler/contest.ts index 8a0be0c9e..6f35e5f4d 100644 --- a/packages/hydrooj/src/handler/contest.ts +++ b/packages/hydrooj/src/handler/contest.ts @@ -100,7 +100,7 @@ class ContestDetailHandler extends ContestHandler { @param('tid', Types.ObjectID) async postDelete(domainId: string, tid: ObjectID) { const tdoc = await contest.get(domainId, tid); - if (tdoc.owner !== this.user._id) this.checkPerm(PERM.PERM_EDIT_CONTEST); + if (!this.user.own(tdoc)) this.checkPerm(PERM.PERM_EDIT_CONTEST); await contest.del(domainId, tid); this.response.redirect = this.url('contest_main'); } @@ -110,7 +110,7 @@ class ContestBoardcastHandler extends ContestHandler { @param('tid', Types.ObjectID) async get(domainId: string, tid: ObjectID) { const tdoc = await contest.get(domainId, tid); - if (tdoc.owner !== this.user._id) throw new PermissionError('Boardcast Message'); + if (!this.user.own(tdoc)) throw new PermissionError('Boardcast Message'); const path = [ ['Hydro', 'homepage'], ['contest_main', 'contest_main'], @@ -125,7 +125,7 @@ class ContestBoardcastHandler extends ContestHandler { @param('content', Types.Content) async post(domainId: string, tid: ObjectID, content: string) { const tdoc = await contest.get(domainId, tid); - if (tdoc.owner !== this.user._id) throw new PermissionError('Boardcast Message'); + if (!this.user.own(tdoc)) throw new PermissionError('Boardcast Message'); const tsdocs = await contest.getMultiStatus(domainId, { docId: tid }).toArray(); const uids: number[] = Array.from(new Set(tsdocs.map((tsdoc) => tsdoc.uid))); await Promise.all( @@ -176,7 +176,7 @@ class ContestEditHandler extends ContestHandler { @param('tid', Types.ObjectID) async prepare(domainId: string, tid: ObjectID) { this.tdoc = await contest.get(domainId, tid); - if (this.tdoc.owner !== this.user._id) this.checkPerm(PERM.PERM_EDIT_CONTEST); + if (!this.user.own(this.tdoc)) this.checkPerm(PERM.PERM_EDIT_CONTEST); else this.checkPerm(PERM.PERM_EDIT_CONTEST_SELF); } @@ -294,7 +294,7 @@ export class ContestProblemFileDownloadHandler extends ContestProblemHandler { @param('filename', Types.Name) @param('noDisposition', Types.Boolean) async get(domainId: string, type = 'additional_file', filename: string, noDisposition = false) { - if (type === 'testdata' && this.user._id !== this.pdoc.owner) this.checkPerm(PERM.PERM_READ_PROBLEM_DATA); + if (type === 'testdata' && !this.user.own(this.pdoc)) this.checkPerm(PERM.PERM_READ_PROBLEM_DATA); this.response.redirect = await storage.signDownloadLink( `problem/${this.pdoc.domainId}/${this.pdoc.docId}/${type}/${filename}`, noDisposition ? undefined : filename, false, 'user', diff --git a/packages/hydrooj/src/handler/discussion.ts b/packages/hydrooj/src/handler/discussion.ts index af381ac49..3ee7ac88e 100644 --- a/packages/hydrooj/src/handler/discussion.ts +++ b/packages/hydrooj/src/handler/discussion.ts @@ -230,7 +230,7 @@ class DiscussionDetailHandler extends DiscussionHandler { await this.limitRate('add_discussion', 3600, 60); await discussion.addTailReply(domainId, drid, this.user._id, content, this.request.ip); const str = JSON.stringify({ message: '{0} replied to discussion reply {1}/{2}.', params: [this.user._id, domainId, drid] }); - if (this.drdoc.owner !== this.user._id) await message.send(1, this.drdoc.owner, str); + if (!this.user.own(this.drdoc)) await message.send(1, this.drdoc.owner, str); this.back(); } @@ -238,7 +238,7 @@ class DiscussionDetailHandler extends DiscussionHandler { @param('content', Types.Content) async postEditReply(domainId: string, drid: ObjectID, content: string) { this.checkPerm(PERM.PERM_EDIT_DISCUSSION_REPLY_SELF); - if (this.drdoc.owner !== this.user._id) throw new PermissionError(PERM.PERM_EDIT_DISCUSSION_REPLY_SELF); + if (!this.user.own(this.drdoc)) throw new PermissionError(PERM.PERM_EDIT_DISCUSSION_REPLY_SELF); await discussion.editReply(domainId, drid, content); await oplog.add({ ...this.drdoc, operator: this.user._id, operateIp: this.request.ip, type: 'edit', @@ -248,9 +248,9 @@ class DiscussionDetailHandler extends DiscussionHandler { @param('drid', Types.ObjectID) async postDeleteReply(domainId: string, drid: ObjectID) { - if (!(this.ddoc.owner === this.user._id + if (!(this.user.own(this.ddoc) && this.user.hasPerm(PERM.PERM_DELETE_DISCUSSION_REPLY_SELF_DISCUSSION))) { - if (this.drdoc.owner !== this.user._id) { + if (!this.user.own(this.drdoc)) { this.checkPerm(PERM.PERM_DELETE_DISCUSSION_REPLY); } else this.checkPerm(PERM.PERM_DELETE_DISCUSSION_SELF); } @@ -266,7 +266,7 @@ class DiscussionDetailHandler extends DiscussionHandler { @param('content', Types.Content) async postEditTailReply(domainId: string, drid: ObjectID, drrid: ObjectID, content: string) { this.checkPerm(PERM.PERM_EDIT_DISCUSSION_REPLY_SELF); - if (this.drrdoc.owner !== this.user._id) throw new PermissionError(PERM.PERM_EDIT_DISCUSSION_REPLY_SELF); + if (!this.user.own(this.drrdoc)) throw new PermissionError(PERM.PERM_EDIT_DISCUSSION_REPLY_SELF); await discussion.editTailReply(domainId, drid, drrid, content); // TODO: history? await oplog.add({ @@ -278,7 +278,7 @@ class DiscussionDetailHandler extends DiscussionHandler { @param('drid', Types.ObjectID) @param('drrid', Types.ObjectID) async postDeleteTailReply(domainId: string, drid: ObjectID, drrid: ObjectID) { - if (this.drrdoc.owner !== this.user._id) this.checkPerm(PERM.PERM_DELETE_DISCUSSION_REPLY); + if (!this.user.own(this.drrdoc)) this.checkPerm(PERM.PERM_DELETE_DISCUSSION_REPLY); await discussion.delTailReply(domainId, drid, drrid); await oplog.add({ ...this.drrdoc, operator: this.user._id, operateIp: this.request.ip, type: 'delete', @@ -342,7 +342,7 @@ class DiscussionEditHandler extends DiscussionHandler { domainId: string, did: ObjectID, title: string, content: string, highlight = false, pin = false, ) { - if (this.ddoc.owner !== this.user._id) this.checkPerm(PERM.PERM_EDIT_DISCUSSION); + if (!this.user.own(this.ddoc)) this.checkPerm(PERM.PERM_EDIT_DISCUSSION); else this.checkPerm(PERM.PERM_EDIT_DISCUSSION_SELF); if (!this.user.hasPerm(PERM.PERM_HIGHLIGHT_DISCUSSION)) highlight = this.ddoc.highlight; if (!this.user.hasPerm(PERM.PERM_PIN_DISCUSSION)) pin = this.ddoc.pin; @@ -356,7 +356,7 @@ class DiscussionEditHandler extends DiscussionHandler { @param('did', Types.ObjectID) async postDelete(domainId: string, did: ObjectID) { - if (this.ddoc.owner !== this.user._id) this.checkPerm(PERM.PERM_DELETE_DISCUSSION); + if (!this.user.own(this.ddoc)) this.checkPerm(PERM.PERM_DELETE_DISCUSSION); else this.checkPerm(PERM.PERM_DELETE_DISCUSSION_SELF); await discussion.del(domainId, did); await oplog.add({ diff --git a/packages/hydrooj/src/handler/homework.ts b/packages/hydrooj/src/handler/homework.ts index 9fa314fe0..394977dbc 100644 --- a/packages/hydrooj/src/handler/homework.ts +++ b/packages/hydrooj/src/handler/homework.ts @@ -112,7 +112,7 @@ class HomeworkDetailHandler extends HomeworkHandler { @param('tid', Types.ObjectID) async postDelete(domainId: string, tid: ObjectID) { const tdoc = await contest.get(domainId, tid, document.TYPE_HOMEWORK); - if (tdoc.owner !== this.user._id) this.checkPerm(PERM.PERM_EDIT_HOMEWORK); + if (!this.user.own(tdoc)) this.checkPerm(PERM.PERM_EDIT_HOMEWORK); await contest.del(domainId, tid, document.TYPE_HOMEWORK); this.response.redirect = this.url('homework_main'); } @@ -268,7 +268,7 @@ class HomeworkEditHandler extends HomeworkHandler { @param('tid', Types.ObjectID) async get(domainId: string, tid: ObjectID) { const tdoc = await contest.get(domainId, tid, document.TYPE_HOMEWORK); - if (tdoc.owner !== this.user._id) this.checkPerm(PERM.PERM_EDIT_HOMEWORK); + if (!this.user.own(tdoc)) this.checkPerm(PERM.PERM_EDIT_HOMEWORK); else this.checkPerm(PERM.PERM_EDIT_HOMEWORK_SELF); const extensionDays = Math.round( (tdoc.endAt.getTime() - tdoc.penaltySince.getTime()) / 36000 / 24, @@ -316,7 +316,7 @@ class HomeworkEditHandler extends HomeworkHandler { return i; }); const tdoc = await contest.get(domainId, tid, document.TYPE_HOMEWORK); - if (tdoc.owner !== this.user._id) this.checkPerm(PERM.PERM_EDIT_HOMEWORK); + if (!this.user.own(this.tdoc)) this.checkPerm(PERM.PERM_EDIT_HOMEWORK); else this.checkPerm(PERM.PERM_EDIT_HOMEWORK_SELF); let beginAt; let penaltySince; diff --git a/packages/hydrooj/src/handler/problem.ts b/packages/hydrooj/src/handler/problem.ts index 3d2fcef74..0d412e098 100644 --- a/packages/hydrooj/src/handler/problem.ts +++ b/packages/hydrooj/src/handler/problem.ts @@ -124,7 +124,7 @@ export class ProblemMainHandler extends ProblemHandler { for (const pid of pids) { // eslint-disable-next-line no-await-in-loop const pdoc = await problem.get(domainId, pid); - if (pdoc.owner !== this.user._id) this.checkPerm(PERM.PERM_EDIT_PROBLEM); + if (!this.user.own(pdoc)) this.checkPerm(PERM.PERM_EDIT_PROBLEM); // eslint-disable-next-line no-await-in-loop await problem.del(domainId, pid); } @@ -136,7 +136,7 @@ export class ProblemMainHandler extends ProblemHandler { for (const pid of pids) { // eslint-disable-next-line no-await-in-loop const pdoc = await problem.get(domainId, pid); - if (pdoc.owner !== this.user._id) this.checkPerm(PERM.PERM_EDIT_PROBLEM); + if (!this.user.own(pdoc)) this.checkPerm(PERM.PERM_EDIT_PROBLEM); // eslint-disable-next-line no-await-in-loop await problem.edit(domainId, pid, { hidden: true }); } @@ -148,7 +148,7 @@ export class ProblemMainHandler extends ProblemHandler { for (const pid of pids) { // eslint-disable-next-line no-await-in-loop const pdoc = await problem.get(domainId, pid); - if (pdoc.owner !== this.user._id) this.checkPerm(PERM.PERM_EDIT_PROBLEM); + if (!this.user.own(pdoc)) this.checkPerm(PERM.PERM_EDIT_PROBLEM); // eslint-disable-next-line no-await-in-loop await problem.edit(domainId, pid, { hidden: false }); } @@ -183,7 +183,7 @@ export class ProblemDetailHandler extends ProblemHandler { this.response.template = 'problem_detail.html'; this.pdoc = await problem.get(domainId, pid, this.user._id); if (!this.pdoc) throw new ProblemNotFoundError(domainId, pid); - if (this.pdoc.hidden && this.pdoc.owner !== this.user._id) { + if (this.pdoc.hidden && !this.user.own(this.pdoc)) { this.checkPerm(PERM.PERM_VIEW_PROBLEM_HIDDEN); } await bus.serial('problem/get', this.pdoc, this); @@ -230,7 +230,7 @@ export class ProblemDetailHandler extends ProblemHandler { } async postDelete() { - if (this.pdoc.owner !== this.user._id) this.checkPerm(PERM.PERM_EDIT_PROBLEM); + if (!this.user.own(this.pdoc)) this.checkPerm(PERM.PERM_EDIT_PROBLEM); await problem.del(this.pdoc.domainId, this.pdoc.docId); this.response.redirect = this.url('problem_main'); } @@ -338,7 +338,7 @@ export class ProblemStatisticsHandler extends ProblemDetailHandler { export class ProblemManageHandler extends ProblemDetailHandler { async prepare() { - if (this.pdoc.owner !== this.user._id) this.checkPerm(PERM.PERM_EDIT_PROBLEM); + if (!this.user.own(this.pdoc)) this.checkPerm(PERM.PERM_EDIT_PROBLEM); else this.checkPerm(PERM.PERM_EDIT_PROBLEM_SELF); } } @@ -379,7 +379,7 @@ export class ProblemFilesHandler extends ProblemDetailHandler { @param('testdata', Types.Boolean) @param('additional_file', Types.Boolean) async get(domainId: string, getTestdata = true, getAdditionalFile = true) { - const canReadData = this.user._id === this.pdoc.owner || this.user.hasPerm(PERM.PERM_READ_PROBLEM_DATA); + const canReadData = !this.user.own(this.pdoc) || this.user.hasPerm(PERM.PERM_READ_PROBLEM_DATA); this.response.body.testdata = (getTestdata && canReadData) ? this.pdoc.data : []; this.response.body.additional_file = (getAdditionalFile ? this.pdoc.additional_file : []); this.response.template = 'problem_files.html'; @@ -388,7 +388,7 @@ export class ProblemFilesHandler extends ProblemDetailHandler { @post('files', Types.Set) @post('type', Types.Range(['testdata', 'additional_file']), true) async postGetLinks(domainId: string, files: Set, type = 'testdata') { - if (type === 'testdata' && this.user._id !== this.pdoc.owner) { + if (type === 'testdata' && !this.user.own(this.pdoc)) { this.checkPerm(PERM.PERM_READ_PROBLEM_DATA); } const links = {}; @@ -408,7 +408,7 @@ export class ProblemFilesHandler extends ProblemDetailHandler { if (!this.request.files.file) throw new ValidationError('file'); if (!filename) filename = this.request.files.file.name || String.random(16); if (filename.includes('/')) throw new ValidationError('filename', 'Bad filename'); - if (this.pdoc.owner !== this.user._id) this.checkPerm(PERM.PERM_EDIT_PROBLEM); + if (!this.user.own(this.pdoc)) this.checkPerm(PERM.PERM_EDIT_PROBLEM); if (filename.endsWith('.zip')) { const zip = new AdmZip(this.request.files.file.path); const entries = zip.getEntries(); @@ -433,7 +433,7 @@ export class ProblemFilesHandler extends ProblemDetailHandler { @post('files', Types.Array) @post('type', Types.Range(['testdata', 'additional_file']), true) async postDeleteFiles(domainId: string, files: string[], type = 'testdata') { - if (this.pdoc.owner !== this.user._id) this.checkPerm(PERM.PERM_EDIT_PROBLEM); + if (!this.user.own(this.pdoc)) this.checkPerm(PERM.PERM_EDIT_PROBLEM); if (type === 'testdata') await problem.delTestdata(domainId, this.pdoc.docId, files); else await problem.delAdditionalFile(domainId, this.pdoc.docId, files); this.back(); @@ -445,7 +445,7 @@ export class ProblemFileDownloadHandler extends ProblemDetailHandler { @param('filename', Types.Name) @param('noDisposition', Types.Boolean) async get(domainId: string, type = 'additional_file', filename: string, noDisposition = false) { - if (type === 'testdata' && this.user._id !== this.pdoc.owner) this.checkPerm(PERM.PERM_READ_PROBLEM_DATA); + if (type === 'testdata' && !this.user.own(this.pdoc)) this.checkPerm(PERM.PERM_READ_PROBLEM_DATA); this.response.redirect = await storage.signDownloadLink( `problem/${this.pdoc.domainId}/${this.pdoc.docId}/${type}/${filename}`, noDisposition ? undefined : filename, false, 'user', @@ -496,7 +496,7 @@ export class ProblemSolutionHandler extends ProblemDetailHandler { @param('psid', Types.ObjectID) async postEditSolution(domainId: string, content: string, psid: ObjectID) { let psdoc = await solution.get(domainId, psid); - if (psdoc.owner !== this.user._id) this.checkPerm(PERM.PERM_EDIT_PROBLEM_SOLUTION); + if (!this.user.own(psdoc)) this.checkPerm(PERM.PERM_EDIT_PROBLEM_SOLUTION); else this.checkPerm(PERM.PERM_EDIT_PROBLEM_SOLUTION_SELF); psdoc = await solution.edit(domainId, psdoc.docId, content); this.back({ psdoc }); @@ -505,7 +505,7 @@ export class ProblemSolutionHandler extends ProblemDetailHandler { @param('psid', Types.ObjectID) async postDeleteSolution(domainId: string, psid: ObjectID) { const psdoc = await solution.get(domainId, psid); - if (psdoc.owner !== this.user._id) this.checkPerm(PERM.PERM_DELETE_PROBLEM_SOLUTION); + if (!this.user.own(psdoc)) this.checkPerm(PERM.PERM_DELETE_PROBLEM_SOLUTION); else this.checkPerm(PERM.PERM_DELETE_PROBLEM_SOLUTION_SELF); await solution.del(domainId, psdoc.docId); this.back(); @@ -526,7 +526,7 @@ export class ProblemSolutionHandler extends ProblemDetailHandler { async postEditReply(domainId: string, psid: ObjectID, psrid: ObjectID, content: string) { const [psdoc, psrdoc] = await solution.getReply(domainId, psid, psrid); if ((!psdoc) || psdoc.parentId !== this.pdoc.docId) throw new SolutionNotFoundError(domainId, psid); - if (!(psrdoc.owner === this.user._id + if (!(!this.user.own(psrdoc) && this.user.hasPerm(PERM.PERM_EDIT_PROBLEM_SOLUTION_REPLY_SELF))) { throw new PermissionError(PERM.PERM_EDIT_PROBLEM_SOLUTION_REPLY_SELF); } @@ -539,7 +539,7 @@ export class ProblemSolutionHandler extends ProblemDetailHandler { async postDeleteReply(domainId: string, psid: ObjectID, psrid: ObjectID) { const [psdoc, psrdoc] = await solution.getReply(domainId, psid, psrid); if ((!psdoc) || psdoc.parentId !== this.pdoc.docId) throw new SolutionNotFoundError(psid); - if (!(psrdoc.owner === this.user._id + if (!(!this.user.own(psrdoc) && this.user.hasPerm(PERM.PERM_DELETE_PROBLEM_SOLUTION_REPLY_SELF))) { this.checkPerm(PERM.PERM_DELETE_PROBLEM_SOLUTION_REPLY); } diff --git a/packages/hydrooj/src/handler/record.ts b/packages/hydrooj/src/handler/record.ts index c4db31f60..020dda153 100644 --- a/packages/hydrooj/src/handler/record.ts +++ b/packages/hydrooj/src/handler/record.ts @@ -99,7 +99,7 @@ class RecordDetailHandler extends RecordHandler { if (!(pdoc && this.user.hasPerm(PERM.PERM_VIEW_PROBLEM))) { pdoc = problem.create(pdoc?.docId || 0, pdoc?.pid || '*'); } - if (!rdoc.contest && pdoc.hidden && pdoc.owner !== this.user._id) { + if (!rdoc.contest && pdoc.hidden && !this.user.own(pdoc)) { if (!this.user.hasPerm(PERM.PERM_VIEW_PROBLEM_HIDDEN)) { pdoc = problem.create(pdoc.docId, pdoc.pid); } diff --git a/packages/hydrooj/src/handler/remote.ts b/packages/hydrooj/src/handler/remote.ts index ad521437b..0d08e5d0f 100644 --- a/packages/hydrooj/src/handler/remote.ts +++ b/packages/hydrooj/src/handler/remote.ts @@ -34,10 +34,10 @@ export class ProblemSendHandler extends Handler { const pids = new Set(); for (const key in pdocs) { pids.add(pdocs[key].docId); - if (pdocs[key].hidden && !getHidden && pdocs[key].owner !== this.user._id) { + if (pdocs[key].hidden && !getHidden && !this.user.own(pdocs[key])) { throw new PermissionError(PERM.PERM_VIEW_PROBLEM_HIDDEN); } - if (!getData && pdocs[key].owner !== this.user._id) { + if (!getData && !this.user.own(pdocs[key])) { throw new PermissionError(PERM.PERM_READ_PROBLEM_DATA); } } diff --git a/packages/hydrooj/src/handler/training.ts b/packages/hydrooj/src/handler/training.ts index 9b0b01b2b..2affa3ae9 100644 --- a/packages/hydrooj/src/handler/training.ts +++ b/packages/hydrooj/src/handler/training.ts @@ -161,7 +161,7 @@ class TrainingDetailHandler extends Handler { @param('tid', Types.ObjectID) async postDelete(domainId: string, tid: ObjectID) { const tdoc = await training.get(domainId, tid); - if (tdoc.owner !== this.user._id) this.checkPerm(PERM.PERM_EDIT_TRAINING); + if (!this.user.own(tdoc)) this.checkPerm(PERM.PERM_EDIT_TRAINING); await training.del(domainId, tid); this.response.redirect = this.url('training_main'); } @@ -205,7 +205,7 @@ class TrainingEditHandler extends Handler { @param('tid', Types.ObjectID) async prepare(domainId: string, tid: ObjectID) { this.tdoc = await training.get(domainId, tid); - if (this.tdoc.owner !== this.user._id) this.checkPerm(PERM.PERM_EDIT_TRAINING); + if (!this.user.own(this.tdoc)) this.checkPerm(PERM.PERM_EDIT_TRAINING); else this.checkPerm(PERM.PERM_EDIT_TRAINING_SELF); } diff --git a/packages/hydrooj/src/interface.ts b/packages/hydrooj/src/interface.ts index 3105ac2bb..ef4d5c252 100644 --- a/packages/hydrooj/src/interface.ts +++ b/packages/hydrooj/src/interface.ts @@ -89,6 +89,7 @@ export interface User extends Record { perm: bigint, scope: bigint, role: string, + own(doc: T): boolean; hasPerm: (...perm: bigint[]) => boolean, hasPriv: (...priv: number[]) => boolean, checkPassword: (password: string) => void, @@ -171,11 +172,12 @@ export type ContentNode = PlainContentNode | TextContentNode | SampleContentNode export type Content = string | ContentNode[] | Record; export interface Document { - _id: ObjectID, - docId: any, - docType: number, - domainId: string, - owner: number, + _id: ObjectID; + docId: any; + docType: number; + domainId: string; + owner: number; + maintainer?: number[]; } declare module './model/problem' { diff --git a/packages/hydrooj/src/model/user.ts b/packages/hydrooj/src/model/user.ts index 0c4e076bd..a86183288 100644 --- a/packages/hydrooj/src/model/user.ts +++ b/packages/hydrooj/src/model/user.ts @@ -65,8 +65,17 @@ class User implements _User { for (const key in setting.DOMAIN_USER_SETTINGS_BY_KEY) { this[key] = dudoc[key] ?? setting.DOMAIN_USER_SETTINGS_BY_KEY[key].value; } + } - bus.serial('user/get', this); + async init() { + await bus.serial('user/get', this); + return this; + } + + own(doc: T, exact = false) { + return exact + ? doc.owner === this._id + : doc.owner === this._id || (doc.maintainer || []).includes(this._id); } hasPerm(...perm: bigint[]) { @@ -122,7 +131,7 @@ class UserModel { if (!udoc) return null; const dudoc = await domain.getDomainUser(domainId, udoc); if (typeof scope === 'string') scope = BigInt(scope); - return new User(udoc, dudoc, scope); + return new UserModel.User(udoc, dudoc, scope).init(); } static async getList(domainId: string, uids: number[]): Promise { @@ -143,7 +152,7 @@ class UserModel { : await coll.findOne({ unameLower }); if (!udoc) return null; const dudoc = await domain.getDomainUser(domainId, udoc); - return new UserModel.User(udoc, dudoc); + return new UserModel.User(udoc, dudoc).init(); } @ArgMethod @@ -156,7 +165,7 @@ class UserModel { : await coll.findOne({ mailLower }); if (!udoc) return null; const dudoc = await domain.getDomainUser(domainId, udoc); - return new UserModel.User(udoc, dudoc); + return new UserModel.User(udoc, dudoc).init(); } @ArgMethod @@ -233,7 +242,7 @@ class UserModel { @ArgMethod static async getPrefixList(domainId: string, prefix: string, limit: number = 50) { prefix = prefix.toLowerCase(); - const $regex = new RegExp(`\\A${escapeRegExp(prefix)}`, 'gmi'); + const $regex = new RegExp(`^${escapeRegExp(prefix)}`, 'gmi'); const uids = await coll.find({ unameLower: { $regex } }) .limit(limit).map((doc) => doc._id).toArray(); const users = []; diff --git a/packages/ui-default/templates/components/comments_discussion.html b/packages/ui-default/templates/components/comments_discussion.html index 5cdf85dd9..763661651 100644 --- a/packages/ui-default/templates/components/comments_discussion.html +++ b/packages/ui-default/templates/components/comments_discussion.html @@ -86,12 +86,12 @@ data-form="{{ {operation: reply_post_op, drid: doc._id}|json }}" > {% endif %} - {% if (handler.user.hasPerm(comment_edit_self_perm) and doc.owner == handler.user._id) or handler.user.hasPerm(comment_edit_perm) %} + {% if (handler.user.hasPerm(comment_edit_self_perm) and handler.user.own(doc)) or handler.user.hasPerm(comment_edit_perm) %} {% endif %} - {% if (handler.user.hasPerm(comment_delete_self_perm) and doc.owner == handler.user._id) or handler.user.hasPerm(comment_delete_perm) %} + {% if (handler.user.hasPerm(comment_delete_self_perm) and handler.user.own(doc)) or handler.user.hasPerm(comment_delete_perm) %} @@ -120,12 +120,12 @@ {% if handler.user.hasPerm(reply_post_perm) %} {% endif %} - {% if doc.owner == handler.user._id or handler.user.hasPerm(reply_edit_perm) %} + {% if handler.user.own(doc) or handler.user.hasPerm(reply_edit_perm) %} {% endif %} - {% if doc.owner == handler.user._id or handler.user.hasPerm(reply_delete_perm) %} + {% if handler.user.own(doc) or handler.user.hasPerm(reply_delete_perm) %} diff --git a/packages/ui-default/templates/components/comments_solution.html b/packages/ui-default/templates/components/comments_solution.html index 022d7dc9e..ad4fb2e63 100644 --- a/packages/ui-default/templates/components/comments_solution.html +++ b/packages/ui-default/templates/components/comments_solution.html @@ -118,12 +118,12 @@ data-form="{{ {operation: reply_post_op, psid: doc._id}|json }}" > {% endif %} - {% if (handler.user.hasPerm(comment_edit_self_perm) and doc.owner == handler.user._id) or handler.user.hasPerm(comment_edit_perm) %} + {% if (handler.user.hasPerm(comment_edit_self_perm) and handler.user.own(doc)) or handler.user.hasPerm(comment_edit_perm) %} {% endif %} - {% if (handler.user.hasPerm(comment_delete_self_perm) and doc.owner == handler.user._id) or handler.user.hasPerm(comment_delete_perm) %} + {% if (handler.user.hasPerm(comment_delete_self_perm) and handler.user.own(doc)) or handler.user.hasPerm(comment_delete_perm) %} @@ -148,12 +148,12 @@ {% if handler.user.hasPerm(reply_post_perm) %} {% endif %} - {% if (handler.user.hasPerm(reply_edit_self_perm) and doc.owner == handler.user._id) or handler.user.hasPerm(reply_edit_perm) %} + {% if (handler.user.hasPerm(reply_edit_self_perm) and handler.user.own(doc)) or handler.user.hasPerm(reply_edit_perm) %} {% endif %} - {% if (handler.user.hasPerm(reply_delete_self_perm) and doc.owner == handler.user._id) or handler.user.hasPerm(reply_delete_perm) %} + {% if (handler.user.hasPerm(reply_delete_self_perm) and handler.user.own(doc)) or handler.user.hasPerm(reply_delete_perm) %} diff --git a/packages/ui-default/templates/contest_edit.html b/packages/ui-default/templates/contest_edit.html index 7ca52e245..0e7f60f78 100644 --- a/packages/ui-default/templates/contest_edit.html +++ b/packages/ui-default/templates/contest_edit.html @@ -79,7 +79,7 @@ - {% if tdoc.owner == handler.user._id or handler.user.hasPerm(perm.PERM_EDIT_CONTEST) %} + {% if handler.user.own(tdoc) or handler.user.hasPerm(perm.PERM_EDIT_CONTEST) %} diff --git a/packages/ui-default/templates/discussion_detail.html b/packages/ui-default/templates/discussion_detail.html index af759326e..b1cd6b82c 100644 --- a/packages/ui-default/templates/discussion_detail.html +++ b/packages/ui-default/templates/discussion_detail.html @@ -29,7 +29,7 @@