diff --git a/.env.example b/.env.example index bda00c7..a91184d 100644 --- a/.env.example +++ b/.env.example @@ -42,4 +42,6 @@ SECURITY_STRICT_LIMIT_WINDOW = [0-9]*(y|d|h|m|s) # default ist 15 SECURITY_STRICT_LIMIT_REQUEST_COUNT = strict_request_count # default ist 15 USE_SECURITY_LIMIT = (true|false) # default ist true SECURITY_LIMIT_WINDOW = [0-9]*(y|d|h|m|s) # default ist 1m -SECURITY_LIMIT_REQUEST_COUNT = request_count # default ist 500 \ No newline at end of file +SECURITY_LIMIT_REQUEST_COUNT = request_count # default ist 500 + +TRUST_PROXY = # wenn leer, wird dieser Wert nicht angewendet. \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index b47f3a6..9be0a83 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,7 @@ "handlebars": "^4.7.8", "helmet": "^8.0.0", "ics": "^3.8.1", + "ip": "^2.0.1", "jsonwebtoken": "^9.0.2", "lodash.uniqby": "^4.7.0", "moment": "^2.30.1", @@ -41,6 +42,7 @@ "devDependencies": { "@types/cors": "^2.8.14", "@types/express": "^4.17.17", + "@types/ip": "^1.1.3", "@types/jsonwebtoken": "^9.0.6", "@types/lodash.uniqby": "^4.7.9", "@types/morgan": "^1.9.9", @@ -535,6 +537,16 @@ "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", "dev": true }, + "node_modules/@types/ip": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@types/ip/-/ip-1.1.3.tgz", + "integrity": "sha512-64waoJgkXFTYnCYDUWgSATJ/dXEBanVkaP5d4Sbk7P6U7cTTMhxVyROTckc6JKdwCrgnAjZMn0k3177aQxtDEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/jsonwebtoken": { "version": "9.0.6", "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.6.tgz", @@ -2681,6 +2693,12 @@ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" }, + "node_modules/ip": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.1.tgz", + "integrity": "sha512-lJUL9imLTNi1ZfXT+DU6rBBdbiKGBuay9B6xGSPVjUeQwaH1RIGqef8RZkUtHioLmSNpPR5M4HVKJGm1j8FWVQ==", + "license": "MIT" + }, "node_modules/ip-address": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", diff --git a/package.json b/package.json index 9cd6223..4987b43 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "handlebars": "^4.7.8", "helmet": "^8.0.0", "ics": "^3.8.1", + "ip": "^2.0.1", "jsonwebtoken": "^9.0.2", "lodash.uniqby": "^4.7.0", "moment": "^2.30.1", @@ -56,6 +57,7 @@ "devDependencies": { "@types/cors": "^2.8.14", "@types/express": "^4.17.17", + "@types/ip": "^1.1.3", "@types/jsonwebtoken": "^9.0.6", "@types/lodash.uniqby": "^4.7.9", "@types/morgan": "^1.9.9", diff --git a/src/command/refreshCommandHandler.ts b/src/command/refreshCommandHandler.ts index 5796851..1588449 100644 --- a/src/command/refreshCommandHandler.ts +++ b/src/command/refreshCommandHandler.ts @@ -23,7 +23,7 @@ export default abstract class RefreshCommandHandler { .into(refresh) .values({ token: refreshToken, - user: await UserService.getById(createRefresh.userId), + userId: createRefresh.userId, expiry: createRefresh.isFromPwa ? new Date(Date.now() + ms(PWA_REFRESH_EXPIRATION)) : new Date(Date.now() + ms(REFRESH_EXPIRATION)), @@ -47,8 +47,7 @@ export default abstract class RefreshCommandHandler { .createQueryBuilder() .delete() .from(refresh) - .where("refresh.token = :token", { token: deleteRefresh.token }) - .andWhere("refresh.userId = :userId", { userId: deleteRefresh.userId }) + .where({ token: deleteRefresh.token, userId: deleteRefresh.userId }) .execute() .then((res) => {}) .catch((err) => { diff --git a/src/env.defaults.ts b/src/env.defaults.ts index 4c8362d..eed7d89 100644 --- a/src/env.defaults.ts +++ b/src/env.defaults.ts @@ -1,5 +1,6 @@ import "dotenv/config"; import ms from "ms"; +import ip from "ip"; export const DB_TYPE = process.env.DB_TYPE ?? "mysql"; export const DB_HOST = process.env.DB_HOST ?? ""; @@ -35,6 +36,24 @@ export const USE_SECURITY_LIMIT = process.env.USE_SECURITY_LIMIT ?? "true"; export const SECURITY_LIMIT_WINDOW = process.env.SECURITY_LIMIT_WINDOW ?? "1m"; export const SECURITY_LIMIT_REQUEST_COUNT = Number(process.env.SECURITY_LIMIT_REQUEST_COUNT ?? "500"); +export const TRUST_PROXY = ((): Array | string | boolean | number | null => { + const proxyVal = process.env.TRUST_PROXY; + if (!proxyVal) return null; + if (proxyVal == "true" || proxyVal == "false") { + return proxyVal == "true"; + } + if (!isNaN(Number(proxyVal))) { + return Number(proxyVal); + } + if (proxyVal.includes(",") && proxyVal.split(",").every((pv) => ip.isV4Format(pv) || ip.isV6Format(pv))) { + return proxyVal.split(","); + } + if (ip.isV4Format(proxyVal) || ip.isV6Format(proxyVal)) { + return proxyVal; + } + return null; +})(); + export function configCheck() { if (DB_TYPE != "mysql" && DB_TYPE != "sqlite" && DB_TYPE != "postgres") throw new Error("set valid value to DB_TYPE (mysql|sqlite|postgres)"); @@ -46,7 +65,7 @@ export function configCheck() { if ((DB_PASSWORD == "" || typeof DB_PASSWORD != "string") && DB_TYPE != "sqlite") throw new Error("set valid value to DB_PASSWORD"); - if (typeof SERVER_PORT != "number") throw new Error("set valid numeric value to SERVER_PORT"); + if (isNaN(SERVER_PORT)) throw new Error("set valid numeric value to SERVER_PORT"); if (JWT_SECRET == "" || typeof JWT_SECRET != "string") throw new Error("set valid value to JWT_SECRET"); checkMS(JWT_EXPIRATION, "JWT_EXPIRATION"); @@ -56,7 +75,7 @@ export function configCheck() { if (MAIL_USERNAME == "" || typeof MAIL_USERNAME != "string") throw new Error("set valid value to MAIL_USERNAME"); if (MAIL_PASSWORD == "" || typeof MAIL_PASSWORD != "string") throw new Error("set valid value to MAIL_PASSWORD"); if (MAIL_HOST == "" || typeof MAIL_HOST != "string") throw new Error("set valid value to MAIL_HOST"); - if (typeof MAIL_PORT != "number") throw new Error("set valid numeric value to MAIL_PORT"); + if (isNaN(MAIL_PORT)) throw new Error("set valid numeric value to MAIL_PORT"); if (MAIL_SECURE != "true" && MAIL_SECURE != "false") throw new Error("set 'true' or 'false' to MAIL_SECURE"); if ( @@ -73,13 +92,16 @@ export function configCheck() { if (USE_SECURITY_STRICT_LIMIT != "true" && USE_SECURITY_STRICT_LIMIT != "false") throw new Error("set 'true' or 'false' to USE_SECURITY_STRICT_LIMIT"); checkMS(SECURITY_STRICT_LIMIT_WINDOW, "SECURITY_STRICT_LIMIT_WINDOW"); - if (typeof SECURITY_STRICT_LIMIT_REQUEST_COUNT != "number") + if (isNaN(SECURITY_STRICT_LIMIT_REQUEST_COUNT)) throw new Error("set valid numeric value to SECURITY_STRICT_LIMIT_REQUEST_COUNT"); if (USE_SECURITY_LIMIT != "true" && USE_SECURITY_LIMIT != "false") throw new Error("set 'true' or 'false' to USE_SECURITY_LIMIT"); checkMS(SECURITY_LIMIT_WINDOW, "SECURITY_LIMIT_WINDOW"); - if (typeof SECURITY_LIMIT_REQUEST_COUNT != "number") - throw new Error("set valid numeric value to SECURITY_LIMIT_REQUEST_COUNT"); + if (isNaN(SECURITY_LIMIT_REQUEST_COUNT)) throw new Error("set valid numeric value to SECURITY_LIMIT_REQUEST_COUNT"); + + if (!TRUST_PROXY && process.env.TRUST_PROXY) { + throw new Error("set valid boolean, number, ip or ips value to TRUST_PROXY"); + } } function checkMS(input: string, origin: string) { diff --git a/src/routes/index.ts b/src/routes/index.ts index ed95543..e614a27 100644 --- a/src/routes/index.ts +++ b/src/routes/index.ts @@ -28,6 +28,7 @@ import { SECURITY_LIMIT_WINDOW, SECURITY_STRICT_LIMIT_REQUEST_COUNT, SECURITY_STRICT_LIMIT_WINDOW, + TRUST_PROXY, USE_SECURITY_LIMIT, USE_SECURITY_STRICT_LIMIT, } from "../env.defaults"; @@ -62,6 +63,9 @@ function excludePaths(middleware: RequestHandler, excludedPaths: Array) } export default (app: Express) => { + if (TRUST_PROXY) { + app.set("trust proxy", TRUST_PROXY); + } app.set("query parser", "extended"); app.use(cors()); app.options("*", cors()); diff --git a/src/service/user/userPermissionService.ts b/src/service/user/userPermissionService.ts index 7cb3ee6..0dee9da 100644 --- a/src/service/user/userPermissionService.ts +++ b/src/service/user/userPermissionService.ts @@ -13,7 +13,7 @@ export default abstract class UserPermissionService { return await dataSource .getRepository(userPermission) .createQueryBuilder("permission") - .where("permission.userId = :userId", { userId: userId }) + .where({ userId: userId }) .getMany() .then((res) => { return res;