xff header check

This commit is contained in:
undefined committed 2020-07-25 10:50:03 +08:00
1 parent 71ab562592
commit 3f84f131a1
8 files changed
+77 -70

No files matched your search

+50 -45
View File
@@ -2,67 +2,72 @@
import os from 'os'; import os from 'os';
import superagent from 'superagent'; import superagent from 'superagent';
import { Dictionary } from 'lodash';
import * as system from './model/system'; import * as system from './model/system';
import * as db from './service/db'; import * as db from './service/db';
const c = {}; const c = {};
async function checkDb(log, warn, error) { type CheckItem = (context: any, log: Function, warn: Function, error: Function) => Promise<void>;
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 function checkPerm(log, warn) { const checks: Dictionary<CheckItem> = {
const { username } = os.userInfo(); async checkDb(ctx, log, warn, error) {
if (username === 'root') warn('Hydro should not be run as root.'); try {
// TODO check cwd read-write 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) { export async function start(ctx, log, warn, error, cb) {
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) {
const id = String.random(6); const id = String.random(6);
cb(id); cb(id);
for (const check of checks) { for (const name in checks) {
if (c[id]) { if (c[id]) {
delete c[id]; delete c[id];
return; return;
} }
// eslint-disable-next-line no-await-in-loop // 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; c[id] = true;
} }
+15 -11
View File
@@ -137,11 +137,6 @@ class DiscussionNodeHandler extends DiscussionHandler {
} }
class DiscussionCreateHandler extends DiscussionHandler { class DiscussionCreateHandler extends DiscussionHandler {
async prepare() {
this.checkPriv(PRIV.PRIV_USER_PROFILE);
this.checkPerm(PERM.PERM_CREATE_DISCUSSION);
}
async get({ type, name }) { async get({ type, name }) {
const path = [ const path = [
['Hydro', 'homepage'], ['Hydro', 'homepage'],
@@ -193,9 +188,7 @@ class DiscussionDetailHandler extends DiscussionHandler {
uids.push(this.ddoc.owner); uids.push(this.ddoc.owner);
for (const drdoc of drdocs) { for (const drdoc of drdocs) {
if (drdoc.reply) { if (drdoc.reply) {
for (const drrdoc of drdocs) { drdocs.forEach((drrdoc) => uids.push(drrdoc.owner));
uids.push(drrdoc.owner);
}
} }
} }
const udict = await user.getList(domainId, uids); const udict = await user.getList(domainId, uids);
@@ -236,14 +229,23 @@ class DiscussionDetailHandler extends DiscussionHandler {
@param('drid', Types.ObjectID) @param('drid', Types.ObjectID)
@param('content', Types.String, isContent) @param('content', Types.String, isContent)
async postEditReply(domainId: string, drid: ObjectID, content: string) { 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); await discussion.editReply(domainId, drid, content);
this.back(); this.back();
} }
@param('drid', Types.ObjectID) @param('drid', Types.ObjectID)
async postDeleteReply(domainId: string, drid: 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); await discussion.delReply(domainId, drid);
this.back(); this.back();
} }
@@ -320,6 +322,7 @@ class DiscussionEditHandler extends DiscussionHandler {
domainId: string, did: ObjectID, title: string, content: string, highlight = false, domainId: string, did: ObjectID, title: string, content: string, highlight = false,
) { ) {
if (this.ddoc.owner !== this.user._id) this.checkPerm(PERM.PERM_EDIT_DISCUSSION); 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); if (highlight && !this.ddoc.highlight) this.checkPerm(PERM.PERM_HIGHLIGHT_DISCUSSION);
await discussion.edit(domainId, did, title, content, highlight); await discussion.edit(domainId, did, title, content, highlight);
this.response.body = { did }; this.response.body = { did };
@@ -329,6 +332,7 @@ class DiscussionEditHandler extends DiscussionHandler {
@param('did', Types.ObjectID) @param('did', Types.ObjectID)
async postDelete(domainId: string, did: ObjectID) { async postDelete(domainId: string, did: ObjectID) {
if (this.ddoc.owner !== this.user._id) this.checkPerm(PERM.PERM_DELETE_DISCUSSION); 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); await discussion.del(domainId, did);
this.response.body = { type: this.ddoc.parentType, parent: this.ddoc.parentId }; this.response.body = { type: this.ddoc.parentType, parent: this.ddoc.parentId };
this.response.redirect = this.url('discussion_node', { 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_reply_raw', '/discuss/:did/:drid/raw', DiscussionReplyRawHandler);
Route('discussion_tail_reply_raw', '/discuss/:did/:drid/:drrid/raw', DiscussionTailReplyRawHandler); Route('discussion_tail_reply_raw', '/discuss/:did/:drid/:drrid/raw', DiscussionTailReplyRawHandler);
Route('discussion_node', '/discuss/:type/:name', DiscussionNodeHandler); 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; global.Hydro.handler.discussion = apply;
+1 -1
View File
@@ -193,7 +193,7 @@ class DomainSearchHandler extends Handler {
} }
export async function apply() { export async function apply() {
Route('ranking', '/ranking', DomainRankHandler); Route('ranking', '/ranking', DomainRankHandler, PERM.PERM_VIEW_RANKING);
Route('domain_dashboard', '/domain/dashboard', DomainDashboardHandler); Route('domain_dashboard', '/domain/dashboard', DomainDashboardHandler);
Route('domain_edit', '/domain/edit', DomainEditHandler); Route('domain_edit', '/domain/edit', DomainEditHandler);
Route('domain_user', '/domain/user', DomainUserHandler); Route('domain_user', '/domain/user', DomainUserHandler);
+2 -1
View File
@@ -144,7 +144,8 @@ class HomeSecurityHandler extends Handler {
if (password !== verify) throw new VerifyPasswordError(); if (password !== verify) throw new VerifyPasswordError();
this.user.checkPassword(current); this.user.checkPassword(current);
await user.setPassword(this.user._id, password); 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) @param('currentPassword', Types.String)
+1 -3
View File
@@ -1,6 +1,4 @@
import yaml from 'js-yaml';
import { ObjectID } from 'mongodb'; import { ObjectID } from 'mongodb';
import { parseTimeMS, parseMemoryMB } from '../utils';
import { JudgeResultBody, Rdoc } from '../interface'; import { JudgeResultBody, Rdoc } from '../interface';
import * as record from '../model/record'; import * as record from '../model/record';
import * as problem from '../model/problem'; import * as problem from '../model/problem';
@@ -10,7 +8,7 @@ import * as domain from '../model/domain';
import * as task from '../model/task'; import * as task from '../model/task';
import * as bus from '../service/bus'; import * as bus from '../service/bus';
import { import {
Route, Handler, Connection, ConnectionHandler, Types, param, Route, Handler, Connection, ConnectionHandler,
} from '../service/server'; } from '../service/server';
async function _postJudge(rdoc: Rdoc) { async function _postJudge(rdoc: Rdoc) {
+1 -1
View File
@@ -59,7 +59,7 @@ class SystemCheckConnHandler extends ConnectionHandler {
const log = (payload: any) => this.send({ type: 'log', payload }); const log = (payload: any) => this.send({ type: 'log', payload });
const warn = (payload: any) => this.send({ type: 'warn', payload }); const warn = (payload: any) => this.send({ type: 'warn', payload });
const error = (payload: any) => this.send({ type: 'error', 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() { async cleanup() {
+6 -7
View File
@@ -1,4 +1,7 @@
// @ts-nocheck // @ts-nocheck
import { Udoc } from '../interface';
/* Why nocheck? /* Why nocheck?
* BitInt requires at least ES2020, but we can't use it as * BitInt requires at least ES2020, but we can't use it as
* NodeJS doesn't support some of this new features. * NodeJS doesn't support some of this new features.
@@ -83,10 +86,10 @@ export const PERM = {
PERM_VIEW_RANKING: 1n << 50n, PERM_VIEW_RANKING: 1n << 50n,
// Placeholder // Placeholder
PERM_ALL: 0n, PERM_ALL: -1n,
PERM_BASIC: 0n, PERM_BASIC: 0n,
PERM_DEFAULT: 0n, PERM_DEFAULT: 0n,
PERM_ADMIN: 0n, PERM_ADMIN: -1n,
}; };
export const Permission = (family: string, key: BigInt, desc: string) => ({ family, key, desc }); 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'), 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 = {}; export const PERMS_BY_FAMILY = {};
for (const p of PERMS) { for (const p of PERMS) {
if (!PERMS_BY_FAMILY[p.family]) PERMS_BY_FAMILY[p.family] = [p]; if (!PERMS_BY_FAMILY[p.family]) PERMS_BY_FAMILY[p.family] = [p];
@@ -268,7 +267,7 @@ export const LEVELS = [
[1, 100], [1, 100],
]; ];
export const BUILTIN_USERS = [ export const BUILTIN_USERS: Udoc[] = [
{ {
_id: 0, _id: 0,
mail: 'Guest@hydro.local', mail: 'Guest@hydro.local',
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "hydrooj", "name": "hydrooj",
"version": "2.10.2", "version": "2.10.3",
"main": "dist/loader.js", "main": "dist/loader.js",
"bin": "bin/hydrooj.js", "bin": "bin/hydrooj.js",
"repository": "https://github.com/hydro-dev/Hydro.git", "repository": "https://github.com/hydro-dev/Hydro.git",