diff --git a/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java b/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java index 29db1d7d76..470127246d 100644 --- a/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java +++ b/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java @@ -22,6 +22,7 @@ import jakarta.xml.bind.DatatypeConverter; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.exception.ExceptionUtils; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.context.MessageSource; import org.springframework.context.annotation.Lazy; import org.springframework.core.NestedRuntimeException; @@ -33,6 +34,7 @@ import org.springframework.stereotype.Service; import org.springframework.ui.freemarker.FreeMarkerTemplateUtils; import org.thingsboard.rule.engine.api.MailService; import org.thingsboard.rule.engine.api.TbEmail; +import org.thingsboard.server.cache.limits.RateLimitService; import org.thingsboard.server.common.data.AdminSettings; import org.thingsboard.server.common.data.ApiFeature; import org.thingsboard.server.common.data.ApiUsageRecordKey; @@ -43,6 +45,7 @@ import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.limit.LimitedApi; import org.thingsboard.server.common.stats.TbApiUsageReportClient; import org.thingsboard.server.dao.exception.IncorrectParameterException; import org.thingsboard.server.dao.settings.AdminSettingsService; @@ -84,6 +87,12 @@ public class DefaultMailService implements MailService { @Autowired private TbMailContextComponent tbMailContextComponent; + @Autowired + private RateLimitService rateLimitService; + + @Value("${mail.per_tenant_rate_limits:}") + private String perTenantRateLimitConfig; + private TbMailSender mailSender; private String mailFrom; @@ -214,6 +223,10 @@ public class DefaultMailService implements MailService { private void sendMail(TenantId tenantId, CustomerId customerId, TbEmail tbEmail, JavaMailSender javaMailSender, long timeout) throws ThingsboardException { if (apiUsageStateService.getApiUsageState(tenantId).isEmailSendEnabled()) { + if (tenantId != null && !tenantId.isSysTenantId() && StringUtils.isNotEmpty(perTenantRateLimitConfig) && + !rateLimitService.checkRateLimit(LimitedApi.EMAILS, (Object) tenantId, perTenantRateLimitConfig)) { + throw new ThingsboardException("Rate limit for emails sending is exceeded", ThingsboardErrorCode.TOO_MANY_REQUESTS); + } try { MimeMessage mailMsg = javaMailSender.createMimeMessage(); boolean multipart = (tbEmail.getImages() != null && !tbEmail.getImages().isEmpty()); diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 191d810c64..2fc2473851 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -164,6 +164,8 @@ mail: oauth2: # Interval for checking refresh token expiration in seconds(by default, 1 day). refreshTokenCheckingInterval: "${REFRESH_TOKEN_EXPIRATION_CHECKING_INTERVAL:86400}" + # Rate limits for sending mails per tenant + per_tenant_rate_limits: "${MAIL_PER_TENANT_RATE_LIMITS:}" # Usage statistics parameters usage: diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/limit/LimitedApi.java b/common/data/src/main/java/org/thingsboard/server/common/data/limit/LimitedApi.java index 1a0f038c90..48f8fdfc92 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/limit/LimitedApi.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/limit/LimitedApi.java @@ -39,7 +39,8 @@ public enum LimitedApi { TWO_FA_VERIFICATION_CODE_SEND(false, true), TWO_FA_VERIFICATION_CODE_CHECK(false, true), TRANSPORT_MESSAGES_PER_TENANT("transport messages", true), - TRANSPORT_MESSAGES_PER_DEVICE("transport messages per device", false); + TRANSPORT_MESSAGES_PER_DEVICE("transport messages per device", false), + EMAILS("emails", true); private Function configExtractor; @Getter