diff --git a/package.json b/package.json
index c9149afd..878c2e2d 100644
--- a/package.json
+++ b/package.json
@@ -83,7 +83,6 @@
"missing-native-js-functions": "^1.2.18",
"morgan": "^1.10.0",
"multer": "^1.4.5-lts.1",
- "mysql2": "^2.3.3",
"node-2fa": "^2.0.3",
"node-fetch": "^2.6.7",
"patch-package": "^6.4.7",
@@ -91,7 +90,6 @@
"prettier": "^2.7.1",
"proxy-agent": "^5.0.0",
"reflect-metadata": "^0.1.13",
- "sqlite3": "^5.1.1",
"typeorm": "^0.3.7",
"typescript": "^4.2.3",
"ws": "^8.8.1"
diff --git a/src/api/routes/auth/register.ts b/src/api/routes/auth/register.ts
index b87c26f6..b5d75a30 100644
--- a/src/api/routes/auth/register.ts
+++ b/src/api/routes/auth/register.ts
@@ -32,6 +32,8 @@ router.post("/", route({ body: "RegisterSchema" }), async (req: Request, res: Re
const body = req.body as RegisterSchema;
const { register, security, limits } = Config.get();
const ip = getIpAdress(req);
+ // tokens bypass requirements:
+ const hasToken = req.get("Referrer") && req.get("Referrer")?.includes("token=");
// email will be slightly modified version of the user supplied email -> e.g. protection against GMail Trick
let email = adjustEmail(body.email);
@@ -43,7 +45,7 @@ router.post("/", route({ body: "RegisterSchema" }), async (req: Request, res: Re
});
}
- if (register.requireCaptcha && security.captcha.enabled) {
+ if (register.requireCaptcha && security.captcha.enabled && !hasToken) {
const { sitekey, service } = security.captcha;
if (!body.captcha_key) {
return res?.status(400).json({
@@ -64,7 +66,7 @@ router.post("/", route({ body: "RegisterSchema" }), async (req: Request, res: Re
}
// check if registration is allowed
- if (!register.allowNewRegistration) {
+ if (!register.allowNewRegistration && !hasToken) {
throw FieldErrors({
email: { code: "REGISTRATION_DISABLED", message: req.t("auth:register.REGISTRATION_DISABLED") }
});
@@ -142,9 +144,9 @@ router.post("/", route({ body: "RegisterSchema" }), async (req: Request, res: Re
});
}
- //check if email starts with any valid registration token
+ //check if referrer starts with any valid registration token
let validToken = false;
- if (req.get("Referrer") && req.get("Referrer")?.includes("token=")) {
+ if (hasToken) {
let token = req.get("Referrer")?.split("token=")[1].split("&")[0];
if (token) {
await ValidRegistrationToken.delete({ expires_at: LessThan(new Date()) });
@@ -172,9 +174,29 @@ router.post("/", route({ body: "RegisterSchema" }), async (req: Request, res: Re
}, ${req.body.username}, ${req.body.invite ?? "No invite given"}`
)
);
- throw FieldErrors({
- email: { code: "TOO_MANY_REGISTRATIONS", message: req.t("auth:register.TOO_MANY_REGISTRATIONS") }
+ let oldest = await User.findOne({
+ where: { created_at: MoreThan(new Date(Date.now() - limits.absoluteRate.register.window)) },
+ order: { created_at: "ASC" }
});
+ if (!oldest) {
+ console.warn(
+ red(
+ `[REGISTER/WARN] Global rate limits exceeded, but no oldest user found. This should not happen. Did you misconfigure the limits?`
+ )
+ );
+ } else {
+ let retryAfterSec = Math.ceil((oldest!.created_at.getTime() - new Date(Date.now() - limits.absoluteRate.register.window).getTime())/1000);
+ return res
+ .status(429)
+ .set("X-RateLimit-Limit", `${limits.absoluteRate.register.limit}`)
+ .set("X-RateLimit-Remaining", "0")
+ .set("X-RateLimit-Reset", `${(oldest!.created_at.getTime() + limits.absoluteRate.register.window) / 1000}`)
+ .set("X-RateLimit-Reset-After", `${retryAfterSec}`)
+ .set("X-RateLimit-Global", `true`)
+ .set("Retry-After", `${retryAfterSec}`)
+ .set("X-RateLimit-Bucket", `register`)
+ .send({ message: req.t("auth:register.TOO_MANY_REGISTRATIONS"), retry_after: retryAfterSec, global: true });
+ }
}
const user = await User.register({ ...body, req });
diff --git a/src/api/routes/channels/#channel_id/messages/index.ts b/src/api/routes/channels/#channel_id/messages/index.ts
index b2822711..65cd42f3 100644
--- a/src/api/routes/channels/#channel_id/messages/index.ts
+++ b/src/api/routes/channels/#channel_id/messages/index.ts
@@ -6,7 +6,6 @@ import {
Config,
DmChannelDTO,
emitEvent,
- FieldErrors,
getIpAdress,
getPermission,
getRights,
@@ -21,7 +20,7 @@ import {
} from "@fosscord/util";
import { Request, Response, Router } from "express";
import multer from "multer";
-import { yellow } from "picocolors";
+import { red, yellow } from "picocolors";
import { FindManyOptions, LessThan, MoreThan } from "typeorm";
import { URL } from "url";
@@ -168,19 +167,39 @@ router.post(
if (
!(await getRights(req.user_id)).has(Rights.FLAGS.BYPASS_RATE_LIMITS) &&
- limits.absoluteRate.register.enabled &&
- (await await Message.count({
+ limits.absoluteRate.sendMessage.enabled &&
+ (await Message.count({
where: { channel_id, timestamp: MoreThan(new Date(Date.now() - limits.absoluteRate.sendMessage.window)) }
- })) >= limits.absoluteRate.register.limit
+ })) >= limits.absoluteRate.sendMessage.limit
) {
console.log(
yellow(
`[MESSAGE] Global register rate limit exceeded for ${getIpAdress(req)}: ${channel_id}, ${req.user_id}, ${body.content}`
)
);
- throw FieldErrors({
- channel_id: { code: "TOO_MANY_MESSAGES", message: req.t("common:toomany.MESSAGE") }
+ let oldest = await Message.findOne({
+ where: { channel_id, timestamp: MoreThan(new Date(Date.now() - limits.absoluteRate.sendMessage.window)) },
+ order: { timestamp: "ASC" }
});
+ if (!oldest) {
+ console.warn(
+ red(
+ `[MESSAGE/WARN] Global rate limits exceeded, but no oldest message found. This should not happen. Did you misconfigure the limits?`
+ )
+ );
+ } else {
+ let retryAfterSec = Math.ceil((oldest!.timestamp.getTime() - new Date(Date.now() - limits.absoluteRate.sendMessage.window).getTime())/1000);
+ return res
+ .status(429)
+ .set("X-RateLimit-Limit", `${limits.absoluteRate.sendMessage.limit}`)
+ .set("X-RateLimit-Remaining", "0")
+ .set("X-RateLimit-Reset", `${(oldest!.timestamp.getTime() + limits.absoluteRate.sendMessage.window) / 1000}`)
+ .set("X-RateLimit-Reset-After", `${retryAfterSec}`)
+ .set("X-RateLimit-Global", `false`)
+ .set("Retry-After", `${retryAfterSec}`)
+ .set("X-RateLimit-Bucket", `chnl_${channel_id}`)
+ .send({ message: req.t("common:toomany.MESSAGE"), retry_after: retryAfterSec, global: false });
+ }
}
const files = (req.files as Express.Multer.File[]) ?? [];
|