mirror of
https://github.com/monkeytypegame/monkeytype.git
synced 2025-11-08 05:03:39 +08:00
Endpoint schemas/Improved Auth Middleware (#2411) by Bruception
* Lots of stuff * Changed code order * Change message * Use strict comparison * Fix Bearer auth * changed failed validation message * removed full stops Co-authored-by: Miodec <bartnikjack@gmail.com>
This commit is contained in:
parent
0966a14316
commit
957b4cf1a8
18 changed files with 235 additions and 152 deletions
|
|
@ -2,16 +2,18 @@ const ConfigDAO = require("../../dao/config");
|
|||
const { validateConfig } = require("../../handlers/validation");
|
||||
|
||||
class ConfigController {
|
||||
static async getConfig(req, res) {
|
||||
const { uid } = req.decodedToken;
|
||||
let config = await ConfigDAO.getConfig(uid);
|
||||
return res.status(200).json(config);
|
||||
static async getConfig(req, _res) {
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
|
||||
return await ConfigDAO.getConfig(uid);
|
||||
}
|
||||
static async saveConfig(req, res) {
|
||||
const { config } = req.body;
|
||||
const { uid } = req.decodedToken;
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
|
||||
validateConfig(config);
|
||||
await ConfigDAO.saveConfig(uid, config);
|
||||
|
||||
return res.sendStatus(200);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,49 +1,40 @@
|
|||
const _ = require("lodash");
|
||||
const LeaderboardsDAO = require("../../dao/leaderboards");
|
||||
const { verifyIdToken } = require("../../handlers/auth");
|
||||
|
||||
class LeaderboardsController {
|
||||
static async get(req, res) {
|
||||
static async get(req, _res) {
|
||||
const { language, mode, mode2, skip, limit } = req.query;
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
|
||||
let uid;
|
||||
const leaderboard = await LeaderboardsDAO.get(
|
||||
mode,
|
||||
mode2,
|
||||
language,
|
||||
skip,
|
||||
limit
|
||||
);
|
||||
|
||||
const { authorization } = req.headers;
|
||||
if (authorization) {
|
||||
const token = authorization.split(" ");
|
||||
if (token[0].trim() == "Bearer")
|
||||
req.decodedToken = await verifyIdToken(token[1]);
|
||||
uid = req.decodedToken.uid;
|
||||
}
|
||||
|
||||
if (!language || !mode || !mode2 || !skip) {
|
||||
return res.status(400).json({
|
||||
message: "Missing parameters",
|
||||
});
|
||||
}
|
||||
let retval = await LeaderboardsDAO.get(mode, mode2, language, skip, limit);
|
||||
retval.forEach((item) => {
|
||||
if (uid && item.uid == uid) {
|
||||
//
|
||||
} else {
|
||||
delete item.discordId;
|
||||
delete item.uid;
|
||||
delete item.difficulty;
|
||||
delete item.language;
|
||||
}
|
||||
const normalizedLeaderboard = _.map(leaderboard, (entry) => {
|
||||
return uid && entry.uid === uid
|
||||
? entry
|
||||
: _.omit(entry, ["discordId", "uid", "difficulty", "language"]);
|
||||
});
|
||||
return res.status(200).json(retval);
|
||||
|
||||
return normalizedLeaderboard;
|
||||
}
|
||||
|
||||
static async getRank(req, res) {
|
||||
const { language, mode, mode2 } = req.query;
|
||||
const { uid } = req.decodedToken;
|
||||
if (!language || !mode || !mode2 || !uid) {
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
|
||||
if (!uid) {
|
||||
return res.status(400).json({
|
||||
message: "Missing parameters",
|
||||
message: "Missing user id.",
|
||||
});
|
||||
}
|
||||
let retval = await LeaderboardsDAO.getRank(mode, mode2, language, uid);
|
||||
return res.status(200).json(retval);
|
||||
|
||||
return await LeaderboardsDAO.getRank(mode, mode2, language, uid);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ const Captcha = require("../../handlers/captcha");
|
|||
|
||||
class NewQuotesController {
|
||||
static async getQuotes(req, _res) {
|
||||
const { uid } = req.decodedToken;
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
const userInfo = await UserDAO.getUser(uid);
|
||||
if (!userInfo.quoteMod) {
|
||||
throw new MonkeyError(403, "You don't have permission to do this");
|
||||
|
|
@ -15,7 +15,7 @@ class NewQuotesController {
|
|||
}
|
||||
|
||||
static async addQuote(req, _res) {
|
||||
const { uid } = req.decodedToken;
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
const { text, source, language, captcha } = req.body;
|
||||
if (!(await Captcha.verify(captcha))) {
|
||||
throw new MonkeyError(400, "Captcha check failed");
|
||||
|
|
@ -24,7 +24,7 @@ class NewQuotesController {
|
|||
}
|
||||
|
||||
static async approve(req, _res) {
|
||||
const { uid } = req.decodedToken;
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
const { quoteId, editText, editSource } = req.body;
|
||||
const userInfo = await UserDAO.getUser(uid);
|
||||
if (!userInfo.quoteMod) {
|
||||
|
|
@ -37,7 +37,7 @@ class NewQuotesController {
|
|||
}
|
||||
|
||||
static async refuse(req, res) {
|
||||
const { uid } = req.decodedToken;
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
const { quoteId } = req.body;
|
||||
|
||||
await NewQuotesDAO.refuse(quoteId, uid);
|
||||
|
|
|
|||
|
|
@ -7,35 +7,33 @@ const MonkeyError = require("../../handlers/error");
|
|||
|
||||
class PresetController {
|
||||
static async getPresets(req, res) {
|
||||
const { uid } = req.decodedToken;
|
||||
let presets = await PresetDAO.getPresets(uid);
|
||||
return res.status(200).json(presets);
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
|
||||
return await PresetDAO.getPresets(uid);
|
||||
}
|
||||
|
||||
static async addPreset(req, res) {
|
||||
const { name, config } = req.body;
|
||||
const { uid } = req.decodedToken;
|
||||
if (!isTagPresetNameValid(name))
|
||||
throw new MonkeyError(400, "Invalid preset name.");
|
||||
validateConfig(config);
|
||||
let preset = await PresetDAO.addPreset(uid, name, config);
|
||||
return res.status(200).json(preset);
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
|
||||
return await PresetDAO.addPreset(uid, name, config);
|
||||
}
|
||||
|
||||
static async editPreset(req, res) {
|
||||
const { _id, name, config } = req.body;
|
||||
const { uid } = req.decodedToken;
|
||||
if (!isTagPresetNameValid(name))
|
||||
throw new MonkeyError(400, "Invalid preset name.");
|
||||
if (config) validateConfig(config);
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
|
||||
await PresetDAO.editPreset(uid, _id, name, config);
|
||||
|
||||
return res.sendStatus(200);
|
||||
}
|
||||
|
||||
static async removePreset(req, res) {
|
||||
const { _id } = req.body;
|
||||
const { uid } = req.decodedToken;
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
|
||||
await PresetDAO.removePreset(uid, _id);
|
||||
|
||||
return res.sendStatus(200);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ class QuoteRatingsController {
|
|||
}
|
||||
|
||||
static async submitRating(req, res) {
|
||||
const { uid } = req.decodedToken;
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
let { quoteId, rating, language } = req.body;
|
||||
|
||||
quoteId = parseInt(quoteId);
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ const Logger = require("../../handlers/logger");
|
|||
|
||||
class QuotesController {
|
||||
static async reportQuote(req, res) {
|
||||
const { uid } = req.decodedToken;
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
|
||||
const user = await UserDAO.getUser(uid);
|
||||
if (user.cannotReport) {
|
||||
|
|
|
|||
|
|
@ -33,27 +33,27 @@ try {
|
|||
|
||||
class ResultController {
|
||||
static async getResults(req, res) {
|
||||
const { uid } = req.decodedToken;
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
const results = await ResultDAO.getResults(uid);
|
||||
return res.status(200).json(results);
|
||||
}
|
||||
|
||||
static async deleteAll(req, res) {
|
||||
const { uid } = req.decodedToken;
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
await ResultDAO.deleteAll(uid);
|
||||
Logger.log("user_results_deleted", "", uid);
|
||||
return res.sendStatus(200);
|
||||
}
|
||||
|
||||
static async updateTags(req, res) {
|
||||
const { uid } = req.decodedToken;
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
const { tags, resultid } = req.body;
|
||||
await ResultDAO.updateTags(uid, resultid, tags);
|
||||
return res.sendStatus(200);
|
||||
}
|
||||
|
||||
static async addResult(req, res) {
|
||||
const { uid } = req.decodedToken;
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
const { result } = req.body;
|
||||
result.uid = uid;
|
||||
if (validateObjectValues(result) > 0)
|
||||
|
|
@ -100,7 +100,7 @@ class ResultController {
|
|||
|
||||
let resulthash = result.hash;
|
||||
delete result.hash;
|
||||
if (req.context.configuration.resultObjectHashCheck.enabled) {
|
||||
if (req.ctx.configuration.resultObjectHashCheck.enabled) {
|
||||
const serverhash = objecthash(result);
|
||||
if (serverhash !== resulthash) {
|
||||
Logger.log(
|
||||
|
|
|
|||
|
|
@ -16,14 +16,14 @@ const uaparser = require("ua-parser-js");
|
|||
class UserController {
|
||||
static async createNewUser(req, res) {
|
||||
const { name } = req.body;
|
||||
const { email, uid } = req.decodedToken;
|
||||
const { email, uid } = req.ctx.decodedToken;
|
||||
await UsersDAO.addUser(name, email, uid);
|
||||
Logger.log("user_created", `${name} ${email}`, uid);
|
||||
return res.sendStatus(200);
|
||||
}
|
||||
|
||||
static async deleteUser(req, res) {
|
||||
const { uid } = req.decodedToken;
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
const userInfo = await UsersDAO.getUser(uid);
|
||||
await UsersDAO.deleteUser(uid);
|
||||
Logger.log("user_deleted", `${userInfo.email} ${userInfo.name}`, uid);
|
||||
|
|
@ -31,7 +31,7 @@ class UserController {
|
|||
}
|
||||
|
||||
static async updateName(req, res) {
|
||||
const { uid } = req.decodedToken;
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
const { name } = req.body;
|
||||
if (!isUsernameValid(name))
|
||||
return res.status(400).json({
|
||||
|
|
@ -49,7 +49,7 @@ class UserController {
|
|||
}
|
||||
|
||||
static async clearPb(req, res) {
|
||||
const { uid } = req.decodedToken;
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
await UsersDAO.clearPb(uid);
|
||||
Logger.log("user_cleared_pbs", "", uid);
|
||||
return res.sendStatus(200);
|
||||
|
|
@ -69,7 +69,7 @@ class UserController {
|
|||
}
|
||||
|
||||
static async updateEmail(req, res) {
|
||||
const { uid } = req.decodedToken;
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
const { newEmail } = req.body;
|
||||
try {
|
||||
await UsersDAO.updateEmail(uid, newEmail);
|
||||
|
|
@ -81,7 +81,7 @@ class UserController {
|
|||
}
|
||||
|
||||
static async getUser(req, res) {
|
||||
const { email, uid } = req.decodedToken;
|
||||
const { email, uid } = req.ctx.decodedToken;
|
||||
let userInfo;
|
||||
try {
|
||||
userInfo = await UsersDAO.getUser(uid);
|
||||
|
|
@ -126,7 +126,7 @@ class UserController {
|
|||
}
|
||||
|
||||
static async linkDiscord(req, res) {
|
||||
const { uid } = req.decodedToken;
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
|
||||
let requser;
|
||||
try {
|
||||
|
|
@ -174,7 +174,7 @@ class UserController {
|
|||
}
|
||||
|
||||
static async unlinkDiscord(req, res) {
|
||||
const { uid } = req.decodedToken;
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
let userInfo;
|
||||
try {
|
||||
userInfo = await UsersDAO.getUser(uid);
|
||||
|
|
@ -191,7 +191,7 @@ class UserController {
|
|||
}
|
||||
|
||||
static async addTag(req, res) {
|
||||
const { uid } = req.decodedToken;
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
const { tagName } = req.body;
|
||||
if (!isTagPresetNameValid(tagName))
|
||||
return res.status(400).json({
|
||||
|
|
@ -203,14 +203,14 @@ class UserController {
|
|||
}
|
||||
|
||||
static async clearTagPb(req, res) {
|
||||
const { uid } = req.decodedToken;
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
const { tagid } = req.body;
|
||||
await UsersDAO.removeTagPb(uid, tagid);
|
||||
return res.sendStatus(200);
|
||||
}
|
||||
|
||||
static async editTag(req, res) {
|
||||
const { uid } = req.decodedToken;
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
const { tagid, newname } = req.body;
|
||||
if (!isTagPresetNameValid(newname))
|
||||
return res.status(400).json({
|
||||
|
|
@ -222,21 +222,21 @@ class UserController {
|
|||
}
|
||||
|
||||
static async removeTag(req, res) {
|
||||
const { uid } = req.decodedToken;
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
const { tagid } = req.body;
|
||||
await UsersDAO.removeTag(uid, tagid);
|
||||
return res.sendStatus(200);
|
||||
}
|
||||
|
||||
static async getTags(req, res) {
|
||||
const { uid } = req.decodedToken;
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
let tags = await UsersDAO.getTags(uid);
|
||||
if (tags == undefined) tags = [];
|
||||
return res.status(200).json(tags);
|
||||
}
|
||||
|
||||
static async updateLbMemory(req, res) {
|
||||
const { uid } = req.decodedToken;
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
const { mode, mode2, language, rank } = req.body;
|
||||
await UsersDAO.updateLbMemory(uid, mode, mode2, language, rank);
|
||||
return res.sendStatus(200);
|
||||
|
|
|
|||
|
|
@ -13,14 +13,14 @@ const router = Router();
|
|||
router.get(
|
||||
"/",
|
||||
RateLimit.configGet,
|
||||
authenticateRequest,
|
||||
authenticateRequest(),
|
||||
asyncHandlerWrapper(ConfigController.getConfig)
|
||||
);
|
||||
|
||||
router.post(
|
||||
"/save",
|
||||
RateLimit.configUpdate,
|
||||
authenticateRequest,
|
||||
authenticateRequest(),
|
||||
requestValidation({
|
||||
body: {
|
||||
config: configSchema,
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
const joi = require("joi");
|
||||
const { authenticateRequest } = require("../../middlewares/auth");
|
||||
const LeaderboardsController = require("../controllers/leaderboards");
|
||||
const RateLimit = require("../../middlewares/rate-limit");
|
||||
|
|
@ -13,13 +14,32 @@ const router = Router();
|
|||
router.get(
|
||||
"/",
|
||||
RateLimit.leaderboardsGet,
|
||||
authenticateRequest({ isPublic: true }),
|
||||
requestValidation({
|
||||
query: {
|
||||
language: joi.string().required(),
|
||||
mode: joi.string().required(),
|
||||
mode2: joi.string().required(),
|
||||
skip: joi.number().min(0).required(),
|
||||
limit: joi.number(),
|
||||
},
|
||||
validationErrorMessage: "Missing parameters",
|
||||
}),
|
||||
asyncHandlerWrapper(LeaderboardsController.get)
|
||||
);
|
||||
|
||||
router.get(
|
||||
"/rank",
|
||||
RateLimit.leaderboardsGet,
|
||||
authenticateRequest,
|
||||
authenticateRequest(),
|
||||
requestValidation({
|
||||
query: {
|
||||
language: joi.string().required(),
|
||||
mode: joi.string().required(),
|
||||
mode2: joi.string().required(),
|
||||
},
|
||||
validationErrorMessage: "Missing parameters",
|
||||
}),
|
||||
asyncHandlerWrapper(LeaderboardsController.getRank)
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
const joi = require("joi");
|
||||
const { authenticateRequest } = require("../../middlewares/auth");
|
||||
const PresetController = require("../controllers/preset");
|
||||
const RateLimit = require("../../middlewares/rate-limit");
|
||||
const configSchema = require("../schemas/config-schema");
|
||||
const {
|
||||
asyncHandlerWrapper,
|
||||
requestValidation,
|
||||
|
|
@ -10,31 +12,65 @@ const { Router } = require("express");
|
|||
|
||||
const router = Router();
|
||||
|
||||
const presetNameSchema = joi
|
||||
.string()
|
||||
.required()
|
||||
.regex(/^[0-9a-zA-Z_.-]+$/)
|
||||
.max(16)
|
||||
.messages({
|
||||
"string.pattern.base": "Invalid preset name",
|
||||
"string.max": "Preset name exceeds maximum of 16 characters",
|
||||
});
|
||||
|
||||
router.get(
|
||||
"/",
|
||||
RateLimit.presetsGet,
|
||||
authenticateRequest,
|
||||
authenticateRequest(),
|
||||
asyncHandlerWrapper(PresetController.getPresets)
|
||||
);
|
||||
|
||||
router.post(
|
||||
"/add",
|
||||
RateLimit.presetsAdd,
|
||||
authenticateRequest,
|
||||
authenticateRequest(),
|
||||
requestValidation({
|
||||
body: {
|
||||
name: presetNameSchema,
|
||||
config: configSchema.keys({
|
||||
tags: joi.array().items(joi.string()),
|
||||
}),
|
||||
},
|
||||
}),
|
||||
asyncHandlerWrapper(PresetController.addPreset)
|
||||
);
|
||||
|
||||
router.post(
|
||||
"/edit",
|
||||
RateLimit.presetsEdit,
|
||||
authenticateRequest,
|
||||
authenticateRequest(),
|
||||
requestValidation({
|
||||
body: {
|
||||
_id: joi.string().required(),
|
||||
name: presetNameSchema,
|
||||
config: configSchema
|
||||
.keys({
|
||||
tags: joi.array().items(joi.string()),
|
||||
})
|
||||
.allow(null),
|
||||
},
|
||||
}),
|
||||
asyncHandlerWrapper(PresetController.editPreset)
|
||||
);
|
||||
|
||||
router.post(
|
||||
"/remove",
|
||||
RateLimit.presetsRemove,
|
||||
authenticateRequest,
|
||||
authenticateRequest(),
|
||||
requestValidation({
|
||||
body: {
|
||||
_id: joi.string().required(),
|
||||
},
|
||||
}),
|
||||
asyncHandlerWrapper(PresetController.removePreset)
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ const quotesRouter = Router();
|
|||
quotesRouter.get(
|
||||
"/",
|
||||
RateLimit.newQuotesGet,
|
||||
authenticateRequest,
|
||||
authenticateRequest(),
|
||||
asyncHandlerWrapper(NewQuotesController.getQuotes)
|
||||
);
|
||||
|
||||
|
|
@ -31,7 +31,7 @@ quotesRouter.post(
|
|||
"Quote submission is disabled temporarily. The queue is quite long and we need some time to catch up.",
|
||||
}),
|
||||
RateLimit.newQuotesAdd,
|
||||
authenticateRequest,
|
||||
authenticateRequest(),
|
||||
requestValidation({
|
||||
body: {
|
||||
text: joi.string().min(60).required(),
|
||||
|
|
@ -47,7 +47,7 @@ quotesRouter.post(
|
|||
quotesRouter.post(
|
||||
"/approve",
|
||||
RateLimit.newQuotesAction,
|
||||
authenticateRequest,
|
||||
authenticateRequest(),
|
||||
requestValidation({
|
||||
body: {
|
||||
quoteId: joi.string().required(),
|
||||
|
|
@ -62,7 +62,7 @@ quotesRouter.post(
|
|||
quotesRouter.post(
|
||||
"/reject",
|
||||
RateLimit.newQuotesAction,
|
||||
authenticateRequest,
|
||||
authenticateRequest(),
|
||||
requestValidation({
|
||||
body: {
|
||||
quoteId: joi.string().required(),
|
||||
|
|
@ -74,7 +74,7 @@ quotesRouter.post(
|
|||
quotesRouter.get(
|
||||
"/rating",
|
||||
RateLimit.quoteRatingsGet,
|
||||
authenticateRequest,
|
||||
authenticateRequest(),
|
||||
requestValidation({
|
||||
query: {
|
||||
quoteId: joi.string().regex(/^\d+$/).required(),
|
||||
|
|
@ -87,7 +87,7 @@ quotesRouter.get(
|
|||
quotesRouter.post(
|
||||
"/rating",
|
||||
RateLimit.quoteRatingsSubmit,
|
||||
authenticateRequest,
|
||||
authenticateRequest(),
|
||||
requestValidation({
|
||||
body: {
|
||||
quoteId: joi.number().required(),
|
||||
|
|
@ -107,7 +107,7 @@ quotesRouter.post(
|
|||
invalidMessage: "Quote reporting is unavailable.",
|
||||
}),
|
||||
RateLimit.quoteReportSubmit,
|
||||
authenticateRequest,
|
||||
authenticateRequest(),
|
||||
requestValidation({
|
||||
body: {
|
||||
quoteId: joi.string().required(),
|
||||
|
|
|
|||
|
|
@ -12,28 +12,28 @@ const router = Router();
|
|||
router.get(
|
||||
"/",
|
||||
RateLimit.resultsGet,
|
||||
authenticateRequest,
|
||||
authenticateRequest(),
|
||||
asyncHandlerWrapper(ResultController.getResults)
|
||||
);
|
||||
|
||||
router.post(
|
||||
"/add",
|
||||
RateLimit.resultsAdd,
|
||||
authenticateRequest,
|
||||
authenticateRequest(),
|
||||
asyncHandlerWrapper(ResultController.addResult)
|
||||
);
|
||||
|
||||
router.post(
|
||||
"/updateTags",
|
||||
RateLimit.resultsTagsUpdate,
|
||||
authenticateRequest,
|
||||
authenticateRequest(),
|
||||
asyncHandlerWrapper(ResultController.updateTags)
|
||||
);
|
||||
|
||||
router.post(
|
||||
"/deleteAll",
|
||||
RateLimit.resultsDeleteAll,
|
||||
authenticateRequest,
|
||||
authenticateRequest(),
|
||||
asyncHandlerWrapper(ResultController.deleteAll)
|
||||
);
|
||||
|
||||
|
|
@ -46,7 +46,7 @@ router.get(
|
|||
router.post(
|
||||
"/checkLeaderboardQualification",
|
||||
RateLimit.resultsLeaderboardQualificationGet,
|
||||
authenticateRequest,
|
||||
authenticateRequest(),
|
||||
asyncHandlerWrapper(ResultController.checkLeaderboardQualification)
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -12,14 +12,14 @@ const router = Router();
|
|||
router.get(
|
||||
"/",
|
||||
RateLimit.userGet,
|
||||
authenticateRequest,
|
||||
authenticateRequest(),
|
||||
asyncHandlerWrapper(UserController.getUser)
|
||||
);
|
||||
|
||||
router.post(
|
||||
"/signup",
|
||||
RateLimit.userSignup,
|
||||
authenticateRequest,
|
||||
authenticateRequest(),
|
||||
asyncHandlerWrapper(UserController.createNewUser)
|
||||
);
|
||||
|
||||
|
|
@ -32,84 +32,84 @@ router.post(
|
|||
router.post(
|
||||
"/delete",
|
||||
RateLimit.userDelete,
|
||||
authenticateRequest,
|
||||
authenticateRequest(),
|
||||
asyncHandlerWrapper(UserController.deleteUser)
|
||||
);
|
||||
|
||||
router.post(
|
||||
"/updateName",
|
||||
RateLimit.userUpdateName,
|
||||
authenticateRequest,
|
||||
authenticateRequest(),
|
||||
asyncHandlerWrapper(UserController.updateName)
|
||||
);
|
||||
|
||||
router.post(
|
||||
"/updateLbMemory",
|
||||
RateLimit.userUpdateLBMemory,
|
||||
authenticateRequest,
|
||||
authenticateRequest(),
|
||||
asyncHandlerWrapper(UserController.updateLbMemory)
|
||||
);
|
||||
|
||||
router.post(
|
||||
"/updateEmail",
|
||||
RateLimit.userUpdateEmail,
|
||||
authenticateRequest,
|
||||
authenticateRequest(),
|
||||
asyncHandlerWrapper(UserController.updateEmail)
|
||||
);
|
||||
|
||||
router.post(
|
||||
"/clearPb",
|
||||
RateLimit.userClearPB,
|
||||
authenticateRequest,
|
||||
authenticateRequest(),
|
||||
asyncHandlerWrapper(UserController.clearPb)
|
||||
);
|
||||
|
||||
router.post(
|
||||
"/tags/add",
|
||||
RateLimit.userTagsAdd,
|
||||
authenticateRequest,
|
||||
authenticateRequest(),
|
||||
asyncHandlerWrapper(UserController.addTag)
|
||||
);
|
||||
|
||||
router.get(
|
||||
"/tags",
|
||||
RateLimit.userTagsGet,
|
||||
authenticateRequest,
|
||||
authenticateRequest(),
|
||||
asyncHandlerWrapper(UserController.getTags)
|
||||
);
|
||||
|
||||
router.post(
|
||||
"/tags/clearPb",
|
||||
RateLimit.userTagsClearPB,
|
||||
authenticateRequest,
|
||||
authenticateRequest(),
|
||||
asyncHandlerWrapper(UserController.clearTagPb)
|
||||
);
|
||||
|
||||
router.post(
|
||||
"/tags/remove",
|
||||
RateLimit.userTagsRemove,
|
||||
authenticateRequest,
|
||||
authenticateRequest(),
|
||||
asyncHandlerWrapper(UserController.removeTag)
|
||||
);
|
||||
|
||||
router.post(
|
||||
"/tags/edit",
|
||||
RateLimit.userTagsEdit,
|
||||
authenticateRequest,
|
||||
authenticateRequest(),
|
||||
asyncHandlerWrapper(UserController.editTag)
|
||||
);
|
||||
|
||||
router.post(
|
||||
"/discord/link",
|
||||
RateLimit.userDiscordLink,
|
||||
authenticateRequest,
|
||||
authenticateRequest(),
|
||||
asyncHandlerWrapper(UserController.linkDiscord)
|
||||
);
|
||||
|
||||
router.post(
|
||||
"/discord/unlink",
|
||||
RateLimit.userDiscordUnlink,
|
||||
authenticateRequest,
|
||||
authenticateRequest(),
|
||||
asyncHandlerWrapper(UserController.unlinkDiscord)
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ function validateConfiguration(options) {
|
|||
const { criteria, invalidMessage } = options;
|
||||
|
||||
return (req, res, next) => {
|
||||
const configuration = req.context.configuration;
|
||||
const configuration = req.ctx.configuration;
|
||||
|
||||
const validated = criteria(configuration);
|
||||
if (!validated) {
|
||||
|
|
@ -78,7 +78,7 @@ function requestValidation(validationSchema) {
|
|||
throw new MonkeyError(
|
||||
500,
|
||||
validationErrorMessage ??
|
||||
`Invalid request: ${errorMessage} (value ${error.details[0].context.value})`
|
||||
`${errorMessage} (${error.details[0].context.value})`
|
||||
);
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,47 +1,83 @@
|
|||
const MonkeyError = require("../handlers/error");
|
||||
const { verifyIdToken } = require("../handlers/auth");
|
||||
|
||||
module.exports = {
|
||||
async authenticateRequest(req, res, next) {
|
||||
const DEFAULT_OPTIONS = {
|
||||
isPublic: false,
|
||||
};
|
||||
|
||||
function authenticateRequest(options = DEFAULT_OPTIONS) {
|
||||
return async (req, _res, next) => {
|
||||
try {
|
||||
if (process.env.MODE === "dev" && !req.headers.authorization) {
|
||||
if (req.body.uid) {
|
||||
req.decodedToken = {
|
||||
uid: req.body.uid,
|
||||
};
|
||||
console.log("Running authorization in dev mode");
|
||||
return next();
|
||||
} else {
|
||||
throw new MonkeyError(
|
||||
400,
|
||||
"Running authorization in dev mode but still no uid was provided"
|
||||
);
|
||||
}
|
||||
}
|
||||
const { authorization } = req.headers;
|
||||
if (!authorization)
|
||||
const { authorization: authHeader } = req.headers;
|
||||
let token = null;
|
||||
|
||||
if (authHeader) {
|
||||
token = await authenticateWithAuthHeader(authHeader);
|
||||
} else if (options.isPublic) {
|
||||
return next();
|
||||
} else if (process.env.MODE === "dev") {
|
||||
token = authenticateWithBody(req.body);
|
||||
} else {
|
||||
throw new MonkeyError(
|
||||
401,
|
||||
"Unauthorized",
|
||||
`endpoint: ${req.baseUrl} no authorization header found`
|
||||
);
|
||||
const token = authorization.split(" ");
|
||||
if (token[0].trim() !== "Bearer")
|
||||
return next(
|
||||
new MonkeyError(400, "Invalid Token", "Incorrect token type")
|
||||
);
|
||||
try {
|
||||
req.decodedToken = await verifyIdToken(token[1]);
|
||||
} catch (err) {
|
||||
if (err.message == "auth/id-token-expired") {
|
||||
new MonkeyError(401, "Unauthorized", "Token expired");
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
return next();
|
||||
} catch (e) {
|
||||
return next(e);
|
||||
|
||||
req.ctx.decodedToken = token;
|
||||
} catch (error) {
|
||||
return next(error);
|
||||
}
|
||||
},
|
||||
|
||||
next();
|
||||
};
|
||||
}
|
||||
|
||||
function authenticateWithBody(body) {
|
||||
const { uid } = body;
|
||||
|
||||
if (!uid) {
|
||||
throw new MonkeyError(
|
||||
400,
|
||||
"Running authorization in dev mode but still no uid was provided"
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
uid,
|
||||
};
|
||||
}
|
||||
|
||||
async function authenticateWithAuthHeader(authHeader) {
|
||||
const token = authHeader.split(" ");
|
||||
|
||||
const authScheme = token[0].trim();
|
||||
const credentials = token[1];
|
||||
|
||||
if (authScheme === "Bearer") {
|
||||
return await authenticateWithBearerToken(credentials);
|
||||
}
|
||||
|
||||
throw new MonkeyError(
|
||||
400,
|
||||
"Unknown authentication scheme",
|
||||
`The authentication scheme "${authScheme}" is not implemented.`
|
||||
);
|
||||
}
|
||||
|
||||
async function authenticateWithBearerToken(token) {
|
||||
try {
|
||||
return await verifyIdToken(token);
|
||||
} catch (error) {
|
||||
if (error.message.includes("auth/id-token-expired")) {
|
||||
throw new MonkeyError(401, "Unauthorized", "Token expired");
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
authenticateRequest,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3,8 +3,11 @@ const ConfigurationDAO = require("../dao/configuration");
|
|||
async function contextMiddleware(req, res, next) {
|
||||
const configuration = await ConfigurationDAO.getCachedConfiguration(true);
|
||||
|
||||
req.context = {
|
||||
req.ctx = {
|
||||
configuration,
|
||||
decodedToken: {
|
||||
uid: null,
|
||||
},
|
||||
};
|
||||
|
||||
next();
|
||||
|
|
|
|||
|
|
@ -26,10 +26,7 @@ app.set("trust proxy", 1);
|
|||
app.use(contextMiddleware);
|
||||
|
||||
app.use((req, res, next) => {
|
||||
if (
|
||||
process.env.MAINTENANCE === "true" ||
|
||||
req.context.configuration.maintenance
|
||||
) {
|
||||
if (process.env.MAINTENANCE === "true" || req.ctx.configuration.maintenance) {
|
||||
res.status(503).json({ message: "Server is down for maintenance" });
|
||||
} else {
|
||||
next();
|
||||
|
|
@ -53,8 +50,8 @@ app.use(function (e, req, res, _next) {
|
|||
//its a server error
|
||||
monkeyError = new MonkeyError(e.status, e.message, e.stack);
|
||||
}
|
||||
if (!monkeyError.uid && req.decodedToken) {
|
||||
monkeyError.uid = req.decodedToken.uid;
|
||||
if (!monkeyError.uid && req.ctx?.decodedToken) {
|
||||
monkeyError.uid = req.ctx.decodedToken.uid;
|
||||
}
|
||||
if (process.env.MODE !== "dev" && monkeyError.status > 400) {
|
||||
Logger.log(
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue