priv (unstable)

This commit is contained in:
undefined committed 2020-06-29 13:33:44 +08:00
1 parent 554698189c
commit e092efca20
63 files changed
+706 -397

No files matched your search

+12 -6
View File
@@ -5,11 +5,16 @@ const {
ContestNotLiveError, ValidationError, ProblemNotFoundError,
ContestNotAttendedError,
} = require('../error');
const {
PERM_CREATE_CONTEST, PERM_EDIT_CONTEST, PERM_READ_RECORD_CODE,
PERM_VIEW_CONTEST,
} = require('../permission');
const paginate = require('../lib/paginate');
const {
PERM: {
PERM_CREATE_CONTEST, PERM_EDIT_CONTEST, PERM_READ_RECORD_CODE,
PERM_VIEW_CONTEST, PERM_EDIT_CONTEST_SELF,
},
PRIV: {
PRIV_READ_RECORD_CODE
}
} = require('../model/builtin');
const contest = require('../model/contest');
const document = require('../model/document');
const problem = require('../model/problem');
@@ -58,7 +63,7 @@ class ContestDetailHandler extends ContestHandler {
this.response.template = 'contest_detail.html';
const [tsdoc, pdict] = await Promise.all([
contest.getStatus(domainId, this.tdoc.docId, this.user._id),
problem.getList(domainId, this.tdoc.pids),
problem.getList(domainId, this.tdoc.pids, true),
]);
const psdict = {};
let rdict = {};
@@ -123,6 +128,7 @@ class ContestScoreboardDownloadHandler extends ContestDetailHandler {
class ContestEditHandler extends ContestDetailHandler {
async prepare() {
if (this.tdoc.owner !== this.user._id) this.checkPerm(PERM_EDIT_CONTEST);
else this.checkPerm(PERM_EDIT_CONTEST_SELF);
}
async get() {
@@ -266,7 +272,7 @@ class ContestProblemSubmitHandler extends ContestProblemHandler {
class ContestCodeHandler extends ContestDetailHandler {
async get({ domainId, tid }) {
this.checkPerm(PERM_READ_RECORD_CODE);
if (!this.user.hasPriv(PRIV_READ_RECORD_CODE)) this.checkPerm(PERM_READ_RECORD_CODE);
this.limitRate('homework_code', 3600, 60);
const [tdoc, tsdocs] = await contest.getAndListStatus(domainId, tid);
const rnames = {};
+16 -10
View File
@@ -4,13 +4,18 @@ const system = require('../model/system');
const user = require('../model/user');
const discussion = require('../model/discussion');
const document = require('../model/document');
const {
PERM: {
PERM_VIEW_DISCUSSION, PERM_EDIT_DISCUSSION, PERM_EDIT_DISCUSSION_REPLY,
PERM_DELETE_DISCUSSION, PERM_DELETE_DISCUSSION_REPLY, PERM_HIGHLIGHT_DISCUSSION,
PERM_CREATE_DISCUSSION, PERM_REPLY_DISCUSSION,
},
PRIV: {
PRIV_USER_PROFILE,
},
} = require('../model/builtin');
const { Route, Handler } = require('../service/server');
const { DiscussionNotFoundError, DocumentNotFoundError } = require('../error');
const {
PERM_VIEW_DISCUSSION, PERM_EDIT_DISCUSSION, PERM_EDIT_DISCUSSION_REPLY,
PERM_DELETE_DISCUSSION, PERM_DELETE_DISCUSSION_REPLY, PERM_HIGHLIGHT_DISCUSSION,
PERM_LOGGEDIN, PERM_CREATE_DISCUSSION, PERM_REPLY_DISCUSSION,
} = require('../permission');
const typeMapper = {
problem: document.TYPE_PROBLEM,
@@ -119,7 +124,7 @@ class DiscussionNodeHandler extends DiscussionHandler {
class DiscussionCreateHandler extends DiscussionHandler {
async prepare() {
this.checkPerm(PERM_LOGGEDIN);
this.checkPriv(PRIV_USER_PROFILE);
this.checkPerm(PERM_CREATE_DISCUSSION);
}
@@ -151,7 +156,7 @@ class DiscussionCreateHandler extends DiscussionHandler {
class DiscussionDetailHandler extends DiscussionHandler {
async get({ domainId, did, page = 1 }) {
const dsdoc = this.user.hasPerm(PERM_LOGGEDIN)
const dsdoc = this.user.hasPriv(PRIV_USER_PROFILE)
? await discussion.getStatus(domainId, did, this.user._id)
: null;
const [drdocs, pcount, drcount] = await paginate(
@@ -181,8 +186,11 @@ class DiscussionDetailHandler extends DiscussionHandler {
};
}
async post() {
this.checkPriv(PRIV_USER_PROFILE);
}
async postReply({ domainId, did, content }) {
this.checkPerm(PERM_LOGGEDIN);
this.checkPerm(PERM_REPLY_DISCUSSION);
this.limitRate('add_discussion', 3600, 30);
await discussion.addReply(domainId, did, this.user._id, content, this.request.ip);
@@ -190,7 +198,6 @@ class DiscussionDetailHandler extends DiscussionHandler {
}
async postTailReply({ domainId, drid, content }) {
this.checkPerm(PERM_LOGGEDIN);
this.checkPerm(PERM_REPLY_DISCUSSION);
this.limitRate('add_discussion', 3600, 30);
await discussion.addTailReply(domainId, drid, this.user._id, content, this.request.ip);
@@ -257,7 +264,6 @@ class DiscussionTailReplyRawHandler extends DiscussionHandler {
class DiscussionEditHandler extends DiscussionHandler {
async get() {
if (this.ddoc.owner !== this.user._id) this.checkPerm(PERM_EDIT_DISCUSSION);
const path = [
['Hydro', 'homepage'],
['discussion_main', 'discussion_main'],
+1 -1
View File
@@ -4,7 +4,7 @@ const system = require('../model/system');
const { DOMAIN_SETTINGS, DOMAIN_SETTINGS_BY_KEY } = require('../model/setting');
const paginate = require('../lib/paginate');
const { Route, Handler } = require('../service/server');
const { PERM_MANAGE } = require('../permission');
const { PERM_MANAGE } = require('../model/builtin').PERM;
const { RoleAlreadyExistError, ValidationError, PermissionError } = require('../error');
class DomainRankHandler extends Handler {
+15 -17
View File
@@ -21,9 +21,14 @@ const discussion = require('../model/discussion');
const token = require('../model/token');
const training = require('../model/training');
const {
PERM_VIEW_TRAINING, PERM_VIEW_CONTEST, PERM_VIEW_DISCUSSION,
PERM_LOGGEDIN, PERM_VIEW_HOMEWORK,
} = require('../permission');
PERM: {
PERM_VIEW_TRAINING, PERM_VIEW_CONTEST, PERM_VIEW_DISCUSSION,
PERM_VIEW_HOMEWORK,
},
PRIV: {
PRIV_USER_PROFILE, PRIV_DELETE_FILE, PRIV_DELETE_FILE_SELF,
},
} = require('../model/builtin');
const { geoip, useragent } = global.Hydro.lib;
@@ -113,10 +118,6 @@ class HomeHandler extends Handler {
}
class HomeSecurityHandler extends Handler {
async prepare() {
this.checkPerm(PERM_LOGGEDIN);
}
async get() {
// TODO(iceboy): pagination? or limit session count for uid?
const sessions = await token.getSessionListByUid(this.user._id);
@@ -172,10 +173,6 @@ class HomeSecurityHandler extends Handler {
}
class HomeSettingsHandler extends Handler {
async prepare() {
this.checkPerm(PERM_LOGGEDIN);
}
async get({ category }) {
// eslint-disable-next-line prefer-destructuring
category = category[0]; // Category would be splitted into array
@@ -284,7 +281,6 @@ class HomeMessagesConnectionHandler extends ConnectionHandler {
}
}
// TODO draft
class HomeFileHandler extends Handler {
async get() {
const ufdocs = await file.getMulti({ owner: this.user._id }).toArray();
@@ -296,6 +292,7 @@ class HomeFileHandler extends Handler {
async postDelete(ufid) {
const ufdoc = await file.get(ufid);
if (ufdoc.owner !== this.user._id) this.checkPriv(PRIV_DELETE_FILE);
else this.checkPriv(PRIV_DELETE_FILE_SELF);
const result = await file.del(ufdoc._id);
if (result) await file.decUsage(this.user._id, ufdoc.length);
this.back();
@@ -304,11 +301,12 @@ class HomeFileHandler extends Handler {
async function apply() {
Route('homepage', '/', HomeHandler);
Route('home_security', '/home/security', HomeSecurityHandler, PERM_LOGGEDIN);
Route('user_changemail_with_code', '/home/changeMail/:code', UserChangemailWithCodeHandler, PERM_LOGGEDIN);
Route('home_settings', '/home/settings/:category', HomeSettingsHandler, PERM_LOGGEDIN);
Route('home_messages', '/home/messages', HomeMessagesHandler, PERM_LOGGEDIN);
Connection('home_messages_conn', '/home/messages-conn', HomeMessagesConnectionHandler);
Route('home_security', '/home/security', HomeSecurityHandler, PRIV_USER_PROFILE);
Route('user_changemail_with_code', '/home/changeMail/:code', UserChangemailWithCodeHandler, PRIV_USER_PROFILE);
Route('home_settings', '/home/settings/:category', HomeSettingsHandler, PRIV_USER_PROFILE);
Route('home_messages', '/home/messages', HomeMessagesHandler, PRIV_USER_PROFILE);
Route('home_file', '/home/file', HomeFileHandler, PRIV_USER_PROFILE);
Connection('home_messages_conn', '/home/messages-conn', HomeMessagesConnectionHandler, PRIV_USER_PROFILE);
}
global.Hydro.handler.home = module.exports = apply;
+3 -3
View File
@@ -6,12 +6,12 @@ const {
ValidationError, HomeworkNotLiveError, ProblemNotFoundError,
HomeworkNotAttendedError,
} = require('../error');
const { Route, Handler } = require('../service/server');
const {
PERM_VIEW_HOMEWORK, PERM_ATTEND_HOMEWORK, PERM_VIEW_PROBLEM,
PERM_SUBMIT_PROBLEM, PERM_CREATE_HOMEWORK, PERM_EDIT_HOMEWORK,
PERM_VIEW_HOMEWORK_SCOREBOARD, PERM_READ_RECORD_CODE,
} = require('../permission');
const { Route, Handler } = require('../service/server');
} = require('../model/builtin').PERM;
const system = require('../model/system');
const user = require('../model/user');
const contest = require('../model/contest');
@@ -53,7 +53,7 @@ class HomeworkDetailHandler extends HomeworkHandler {
const tdoc = await contest.get(domainId, tid, document.TYPE_HOMEWORK);
const [tsdoc, pdict] = await Promise.all([
contest.getStatus(domainId, tdoc.docId, this.user._id, document.TYPE_HOMEWORK),
problem.getList(domainId, tdoc.pids),
problem.getList(domainId, tdoc.pids, true),
]);
const psdict = {};
let rdict = {};
+1 -1
View File
@@ -2,8 +2,8 @@ const fs = require('fs');
const path = require('path');
const os = require('os');
const problem = require('../model/problem');
const { PERM_CREATE_PROBLEM } = require('../model/builtin').PERM;
const { Route, Handler } = require('../service/server');
const { PERM_CREATE_PROBLEM } = require('../permission');
const { ValidationError } = require('../error');
class ProblemImportHandler extends Handler {
+2 -3
View File
@@ -1,5 +1,4 @@
const yaml = require('js-yaml');
const { PERM_JUDGE } = require('../permission');
const { parseTimeMS, parseMemoryMB } = require('../utils');
const record = require('../model/record');
const problem = require('../model/problem');
@@ -164,8 +163,8 @@ class JudgeConnectionHandler extends ConnectionHandler {
async function apply() {
Route('pretest', '/pretest', PretestHandler);
Route('judge', '/judge', JudgeHandler, PERM_JUDGE);
Connection('judge_conn', '/judge/conn', JudgeConnectionHandler, PERM_JUDGE);
Route('judge', '/judge', JudgeHandler, builtin.PERM.PERM_JUDGE);
Connection('judge_conn', '/judge/conn', JudgeConnectionHandler, builtin.PERM.PERM_JUDGE);
}
apply.next = next;
+3 -3
View File
@@ -1,4 +1,4 @@
const { PERM_JUDGE, PERM_LOGGEDIN } = require('../permission');
const { PRIV_JUDGE, PRIV_USER_PROFILE } = require('../model/builtin').PRIV;
const file = require('../model/file');
const user = require('../model/user');
const markdown = require('../lib/markdown');
@@ -37,7 +37,7 @@ class StatusHandler extends Handler {
class StatusUpdateHandler extends Handler {
async post(args) {
this.checkPerm(PERM_JUDGE);
this.checkPerm(PRIV_JUDGE);
args.type = 'judger';
return coll.updateOne(
{ mid: args.mid, type: 'judger' },
@@ -49,7 +49,7 @@ class StatusUpdateHandler extends Handler {
class SwitchLanguageHandler extends Handler {
async get({ lang }) {
if (this.user.hasPerm(PERM_LOGGEDIN)) await user.setById(this.user._id, { viewLang: lang });
if (this.user.hasPriv(PRIV_USER_PROFILE)) await user.setById(this.user._id, { viewLang: lang });
else this.session.viewLang = lang;
this.back();
}
+18 -11
View File
@@ -6,6 +6,18 @@ const record = require('../model/record');
const user = require('../model/user');
const solution = require('../model/solution');
const system = require('../model/system');
const {
PERM: {
PERM_VIEW_PROBLEM, PERM_VIEW_PROBLEM_HIDDEN, PERM_SUBMIT_PROBLEM,
PERM_CREATE_PROBLEM, PERM_READ_PROBLEM_DATA, PERM_EDIT_PROBLEM,
PERM_VIEW_PROBLEM_SOLUTION, PERM_CREATE_PROBLEM_SOLUTION,
PERM_EDIT_PROBLEM_SOLUTION, PERM_DELETE_PROBLEM_SOLUTION, PERM_EDIT_PROBLEM_SOLUTION_REPLY,
PERM_REPLY_PROBLEM_SOLUTION,
},
PRIV: {
PRIV_USER_PROFILE, PRIV_JUDGE,
},
} = require('../model/builtin');
const bus = require('../service/bus');
const {
Route, Connection, Handler, ConnectionHandler,
@@ -14,13 +26,6 @@ const {
NoProblemError, ProblemDataNotFoundError, BadRequestError,
SolutionNotFoundError,
} = require('../error');
const {
PERM_VIEW_PROBLEM, PERM_VIEW_PROBLEM_HIDDEN, PERM_SUBMIT_PROBLEM,
PERM_CREATE_PROBLEM, PERM_READ_PROBLEM_DATA, PERM_EDIT_PROBLEM,
PERM_JUDGE, PERM_VIEW_PROBLEM_SOLUTION, PERM_CREATE_PROBLEM_SOLUTION,
PERM_EDIT_PROBLEM_SOLUTION, PERM_DELETE_PROBLEM_SOLUTION, PERM_EDIT_PROBLEM_SOLUTION_REPLY,
PERM_REPLY_PROBLEM_SOLUTION, PERM_LOGGEDIN,
} = require('../permission');
class ProblemHandler extends Handler {
async _prepare() {
@@ -47,7 +52,7 @@ class ProblemHandler extends Handler {
page,
await system.get('PROBLEM_PER_PAGE'),
);
if (this.user.hasPerm(PERM_LOGGEDIN)) {
if (this.user.hasPriv(PRIV_USER_PROFILE)) {
psdict = await problem.getListStatus(
domainId, this.user._id, pdocs.map((pdoc) => pdoc.docId),
);
@@ -109,7 +114,7 @@ class ProblemCategoryHandler extends ProblemHandler {
page,
await system.get('PROBLEM_PER_PAGE'),
);
if (this.user.hasPerm(PERM_LOGGEDIN)) {
if (this.user.hasPriv(PRIV_USER_PROFILE)) {
psdict = await problem.getListStatus(
domainId, this.user._id, pdocs.map((pdoc) => pdoc.docId),
);
@@ -302,7 +307,7 @@ class ProblemEditHandler extends ProblemManageHandler {
const pid = validator.checkPid(this.request.body.pid);
const pdoc = await problem.get(domainId, this.request.params.pid);
await problem.edit(domainId, pdoc.docId, { title, content, pid });
this.response.redirect = `/p/${pid}`;
this.back();
}
}
@@ -332,7 +337,9 @@ class ProblemDataUploadHandler extends ProblemManageHandler {
class ProblemDataDownloadHandler extends ProblemDetailHandler {
async get({ pid }) {
if (this.user._id !== this.pdoc.owner) this.checkPerm([PERM_READ_PROBLEM_DATA, PERM_JUDGE]);
if (this.user._id !== this.pdoc.owner) {
if (!this.user.hasPerm(PERM_READ_PROBLEM_DATA)) this.checkPriv(PRIV_JUDGE);
}
if (!this.pdoc.data) throw new ProblemDataNotFoundError(pid);
else if (typeof this.pdoc.data === 'string') [, this.response.redirect] = this.pdoc.data.split('from:');
this.response.redirect = await file.url(this.pdoc.data, `${this.pdoc.title}.zip`);
+2 -2
View File
@@ -1,6 +1,6 @@
const { ObjectID } = require('bson');
const { PERM_READ_RECORD_CODE, PERM_REJUDGE, PERM_VIEW_PROBLEM_HIDDEN } = require('../permission');
const { PermissionError } = require('../error');
const { PERM_READ_RECORD_CODE, PERM_REJUDGE, PERM_VIEW_PROBLEM_HIDDEN } = require('../model/builtin').PERM;
const problem = require('../model/problem');
const record = require('../model/record');
const contest = require('../model/contest');
@@ -34,7 +34,7 @@ class RecordListHandler extends RecordHandler {
);
const [udict, pdict] = await Promise.all([
user.getList(domainId, rdocs.map((rdoc) => rdoc.uid)),
problem.getList(domainId, rdocs.map((rdoc) => rdoc.pid), false),
problem.getList(domainId, rdocs.map((rdoc) => rdoc.pid), false, false),
]);
const path = [
['Hydro', 'homepage'],
+1 -1
View File
@@ -2,7 +2,7 @@ const domain = require('../model/domain');
const system = require('../model/system');
const setting = require('../model/setting');
const { Route, Handler } = require('../service/server');
const { PERM_MANAGE } = require('../permission');
const { PERM_MANAGE } = require('../model/builtin').PERM;
const hpm = require('../lib/hpm');
const loader = require('../loader');
+12 -8
View File
@@ -1,10 +1,15 @@
const assert = require('assert');
const { ValidationError, ProblemNotFoundError } = require('../error');
const {
PERM_LOGGEDIN, PERM_VIEW_TRAINING, PERM_VIEW_PROBLEM_HIDDEN,
PERM_CREATE_TRAINING, PERM_EDIT_TRAINING,
} = require('../permission');
const paginate = require('../lib/paginate');
const {
PERM: {
PERM_VIEW_TRAINING, PERM_VIEW_PROBLEM_HIDDEN, PERM_CREATE_TRAINING,
PERM_EDIT_TRAINING,
},
PRIV: {
PRIV_USER_PROFILE,
},
} = require('../model/builtin');
const problem = require('../model/problem');
const builtin = require('../model/builtin');
const training = require('../model/training');
@@ -70,7 +75,7 @@ class TrainingMainHandler extends TrainingHandler {
for (const tdoc of tdocs) tids.add(tdoc.docId);
const tsdict = {};
let tdict = {};
if (this.user.hasPerm(PERM_LOGGEDIN)) {
if (this.user.hasPriv(PRIV_USER_PROFILE)) {
const enrolledTids = new Set();
const tsdocs = await training.getMultiStatus(domainId, {
uid: this.user._id,
@@ -101,10 +106,9 @@ class TrainingDetailHandler extends TrainingHandler {
async get({ domainId, tid }) {
const tdoc = await training.get(domainId, tid);
const pids = training.getPids(tdoc);
const f = this.user.hasPerm(PERM_VIEW_PROBLEM_HIDDEN) ? {} : { hidden: false };
const [owner, pdict] = await Promise.all([
user.getById(domainId, tdoc.owner),
problem.getList(domainId, pids, f),
problem.getList(domainId, pids, this.user.hasPerm(PERM_VIEW_PROBLEM_HIDDEN)),
]);
const psdict = await problem.getListStatus(domainId, this.user._id, pids);
const donePids = new Set();
@@ -151,7 +155,7 @@ class TrainingDetailHandler extends TrainingHandler {
}
async postEnroll({ domainId, tid }) {
this.checkPerm(PERM_LOGGEDIN);
this.checkPriv(PRIV_USER_PROFILE);
const tdoc = await training.get(domainId, tid);
await training.enroll(domainId, tdoc.docId, this.user._id);
this.back();
+23 -18
View File
@@ -12,9 +12,16 @@ const misc = require('../lib/misc');
const {
UserAlreadyExistError, InvalidTokenError, VerifyPasswordError,
UserNotFoundError, LoginError, SystemError,
PermissionError, BlacklistedError, UserFacingError,
BlacklistedError, UserFacingError,
} = require('../error');
const { PERM_LOGGEDIN } = require('../permission.js');
const {
PERM: {
PERM_VIEW_PROBLEM_HIDDEN,
},
PRIV: {
PRIV_USER_PROFILE, PRIV_REGISTER_USER,
},
} = require('../model/builtin');
class UserLoginHandler extends Handler {
async get() {
@@ -47,17 +54,11 @@ class UserLogoutHandler extends Handler {
}
async post() {
this.session.uid = 1;
this.session.uid = 0;
}
}
class UserRegisterHandler extends Handler {
async prepare() { // eslint-disable-line class-methods-use-this
if (!await system.get('user.register')) {
throw new PermissionError('Register');
}
}
async get() {
this.response.template = 'user_register.html';
}
@@ -78,7 +79,7 @@ class UserRegisterHandler extends Handler {
await sendMail(mail, 'Sign Up', 'user_register_mail', m);
this.response.template = 'user_register_mail_sent.html';
} else {
this.response.redirect = `/register/${t[0]}`;
this.response.redirect = this.url('user_register_with_code', { code: t[0] });
}
}
}
@@ -103,7 +104,7 @@ class UserRegisterWithCodeHandler extends Handler {
});
await token.del(code, token.TYPE_REGISTRATION);
this.session.uid = uid;
this.response.redirect = '/';
this.response.redirect = this.url('homepage');
}
}
@@ -142,7 +143,7 @@ class UserLostPassWithCodeHandler extends Handler {
if (password !== verifyPassword) throw new VerifyPasswordError();
await user.setPassword(tdoc.uid, password);
await token.del(code, token.TYPE_LOSTPASS);
this.response.redirect = '/';
this.response.redirect = this.url('homepage');
}
}
@@ -152,9 +153,12 @@ class UserDetailHandler extends Handler {
const udoc = await user.getById(domainId, uid, true);
const sdoc = await token.getMostRecentSessionByUid(uid);
const rdocs = await record.getByUid(domainId, uid);
const pdict = await problem.getList(domainId, rdocs.map((rdoc) => rdoc.pid), false);
const pdict = await problem.getList(
domainId, rdocs.map((rdoc) => rdoc.pid),
this.user.hasPerm(PERM_VIEW_PROBLEM_HIDDEN), false,
);
// Remove sensitive data
if (!isSelfProfile) sdoc.createIp = sdoc.updateIp = sdoc._id = '';
if (!isSelfProfile && sdoc) sdoc.createIp = sdoc.updateIp = sdoc._id = '';
this.response.template = 'user_detail.html';
this.response.body = {
isSelfProfile, udoc, sdoc, rdocs, pdict,
@@ -296,6 +300,7 @@ class OauthCallbackHandler extends Handler {
if (udoc) {
this.session.uid = udoc._id;
} else {
this.checkPriv(PRIV_REGISTER_USER);
let username = '';
r.uname = r.uname || [];
r.uname.push(String.random(16));
@@ -325,13 +330,13 @@ async function apply() {
Route('user_login', '/login', UserLoginHandler);
Route('user_oauth', '/oauth/:type', OauthHandler);
Route('user_oauth_callback', '/oauth/:type/callback', OauthCallbackHandler);
Route('user_register', '/register', UserRegisterHandler);
Route('user_register_with_code', '/register/:code', UserRegisterWithCodeHandler);
Route('user_logout', '/logout', UserLogoutHandler);
Route('user_register', '/register', UserRegisterHandler, PRIV_REGISTER_USER);
Route('user_register_with_code', '/register/:code', UserRegisterWithCodeHandler, PRIV_REGISTER_USER);
Route('user_logout', '/logout', UserLogoutHandler, PRIV_USER_PROFILE);
Route('user_lostpass', '/lostpass', UserLostPassHandler);
Route('user_lostpass_with_code', '/lostpass/:code', UserLostPassWithCodeHandler);
Route('user_search', '/user/search', UserSearchHandler);
Route('user_delete', '/user/delete', UserDeleteHandler, PERM_LOGGEDIN);
Route('user_delete', '/user/delete', UserDeleteHandler, PRIV_USER_PROFILE);
Route('user_detail', '/user/:uid', UserDetailHandler);
}