diff --git a/application/src/main/java/org/thingsboard/server/config/RateLimitProcessingFilter.java b/application/src/main/java/org/thingsboard/server/config/RateLimitProcessingFilter.java index d9b7573d5e..381e767cce 100644 --- a/application/src/main/java/org/thingsboard/server/config/RateLimitProcessingFilter.java +++ b/application/src/main/java/org/thingsboard/server/config/RateLimitProcessingFilter.java @@ -16,16 +16,17 @@ package org.thingsboard.server.config; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Component; import org.springframework.web.filter.GenericFilterBean; import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.msg.tools.TbRateLimits; import org.thingsboard.server.common.msg.tools.TbRateLimitsException; +import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import org.thingsboard.server.exception.ThingsboardErrorResponseHandler; import org.thingsboard.server.service.security.model.SecurityUser; @@ -41,18 +42,21 @@ import java.util.concurrent.ConcurrentMap; @Component public class RateLimitProcessingFilter extends GenericFilterBean { - @Value("${server.rest.limits.tenant.enabled:false}") - private boolean perTenantLimitsEnabled; - @Value("${server.rest.limits.tenant.configuration:}") - private String perTenantLimitsConfiguration; - @Value("${server.rest.limits.customer.enabled:false}") - private boolean perCustomerLimitsEnabled; - @Value("${server.rest.limits.customer.configuration:}") - private String perCustomerLimitsConfiguration; +// @Value("${server.rest.limits.tenant.enabled:false}") +// private boolean perTenantLimitsEnabled; +// @Value("${server.rest.limits.tenant.configuration:}") +// private String perTenantLimitsConfiguration; +// @Value("${server.rest.limits.customer.enabled:false}") +// private boolean perCustomerLimitsEnabled; +// @Value("${server.rest.limits.customer.configuration:}") +// private String perCustomerLimitsConfiguration; @Autowired private ThingsboardErrorResponseHandler errorResponseHandler; + @Autowired + private TbTenantProfileCache tenantProfileCache; + private ConcurrentMap perTenantLimits = new ConcurrentHashMap<>(); private ConcurrentMap perCustomerLimits = new ConcurrentHashMap<>(); @@ -60,18 +64,29 @@ public class RateLimitProcessingFilter extends GenericFilterBean { public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { SecurityUser user = getCurrentUser(); if (user != null && !user.isSystemAdmin()) { - if (perTenantLimitsEnabled) { - TbRateLimits rateLimits = perTenantLimits.computeIfAbsent(user.getTenantId(), id -> new TbRateLimits(perTenantLimitsConfiguration)); - if (!rateLimits.tryConsume()) { - errorResponseHandler.handle(new TbRateLimitsException(EntityType.TENANT), (HttpServletResponse) response); - return; + + var profile= tenantProfileCache.get(user.getTenantId()).getDefaultTenantConfiguration(); + + if(profile != null) { + if (StringUtils.isNotEmpty(profile.getRateLimitsTenantConfiguration())) { + + TbRateLimits rateLimits = perTenantLimits.computeIfAbsent( + user.getTenantId(), id -> new TbRateLimits(profile.getRateLimitsTenantConfiguration()) + ); + if (!rateLimits.tryConsume()) { + errorResponseHandler.handle(new TbRateLimitsException(EntityType.TENANT), (HttpServletResponse) response); + return; + } } - } - if (perCustomerLimitsEnabled && user.isCustomerUser()) { - TbRateLimits rateLimits = perCustomerLimits.computeIfAbsent(user.getCustomerId(), id -> new TbRateLimits(perCustomerLimitsConfiguration)); - if (!rateLimits.tryConsume()) { - errorResponseHandler.handle(new TbRateLimitsException(EntityType.CUSTOMER), (HttpServletResponse) response); - return; + if (StringUtils.isNotEmpty(profile.getRateLimitsCustomerConfiguration())) { + + TbRateLimits rateLimits = perCustomerLimits.computeIfAbsent( + user.getCustomerId(), id -> new TbRateLimits(profile.getRateLimitsCustomerConfiguration()) + ); + if (!rateLimits.tryConsume()) { + errorResponseHandler.handle(new TbRateLimitsException(EntityType.CUSTOMER), (HttpServletResponse) response); + return; + } } } } diff --git a/application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java b/application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java index 0e59030fe2..ea1b02eb7d 100644 --- a/application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java +++ b/application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java @@ -33,6 +33,7 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.msg.tools.TbRateLimits; import org.thingsboard.server.config.WebSocketConfiguration; +import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.model.UserPrincipal; @@ -68,22 +69,11 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr @Autowired private TelemetryWebSocketService webSocketService; + @Autowired + private TbTenantProfileCache tenantProfileCache; + @Value("${server.ws.send_timeout:5000}") private long sendTimeout; - @Value("${server.ws.limits.max_sessions_per_tenant:0}") - private int maxSessionsPerTenant; - @Value("${server.ws.limits.max_sessions_per_customer:0}") - private int maxSessionsPerCustomer; - @Value("${server.ws.limits.max_sessions_per_regular_user:0}") - private int maxSessionsPerRegularUser; - @Value("${server.ws.limits.max_sessions_per_public_user:0}") - private int maxSessionsPerPublicUser; - @Value("${server.ws.limits.max_queue_per_ws_session:1000}") - private int maxMsgQueuePerSession; - - @Value("${server.ws.limits.max_updates_per_session:}") - private String perSessionUpdatesConfiguration; - @Value("${server.ws.ping_timeout:30000}") private long pingTimeout; @@ -124,10 +114,15 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr String internalSessionId = session.getId(); TelemetryWebSocketSessionRef sessionRef = toRef(session); String externalSessionId = sessionRef.getSessionId(); + if (!checkLimits(session, sessionRef)) { return; } - internalSessionMap.put(internalSessionId, new SessionMetaData(session, sessionRef, maxMsgQueuePerSession)); + var tenantProfileConfiguration = tenantProfileCache.get(sessionRef.getSecurityCtx().getTenantId()).getDefaultTenantConfiguration(); + if(tenantProfileConfiguration.getWsLimitQueuePerWsSession() <= 0) { + tenantProfileConfiguration.setWsLimitQueuePerWsSession(500); + } + internalSessionMap.put(internalSessionId, new SessionMetaData(session, sessionRef, tenantProfileConfiguration.getWsLimitQueuePerWsSession())); externalSessionMap.put(externalSessionId, internalSessionId); processInWebSocketService(sessionRef, SessionEvent.onEstablished()); @@ -287,11 +282,14 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr String externalId = sessionRef.getSessionId(); log.debug("[{}] Processing {}", externalId, msg); String internalId = externalSessionMap.get(externalId); + + var tenantProfileConfiguration = tenantProfileCache.get(sessionRef.getSecurityCtx().getTenantId()).getDefaultTenantConfiguration(); + if (internalId != null) { SessionMetaData sessionMd = internalSessionMap.get(internalId); if (sessionMd != null) { - if (!StringUtils.isEmpty(perSessionUpdatesConfiguration)) { - TbRateLimits rateLimits = perSessionUpdateLimits.computeIfAbsent(sessionRef.getSessionId(), sid -> new TbRateLimits(perSessionUpdatesConfiguration)); + if (!StringUtils.isEmpty(tenantProfileConfiguration.getWsLimitUpdatesPerSession())) { + TbRateLimits rateLimits = perSessionUpdateLimits.computeIfAbsent(sessionRef.getSessionId(), sid -> new TbRateLimits(tenantProfileConfiguration.getWsLimitUpdatesPerSession())); if (!rateLimits.tryConsume()) { if (blacklistedSessions.putIfAbsent(externalId, sessionRef) == null) { log.info("[{}][{}][{}] Failed to process session update. Max session updates limit reached" @@ -347,11 +345,18 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr } private boolean checkLimits(WebSocketSession session, TelemetryWebSocketSessionRef sessionRef) throws Exception { + var tenantProfileConfiguration = + tenantProfileCache.get(sessionRef.getSecurityCtx().getTenantId()).getDefaultTenantConfiguration(); + + if(tenantProfileConfiguration == null) { + return true; + } + String sessionId = session.getId(); - if (maxSessionsPerTenant > 0) { + if (tenantProfileConfiguration.getWsLimitMaxSessionsPerTenant() > 0) { Set tenantSessions = tenantSessionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getTenantId(), id -> ConcurrentHashMap.newKeySet()); synchronized (tenantSessions) { - if (tenantSessions.size() < maxSessionsPerTenant) { + if (tenantSessions.size() < tenantProfileConfiguration.getWsLimitMaxSessionsPerTenant()) { tenantSessions.add(sessionId); } else { log.info("[{}][{}][{}] Failed to start session. Max tenant sessions limit reached" @@ -363,10 +368,10 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr } if (sessionRef.getSecurityCtx().isCustomerUser()) { - if (maxSessionsPerCustomer > 0) { + if (tenantProfileConfiguration.getWsLimitMaxSessionsPerCustomer() > 0) { Set customerSessions = customerSessionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getCustomerId(), id -> ConcurrentHashMap.newKeySet()); synchronized (customerSessions) { - if (customerSessions.size() < maxSessionsPerCustomer) { + if (customerSessions.size() < tenantProfileConfiguration.getWsLimitMaxSessionsPerCustomer()) { customerSessions.add(sessionId); } else { log.info("[{}][{}][{}] Failed to start session. Max customer sessions limit reached" @@ -376,10 +381,11 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr } } } - if (maxSessionsPerRegularUser > 0 && UserPrincipal.Type.USER_NAME.equals(sessionRef.getSecurityCtx().getUserPrincipal().getType())) { + if (tenantProfileConfiguration.getWsLimitMaxSessionsPerRegularUser() > 0 + && UserPrincipal.Type.USER_NAME.equals(sessionRef.getSecurityCtx().getUserPrincipal().getType())) { Set regularUserSessions = regularUserSessionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getId(), id -> ConcurrentHashMap.newKeySet()); synchronized (regularUserSessions) { - if (regularUserSessions.size() < maxSessionsPerRegularUser) { + if (regularUserSessions.size() < tenantProfileConfiguration.getWsLimitMaxSessionsPerRegularUser()) { regularUserSessions.add(sessionId); } else { log.info("[{}][{}][{}] Failed to start session. Max regular user sessions limit reached" @@ -389,10 +395,11 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr } } } - if (maxSessionsPerPublicUser > 0 && UserPrincipal.Type.PUBLIC_ID.equals(sessionRef.getSecurityCtx().getUserPrincipal().getType())) { + if (tenantProfileConfiguration.getWsLimitMaxSessionsPerPublicUser() > 0 + && UserPrincipal.Type.PUBLIC_ID.equals(sessionRef.getSecurityCtx().getUserPrincipal().getType())) { Set publicUserSessions = publicUserSessionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getId(), id -> ConcurrentHashMap.newKeySet()); synchronized (publicUserSessions) { - if (publicUserSessions.size() < maxSessionsPerPublicUser) { + if (publicUserSessions.size() < tenantProfileConfiguration.getWsLimitMaxSessionsPerPublicUser()) { publicUserSessions.add(sessionId); } else { log.info("[{}][{}][{}] Failed to start session. Max public user sessions limit reached" @@ -407,29 +414,31 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr } private void cleanupLimits(WebSocketSession session, TelemetryWebSocketSessionRef sessionRef) { + var tenantProfileConfiguration = tenantProfileCache.get(sessionRef.getSecurityCtx().getTenantId()).getDefaultTenantConfiguration(); + String sessionId = session.getId(); perSessionUpdateLimits.remove(sessionRef.getSessionId()); blacklistedSessions.remove(sessionRef.getSessionId()); - if (maxSessionsPerTenant > 0) { + if (tenantProfileConfiguration.getWsLimitMaxSessionsPerTenant() > 0) { Set tenantSessions = tenantSessionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getTenantId(), id -> ConcurrentHashMap.newKeySet()); synchronized (tenantSessions) { tenantSessions.remove(sessionId); } } if (sessionRef.getSecurityCtx().isCustomerUser()) { - if (maxSessionsPerCustomer > 0) { + if (tenantProfileConfiguration.getWsLimitMaxSessionsPerCustomer() > 0) { Set customerSessions = customerSessionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getCustomerId(), id -> ConcurrentHashMap.newKeySet()); synchronized (customerSessions) { customerSessions.remove(sessionId); } } - if (maxSessionsPerRegularUser > 0 && UserPrincipal.Type.USER_NAME.equals(sessionRef.getSecurityCtx().getUserPrincipal().getType())) { + if (tenantProfileConfiguration.getWsLimitMaxSessionsPerRegularUser() > 0 && UserPrincipal.Type.USER_NAME.equals(sessionRef.getSecurityCtx().getUserPrincipal().getType())) { Set regularUserSessions = regularUserSessionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getId(), id -> ConcurrentHashMap.newKeySet()); synchronized (regularUserSessions) { regularUserSessions.remove(sessionId); } } - if (maxSessionsPerPublicUser > 0 && UserPrincipal.Type.PUBLIC_ID.equals(sessionRef.getSecurityCtx().getUserPrincipal().getType())) { + if (tenantProfileConfiguration.getWsLimitMaxSessionsPerPublicUser() > 0 && UserPrincipal.Type.PUBLIC_ID.equals(sessionRef.getSecurityCtx().getUserPrincipal().getType())) { Set publicUserSessions = publicUserSessionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getId(), id -> ConcurrentHashMap.newKeySet()); synchronized (publicUserSessions) { publicUserSessions.remove(sessionId); diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetryWebSocketService.java b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetryWebSocketService.java index 73eed820ab..edb36c11c8 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetryWebSocketService.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetryWebSocketService.java @@ -24,7 +24,6 @@ import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; import org.springframework.web.socket.CloseStatus; @@ -42,7 +41,9 @@ import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery; import org.thingsboard.server.common.data.kv.BasicTsKvEntry; import org.thingsboard.server.common.data.kv.ReadTsKvQuery; import org.thingsboard.server.common.data.kv.TsKvEntry; +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.dao.attributes.AttributesService; +import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import org.thingsboard.server.dao.timeseries.TimeseriesService; import org.thingsboard.server.dao.util.TenantRateLimitException; import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; @@ -136,15 +137,8 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi @Autowired private TbServiceInfoProvider serviceInfoProvider; - - @Value("${server.ws.limits.max_subscriptions_per_tenant:0}") - private int maxSubscriptionsPerTenant; - @Value("${server.ws.limits.max_subscriptions_per_customer:0}") - private int maxSubscriptionsPerCustomer; - @Value("${server.ws.limits.max_subscriptions_per_regular_user:0}") - private int maxSubscriptionsPerRegularUser; - @Value("${server.ws.limits.max_subscriptions_per_public_user:0}") - private int maxSubscriptionsPerPublicUser; + @Autowired + private TbTenantProfileCache tenantProfileCache; private ConcurrentMap> tenantSubscriptionsMap = new ConcurrentHashMap<>(); private ConcurrentMap> customerSubscriptionsMap = new ConcurrentHashMap<>(); @@ -304,44 +298,50 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi } private void processSessionClose(TelemetryWebSocketSessionRef sessionRef) { - String sessionId = "[" + sessionRef.getSessionId() + "]"; - if (maxSubscriptionsPerTenant > 0) { - Set tenantSubscriptions = tenantSubscriptionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getTenantId(), id -> ConcurrentHashMap.newKeySet()); - synchronized (tenantSubscriptions) { - tenantSubscriptions.removeIf(subId -> subId.startsWith(sessionId)); - } - } - if (sessionRef.getSecurityCtx().isCustomerUser()) { - if (maxSubscriptionsPerCustomer > 0) { - Set customerSessions = customerSubscriptionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getCustomerId(), id -> ConcurrentHashMap.newKeySet()); - synchronized (customerSessions) { - customerSessions.removeIf(subId -> subId.startsWith(sessionId)); + var tenantProfileConfiguration = (DefaultTenantProfileConfiguration) tenantProfileCache.get(sessionRef.getSecurityCtx().getTenantId()).getDefaultTenantConfiguration(); + if(tenantProfileConfiguration != null) { + String sessionId = "[" + sessionRef.getSessionId() + "]"; + + if (tenantProfileConfiguration.getWsLimitMaxSubscriptionsPerTenant() > 0) { + Set tenantSubscriptions = tenantSubscriptionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getTenantId(), id -> ConcurrentHashMap.newKeySet()); + synchronized (tenantSubscriptions) { + tenantSubscriptions.removeIf(subId -> subId.startsWith(sessionId)); } } - if (maxSubscriptionsPerRegularUser > 0 && UserPrincipal.Type.USER_NAME.equals(sessionRef.getSecurityCtx().getUserPrincipal().getType())) { - Set regularUserSessions = regularUserSubscriptionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getId(), id -> ConcurrentHashMap.newKeySet()); - synchronized (regularUserSessions) { - regularUserSessions.removeIf(subId -> subId.startsWith(sessionId)); + if (sessionRef.getSecurityCtx().isCustomerUser()) { + if (tenantProfileConfiguration.getWsLimitMaxSubscriptionsPerCustomer() > 0) { + Set customerSessions = customerSubscriptionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getCustomerId(), id -> ConcurrentHashMap.newKeySet()); + synchronized (customerSessions) { + customerSessions.removeIf(subId -> subId.startsWith(sessionId)); + } } - } - if (maxSubscriptionsPerPublicUser > 0 && UserPrincipal.Type.PUBLIC_ID.equals(sessionRef.getSecurityCtx().getUserPrincipal().getType())) { - Set publicUserSessions = publicUserSubscriptionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getId(), id -> ConcurrentHashMap.newKeySet()); - synchronized (publicUserSessions) { - publicUserSessions.removeIf(subId -> subId.startsWith(sessionId)); + if (tenantProfileConfiguration.getWsLimitMaxSubscriptionsPerRegularUser() > 0 && UserPrincipal.Type.USER_NAME.equals(sessionRef.getSecurityCtx().getUserPrincipal().getType())) { + Set regularUserSessions = regularUserSubscriptionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getId(), id -> ConcurrentHashMap.newKeySet()); + synchronized (regularUserSessions) { + regularUserSessions.removeIf(subId -> subId.startsWith(sessionId)); + } + } + if (tenantProfileConfiguration.getWsLimitMaxSubscriptionsPerPublicUser() > 0 && UserPrincipal.Type.PUBLIC_ID.equals(sessionRef.getSecurityCtx().getUserPrincipal().getType())) { + Set publicUserSessions = publicUserSubscriptionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getId(), id -> ConcurrentHashMap.newKeySet()); + synchronized (publicUserSessions) { + publicUserSessions.removeIf(subId -> subId.startsWith(sessionId)); + } } } } } private boolean processSubscription(TelemetryWebSocketSessionRef sessionRef, SubscriptionCmd cmd) { + var tenantProfileConfiguration = (DefaultTenantProfileConfiguration) tenantProfileCache.get(sessionRef.getSecurityCtx().getTenantId()).getDefaultTenantConfiguration(); + String subId = "[" + sessionRef.getSessionId() + "]:[" + cmd.getCmdId() + "]"; try { - if (maxSubscriptionsPerTenant > 0) { + if (tenantProfileConfiguration.getWsLimitMaxSubscriptionsPerTenant() > 0) { Set tenantSubscriptions = tenantSubscriptionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getTenantId(), id -> ConcurrentHashMap.newKeySet()); synchronized (tenantSubscriptions) { if (cmd.isUnsubscribe()) { tenantSubscriptions.remove(subId); - } else if (tenantSubscriptions.size() < maxSubscriptionsPerTenant) { + } else if (tenantSubscriptions.size() < tenantProfileConfiguration.getWsLimitMaxSubscriptionsPerTenant()) { tenantSubscriptions.add(subId); } else { log.info("[{}][{}][{}] Failed to start subscription. Max tenant subscriptions limit reached" @@ -353,12 +353,12 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi } if (sessionRef.getSecurityCtx().isCustomerUser()) { - if (maxSubscriptionsPerCustomer > 0) { + if (tenantProfileConfiguration.getWsLimitMaxSubscriptionsPerCustomer() > 0) { Set customerSessions = customerSubscriptionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getCustomerId(), id -> ConcurrentHashMap.newKeySet()); synchronized (customerSessions) { if (cmd.isUnsubscribe()) { customerSessions.remove(subId); - } else if (customerSessions.size() < maxSubscriptionsPerCustomer) { + } else if (customerSessions.size() < tenantProfileConfiguration.getWsLimitMaxSubscriptionsPerCustomer()) { customerSessions.add(subId); } else { log.info("[{}][{}][{}] Failed to start subscription. Max customer subscriptions limit reached" @@ -368,10 +368,10 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi } } } - if (maxSubscriptionsPerRegularUser > 0 && UserPrincipal.Type.USER_NAME.equals(sessionRef.getSecurityCtx().getUserPrincipal().getType())) { + if (tenantProfileConfiguration.getWsLimitMaxSubscriptionsPerRegularUser() > 0 && UserPrincipal.Type.USER_NAME.equals(sessionRef.getSecurityCtx().getUserPrincipal().getType())) { Set regularUserSessions = regularUserSubscriptionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getId(), id -> ConcurrentHashMap.newKeySet()); synchronized (regularUserSessions) { - if (regularUserSessions.size() < maxSubscriptionsPerRegularUser) { + if (regularUserSessions.size() < tenantProfileConfiguration.getWsLimitMaxSubscriptionsPerRegularUser()) { regularUserSessions.add(subId); } else { log.info("[{}][{}][{}] Failed to start subscription. Max regular user subscriptions limit reached" @@ -381,10 +381,10 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi } } } - if (maxSubscriptionsPerPublicUser > 0 && UserPrincipal.Type.PUBLIC_ID.equals(sessionRef.getSecurityCtx().getUserPrincipal().getType())) { + if (tenantProfileConfiguration.getWsLimitMaxSubscriptionsPerPublicUser() > 0 && UserPrincipal.Type.PUBLIC_ID.equals(sessionRef.getSecurityCtx().getUserPrincipal().getType())) { Set publicUserSessions = publicUserSubscriptionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getId(), id -> ConcurrentHashMap.newKeySet()); synchronized (publicUserSessions) { - if (publicUserSessions.size() < maxSubscriptionsPerPublicUser) { + if (publicUserSessions.size() < tenantProfileConfiguration.getWsLimitMaxSubscriptionsPerPublicUser()) { publicUserSessions.add(subId); } else { log.info("[{}][{}][{}] Failed to start subscription. Max public user subscriptions limit reached" diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/TenantProfile.java b/common/data/src/main/java/org/thingsboard/server/common/data/TenantProfile.java index a0fefea6cc..5efc041806 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/TenantProfile.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/TenantProfile.java @@ -107,4 +107,11 @@ public class TenantProfile extends SearchTextBased implements H } } + public DefaultTenantProfileConfiguration getDefaultTenantConfiguration() { + if(this.profileData != null && this.profileData.getConfiguration().getType().equals(TenantProfileType.DEFAULT)) { + return (DefaultTenantProfileConfiguration) this.profileData.getConfiguration(); + } + return null; + } + } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java index b9bd72b0db..4fe4811350 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java @@ -42,6 +42,23 @@ public class DefaultTenantProfileConfiguration implements TenantProfileConfigura private String transportDeviceTelemetryMsgRateLimit; private String transportDeviceTelemetryDataPointsRateLimit; + private String rateLimitsTenantConfiguration; + private String rateLimitsCustomerConfiguration; + + private int wsLimitMaxSessionsPerTenant; + private int wsLimitMaxSessionsPerCustomer; + private int wsLimitMaxSessionsPerRegularUser; + private int wsLimitMaxSessionsPerPublicUser; + private int wsLimitQueuePerWsSession; + private long wsLimitMaxSubscriptionsPerTenant; + private long wsLimitMaxSubscriptionsPerCustomer; + private long wsLimitMaxSubscriptionsPerRegularUser; + private long wsLimitMaxSubscriptionsPerPublicUser; + private String wsLimitUpdatesPerSession; + + private String cassandraTenantLimitsConfiguration; + private boolean printTenantNames; + private long maxTransportMessages; private long maxTransportDataPoints; private long maxREExecutions; diff --git a/dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraBufferedRateExecutor.java b/dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraBufferedRateExecutor.java index ac61a73f0c..02662e058d 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraBufferedRateExecutor.java +++ b/dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraBufferedRateExecutor.java @@ -22,14 +22,18 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.msg.tools.TbRateLimits; import org.thingsboard.server.common.stats.DefaultCounter; import org.thingsboard.server.common.stats.StatsCounter; import org.thingsboard.server.common.stats.StatsFactory; import org.thingsboard.server.dao.entity.EntityService; +import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import org.thingsboard.server.dao.util.AbstractBufferedRateExecutor; import org.thingsboard.server.dao.util.AsyncTaskContext; import org.thingsboard.server.dao.util.NoSqlAnyDao; +import org.thingsboard.server.dao.util.TenantRateLimitException; import javax.annotation.PreDestroy; import java.util.HashMap; @@ -45,6 +49,9 @@ public class CassandraBufferedRateExecutor extends AbstractBufferedRateExecutor< @Autowired private EntityService entityService; + @Autowired + private TbTenantProfileCache tenantProfileCache; + private Map tenantNamesCache = new HashMap<>(); private boolean printTenantNames; @@ -56,12 +63,10 @@ public class CassandraBufferedRateExecutor extends AbstractBufferedRateExecutor< @Value("${cassandra.query.dispatcher_threads:2}") int dispatcherThreads, @Value("${cassandra.query.callback_threads:4}") int callbackThreads, @Value("${cassandra.query.poll_ms:50}") long pollMs, - @Value("${cassandra.query.tenant_rate_limits.enabled}") boolean tenantRateLimitsEnabled, - @Value("${cassandra.query.tenant_rate_limits.configuration}") String tenantRateLimitsConfiguration, @Value("${cassandra.query.tenant_rate_limits.print_tenant_names}") boolean printTenantNames, @Value("${cassandra.query.print_queries_freq:0}") int printQueriesFreq, @Autowired StatsFactory statsFactory) { - super(queueLimit, concurrencyLimit, maxWaitTime, dispatcherThreads, callbackThreads, pollMs, tenantRateLimitsEnabled, tenantRateLimitsConfiguration, printQueriesFreq, statsFactory); + super(queueLimit, concurrencyLimit, maxWaitTime, dispatcherThreads, callbackThreads, pollMs, printQueriesFreq, statsFactory); this.printTenantNames = printTenantNames; } @@ -97,7 +102,8 @@ public class CassandraBufferedRateExecutor extends AbstractBufferedRateExecutor< DefaultCounter counter = entry.getValue(); int rateLimitedRequests = counter.get(); counter.clear(); - if (printTenantNames) { + var profile = tenantProfileCache.get(tenantId).getDefaultTenantConfiguration(); + if (profile != null && profile.isPrintTenantNames()) { String name = tenantNamesCache.computeIfAbsent(tenantId, tId -> { try { return entityService.fetchEntityNameAsync(TenantId.SYS_TENANT_ID, tenantId).get(); @@ -137,4 +143,24 @@ public class CassandraBufferedRateExecutor extends AbstractBufferedRateExecutor< ); } + @Override + protected boolean checkRateLimits(CassandraStatementTask task, SettableFuture future) { + var tenantProfileConfiguration = tenantProfileCache.get(task.getTenantId()).getDefaultTenantConfiguration(); + if (StringUtils.isNotEmpty(tenantProfileConfiguration.getCassandraTenantLimitsConfiguration())) { + if (task.getTenantId() == null) { + log.info("Invalid task received: {}", task); + } else if (!task.getTenantId().isNullUid()) { + TbRateLimits rateLimits = perTenantLimits.computeIfAbsent( + task.getTenantId(), id -> new TbRateLimits(tenantProfileConfiguration.getCassandraTenantLimitsConfiguration()) + ); + if (!rateLimits.tryConsume()) { + stats.incrementRateLimitedTenant(task.getTenantId()); + stats.getTotalRateLimited().increment(); + future.setException(new TenantRateLimitException()); + return true; + } + } + } + return false; + } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/util/AbstractBufferedRateExecutor.java b/dao/src/main/java/org/thingsboard/server/dao/util/AbstractBufferedRateExecutor.java index 157755db19..f201f36a09 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/util/AbstractBufferedRateExecutor.java +++ b/dao/src/main/java/org/thingsboard/server/dao/util/AbstractBufferedRateExecutor.java @@ -66,9 +66,7 @@ public abstract class AbstractBufferedRateExecutor perTenantLimits = new ConcurrentHashMap<>(); + protected final ConcurrentMap perTenantLimits = new ConcurrentHashMap<>(); private final AtomicInteger printQueriesIdx = new AtomicInteger(0); @@ -76,7 +74,7 @@ public abstract class AbstractBufferedRateExecutor future); + @Override public F submit(T task) { SettableFuture settableFuture = create(); F result = wrap(task, settableFuture); - boolean perTenantLimitReached = false; - if (perTenantLimitsEnabled) { - if (task.getTenantId() == null) { - log.info("Invalid task received: {}", task); - } else if (!task.getTenantId().isNullUid()) { - TbRateLimits rateLimits = perTenantLimits.computeIfAbsent(task.getTenantId(), id -> new TbRateLimits(perTenantLimitsConfiguration)); - if (!rateLimits.tryConsume()) { - stats.incrementRateLimitedTenant(task.getTenantId()); - stats.getTotalRateLimited().increment(); - settableFuture.setException(new TenantRateLimitException()); - perTenantLimitReached = true; - } - } - } + boolean perTenantLimitReached = checkRateLimits(task, settableFuture); + if (!perTenantLimitReached) { try { stats.getTotalAdded().increment(); diff --git a/ui-ngx/src/app/modules/home/components/profile/tenant-profile-data.component.html b/ui-ngx/src/app/modules/home/components/profile/tenant-profile-data.component.html index 9af8d294de..1fdc6aba83 100644 --- a/ui-ngx/src/app/modules/home/components/profile/tenant-profile-data.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/tenant-profile-data.component.html @@ -16,17 +16,21 @@ -->
- - - -
tenant-profile.profile-configuration
-
-
- - - - -
+ + + + + + + + + + + + + + +
diff --git a/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.html b/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.html index 13e3976299..78ba0ba1da 100644 --- a/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.html @@ -15,221 +15,371 @@ limitations under the License. --> -
- - tenant-profile.maximum-devices - - - {{ 'tenant-profile.maximum-devices-required' | translate}} - - - {{ 'tenant-profile.maximum-devices-range' | translate}} - - - - tenant-profile.maximum-assets - - - {{ 'tenant-profile.maximum-assets-required' | translate}} - - - {{ 'tenant-profile.maximum-assets-range' | translate}} - - - - tenant-profile.maximum-customers - - - {{ 'tenant-profile.maximum-customers-required' | translate}} - - - {{ 'tenant-profile.maximum-customers-range' | translate}} - - - - tenant-profile.maximum-users - - - {{ 'tenant-profile.maximum-users-required' | translate}} - - - {{ 'tenant-profile.maximum-users-range' | translate}} - - - - tenant-profile.maximum-dashboards - - - {{ 'tenant-profile.maximum-dashboards-required' | translate}} - - - {{ 'tenant-profile.maximum-dashboards-range' | translate}} - - - - tenant-profile.maximum-rule-chains - - - {{ 'tenant-profile.maximum-rule-chains-required' | translate}} - - - {{ 'tenant-profile.maximum-rule-chains-range' | translate}} - - - - tenant-profile.max-transport-messages - - - {{ 'tenant-profile.max-transport-messages-range' | translate}} - - - {{ 'tenant-profile.max-transport-messages-required' | translate}} - - - - tenant-profile.max-transport-data-points - - - {{ 'tenant-profile.max-transport-data-points-required' | translate}} - - - {{ 'tenant-profile.max-transport-data-points-range' | translate}} - - - - tenant-profile.max-r-e-executions - - - {{ 'tenant-profile.max-r-e-executions-required' | translate}} - - - {{ 'tenant-profile.max-r-e-executions-range' | translate}} - - - - tenant-profile.max-j-s-executions - - - {{ 'tenant-profile.max-j-s-executions-required' | translate}} - - - {{ 'tenant-profile.max-j-s-executions-range' | translate}} - - - - tenant-profile.max-d-p-storage-days - - - {{ 'tenant-profile.max-d-p-storage-days-required' | translate}} - - - {{ 'tenant-profile.max-d-p-storage-days-range' | translate}} - - - - tenant-profile.default-storage-ttl-days - - - {{ 'tenant-profile.default-storage-ttl-days-required' | translate}} - - - {{ 'tenant-profile.default-storage-ttl-days-range' | translate}} - - - - tenant-profile.max-rule-node-executions-per-message - - - {{ 'tenant-profile.max-rule-node-executions-per-message-required' | translate}} - - - {{ 'tenant-profile.max-rule-node-executions-per-message-range' | translate}} - - - - tenant-profile.max-emails - - - {{ 'tenant-profile.max-emails-required' | translate}} - - - {{ 'tenant-profile.max-emails-range' | translate}} - - - - tenant-profile.max-sms - - - {{ 'tenant-profile.max-sms-required' | translate}} - - - {{ 'tenant-profile.max-sms-range' | translate}} - - - - tenant-profile.max-created-alarms - - - {{ 'tenant-profile.max-created-alarms-required' | translate}} - - - {{ 'tenant-profile.max-created-alarms-range' | translate}} - - - - tenant-profile.transport-tenant-msg-rate-limit - - - - tenant-profile.transport-tenant-telemetry-msg-rate-limit - - - - tenant-profile.transport-tenant-telemetry-data-points-rate-limit - - - - tenant-profile.transport-device-msg-rate-limit - - - - tenant-profile.transport-device-telemetry-msg-rate-limit - - - - tenant-profile.transport-device-telemetry-data-points-rate-limit - - +
+ + + + Thingsboard rate limits + + + + tenant-profile.maximum-devices + + + {{ 'tenant-profile.maximum-devices-required' | translate}} + + + {{ 'tenant-profile.maximum-devices-range' | translate}} + + + + tenant-profile.maximum-assets + + + {{ 'tenant-profile.maximum-assets-required' | translate}} + + + {{ 'tenant-profile.maximum-assets-range' | translate}} + + + + tenant-profile.maximum-customers + + + {{ 'tenant-profile.maximum-customers-required' | translate}} + + + {{ 'tenant-profile.maximum-customers-range' | translate}} + + + + tenant-profile.maximum-users + + + {{ 'tenant-profile.maximum-users-required' | translate}} + + + {{ 'tenant-profile.maximum-users-range' | translate}} + + + + tenant-profile.maximum-dashboards + + + {{ 'tenant-profile.maximum-dashboards-required' | translate}} + + + {{ 'tenant-profile.maximum-dashboards-range' | translate}} + + + + tenant-profile.maximum-rule-chains + + + {{ 'tenant-profile.maximum-rule-chains-required' | translate}} + + + {{ 'tenant-profile.maximum-rule-chains-range' | translate}} + + + + tenant-profile.max-transport-messages + + + {{ 'tenant-profile.max-transport-messages-range' | translate}} + + + {{ 'tenant-profile.max-transport-messages-required' | translate}} + + + + tenant-profile.max-transport-data-points + + + {{ 'tenant-profile.max-transport-data-points-required' | translate}} + + + {{ 'tenant-profile.max-transport-data-points-range' | translate}} + + + + tenant-profile.max-r-e-executions + + + {{ 'tenant-profile.max-r-e-executions-required' | translate}} + + + {{ 'tenant-profile.max-r-e-executions-range' | translate}} + + + + tenant-profile.max-j-s-executions + + + {{ 'tenant-profile.max-j-s-executions-required' | translate}} + + + {{ 'tenant-profile.max-j-s-executions-range' | translate}} + + + + tenant-profile.max-d-p-storage-days + + + {{ 'tenant-profile.max-d-p-storage-days-required' | translate}} + + + {{ 'tenant-profile.max-d-p-storage-days-range' | translate}} + + + + tenant-profile.default-storage-ttl-days + + + {{ 'tenant-profile.default-storage-ttl-days-required' | translate}} + + + {{ 'tenant-profile.default-storage-ttl-days-range' | translate}} + + + + tenant-profile.max-rule-node-executions-per-message + + + {{ 'tenant-profile.max-rule-node-executions-per-message-required' | translate}} + + + {{ 'tenant-profile.max-rule-node-executions-per-message-range' | translate}} + + + + tenant-profile.max-emails + + + {{ 'tenant-profile.max-emails-required' | translate}} + + + {{ 'tenant-profile.max-emails-range' | translate}} + + + + tenant-profile.max-sms + + + {{ 'tenant-profile.max-sms-required' | translate}} + + + {{ 'tenant-profile.max-sms-range' | translate}} + + + + tenant-profile.max-created-alarms + + + {{ 'tenant-profile.max-created-alarms-required' | translate}} + + + {{ 'tenant-profile.max-created-alarms-range' | translate}} + + + + + + + + Server rate limits + + + + tenant-profile.tenant-rest-limits + + + {{ 'tenant-profile.incorrect-pattern-for-rate-limits' | translate}} + + + + tenant-profile.customer-rest-limits + + + {{ 'tenant-profile.incorrect-pattern-for-rate-limits' | translate}} + + + + + + + + Transport rate limits + + + + tenant-profile.transport-tenant-msg-rate-limit + + + {{ 'tenant-profile.incorrect-pattern-for-rate-limits' | translate}} + + + + tenant-profile.transport-tenant-telemetry-msg-rate-limit + + + {{ 'tenant-profile.incorrect-pattern-for-rate-limits' | translate}} + + + + tenant-profile.transport-tenant-telemetry-data-points-rate-limit + + + {{ 'tenant-profile.incorrect-pattern-for-rate-limits' | translate}} + + + + tenant-profile.transport-device-msg-rate-limit + + + {{ 'tenant-profile.incorrect-pattern-for-rate-limits' | translate}} + + + + tenant-profile.transport-device-telemetry-msg-rate-limit + + + {{ 'tenant-profile.incorrect-pattern-for-rate-limits' | translate}} + + + + tenant-profile.transport-device-telemetry-data-points-rate-limit + + + {{ 'tenant-profile.incorrect-pattern-for-rate-limits' | translate}} + + + + + + + + Web Socket rate limits + + + + tenant-profile.ws-limit-max-sessions-per-tenant + + + {{ 'tenant-profile.too-small-value-zero' | translate}} + + + + tenant-profile.ws-limit-max-sessions-per-customer + + + {{ 'tenant-profile.too-small-value-zero' | translate}} + + + + tenant-profile.ws-limit-max-sessions-per-public-user + + + {{ 'tenant-profile.too-small-value-zero' | translate}} + + + + tenant-profile.ws-limit-queue-per-session + + + {{ 'tenant-profile.too-small-value-one' | translate}} + + + + tenant-profile.ws-limit-max-subscriptions-per-tenant + + + {{ 'tenant-profile.too-small-value-zero' | translate}} + + + + tenant-profile.ws-limit-max-subscriptions-per-customer + + + {{ 'tenant-profile.too-small-value-zero' | translate}} + + + + tenant-profile.ws-limit-max-subscriptions-per-regular-user + + + {{ 'tenant-profile.too-small-value-zero' | translate}} + + + + tenant-profile.ws-limit-max-subscriptions-per-public-user + + + {{ 'tenant-profile.too-small-value-zero' | translate}} + + + + tenant-profile.ws-limit-updates-per-session + + + {{ 'tenant-profile.incorrect-pattern-for-rate-limits' | translate}} + + + + + + + + Cassandra rate limits + + + + tenant-profile.cassandra-tenant-limits-configuration + + + {{ 'tenant-profile.incorrect-pattern-for-rate-limits' | translate}} + + + + {{ 'tenant-profile.cassandra-print-tenant-names' | translate }} + + + +
diff --git a/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.ts b/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.ts index ad00df7476..6509ef0b20 100644 --- a/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.ts @@ -35,6 +35,7 @@ import { isDefinedAndNotNull } from '@core/utils'; export class DefaultTenantProfileConfigurationComponent implements ControlValueAccessor, OnInit { defaultTenantProfileConfigurationFormGroup: FormGroup; + rateLimitsPattern = '^(((\\d+):(\\d+)),)*((\\d+):(\\d+))$'; private requiredValue: boolean; get required(): boolean { @@ -59,12 +60,12 @@ export class DefaultTenantProfileConfigurationComponent implements ControlValueA maxUsers: [null, [Validators.required, Validators.min(0)]], maxDashboards: [null, [Validators.required, Validators.min(0)]], maxRuleChains: [null, [Validators.required, Validators.min(0)]], - transportTenantMsgRateLimit: [null, []], - transportTenantTelemetryMsgRateLimit: [null, []], - transportTenantTelemetryDataPointsRateLimit: [null, []], - transportDeviceMsgRateLimit: [null, []], - transportDeviceTelemetryMsgRateLimit: [null, []], - transportDeviceTelemetryDataPointsRateLimit: [null, []], + transportTenantMsgRateLimit: [null, [Validators.pattern(this.rateLimitsPattern)]], + transportTenantTelemetryMsgRateLimit: [null, [Validators.pattern(this.rateLimitsPattern)]], + transportTenantTelemetryDataPointsRateLimit: [null, [Validators.pattern(this.rateLimitsPattern)]], + transportDeviceMsgRateLimit: [null, [Validators.pattern(this.rateLimitsPattern)]], + transportDeviceTelemetryMsgRateLimit: [null, [Validators.pattern(this.rateLimitsPattern)]], + transportDeviceTelemetryDataPointsRateLimit: [null, [Validators.pattern(this.rateLimitsPattern)]], maxTransportMessages: [null, [Validators.required, Validators.min(0)]], maxTransportDataPoints: [null, [Validators.required, Validators.min(0)]], maxREExecutions: [null, [Validators.required, Validators.min(0)]], @@ -74,7 +75,23 @@ export class DefaultTenantProfileConfigurationComponent implements ControlValueA maxEmails: [null, [Validators.required, Validators.min(0)]], maxSms: [null, [Validators.required, Validators.min(0)]], maxCreatedAlarms: [null, [Validators.required, Validators.min(0)]], - defaultStorageTtlDays: [null, [Validators.required, Validators.min(0)]] + defaultStorageTtlDays: [null, [Validators.required, Validators.min(0)]], + + rateLimitsTenantConfiguration: [null, [Validators.pattern(this.rateLimitsPattern)]], + rateLimitsCustomerConfiguration: [null, [Validators.pattern(this.rateLimitsPattern)]], + + wsLimitMaxSessionsPerTenant: [null, [Validators.min(0)]], + wsLimitMaxSessionsPerCustomer: [null, [Validators.min(0)]], + wsLimitMaxSessionsPerPublicUser: [null, [Validators.min(0)]], + wsLimitQueuePerWsSession: [null, [Validators.min(0)]], + wsLimitMaxSubscriptionsPerTenant: [null, [Validators.min(0)]], + wsLimitMaxSubscriptionsPerCustomer: [null, [Validators.min(0)]], + wsLimitMaxSubscriptionsPerRegularUser: [null, [Validators.min(0)]], + wsLimitMaxSubscriptionsPerPublicUser: [null, [Validators.min(0)]], + wsLimitUpdatesPerSession: [null, [Validators.pattern(this.rateLimitsPattern)]], + + cassandraTenantLimitsConfiguration: [null, [Validators.pattern(this.rateLimitsPattern)]], + printTenantNames: [null, []] }); this.defaultTenantProfileConfigurationFormGroup.valueChanges.subscribe(() => { this.updateModel(); diff --git a/ui-ngx/src/app/shared/models/tenant.model.ts b/ui-ngx/src/app/shared/models/tenant.model.ts index e59832fc85..91e874d530 100644 --- a/ui-ngx/src/app/shared/models/tenant.model.ts +++ b/ui-ngx/src/app/shared/models/tenant.model.ts @@ -46,6 +46,24 @@ export interface DefaultTenantProfileConfiguration { maxRuleNodeExecutionsPerMessage: number; maxEmails: number; maxSms: number; + maxCreatedAlarms: number; + + rateLimitsTenantConfiguration: string; + rateLimitsCustomerConfiguration: string; + + wsLimitMaxSessionsPerTenant: number; + wsLimitMaxSessionsPerCustomer: number; + wsLimitMaxSessionsPerRegularUser: number; + wsLimitMaxSessionsPerPublicUser: number; + wsLimitQueuePerWsSession: number; + wsLimitMaxSubscriptionsPerTenant: number; + wsLimitMaxSubscriptionsPerCustomer: number; + wsLimitMaxSubscriptionsPerRegularUser: number; + wsLimitMaxSubscriptionsPerPublicUser: number; + wsLimitUpdatesPerSession: string; + + cassandraTenantLimitsConfiguration: string; + printTenantNames: boolean; defaultStorageTtlDays: number; } @@ -76,7 +94,25 @@ export function createTenantProfileConfiguration(type: TenantProfileType): Tenan maxRuleNodeExecutionsPerMessage: 0, maxEmails: 0, maxSms: 0, - defaultStorageTtlDays: 0 + defaultStorageTtlDays: 0, + maxCreatedAlarms: 0, + + rateLimitsTenantConfiguration: '', + rateLimitsCustomerConfiguration: '', + + wsLimitMaxSessionsPerTenant: 0, + wsLimitMaxSessionsPerCustomer: 0, + wsLimitMaxSessionsPerRegularUser: 0, + wsLimitMaxSessionsPerPublicUser: 0, + wsLimitQueuePerWsSession: 500, + wsLimitMaxSubscriptionsPerTenant: 0, + wsLimitMaxSubscriptionsPerCustomer: 0, + wsLimitMaxSubscriptionsPerRegularUser: 0, + wsLimitMaxSubscriptionsPerPublicUser: 0, + wsLimitUpdatesPerSession: '' , + + cassandraTenantLimitsConfiguration: '', + printTenantNames: false }; configuration = {...defaultConfiguration, type: TenantProfileType.DEFAULT}; break; diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 3da7b4f4bb..837e47b3d6 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -2533,7 +2533,23 @@ "max-sms-range": "Maximum number of SMS sent can't be negative", "max-created-alarms": "Maximum number of alarms created (0 - unlimited)", "max-created-alarms-required": "Maximum number of alarms created is required.", - "max-created-alarms-range": "Maximum number of alarms created can't be negative" + "max-created-alarms-range": "Maximum number of alarms created can't be negative", + "tenant-rest-limits": "Server limits of request for Tenant", + "customer-rest-limits": "Server limits of request for Customer", + "incorrect-pattern-for-rate-limits": "Here should be conformity of a number of requests per seconds divided by colon, comma separated. For example: 100:1,2000:60", + "too-small-value-zero": "Too small value specified, it should be more than 0", + "too-small-value-one": "Too small value specified, it should be more than 1", + "cassandra-tenant-limits-configuration": "Tenant limits configuration", + "ws-limit-max-sessions-per-tenant": "Max sessions per Tenant", + "ws-limit-max-sessions-per-customer": "Max sessions per Customer", + "ws-limit-max-sessions-per-public-user": "Max sessions per Public User", + "ws-limit-queue-per-session": "Limit queue per session", + "ws-limit-max-subscriptions-per-tenant": "Max subscriptions per Tenant", + "ws-limit-max-subscriptions-per-customer": "Max subscriptions per Customer", + "ws-limit-max-subscriptions-per-regular-user": "Max subscriptions per regular User", + "ws-limit-max-subscriptions-per-public-user": "Max subscriptions per public User", + "ws-limit-updates-per-session": "Constraint of updates per session", + "cassandra-print-tenant-names": "Print Tenant names to log" }, "timeinterval": { "seconds-interval": "{ seconds, plural, 1 {1 second} other {# seconds} }",