mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-19 17:38:19 +08:00
移除权限节点,添加 get post route 修饰器
PERM_EDIT_DISCUSSION_REPLY_SELF_DISCUSSION PERM_EDIT_DISCUSSION_REPLY PERM_EDIT_PROBLEM_SOLUTION_REPLY
This commit is contained in:
1 parent
6dd7a4dcb8
commit
99b7d33157
8 files changed
+120
-63
No files matched your search
@@ -9,5 +9,6 @@ message.markAsRead
|
||||
评测队列优先级系统
|
||||
讨论浏览数量显示
|
||||
解离mail模块
|
||||
@param @get @post @arg
|
||||
extend problem settings
|
||||
存储系统抽象化
|
||||
extend problem settings
|
||||
+oplog
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "hydrooj",
|
||||
"version": "2.13.16",
|
||||
"version": "2.13.17",
|
||||
"bin": "bin/hydrooj.js",
|
||||
"main": "dist/loader.js",
|
||||
"typings": "dist/loader.d.ts",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ObjectID } from 'mongodb';
|
||||
import { isSafeInteger } from 'lodash';
|
||||
import { DiscussionNotFoundError, DocumentNotFoundError } from '../error';
|
||||
import { DiscussionNotFoundError, DocumentNotFoundError, PermissionError } from '../error';
|
||||
import {
|
||||
Mdoc, Drdoc, Ddoc, Drrdoc,
|
||||
} from '../interface';
|
||||
@@ -242,11 +242,8 @@ class DiscussionDetailHandler extends DiscussionHandler {
|
||||
@param('drid', Types.ObjectID)
|
||||
@param('content', Types.String, isContent)
|
||||
async postEditReply(domainId: string, drid: ObjectID, content: string) {
|
||||
if (!(this.ddoc.owner === this.user._id
|
||||
&& this.user.hasPerm(PERM.PERM_EDIT_DISCUSSION_REPLY_SELF_DISCUSSION))) {
|
||||
if (this.drdoc.owner !== this.user._id) this.checkPerm(PERM.PERM_EDIT_DISCUSSION_REPLY);
|
||||
else this.checkPerm(PERM.PERM_EDIT_DISCUSSION_REPLY_SELF);
|
||||
}
|
||||
this.checkPerm(PERM.PERM_EDIT_DISCUSSION_REPLY_SELF);
|
||||
if (this.drdoc.owner !== this.user._id) throw new PermissionError(PERM.PERM_EDIT_DISCUSSION_REPLY_SELF);
|
||||
await discussion.editReply(domainId, drid, content);
|
||||
this.back();
|
||||
}
|
||||
@@ -267,7 +264,8 @@ class DiscussionDetailHandler extends DiscussionHandler {
|
||||
@param('drrid', Types.ObjectID)
|
||||
@param('content', Types.String, isContent)
|
||||
async postEditTailReply(domainId: string, drid: ObjectID, drrid: ObjectID, content: string) {
|
||||
if (this.drdoc.owner !== this.user._id) this.checkPerm(PERM.PERM_EDIT_DISCUSSION_REPLY);
|
||||
this.checkPerm(PERM.PERM_EDIT_DISCUSSION_REPLY_SELF);
|
||||
if (this.drdoc.owner !== this.user._id) throw new PermissionError(PERM.PERM_EDIT_DISCUSSION_REPLY_SELF);
|
||||
await discussion.editTailReply(domainId, drid, drrid, content);
|
||||
this.back();
|
||||
}
|
||||
|
||||
@@ -556,7 +556,7 @@ export class ProblemSolutionHandler extends ProblemDetailHandler {
|
||||
if ((!psdoc) || psdoc.pid !== this.pdoc.docId) throw new SolutionNotFoundError(psid);
|
||||
if (!(psrdoc.owner === this.user._id
|
||||
&& this.user.hasPerm(PERM.PERM_EDIT_PROBLEM_SOLUTION_REPLY_SELF))) {
|
||||
this.checkPerm(PERM.PERM_EDIT_PROBLEM_SOLUTION_REPLY);
|
||||
throw new PermissionError(PERM.PERM_EDIT_PROBLEM_SOLUTION_REPLY_SELF);
|
||||
}
|
||||
await solution.editReply(domainId, psid, psrid, content);
|
||||
this.back();
|
||||
|
||||
@@ -140,4 +140,6 @@ export class Logger {
|
||||
}
|
||||
}
|
||||
|
||||
// This fails in jest
|
||||
// @ts-ignore
|
||||
global.Hydro.Logger = { Logger };
|
||||
@@ -37,7 +37,6 @@ export const PERM = {
|
||||
PERM_DELETE_PROBLEM_SOLUTION: 1n << 20n,
|
||||
PERM_DELETE_PROBLEM_SOLUTION_SELF: 1n << 21n,
|
||||
PERM_REPLY_PROBLEM_SOLUTION: 1n << 22n,
|
||||
PERM_EDIT_PROBLEM_SOLUTION_REPLY: 1n << 23n,
|
||||
PERM_EDIT_PROBLEM_SOLUTION_REPLY_SELF: 1n << 24n,
|
||||
PERM_DELETE_PROBLEM_SOLUTION_REPLY: 1n << 25n,
|
||||
PERM_DELETE_PROBLEM_SOLUTION_REPLY_SELF: 1n << 26n,
|
||||
@@ -52,9 +51,7 @@ export const PERM = {
|
||||
PERM_DELETE_DISCUSSION: 1n << 32n,
|
||||
PERM_DELETE_DISCUSSION_SELF: 1n << 33n,
|
||||
PERM_REPLY_DISCUSSION: 1n << 34n,
|
||||
PERM_EDIT_DISCUSSION_REPLY: 1n << 35n,
|
||||
PERM_EDIT_DISCUSSION_REPLY_SELF: 1n << 36n,
|
||||
PERM_EDIT_DISCUSSION_REPLY_SELF_DISCUSSION: 1n << 37n,
|
||||
PERM_DELETE_DISCUSSION_REPLY: 1n << 38n,
|
||||
PERM_DELETE_DISCUSSION_REPLY_SELF: 1n << 39n,
|
||||
PERM_DELETE_DISCUSSION_REPLY_SELF_DISCUSSION: 1n << 40n,
|
||||
@@ -118,7 +115,6 @@ export const PERMS = [
|
||||
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'),
|
||||
@@ -130,9 +126,7 @@ export const PERMS = [
|
||||
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'),
|
||||
@@ -627,6 +621,7 @@ export const CONSTANT = {
|
||||
global.Hydro.model.builtin = {
|
||||
Permission,
|
||||
registerLanguage,
|
||||
CONSTANT,
|
||||
PERM,
|
||||
PERMS,
|
||||
PERMS_BY_FAMILY,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* eslint-disable prefer-destructuring */
|
||||
import assert from 'assert';
|
||||
import { PassThrough } from 'stream';
|
||||
import path from 'path';
|
||||
import { resolve } from 'path';
|
||||
import os from 'os';
|
||||
import http from 'http';
|
||||
import moment from 'moment-timezone';
|
||||
@@ -43,6 +43,7 @@ type Converter = (value: any) => any;
|
||||
type Validator = (value: any) => boolean;
|
||||
interface ParamOption {
|
||||
name: string,
|
||||
source: 'all' | 'get' | 'post' | 'route',
|
||||
isOptional?: boolean,
|
||||
convert?: Converter,
|
||||
validate?: Validator,
|
||||
@@ -130,59 +131,52 @@ export const Types: Types = {
|
||||
],
|
||||
};
|
||||
|
||||
export function param(name: string, type: Type, validate: Validator): MethodDecorator;
|
||||
export function param(name: string, type?: Type, isOptional?: boolean): MethodDecorator;
|
||||
export function param(
|
||||
name: string, type: Type, validate: null, convert: Converter
|
||||
): MethodDecorator;
|
||||
export function param(
|
||||
name: string, type: Type, validate?: Validator, convert?: Converter,
|
||||
): MethodDecorator;
|
||||
export function param(
|
||||
name: string, type: Type, isOptional?: boolean, validate?: Validator, convert?: Converter,
|
||||
): MethodDecorator;
|
||||
export function param(
|
||||
name: string, ...args: Array<Type | boolean | Converter | Validator>
|
||||
): MethodDecorator;
|
||||
export function param(name: string, ...args: any): MethodDecorator {
|
||||
function _buildParam(name: string, source: 'get' | 'post' | 'all' | 'route', ...args: Array<Type | boolean | Validator | Converter>) {
|
||||
let cursor = 0;
|
||||
const v: ParamOption = { name };
|
||||
const v: ParamOption = { name, source };
|
||||
let isValidate = true;
|
||||
while (cursor < args.length) {
|
||||
if (args[cursor] instanceof Array) {
|
||||
const type = args[cursor];
|
||||
const current = args[cursor];
|
||||
if (current instanceof Array) {
|
||||
const type = current;
|
||||
if (type[0]) v.convert = type[0];
|
||||
if (type[1]) v.validate = type[1];
|
||||
if (type[2]) v.isOptional = type[2];
|
||||
} else if (typeof args[cursor] === 'boolean') v.isOptional = args[cursor];
|
||||
} else if (typeof current === 'boolean') v.isOptional = current;
|
||||
else if (isValidate) {
|
||||
if (args[cursor] !== null) v.validate = args[cursor];
|
||||
if (current !== null) v.validate = current;
|
||||
isValidate = false;
|
||||
} else {
|
||||
v.convert = args[cursor];
|
||||
}
|
||||
} else v.convert = current;
|
||||
cursor++;
|
||||
}
|
||||
return function desc(target: any, funcName: string, obj: any) {
|
||||
return v;
|
||||
}
|
||||
|
||||
function _descriptor(v: ParamOption) {
|
||||
return function desc(this: Handler, target: any, funcName: string, obj: any) {
|
||||
if (!target.__param) target.__param = {};
|
||||
if (!target.__param[target.constructor.name]) target.__param[target.constructor.name] = {};
|
||||
if (!target.__param[target.constructor.name][funcName]) {
|
||||
target.__param[target.constructor.name][funcName] = [{ name: 'domainId', type: 'string' }];
|
||||
target.__param[target.constructor.name][funcName] = [{ name: 'domainId', type: 'string', source: 'route' }];
|
||||
const originalMethod = obj.value;
|
||||
obj.value = function validate(rawArgs: any, ...extra: any[]) {
|
||||
if (typeof rawArgs === 'string' || extra.length) return originalMethod.call(this, rawArgs, ...extra);
|
||||
obj.value = function validate(this: Handler, rawArgs: any, ...extra: any[]) {
|
||||
if (typeof rawArgs !== 'object' || extra.length) return originalMethod.call(this, rawArgs, ...extra);
|
||||
const c = [];
|
||||
const arglist: ParamOption[] = this.__param[target.constructor.name][funcName];
|
||||
for (const item of arglist) {
|
||||
if (!item.isOptional || rawArgs[item.name]) {
|
||||
if (!rawArgs[item.name]) throw new ValidationError(item.name);
|
||||
if (item.validate) {
|
||||
if (!item.validate(rawArgs[item.name])) {
|
||||
throw new ValidationError(item.name);
|
||||
}
|
||||
}
|
||||
if (item.convert) c.push(item.convert(rawArgs[item.name]));
|
||||
else c.push(rawArgs[item.name]);
|
||||
const src = item.source === 'all'
|
||||
? rawArgs
|
||||
: item.source === 'get'
|
||||
? this.request.query
|
||||
: item.source === 'route'
|
||||
? this.request.params
|
||||
: this.request.body;
|
||||
const value = src[item.name];
|
||||
if (!item.isOptional || value) {
|
||||
if (!value) throw new ValidationError(item.name);
|
||||
if (item.validate && !item.validate(value)) throw new ValidationError(item.name);
|
||||
if (item.convert) c.push(item.convert(value));
|
||||
else c.push(value);
|
||||
} else c.push(undefined);
|
||||
}
|
||||
return originalMethod.call(this, ...c);
|
||||
@@ -193,6 +187,70 @@ export function param(name: string, ...args: any): MethodDecorator {
|
||||
};
|
||||
}
|
||||
|
||||
export function get(
|
||||
name: string, type: Type, validate: null, convert: Converter
|
||||
): MethodDecorator;
|
||||
export function get(
|
||||
name: string, type: Type, validate?: Validator, convert?: Converter,
|
||||
): MethodDecorator;
|
||||
export function get(
|
||||
name: string, type?: Type, isOptional?: boolean, validate?: Validator, convert?: Converter,
|
||||
): MethodDecorator;
|
||||
export function get(
|
||||
name: string, ...args: Array<Type | boolean | Validator | Converter>
|
||||
): MethodDecorator;
|
||||
export function get(name: string, ...args: any): MethodDecorator {
|
||||
return _descriptor(_buildParam(name, 'get', ...args));
|
||||
}
|
||||
|
||||
export function post(
|
||||
name: string, type: Type, validate: null, convert: Converter
|
||||
): MethodDecorator;
|
||||
export function post(
|
||||
name: string, type?: Type, validate?: Validator, convert?: Converter,
|
||||
): MethodDecorator;
|
||||
export function post(
|
||||
name: string, type?: Type, isOptional?: boolean, validate?: Validator, convert?: Converter,
|
||||
): MethodDecorator;
|
||||
export function post(
|
||||
name: string, ...args: Array<Type | boolean | Converter | Validator>
|
||||
): MethodDecorator;
|
||||
export function post(name: string, ...args: any): MethodDecorator {
|
||||
return _descriptor(_buildParam(name, 'post', ...args));
|
||||
}
|
||||
|
||||
export function route(
|
||||
name: string, type: Type, validate: null, convert: Converter
|
||||
): MethodDecorator;
|
||||
export function route(
|
||||
name: string, type?: Type, validate?: Validator, convert?: Converter,
|
||||
): MethodDecorator;
|
||||
export function route(
|
||||
name: string, type?: Type, isOptional?: boolean, validate?: Validator, convert?: Converter,
|
||||
): MethodDecorator;
|
||||
export function route(
|
||||
name: string, ...args: Array<Type | boolean | Converter | Validator>
|
||||
): MethodDecorator;
|
||||
export function route(name: string, ...args: any): MethodDecorator {
|
||||
return _descriptor(_buildParam(name, 'route', ...args));
|
||||
}
|
||||
|
||||
export function param(
|
||||
name: string, type: Type, validate: null, convert: Converter
|
||||
): MethodDecorator;
|
||||
export function param(
|
||||
name: string, type?: Type, validate?: Validator, convert?: Converter,
|
||||
): MethodDecorator;
|
||||
export function param(
|
||||
name: string, type?: Type, isOptional?: boolean, validate?: Validator, convert?: Converter,
|
||||
): MethodDecorator;
|
||||
export function param(
|
||||
name: string, ...args: Array<Type | boolean | Converter | Validator>
|
||||
): MethodDecorator;
|
||||
export function param(name: string, ...args: any): MethodDecorator {
|
||||
return _descriptor(_buildParam(name, 'all', ...args));
|
||||
}
|
||||
|
||||
export function requireCsrfToken(target: any, funcName: string, obj: any) {
|
||||
const originalMethod = obj.value;
|
||||
obj.value = async function checkCsrfToken(...args: any[]) {
|
||||
@@ -211,7 +269,7 @@ export async function prepare() {
|
||||
maxAge: 0,
|
||||
}));
|
||||
} else {
|
||||
app.use(cache(path.join(os.tmpdir(), 'hydro', 'public'), {
|
||||
app.use(cache(resolve(os.tmpdir(), 'hydro', 'public'), {
|
||||
maxAge: 365 * 24 * 60 * 60,
|
||||
}));
|
||||
}
|
||||
@@ -265,6 +323,8 @@ export class Handler {
|
||||
|
||||
loginMethods: any;
|
||||
|
||||
__param: Record<string, ParamOption[]>;
|
||||
|
||||
constructor(ctx: Koa.Context) {
|
||||
this.ctx = ctx;
|
||||
this.request = {
|
||||
@@ -624,10 +684,10 @@ const Checker = (permPrivChecker) => {
|
||||
};
|
||||
};
|
||||
|
||||
export function Route(name: string, route: string, RouteHandler: any, ...permPrivChecker) {
|
||||
export function Route(name: string, path: string, RouteHandler: any, ...permPrivChecker) {
|
||||
const checker = Checker(permPrivChecker);
|
||||
router.all(name, route, (ctx) => handle(ctx, RouteHandler, checker));
|
||||
router.all(`${name}_with_domainId`, `/d/:domainId${route}`, (ctx) => handle(ctx, RouteHandler, checker));
|
||||
router.all(name, path, (ctx) => handle(ctx, RouteHandler, checker));
|
||||
router.all(`${name}_with_domainId`, `/d/:domainId${path}`, (ctx) => handle(ctx, RouteHandler, checker));
|
||||
}
|
||||
|
||||
export class ConnectionHandler {
|
||||
@@ -772,10 +832,8 @@ export function Connection(
|
||||
try {
|
||||
const args = { domainId: 'system', ...h.request.params };
|
||||
h.args = args;
|
||||
const cookie = await new Promise((resolve) => {
|
||||
conn.once('data', (c) => {
|
||||
resolve(c);
|
||||
});
|
||||
const cookie = await new Promise((r) => {
|
||||
conn.once('data', r);
|
||||
});
|
||||
args.cookie = cookie;
|
||||
await h.init(args);
|
||||
@@ -842,6 +900,9 @@ global.Hydro.service.server = {
|
||||
app,
|
||||
server,
|
||||
router,
|
||||
get,
|
||||
post,
|
||||
route,
|
||||
param,
|
||||
requireCsrfToken,
|
||||
Handler,
|
||||
|
||||
Reference in new issue
Block a user