diff --git a/app/api/server/api.js b/app/api/server/api.js index 01a71effe8a3..6a94630edd5c 100644 --- a/app/api/server/api.js +++ b/app/api/server/api.js @@ -4,8 +4,8 @@ import { DDPCommon } from 'meteor/ddp-common'; import { DDP } from 'meteor/ddp'; import { Accounts } from 'meteor/accounts-base'; import { Restivus } from 'meteor/nimble:restivus'; -import { RateLimiter } from 'meteor/rate-limit'; import _ from 'underscore'; +import { RateLimiter } from 'meteor/rate-limit'; import { Logger } from '../../../server/lib/logger/Logger'; import { getRestPayload } from '../../../server/lib/logger/logPayloads'; @@ -447,6 +447,14 @@ export class APIClass extends Restivus { }); } + updateRateLimiterDictionaryForRoute(route, numRequestsAllowed, intervalTimeInMS) { + if (rateLimiterDictionary[route]) { + rateLimiterDictionary[route].options.numRequestsAllowed = numRequestsAllowed ?? rateLimiterDictionary[route].options.numRequestsAllowed; + rateLimiterDictionary[route].options.intervalTimeInMS = intervalTimeInMS ?? rateLimiterDictionary[route].options.intervalTimeInMS; + API.v1.reloadRoutesToRefreshRateLimiter(); + } + } + _initAuth() { const loginCompatibility = (bodyParams, request) => { // Grab the username or email that the user is logging in with @@ -771,6 +779,7 @@ settings.watch('API_Enable_Rate_Limiter_Limit_Calls_Default', (value) => { API.v1.reloadRoutesToRefreshRateLimiter(); }); + settings.watch('Prometheus_API_User_Agent', (value) => { prometheusAPIUserAgent = value; }); diff --git a/app/api/server/v1/users.js b/app/api/server/v1/users.js index 1f3f22e38e6c..a6c514a7c8ec 100644 --- a/app/api/server/v1/users.js +++ b/app/api/server/v1/users.js @@ -27,6 +27,7 @@ import { setUserStatus } from '../../../../imports/users-presence/server/activeU import { resetTOTP } from '../../../2fa/server/functions/resetTOTP'; import { Team } from '../../../../server/sdk'; + API.v1.addRoute('users.create', { authRequired: true }, { post() { check(this.bodyParams, { @@ -283,7 +284,11 @@ API.v1.addRoute('users.list', { authRequired: true }, { }, }); -API.v1.addRoute('users.register', { authRequired: false }, { +API.v1.addRoute('users.register', { authRequired: false, + rateLimiterOptions: { + numRequestsAllowed: settings.get('Rate_Limiter_Limit_RegisterUser'), + intervalTimeInMS: settings.get('API_Enable_Rate_Limiter_Limit_Time_Default'), + } }, { post() { if (this.userId) { return API.v1.failure('Logged in users can not register again.'); @@ -944,3 +949,9 @@ API.v1.addRoute('users.logout', { authRequired: true }, { }); }, }); + +settings.watch('Rate_Limiter_Limit_RegisterUser', (value) => { + const userRegisterRoute = '/api/v1/users.registerpost'; + + API.v1.updateRateLimiterDictionaryForRoute(userRegisterRoute, value); +}); diff --git a/app/lib/server/startup/settings.ts b/app/lib/server/startup/settings.ts index b8c3309b5cf2..68e644eb120d 100644 --- a/app/lib/server/startup/settings.ts +++ b/app/lib/server/startup/settings.ts @@ -2977,7 +2977,7 @@ settingsRegistry.addGroup('Setup_Wizard', function() { }); settingsRegistry.addGroup('Rate Limiter', function() { - this.section('DDP Rate Limiter', function() { + this.section('DDP_Rate_Limiter', function() { this.add('DDP_Rate_Limit_IP_Enabled', true, { type: 'boolean' }); this.add('DDP_Rate_Limit_IP_Requests_Allowed', 120000, { type: 'int', enableQuery: { _id: 'DDP_Rate_Limit_IP_Enabled', value: true } }); this.add('DDP_Rate_Limit_IP_Interval_Time', 60000, { type: 'int', enableQuery: { _id: 'DDP_Rate_Limit_IP_Enabled', value: true } }); @@ -2999,12 +2999,16 @@ settingsRegistry.addGroup('Rate Limiter', function() { this.add('DDP_Rate_Limit_Connection_By_Method_Interval_Time', 10000, { type: 'int', enableQuery: { _id: 'DDP_Rate_Limit_Connection_By_Method_Enabled', value: true } }); }); - this.section('API Rate Limiter', function() { + this.section('API_Rate_Limiter', function() { this.add('API_Enable_Rate_Limiter', true, { type: 'boolean' }); this.add('API_Enable_Rate_Limiter_Dev', true, { type: 'boolean', enableQuery: { _id: 'API_Enable_Rate_Limiter', value: true } }); this.add('API_Enable_Rate_Limiter_Limit_Calls_Default', 10, { type: 'int', enableQuery: { _id: 'API_Enable_Rate_Limiter', value: true } }); this.add('API_Enable_Rate_Limiter_Limit_Time_Default', 60000, { type: 'int', enableQuery: { _id: 'API_Enable_Rate_Limiter', value: true } }); }); + + this.section('Feature_Limiting', function() { + this.add('Rate_Limiter_Limit_RegisterUser', 1, { type: 'int', enableQuery: { _id: 'API_Enable_Rate_Limiter', value: true } }); + }); }); settingsRegistry.addGroup('Troubleshoot', function() { diff --git a/packages/rocketchat-i18n/i18n/en.i18n.json b/packages/rocketchat-i18n/i18n/en.i18n.json index a616292d9191..e3d09b5700bb 100644 --- a/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/packages/rocketchat-i18n/i18n/en.i18n.json @@ -370,6 +370,8 @@ "API_Enable_Rate_Limiter_Dev": "Enable Rate Limiter in development", "API_Enable_Rate_Limiter_Dev_Description": "Should limit the amount of calls to the endpoints in the development environment?", "API_Enable_Rate_Limiter_Limit_Calls_Default": "Default number calls to the rate limiter", + "Rate_Limiter_Limit_RegisterUser": "Default number calls to the rate limiter for registering a user", + "Rate_Limiter_Limit_RegisterUser_Description": "Number of default calls for user registering endpoints(REST and real-time API's), allowed within the time range defined in the API Rate Limiter section.", "API_Enable_Rate_Limiter_Limit_Calls_Default_Description": "Number of default calls for each endpoint of the REST API, allowed within the time range defined below", "API_Enable_Rate_Limiter_Limit_Time_Default": "Default time limit for the rate limiter (in ms)", "API_Enable_Rate_Limiter_Limit_Time_Default_Description": "Default timeout to limit the number of calls at each endpoint of the REST API(in ms)", @@ -385,6 +387,7 @@ "API_Personal_Access_Tokens_Regenerate_Modal": "If you lost or forgot your token, you can regenerate it, but remember that all applications that use this token should be updated", "API_Personal_Access_Tokens_Remove_Modal": "Are you sure you wish to remove this personal access token?", "API_Personal_Access_Tokens_To_REST_API": "Personal access tokens to REST API", + "API_Rate_Limiter": "API Rate Limiter", "API_Shield_Types": "Shield Types", "API_Shield_Types_Description": "Types of shields to enable as a comma separated list, choose from `online`, `channel` or `*` for all", "API_Shield_user_require_auth": "Require authentication for users shields", @@ -1340,6 +1343,7 @@ "Days": "Days", "DB_Migration": "Database Migration", "DB_Migration_Date": "Database Migration Date", + "DDP_Rate_Limit": "DDP Rate Limit", "DDP_Rate_Limit_Connection_By_Method_Enabled": "Limit by Connection per Method: enabled", "DDP_Rate_Limit_Connection_By_Method_Interval_Time": "Limit by Connection per Method: interval time", "DDP_Rate_Limit_Connection_By_Method_Requests_Allowed": "Limit by Connection per Method: requests allowed", @@ -1837,6 +1841,7 @@ "Favorite_Rooms": "Enable Favorite Rooms", "Favorites": "Favorites", "Feature_Depends_on_Livechat_Visitor_navigation_as_a_message_to_be_enabled": "This feature depends on \"Send Visitor Navigation History as a Message\" to be enabled.", + "Feature_Limiting": "Feature Limiting", "Features": "Features", "Features_Enabled": "Features Enabled", "Feature_Disabled": "Feature Disabled", diff --git a/packages/rocketchat-i18n/i18n/pt-BR.i18n.json b/packages/rocketchat-i18n/i18n/pt-BR.i18n.json index f34ce0b737cd..04b8f0f6528f 100644 --- a/packages/rocketchat-i18n/i18n/pt-BR.i18n.json +++ b/packages/rocketchat-i18n/i18n/pt-BR.i18n.json @@ -370,7 +370,9 @@ "API_Enable_Rate_Limiter_Dev": "Ativar limitador de taxa em desenvolvimento", "API_Enable_Rate_Limiter_Dev_Description": "Deve limitar a quantidade de chamadas para os endpoints no ambiente de desenvolvimento?", "API_Enable_Rate_Limiter_Limit_Calls_Default": "Número padrão de chamadas para o limitador de taxa", - "API_Enable_Rate_Limiter_Limit_Calls_Default_Description": "Número de chamadas padrão para cada endpoint da API REST, permitido dentro do intervalo de tempo definido abaixo", + "Rate_Limiter_Limit_RegisterUser": "Número de chamadas para as endpoint de registro de usuário", + "Rate_Limiter_Limit_RegisterUser_Description": "Número de chamadas padrão para as endpoints de registro de usuário(API REST e real-time), permitido dentro do intervalo de tempo definido abaixo", + "API_Enable_Rate_Limiter_Limit_Calls_Default_Description": "Número de chamadas padrão para cada endpoint da API REST, permitido dentro do intervalo de tempo definido na seção de limitação de taxa.", "API_Enable_Rate_Limiter_Limit_Time_Default": "Limite de tempo padrão para o limitador de taxa (em ms)", "API_Enable_Rate_Limiter_Limit_Time_Default_Description": "Tempo limite padrão para limitar o número de chamadas em cada endpoint da API REST (em ms)", "API_Enable_Shields": "Ativar Protetores", @@ -385,6 +387,7 @@ "API_Personal_Access_Tokens_Regenerate_Modal": "Se perdeu ou esqueceu o seu código, pode recuperá-lo, mas lembre-se de que todos os aplicativos que usam esse código devem ser atualizados", "API_Personal_Access_Tokens_Remove_Modal": "Tem certeza de que deseja remover este código de acesso pessoal?", "API_Personal_Access_Tokens_To_REST_API": "Código de acesso pessoal para API REST", + "API_Rate_Limiter": "Limitação de Taxa de API", "API_Shield_Types": "Tipos de escudo", "API_Shield_Types_Description": "Tipos de escudos para habilitar como uma lista separada por vírgulas, escolha entre `online`, `channel` ou `*` para todos", "API_Shield_user_require_auth": "Exigir autenticaçāo para escudos de usuários", @@ -1340,6 +1343,7 @@ "Days": "Dias", "DB_Migration": "Migração de Banco de Dados", "DB_Migration_Date": "Data da migração do banco de dados", + "DDP_Rate_Limit": "Limitação de Taxa de DDP", "DDP_Rate_Limit_Connection_By_Method_Enabled": "Limite por Conexão por Método: Habilitado", "DDP_Rate_Limit_Connection_By_Method_Interval_Time": "Limite por Conexão por Método: Intervalo de tempo", "DDP_Rate_Limit_Connection_By_Method_Requests_Allowed": "Limite por Conexão por Método: Solicitações permitidas", @@ -1838,6 +1842,7 @@ "Favorites": "Favoritos", "Feature_Depends_on_Livechat_Visitor_navigation_as_a_message_to_be_enabled": "Esse recurso depende de \"Enviar histórico de navegação do visitante como uma mensagem\" para ser ativado.", "Features": "Funcionalidades", + "Feature_Limiting": "Limitação de funcionalidades", "Features_Enabled": "Funcionalidades habilitadas", "Feature_Disabled": "Funcionalidade desabilitada", "Federation": "Federação", diff --git a/server/methods/registerUser.js b/server/methods/registerUser.js index 802f271efc40..5dd2675bc384 100644 --- a/server/methods/registerUser.js +++ b/server/methods/registerUser.js @@ -2,10 +2,11 @@ import { Meteor } from 'meteor/meteor'; import { Match, check } from 'meteor/check'; import { Accounts } from 'meteor/accounts-base'; import s from 'underscore.string'; +import { DDPRateLimiter } from 'meteor/ddp-rate-limiter'; import { Users } from '../../app/models'; import { settings } from '../../app/settings'; -import { validateEmailDomain, passwordPolicy } from '../../app/lib'; +import { validateEmailDomain, passwordPolicy, RateLimiter } from '../../app/lib'; import { validateInviteToken } from '../../app/invites/server/functions/validateInviteToken'; Meteor.methods({ @@ -97,3 +98,18 @@ Meteor.methods({ return userId; }, }); + +let registerUserRuleId = RateLimiter.limitMethod('registerUser', + settings.get('Rate_Limiter_Limit_RegisterUser'), + settings.get('API_Enable_Rate_Limiter_Limit_Time_Default'), { + userId() { return true; }, + }); + + +settings.watch('Rate_Limiter_Limit_RegisterUser', (value) => { + // remove old DDP rate limiter rule and create a new one with the updated setting value + DDPRateLimiter.removeRule(registerUserRuleId); + registerUserRuleId = RateLimiter.limitMethod('registerUser', value, settings.get('API_Enable_Rate_Limiter_Limit_Time_Default'), { + userId() { return true; }, + }); +});