diff --git a/packages/hydrooj/package.json b/packages/hydrooj/package.json index bd4fee0d7..441135233 100644 --- a/packages/hydrooj/package.json +++ b/packages/hydrooj/package.json @@ -1,6 +1,6 @@ { "name": "hydrooj", - "version": "2.13.25", + "version": "2.13.26", "bin": "bin/hydrooj.js", "main": "dist/loader.js", "typings": "dist/loader.d.ts", diff --git a/packages/hydrooj/src/handler/user.ts b/packages/hydrooj/src/handler/user.ts index 150fa1440..f6266ffc4 100644 --- a/packages/hydrooj/src/handler/user.ts +++ b/packages/hydrooj/src/handler/user.ts @@ -36,7 +36,7 @@ class UserLoginHandler extends Handler { await user.setById(udoc._id, { loginat: new Date(), loginip: this.request.ip }); if (udoc.priv === PRIV.PRIV_NONE) throw new BlacklistedError(uname); this.session.uid = udoc._id; - this.session.scope = PERM.PERM_ALL; + this.session.scope = PERM.PERM_ALL.toString(); this.session.save = rememberme; this.response.redirect = this.request.referer.endsWith('/login') ? '/' : this.request.referer; } @@ -49,7 +49,7 @@ class UserLogoutHandler extends Handler { async post() { this.session.uid = 0; - this.session.scope = PERM.PERM_ALL; + this.session.scope = PERM.PERM_ALL.toString(); } } @@ -234,7 +234,7 @@ class OauthCallbackHandler extends Handler { const uid = await oauth.get(r._id); if (uid) { this.session.uid = uid; - this.session.scope = PERM.PERM_ALL; + this.session.scope = PERM.PERM_ALL.toString(); } else { this.checkPriv(PRIV.PRIV_REGISTER_USER); let username = ''; @@ -262,7 +262,7 @@ class OauthCallbackHandler extends Handler { oauth.set(r.email, _id), ]); this.session.uid = _id; - this.session.scope = PERM.PERM_ALL; + this.session.scope = PERM.PERM_ALL.toString(); } } } diff --git a/packages/hydrooj/src/model/document.ts b/packages/hydrooj/src/model/document.ts index bc32f605e..8cc9d6483 100644 --- a/packages/hydrooj/src/model/document.ts +++ b/packages/hydrooj/src/model/document.ts @@ -6,7 +6,8 @@ import { import { Pdoc, Ddoc, Drdoc, Tdoc, TrainingDoc, ProblemStatusDoc, } from '../interface'; -import { NumberKeys, ArrayKeys } from '../typeutils'; +import { buildProjection } from '../utils'; +import { NumberKeys, ArrayKeys, Projection } from '../typeutils'; import * as db from '../service/db'; import * as bus from '../service/bus'; @@ -82,8 +83,12 @@ export async function add( return docId || res.insertedId; } -export function get(domainId: string, docType: K, docId: DocType[K]['docId']) { - return coll.findOne({ domainId, docType, docId }); +export async function get(domainId: string, docType: K, docId: DocType[K]['docId'], projection?: Projection) { + let cursor = coll.find({ domainId, docType, docId }).limit(1); + if (projection) cursor = cursor.project(buildProjection(projection)); + const result = await cursor.toArray(); + if (result.length) return result[0]; + return null; } export async function set( @@ -120,9 +125,11 @@ export function deleteMultiStatus( } export function getMulti( - domainId: string, docType: K, query?: FilterQuery, + domainId: string, docType: K, query?: FilterQuery, projection?: Projection, ): Cursor { - return coll.find({ ...query, docType, domainId }); + let cursor = coll.find({ ...query, docType, domainId }); + if (projection) cursor = cursor.project(buildProjection(projection)); + return cursor; } export async function inc( diff --git a/packages/hydrooj/src/model/problem.ts b/packages/hydrooj/src/model/problem.ts index 42a9927bb..9afa17099 100644 --- a/packages/hydrooj/src/model/problem.ts +++ b/packages/hydrooj/src/model/problem.ts @@ -8,7 +8,7 @@ import * as domain from './domain'; import { Pdoc, Pdict, ProblemDataSource, ProblemStatusDoc, } from '../interface'; -import { NumberKeys } from '../typeutils'; +import { NumberKeys, Projection } from '../typeutils'; import { ProblemNotFoundError } from '../error'; import * as testdataConfig from '../lib/testdataConfig'; @@ -43,6 +43,17 @@ export const pdocHidden: Pdoc = { acMsg: '', }; +export const PROJECTION_LIST: Projection = [ + '_id', 'domainId', 'docType', 'docId', 'pid', + 'owner', 'title', 'nSubmit', 'nAccept', 'difficulty', + 'tag', 'category', 'hidden', +]; + +export const PROJECTION_PUBLIC: Projection = [ + ...PROJECTION_LIST, + 'content', 'html', 'data', 'config', 'acMsg', +]; + export async function add( domainId: string, pid: string = null, title: string, content: string, owner: number, tag: string[] = [], category: string[] = [], data: ProblemDataSource = null, hidden = false, @@ -59,13 +70,13 @@ export async function add( export async function get( domainId: string, pid: string | number, - uid: number = null, + uid: number = null, projection: Projection = PROJECTION_PUBLIC, ): Promise { if (typeof pid !== 'number') { if (Number.isSafeInteger(parseInt(pid, 10))) pid = parseInt(pid, 10); } const pdoc = typeof pid === 'number' - ? await document.get(domainId, document.TYPE_PROBLEM, pid) + ? await document.get(domainId, document.TYPE_PROBLEM, pid, projection) : (await document.getMulti(domainId, document.TYPE_PROBLEM, { pid }).toArray())[0]; if (!pdoc) return null; if (uid) { @@ -74,8 +85,8 @@ export async function get( return pdoc; } -export function getMulti(domainId: string, query: FilterQuery) { - return document.getMulti(domainId, document.TYPE_PROBLEM, query); +export function getMulti(domainId: string, query: FilterQuery, projection = PROJECTION_LIST) { + return document.getMulti(domainId, document.TYPE_PROBLEM, query, projection); } export function getMultiStatus(domainId: string, query: FilterQuery) { @@ -162,6 +173,9 @@ export async function setTestdata(domainId: string, _id: number, filePath: strin } global.Hydro.model.problem = { + PROJECTION_LIST, + PROJECTION_PUBLIC, + SETTING_DIFFICULTY_ADMIN, SETTING_DIFFICULTY_ALGORITHM, SETTING_DIFFICULTY_AVERAGE, diff --git a/packages/hydrooj/src/model/user.ts b/packages/hydrooj/src/model/user.ts index 11663b795..5705a0d8b 100644 --- a/packages/hydrooj/src/model/user.ts +++ b/packages/hydrooj/src/model/user.ts @@ -92,7 +92,7 @@ export class User implements _User { this.loginat = udoc.loginat; this.loginip = () => udoc.loginip; this.perm = () => dudoc.perm; - this.scope = () => scope; + this.scope = () => (typeof scope === 'string' ? BigInt(scope) : scope); this.role = dudoc.role || 'default'; for (const key in setting.SETTINGS_BY_KEY) { @@ -114,7 +114,7 @@ export class User implements _User { hasPerm(...perm: bigint[]) { for (const i in perm) { - if ((this.perm() * this.scope() & perm[i]) === perm[i]) return true; + if ((this.perm() & this.scope() & perm[i]) === perm[i]) return true; } return false; } diff --git a/packages/hydrooj/src/service/server.ts b/packages/hydrooj/src/service/server.ts index c865059a8..9c64acd0e 100644 --- a/packages/hydrooj/src/service/server.ts +++ b/packages/hydrooj/src/service/server.ts @@ -483,8 +483,8 @@ export class Handler { this.user = await user.getById(domainId, this.session.uid, this.session.scope); if (!this.user) { this.session.uid = 0; - this.session.scope = PERM.PERM_ALL; - this.user = await user.getById(domainId, this.session.uid); + this.session.scope = PERM.PERM_ALL.toString(); + this.user = await user.getById(domainId, this.session.uid, this.session.scope); } this.csrfToken = this.getCsrfToken(this.session._id || String.random(32)); this.UIContext.csrfToken = this.csrfToken; diff --git a/packages/hydrooj/src/typeutils.ts b/packages/hydrooj/src/typeutils.ts index 59abdde3c..e69e0b55b 100644 --- a/packages/hydrooj/src/typeutils.ts +++ b/packages/hydrooj/src/typeutils.ts @@ -9,3 +9,5 @@ export type ArrayKeys = { export type Value = { [K in keyof O]: V } + +export type Projection = (string & keyof O)[] diff --git a/packages/hydrooj/src/utils.ts b/packages/hydrooj/src/utils.ts index bd3b4cd9d..8f7053a44 100644 --- a/packages/hydrooj/src/utils.ts +++ b/packages/hydrooj/src/utils.ts @@ -179,3 +179,9 @@ export function streamToBuffer(stream): Promise { stream.on('end', () => resolve(Buffer.concat(buffers))); }); } + +export function buildProjection(fields: string[]): Record { + const o = {}; + for (const k of fields) o[k] = 1; + return o; +}