From 3f84f131a1d6fed607b9dd0cf6a5a0a22b09c9d6 Mon Sep 17 00:00:00 2001 From: undefined Date: Sat, 25 Jul 2020 10:50:03 +0800 Subject: [PATCH] xff header check --- hydro/check.ts | 95 +++++++++++++++++++------------------ hydro/handler/discussion.ts | 26 +++++----- hydro/handler/domain.ts | 2 +- hydro/handler/home.ts | 3 +- hydro/handler/judge.ts | 4 +- hydro/handler/manage.ts | 2 +- hydro/model/builtin.ts | 13 +++-- package.json | 2 +- 8 files changed, 77 insertions(+), 70 deletions(-) diff --git a/hydro/check.ts b/hydro/check.ts index 8f314ed6c..de92c833c 100644 --- a/hydro/check.ts +++ b/hydro/check.ts @@ -2,67 +2,72 @@ import os from 'os'; import superagent from 'superagent'; +import { Dictionary } from 'lodash'; import * as system from './model/system'; import * as db from './service/db'; const c = {}; -async function checkDb(log, warn, error) { - try { - const coll = db.collection('check'); - const d = await coll.findOne({ _id: 'check' }); - if (!d) await coll.insertOne({ _id: 'check', value: 'check' }); - await coll.createIndex('check'); - await coll.deleteOne({ _id: 'check' }); - await coll.drop(); - } catch (e) { - error(`Mongo Error: Database read-write failed.\n${e.message}`); - } -} +type CheckItem = (context: any, log: Function, warn: Function, error: Function) => Promise; -async function checkPerm(log, warn) { - const { username } = os.userInfo(); - if (username === 'root') warn('Hydro should not be run as root.'); - // TODO check cwd read-write -} +const checks: Dictionary = { + async checkDb(ctx, log, warn, error) { + try { + const coll = db.collection('check'); + const d = await coll.findOne({ _id: 'check' }); + if (!d) await coll.insertOne({ _id: 'check', value: 'check' }); + await coll.createIndex('check'); + await coll.deleteOne({ _id: 'check' }); + await coll.drop(); + } catch (e) { + error(`Mongo Error: Database read-write failed.\n${e.message}`); + } + }, + async checkPerm(ctx, log, warn) { + const { username } = os.userInfo(); + if (username === 'root') warn('Hydro should not be run as root.'); + // TODO check cwd read-write + }, + async checkSystem(ctx, log, warn) { + const platform = os.platform(); + if (platform !== 'linux') { + warn(`Hydro is running on ${platform}, suggest to use linux4.4+.`); + } + }, + async checkMail(ctx, log, warn) { + const from = await system.get('smtp.from'); + if (!from) warn('SMTP account was not provided, email verification disabled.'); + }, + async checkProxy(ctx, log, warn) { + const [github, google, proxy] = await system.getMany([ + 'oauth.githubappid', 'oauth.googleappid', 'proxy', + ]); + if (proxy) { + superagent.get('https://www.google.com/').timeout(10000).proxy(proxy) + .catch(() => warn('The proxy configured seem to be invalid.')); + } else if (github || google) { + warn('OAuth enabled. But for well-known reasons, a proxy is required.'); + } + }, + async checkIpHeader(ctx, log, warn) { + const header = await system.get('server.xff'); + if (header && !ctx.request.ip) warn('IP header seems incorrect.\nCheck dashboard>settings>server.'); + }, +}; -async function checkMail(log, warn) { - const from = await system.get('smtp.from'); - if (!from) warn('SMTP account was not provided, email verification disabled.'); -} - -async function checkProxy(log, warn) { - const [github, google, proxy] = await system.getMany([ - 'oauth.githubappid', 'oauth.googleappid', 'proxy', - ]); - if (proxy) { - superagent.get('https://www.google.com/').timeout(10000).proxy(proxy) - .catch(() => warn('The proxy configured seem to be invalid.')); - } else if (github || google) { - warn('OAuth enabled. But for well-known reasons, a proxy is required.'); - } -} - -const checks = [ - checkDb, - checkPerm, - checkMail, - checkProxy, -]; - -export async function start(log, warn, error, cb) { +export async function start(ctx, log, warn, error, cb) { const id = String.random(6); cb(id); - for (const check of checks) { + for (const name in checks) { if (c[id]) { delete c[id]; return; } // eslint-disable-next-line no-await-in-loop - await check(log, warn, error); + await checks[name](ctx, log, warn, error); } } -export async function cancel(id) { +export async function cancel(id: string) { c[id] = true; } diff --git a/hydro/handler/discussion.ts b/hydro/handler/discussion.ts index 4205895c0..bbf46fef5 100644 --- a/hydro/handler/discussion.ts +++ b/hydro/handler/discussion.ts @@ -137,11 +137,6 @@ class DiscussionNodeHandler extends DiscussionHandler { } class DiscussionCreateHandler extends DiscussionHandler { - async prepare() { - this.checkPriv(PRIV.PRIV_USER_PROFILE); - this.checkPerm(PERM.PERM_CREATE_DISCUSSION); - } - async get({ type, name }) { const path = [ ['Hydro', 'homepage'], @@ -193,9 +188,7 @@ class DiscussionDetailHandler extends DiscussionHandler { uids.push(this.ddoc.owner); for (const drdoc of drdocs) { if (drdoc.reply) { - for (const drrdoc of drdocs) { - uids.push(drrdoc.owner); - } + drdocs.forEach((drrdoc) => uids.push(drrdoc.owner)); } } const udict = await user.getList(domainId, uids); @@ -236,14 +229,23 @@ class DiscussionDetailHandler extends DiscussionHandler { @param('drid', Types.ObjectID) @param('content', Types.String, isContent) async postEditReply(domainId: string, drid: ObjectID, content: string) { - if (this.drdoc.owner !== this.user._id) this.checkPerm(PERM.PERM_EDIT_DISCUSSION_REPLY); + 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); + } await discussion.editReply(domainId, drid, content); this.back(); } @param('drid', Types.ObjectID) async postDeleteReply(domainId: string, drid: ObjectID) { - if (this.drdoc.owner !== this.user._id) this.checkPerm(PERM.PERM_DELETE_DISCUSSION_REPLY); + if (!(this.ddoc.owner === this.user._id + && this.user.hasPerm(PERM.PERM_DELETE_DISCUSSION_REPLY_SELF_DISCUSSION))) { + if (this.drdoc.owner !== this.user._id) { + this.checkPerm(PERM.PERM_DELETE_DISCUSSION_REPLY); + } else this.checkPerm(PERM.PERM_DELETE_DISCUSSION_SELF); + } await discussion.delReply(domainId, drid); this.back(); } @@ -320,6 +322,7 @@ class DiscussionEditHandler extends DiscussionHandler { domainId: string, did: ObjectID, title: string, content: string, highlight = false, ) { if (this.ddoc.owner !== this.user._id) this.checkPerm(PERM.PERM_EDIT_DISCUSSION); + else this.checkPerm(PERM.PERM_EDIT_DISCUSSION_SELF); if (highlight && !this.ddoc.highlight) this.checkPerm(PERM.PERM_HIGHLIGHT_DISCUSSION); await discussion.edit(domainId, did, title, content, highlight); this.response.body = { did }; @@ -329,6 +332,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); + else this.checkPerm(PERM.PERM_DELETE_DISCUSSION_SELF); await discussion.del(domainId, did); this.response.body = { type: this.ddoc.parentType, parent: this.ddoc.parentId }; this.response.redirect = this.url('discussion_node', { @@ -346,7 +350,7 @@ export async function apply() { Route('discussion_reply_raw', '/discuss/:did/:drid/raw', DiscussionReplyRawHandler); Route('discussion_tail_reply_raw', '/discuss/:did/:drid/:drrid/raw', DiscussionTailReplyRawHandler); Route('discussion_node', '/discuss/:type/:name', DiscussionNodeHandler); - Route('discussion_create', '/discuss/:type/:name/create', DiscussionCreateHandler); + Route('discussion_create', '/discuss/:type/:name/create', DiscussionCreateHandler, PRIV.PRIV_USER_PROFILE, PERM.PERM_CREATE_DISCUSSION); } global.Hydro.handler.discussion = apply; diff --git a/hydro/handler/domain.ts b/hydro/handler/domain.ts index 3e116a2ad..d06bec4ec 100644 --- a/hydro/handler/domain.ts +++ b/hydro/handler/domain.ts @@ -193,7 +193,7 @@ class DomainSearchHandler extends Handler { } export async function apply() { - Route('ranking', '/ranking', DomainRankHandler); + Route('ranking', '/ranking', DomainRankHandler, PERM.PERM_VIEW_RANKING); Route('domain_dashboard', '/domain/dashboard', DomainDashboardHandler); Route('domain_edit', '/domain/edit', DomainEditHandler); Route('domain_user', '/domain/user', DomainUserHandler); diff --git a/hydro/handler/home.ts b/hydro/handler/home.ts index 84c94cc48..8977cbd6d 100644 --- a/hydro/handler/home.ts +++ b/hydro/handler/home.ts @@ -144,7 +144,8 @@ class HomeSecurityHandler extends Handler { if (password !== verify) throw new VerifyPasswordError(); this.user.checkPassword(current); await user.setPassword(this.user._id, password); - this.back(); + await token.delByUid(this.user._id); + this.response.redirect = this.url('user_login'); } @param('currentPassword', Types.String) diff --git a/hydro/handler/judge.ts b/hydro/handler/judge.ts index d065e9b2c..11c6a8b8d 100644 --- a/hydro/handler/judge.ts +++ b/hydro/handler/judge.ts @@ -1,6 +1,4 @@ -import yaml from 'js-yaml'; import { ObjectID } from 'mongodb'; -import { parseTimeMS, parseMemoryMB } from '../utils'; import { JudgeResultBody, Rdoc } from '../interface'; import * as record from '../model/record'; import * as problem from '../model/problem'; @@ -10,7 +8,7 @@ import * as domain from '../model/domain'; import * as task from '../model/task'; import * as bus from '../service/bus'; import { - Route, Handler, Connection, ConnectionHandler, Types, param, + Route, Handler, Connection, ConnectionHandler, } from '../service/server'; async function _postJudge(rdoc: Rdoc) { diff --git a/hydro/handler/manage.ts b/hydro/handler/manage.ts index dc9c806fb..275f652a7 100644 --- a/hydro/handler/manage.ts +++ b/hydro/handler/manage.ts @@ -59,7 +59,7 @@ class SystemCheckConnHandler extends ConnectionHandler { const log = (payload: any) => this.send({ type: 'log', payload }); const warn = (payload: any) => this.send({ type: 'warn', payload }); const error = (payload: any) => this.send({ type: 'error', payload }); - await check.start(log, warn, error, (id) => { this.id = id; }); + await check.start(this, log, warn, error, (id) => { this.id = id; }); } async cleanup() { diff --git a/hydro/model/builtin.ts b/hydro/model/builtin.ts index 0ceb957af..0b031855e 100644 --- a/hydro/model/builtin.ts +++ b/hydro/model/builtin.ts @@ -1,4 +1,7 @@ // @ts-nocheck + +import { Udoc } from '../interface'; + /* Why nocheck? * BitInt requires at least ES2020, but we can't use it as * NodeJS doesn't support some of this new features. @@ -83,10 +86,10 @@ export const PERM = { PERM_VIEW_RANKING: 1n << 50n, // Placeholder - PERM_ALL: 0n, + PERM_ALL: -1n, PERM_BASIC: 0n, PERM_DEFAULT: 0n, - PERM_ADMIN: 0n, + PERM_ADMIN: -1n, }; export const Permission = (family: string, key: BigInt, desc: string) => ({ family, key, desc }); @@ -153,10 +156,6 @@ export const PERMS = [ Permission('perm_ranking', PERM.PERM_VIEW_RANKING, 'View ranking'), ]; -let PERM_ALL = 0n; -for (const p in PERM) if (PERM[p]) PERM_ALL |= PERM[p]; -PERM.PERM_ALL = PERM_ALL; - export const PERMS_BY_FAMILY = {}; for (const p of PERMS) { if (!PERMS_BY_FAMILY[p.family]) PERMS_BY_FAMILY[p.family] = [p]; @@ -268,7 +267,7 @@ export const LEVELS = [ [1, 100], ]; -export const BUILTIN_USERS = [ +export const BUILTIN_USERS: Udoc[] = [ { _id: 0, mail: 'Guest@hydro.local', diff --git a/package.json b/package.json index 6cd77763d..4151d2e3e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hydrooj", - "version": "2.10.2", + "version": "2.10.3", "main": "dist/loader.js", "bin": "bin/hydrooj.js", "repository": "https://github.com/hydro-dev/Hydro.git",