Просмотр исходного кода

Merge pull request #5953 from weseek/feat/96209-defalt-config

add default config
Yuki Takei 3 лет назад
Родитель
Сommit
54e3ca800a

+ 18 - 3
packages/app/src/server/middlewares/api-rate-limiter.ts

@@ -5,7 +5,7 @@ import { RateLimiterMongo } from 'rate-limiter-flexible';
 
 
 import loggerFactory from '~/utils/logger';
 import loggerFactory from '~/utils/logger';
 
 
-import { generateApiRateLimitConfig } from '../util/generateApiRateLimitConfig';
+import { generateApiRateLimitConfig } from '../util/api-rate-limit-config/generateApiRateLimitConfig';
 
 
 
 
 const logger = loggerFactory('growi:middleware:api-rate-limit');
 const logger = loggerFactory('growi:middleware:api-rate-limit');
@@ -26,7 +26,11 @@ const opts = {
 const rateLimiter = new RateLimiterMongo(opts);
 const rateLimiter = new RateLimiterMongo(opts);
 
 
 // generate ApiRateLimitConfig for api rate limiter
 // generate ApiRateLimitConfig for api rate limiter
-const apiRateLimitConfig = generateApiRateLimitConfig();
+const apiRateLimitConfigWithoutRegExp = generateApiRateLimitConfig(false);
+const apiRateLimitConfigWithRegExp = generateApiRateLimitConfig(true);
+const allRegExp = new RegExp(Object.keys(apiRateLimitConfigWithRegExp).join('|'));
+const keysWithRegExp = Object.keys(apiRateLimitConfigWithRegExp).map(key => new RegExp(key));
+const valuesWithRegExp = Object.values(apiRateLimitConfigWithRegExp);
 
 
 const consumePoints = async(rateLimiter: RateLimiterMongo, key: string, points: number) => {
 const consumePoints = async(rateLimiter: RateLimiterMongo, key: string, points: number) => {
   const consumePoints = defaultMaxPoints / points;
   const consumePoints = defaultMaxPoints / points;
@@ -40,7 +44,18 @@ module.exports = () => {
     const endpoint = req.path;
     const endpoint = req.path;
     const key = md5(req.ip + endpoint);
     const key = md5(req.ip + endpoint);
 
 
-    const customizedConfig = apiRateLimitConfig[endpoint];
+    let customizedConfig;
+    const configForEndpoint = apiRateLimitConfigWithoutRegExp[endpoint];
+    if (configForEndpoint) {
+      customizedConfig = configForEndpoint;
+    }
+    else if (allRegExp.test(endpoint)) {
+      keysWithRegExp.forEach((key, index) => {
+        if (key.test(endpoint)) {
+          customizedConfig = valuesWithRegExp[index];
+        }
+      });
+    }
 
 
     try {
     try {
       if (customizedConfig === undefined) {
       if (customizedConfig === undefined) {

+ 62 - 0
packages/app/src/server/util/api-rate-limit-config/defaultApiRateLimitConfig.ts

@@ -0,0 +1,62 @@
+import { IApiRateLimitConfig } from '../../interfaces/api-rate-limit-config';
+
+// strict config
+const defaultStrictMaxRequests = 1; // per second
+const defaultStrictConfig: IApiRateLimitConfig = {
+  '/login/activateInvited': {
+    method: 'POST',
+    maxRequests: defaultStrictMaxRequests,
+  },
+  '/login': {
+    method: 'POST',
+    maxRequests: defaultStrictMaxRequests,
+  },
+  '/register': {
+    method: 'POST',
+    maxRequests: defaultStrictMaxRequests,
+  },
+  '/installer': {
+    method: 'POST',
+    maxRequests: defaultStrictMaxRequests,
+  },
+  '/_api/login/testLdap': {
+    method: 'POST',
+    maxRequests: defaultStrictMaxRequests,
+  },
+  '/user-activation/register': {
+    method: 'POST',
+    maxRequests: defaultStrictMaxRequests,
+  },
+};
+
+
+// infinity config
+const defaultInfinityConfig: IApiRateLimitConfig = {
+  '/_api/v3/healthcheck': {
+    method: 'GET',
+    maxRequests: Infinity,
+  },
+};
+
+// default config without reg exp
+export const defaultConfigWithoutRegExp = { ...defaultStrictConfig, ...defaultInfinityConfig };
+
+// default config with reg exp
+export const defaultConfigWithRegExp = {
+  '/forgot-password/.*': {
+    method: 'GET',
+    maxRequests: defaultStrictMaxRequests,
+  },
+  '/user-activation/.*': {
+    method: 'GET',
+    maxRequests: defaultStrictMaxRequests,
+  },
+  '/download/[0-9a-z]{24}': {
+    method: 'GET',
+    maxRequests: defaultStrictMaxRequests,
+  },
+  '/share/[0-9a-z]{24}': {
+    method: 'GET',
+    maxRequests: defaultStrictMaxRequests,
+  },
+};

+ 60 - 0
packages/app/src/server/util/api-rate-limit-config/generateApiRateLimitConfig.ts

@@ -0,0 +1,60 @@
+import { IApiRateLimitConfig } from '../../interfaces/api-rate-limit-config';
+
+import { defaultConfigWithoutRegExp, defaultConfigWithRegExp } from './defaultApiRateLimitConfig';
+
+const envVar = process.env;
+
+const getTargetFromKey = (key: string, withRegExp: boolean) => {
+  // eslint-disable-next-line regex/invalid
+  const regExp = new RegExp(withRegExp ? '(?<=API_RATE_LIMIT_).*(?=_ENDPOINT_WITH_REGEXP)' : '(?<=API_RATE_LIMIT_).*(?=_ENDPOINT)');
+  return key.match(regExp);
+};
+
+const generateApiRateLimitConfigFromEndpoint = (envVar: NodeJS.ProcessEnv, endpointKeys: string[], withRegExp: boolean): IApiRateLimitConfig => {
+  const apiRateLimitConfig: IApiRateLimitConfig = {};
+  endpointKeys.forEach((key) => {
+
+    const endpoint = envVar[key];
+
+    if (endpoint == null || Object.keys(apiRateLimitConfig).includes(endpoint)) {
+      return;
+    }
+
+    const target = getTargetFromKey(key, withRegExp);
+    const methodKey = `API_RATE_LIMIT_${target}_METHODS`;
+    const maxRequestsKey = `API_RATE_LIMIT_${target}_MAX_REQUESTS`;
+    const method = envVar[methodKey] ?? 'ALL';
+    const maxRequests = Number(envVar[maxRequestsKey]);
+
+    if (endpoint == null || maxRequests == null) {
+      return;
+    }
+
+    const config = {
+      method,
+      maxRequests,
+    };
+
+    apiRateLimitConfig[endpoint] = config;
+  });
+
+  return apiRateLimitConfig;
+};
+
+export const generateApiRateLimitConfig = (withRegExp: boolean): IApiRateLimitConfig => {
+
+  const apiRateEndpointKeys = Object.keys(envVar).filter((key) => {
+    const target = getTargetFromKey(key, withRegExp);
+    return target;
+  });
+
+  // sort priority
+  apiRateEndpointKeys.sort().reverse();
+
+  // get config
+  const apiRateLimitConfig = generateApiRateLimitConfigFromEndpoint(envVar, apiRateEndpointKeys, withRegExp);
+
+  const defaultConfig = withRegExp ? defaultConfigWithRegExp : defaultConfigWithoutRegExp;
+
+  return { ...defaultConfig, ...apiRateLimitConfig };
+};

+ 0 - 58
packages/app/src/server/util/generateApiRateLimitConfig.ts

@@ -1,58 +0,0 @@
-import { IApiRateLimitConfig } from '../interfaces/api-rate-limit-config';
-
-const getTargetFromKey = (key: string) => {
-  return key.replace(/^API_RATE_LIMIT_/, '').replace(/_ENDPOINT$/, '');
-};
-
-const generateApiRateLimitConfigFromEndpoint = (envVar: NodeJS.ProcessEnv, endpointKeys: string[]): IApiRateLimitConfig => {
-  const apiRateLimitConfig: IApiRateLimitConfig = {};
-  endpointKeys.forEach((key) => {
-
-    const endpoint = envVar[key];
-
-    if (endpoint == null || Object.keys(apiRateLimitConfig).includes(endpoint)) {
-      return;
-    }
-
-    const target = getTargetFromKey(key);
-    const method = envVar[`API_RATE_LIMIT_${target}_METHODS`] ?? 'ALL';
-    const maxRequests = Number(envVar[`API_RATE_LIMIT_${target}_MAX_REQUESTS`]);
-
-    if (endpoint == null || maxRequests == null) {
-      return;
-    }
-
-    const config = {
-      method,
-      maxRequests,
-    };
-
-    apiRateLimitConfig[endpoint] = config;
-  });
-
-  return apiRateLimitConfig;
-};
-
-// this method is called only one server starts
-export const generateApiRateLimitConfig = (): IApiRateLimitConfig => {
-  const envVar = process.env;
-
-  const apiRateEndpointKeys = Object.keys(envVar).filter((key) => {
-    const endpointRegExp = /^API_RATE_LIMIT_\w+_ENDPOINT/;
-    return endpointRegExp.test(key);
-  });
-
-  // sort priority
-  apiRateEndpointKeys.sort().reverse();
-
-  // get config
-  const apiRateLimitConfig = generateApiRateLimitConfigFromEndpoint(envVar, apiRateEndpointKeys);
-
-  // default setting e.g. healthchack
-  apiRateLimitConfig['/_api/v3/healthcheck'] = {
-    method: 'GET',
-    maxRequests: 0,
-  };
-
-  return apiRateLimitConfig;
-};