From 0248e4e5de2f566a2113f46a3fe9a7c8d186b30a Mon Sep 17 00:00:00 2001 From: zkldi Date: Wed, 16 Jun 2021 16:32:10 +0100 Subject: [PATCH] rename SYMBOL_TachiAPIData to TachiAPIAuth --- server/@types/express/index.d.ts | 4 +- server/src/lib/constants/tachi.ts | 2 +- server/src/server/middleware/auth.test.ts | 8 ++-- server/src/server/middleware/auth.ts | 18 ++++---- .../src/server/router/api/v1/status/router.ts | 6 +-- .../games/!game/!playtype/middleware.ts | 40 +++++++++++++++++ .../!userID/games/!game/!playtype/router.ts | 24 +++++++++++ .../router/api/v1/users/!userID/middleware.ts | 25 +++++++++++ .../router/api/v1/users/!userID/router.ts | 43 +++++++++++++++++++ .../src/server/router/api/v1/users/router.ts | 19 ++++++++ .../src/server/router/ir/barbatos/router.ts | 4 +- .../router/ir/beatoraja/charts/router.ts | 4 +- .../src/server/router/ir/beatoraja/router.ts | 6 +-- .../src/server/router/ir/chunitachi/router.ts | 4 +- .../server/router/ir/direct-manual/router.ts | 4 +- .../src/server/router/ir/fervidex/router.ts | 24 +++++------ server/src/server/router/ir/usc/router.ts | 10 ++--- server/src/utils/misc.ts | 11 +++++ server/src/utils/types.ts | 5 ++- 19 files changed, 213 insertions(+), 48 deletions(-) create mode 100644 server/src/server/router/api/v1/users/!userID/games/!game/!playtype/middleware.ts create mode 100644 server/src/server/router/api/v1/users/!userID/games/!game/!playtype/router.ts create mode 100644 server/src/server/router/api/v1/users/!userID/middleware.ts create mode 100644 server/src/server/router/api/v1/users/!userID/router.ts create mode 100644 server/src/server/router/api/v1/users/router.ts diff --git a/server/@types/express/index.d.ts b/server/@types/express/index.d.ts index c8f799876..90feec3d6 100644 --- a/server/@types/express/index.d.ts +++ b/server/@types/express/index.d.ts @@ -1,5 +1,5 @@ import { Session, SessionData } from "express-session"; -import { SYMBOL_TachiAPIData, SYMBOL_TachiData } from "../../src/lib/constants/tachi"; +import { SYMBOL_TachiAPIAuth, SYMBOL_TachiData } from "../../src/lib/constants/tachi"; import { TachiRequestData, TachiSessionData } from "../../src/utils/types"; import { APITokenDocument } from "tachi-common"; @@ -21,7 +21,7 @@ declare global { [SYMBOL_TachiData]?: Partial; // even though this is technically *not* present on every request // it's always assigned in the main router, so its functionally equivalent. - [SYMBOL_TachiAPIData]: APITokenDocument; + [SYMBOL_TachiAPIAuth]: APITokenDocument; } } } diff --git a/server/src/lib/constants/tachi.ts b/server/src/lib/constants/tachi.ts index 3657780f4..50d466343 100644 --- a/server/src/lib/constants/tachi.ts +++ b/server/src/lib/constants/tachi.ts @@ -1,2 +1,2 @@ export const SYMBOL_TachiData = Symbol("tachi-request-patch"); -export const SYMBOL_TachiAPIData = Symbol("tachi-api-auth-info"); +export const SYMBOL_TachiAPIAuth = Symbol("tachi-api-auth-info"); diff --git a/server/src/server/middleware/auth.test.ts b/server/src/server/middleware/auth.test.ts index 70cb6c8a4..ce38eadc2 100644 --- a/server/src/server/middleware/auth.test.ts +++ b/server/src/server/middleware/auth.test.ts @@ -2,7 +2,7 @@ import t from "tap"; import { SetRequestPermissions, AllPermissions, RequireNotGuest, RequirePermissions } from "./auth"; import expMiddlewareMock from "express-request-mock"; import db from "../../external/mongo/db"; -import { SYMBOL_TachiAPIData } from "../../lib/constants/tachi"; +import { SYMBOL_TachiAPIAuth } from "../../lib/constants/tachi"; import { CloseAllConnections } from "../../test-utils/close-connections"; t.test("#SetRequestPermissions", (t) => { @@ -22,7 +22,7 @@ t.test("#SetRequestPermissions", (t) => { }, }); - t.strictSame(req[SYMBOL_TachiAPIData], { + t.strictSame(req[SYMBOL_TachiAPIAuth], { userID: 1, identifier: "Mock API Token", permissions: { @@ -37,7 +37,7 @@ t.test("#SetRequestPermissions", (t) => { t.test("Should assign guest APIKey information if no auth present", async (t) => { const { req } = await expMiddlewareMock(SetRequestPermissions); - t.strictSame(req[SYMBOL_TachiAPIData], { + t.strictSame(req[SYMBOL_TachiAPIAuth], { userID: null, identifier: "Guest Token", permissions: {}, @@ -101,7 +101,7 @@ t.test("#SetRequestPermissions", (t) => { }, }); - t.strictSame(req[SYMBOL_TachiAPIData], { + t.strictSame(req[SYMBOL_TachiAPIAuth], { userID: 1, identifier: `Session-Key 1`, token: null, diff --git a/server/src/server/middleware/auth.ts b/server/src/server/middleware/auth.ts index 255d4a34a..5e5a3f6b8 100644 --- a/server/src/server/middleware/auth.ts +++ b/server/src/server/middleware/auth.ts @@ -1,6 +1,6 @@ import { RequestHandler } from "express"; import db from "../../external/mongo/db"; -import { SYMBOL_TachiAPIData } from "../../lib/constants/tachi"; +import { SYMBOL_TachiAPIAuth } from "../../lib/constants/tachi"; import { SplitAuthorizationHeader } from "../../utils/misc"; import { APITokenDocument, APIPermissions } from "tachi-common"; import CreateLogCtx from "../../lib/logger/logger"; @@ -36,7 +36,7 @@ export const AllPermissions: Record = { */ export const SetRequestPermissions: RequestHandler = async (req, res, next) => { if (req.session?.tachi?.userID) { - req[SYMBOL_TachiAPIData] = { + req[SYMBOL_TachiAPIAuth] = { userID: req.session.tachi.userID, identifier: `Session-Key ${req.session.tachi.userID}`, token: null, @@ -49,7 +49,7 @@ export const SetRequestPermissions: RequestHandler = async (req, res, next) => { // if no auth was attempted, default to the guest token. if (!header) { - req[SYMBOL_TachiAPIData] = GuestToken; + req[SYMBOL_TachiAPIAuth] = GuestToken; return next(); } @@ -80,7 +80,7 @@ export const SetRequestPermissions: RequestHandler = async (req, res, next) => { }); } - req[SYMBOL_TachiAPIData] = { + req[SYMBOL_TachiAPIAuth] = { userID: apiTokenData.userID, token, permissions: apiTokenData.permissions, @@ -98,7 +98,7 @@ export const SetRequestPermissions: RequestHandler = async (req, res, next) => { export const RequirePermissions = (...perms: APIPermissions[]): RequestHandler => (req, res, next) => { - if (!req[SYMBOL_TachiAPIData]) { + if (!req[SYMBOL_TachiAPIAuth]) { logger.error(`RequirePermissions middleware was hit without any TachiAPIData?`); return res.status(500).json({ @@ -109,7 +109,7 @@ export const RequirePermissions = const missingPerms = []; for (const perm of perms) { - if (!req[SYMBOL_TachiAPIData]!.permissions[perm]) { + if (!req[SYMBOL_TachiAPIAuth]!.permissions[perm]) { missingPerms.push(perm); } } @@ -117,7 +117,7 @@ export const RequirePermissions = if (missingPerms.length > 0) { logger.info( `IP ${req.ip} - userID ${ - req[SYMBOL_TachiAPIData].userID + req[SYMBOL_TachiAPIAuth].userID } had insufficient permissions for request ${req.method} ${ req.url }. ${missingPerms.join(", ")}` @@ -134,7 +134,7 @@ export const RequirePermissions = }; export const RequireNotGuest: RequestHandler = (req, res, next) => { - if (!req[SYMBOL_TachiAPIData]) { + if (!req[SYMBOL_TachiAPIAuth]) { logger.error(`RequirePermissions middleware was hit without any TachiAPIData?`); return res.status(500).json({ success: false, @@ -142,7 +142,7 @@ export const RequireNotGuest: RequestHandler = (req, res, next) => { }); } - if (!req[SYMBOL_TachiAPIData].userID) { + if (!req[SYMBOL_TachiAPIAuth].userID) { logger.info(`Request to ${req.method} ${req.url} was attempted by guest.`); return res.status(401).json({ success: false, diff --git a/server/src/server/router/api/v1/status/router.ts b/server/src/server/router/api/v1/status/router.ts index c6e2097b4..21e81694b 100644 --- a/server/src/server/router/api/v1/status/router.ts +++ b/server/src/server/router/api/v1/status/router.ts @@ -1,5 +1,5 @@ import { Router } from "express"; -import { SYMBOL_TachiAPIData } from "../../../../../lib/constants/tachi"; +import { SYMBOL_TachiAPIAuth } from "../../../../../lib/constants/tachi"; import { FormatVersion } from "../../../../../lib/constants/version"; const router: Router = Router({ mergeParams: true }); @@ -17,7 +17,7 @@ router.get("/", (req, res) => { serverTime: Date.now(), version: FormatVersion(), // converts {foo: true, bar: false, baz: true} into [foo, baz] - permissions: Object.entries(req[SYMBOL_TachiAPIData].permissions) + permissions: Object.entries(req[SYMBOL_TachiAPIAuth].permissions) .filter((e) => e[1]) .map((e) => e[0]), echo, @@ -38,7 +38,7 @@ router.post("/", (req, res) => { serverTime: Date.now(), version: FormatVersion(), // converts {foo: true, bar: false, baz: true} into [foo, baz] - permissions: Object.entries(req[SYMBOL_TachiAPIData].permissions) + permissions: Object.entries(req[SYMBOL_TachiAPIAuth].permissions) .filter((e) => e[1]) .map((e) => e[0]), echo, diff --git a/server/src/server/router/api/v1/users/!userID/games/!game/!playtype/middleware.ts b/server/src/server/router/api/v1/users/!userID/games/!game/!playtype/middleware.ts new file mode 100644 index 000000000..de5335e8f --- /dev/null +++ b/server/src/server/router/api/v1/users/!userID/games/!game/!playtype/middleware.ts @@ -0,0 +1,40 @@ +import { RequestHandler } from "express"; +import db from "../../../../../../../../../external/mongo/db"; +import { SYMBOL_TachiData } from "../../../../../../../../../lib/constants/tachi"; +import { IsValidGame, IsValidPlaytype } from "../../../../../../../../../utils/misc"; +import { AssignToReqTachiData } from "../../../../../../../../../utils/req-tachi-data"; + +export const CheckUserPlayedGamePlaytype: RequestHandler = async (req, res, next) => { + const user = req[SYMBOL_TachiData]!.requestedUser!; + + if (!IsValidGame(req.params.game)) { + return res.status(400).json({ + success: false, + description: `The game ${req.params.game} is not supported.`, + }); + } + + if (!IsValidPlaytype(req.params.game, req.params.playtype)) { + return res.status(400).json({ + success: false, + description: `The game ${req.params.game} does not have a playtype called ${req.params.playtype}.`, + }); + } + + const stats = await db["game-stats"].findOne({ + userID: user.id, + game: req.params.game, + playtype: req.params.playtype, + }); + + if (!stats) { + return res.status(404).json({ + success: false, + description: `The user ${user.username} has not played ${req.params.game} (${req.params.playtype})`, + }); + } + + AssignToReqTachiData(req, { requestedUserGameStats: stats }); + + return next(); +}; diff --git a/server/src/server/router/api/v1/users/!userID/games/!game/!playtype/router.ts b/server/src/server/router/api/v1/users/!userID/games/!game/!playtype/router.ts new file mode 100644 index 000000000..b2383b16e --- /dev/null +++ b/server/src/server/router/api/v1/users/!userID/games/!game/!playtype/router.ts @@ -0,0 +1,24 @@ +import { Router } from "express"; +import { SYMBOL_TachiData } from "../../../../../../../../../lib/constants/tachi"; +import { CheckUserPlayedGamePlaytype } from "./middleware"; + +const router: Router = Router({ mergeParams: true }); + +router.use(CheckUserPlayedGamePlaytype); + +/** + * Returns information about a user for this game + playtype. + * @name GET /api/v1/users/:userID/games/:game/:playtype + */ +router.use("/", async (req, res) => { + const user = req[SYMBOL_TachiData]!.requestedUser!; + const stats = req[SYMBOL_TachiData]!.requestedUserGameStats!; + + return res.status(200).json({ + success: true, + description: `Retrieved user statistics for ${user.username} (${req.params.game} ${req.params.playtype})`, + body: + }); +}); + +export default router; diff --git a/server/src/server/router/api/v1/users/!userID/middleware.ts b/server/src/server/router/api/v1/users/!userID/middleware.ts new file mode 100644 index 000000000..75ccc73c9 --- /dev/null +++ b/server/src/server/router/api/v1/users/!userID/middleware.ts @@ -0,0 +1,25 @@ +import { RequestHandler } from "express"; +import { AssignToReqTachiData } from "../../../../../../utils/req-tachi-data"; +import { ResolveUser } from "../../../../../../utils/user"; + +export const GetUserFromParam: RequestHandler = async (req, res, next) => { + if (!req.params.userID) { + return res.status(400).json({ + success: false, + description: "No userID given.", + }); + } + + const user = await ResolveUser(req.params.userID); + + if (!user) { + return res.status(404).json({ + success: false, + description: `The user ${req.params.userID} does not exist.`, + }); + } + + AssignToReqTachiData(req, { requestedUser: user }); + + return next(); +}; diff --git a/server/src/server/router/api/v1/users/!userID/router.ts b/server/src/server/router/api/v1/users/!userID/router.ts new file mode 100644 index 000000000..7d6195f77 --- /dev/null +++ b/server/src/server/router/api/v1/users/!userID/router.ts @@ -0,0 +1,43 @@ +import { Router } from "express"; +import db from "../../../../../../external/mongo/db"; +import { SYMBOL_TachiData } from "../../../../../../lib/constants/tachi"; +import { GetUserFromParam } from "./middleware"; + +const router: Router = Router({ mergeParams: true }); + +router.use(GetUserFromParam); + +/** + * Get the user at this ID or name. + * @name GET /api/v1/users/:userID + */ +router.get("/", (req, res) => { + const user = req[SYMBOL_TachiData]!.requestedUser!; + + return res.status(200).json({ + success: true, + description: `Found user ${user.username}.`, + body: user, + }); +}); + +/** + * Returns all of the game-stats this user has. + * This endpoint doubles up as a way of checking what games a user has played. + * + * @name GET /api/v1/users/:userID/stats + */ +router.get("/stats", async (req, res) => { + const user = req[SYMBOL_TachiData]!.requestedUser!; + + // a user has played a game if and only if they have stats for it. + const stats = await db["game-stats"].find({ userID: user.id }); + + return res.status(200).json({ + success: true, + description: `Returned ${stats.length} stats objects.`, + body: stats, + }); +}); + +export default router; diff --git a/server/src/server/router/api/v1/users/router.ts b/server/src/server/router/api/v1/users/router.ts new file mode 100644 index 000000000..cb78cb4a2 --- /dev/null +++ b/server/src/server/router/api/v1/users/router.ts @@ -0,0 +1,19 @@ +import { Router } from "express"; +import db from "../../../../../external/mongo/db"; +import userIDRouter from "./!userID/router"; + +const router: Router = Router({ mergeParams: true }); + +/** + * List Users. + * @param online - Only return online users. + * @param username - Return users similar to this username. + * @name GET /api/v1/users + */ +router.get("/", async (req, res) => { + // i dont know yet. +}); + +router.use("/:userID", userIDRouter); + +export default router; diff --git a/server/src/server/router/ir/barbatos/router.ts b/server/src/server/router/ir/barbatos/router.ts index 6c1dc656a..5ba326bff 100644 --- a/server/src/server/router/ir/barbatos/router.ts +++ b/server/src/server/router/ir/barbatos/router.ts @@ -3,7 +3,7 @@ import { GetUserWithIDGuaranteed } from "../../../../utils/user"; import { ExpressWrappedScoreImportMain } from "../../../../lib/score-import/framework/express-wrapper"; import { ParseBarbatosSingle } from "../../../../lib/score-import/import-types/ir/barbatos/parser"; import { RequirePermissions } from "../../../middleware/auth"; -import { SYMBOL_TachiAPIData } from "../../../../lib/constants/tachi"; +import { SYMBOL_TachiAPIAuth } from "../../../../lib/constants/tachi"; const router: Router = Router({ mergeParams: true }); @@ -14,7 +14,7 @@ router.use(RequirePermissions("submit:score")); * @name POST /ir/barbatos/score/submit */ router.post("/score/submit", async (req, res) => { - const userDoc = await GetUserWithIDGuaranteed(req[SYMBOL_TachiAPIData]!.userID!); + const userDoc = await GetUserWithIDGuaranteed(req[SYMBOL_TachiAPIAuth]!.userID!); const responseData = await ExpressWrappedScoreImportMain( userDoc, diff --git a/server/src/server/router/ir/beatoraja/charts/router.ts b/server/src/server/router/ir/beatoraja/charts/router.ts index f91325a65..ee50500dd 100644 --- a/server/src/server/router/ir/beatoraja/charts/router.ts +++ b/server/src/server/router/ir/beatoraja/charts/router.ts @@ -1,7 +1,7 @@ import { ChartDocument, PBScoreDocument } from "tachi-common"; import { Router, RequestHandler } from "express"; import db from "../../../../../external/mongo/db"; -import { SYMBOL_TachiAPIData, SYMBOL_TachiData } from "../../../../../lib/constants/tachi"; +import { SYMBOL_TachiAPIAuth, SYMBOL_TachiData } from "../../../../../lib/constants/tachi"; import { TachiPBScoreToBeatorajaFormat } from "./convert-scores"; import { AssignToReqTachiData } from "../../../../../utils/req-tachi-data"; @@ -41,7 +41,7 @@ router.get("/scores", async (req, res) => { // @optimisable - This should be solved with a couple queries and a hashmap. const beatorajaScores = await Promise.all( scores.map((e) => - TachiPBScoreToBeatorajaFormat(e, chart, req[SYMBOL_TachiAPIData]!.userID!) + TachiPBScoreToBeatorajaFormat(e, chart, req[SYMBOL_TachiAPIAuth]!.userID!) ) ); diff --git a/server/src/server/router/ir/beatoraja/router.ts b/server/src/server/router/ir/beatoraja/router.ts index f6e2e3929..e9d839e24 100644 --- a/server/src/server/router/ir/beatoraja/router.ts +++ b/server/src/server/router/ir/beatoraja/router.ts @@ -1,6 +1,6 @@ import { Router } from "express"; import db from "../../../../external/mongo/db"; -import { SYMBOL_TachiAPIData } from "../../../../lib/constants/tachi"; +import { SYMBOL_TachiAPIAuth } from "../../../../lib/constants/tachi"; import CreateLogCtx, { KtLogger } from "../../../../lib/logger/logger"; import { ExpressWrappedScoreImportMain } from "../../../../lib/score-import/framework/express-wrapper"; import { ParseBeatorajaSingle } from "../../../../lib/score-import/import-types/ir/beatoraja/parser"; @@ -21,7 +21,7 @@ router.use(ValidateIRClientVersion); * @name POST /ir/beatoraja/submit-score */ router.post("/submit-score", async (req, res) => { - const userDoc = await GetUserWithIDGuaranteed(req[SYMBOL_TachiAPIData]!.userID!); + const userDoc = await GetUserWithIDGuaranteed(req[SYMBOL_TachiAPIAuth]!.userID!); const ParserFunction = (logger: KtLogger) => ParseBeatorajaSingle(req.body, logger); @@ -164,7 +164,7 @@ router.post("/submit-course", async (req, res) => { }); } - const userID = req[SYMBOL_TachiAPIData]!.userID!; + const userID = req[SYMBOL_TachiAPIAuth]!.userID!; const result = await UpdateClassIfGreater( userID, diff --git a/server/src/server/router/ir/chunitachi/router.ts b/server/src/server/router/ir/chunitachi/router.ts index 0727c50a7..9b531028e 100644 --- a/server/src/server/router/ir/chunitachi/router.ts +++ b/server/src/server/router/ir/chunitachi/router.ts @@ -3,7 +3,7 @@ import { GetUserWithIDGuaranteed } from "../../../../utils/user"; import { ExpressWrappedScoreImportMain } from "../../../../lib/score-import/framework/express-wrapper"; import ParseDirectManual from "../../../../lib/score-import/import-types/ir/direct-manual/parser"; import { RequirePermissions } from "../../../middleware/auth"; -import { SYMBOL_TachiAPIData } from "../../../../lib/constants/tachi"; +import { SYMBOL_TachiAPIAuth } from "../../../../lib/constants/tachi"; const router: Router = Router({ mergeParams: true }); @@ -12,7 +12,7 @@ const router: Router = Router({ mergeParams: true }); * @name POST /ir/chunitachi/score/submit */ router.post("/import", RequirePermissions("submit:score"), async (req, res) => { - const userDoc = await GetUserWithIDGuaranteed(req[SYMBOL_TachiAPIData].userID!); + const userDoc = await GetUserWithIDGuaranteed(req[SYMBOL_TachiAPIAuth].userID!); if (req.body?.head?.game !== "chunithm") { return res.status(400).json({ diff --git a/server/src/server/router/ir/direct-manual/router.ts b/server/src/server/router/ir/direct-manual/router.ts index 110997ad1..b0fdf1482 100644 --- a/server/src/server/router/ir/direct-manual/router.ts +++ b/server/src/server/router/ir/direct-manual/router.ts @@ -3,7 +3,7 @@ import { GetUserWithIDGuaranteed } from "../../../../utils/user"; import { RequireLoggedInSession } from "../../../middleware/require-logged-in"; import { ExpressWrappedScoreImportMain } from "../../../../lib/score-import/framework/express-wrapper"; import ParseDirectManual from "../../../../lib/score-import/import-types/ir/direct-manual/parser"; -import { SYMBOL_TachiAPIData } from "../../../../lib/constants/tachi"; +import { SYMBOL_TachiAPIAuth } from "../../../../lib/constants/tachi"; import { RequirePermissions } from "../../../middleware/auth"; const router: Router = Router({ mergeParams: true }); @@ -17,7 +17,7 @@ router.post( RequirePermissions("submit:score"), RequireLoggedInSession, async (req, res) => { - const userDoc = await GetUserWithIDGuaranteed(req[SYMBOL_TachiAPIData].userID!); + const userDoc = await GetUserWithIDGuaranteed(req[SYMBOL_TachiAPIAuth].userID!); const intent = req.header("X-User-Intent"); diff --git a/server/src/server/router/ir/fervidex/router.ts b/server/src/server/router/ir/fervidex/router.ts index 6538e4f6d..4746d6260 100644 --- a/server/src/server/router/ir/fervidex/router.ts +++ b/server/src/server/router/ir/fervidex/router.ts @@ -9,7 +9,7 @@ import { ParseFervidexStatic } from "../../../../lib/score-import/import-types/i import { ParseFervidexSingle } from "../../../../lib/score-import/import-types/ir/fervidex/parser"; import { Playtypes, integer } from "tachi-common"; import CreateLogCtx from "../../../../lib/logger/logger"; -import { SYMBOL_TachiAPIData } from "../../../../lib/constants/tachi"; +import { SYMBOL_TachiAPIAuth } from "../../../../lib/constants/tachi"; import { RequirePermissions } from "../../../middleware/auth"; const logger = CreateLogCtx(__filename); @@ -21,7 +21,7 @@ const ValidateFervidexHeader: RequestHandler = (req, res, next) => { if (!agent) { logger.debug( - `Rejected fervidex client with no agent from user ${req[SYMBOL_TachiAPIData].userID!}.` + `Rejected fervidex client with no agent from user ${req[SYMBOL_TachiAPIAuth].userID!}.` ); return res.status(400).json({ success: false, @@ -32,7 +32,7 @@ const ValidateFervidexHeader: RequestHandler = (req, res, next) => { if (!agent.startsWith("fervidex/")) { logger.info( `Rejected fervidex client with invalid agent ${agent} from user ${req[ - SYMBOL_TachiAPIData + SYMBOL_TachiAPIAuth ].userID!}.` ); return res.status(400).json({ @@ -46,7 +46,7 @@ const ValidateFervidexHeader: RequestHandler = (req, res, next) => { if (!versions.every((e) => !Number.isNaN(e))) { logger.info( `Rejected fervidex client with agent ${agent} for NaN-like versions from user ${req[ - SYMBOL_TachiAPIData + SYMBOL_TachiAPIAuth ].userID!}.` ); return res.status(400).json({ @@ -58,7 +58,7 @@ const ValidateFervidexHeader: RequestHandler = (req, res, next) => { // version.minor if (versions[1] < 3) { logger.debug( - `Rejected outdated fervidex client from user ${req[SYMBOL_TachiAPIData].userID!}.` + `Rejected outdated fervidex client from user ${req[SYMBOL_TachiAPIAuth].userID!}.` ); return res.status(400).json({ success: false, @@ -74,7 +74,7 @@ const RequireInf2ModelHeader: RequestHandler = (req, res, next) => { if (!swModel) { logger.debug( - `Rejected empty X-Software-Model from user ${req[SYMBOL_TachiAPIData].userID!}.` + `Rejected empty X-Software-Model from user ${req[SYMBOL_TachiAPIAuth].userID!}.` ); return res.status(400).json({ success: false, @@ -86,7 +86,7 @@ const RequireInf2ModelHeader: RequestHandler = (req, res, next) => { const softID = ParseEA3SoftID(swModel); if (softID.model !== MODEL_INFINITAS_2) { - logger.debug(`Rejected non-inf2 model from user ${req[SYMBOL_TachiAPIData].userID!}.`); + logger.debug(`Rejected non-inf2 model from user ${req[SYMBOL_TachiAPIAuth].userID!}.`); return res.status(400).send({ success: false, description: "This endpoint is only available for INF2 clients.", @@ -108,7 +108,7 @@ const ValidateModelHeader: RequestHandler = (req, res, next) => { if (!swModel) { logger.debug( - `Rejected empty X-Software Model from user ${req[SYMBOL_TachiAPIData].userID!}.` + `Rejected empty X-Software Model from user ${req[SYMBOL_TachiAPIAuth].userID!}.` ); return res.status(400).json({ success: false, @@ -132,7 +132,7 @@ const ValidateModelHeader: RequestHandler = (req, res, next) => { if (softID.ext !== EXT_HEROIC_VERSE) { logger.info( - `Rejected invalid Software Model ${softID.ext} from user ${req[SYMBOL_TachiAPIData] + `Rejected invalid Software Model ${softID.ext} from user ${req[SYMBOL_TachiAPIAuth] .userID!}.` ); return res.status(400).json({ @@ -162,7 +162,7 @@ router.use(RequirePermissions("submit:score"), ValidateFervidexHeader, ValidateM * @name POST /ir/fervidex/profile/submit */ router.post("/profile/submit", RequireInf2ModelHeader, async (req, res) => { - const userDoc = await GetUserWithIDGuaranteed(req[SYMBOL_TachiAPIData].userID!); + const userDoc = await GetUserWithIDGuaranteed(req[SYMBOL_TachiAPIAuth].userID!); const headers = { // guaranteed to exist because of RequireInf2ModelHeader @@ -187,7 +187,7 @@ router.post("/profile/submit", RequireInf2ModelHeader, async (req, res) => { * @name POST /ir/fervidex/score/submit */ router.post("/score/submit", ValidateModelHeader, async (req, res) => { - const userDoc = await GetUserWithIDGuaranteed(req[SYMBOL_TachiAPIData].userID!); + const userDoc = await GetUserWithIDGuaranteed(req[SYMBOL_TachiAPIAuth].userID!); const model = req.header("X-Software-Model"); @@ -250,7 +250,7 @@ router.post("/class/submit", ValidateModelHeader, async (req, res) => { const playtype: Playtypes["iidx"] = req.body.play_style === 0 ? "SP" : "DP"; const r = await UpdateClassIfGreater( - req[SYMBOL_TachiAPIData].userID!, + req[SYMBOL_TachiAPIAuth].userID!, "iidx", playtype, "dan", diff --git a/server/src/server/router/ir/usc/router.ts b/server/src/server/router/ir/usc/router.ts index 56f82ea6e..985c21bb0 100644 --- a/server/src/server/router/ir/usc/router.ts +++ b/server/src/server/router/ir/usc/router.ts @@ -1,6 +1,6 @@ import { Router, RequestHandler } from "express"; import { FindChartOnSHA256 } from "../../../../utils/queries/charts"; -import { SYMBOL_TachiAPIData, SYMBOL_TachiData } from "../../../../lib/constants/tachi"; +import { SYMBOL_TachiAPIAuth, SYMBOL_TachiData } from "../../../../lib/constants/tachi"; import db from "../../../../external/mongo/db"; import { ChartDocument, @@ -65,7 +65,7 @@ const ValidateUSCRequest: RequestHandler = async (req, res, next) => { }); } - req[SYMBOL_TachiAPIData] = uscAuthDoc; + req[SYMBOL_TachiAPIAuth] = uscAuthDoc; return next(); }; @@ -261,10 +261,10 @@ router.post("/scores", RequirePermissions("submit:score"), async (req, res) => { }); } - const userDoc = await GetUserWithID(req[SYMBOL_TachiAPIData]!.userID!); + const userDoc = await GetUserWithID(req[SYMBOL_TachiAPIAuth]!.userID!); if (!userDoc) { - logger.severe(`User ${req[SYMBOL_TachiAPIData]!.userID!} as no parent userDoc?`); + logger.severe(`User ${req[SYMBOL_TachiAPIAuth]!.userID!} as no parent userDoc?`); return res.status(200).json({ statusCode: STATUS_CODES.SERVER_ERROR, description: "An internal server error has occured.", @@ -334,7 +334,7 @@ router.post( } const correspondingScore = await db.scores.findOne({ - userID: req[SYMBOL_TachiAPIData]!.userID!, + userID: req[SYMBOL_TachiAPIAuth]!.userID!, game: "usc", scoreID: req.body.identifier, }); diff --git a/server/src/utils/misc.ts b/server/src/utils/misc.ts index 6614a3f83..0b612c42e 100644 --- a/server/src/utils/misc.ts +++ b/server/src/utils/misc.ts @@ -1,4 +1,7 @@ import crypto from "crypto"; +import { CONF_INFO } from "../lib/setup/config"; +import { Game, Playtypes } from "tachi-common"; +import { validPlaytypes } from "tachi-common/js/config"; // https://github.com/sindresorhus/escape-string-regexp/blob/main/index.js // the developer of this has migrated everything to Force ES6 style modules, @@ -58,3 +61,11 @@ export function SplitAuthorizationHeader(authHeader: string) { export function HasOwnProperty(obj: T, key: string | number | symbol): key is keyof T { return Object.prototype.hasOwnProperty.call(obj, key); } + +export function IsValidGame(str: string): str is Game { + return !!CONF_INFO.SUPPORTED_GAMES.includes(str as Game); +} + +export function IsValidPlaytype(game: Game, str: string): str is Playtypes[Game] { + return validPlaytypes[game].includes(str as Playtypes[Game]); +} diff --git a/server/src/utils/types.ts b/server/src/utils/types.ts index 35e6ecab1..722e7ccf7 100644 --- a/server/src/utils/types.ts +++ b/server/src/utils/types.ts @@ -1,4 +1,4 @@ -import { integer, ChartDocument } from "tachi-common"; +import { integer, ChartDocument, PublicUserDocument, UserGameStats } from "tachi-common"; declare module "express-session" { // Inject additional properties on express-session @@ -36,4 +36,7 @@ export interface TachiRequestData { uscChartDoc?: ChartDocument<"usc:Single">; beatorajaChartDoc?: ChartDocument<"bms:7K" | "bms:14K">; + + requestedUser?: PublicUserDocument; + requestedUserGameStats?: UserGameStats; }