Commit c03356e94456c526bc17c56acfc1d9563fb20483

Authored by Viacheslav Kukhtyn
2 parents 6ccb9d60 627c0577

Merge branch 'master' into feature/log-telemetry-updated

Showing 31 changed files with 206 additions and 63 deletions
@@ -392,6 +392,11 @@ public class TelemetryController extends BaseController { @@ -392,6 +392,11 @@ public class TelemetryController extends BaseController {
392 if (attributes.isEmpty()) { 392 if (attributes.isEmpty()) {
393 return getImmediateDeferredResult("No attributes data found in request body!", HttpStatus.BAD_REQUEST); 393 return getImmediateDeferredResult("No attributes data found in request body!", HttpStatus.BAD_REQUEST);
394 } 394 }
  395 + for (AttributeKvEntry attributeKvEntry: attributes) {
  396 + if (attributeKvEntry.getKey().isEmpty() || attributeKvEntry.getKey().trim().length() == 0) {
  397 + return getImmediateDeferredResult("Key cannot be empty or contains only spaces", HttpStatus.BAD_REQUEST);
  398 + }
  399 + }
395 SecurityUser user = getCurrentUser(); 400 SecurityUser user = getCurrentUser();
396 return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.WRITE_ATTRIBUTES, entityIdSrc, (result, tenantId, entityId) -> { 401 return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.WRITE_ATTRIBUTES, entityIdSrc, (result, tenantId, entityId) -> {
397 tsSubService.saveAndNotify(tenantId, entityId, scope, attributes, new FutureCallback<Void>() { 402 tsSubService.saveAndNotify(tenantId, entityId, scope, attributes, new FutureCallback<Void>() {
@@ -221,6 +221,7 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService { @@ -221,6 +221,7 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService {
221 221
222 @Override 222 @Override
223 public void onTenantProfileUpdate(TenantProfileId tenantProfileId) { 223 public void onTenantProfileUpdate(TenantProfileId tenantProfileId) {
  224 + log.info("[{}] On Tenant Profile Update", tenantProfileId);
224 TenantProfile tenantProfile = tenantProfileCache.get(tenantProfileId); 225 TenantProfile tenantProfile = tenantProfileCache.get(tenantProfileId);
225 updateLock.lock(); 226 updateLock.lock();
226 try { 227 try {
@@ -236,6 +237,7 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService { @@ -236,6 +237,7 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService {
236 237
237 @Override 238 @Override
238 public void onTenantUpdate(TenantId tenantId) { 239 public void onTenantUpdate(TenantId tenantId) {
  240 + log.info("[{}] On Tenant Update.", tenantId);
239 TenantProfile tenantProfile = tenantProfileCache.get(tenantId); 241 TenantProfile tenantProfile = tenantProfileCache.get(tenantId);
240 updateLock.lock(); 242 updateLock.lock();
241 try { 243 try {
@@ -248,16 +250,16 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService { @@ -248,16 +250,16 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService {
248 } 250 }
249 } 251 }
250 252
251 - private void updateTenantState(TenantApiUsageState state, TenantProfile tenantProfile) { 253 + private void updateTenantState(TenantApiUsageState state, TenantProfile profile) {
252 TenantProfileData oldProfileData = state.getTenantProfileData(); 254 TenantProfileData oldProfileData = state.getTenantProfileData();
253 - state.setTenantProfileId(tenantProfile.getId());  
254 - state.setTenantProfileData(tenantProfile.getProfileData()); 255 + state.setTenantProfileId(profile.getId());
  256 + state.setTenantProfileData(profile.getProfileData());
255 Map<ApiFeature, ApiUsageStateValue> result = state.checkStateUpdatedDueToThresholds(); 257 Map<ApiFeature, ApiUsageStateValue> result = state.checkStateUpdatedDueToThresholds();
256 if (!result.isEmpty()) { 258 if (!result.isEmpty()) {
257 persistAndNotify(state, result); 259 persistAndNotify(state, result);
258 } 260 }
259 updateProfileThresholds(state.getTenantId(), state.getApiUsageState().getId(), 261 updateProfileThresholds(state.getTenantId(), state.getApiUsageState().getId(),
260 - oldProfileData.getConfiguration(), tenantProfile.getProfileData().getConfiguration()); 262 + oldProfileData.getConfiguration(), profile.getProfileData().getConfiguration());
261 } 263 }
262 264
263 private void updateProfileThresholds(TenantId tenantId, ApiUsageStateId id, 265 private void updateProfileThresholds(TenantId tenantId, ApiUsageStateId id,
@@ -134,7 +134,6 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { @@ -134,7 +134,6 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
134 TenantProfile isolatedTbCoreProfile = new TenantProfile(); 134 TenantProfile isolatedTbCoreProfile = new TenantProfile();
135 isolatedTbCoreProfile.setDefault(false); 135 isolatedTbCoreProfile.setDefault(false);
136 isolatedTbCoreProfile.setName("Isolated TB Core"); 136 isolatedTbCoreProfile.setName("Isolated TB Core");
137 - isolatedTbCoreProfile.setProfileData(new TenantProfileData());  
138 isolatedTbCoreProfile.setDescription("Isolated TB Core tenant profile"); 137 isolatedTbCoreProfile.setDescription("Isolated TB Core tenant profile");
139 isolatedTbCoreProfile.setIsolatedTbCore(true); 138 isolatedTbCoreProfile.setIsolatedTbCore(true);
140 isolatedTbCoreProfile.setIsolatedTbRuleEngine(false); 139 isolatedTbCoreProfile.setIsolatedTbRuleEngine(false);
@@ -148,7 +147,6 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { @@ -148,7 +147,6 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
148 TenantProfile isolatedTbRuleEngineProfile = new TenantProfile(); 147 TenantProfile isolatedTbRuleEngineProfile = new TenantProfile();
149 isolatedTbRuleEngineProfile.setDefault(false); 148 isolatedTbRuleEngineProfile.setDefault(false);
150 isolatedTbRuleEngineProfile.setName("Isolated TB Rule Engine"); 149 isolatedTbRuleEngineProfile.setName("Isolated TB Rule Engine");
151 - isolatedTbRuleEngineProfile.setProfileData(new TenantProfileData());  
152 isolatedTbRuleEngineProfile.setDescription("Isolated TB Rule Engine tenant profile"); 150 isolatedTbRuleEngineProfile.setDescription("Isolated TB Rule Engine tenant profile");
153 isolatedTbRuleEngineProfile.setIsolatedTbCore(false); 151 isolatedTbRuleEngineProfile.setIsolatedTbCore(false);
154 isolatedTbRuleEngineProfile.setIsolatedTbRuleEngine(true); 152 isolatedTbRuleEngineProfile.setIsolatedTbRuleEngine(true);
@@ -163,7 +161,6 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { @@ -163,7 +161,6 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
163 TenantProfile isolatedTbCoreAndTbRuleEngineProfile = new TenantProfile(); 161 TenantProfile isolatedTbCoreAndTbRuleEngineProfile = new TenantProfile();
164 isolatedTbCoreAndTbRuleEngineProfile.setDefault(false); 162 isolatedTbCoreAndTbRuleEngineProfile.setDefault(false);
165 isolatedTbCoreAndTbRuleEngineProfile.setName("Isolated TB Core and TB Rule Engine"); 163 isolatedTbCoreAndTbRuleEngineProfile.setName("Isolated TB Core and TB Rule Engine");
166 - isolatedTbCoreAndTbRuleEngineProfile.setProfileData(new TenantProfileData());  
167 isolatedTbCoreAndTbRuleEngineProfile.setDescription("Isolated TB Core and TB Rule Engine tenant profile"); 164 isolatedTbCoreAndTbRuleEngineProfile.setDescription("Isolated TB Core and TB Rule Engine tenant profile");
168 isolatedTbCoreAndTbRuleEngineProfile.setIsolatedTbCore(true); 165 isolatedTbCoreAndTbRuleEngineProfile.setIsolatedTbCore(true);
169 isolatedTbCoreAndTbRuleEngineProfile.setIsolatedTbRuleEngine(true); 166 isolatedTbCoreAndTbRuleEngineProfile.setIsolatedTbRuleEngine(true);
@@ -274,6 +274,7 @@ public class DefaultTbClusterService implements TbClusterService { @@ -274,6 +274,7 @@ public class DefaultTbClusterService implements TbClusterService {
274 TbQueueProducer<TbProtoQueueMsg<ToRuleEngineNotificationMsg>> toRuleEngineProducer = producerProvider.getRuleEngineNotificationsMsgProducer(); 274 TbQueueProducer<TbProtoQueueMsg<ToRuleEngineNotificationMsg>> toRuleEngineProducer = producerProvider.getRuleEngineNotificationsMsgProducer();
275 Set<String> tbRuleEngineServices = new HashSet<>(partitionService.getAllServiceIds(ServiceType.TB_RULE_ENGINE)); 275 Set<String> tbRuleEngineServices = new HashSet<>(partitionService.getAllServiceIds(ServiceType.TB_RULE_ENGINE));
276 if (msg.getEntityId().getEntityType().equals(EntityType.TENANT) 276 if (msg.getEntityId().getEntityType().equals(EntityType.TENANT)
  277 + || msg.getEntityId().getEntityType().equals(EntityType.TENANT_PROFILE)
277 || msg.getEntityId().getEntityType().equals(EntityType.DEVICE_PROFILE) 278 || msg.getEntityId().getEntityType().equals(EntityType.DEVICE_PROFILE)
278 || msg.getEntityId().getEntityType().equals(EntityType.API_USAGE_STATE)) { 279 || msg.getEntityId().getEntityType().equals(EntityType.API_USAGE_STATE)) {
279 TbQueueProducer<TbProtoQueueMsg<ToCoreNotificationMsg>> toCoreNfProducer = producerProvider.getTbCoreNotificationsMsgProducer(); 280 TbQueueProducer<TbProtoQueueMsg<ToCoreNotificationMsg>> toCoreNfProducer = producerProvider.getTbCoreNotificationsMsgProducer();
@@ -153,6 +153,8 @@ public abstract class AbstractConsumerService<N extends com.google.protobuf.Gene @@ -153,6 +153,8 @@ public abstract class AbstractConsumerService<N extends com.google.protobuf.Gene
153 TbActorMsg actorMsg = actorMsgOpt.get(); 153 TbActorMsg actorMsg = actorMsgOpt.get();
154 if (actorMsg instanceof ComponentLifecycleMsg) { 154 if (actorMsg instanceof ComponentLifecycleMsg) {
155 ComponentLifecycleMsg componentLifecycleMsg = (ComponentLifecycleMsg) actorMsg; 155 ComponentLifecycleMsg componentLifecycleMsg = (ComponentLifecycleMsg) actorMsg;
  156 + log.info("[{}][{}][{}] Received Lifecycle event: {}", componentLifecycleMsg.getTenantId(), componentLifecycleMsg.getEntityId().getEntityType(),
  157 + componentLifecycleMsg.getEntityId(), componentLifecycleMsg.getEvent());
156 if (EntityType.TENANT_PROFILE.equals(componentLifecycleMsg.getEntityId().getEntityType())) { 158 if (EntityType.TENANT_PROFILE.equals(componentLifecycleMsg.getEntityId().getEntityType())) {
157 TenantProfileId tenantProfileId = new TenantProfileId(componentLifecycleMsg.getEntityId().getId()); 159 TenantProfileId tenantProfileId = new TenantProfileId(componentLifecycleMsg.getEntityId().getId());
158 tenantProfileCache.evict(tenantProfileId); 160 tenantProfileCache.evict(tenantProfileId);
@@ -35,14 +35,17 @@ import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationInfo; @@ -35,14 +35,17 @@ import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationInfo;
35 import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig; 35 import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig;
36 import org.thingsboard.server.common.data.page.PageData; 36 import org.thingsboard.server.common.data.page.PageData;
37 import org.thingsboard.server.common.data.page.PageLink; 37 import org.thingsboard.server.common.data.page.PageLink;
  38 +import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
38 import org.thingsboard.server.common.data.security.Authority; 39 import org.thingsboard.server.common.data.security.Authority;
39 import org.thingsboard.server.common.data.security.UserCredentials; 40 import org.thingsboard.server.common.data.security.UserCredentials;
40 import org.thingsboard.server.dao.customer.CustomerService; 41 import org.thingsboard.server.dao.customer.CustomerService;
41 import org.thingsboard.server.dao.dashboard.DashboardService; 42 import org.thingsboard.server.dao.dashboard.DashboardService;
42 import org.thingsboard.server.dao.oauth2.OAuth2User; 43 import org.thingsboard.server.dao.oauth2.OAuth2User;
  44 +import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
43 import org.thingsboard.server.dao.tenant.TenantService; 45 import org.thingsboard.server.dao.tenant.TenantService;
44 import org.thingsboard.server.dao.user.UserService; 46 import org.thingsboard.server.dao.user.UserService;
45 import org.thingsboard.server.service.install.InstallScripts; 47 import org.thingsboard.server.service.install.InstallScripts;
  48 +import org.thingsboard.server.service.queue.TbClusterService;
46 import org.thingsboard.server.service.security.model.SecurityUser; 49 import org.thingsboard.server.service.security.model.SecurityUser;
47 import org.thingsboard.server.service.security.model.UserPrincipal; 50 import org.thingsboard.server.service.security.model.UserPrincipal;
48 51
@@ -76,6 +79,12 @@ public abstract class AbstractOAuth2ClientMapper { @@ -76,6 +79,12 @@ public abstract class AbstractOAuth2ClientMapper {
76 @Autowired 79 @Autowired
77 private InstallScripts installScripts; 80 private InstallScripts installScripts;
78 81
  82 + @Autowired
  83 + protected TbTenantProfileCache tenantProfileCache;
  84 +
  85 + @Autowired
  86 + protected TbClusterService tbClusterService;
  87 +
79 private final Lock userCreationLock = new ReentrantLock(); 88 private final Lock userCreationLock = new ReentrantLock();
80 89
81 protected SecurityUser getOrCreateSecurityUserFromOAuth2User(OAuth2User oauth2User, OAuth2ClientRegistrationInfo clientRegistration) { 90 protected SecurityUser getOrCreateSecurityUserFromOAuth2User(OAuth2User oauth2User, OAuth2ClientRegistrationInfo clientRegistration) {
@@ -162,6 +171,10 @@ public abstract class AbstractOAuth2ClientMapper { @@ -162,6 +171,10 @@ public abstract class AbstractOAuth2ClientMapper {
162 tenant.setTitle(tenantName); 171 tenant.setTitle(tenantName);
163 tenant = tenantService.saveTenant(tenant); 172 tenant = tenantService.saveTenant(tenant);
164 installScripts.createDefaultRuleChains(tenant.getId()); 173 installScripts.createDefaultRuleChains(tenant.getId());
  174 + tenantProfileCache.evict(tenant.getId());
  175 + tbClusterService.onTenantChange(tenant, null);
  176 + tbClusterService.onEntityStateChange(tenant.getId(), tenant.getId(),
  177 + ComponentLifecycleEvent.CREATED);
165 } else { 178 } else {
166 tenant = tenants.get(0); 179 tenant = tenants.get(0);
167 } 180 }
@@ -15,10 +15,15 @@ @@ -15,10 +15,15 @@
15 */ 15 */
16 package org.thingsboard.server.service.security.auth.oauth2; 16 package org.thingsboard.server.service.security.auth.oauth2;
17 17
  18 +import org.springframework.beans.factory.annotation.Autowired;
18 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 19 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
19 import org.springframework.security.core.AuthenticationException; 20 import org.springframework.security.core.AuthenticationException;
20 import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler; 21 import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
21 import org.springframework.stereotype.Component; 22 import org.springframework.stereotype.Component;
  23 +import org.thingsboard.server.common.data.id.CustomerId;
  24 +import org.thingsboard.server.common.data.id.EntityId;
  25 +import org.thingsboard.server.common.data.id.TenantId;
  26 +import org.thingsboard.server.service.security.system.SystemSecurityService;
22 import org.thingsboard.server.utils.MiscUtils; 27 import org.thingsboard.server.utils.MiscUtils;
23 28
24 import javax.servlet.ServletException; 29 import javax.servlet.ServletException;
@@ -32,11 +37,18 @@ import java.nio.charset.StandardCharsets; @@ -32,11 +37,18 @@ import java.nio.charset.StandardCharsets;
32 @ConditionalOnProperty(prefix = "security.oauth2", value = "enabled", havingValue = "true") 37 @ConditionalOnProperty(prefix = "security.oauth2", value = "enabled", havingValue = "true")
33 public class Oauth2AuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler { 38 public class Oauth2AuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
34 39
  40 + private final SystemSecurityService systemSecurityService;
  41 +
  42 + @Autowired
  43 + public Oauth2AuthenticationFailureHandler(final SystemSecurityService systemSecurityService) {
  44 + this.systemSecurityService = systemSecurityService;
  45 + }
  46 +
35 @Override 47 @Override
36 public void onAuthenticationFailure(HttpServletRequest request, 48 public void onAuthenticationFailure(HttpServletRequest request,
37 HttpServletResponse response, AuthenticationException exception) 49 HttpServletResponse response, AuthenticationException exception)
38 throws IOException, ServletException { 50 throws IOException, ServletException {
39 - String baseUrl = MiscUtils.constructBaseUrl(request); 51 + String baseUrl = this.systemSecurityService.getBaseUrl(TenantId.SYS_TENANT_ID, new CustomerId(EntityId.NULL_UUID), request);
40 getRedirectStrategy().sendRedirect(request, response, baseUrl + "/login?loginError=" + 52 getRedirectStrategy().sendRedirect(request, response, baseUrl + "/login?loginError=" +
41 URLEncoder.encode(exception.getMessage(), StandardCharsets.UTF_8.toString())); 53 URLEncoder.encode(exception.getMessage(), StandardCharsets.UTF_8.toString()));
42 } 54 }
@@ -22,12 +22,16 @@ import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService; @@ -22,12 +22,16 @@ import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;
22 import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; 22 import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
23 import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler; 23 import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
24 import org.springframework.stereotype.Component; 24 import org.springframework.stereotype.Component;
  25 +import org.thingsboard.server.common.data.id.CustomerId;
  26 +import org.thingsboard.server.common.data.id.EntityId;
  27 +import org.thingsboard.server.common.data.id.TenantId;
25 import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationInfo; 28 import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationInfo;
26 import org.thingsboard.server.dao.oauth2.OAuth2Service; 29 import org.thingsboard.server.dao.oauth2.OAuth2Service;
27 import org.thingsboard.server.service.security.auth.jwt.RefreshTokenRepository; 30 import org.thingsboard.server.service.security.auth.jwt.RefreshTokenRepository;
28 import org.thingsboard.server.service.security.model.SecurityUser; 31 import org.thingsboard.server.service.security.model.SecurityUser;
29 import org.thingsboard.server.service.security.model.token.JwtToken; 32 import org.thingsboard.server.service.security.model.token.JwtToken;
30 import org.thingsboard.server.service.security.model.token.JwtTokenFactory; 33 import org.thingsboard.server.service.security.model.token.JwtTokenFactory;
  34 +import org.thingsboard.server.service.security.system.SystemSecurityService;
31 import org.thingsboard.server.utils.MiscUtils; 35 import org.thingsboard.server.utils.MiscUtils;
32 36
33 import javax.servlet.http.HttpServletRequest; 37 import javax.servlet.http.HttpServletRequest;
@@ -45,25 +49,27 @@ public class Oauth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationS @@ -45,25 +49,27 @@ public class Oauth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationS
45 private final OAuth2ClientMapperProvider oauth2ClientMapperProvider; 49 private final OAuth2ClientMapperProvider oauth2ClientMapperProvider;
46 private final OAuth2Service oAuth2Service; 50 private final OAuth2Service oAuth2Service;
47 private final OAuth2AuthorizedClientService oAuth2AuthorizedClientService; 51 private final OAuth2AuthorizedClientService oAuth2AuthorizedClientService;
  52 + private final SystemSecurityService systemSecurityService;
48 53
49 @Autowired 54 @Autowired
50 public Oauth2AuthenticationSuccessHandler(final JwtTokenFactory tokenFactory, 55 public Oauth2AuthenticationSuccessHandler(final JwtTokenFactory tokenFactory,
51 final RefreshTokenRepository refreshTokenRepository, 56 final RefreshTokenRepository refreshTokenRepository,
52 final OAuth2ClientMapperProvider oauth2ClientMapperProvider, 57 final OAuth2ClientMapperProvider oauth2ClientMapperProvider,
53 final OAuth2Service oAuth2Service, 58 final OAuth2Service oAuth2Service,
54 - final OAuth2AuthorizedClientService oAuth2AuthorizedClientService) { 59 + final OAuth2AuthorizedClientService oAuth2AuthorizedClientService, final SystemSecurityService systemSecurityService) {
55 this.tokenFactory = tokenFactory; 60 this.tokenFactory = tokenFactory;
56 this.refreshTokenRepository = refreshTokenRepository; 61 this.refreshTokenRepository = refreshTokenRepository;
57 this.oauth2ClientMapperProvider = oauth2ClientMapperProvider; 62 this.oauth2ClientMapperProvider = oauth2ClientMapperProvider;
58 this.oAuth2Service = oAuth2Service; 63 this.oAuth2Service = oAuth2Service;
59 this.oAuth2AuthorizedClientService = oAuth2AuthorizedClientService; 64 this.oAuth2AuthorizedClientService = oAuth2AuthorizedClientService;
  65 + this.systemSecurityService = systemSecurityService;
60 } 66 }
61 67
62 @Override 68 @Override
63 public void onAuthenticationSuccess(HttpServletRequest request, 69 public void onAuthenticationSuccess(HttpServletRequest request,
64 HttpServletResponse response, 70 HttpServletResponse response,
65 Authentication authentication) throws IOException { 71 Authentication authentication) throws IOException {
66 - String baseUrl = MiscUtils.constructBaseUrl(request); 72 + String baseUrl = this.systemSecurityService.getBaseUrl(TenantId.SYS_TENANT_ID, new CustomerId(EntityId.NULL_UUID), request);
67 try { 73 try {
68 OAuth2AuthenticationToken token = (OAuth2AuthenticationToken) authentication; 74 OAuth2AuthenticationToken token = (OAuth2AuthenticationToken) authentication;
69 75
@@ -202,16 +202,19 @@ public class DefaultSystemSecurityService implements SystemSecurityService { @@ -202,16 +202,19 @@ public class DefaultSystemSecurityService implements SystemSecurityService {
202 202
203 @Override 203 @Override
204 public String getBaseUrl(TenantId tenantId, CustomerId customerId, HttpServletRequest httpServletRequest) { 204 public String getBaseUrl(TenantId tenantId, CustomerId customerId, HttpServletRequest httpServletRequest) {
205 - String baseUrl; 205 + String baseUrl = null;
206 AdminSettings generalSettings = adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, "general"); 206 AdminSettings generalSettings = adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, "general");
207 207
208 JsonNode prohibitDifferentUrl = generalSettings.getJsonValue().get("prohibitDifferentUrl"); 208 JsonNode prohibitDifferentUrl = generalSettings.getJsonValue().get("prohibitDifferentUrl");
209 209
210 if (prohibitDifferentUrl != null && prohibitDifferentUrl.asBoolean()) { 210 if (prohibitDifferentUrl != null && prohibitDifferentUrl.asBoolean()) {
211 baseUrl = generalSettings.getJsonValue().get("baseUrl").asText(); 211 baseUrl = generalSettings.getJsonValue().get("baseUrl").asText();
212 - } else { 212 + }
  213 +
  214 + if (StringUtils.isEmpty(baseUrl)) {
213 baseUrl = MiscUtils.constructBaseUrl(httpServletRequest); 215 baseUrl = MiscUtils.constructBaseUrl(httpServletRequest);
214 } 216 }
  217 +
215 return baseUrl; 218 return baseUrl;
216 } 219 }
217 220
@@ -108,7 +108,7 @@ public class DefaultTbLocalSubscriptionService implements TbLocalSubscriptionSer @@ -108,7 +108,7 @@ public class DefaultTbLocalSubscriptionService implements TbLocalSubscriptionSer
108 * Since number of subscriptions is usually much less then number of devices that are pushing data. 108 * Since number of subscriptions is usually much less then number of devices that are pushing data.
109 */ 109 */
110 subscriptionsBySessionId.values().forEach(map -> map.values() 110 subscriptionsBySessionId.values().forEach(map -> map.values()
111 - .forEach(sub -> pushSubscriptionToManagerService(sub, false))); 111 + .forEach(sub -> pushSubscriptionToManagerService(sub, true)));
112 } 112 }
113 } 113 }
114 114
@@ -164,22 +164,25 @@ public class DefaultTransportApiService implements TransportApiService { @@ -164,22 +164,25 @@ public class DefaultTransportApiService implements TransportApiService {
164 } 164 }
165 165
166 private ListenableFuture<TransportApiResponseMsg> validateCredentials(TransportProtos.ValidateBasicMqttCredRequestMsg mqtt) { 166 private ListenableFuture<TransportApiResponseMsg> validateCredentials(TransportProtos.ValidateBasicMqttCredRequestMsg mqtt) {
167 - DeviceCredentials credentials = deviceCredentialsService.findDeviceCredentialsByCredentialsId(mqtt.getUserName());  
168 - if (credentials != null) {  
169 - if (credentials.getCredentialsType() == DeviceCredentialsType.ACCESS_TOKEN) {  
170 - return getDeviceInfo(credentials.getDeviceId(), credentials);  
171 - } else if (credentials.getCredentialsType() == DeviceCredentialsType.MQTT_BASIC) {  
172 - if (!checkMqttCredentials(mqtt, credentials)) {  
173 - credentials = null; 167 + DeviceCredentials credentials = null;
  168 + if (!StringUtils.isEmpty(mqtt.getUserName())) {
  169 + credentials = deviceCredentialsService.findDeviceCredentialsByCredentialsId(mqtt.getUserName());
  170 + if (credentials != null) {
  171 + if (credentials.getCredentialsType() == DeviceCredentialsType.ACCESS_TOKEN) {
  172 + return getDeviceInfo(credentials.getDeviceId(), credentials);
  173 + } else if (credentials.getCredentialsType() == DeviceCredentialsType.MQTT_BASIC) {
  174 + if (!checkMqttCredentials(mqtt, credentials)) {
  175 + credentials = null;
  176 + }
174 } 177 }
175 } 178 }
176 - }  
177 - if (credentials == null) {  
178 - credentials = checkMqttCredentials(mqtt, EncryptionUtil.getSha3Hash("|", mqtt.getClientId(), mqtt.getUserName()));  
179 if (credentials == null) { 179 if (credentials == null) {
180 - credentials = checkMqttCredentials(mqtt, EncryptionUtil.getSha3Hash(mqtt.getClientId())); 180 + credentials = checkMqttCredentials(mqtt, EncryptionUtil.getSha3Hash("|", mqtt.getClientId(), mqtt.getUserName()));
181 } 181 }
182 } 182 }
  183 + if (credentials == null) {
  184 + credentials = checkMqttCredentials(mqtt, EncryptionUtil.getSha3Hash(mqtt.getClientId()));
  185 + }
183 if (credentials != null) { 186 if (credentials != null) {
184 return getDeviceInfo(credentials.getDeviceId(), credentials); 187 return getDeviceInfo(credentials.getDeviceId(), credentials);
185 } else { 188 } else {
@@ -766,7 +766,6 @@ metrics: @@ -766,7 +766,6 @@ metrics:
766 # Metrics percentiles returned by actuator for timer metrics. List of double values (divided by ,). 766 # Metrics percentiles returned by actuator for timer metrics. List of double values (divided by ,).
767 percentiles: "${METRICS_TIMER_PERCENTILES:0.5}" 767 percentiles: "${METRICS_TIMER_PERCENTILES:0.5}"
768 768
769 -  
770 management: 769 management:
771 endpoints: 770 endpoints:
772 web: 771 web:
@@ -21,5 +21,5 @@ import java.io.Serializable; @@ -21,5 +21,5 @@ import java.io.Serializable;
21 * @author Andrew Shvayka 21 * @author Andrew Shvayka
22 */ 22 */
23 public enum ComponentLifecycleEvent implements Serializable { 23 public enum ComponentLifecycleEvent implements Serializable {
24 - CREATED, STARTED, ACTIVATED, SUSPENDED, UPDATED, STOPPED, DELETED, ADDED_TO_ALLOW_LIST, ADDED_TO_DENY_LIST 24 + CREATED, STARTED, ACTIVATED, SUSPENDED, UPDATED, STOPPED, DELETED
25 } 25 }
@@ -94,7 +94,9 @@ public class DefaultTbApiUsageClient implements TbApiUsageClient { @@ -94,7 +94,9 @@ public class DefaultTbApiUsageClient implements TbApiUsageClient {
94 TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_CORE, tenantId, tenantId).newByTopic(msgProducer.getDefaultTopic()); 94 TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_CORE, tenantId, tenantId).newByTopic(msgProducer.getDefaultTopic());
95 msgProducer.send(tpi, new TbProtoQueueMsg<>(UUID.randomUUID(), builder.build()), null); 95 msgProducer.send(tpi, new TbProtoQueueMsg<>(UUID.randomUUID(), builder.build()), null);
96 })); 96 }));
97 - log.info("Report statistics for: {} tenants", report.size()); 97 + if (!report.isEmpty()) {
  98 + log.info("Report statistics for: {} tenants", report.size());
  99 + }
98 } catch (Exception e) { 100 } catch (Exception e) {
99 log.warn("Failed to report statistics: ", e); 101 log.warn("Failed to report statistics: ", e);
100 } 102 }
@@ -122,7 +122,13 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement @@ -122,7 +122,13 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
122 log.trace("[{}] Processing msg: {}", sessionId, msg); 122 log.trace("[{}] Processing msg: {}", sessionId, msg);
123 try { 123 try {
124 if (msg instanceof MqttMessage) { 124 if (msg instanceof MqttMessage) {
125 - processMqttMsg(ctx, (MqttMessage) msg); 125 + MqttMessage message = (MqttMessage) msg;
  126 + if (message.decoderResult().isSuccess()) {
  127 + processMqttMsg(ctx, message);
  128 + } else {
  129 + log.error("[{}] Message processing failed: {}", sessionId, message.decoderResult().cause().getMessage());
  130 + ctx.close();
  131 + }
126 } else { 132 } else {
127 ctx.close(); 133 ctx.close();
128 } 134 }
@@ -464,8 +470,10 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement @@ -464,8 +470,10 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
464 String userName = msg.payload().userName(); 470 String userName = msg.payload().userName();
465 log.info("[{}] Processing connect msg for client with user name: {}!", sessionId, userName); 471 log.info("[{}] Processing connect msg for client with user name: {}!", sessionId, userName);
466 TransportProtos.ValidateBasicMqttCredRequestMsg.Builder request = TransportProtos.ValidateBasicMqttCredRequestMsg.newBuilder() 472 TransportProtos.ValidateBasicMqttCredRequestMsg.Builder request = TransportProtos.ValidateBasicMqttCredRequestMsg.newBuilder()
467 - .setClientId(msg.payload().clientIdentifier())  
468 - .setUserName(userName); 473 + .setClientId(msg.payload().clientIdentifier());
  474 + if (userName != null) {
  475 + request.setUserName(userName);
  476 + }
469 String password = msg.payload().password(); 477 String password = msg.payload().password();
470 if (password != null) { 478 if (password != null) {
471 request.setPassword(password); 479 request.setPassword(password);
@@ -21,6 +21,7 @@ import org.springframework.util.StringUtils; @@ -21,6 +21,7 @@ import org.springframework.util.StringUtils;
21 import org.thingsboard.server.common.data.EntityType; 21 import org.thingsboard.server.common.data.EntityType;
22 import org.thingsboard.server.common.data.TenantProfile; 22 import org.thingsboard.server.common.data.TenantProfile;
23 import org.thingsboard.server.common.data.id.DeviceId; 23 import org.thingsboard.server.common.data.id.DeviceId;
  24 +import org.thingsboard.server.common.data.id.EntityId;
24 import org.thingsboard.server.common.data.id.TenantId; 25 import org.thingsboard.server.common.data.id.TenantId;
25 import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; 26 import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration;
26 import org.thingsboard.server.common.data.tenant.profile.TenantProfileData; 27 import org.thingsboard.server.common.data.tenant.profile.TenantProfileData;
@@ -77,6 +78,7 @@ public class DefaultTransportRateLimitService implements TransportRateLimitServi @@ -77,6 +78,7 @@ public class DefaultTransportRateLimitService implements TransportRateLimitServi
77 78
78 @Override 79 @Override
79 public void update(TenantProfileUpdateResult update) { 80 public void update(TenantProfileUpdateResult update) {
  81 + log.info("Received tenant profile update: {}", update.getProfile());
80 EntityTransportRateLimits tenantRateLimitPrototype = createRateLimits(update.getProfile(), true); 82 EntityTransportRateLimits tenantRateLimitPrototype = createRateLimits(update.getProfile(), true);
81 EntityTransportRateLimits deviceRateLimitPrototype = createRateLimits(update.getProfile(), false); 83 EntityTransportRateLimits deviceRateLimitPrototype = createRateLimits(update.getProfile(), false);
82 for (TenantId tenantId : update.getAffectedTenants()) { 84 for (TenantId tenantId : update.getAffectedTenants()) {
@@ -114,16 +116,26 @@ public class DefaultTransportRateLimitService implements TransportRateLimitServi @@ -114,16 +116,26 @@ public class DefaultTransportRateLimitService implements TransportRateLimitServi
114 tenantAllowed.put(tenantId, allowed); 116 tenantAllowed.put(tenantId, allowed);
115 } 117 }
116 118
117 - private <T> void mergeLimits(T deviceId, EntityTransportRateLimits newRateLimits,  
118 - Function<T, EntityTransportRateLimits> getFunction,  
119 - BiConsumer<T, EntityTransportRateLimits> putFunction) {  
120 - EntityTransportRateLimits oldRateLimits = getFunction.apply(deviceId); 119 + private <T extends EntityId> void mergeLimits(T entityId, EntityTransportRateLimits newRateLimits,
  120 + Function<T, EntityTransportRateLimits> getFunction,
  121 + BiConsumer<T, EntityTransportRateLimits> putFunction) {
  122 + EntityTransportRateLimits oldRateLimits = getFunction.apply(entityId);
121 if (oldRateLimits == null) { 123 if (oldRateLimits == null) {
122 - putFunction.accept(deviceId, newRateLimits); 124 + if (EntityType.TENANT.equals(entityId.getEntityType())) {
  125 + log.info("[{}] New rate limits: {}", entityId, newRateLimits);
  126 + } else {
  127 + log.debug("[{}] New rate limits: {}", entityId, newRateLimits);
  128 + }
  129 + putFunction.accept(entityId, newRateLimits);
123 } else { 130 } else {
124 EntityTransportRateLimits updated = merge(oldRateLimits, newRateLimits); 131 EntityTransportRateLimits updated = merge(oldRateLimits, newRateLimits);
125 if (updated != null) { 132 if (updated != null) {
126 - putFunction.accept(deviceId, updated); 133 + if (EntityType.TENANT.equals(entityId.getEntityType())) {
  134 + log.info("[{}] Updated rate limits: {}", entityId, updated);
  135 + } else {
  136 + log.debug("[{}] Updated rate limits: {}", entityId, updated);
  137 + }
  138 + putFunction.accept(entityId, updated);
127 } 139 }
128 } 140 }
129 } 141 }
@@ -548,7 +548,7 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe @@ -548,7 +548,7 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
548 548
549 @Override 549 @Override
550 protected void validateDataImpl(TenantId tenantId, Device device) { 550 protected void validateDataImpl(TenantId tenantId, Device device) {
551 - if (StringUtils.isEmpty(device.getName())) { 551 + if (StringUtils.isEmpty(device.getName()) || device.getName().trim().length() == 0) {
552 throw new DataValidationException("Device name should be specified!"); 552 throw new DataValidationException("Device name should be specified!");
553 } 553 }
554 if (device.getTenantId() == null) { 554 if (device.getTenantId() == null) {
@@ -202,6 +202,9 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { @@ -202,6 +202,9 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
202 " THEN (select additional_info from entity_view where id = entity_id)" + 202 " THEN (select additional_info from entity_view where id = entity_id)" +
203 " END as additional_info"; 203 " END as additional_info";
204 204
  205 + private static final String SELECT_API_USAGE_STATE = "(select aus.id, aus.created_time, aus.tenant_id, '13814000-1dd2-11b2-8080-808080808080'::uuid as customer_id, " +
  206 + "(select title from tenant where id = aus.tenant_id) as name from api_usage_state as aus)";
  207 +
205 static { 208 static {
206 entityTableMap.put(EntityType.ASSET, "asset"); 209 entityTableMap.put(EntityType.ASSET, "asset");
207 entityTableMap.put(EntityType.DEVICE, "device"); 210 entityTableMap.put(EntityType.DEVICE, "device");
@@ -210,7 +213,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { @@ -210,7 +213,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
210 entityTableMap.put(EntityType.CUSTOMER, "customer"); 213 entityTableMap.put(EntityType.CUSTOMER, "customer");
211 entityTableMap.put(EntityType.USER, "tb_user"); 214 entityTableMap.put(EntityType.USER, "tb_user");
212 entityTableMap.put(EntityType.TENANT, "tenant"); 215 entityTableMap.put(EntityType.TENANT, "tenant");
213 - entityTableMap.put(EntityType.API_USAGE_STATE, "(select aus.id, aus.created_time, aus.tenant_id, '' as name, '' as additional_info from api_usage_state as aus)"); 216 + entityTableMap.put(EntityType.API_USAGE_STATE, SELECT_API_USAGE_STATE);
214 } 217 }
215 218
216 public static EntityType[] RELATION_QUERY_ENTITY_TYPES = new EntityType[]{ 219 public static EntityType[] RELATION_QUERY_ENTITY_TYPES = new EntityType[]{
@@ -80,6 +80,7 @@ public class EntityKeyMapping { @@ -80,6 +80,7 @@ public class EntityKeyMapping {
80 public static final List<String> labeledEntityFields = Arrays.asList(CREATED_TIME, ENTITY_TYPE, NAME, TYPE, LABEL, ADDITIONAL_INFO); 80 public static final List<String> labeledEntityFields = Arrays.asList(CREATED_TIME, ENTITY_TYPE, NAME, TYPE, LABEL, ADDITIONAL_INFO);
81 public static final List<String> contactBasedEntityFields = Arrays.asList(CREATED_TIME, ENTITY_TYPE, EMAIL, TITLE, COUNTRY, STATE, CITY, ADDRESS, ADDRESS_2, ZIP, PHONE, ADDITIONAL_INFO); 81 public static final List<String> contactBasedEntityFields = Arrays.asList(CREATED_TIME, ENTITY_TYPE, EMAIL, TITLE, COUNTRY, STATE, CITY, ADDRESS, ADDRESS_2, ZIP, PHONE, ADDITIONAL_INFO);
82 82
  83 + public static final Set<String> apiUsageStateEntityFields = new HashSet<>(Arrays.asList(CREATED_TIME, ENTITY_TYPE, NAME));
83 public static final Set<String> commonEntityFieldsSet = new HashSet<>(commonEntityFields); 84 public static final Set<String> commonEntityFieldsSet = new HashSet<>(commonEntityFields);
84 public static final Set<String> relationQueryEntityFieldsSet = new HashSet<>(Arrays.asList(CREATED_TIME, ENTITY_TYPE, NAME, TYPE, LABEL, FIRST_NAME, LAST_NAME, EMAIL, REGION, TITLE, COUNTRY, STATE, CITY, ADDRESS, ADDRESS_2, ZIP, PHONE, ADDITIONAL_INFO)); 85 public static final Set<String> relationQueryEntityFieldsSet = new HashSet<>(Arrays.asList(CREATED_TIME, ENTITY_TYPE, NAME, TYPE, LABEL, FIRST_NAME, LAST_NAME, EMAIL, REGION, TITLE, COUNTRY, STATE, CITY, ADDRESS, ADDRESS_2, ZIP, PHONE, ADDITIONAL_INFO));
85 86
@@ -99,6 +100,7 @@ public class EntityKeyMapping { @@ -99,6 +100,7 @@ public class EntityKeyMapping {
99 allowedEntityFieldMap.put(EntityType.RULE_NODE, new HashSet<>(commonEntityFields)); 100 allowedEntityFieldMap.put(EntityType.RULE_NODE, new HashSet<>(commonEntityFields));
100 allowedEntityFieldMap.put(EntityType.WIDGET_TYPE, new HashSet<>(widgetEntityFields)); 101 allowedEntityFieldMap.put(EntityType.WIDGET_TYPE, new HashSet<>(widgetEntityFields));
101 allowedEntityFieldMap.put(EntityType.WIDGETS_BUNDLE, new HashSet<>(widgetEntityFields)); 102 allowedEntityFieldMap.put(EntityType.WIDGETS_BUNDLE, new HashSet<>(widgetEntityFields));
  103 + allowedEntityFieldMap.put(EntityType.API_USAGE_STATE, apiUsageStateEntityFields);
102 104
103 entityFieldColumnMap.put(CREATED_TIME, ModelConstants.CREATED_TIME_PROPERTY); 105 entityFieldColumnMap.put(CREATED_TIME, ModelConstants.CREATED_TIME_PROPERTY);
104 entityFieldColumnMap.put(ENTITY_TYPE, ModelConstants.ENTITY_TYPE_PROPERTY); 106 entityFieldColumnMap.put(ENTITY_TYPE, ModelConstants.ENTITY_TYPE_PROPERTY);
@@ -17,6 +17,7 @@ package org.thingsboard.server.dao.usagerecord; @@ -17,6 +17,7 @@ package org.thingsboard.server.dao.usagerecord;
17 17
18 import lombok.extern.slf4j.Slf4j; 18 import lombok.extern.slf4j.Slf4j;
19 import org.springframework.stereotype.Service; 19 import org.springframework.stereotype.Service;
  20 +import org.thingsboard.server.common.data.ApiFeature;
20 import org.thingsboard.server.common.data.ApiUsageRecordKey; 21 import org.thingsboard.server.common.data.ApiUsageRecordKey;
21 import org.thingsboard.server.common.data.ApiUsageState; 22 import org.thingsboard.server.common.data.ApiUsageState;
22 import org.thingsboard.server.common.data.ApiUsageStateValue; 23 import org.thingsboard.server.common.data.ApiUsageStateValue;
@@ -27,6 +28,7 @@ import org.thingsboard.server.common.data.id.ApiUsageStateId; @@ -27,6 +28,7 @@ import org.thingsboard.server.common.data.id.ApiUsageStateId;
27 import org.thingsboard.server.common.data.id.TenantId; 28 import org.thingsboard.server.common.data.id.TenantId;
28 import org.thingsboard.server.common.data.kv.BasicTsKvEntry; 29 import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
29 import org.thingsboard.server.common.data.kv.LongDataEntry; 30 import org.thingsboard.server.common.data.kv.LongDataEntry;
  31 +import org.thingsboard.server.common.data.kv.StringDataEntry;
30 import org.thingsboard.server.common.data.kv.TsKvEntry; 32 import org.thingsboard.server.common.data.kv.TsKvEntry;
31 import org.thingsboard.server.common.data.tenant.profile.TenantProfileConfiguration; 33 import org.thingsboard.server.common.data.tenant.profile.TenantProfileConfiguration;
32 import org.thingsboard.server.dao.entity.AbstractEntityService; 34 import org.thingsboard.server.dao.entity.AbstractEntityService;
@@ -83,7 +85,19 @@ public class ApiUsageStateServiceImpl extends AbstractEntityService implements A @@ -83,7 +85,19 @@ public class ApiUsageStateServiceImpl extends AbstractEntityService implements A
83 Tenant tenant = tenantDao.findById(tenantId, tenantId.getId()); 85 Tenant tenant = tenantDao.findById(tenantId, tenantId.getId());
84 TenantProfile tenantProfile = tenantProfileDao.findById(tenantId, tenant.getTenantProfileId().getId()); 86 TenantProfile tenantProfile = tenantProfileDao.findById(tenantId, tenant.getTenantProfileId().getId());
85 TenantProfileConfiguration configuration = tenantProfile.getProfileData().getConfiguration(); 87 TenantProfileConfiguration configuration = tenantProfile.getProfileData().getConfiguration();
  88 + List<TsKvEntry> apiUsageStates = new ArrayList<>();
  89 + apiUsageStates.add(new BasicTsKvEntry(saved.getCreatedTime(),
  90 + new StringDataEntry(ApiFeature.TRANSPORT.getApiStateKey(), ApiUsageStateValue.ENABLED.name())));
  91 + apiUsageStates.add(new BasicTsKvEntry(saved.getCreatedTime(),
  92 + new StringDataEntry(ApiFeature.DB.getApiStateKey(), ApiUsageStateValue.ENABLED.name())));
  93 + apiUsageStates.add(new BasicTsKvEntry(saved.getCreatedTime(),
  94 + new StringDataEntry(ApiFeature.RE.getApiStateKey(), ApiUsageStateValue.ENABLED.name())));
  95 + apiUsageStates.add(new BasicTsKvEntry(saved.getCreatedTime(),
  96 + new StringDataEntry(ApiFeature.JS.getApiStateKey(), ApiUsageStateValue.ENABLED.name())));
  97 + tsService.save(tenantId, saved.getId(), apiUsageStates, 0L);
  98 +
86 List<TsKvEntry> profileThresholds = new ArrayList<>(); 99 List<TsKvEntry> profileThresholds = new ArrayList<>();
  100 +
87 for (ApiUsageRecordKey key : ApiUsageRecordKey.values()) { 101 for (ApiUsageRecordKey key : ApiUsageRecordKey.values()) {
88 profileThresholds.add(new BasicTsKvEntry(saved.getCreatedTime(), new LongDataEntry(key.getApiLimitKey(), configuration.getProfileThreshold(key)))); 102 profileThresholds.add(new BasicTsKvEntry(saved.getCreatedTime(), new LongDataEntry(key.getApiLimitKey(), configuration.getProfileThreshold(key))));
89 } 103 }
@@ -58,7 +58,7 @@ In case of any issues you can examine service logs for errors. @@ -58,7 +58,7 @@ In case of any issues you can examine service logs for errors.
58 For example to see ThingsBoard node logs execute the following command: 58 For example to see ThingsBoard node logs execute the following command:
59 59
60 ` 60 `
61 -$ docker-compose logs -f tb-core1 tb-rule-engine1 61 +$ docker-compose logs -f tb-core1 tb-core2 tb-rule-engine1 tb-rule-engine2 tb-mqtt-transport1 tb-mqtt-transport2
62 ` 62 `
63 63
64 Or use `docker-compose ps` to see the state of all the containers. 64 Or use `docker-compose ps` to see the state of all the containers.
@@ -67,6 +67,7 @@ class AlarmState { @@ -67,6 +67,7 @@ class AlarmState {
67 initCurrentAlarm(ctx); 67 initCurrentAlarm(ctx);
68 lastMsgMetaData = msg.getMetaData(); 68 lastMsgMetaData = msg.getMetaData();
69 lastMsgQueueName = msg.getQueueName(); 69 lastMsgQueueName = msg.getQueueName();
  70 + this.dataSnapshot = data;
70 return createOrClearAlarms(ctx, data, update, AlarmRuleState::eval); 71 return createOrClearAlarms(ctx, data, update, AlarmRuleState::eval);
71 } 72 }
72 73
@@ -90,8 +91,7 @@ class AlarmState { @@ -90,8 +91,7 @@ class AlarmState {
90 resultState = state; 91 resultState = state;
91 break; 92 break;
92 } else if (AlarmEvalResult.FALSE.equals(evalResult)) { 93 } else if (AlarmEvalResult.FALSE.equals(evalResult)) {
93 - state.clear();  
94 - stateUpdate |= state.checkUpdate(); 94 + stateUpdate = clearAlarmState(stateUpdate, state);
95 } 95 }
96 } 96 }
97 if (resultState != null) { 97 if (resultState != null) {
@@ -99,6 +99,7 @@ class AlarmState { @@ -99,6 +99,7 @@ class AlarmState {
99 if (result != null) { 99 if (result != null) {
100 pushMsg(ctx, result); 100 pushMsg(ctx, result);
101 } 101 }
  102 + stateUpdate = clearAlarmState(stateUpdate, clearState);
102 } else if (currentAlarm != null && clearState != null) { 103 } else if (currentAlarm != null && clearState != null) {
103 if (!validateUpdate(update, clearState)) { 104 if (!validateUpdate(update, clearState)) {
104 log.debug("[{}] Update is not valid for current clear state", alarmDefinition.getId()); 105 log.debug("[{}] Update is not valid for current clear state", alarmDefinition.getId());
@@ -106,23 +107,26 @@ class AlarmState { @@ -106,23 +107,26 @@ class AlarmState {
106 } 107 }
107 AlarmEvalResult evalResult = evalFunction.apply(clearState, data); 108 AlarmEvalResult evalResult = evalFunction.apply(clearState, data);
108 if (AlarmEvalResult.TRUE.equals(evalResult)) { 109 if (AlarmEvalResult.TRUE.equals(evalResult)) {
109 - clearState.clear();  
110 - stateUpdate |= clearState.checkUpdate(); 110 + stateUpdate = clearAlarmState(stateUpdate, clearState);
111 for (AlarmRuleState state : createRulesSortedBySeverityDesc) { 111 for (AlarmRuleState state : createRulesSortedBySeverityDesc) {
112 - state.clear();  
113 - stateUpdate |= state.checkUpdate(); 112 + stateUpdate = clearAlarmState(stateUpdate, state);
114 } 113 }
115 ctx.getAlarmService().clearAlarm(ctx.getTenantId(), currentAlarm.getId(), JacksonUtil.OBJECT_MAPPER.createObjectNode(), System.currentTimeMillis()); 114 ctx.getAlarmService().clearAlarm(ctx.getTenantId(), currentAlarm.getId(), JacksonUtil.OBJECT_MAPPER.createObjectNode(), System.currentTimeMillis());
116 pushMsg(ctx, new TbAlarmResult(false, false, true, currentAlarm)); 115 pushMsg(ctx, new TbAlarmResult(false, false, true, currentAlarm));
117 currentAlarm = null; 116 currentAlarm = null;
118 } else if (AlarmEvalResult.FALSE.equals(evalResult)) { 117 } else if (AlarmEvalResult.FALSE.equals(evalResult)) {
119 - clearState.clear();  
120 - stateUpdate |= clearState.checkUpdate(); 118 + stateUpdate = clearAlarmState(stateUpdate, clearState);
121 } 119 }
122 } 120 }
123 return stateUpdate; 121 return stateUpdate;
124 } 122 }
125 123
  124 + public boolean clearAlarmState(boolean stateUpdate, AlarmRuleState state) {
  125 + state.clear();
  126 + stateUpdate |= state.checkUpdate();
  127 + return stateUpdate;
  128 + }
  129 +
126 public boolean validateUpdate(SnapshotUpdate update, AlarmRuleState state) { 130 public boolean validateUpdate(SnapshotUpdate update, AlarmRuleState state) {
127 if (update != null) { 131 if (update != null) {
128 //Check that the update type and that keys match. 132 //Check that the update type and that keys match.
@@ -190,7 +194,7 @@ class AlarmState { @@ -190,7 +194,7 @@ class AlarmState {
190 } 194 }
191 } 195 }
192 196
193 - private <T> TbAlarmResult calculateAlarmResult(TbContext ctx, AlarmRuleState ruleState) { 197 + private TbAlarmResult calculateAlarmResult(TbContext ctx, AlarmRuleState ruleState) {
194 AlarmSeverity severity = ruleState.getSeverity(); 198 AlarmSeverity severity = ruleState.getSeverity();
195 if (currentAlarm != null) { 199 if (currentAlarm != null) {
196 // TODO: In some extremely rare cases, we might miss the event of alarm clear (If one use in-mem queue and restarted the server) or (if one manipulated the rule chain). 200 // TODO: In some extremely rare cases, we might miss the event of alarm clear (If one use in-mem queue and restarted the server) or (if one manipulated the rule chain).
@@ -230,7 +234,7 @@ class AlarmState { @@ -230,7 +234,7 @@ class AlarmState {
230 } 234 }
231 } 235 }
232 236
233 - private <T> JsonNode createDetails(AlarmRuleState ruleState) { 237 + private JsonNode createDetails(AlarmRuleState ruleState) {
234 ObjectNode details = JacksonUtil.OBJECT_MAPPER.createObjectNode(); 238 ObjectNode details = JacksonUtil.OBJECT_MAPPER.createObjectNode();
235 String alarmDetails = ruleState.getAlarmRule().getAlarmDetails(); 239 String alarmDetails = ruleState.getAlarmRule().getAlarmDetails();
236 240
@@ -273,8 +277,7 @@ class AlarmState { @@ -273,8 +277,7 @@ class AlarmState {
273 if (currentAlarm != null && currentAlarm.getId().equals(alarmNf.getId())) { 277 if (currentAlarm != null && currentAlarm.getId().equals(alarmNf.getId())) {
274 currentAlarm = null; 278 currentAlarm = null;
275 for (AlarmRuleState state : createRulesSortedBySeverityDesc) { 279 for (AlarmRuleState state : createRulesSortedBySeverityDesc) {
276 - state.clear();  
277 - updated |= state.checkUpdate(); 280 + updated = clearAlarmState(updated, state);
278 } 281 }
279 } 282 }
280 return updated; 283 return updated;
@@ -630,6 +630,9 @@ export class EntityService { @@ -630,6 +630,9 @@ export class EntityService {
630 case EntityType.DASHBOARD: 630 case EntityType.DASHBOARD:
631 entityFieldKeys.push(entityFields.title.keyName); 631 entityFieldKeys.push(entityFields.title.keyName);
632 break; 632 break;
  633 + case EntityType.API_USAGE_STATE:
  634 + entityFieldKeys.push(entityFields.name.keyName);
  635 + break;
633 } 636 }
634 return query ? entityFieldKeys.filter((entityField) => entityField.toLowerCase().indexOf(query) === 0) : entityFieldKeys; 637 return query ? entityFieldKeys.filter((entityField) => entityField.toLowerCase().indexOf(query) === 0) : entityFieldKeys;
635 } 638 }
@@ -422,6 +422,12 @@ export class DashboardUtilsService { @@ -422,6 +422,12 @@ export class DashboardUtilsService {
422 widgetLayout.row = row; 422 widgetLayout.row = row;
423 widgetLayout.col = 0; 423 widgetLayout.col = 0;
424 } 424 }
  425 +
  426 + widgetLayout.sizeX = Math.floor(widgetLayout.sizeX);
  427 + widgetLayout.sizeY = Math.floor(widgetLayout.sizeY);
  428 + widgetLayout.row = Math.floor(widgetLayout.row);
  429 + widgetLayout.col = Math.floor(widgetLayout.col);
  430 +
425 layout.widgets[widget.id] = widgetLayout; 431 layout.widgets[widget.id] = widgetLayout;
426 } 432 }
427 433
@@ -63,6 +63,9 @@ @@ -63,6 +63,9 @@
63 </mat-error> 63 </mat-error>
64 </mat-form-field> 64 </mat-form-field>
65 </div> 65 </div>
  66 + <mat-error *ngIf="mqttDeviceProfileTransportConfigurationFormGroup.get('configuration').hasError('unique')">
  67 + {{ 'device-profile.mqtt-device-topic-filters-unique' | translate }}
  68 + </mat-error>
66 <div class="tb-hint" innerHTML="{{ 'device-profile.support-level-wildcards' | translate }}"></div> 69 <div class="tb-hint" innerHTML="{{ 'device-profile.support-level-wildcards' | translate }}"></div>
67 <div class="tb-hint" innerHTML="{{ 'device-profile.single-level-wildcards-hint' | translate }}"></div> 70 <div class="tb-hint" innerHTML="{{ 'device-profile.single-level-wildcards-hint' | translate }}"></div>
68 <div class="tb-hint" innerHTML="{{ 'device-profile.multi-level-wildcards-hint' | translate }}"></div> 71 <div class="tb-hint" innerHTML="{{ 'device-profile.multi-level-wildcards-hint' | translate }}"></div>
@@ -51,7 +51,6 @@ export class MqttDeviceProfileTransportConfigurationComponent implements Control @@ -51,7 +51,6 @@ export class MqttDeviceProfileTransportConfigurationComponent implements Control
51 51
52 mqttTransportPayloadTypeTranslations = mqttTransportPayloadTypeTranslationMap; 52 mqttTransportPayloadTypeTranslations = mqttTransportPayloadTypeTranslationMap;
53 53
54 -  
55 mqttDeviceProfileTransportConfigurationFormGroup: FormGroup; 54 mqttDeviceProfileTransportConfigurationFormGroup: FormGroup;
56 55
57 private requiredValue: boolean; 56 private requiredValue: boolean;
@@ -87,7 +86,7 @@ export class MqttDeviceProfileTransportConfigurationComponent implements Control @@ -87,7 +86,7 @@ export class MqttDeviceProfileTransportConfigurationComponent implements Control
87 deviceAttributesTopic: [null, [Validators.required, this.validationMQTTTopic()]], 86 deviceAttributesTopic: [null, [Validators.required, this.validationMQTTTopic()]],
88 deviceTelemetryTopic: [null, [Validators.required, this.validationMQTTTopic()]], 87 deviceTelemetryTopic: [null, [Validators.required, this.validationMQTTTopic()]],
89 transportPayloadType: [MqttTransportPayloadType.JSON, Validators.required] 88 transportPayloadType: [MqttTransportPayloadType.JSON, Validators.required]
90 - }) 89 + }, {validator: this.uniqueDeviceTopicValidator})
91 }); 90 });
92 this.mqttDeviceProfileTransportConfigurationFormGroup.valueChanges.subscribe(() => { 91 this.mqttDeviceProfileTransportConfigurationFormGroup.valueChanges.subscribe(() => {
93 this.updateModel(); 92 this.updateModel();
@@ -147,4 +146,14 @@ export class MqttDeviceProfileTransportConfigurationComponent implements Control @@ -147,4 +146,14 @@ export class MqttDeviceProfileTransportConfigurationComponent implements Control
147 return null; 146 return null;
148 }; 147 };
149 } 148 }
  149 +
  150 + private uniqueDeviceTopicValidator(control: FormGroup): { [key: string]: boolean } | null {
  151 + if (control.value) {
  152 + const formValue = control.value as MqttDeviceProfileTransportConfiguration;
  153 + if (formValue.deviceAttributesTopic === formValue.deviceTelemetryTopic) {
  154 + return {unique: true};
  155 + }
  156 + }
  157 + return null;
  158 + }
150 } 159 }
@@ -131,10 +131,13 @@ export default abstract class LeafletMap { @@ -131,10 +131,13 @@ export default abstract class LeafletMap {
131 tooltipAnchor: [16, -28], 131 tooltipAnchor: [16, -28],
132 shadowSize: [41, 41] 132 shadowSize: [41, 41]
133 }); 133 });
  134 + const customLatLng = this.convertToCustomFormat(mousePositionOnMap);
  135 + mousePositionOnMap.lat = customLatLng[this.options.latKeyName];
  136 + mousePositionOnMap.lng = customLatLng[this.options.lngKeyName];
  137 +
134 const newMarker = L.marker(mousePositionOnMap, { icon }).addTo(this.map); 138 const newMarker = L.marker(mousePositionOnMap, { icon }).addTo(this.map);
135 this.addMarkers.push(newMarker); 139 this.addMarkers.push(newMarker);
136 const datasourcesList = document.createElement('div'); 140 const datasourcesList = document.createElement('div');
137 - const customLatLng = this.convertToCustomFormat(mousePositionOnMap);  
138 const header = document.createElement('p'); 141 const header = document.createElement('p');
139 header.appendChild(document.createTextNode('Select entity:')); 142 header.appendChild(document.createTextNode('Select entity:'));
140 header.setAttribute('style', 'font-size: 14px; margin: 8px 0'); 143 header.setAttribute('style', 'font-size: 14px; margin: 8px 0');
@@ -410,10 +413,15 @@ export default abstract class LeafletMap { @@ -410,10 +413,15 @@ export default abstract class LeafletMap {
410 } 413 }
411 414
412 convertToCustomFormat(position: L.LatLng): object { 415 convertToCustomFormat(position: L.LatLng): object {
413 - return {  
414 - [this.options.latKeyName]: position.lat % 90,  
415 - [this.options.lngKeyName]: position.lng % 180  
416 - }; 416 + if (position.lng > 180) {
  417 + position.lng = 180;
  418 + } else if (position.lng < -180) {
  419 + position.lng = -180;
  420 + }
  421 + return {
  422 + [this.options.latKeyName]: position.lat,
  423 + [this.options.lngKeyName]: position.lng
  424 + };
417 } 425 }
418 426
419 convertToPolygonFormat(points: Array<any>): Array<any> { 427 convertToPolygonFormat(points: Array<any>): Array<any> {
@@ -94,7 +94,7 @@ export class Marker { @@ -94,7 +94,7 @@ export class Marker {
94 } 94 }
95 95
96 updateMarkerPosition(position: L.LatLng) { 96 updateMarkerPosition(position: L.LatLng) {
97 - if (!this.location.equals(position)) { 97 + if (!this.leafletMarker.getLatLng().equals(position)) {
98 this.location = position; 98 this.location = position;
99 this.leafletMarker.setLatLng(position); 99 this.leafletMarker.setLatLng(position);
100 } 100 }
@@ -259,9 +259,27 @@ export class ImageMap extends LeafletMap { @@ -259,9 +259,27 @@ export class ImageMap extends LeafletMap {
259 259
260 convertToCustomFormat(position: L.LatLng, width = this.width, height = this.height): object { 260 convertToCustomFormat(position: L.LatLng, width = this.width, height = this.height): object {
261 const point = this.latLngToPoint(position); 261 const point = this.latLngToPoint(position);
  262 + const customX = calculateNewPointCoordinate(point.x, width);
  263 + const customY = calculateNewPointCoordinate(point.y, height);
  264 +
  265 + if (customX === 0) {
  266 + point.x = 0;
  267 + } else if (customX === 1) {
  268 + point.x = width;
  269 + }
  270 +
  271 + if (customY === 0) {
  272 + point.y = 0;
  273 + } else if (customY === 1) {
  274 + point.y = height;
  275 + }
  276 + const customLatLng = this.pointToLatLng(point.x, point.y);
  277 +
262 return { 278 return {
263 - [this.options.xPosKeyName]: calculateNewPointCoordinate(point.x, width),  
264 - [this.options.yPosKeyName]: calculateNewPointCoordinate(point.y, height) 279 + [this.options.xPosKeyName]: customX,
  280 + [this.options.yPosKeyName]: customY,
  281 + [this.options.latKeyName]: customLatLng.lat,
  282 + [this.options.lngKeyName]: customLatLng.lng
265 }; 283 };
266 } 284 }
267 285
@@ -47,7 +47,8 @@ export enum EntityType { @@ -47,7 +47,8 @@ export enum EntityType {
47 RULE_NODE = 'RULE_NODE', 47 RULE_NODE = 'RULE_NODE',
48 ENTITY_VIEW = 'ENTITY_VIEW', 48 ENTITY_VIEW = 'ENTITY_VIEW',
49 WIDGETS_BUNDLE = 'WIDGETS_BUNDLE', 49 WIDGETS_BUNDLE = 'WIDGETS_BUNDLE',
50 - WIDGET_TYPE = 'WIDGET_TYPE' 50 + WIDGET_TYPE = 'WIDGET_TYPE',
  51 + API_USAGE_STATE = 'API_USAGE_STATE'
51 } 52 }
52 53
53 export enum AliasEntityType { 54 export enum AliasEntityType {
@@ -239,6 +240,12 @@ export const entityTypeTranslations = new Map<EntityType | AliasEntityType, Enti @@ -239,6 +240,12 @@ export const entityTypeTranslations = new Map<EntityType | AliasEntityType, Enti
239 } 240 }
240 ], 241 ],
241 [ 242 [
  243 + EntityType.API_USAGE_STATE,
  244 + {
  245 + type: 'entity.type-api-usage-state'
  246 + }
  247 + ],
  248 + [
242 EntityType.WIDGETS_BUNDLE, 249 EntityType.WIDGETS_BUNDLE,
243 { 250 {
244 details: 'widgets-bundle.widgets-bundle-details', 251 details: 'widgets-bundle.widgets-bundle-details',
@@ -885,6 +885,7 @@ @@ -885,6 +885,7 @@
885 "no-device-profiles-found": "No device profiles found.", 885 "no-device-profiles-found": "No device profiles found.",
886 "create-new-device-profile": "Create a new one!", 886 "create-new-device-profile": "Create a new one!",
887 "mqtt-device-topic-filters": "MQTT device topic filters", 887 "mqtt-device-topic-filters": "MQTT device topic filters",
  888 + "mqtt-device-topic-filters-unique": "MQTT device topic filters need to be unique.",
888 "mqtt-device-payload-type": "MQTT device payload", 889 "mqtt-device-payload-type": "MQTT device payload",
889 "mqtt-device-payload-type-json": "JSON", 890 "mqtt-device-payload-type-json": "JSON",
890 "mqtt-device-payload-type-proto": "Protobuf", 891 "mqtt-device-payload-type-proto": "Protobuf",
@@ -1099,7 +1100,8 @@ @@ -1099,7 +1100,8 @@
1099 "details": "Entity details", 1100 "details": "Entity details",
1100 "no-entities-prompt": "No entities found", 1101 "no-entities-prompt": "No entities found",
1101 "no-data": "No data to display", 1102 "no-data": "No data to display",
1102 - "columns-to-display": "Columns to Display" 1103 + "columns-to-display": "Columns to Display",
  1104 + "type-api-usage-state": "Api Usage State"
1103 }, 1105 },
1104 "entity-field": { 1106 "entity-field": {
1105 "created-time": "Created time", 1107 "created-time": "Created time",