From 7f694f1a4a0bd487ac11b5e15ee2d26e3501bffa Mon Sep 17 00:00:00 2001 From: stringsplit <77242831+nbitzz@users.noreply.github.com> Date: Fri, 4 Aug 2023 04:05:52 +0000 Subject: [PATCH] updates --- src/server/lib/ratelimit.ts | 45 ++++++++++++++++++++++++++++++++ src/server/routes/adminRoutes.ts | 7 +++++ src/server/routes/authRoutes.ts | 23 +++++++++------- 3 files changed, 65 insertions(+), 10 deletions(-) create mode 100644 src/server/lib/ratelimit.ts diff --git a/src/server/lib/ratelimit.ts b/src/server/lib/ratelimit.ts new file mode 100644 index 0000000..a53533a --- /dev/null +++ b/src/server/lib/ratelimit.ts @@ -0,0 +1,45 @@ +import { RequestHandler } from "express" +import { type Account } from "./accounts" +import ServeError from "./errors" + +interface ratelimitSettings { + + requests: number + per: number + +} + +export function accountRatelimit( settings: ratelimitSettings ): RequestHandler { + let activeLimits: { + [ key: string ]: { + requests: number, + expirationHold: NodeJS.Timeout + } + } = {} + + return (req, res, next) => { + if (res.locals.acc) { + let accId = res.locals.acc.id + let aL = activeLimits[accId] + + if (!aL) { + activeLimits[accId] = { + requests: 0, + expirationHold: setTimeout(() => delete activeLimits[accId], settings.per) + } + aL = activeLimits[accId] + } + + if (aL.requests < settings.requests) { + res.locals.undoCount = () => { + if (activeLimits[accId]) { + activeLimits[accId].requests-- + } + } + next() + } else { + ServeError(res, 429, "too many requests") + } + } + } +} \ No newline at end of file diff --git a/src/server/routes/adminRoutes.ts b/src/server/routes/adminRoutes.ts index 751306e..83debcb 100644 --- a/src/server/routes/adminRoutes.ts +++ b/src/server/routes/adminRoutes.ts @@ -48,6 +48,13 @@ adminRoutes.post("/reset", parser, (req,res) => { auth.AuthTokens.filter(e => e.account == targetAccount?.id).forEach((v) => { auth.invalidate(v.token) }) + + + sendMail(req.body.email, `Your login details have been updated`, `Hello there! This email is to notify you of a password change that an administrator, ${acc.username}, has initiated. You have been logged out of your devices. Thank you for using monofile.`).then(() => { + res.send("OK") + }).catch((err) => {}) + + res.send() }) diff --git a/src/server/routes/authRoutes.ts b/src/server/routes/authRoutes.ts index 9c6be28..8d2eb96 100644 --- a/src/server/routes/authRoutes.ts +++ b/src/server/routes/authRoutes.ts @@ -4,6 +4,7 @@ import * as Accounts from "../lib/accounts"; import * as auth from "../lib/auth"; import { sendMail } from "../lib/mail"; import { getAccount, requiresAccount } from "../lib/middleware" +import { accountRatelimit } from "../lib/ratelimit" import ServeError from "../lib/errors"; import Files, { FileVisibility, generateFileId, id_check_regex } from "../lib/files"; @@ -239,14 +240,18 @@ authRoutes.post("/change_username", requiresAccount, parser, (req,res) => { acc.username = req.body.username Accounts.save() + sendMail(req.body.email, `Your login details have been updated`, `Hello there! Your username has been updated to ${req.body.username}. Please update your devices accordingly. Thank you for using monofile.`).then(() => { + res.send("OK") + }).catch((err) => {}) + res.send("username changed") }) // shit way to do this but... -let verificationCodes = new Map() +let verificationCodes = new Map() -authRoutes.post("/request_email_change", requiresAccount, parser, (req,res) => { +authRoutes.post("/request_email_change", requiresAccount, accountRatelimit({ requests: 4, per: 60*60*1000 }), parser, (req,res) => { let acc = res.locals.acc as Accounts.Account @@ -257,12 +262,6 @@ authRoutes.post("/request_email_change", requiresAccount, parser, (req,res) => { let vcode = verificationCodes.get(acc.id) - if (vcode && vcode.requestedAt+(15*60*1000) > Date.now()) { - ServeError(res, 429, `Please wait a few moments to request another email change.`) - return - } - - // delete previous if any let e = vcode?.expiry if (e) clearTimeout(e) @@ -275,8 +274,7 @@ authRoutes.post("/request_email_change", requiresAccount, parser, (req,res) => { verificationCodes.set(acc.id, { code, email: req.body.email, - expiry: setTimeout( () => verificationCodes.delete(acc?.id||""), 15*60*1000), - requestedAt: Date.now() + expiry: setTimeout( () => verificationCodes.delete(acc?.id||""), 15*60*1000) }) // this is a mess but it's fine @@ -287,6 +285,7 @@ authRoutes.post("/request_email_change", requiresAccount, parser, (req,res) => { let e = verificationCodes.get(acc?.id||"")?.expiry if (e) clearTimeout(e) verificationCodes.delete(acc?.id||"") + res.locals.undoCount(); ServeError(res, 500, err?.toString()) }) }) @@ -408,6 +407,10 @@ authRoutes.post("/change_password", requiresAccount, parser, (req,res) => { auth.invalidate(v.token) }) + sendMail(req.body.email, `Your login details have been updated`, `Hello there! This email is to notify you of a password change that you have initiated. You have been logged out of your devices. Thank you for using monofile.`).then(() => { + res.send("OK") + }).catch((err) => {}) + res.send("password changed - logged out all sessions") })