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")
})